diff --git a/lib/aprsme/geo_utils.ex b/lib/aprsme/geo_utils.ex
index 1ff155f..8db3241 100644
--- a/lib/aprsme/geo_utils.ex
+++ b/lib/aprsme/geo_utils.ex
@@ -12,7 +12,7 @@ defmodule Aprsme.GeoUtils do
## Examples
- iex> Aprsme.GeoUtils.haversine_distance(33.16961, -96.4921, 33.16962, -96.4921)
+ iex> Aprsme.GeoUtils.haversine_distance(33.16961, -96.4921, 33.16962, -96.4921) |> Float.round(2)
1.11
"""
@spec haversine_distance(number(), number(), number(), number()) :: float()
diff --git a/lib/aprsme_web/aprs_symbol.ex b/lib/aprsme_web/aprs_symbol.ex
index 64ec2a5..ea6c126 100644
--- a/lib/aprsme_web/aprs_symbol.ex
+++ b/lib/aprsme_web/aprs_symbol.ex
@@ -21,7 +21,7 @@ defmodule AprsmeWeb.AprsSymbol do
iex> AprsmeWeb.AprsSymbol.get_sprite_info("/", "_")
%{
sprite_file: "/aprs-symbols/aprs-symbols-128-0@2x.png",
- background_position: "-352px -32px",
+ background_position: "-448.0px -96.0px",
background_size: "512px 192px"
}
"""
@@ -150,7 +150,7 @@ defmodule AprsmeWeb.AprsSymbol do
iex> AprsmeWeb.AprsSymbol.get_table_id("/")
"0"
- iex> AprsmeWeb.AprsSymbol.get_table_id("\\")
+ iex> AprsmeWeb.AprsSymbol.get_table_id("\\\\")
"1"
iex> AprsmeWeb.AprsSymbol.get_table_id("]")
@@ -168,8 +168,8 @@ defmodule AprsmeWeb.AprsSymbol do
## Examples
- iex> AprsmeWeb.AprsSymbol.render_marker_html("/", "_", "W1AW")
- "
..."
+ iex> AprsmeWeb.AprsSymbol.render_marker_html("/", "_", "W1AW") |> String.contains?("W1AW")
+ true
"""
@spec render_marker_html(String.t() | nil, String.t() | nil, String.t() | nil, integer()) :: String.t()
def render_marker_html(symbol_table, symbol_code, callsign \\ nil, size \\ 32) do
@@ -268,7 +268,7 @@ defmodule AprsmeWeb.AprsSymbol do
## Examples
iex> AprsmeWeb.AprsSymbol.render_style("/", "_", 32)
- "width: 32px; height: 32px; background-image: url(/aprs-symbols/aprs-symbols-128-0@2x.png); ..."
+ "width: 32px; height: 32px; background-image: url(/aprs-symbols/aprs-symbols-128-0@2x.png); background-position: -448.0px -96.0px; background-size: 512px 192px; background-repeat: no-repeat; image-rendering: pixelated; opacity: 1.0; display: inline-block; vertical-align: middle; margin-bottom: -6px;"
"""
@spec render_style(String.t() | nil, String.t() | nil, integer()) :: String.t()
def render_style(symbol_table, symbol_code, size \\ 32) do
diff --git a/test/aprs_is_mock_test.exs b/test/aprs_is_mock_test.exs
index d826b5c..ab07a16 100644
--- a/test/aprs_is_mock_test.exs
+++ b/test/aprs_is_mock_test.exs
@@ -29,7 +29,9 @@ defmodule AprsIsMockTest do
assert status.server == "mock.aprs.test"
assert status.port == 14_580
assert is_nil(status.connected_at)
- assert status.packet_stats == %{}
+ assert status.packet_stats.total_packets == 0
+ assert is_nil(status.packet_stats.last_packet_at)
+ assert status.packet_stats.packets_per_second == 0
assert status.stored_packet_count == 0
end
diff --git a/test/aprsme/is_test.exs b/test/aprsme/is_test.exs
index 5966010..8c0ff30 100644
--- a/test/aprsme/is_test.exs
+++ b/test/aprsme/is_test.exs
@@ -35,7 +35,8 @@ defmodule Aprsme.IsTest do
filter: "r/33/-96/100"
},
backpressure_active: false,
- safety_valve_timer: nil
+ safety_valve_timer: nil,
+ failure_started_at: nil
}
Map.merge(base, overrides)
@@ -128,23 +129,32 @@ defmodule Aprsme.IsTest do
end
test "dispatches invalid packet and stores bad packet" do
+ Logger.configure(level: :debug)
+
log =
capture_log(fn ->
Aprsme.Is.dispatch("totally invalid data that cannot be parsed")
end)
+ Logger.configure(level: :warning)
+
# Should not crash — the function handles errors gracefully
assert log =~ "PARSE ERROR"
end
- test "handles parse error with specific error message" do
+ test "handles minimal valid APRS packet with empty body" do
+ Logger.configure(level: :debug)
+
log =
capture_log(fn ->
- # A packet that triggers {:error, some_message} rather than {:error, :invalid_packet}
+ # "X>Y:" is a minimal valid APRS packet (sender X, destination Y, empty body).
+ # Aprs.parse/1 returns {:ok, ...} for this input, so no PARSE ERROR is emitted.
Aprsme.Is.dispatch("X>Y:")
end)
- assert log =~ "PARSE ERROR"
+ Logger.configure(level: :warning)
+
+ refute log =~ "PARSE ERROR"
end
end
@@ -871,7 +881,7 @@ defmodule Aprsme.IsTest do
capture_log(fn ->
assert {:noreply, same_state} = Aprsme.Is.handle_info(:reconnect, state)
- assert same_state == state
+ refute is_nil(same_state.failure_started_at)
end)
end
end
@@ -1283,20 +1293,28 @@ defmodule Aprsme.IsTest do
end
test "handles rescue in dispatch when packet processing raises" do
+ Logger.configure(level: :debug)
+
log =
capture_log(fn ->
Aprsme.Is.dispatch("???")
end)
+ Logger.configure(level: :warning)
+
assert log =~ "PARSE ERROR"
end
test "stores bad packet on parse error" do
+ Logger.configure(level: :debug)
+
log =
capture_log(fn ->
Aprsme.Is.dispatch("not a valid aprs packet at all")
end)
+ Logger.configure(level: :warning)
+
assert log =~ "PARSE ERROR"
end
@@ -1435,7 +1453,9 @@ defmodule Aprsme.IsTest do
status = Aprsme.Is.get_status()
assert status.connected == false
- assert status.server == "rotate.aprs2.net"
+ # disconnected_status/1 reads the configured :aprs_is_server, overriding
+ # the fallback argument — test config sets it to "mock.aprs.test".
+ assert status.server == "mock.aprs.test"
after
if Process.whereis(Aprsme.Is) == pid do
:erlang.unregister(Aprsme.Is)
diff --git a/test/aprsme/packet_replay_test.exs b/test/aprsme/packet_replay_test.exs
index bcb3d97..93c4b49 100644
--- a/test/aprsme/packet_replay_test.exs
+++ b/test/aprsme/packet_replay_test.exs
@@ -4,7 +4,6 @@ defmodule Aprsme.PacketReplayTest do
alias Aprsme.PacketReplay
setup do
- start_supervised!({Registry, keys: :unique, name: Aprsme.ReplayRegistry})
:ok
end
@@ -251,7 +250,7 @@ defmodule Aprsme.PacketReplayTest do
state = %{state | paused: true}
assert {:reply, :ok, new_state} = PacketReplay.handle_call(:resume, self(), state)
refute new_state.paused
- assert_received {:continue_replay}
+ assert_received :start_replay
end
test "resume when already running is a no-op", %{state: state} do
diff --git a/test/aprsme_web/controllers/api/v1/callsign_controller_test.exs b/test/aprsme_web/controllers/api/v1/callsign_controller_test.exs
index 0881acc..af23acb 100644
--- a/test/aprsme_web/controllers/api/v1/callsign_controller_test.exs
+++ b/test/aprsme_web/controllers/api/v1/callsign_controller_test.exs
@@ -40,7 +40,7 @@ defmodule AprsmeWeb.Api.V1.CallsignControllerTest do
assert conn.status == 200
body = json_response(conn, 200)
assert body["data"]["callsign"] == "W1ABC-9"
- assert Map.has_key?(body["data"]["position"], :lat)
+ assert Map.has_key?(body["data"]["position"], "latitude")
end
test "normalizes the callsign to uppercase", %{conn: conn} do
diff --git a/test/aprsme_web/controllers/api/v1/fallback_controller_test.exs b/test/aprsme_web/controllers/api/v1/fallback_controller_test.exs
index 7aaa509..7a263f7 100644
--- a/test/aprsme_web/controllers/api/v1/fallback_controller_test.exs
+++ b/test/aprsme_web/controllers/api/v1/fallback_controller_test.exs
@@ -22,7 +22,7 @@ defmodule AprsmeWeb.Api.V1.FallbackControllerTest do
assert conn.status == 422
body = json(conn)
assert body["error"]["code"] == "validation_error"
- assert Map.has_key?(body["error"]["details"], :identifier)
+ assert Map.has_key?(body["error"]["details"], "identifier")
end
test "handles {:error, :not_found}", %{conn: conn} do
diff --git a/test/aprsme_web/live/map_live/movement_test.exs b/test/aprsme_web/live/map_live/movement_test.exs
index 46c5e5c..442b6fc 100644
--- a/test/aprsme_web/live/map_live/movement_test.exs
+++ b/test/aprsme_web/live/map_live/movement_test.exs
@@ -17,12 +17,11 @@ defmodule AprsmeWeb.MapLive.MovementTest do
test "does not update marker for GPS drift", %{conn: conn} do
{:ok, view, _html} = live(conn, "/", on_error: :warn)
- assert {:ok, _v} =
- Phoenix.LiveViewTest.render_hook(
- view,
- "update_map_state",
- %{"center" => %{"lat" => 33.16961, "lng" => -96.4921}, "zoom" => 9}
- )
+ assert render_hook(
+ view,
+ "update_map_state",
+ %{"center" => %{"lat" => 33.16961, "lng" => -96.4921}, "zoom" => 9}
+ )
bounds_params = %{
"bounds" => %{
@@ -33,12 +32,11 @@ defmodule AprsmeWeb.MapLive.MovementTest do
}
}
- assert {:ok, _v} =
- Phoenix.LiveViewTest.render_hook(
- view,
- "bounds_changed",
- bounds_params
- )
+ assert render_hook(
+ view,
+ "bounds_changed",
+ bounds_params
+ )
initial_packet = %{
id: "TEST-1",
@@ -77,7 +75,7 @@ defmodule AprsmeWeb.MapLive.MovementTest do
test "updates marker for significant movement", %{conn: conn} do
{:ok, view, _html} = live(conn, "/?z=15", on_error: :warn)
- assert {:ok, _v} = Phoenix.LiveViewTest.render_hook(view, "map_ready", %{})
+ assert render_hook(view, "map_ready", %{})
bounds_params = %{
"bounds" => %{
@@ -88,12 +86,11 @@ defmodule AprsmeWeb.MapLive.MovementTest do
}
}
- assert {:ok, _v} =
- Phoenix.LiveViewTest.render_hook(
- view,
- "bounds_changed",
- bounds_params
- )
+ assert render_hook(
+ view,
+ "bounds_changed",
+ bounds_params
+ )
Phoenix.LiveViewTest.render(view)
flush_push_events(view)
diff --git a/test/aprsme_web/live/map_live/packet_utils_test.exs b/test/aprsme_web/live/map_live/packet_utils_test.exs
index 04d871b..7953ceb 100644
--- a/test/aprsme_web/live/map_live/packet_utils_test.exs
+++ b/test/aprsme_web/live/map_live/packet_utils_test.exs
@@ -384,13 +384,13 @@ defmodule AprsmeWeb.MapLive.PacketUtilsTest do
test "converts tuple values inside maps" do
result = PacketUtils.convert_tuples_to_strings(%{a: {1, 2}, b: "keep"})
- assert not is_nil(result).a == "{1, 2}"
- assert not is_nil(result).b == "keep"
+ assert result.a == "{1, 2}"
+ assert result.b == "keep"
end
test "walks nested maps" do
result = PacketUtils.convert_tuples_to_strings(%{outer: %{inner: {:ok, "v"}}})
- assert not is_nil(result).outer.inner =~ "{:ok,"
+ assert result.outer.inner =~ "{:ok,"
end
test "walks lists" do
diff --git a/test/aprsme_web/live/status_live/index_test.exs b/test/aprsme_web/live/status_live/index_test.exs
index 87952b0..e91bb05 100644
--- a/test/aprsme_web/live/status_live/index_test.exs
+++ b/test/aprsme_web/live/status_live/index_test.exs
@@ -131,7 +131,6 @@ defmodule AprsmeWeb.StatusLive.IndexTest do
assert {:noreply, new_socket} = Index.handle_info({:status_updated, status}, socket)
assigns = Map.get(new_socket, :assigns)
assert assigns.aprs_status == status
- assert assigns.loading == false
assert %DateTime{} = assigns.current_time
# Connected+uptime 60s → health score 2.
assert assigns.health_score == 2
@@ -277,7 +276,8 @@ defmodule AprsmeWeb.StatusLive.IndexTest do
assert {:noreply, new_socket} = Index.handle_info({:status_updated, status}, socket)
assigns = Map.get(new_socket, :assigns)
- assert assigns.loading == false
+ # Loading is unchanged (handle_info doesn't touch it)
+ assert assigns.loading == true
assert assigns.aprs_status == status
end
end
diff --git a/test/aprsme_web/live/theme_manager_test.exs b/test/aprsme_web/live/theme_manager_test.exs
index dfb7b9e..653cf96 100644
--- a/test/aprsme_web/live/theme_manager_test.exs
+++ b/test/aprsme_web/live/theme_manager_test.exs
@@ -70,7 +70,7 @@ defmodule AprsmeWeb.ThemeManagerTest do
assigns = Map.get(result, :assigns)
assert assigns.theme == "dark"
assert assigns.resolved_theme == "dark"
- assert assigns.theme_colors.text == "#111827"
+ assert assigns.theme_colors.text == "#e5e7eb"
end
test "defaults to 'auto' when no session theme" do