aprs.me/test/aprsme_web/live/info_live/show_helpers_test.exs
Graham McIntire 1d68868d27
refactor: pattern-match InfoLive.Show path parsing + add tests
- parse_path_element_with_link splits its fat `if`/or-chain into three
  focused helpers: linkable_callsign?/1 (regex + alias check),
  path_alias?/1 (four String.starts_with guards), and
  classify_path_element/2 (link vs. text dispatched on the boolean).
- display_for_element/2 chooses the callsign form with/without the
  asterisk via two function heads instead of an inline `if`.

Adds 7 tests covering the parse_path_with_links/1 public API — plain
callsigns, heard-via asterisk preservation, non-linkable aliases
(WIDE/TRACE/RELAY/TCPIP), q-constructs, whitespace trimming, and mixed
paths.
2026-04-23 14:23:16 -05:00

145 lines
4.5 KiB
Elixir

defmodule AprsmeWeb.InfoLive.ShowHelpersTest do
use ExUnit.Case, async: true
alias AprsmeWeb.InfoLive.Show
describe "format_distance/2 with English locale (imperial)" do
test "uses feet for distances under a mile" do
# 0.5 km → ~0.31 miles → ~1641 ft
result = Show.format_distance(0.5, "en")
assert result =~ "ft"
refute result =~ "mi"
end
test "uses miles for distances >= 1 mile" do
# 10 km → ~6.21 miles
result = Show.format_distance(10.0, "en")
assert result =~ "mi"
refute result =~ "ft"
end
test "default locale is English" do
assert Show.format_distance(10.0) =~ "mi"
end
end
describe "format_distance/2 with non-English locale (metric)" do
test "uses meters for distances under 1 km" do
result = Show.format_distance(0.5, "de")
assert result =~ "m"
refute result =~ "km"
end
test "uses km for longer distances" do
result = Show.format_distance(5.0, "fr")
assert result =~ "km"
end
test "handles multiple metric locales" do
for loc <- ["es", "de", "fr", "ja"] do
assert Show.format_distance(10.0, loc) =~ "km"
end
end
end
describe "calculate_course/4" do
test "returns 0 (or 360) for due north" do
# From (0,0) to (1,0) — moving north.
bearing = Show.calculate_course(0.0, 0.0, 1.0, 0.0)
assert bearing == 0.0 or bearing == 360.0
end
test "returns ~90 for due east" do
# From (0,0) to (0,1) — moving east.
bearing = Show.calculate_course(0.0, 0.0, 0.0, 1.0)
assert_in_delta bearing, 90.0, 0.5
end
test "returns ~180 for due south" do
bearing = Show.calculate_course(1.0, 0.0, 0.0, 0.0)
assert_in_delta bearing, 180.0, 0.5
end
test "returns ~270 for due west" do
bearing = Show.calculate_course(0.0, 1.0, 0.0, 0.0)
assert_in_delta bearing, 270.0, 0.5
end
test "always returns a value in 0..360" do
for {lat1, lon1, lat2, lon2} <- [
{0.0, 0.0, 45.0, 45.0},
{10.0, 20.0, -10.0, -20.0},
{0.0, 0.0, 0.0, -1.0}
] do
b = Show.calculate_course(lat1, lon1, lat2, lon2)
assert b >= 0
assert b <= 360
end
end
test "accepts Decimal inputs" do
result = Show.calculate_course(Decimal.new("0.0"), Decimal.new("0.0"), Decimal.new("1.0"), Decimal.new("0.0"))
assert is_float(result)
end
end
describe "haversine/4" do
test "returns 0 for identical points" do
assert Show.haversine(40.0, -100.0, 40.0, -100.0) == 0.0
end
test "is symmetric" do
a = Show.haversine(40.0, -100.0, 41.0, -99.0)
b = Show.haversine(41.0, -99.0, 40.0, -100.0)
assert_in_delta a, b, 0.001
end
test "returns positive distance for distinct points" do
d = Show.haversine(40.0, -100.0, 41.0, -99.0)
assert d > 0
end
end
describe "parse_path_with_links/1" do
test "returns empty list for non-binary input" do
assert Show.parse_path_with_links(nil) == []
assert Show.parse_path_with_links("") == []
assert Show.parse_path_with_links(123) == []
end
test "splits on commas and links plain callsigns" do
result = Show.parse_path_with_links("K5ABC-1,W1XYZ")
assert [{:link, "K5ABC-1", "K5ABC-1"}, {:link, "W1XYZ", "W1XYZ"}] = result
end
test "preserves the heard-via asterisk in the display text" do
assert [{:link, "K5ABC", "K5ABC*"}] = Show.parse_path_with_links("K5ABC*")
end
test "does not link WIDE/TRACE/RELAY/TCPIP path aliases" do
assert [{:text, "WIDE1-1", "WIDE1-1"}] = Show.parse_path_with_links("WIDE1-1")
assert [{:text, "TRACE3-3", "TRACE3-3"}] = Show.parse_path_with_links("TRACE3-3")
assert [{:text, "RELAY", "RELAY"}] = Show.parse_path_with_links("RELAY")
assert [{:text, "TCPIP*", "TCPIP*"}] = Show.parse_path_with_links("TCPIP*")
end
test "does not link q-constructs" do
assert [{:text, "qAC", "qAC"}] = Show.parse_path_with_links("qAC")
assert [{:text, "qAR", "qAR"}] = Show.parse_path_with_links("qAR")
end
test "trims whitespace before linking" do
assert [{:link, "K5ABC", "K5ABC"}] = Show.parse_path_with_links(" K5ABC ")
end
test "mixes links and text in a single path" do
result = Show.parse_path_with_links("K5ABC*,WIDE1-1,W1XYZ")
assert [
{:link, "K5ABC", "K5ABC*"},
{:text, "WIDE1-1", "WIDE1-1"},
{:link, "W1XYZ", "W1XYZ"}
] = result
end
end
end