- Radio context with QSO schema and CSV import script (58K contacts) - Solar index schema, GFZ client, and daily Oban cron worker - LiveView dashboard with QSO table and weather correlation views - Parallelize weather import script using Task.async_stream (10 concurrent workers) - Replace sequential rate_limit sleeps with concurrency-based backpressure - Atomic progress counter for interleave-safe reporting
150 lines
4.1 KiB
Elixir
150 lines
4.1 KiB
Elixir
defmodule MicrowavepropWeb.QsoLiveTest do
|
|
use MicrowavepropWeb.ConnCase, async: true
|
|
|
|
import Phoenix.LiveViewTest
|
|
|
|
alias Microwaveprop.Radio.Qso
|
|
alias Microwaveprop.Repo
|
|
|
|
defp create_qso(attrs \\ %{}) do
|
|
default = %{
|
|
station1: "W5XD",
|
|
station2: "K5TR",
|
|
qso_timestamp: ~U[2026-03-28 18:00:00Z],
|
|
mode: "CW",
|
|
band: Decimal.new("1296"),
|
|
grid1: "EM12",
|
|
grid2: "EM00",
|
|
pos1: %{"lat" => 32.9, "lon" => -97.0},
|
|
pos2: %{"lat" => 30.3, "lon" => -97.7},
|
|
distance_km: Decimal.new("295")
|
|
}
|
|
|
|
{:ok, qso} =
|
|
%Qso{}
|
|
|> Qso.changeset(Map.merge(default, attrs))
|
|
|> Repo.insert()
|
|
|
|
qso
|
|
end
|
|
|
|
describe "Index" do
|
|
test "renders page with QSOs heading", %{conn: conn} do
|
|
{:ok, _lv, html} = live(conn, ~p"/qsos")
|
|
assert html =~ "QSOs"
|
|
end
|
|
|
|
test "table shows QSO data", %{conn: conn} do
|
|
create_qso()
|
|
|
|
{:ok, _lv, html} = live(conn, ~p"/qsos")
|
|
assert html =~ "W5XD"
|
|
assert html =~ "K5TR"
|
|
assert html =~ "CW"
|
|
assert html =~ "1296"
|
|
end
|
|
|
|
test "row links to detail page", %{conn: conn} do
|
|
qso = create_qso()
|
|
|
|
{:ok, _lv, html} = live(conn, ~p"/qsos")
|
|
assert html =~ ~p"/qsos/#{qso.id}"
|
|
end
|
|
|
|
test "clicking sort header changes URL params", %{conn: conn} do
|
|
create_qso(%{station1: "ZZ9ZZ"})
|
|
create_qso(%{station1: "AA1AA"})
|
|
|
|
{:ok, lv, _html} = live(conn, ~p"/qsos")
|
|
|
|
lv |> element("span[phx-click=sort][phx-value-field=station1]") |> render_click()
|
|
|
|
assert_patch(lv, "/qsos?sort_by=station1&sort_order=asc")
|
|
end
|
|
|
|
test "sort params order the table", %{conn: conn} do
|
|
create_qso(%{station1: "ZZ9ZZ"})
|
|
create_qso(%{station1: "AA1AA"})
|
|
|
|
{:ok, _lv, html} = live(conn, ~p"/qsos?sort_by=station1&sort_order=asc")
|
|
|
|
# AA1AA should appear before ZZ9ZZ
|
|
aa_pos = html |> :binary.match("AA1AA") |> elem(0)
|
|
zz_pos = html |> :binary.match("ZZ9ZZ") |> elem(0)
|
|
assert aa_pos < zz_pos
|
|
end
|
|
|
|
test "clicking same sort header toggles direction", %{conn: conn} do
|
|
create_qso()
|
|
|
|
{:ok, lv, _html} = live(conn, ~p"/qsos?sort_by=station1&sort_order=asc")
|
|
|
|
lv |> element("span[phx-click=sort][phx-value-field=station1]") |> render_click()
|
|
|
|
assert_patch(lv, "/qsos?sort_by=station1&sort_order=desc")
|
|
end
|
|
|
|
test "pagination works", %{conn: conn} do
|
|
for i <- 1..25 do
|
|
ts = DateTime.add(~U[2026-01-01 00:00:00Z], i * 3600, :second)
|
|
create_qso(%{qso_timestamp: ts})
|
|
end
|
|
|
|
{:ok, _lv, html} = live(conn, ~p"/qsos")
|
|
assert html =~ "Page 1 of 2"
|
|
|
|
{:ok, _lv, html2} = live(conn, ~p"/qsos?page=2")
|
|
assert html2 =~ "Page 2 of 2"
|
|
end
|
|
end
|
|
|
|
describe "Show" do
|
|
test "renders QSO detail fields", %{conn: conn} do
|
|
qso = create_qso()
|
|
|
|
{:ok, _lv, html} = live(conn, ~p"/qsos/#{qso.id}")
|
|
assert html =~ "W5XD"
|
|
assert html =~ "K5TR"
|
|
assert html =~ "CW"
|
|
assert html =~ "1296"
|
|
assert html =~ "EM12"
|
|
assert html =~ "EM00"
|
|
end
|
|
|
|
test "shows surface observations section", %{conn: conn} do
|
|
qso = create_qso()
|
|
{:ok, _lv, html} = live(conn, ~p"/qsos/#{qso.id}")
|
|
assert html =~ "Surface Observations"
|
|
end
|
|
|
|
test "shows sounding section", %{conn: conn} do
|
|
qso = create_qso()
|
|
{:ok, _lv, html} = live(conn, ~p"/qsos/#{qso.id}")
|
|
assert html =~ "Soundings"
|
|
end
|
|
|
|
test "shows solar index section", %{conn: conn} do
|
|
qso = create_qso()
|
|
{:ok, _lv, html} = live(conn, ~p"/qsos/#{qso.id}")
|
|
assert html =~ "Solar Conditions"
|
|
end
|
|
|
|
test "renders with sort assigns", %{conn: conn} do
|
|
qso = create_qso()
|
|
{:ok, lv, html} = live(conn, ~p"/qsos/#{qso.id}")
|
|
|
|
# Page renders without error
|
|
assert html =~ "Surface Observations"
|
|
assert html =~ "Soundings"
|
|
|
|
# Sort assigns are initialized (no crash on render)
|
|
assert render(lv) =~ qso.station1
|
|
end
|
|
|
|
test "raises for bad UUID", %{conn: conn} do
|
|
assert_raise Ecto.NoResultsError, fn ->
|
|
live(conn, ~p"/qsos/#{Ecto.UUID.generate()}")
|
|
end
|
|
end
|
|
end
|
|
end
|