defmodule MicrowavepropWeb.ContactLiveTest do use MicrowavepropWeb.ConnCase, async: true import Microwaveprop.AccountsFixtures import Phoenix.LiveViewTest alias Microwaveprop.Radio.Contact alias Microwaveprop.Repo setup do Req.Test.stub(Microwaveprop.Terrain.ElevationClient, fn conn -> Req.Test.json(conn, []) end) Req.Test.stub(Microwaveprop.Weather.HrrrClient, fn conn -> Plug.Conn.send_resp(conn, 404, "not found") end) Req.Test.stub(Microwaveprop.Weather.SolarClient, fn conn -> Req.Test.text(conn, "") end) :ok end defp create_contact(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") } {changeset_attrs, direct_attrs} = Map.split(Map.merge(default, attrs), [:user_submitted]) {:ok, contact} = %Contact{} |> Contact.changeset(direct_attrs) |> Ecto.Changeset.change(changeset_attrs) |> Repo.insert() contact end describe "Index" do test "renders page with Contacts heading", %{conn: conn} do {:ok, _lv, html} = live(conn, ~p"/contacts") assert html =~ "Contacts" end test "table shows contact data", %{conn: conn} do create_contact() {:ok, _lv, html} = live(conn, ~p"/contacts") assert html =~ "W5XD" assert html =~ "K5TR" assert html =~ "CW" assert html =~ "1296" end test "row links to detail page", %{conn: conn} do contact = create_contact() {:ok, _lv, html} = live(conn, ~p"/contacts") assert html =~ ~p"/contacts/#{contact.id}" end test "sort params order the table", %{conn: conn} do create_contact(%{station1: "ZZ9ZZ"}) create_contact(%{station1: "AA1AA"}) {:ok, _lv, html} = live(conn, ~p"/contacts?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 "search by single callsign matches either station", %{conn: conn} do create_contact(%{station1: "W5LUA", station2: "W5HN"}) create_contact(%{station1: "K5TR", station2: "N5AC"}) {:ok, _lv, html} = live(conn, ~p"/contacts?search=W5LUA") assert html =~ "W5LUA" refute html =~ "K5TR" end test "search by two callsigns finds contacts between that pair", %{conn: conn} do create_contact(%{station1: "W5LUA", station2: "W5HN"}) create_contact(%{station1: "W5HN", station2: "W5LUA"}) create_contact(%{station1: "W5LUA", station2: "K5TR"}) {:ok, _lv, html} = live(conn, ~p"/contacts?search=W5LUA+W5HN") assert html =~ "W5LUA" assert html =~ "W5HN" # The W5LUA-K5TR contact should not appear refute html =~ "K5TR" 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_contact(%{qso_timestamp: ts}) end {:ok, _lv, html} = live(conn, ~p"/contacts") assert html =~ "Page 1 of 2" {:ok, _lv, html2} = live(conn, ~p"/contacts?page=2") assert html2 =~ "Page 2 of 2" end test "Submitted column shows the user's callsign when linked to an account", %{conn: conn} do user = user_fixture(%{callsign: "W5UNIQ"}) create_contact(%{station1: "K5ONE", user_submitted: true, user_id: user.id}) {:ok, _lv, html} = live(conn, ~p"/contacts?search=K5ONE") assert html =~ "W5UNIQ" end test "Submitted column shows 'Yes' when user_submitted but no user linked", %{conn: conn} do create_contact(%{station1: "K5ANON", user_submitted: true, user_id: nil}) {:ok, _lv, html} = live(conn, ~p"/contacts?search=K5ANON") # Locate the row containing our station and assert "Yes" appears in it [row] = Regex.run(~r/