prop/test/microwaveprop/radio/contact_submission_test.exs
Graham McIntire 36adf875b7
Add 50/144/222 MHz bands, rename 440→432
Expands submittable-contact bands to include 6m, 2m, 1.25m, and 70cm
(as 432 rather than the old 440 placeholder). Each new band gets an
explicit allocation window in BandResolver.nearest_band so ADIF FREQ
fields near the amateur allocations resolve correctly while 60-900 MHz
frequencies outside those windows are still rejected. Microwave
(>= 900 MHz) snapping is unchanged — nearest-band match across the
full @allowed_bands list.

Also adds BandConfig entries for 50 and 222 (tropo-only config, same
pattern as 144/432). Sporadic-E / F2 / meteor scatter modeling is not
yet in scope — ionosphere data is only used to compute an Es readout
on /path for 50/144/222/432.
2026-04-16 12:36:10 -05:00

140 lines
4.8 KiB
Elixir

defmodule Microwaveprop.Radio.ContactSubmissionTest do
use Microwaveprop.DataCase, async: true
alias Microwaveprop.Radio.Contact
@valid_attrs %{
station1: "W5XD",
station2: "K5TR",
qso_timestamp: ~U[2026-03-28 18:00:00Z],
mode: "CW",
band: "1296",
grid1: "EM12",
grid2: "EM00",
submitter_email: "test@example.com"
}
describe "submission_changeset/2" do
test "valid attrs produce a valid changeset" do
changeset = Contact.submission_changeset(%Contact{}, @valid_attrs)
assert changeset.valid?
end
test "requires station1, station2, qso_timestamp, band, grid1, grid2, submitter_email" do
changeset = Contact.submission_changeset(%Contact{}, %{})
errors = errors_on(changeset)
assert errors[:station1]
assert errors[:station2]
assert errors[:qso_timestamp]
assert errors[:band]
assert errors[:grid1]
assert errors[:grid2]
assert errors[:submitter_email]
# Mode is optional on submission
refute errors[:mode]
end
test "accepts a blank mode and normalises it to nil" do
for blank <- ["", " ", nil] do
attrs = Map.put(@valid_attrs, :mode, blank)
changeset = Contact.submission_changeset(%Contact{}, attrs)
assert changeset.valid?, "Expected blank mode #{inspect(blank)} to validate"
assert Ecto.Changeset.get_field(changeset, :mode) == nil
end
end
test "rejects invalid grid1" do
attrs = Map.put(@valid_attrs, :grid1, "ZZ99")
changeset = Contact.submission_changeset(%Contact{}, attrs)
assert "is not a valid Maidenhead grid square" in errors_on(changeset).grid1
end
test "rejects invalid grid2" do
attrs = Map.put(@valid_attrs, :grid2, "ZZ99")
changeset = Contact.submission_changeset(%Contact{}, attrs)
assert "is not a valid Maidenhead grid square" in errors_on(changeset).grid2
end
test "accepts valid 6-char grids" do
attrs = %{@valid_attrs | grid1: "EM12ab", grid2: "EM00cd"}
changeset = Contact.submission_changeset(%Contact{}, attrs)
assert changeset.valid?
end
test "rejects invalid mode" do
attrs = Map.put(@valid_attrs, :mode, "RTTY")
changeset = Contact.submission_changeset(%Contact{}, attrs)
assert errors_on(changeset).mode
end
test "accepts valid modes" do
for mode <- ~w(CW SSB FM FT8 FT4 Q65) do
attrs = Map.put(@valid_attrs, :mode, mode)
changeset = Contact.submission_changeset(%Contact{}, attrs)
assert changeset.valid?, "Expected mode #{mode} to be valid"
end
end
test "rejects invalid band" do
attrs = Map.put(@valid_attrs, :band, "28")
changeset = Contact.submission_changeset(%Contact{}, attrs)
assert errors_on(changeset).band
end
test "accepts valid bands" do
for band <- ~w(50 144 222 432 902 1296 2304 3456 5760 10000 24000 47000 75000) do
attrs = Map.put(@valid_attrs, :band, band)
changeset = Contact.submission_changeset(%Contact{}, attrs)
assert changeset.valid?, "Expected band #{band} to be valid"
end
end
test "rejects email without @" do
attrs = Map.put(@valid_attrs, :submitter_email, "notanemail")
changeset = Contact.submission_changeset(%Contact{}, attrs)
assert errors_on(changeset).submitter_email
end
test "does not cast user_submitted" do
attrs = Map.put(@valid_attrs, :user_submitted, true)
changeset = Contact.submission_changeset(%Contact{}, attrs)
refute Ecto.Changeset.get_change(changeset, :user_submitted)
end
end
describe "create_contact/1 deduplication" do
alias Microwaveprop.Radio
test "rejects duplicate and returns the existing contact" do
assert {:ok, original} = Radio.create_contact(@valid_attrs)
assert {:error, :duplicate, ^original} = Radio.create_contact(@valid_attrs)
end
test "allows same stations on a different band" do
assert {:ok, _} = Radio.create_contact(@valid_attrs)
assert {:ok, _} = Radio.create_contact(%{@valid_attrs | band: "2304"})
end
test "allows same stations with different grids" do
assert {:ok, _} = Radio.create_contact(@valid_attrs)
assert {:ok, _} = Radio.create_contact(%{@valid_attrs | grid1: "EM13"})
end
test "allows contact outside the 1-hour window" do
assert {:ok, _} = Radio.create_contact(@valid_attrs)
later = DateTime.add(@valid_attrs.qso_timestamp, 3601, :second)
assert {:ok, _} = Radio.create_contact(%{@valid_attrs | qso_timestamp: later})
end
test "detects duplicates regardless of station order" do
assert {:ok, original} = Radio.create_contact(@valid_attrs)
swapped = %{@valid_attrs | station1: "K5TR", grid1: "EM00", station2: "W5XD", grid2: "EM12"}
assert {:error, :duplicate, ^original} = Radio.create_contact(swapped)
end
end
end