fix: repair all 60 failing tests after refactor and dep updates

- IS GenServer: add missing :failure_started_at to test build_state/1 map
- IS GenServer: fix stale status server assertion and reconnection test
- IS GenServer: set Logger level to :debug for dispatch parse-error tests
- PacketReplay: remove conflicting Registry start_supervised! from setup
- PacketReplay: update assertion from {:continue_replay} to :start_replay
- Doctests: fix float precision, stale sprite coords, quoting escapes
- API controllers: use string keys for JSON error/postion details
- PacketUtils: fix operator precedence (not is_nil(result).field)
- ThemeManager: update expected dark theme text color
- StatusLive: remove/update stale :loading assign assertions
- Movement: remove {:ok, _v} wrapper from render_hook/3 (returns HTML)
- AprsIsMock: update packet_stats assertion for populated default shape
This commit is contained in:
Graham McIntire 2026-07-01 09:41:10 -05:00
parent 01ecf3d6bc
commit 80983e6223
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
11 changed files with 60 additions and 42 deletions

View file

@ -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()

View file

@ -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")
"<div style=\"position: relative; width: 32px; height: 32px; display: flex; align-items: center;\">..."
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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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