From b085c8fcdb40d341ad6a91efe12913e38ad88856 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Thu, 23 Apr 2026 17:36:31 -0500 Subject: [PATCH] test: expand QueryBuilder coverage with time range and callsign filters --- test/aprsme/packets/query_builder_test.exs | 137 +++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/test/aprsme/packets/query_builder_test.exs b/test/aprsme/packets/query_builder_test.exs index bc8d098..84d2228 100644 --- a/test/aprsme/packets/query_builder_test.exs +++ b/test/aprsme/packets/query_builder_test.exs @@ -98,4 +98,141 @@ defmodule Aprsme.Packets.QueryBuilderTest do assert Enum.all?(results, fn p -> p.has_weather == true end) end end + + describe "with_time_range/2" do + test "filters by start_time only" do + now = DateTime.utc_now() + + _old = + PacketsFixtures.packet_fixture(%{ + sender: "TR-OLD", + received_at: DateTime.add(now, -7200, :second) + }) + + _recent = + PacketsFixtures.packet_fixture(%{ + sender: "TR-RECENT", + received_at: DateTime.add(now, -60, :second) + }) + + start_time = DateTime.add(now, -3600, :second) + query = QueryBuilder.with_time_range(Packet, start_time: start_time) + results = Aprsme.Repo.all(query) + callsigns = Enum.map(results, & &1.sender) + + assert "TR-RECENT" in callsigns + refute "TR-OLD" in callsigns + end + + test "filters by end_time only" do + now = DateTime.utc_now() + + _future = + PacketsFixtures.packet_fixture(%{ + sender: "TR-FUT", + received_at: DateTime.add(now, 3600, :second) + }) + + _past = + PacketsFixtures.packet_fixture(%{ + sender: "TR-PAST", + received_at: DateTime.add(now, -60, :second) + }) + + query = QueryBuilder.with_time_range(Packet, end_time: now) + results = Aprsme.Repo.all(query) + callsigns = Enum.map(results, & &1.sender) + + assert "TR-PAST" in callsigns + refute "TR-FUT" in callsigns + end + + test "accepts options as keyword list or map" do + start_time = DateTime.add(DateTime.utc_now(), -3600, :second) + q1 = QueryBuilder.with_time_range(Packet, start_time: start_time) + q2 = QueryBuilder.with_time_range(Packet, %{start_time: start_time}) + # Both should produce semantically equivalent queries. + assert inspect(q1) == inspect(q2) + end + + test "accepts string keys" do + start_time = DateTime.add(DateTime.utc_now(), -3600, :second) + query = QueryBuilder.with_time_range(Packet, %{"start_time" => start_time}) + # Should run without raising. + assert is_list(Aprsme.Repo.all(query)) + end + end + + describe "with_position/1" do + test "filters to packets with has_position == true" do + _positioned = + PacketsFixtures.packet_fixture(%{ + sender: "POS-QBT", + has_position: true + }) + + query = QueryBuilder.with_position(Packet) + results = Aprsme.Repo.all(query) + + assert Enum.all?(results, fn p -> p.has_position == true end) + end + end + + describe "for_callsign/2 and for_base_callsign/2" do + test "matches callsign case-insensitively" do + _p = + PacketsFixtures.packet_fixture(%{ + sender: "FCS-1", + base_callsign: "FCS" + }) + + # Case-insensitive match via upper(). + query = QueryBuilder.for_callsign(Packet, "fcs-1") + results = Aprsme.Repo.all(query) + assert Enum.any?(results, &(&1.sender == "FCS-1")) + end + + test "filters by base callsign exactly" do + _p = + PacketsFixtures.packet_fixture(%{ + sender: "BASECS-9", + base_callsign: "BASECS" + }) + + query = QueryBuilder.for_base_callsign(Packet, "BASECS") + results = Aprsme.Repo.all(query) + assert Enum.any?(results, &(&1.base_callsign == "BASECS")) + end + end + + describe "ordering helpers" do + test "recent_first/1 orders newest first" do + now = DateTime.utc_now() + + _a = + PacketsFixtures.packet_fixture(%{ + sender: "ORDA-1", + received_at: DateTime.add(now, -600, :second) + }) + + _b = + PacketsFixtures.packet_fixture(%{ + sender: "ORDB-2", + received_at: now + }) + + query = + Packet + |> QueryBuilder.for_base_callsign("ORDB") + |> QueryBuilder.recent_first() + + assert is_list(Aprsme.Repo.all(query)) + end + + test "chronological/1 orders oldest first" do + query = QueryBuilder.chronological(Packet) + # Just verifies the query composes without raising. + assert is_list(query |> limit(10) |> Aprsme.Repo.all()) + end + end end