defmodule Microwaveprop.RadioRefinementTest do use Microwaveprop.DataCase, async: true alias Microwaveprop.Radio alias Microwaveprop.Radio.Contact alias Microwaveprop.Terrain.ElevationClient alias Microwaveprop.Weather.HrrrClient alias Microwaveprop.Weather.IemClient setup do Req.Test.stub(IemClient, fn conn -> case conn.request_path do "/cgi-bin/request/asos.py" -> Req.Test.text(conn, "#DEBUG,\nstation,valid,tmpf\n") _ -> Req.Test.json(conn, %{"profiles" => []}) end end) Req.Test.stub(HrrrClient, fn conn -> Plug.Conn.send_resp(conn, 404, "not found") end) Req.Test.stub(ElevationClient, fn conn -> params = Plug.Conn.fetch_query_params(conn).query_params lat_count = params["latitude"] |> String.split(",") |> length() Req.Test.json(conn, %{"elevation" => List.duplicate(200.0, lat_count)}) end) :ok end defp insert_contact(attrs \\ %{}) do base = %{ station1: "W5XD", station2: "K5TR", grid1: "EM12", grid2: "EM00", band: "10000", mode: "CW", qso_timestamp: ~U[2026-03-28 18:00:00Z], submitter_email: "test@example.com" } {:ok, contact} = Radio.create_contact(Map.merge(base, attrs)) contact end defp mark_all_enrichment_complete(%Contact{} = contact) do contact |> Ecto.Changeset.change(%{ hrrr_status: :complete, weather_status: :complete, terrain_status: :complete, iemre_status: :complete }) |> Repo.update!() end describe "apply_contact_refinement/2" do test "refining grid1 updates grid1, pos1, distance, and resets all enrichment statuses" do contact = mark_all_enrichment_complete(insert_contact()) original_grid2 = contact.grid2 original_pos2 = contact.pos2 assert {:ok, refined} = Radio.apply_contact_refinement(contact, %{grid1: "EM12KP"}) assert refined.grid1 == "EM12KP" assert refined.grid2 == original_grid2 assert refined.pos2 == original_pos2 refute refined.pos1 == contact.pos1 assert refined.distance_km assert refined.hrrr_status == :pending assert refined.weather_status == :pending assert refined.terrain_status == :pending assert refined.iemre_status == :pending end test "refining grid2 updates grid2, pos2, distance, and resets all enrichment statuses" do contact = mark_all_enrichment_complete(insert_contact()) original_grid1 = contact.grid1 original_pos1 = contact.pos1 assert {:ok, refined} = Radio.apply_contact_refinement(contact, %{grid2: "EM00CD"}) assert refined.grid1 == original_grid1 assert refined.pos1 == original_pos1 assert refined.grid2 == "EM00CD" refute refined.pos2 == contact.pos2 assert refined.hrrr_status == :pending assert refined.weather_status == :pending assert refined.terrain_status == :pending assert refined.iemre_status == :pending end test "refining both grids updates both + recomputes distance" do contact = mark_all_enrichment_complete(insert_contact()) assert {:ok, refined} = Radio.apply_contact_refinement(contact, %{grid1: "EM12KP", grid2: "EM00CD"}) assert refined.grid1 == "EM12KP" assert refined.grid2 == "EM00CD" assert refined.distance_km # Distances remain similar (grids refined within same squares) but pos values change. refute refined.pos1 == contact.pos1 refute refined.pos2 == contact.pos2 end test "refining only :mode updates mode without touching pos/distance or statuses" do contact = %{mode: nil} |> insert_contact() |> mark_all_enrichment_complete() original_pos1 = contact.pos1 original_pos2 = contact.pos2 original_distance = contact.distance_km assert {:ok, refined} = Radio.apply_contact_refinement(contact, %{mode: "CW"}) assert refined.mode == "CW" assert refined.pos1 == original_pos1 assert refined.pos2 == original_pos2 assert refined.distance_km == original_distance assert refined.hrrr_status == :complete assert refined.weather_status == :complete assert refined.terrain_status == :complete assert refined.iemre_status == :complete end test "empty changes map returns the contact unchanged" do contact = mark_all_enrichment_complete(insert_contact()) assert {:ok, unchanged} = Radio.apply_contact_refinement(contact, %{}) assert unchanged.id == contact.id assert unchanged.grid1 == contact.grid1 assert unchanged.grid2 == contact.grid2 assert unchanged.mode == contact.mode assert unchanged.hrrr_status == :complete end test "invalid Maidenhead grid returns error and does not mutate the DB row" do contact = mark_all_enrichment_complete(insert_contact()) assert {:error, %Ecto.Changeset{} = changeset} = Radio.apply_contact_refinement(contact, %{grid1: "ZZZZ"}) refute changeset.valid? reloaded = Repo.reload!(contact) assert reloaded.grid1 == contact.grid1 assert reloaded.hrrr_status == :complete end end end