Adds ~800 new tests (incl. 27 StreamData properties) across 22 modules that previously had little or no coverage. Highlights: - Pure utility modules (LogSanitizer, PacketFieldWhitelist, PacketSanitizer, DeviceParser, CoordinateUtils, BoundsUtils, ParamUtils, Convert, WeatherUnits) get thorough unit + property coverage including UTF-8 truncation boundaries, antimeridian longitude wrap, Haversine symmetry, and unit-conversion inverses. - JSON view modules (CallsignJSON, ErrorJSON, ChangesetJSON) verified for envelope shape and error templating. - Plugs (HealthCheck, RateLimiter, ApiCSRF) exercised for happy-path and halt paths. - GenServer modules (RegexCache, CleanupScheduler, DeploymentNotifier) tested without touching the supervised singleton where possible. - Drops the orphaned map_live/map_helpers_test.exs whose module name collided with the new live/shared/coordinate_utils_test.exs.
114 lines
4.4 KiB
Elixir
114 lines
4.4 KiB
Elixir
defmodule Aprsme.DeviceParserTest do
|
|
use ExUnit.Case, async: true
|
|
use ExUnitProperties
|
|
|
|
alias Aprsme.DeviceParser
|
|
|
|
describe "extract_device_identifier/1" do
|
|
test "returns the device_identifier atom-key value first" do
|
|
assert DeviceParser.extract_device_identifier(%{device_identifier: "APRS123"}) == "APRS123"
|
|
end
|
|
|
|
test "returns the device_identifier string-key value" do
|
|
assert DeviceParser.extract_device_identifier(%{"device_identifier" => "APRS123"}) ==
|
|
"APRS123"
|
|
end
|
|
|
|
test "falls back to destination when device_identifier missing" do
|
|
assert DeviceParser.extract_device_identifier(%{destination: "APN382"}) == "APN382"
|
|
assert DeviceParser.extract_device_identifier(%{"destination" => "APN382"}) == "APN382"
|
|
end
|
|
|
|
test "prefers device_identifier over destination" do
|
|
packet = %{device_identifier: "DIRECT", destination: "APN382"}
|
|
assert DeviceParser.extract_device_identifier(packet) == "DIRECT"
|
|
end
|
|
|
|
test "falls back to symbol table/code from atom-keyed data_extended" do
|
|
packet = %{data_extended: %{symbol_table_id: "/", symbol_code: ">"}}
|
|
assert DeviceParser.extract_device_identifier(packet) == "/>"
|
|
end
|
|
|
|
test "falls back to symbol table/code from string-keyed data_extended" do
|
|
packet = %{"data_extended" => %{"symbol_table_id" => "\\", "symbol_code" => "k"}}
|
|
assert DeviceParser.extract_device_identifier(packet) == "\\k"
|
|
end
|
|
|
|
test "prefers destination over data_extended symbol (destination clause matches first)" do
|
|
# Pattern-match order in find_device_identifier/1 checks destination before data_extended.
|
|
packet = %{
|
|
data_extended: %{symbol_table_id: "/", symbol_code: ">"},
|
|
destination: "APN382"
|
|
}
|
|
|
|
assert DeviceParser.extract_device_identifier(packet) == "APN382"
|
|
end
|
|
|
|
test "falls back to data_extended symbol when no destination is set" do
|
|
packet = %{data_extended: %{symbol_table_id: "/", symbol_code: ">"}}
|
|
assert DeviceParser.extract_device_identifier(packet) == "/>"
|
|
end
|
|
|
|
test "returns nil for empty device_identifier strings" do
|
|
assert DeviceParser.extract_device_identifier(%{device_identifier: ""}) == nil
|
|
end
|
|
|
|
test "returns nil for empty destination strings" do
|
|
assert DeviceParser.extract_device_identifier(%{destination: ""}) == nil
|
|
end
|
|
|
|
test "returns nil for empty map" do
|
|
assert DeviceParser.extract_device_identifier(%{}) == nil
|
|
end
|
|
|
|
test "returns nil for non-map input" do
|
|
assert DeviceParser.extract_device_identifier(nil) == nil
|
|
assert DeviceParser.extract_device_identifier("hello") == nil
|
|
assert DeviceParser.extract_device_identifier(42) == nil
|
|
assert DeviceParser.extract_device_identifier([]) == nil
|
|
end
|
|
|
|
test "returns nil when device_identifier is a non-binary truthy value" do
|
|
assert DeviceParser.extract_device_identifier(%{device_identifier: :atom}) == nil
|
|
assert DeviceParser.extract_device_identifier(%{device_identifier: 123}) == nil
|
|
end
|
|
end
|
|
|
|
describe "normalize_device_identifier/1" do
|
|
test "trims whitespace from strings" do
|
|
assert DeviceParser.normalize_device_identifier(" APRS123 ") == "APRS123"
|
|
end
|
|
|
|
test "leaves clean strings alone" do
|
|
assert DeviceParser.normalize_device_identifier("APRS123") == "APRS123"
|
|
end
|
|
|
|
test "converts atoms to strings" do
|
|
assert DeviceParser.normalize_device_identifier(:yaesu_ft1d) == "yaesu_ft1d"
|
|
end
|
|
|
|
test "returns nil for nil" do
|
|
assert DeviceParser.normalize_device_identifier(nil) == nil
|
|
end
|
|
|
|
test "returns nil for unsupported types" do
|
|
assert DeviceParser.normalize_device_identifier(123) == nil
|
|
assert DeviceParser.normalize_device_identifier(%{}) == nil
|
|
assert DeviceParser.normalize_device_identifier([]) == nil
|
|
end
|
|
|
|
property "strings round-trip through normalization without surrounding whitespace" do
|
|
check all(s <- string(:alphanumeric, min_length: 1)) do
|
|
assert DeviceParser.normalize_device_identifier(s) == s
|
|
assert DeviceParser.normalize_device_identifier(" " <> s <> " ") == s
|
|
end
|
|
end
|
|
|
|
property "atom inputs normalize to their string form" do
|
|
check all(s <- string(:alphanumeric, min_length: 1, max_length: 10)) do
|
|
atom = String.to_atom(s)
|
|
assert DeviceParser.normalize_device_identifier(atom) == s
|
|
end
|
|
end
|
|
end
|
|
end
|