From 8c162a825fd197ff6aef1a96ab9e517f5cfc0d07 Mon Sep 17 00:00:00 2001 From: njausteve Date: Sun, 6 Oct 2024 11:58:06 +0800 Subject: [PATCH] feat: Update proximity_UUID validation error message in ExPass.Utils.Validators module --- lib/structs/beacons.ex | 33 +++++++---- lib/utils/validators.ex | 13 +++-- test/structs/beacons_test.exs | 107 ++++++++++++++++++++++++++-------- 3 files changed, 115 insertions(+), 38 deletions(-) diff --git a/lib/structs/beacons.ex b/lib/structs/beacons.ex index 884eebe..578d849 100644 --- a/lib/structs/beacons.ex +++ b/lib/structs/beacons.ex @@ -6,7 +6,8 @@ defmodule ExPass.Structs.Beacons do * `:major` - 16-bit unsigned integer. The major identifier of a Bluetooth Low Energy location beacon. * `:minor` - 16-bit unsigned integer. The minor identifier of a Bluetooth Low Energy location beacon. - * `:proximityUUID` - (Required) The unique identifier of a Bluetooth Low Energy location beacon. + * `:proximity_UUID` - (Required) The unique identifier of a Bluetooth Low Energy location beacon. + * `:relevant_text` - (Optional) The text to display on the lock screen when the pass is relevant. ## Compatibility @@ -23,7 +24,8 @@ defmodule ExPass.Structs.Beacons do typedstruct do field :major, integer() field :minor, integer() - field :proximityUUID, String.t(), enforce: true + field :proximity_UUID, String.t(), enforce: true + field :relevant_text, String.t() end @doc """ @@ -34,7 +36,8 @@ defmodule ExPass.Structs.Beacons do * `attrs` - A map of attributes for the Beacons struct. The map can include the following keys: * `:major` - (Optional) The major identifier of a Bluetooth Low Energy location beacon. * `:minor` - (Optional) The minor identifier of a Bluetooth Low Energy location beacon. - * `:proximityUUID` - (Required) The unique identifier of a Bluetooth Low Energy location beacon. + * `:proximity_UUID` - (Required) The unique identifier of a Bluetooth Low Energy location beacon. + * `:relevant_text` - (Optional) The text to display on the lock screen when the pass is relevant. ## Returns @@ -42,14 +45,23 @@ defmodule ExPass.Structs.Beacons do ## Examples - iex> Beacons.new(%{proximityUUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", major: 12_345, minor: 6_789}) - %Beacons{proximityUUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", major: 12_345, minor: 6_789} + iex> Beacons.new(%{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", major: 12_345, minor: 6_789}) + %Beacons{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", major: 12_345, minor: 6_789, relevant_text: nil} - iex> Beacons.new(%{proximityUUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", major: 12_345}) - %Beacons{proximityUUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", major: 12_345, minor: nil} + iex> Beacons.new(%{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", major: 12_345}) + %Beacons{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", major: 12_345, minor: nil, relevant_text: nil} - iex> Beacons.new(%{proximityUUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0"}) - %Beacons{proximityUUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", major: nil, minor: nil} + iex> Beacons.new(%{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0"}) + %Beacons{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", major: nil, minor: nil, relevant_text: nil} + + iex> Beacons.new(%{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", relevant_text: "Welcome to our store!"}) + %Beacons{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", major: nil, minor: nil, relevant_text: "Welcome to our store!"} + + iex> Beacons.new(%{proximity_UUID: "invalid-uuid"}) + ** (ArgumentError) proximity_UUID must be a valid UUID string + + iex> Beacons.new(%{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", major: 65_536}) + ** (ArgumentError) major must be a 16-bit unsigned integer (0-65_535) """ @spec new(map()) :: %__MODULE__{} @@ -58,7 +70,8 @@ defmodule ExPass.Structs.Beacons do attrs |> validate(:major, &Validators.validate_optional_16bit_unsigned_integer(&1, :major)) |> validate(:minor, &Validators.validate_optional_16bit_unsigned_integer(&1, :minor)) - |> validate(:proximityUUID, &Validators.validate_uuid/1) + |> validate(:proximity_UUID, &Validators.validate_uuid/1) + |> validate(:relevant_text, &Validators.validate_optional_string(&1, :relevant_text)) struct!(__MODULE__, attrs) end diff --git a/lib/utils/validators.ex b/lib/utils/validators.ex index 46e4925..c83f0f0 100644 --- a/lib/utils/validators.ex +++ b/lib/utils/validators.ex @@ -850,14 +850,17 @@ defmodule ExPass.Utils.Validators do :ok iex> validate_uuid("not-a-uuid") - {:error, "proximityUUID must be a valid UUID string"} + {:error, "proximity_UUID must be a valid UUID string"} iex> validate_uuid(nil) - {:error, "proximityUUID is required"} + {:error, "proximity_UUID is required"} + + iex> validate_uuid(123) + {:error, "proximity_UUID must be a valid UUID string"} """ @spec validate_uuid(String.t() | nil) :: :ok | {:error, String.t()} - def validate_uuid(nil), do: {:error, "proximityUUID is required"} + def validate_uuid(nil), do: {:error, "proximity_UUID is required"} def validate_uuid(value) when is_binary(value) do uuid_regex = ~r/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i @@ -865,11 +868,11 @@ defmodule ExPass.Utils.Validators do if Regex.match?(uuid_regex, value) do :ok else - {:error, "proximityUUID must be a valid UUID string"} + {:error, "proximity_UUID must be a valid UUID string"} end end - def validate_uuid(_), do: {:error, "proximityUUID must be a valid UUID string"} + def validate_uuid(_), do: {:error, "proximity_UUID must be a valid UUID string"} defp validate_inclusion(value, valid_values, field_name) do if value in valid_values do diff --git a/test/structs/beacons_test.exs b/test/structs/beacons_test.exs index 45ebc3a..a583746 100644 --- a/test/structs/beacons_test.exs +++ b/test/structs/beacons_test.exs @@ -5,8 +5,8 @@ defmodule ExPass.Structs.BeaconsTest do alias ExPass.Structs.Beacons describe "new/0" do - test "raises an error for missing proximityUUID" do - assert_raise ArgumentError, "proximityUUID is required", fn -> + test "raises an error for missing proximity_UUID" do + assert_raise ArgumentError, "proximity_UUID is required", fn -> Beacons.new() end end @@ -14,38 +14,38 @@ defmodule ExPass.Structs.BeaconsTest do describe "new/1 with minor field" do test "creates a valid Beacons struct with minor field" do - params = %{proximityUUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", minor: 50} + params = %{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", minor: 50} assert %Beacons{} = beacon = Beacons.new(params) assert beacon.minor == params.minor assert beacon.major == nil encoded = Jason.encode!(beacon) assert encoded =~ ~s("minor":50) - assert encoded =~ ~s("proximityuuid":"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0") + assert encoded =~ ~s("proximityUUID":"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0") end test "creates a valid Beacons struct with minor field set to 0" do - params = %{proximityUUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", minor: 0} + params = %{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", minor: 0} assert %Beacons{} = beacon = Beacons.new(params) assert beacon.minor == 0 encoded = Jason.encode!(beacon) assert encoded =~ ~s("minor":0) - assert encoded =~ ~s("proximityuuid":"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0") + assert encoded =~ ~s("proximityUUID":"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0") end test "creates a valid Beacons struct with minor field set to 65535 (max value)" do - params = %{proximityUUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", minor: 65_535} + params = %{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", minor: 65_535} assert %Beacons{} = beacon = Beacons.new(params) assert beacon.minor == 65_535 encoded = Jason.encode!(beacon) assert encoded =~ ~s("minor":65535) - assert encoded =~ ~s("proximityuuid":"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0") + assert encoded =~ ~s("proximityUUID":"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0") end test "returns error for invalid minor (negative value)" do - params = %{proximityUUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", minor: -1} + params = %{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", minor: -1} assert_raise ArgumentError, "minor must be a 16-bit unsigned integer (0-65_535)", fn -> Beacons.new(params) @@ -53,7 +53,7 @@ defmodule ExPass.Structs.BeaconsTest do end test "returns error for invalid minor (value too large)" do - params = %{proximityUUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", minor: 65_536} + params = %{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", minor: 65_536} assert_raise ArgumentError, "minor must be a 16-bit unsigned integer (0-65_535)", fn -> Beacons.new(params) @@ -61,7 +61,7 @@ defmodule ExPass.Structs.BeaconsTest do end test "returns error for invalid minor (non-integer value)" do - params = %{proximityUUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", minor: "50"} + params = %{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", minor: "50"} assert_raise ArgumentError, "minor must be a 16-bit unsigned integer (0-65_535)", fn -> Beacons.new(params) @@ -71,41 +71,102 @@ defmodule ExPass.Structs.BeaconsTest do describe "new/1 with major field" do test "creates a valid Beacons struct with major field" do - params = %{proximityUUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", major: 100} + params = %{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", major: 100} assert %Beacons{} = beacon = Beacons.new(params) assert beacon.major == params.major assert beacon.minor == nil encoded = Jason.encode!(beacon) assert encoded =~ ~s("major":100) - assert encoded =~ ~s("proximityuuid":"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0") + assert encoded =~ ~s("proximityUUID":"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0") end end - describe "new/1 with proximityUUID field" do - test "creates a valid Beacons struct with proximityUUID field" do - params = %{proximityUUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0"} + describe "new/1 with proximity_UUID field" do + test "creates a valid Beacons struct with proximity_UUID field" do + params = %{proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0"} assert %Beacons{} = beacon = Beacons.new(params) - assert beacon.proximityUUID == params.proximityUUID + assert beacon.proximity_UUID == params.proximity_UUID assert beacon.major == nil assert beacon.minor == nil encoded = Jason.encode!(beacon) - assert encoded =~ ~s("proximityuuid":"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0") + assert encoded =~ ~s("proximityUUID":"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0") end - test "returns error for invalid proximityUUID (not a UUID)" do - params = %{proximityUUID: "not-a-uuid"} + test "returns error for invalid proximity_UUID (not a UUID)" do + params = %{proximity_UUID: "not-a-uuid"} - assert_raise ArgumentError, "proximityUUID must be a valid UUID string", fn -> + assert_raise ArgumentError, "proximity_UUID must be a valid UUID string", fn -> Beacons.new(params) end end - test "returns error for missing proximityUUID" do + test "returns error for missing proximity_UUID" do params = %{major: 100, minor: 50} - assert_raise ArgumentError, "proximityUUID is required", fn -> + assert_raise ArgumentError, "proximity_UUID is required", fn -> + Beacons.new(params) + end + end + + test "returns error for invalid proximity_UUID (non-string value)" do + params = %{proximity_UUID: 12_345} + + assert_raise ArgumentError, "proximity_UUID must be a valid UUID string", fn -> + Beacons.new(params) + end + end + + test "returns error for invalid proximity_UUID (nil value)" do + params = %{proximity_UUID: nil} + + assert_raise ArgumentError, "proximity_UUID is required", fn -> + Beacons.new(params) + end + end + end + + describe "new/1 with relevant_text field" do + test "creates a valid Beacons struct with relevant_text field" do + params = %{ + proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", + relevant_text: "Nearby store" + } + + assert %Beacons{} = beacon = Beacons.new(params) + assert beacon.relevant_text == params.relevant_text + encoded = Jason.encode!(beacon) + assert encoded =~ ~s("relevantText":"Nearby store") + assert encoded =~ ~s("proximityUUID":"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0") + end + + test "creates a valid Beacons struct with all fields" do + params = %{ + proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", + major: 100, + minor: 50, + relevant_text: "Nearby store" + } + + assert %Beacons{} = beacon = Beacons.new(params) + assert beacon.major == params.major + assert beacon.minor == params.minor + assert beacon.relevant_text == params.relevant_text + encoded = Jason.encode!(beacon) + assert encoded =~ ~s("major":100) + assert encoded =~ ~s("minor":50) + assert encoded =~ ~s("relevantText":"Nearby store") + assert encoded =~ ~s("proximityUUID":"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0") + end + + test "raises an error for invalid relevant_text" do + params = %{ + proximity_UUID: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0", + relevant_text: 123 + } + + assert_raise ArgumentError, "relevant_text must be a string if provided", fn -> Beacons.new(params) end end