defmodule Microwaveprop.QrzTest do use Microwaveprop.DataCase, async: false alias Microwaveprop.Qrz alias Microwaveprop.Qrz.Callsign alias Microwaveprop.Qrz.Client alias Microwaveprop.Qrz.Record setup do Client.reset_session() :ok end defp stub_login_and_lookup(callsign_xml) do Req.Test.expect(Client, 2, fn conn -> params = Plug.Conn.fetch_query_params(conn).query_params if Map.has_key?(params, "username") do Plug.Conn.send_resp(conn, 200, """ session123 """) else Plug.Conn.send_resp(conn, 200, """ #{callsign_xml} session123 """) end end) end defp w1aw_xml do """ W1AW Hiram Percy Maxim FN31pr """ end describe "lookup_callsign/1" do test "fetches from API when not cached, stores in DB" do stub_login_and_lookup(w1aw_xml()) assert {:ok, %Record{} = record} = Qrz.lookup_callsign("W1AW") assert record.call == "W1AW" assert record.fname == "Hiram Percy" assert record.grid == "FN31pr" assert Repo.get_by(Callsign, callsign: "W1AW") end test "returns cached data when within TTL" do {:ok, _} = %Callsign{} |> Callsign.changeset(%{callsign: "W1AW", data: %{"call" => "W1AW", "fname" => "Cached"}}) |> Repo.insert() # Test config forces cache_ttl_hours: 0 so entries are always stale. # Override for this test to make the cache fresh. original = Application.get_env(:microwaveprop, Qrz) Application.put_env(:microwaveprop, Qrz, Keyword.put(original, :cache_ttl_hours, 24)) on_exit(fn -> Application.put_env(:microwaveprop, Qrz, original) end) assert {:ok, %Record{} = record} = Qrz.lookup_callsign("W1AW") assert record.fname == "Cached" end test "re-fetches when cache entry is stale" do past = DateTime.utc_now() |> DateTime.add(-48, :hour) |> DateTime.truncate(:second) Repo.insert!(%Callsign{ callsign: "W1AW", data: %{"call" => "W1AW", "fname" => "Old"}, inserted_at: past, updated_at: past }) stub_login_and_lookup(w1aw_xml()) assert {:ok, %Record{} = record} = Qrz.lookup_callsign("W1AW") assert record.fname == "Hiram Percy" end test "upserts (updates existing record on re-fetch)" do past = DateTime.utc_now() |> DateTime.add(-48, :hour) |> DateTime.truncate(:second) original = Repo.insert!(%Callsign{ callsign: "W1AW", data: %{"call" => "W1AW", "fname" => "Old"}, inserted_at: past, updated_at: past }) stub_login_and_lookup(w1aw_xml()) assert {:ok, %Record{} = record} = Qrz.lookup_callsign("W1AW") assert record.fname == "Hiram Percy" assert Repo.get!(Callsign, original.id).data["fname"] == "Hiram Percy" end test "returns {:error, message} when API fails" do Req.Test.expect(Client, 2, fn conn -> params = Plug.Conn.fetch_query_params(conn).query_params if Map.has_key?(params, "username") do Plug.Conn.send_resp(conn, 200, """ session123 """) else Plug.Conn.send_resp(conn, 200, """ Not found: INVALIDCALL session123 """) end end) assert {:error, "Not found: INVALIDCALL"} = Qrz.lookup_callsign("INVALIDCALL") end test "normalizes callsign to uppercase" do stub_login_and_lookup(w1aw_xml()) assert {:ok, %Record{call: "W1AW"}} = Qrz.lookup_callsign("w1aw") end end describe "get_callsign/1" do test "returns nil for unknown callsign" do assert Qrz.get_callsign("NONEXISTENT") == nil end test "returns record regardless of TTL" do past = DateTime.utc_now() |> DateTime.add(-999, :hour) |> DateTime.truncate(:second) Repo.insert!(%Callsign{ callsign: "W1AW", data: %{"call" => "W1AW"}, inserted_at: past, updated_at: past }) assert %Callsign{callsign: "W1AW"} = Qrz.get_callsign("W1AW") end end end