- Fix leader election guard test for :undefined pid - Fix cpu_sup.util() pattern matching to handle numeric return - Fix encoding_utils to always return explicit nil when needed - Remove unused finite_float? function - Fix signal handler to match :ok return from :os.set_signal/2 - Remove redundant catch-all pattern in database metrics - Fix Gridsquare pattern matching in info_live template - Remove unnecessary nil check for calculate_course result - Fix get_packet_received_at to not check for nil (always returns DateTime) - Remove redundant catch-all pattern in weather format_weather_value - Fix query builder to use from(p in Packet) instead of bare Packet atom Reduced dialyzer errors from 49 to 6. Remaining warnings are mostly false positives from template compilation and overloaded function specs. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
296 lines
8.8 KiB
Elixir
296 lines
8.8 KiB
Elixir
defmodule Aprsme.GeometryTest do
|
|
use Aprsme.DataCase
|
|
|
|
alias Aprsme.Packet
|
|
alias Aprsme.Packets
|
|
alias Geo.PostGIS.Geometry
|
|
|
|
describe "PostGIS geometry functionality" do
|
|
test "create_point/2 creates valid Point geometry" do
|
|
lat = 32.7767
|
|
lon = -96.7970
|
|
|
|
point = Packet.create_point(lat, lon)
|
|
|
|
assert %Geo.Point{} = point
|
|
assert point.coordinates == {lon, lat}
|
|
assert point.srid == 4326
|
|
end
|
|
|
|
test "create_point/2 returns nil for invalid coordinates" do
|
|
# Test invalid latitude
|
|
assert Packet.create_point(91.0, -96.7970) == nil
|
|
assert Packet.create_point(-91.0, -96.7970) == nil
|
|
|
|
# Test invalid longitude
|
|
assert Packet.create_point(32.7767, 181.0) == nil
|
|
assert Packet.create_point(32.7767, -181.0) == nil
|
|
|
|
# Test nil values
|
|
assert Packet.create_point(nil, -96.7970) == nil
|
|
assert Packet.create_point(32.7767, nil) == nil
|
|
end
|
|
|
|
test "extract_coordinates/1 extracts lat/lon from Point geometry" do
|
|
point = %Geo.Point{coordinates: {-96.7970, 32.7767}, srid: 4326}
|
|
|
|
{lat, lon} = Packet.extract_coordinates(point)
|
|
|
|
assert lat == 32.7767
|
|
assert lon == -96.7970
|
|
end
|
|
|
|
test "extract_coordinates/1 returns {nil, nil} for invalid geometry" do
|
|
assert Packet.extract_coordinates(nil) == {nil, nil}
|
|
assert Packet.extract_coordinates("invalid") == {nil, nil}
|
|
end
|
|
|
|
test "packet changeset creates geometry from lat/lon" do
|
|
attrs = %{
|
|
base_callsign: "TEST",
|
|
data_type: "position",
|
|
destination: "APRS",
|
|
information_field: "test",
|
|
path: "WIDE1-1",
|
|
sender: "TEST-1",
|
|
ssid: "1",
|
|
received_at: DateTime.utc_now(),
|
|
lat: 32.7767,
|
|
lon: -96.7970
|
|
}
|
|
|
|
changeset = Packet.changeset(%Packet{}, attrs)
|
|
|
|
assert changeset.valid?
|
|
assert get_change(changeset, :location)
|
|
assert get_change(changeset, :has_position) == true
|
|
|
|
location = get_change(changeset, :location)
|
|
assert %Geo.Point{} = location
|
|
assert location.coordinates == {-96.7970, 32.7767}
|
|
end
|
|
|
|
test "packet changeset handles invalid coordinates gracefully" do
|
|
attrs = %{
|
|
base_callsign: "TEST",
|
|
data_type: "position",
|
|
destination: "APRS",
|
|
information_field: "test",
|
|
path: "WIDE1-1",
|
|
sender: "TEST-1",
|
|
ssid: "1",
|
|
received_at: DateTime.utc_now(),
|
|
# Invalid latitude
|
|
lat: 91.0,
|
|
lon: -96.7970
|
|
}
|
|
|
|
changeset = Packet.changeset(%Packet{}, attrs)
|
|
# Should still be valid, just without location
|
|
assert changeset.valid?
|
|
assert get_change(changeset, :location) == nil
|
|
assert get_change(changeset, :has_position) != true
|
|
end
|
|
|
|
test "store_packet/1 successfully stores packet with geometry" do
|
|
attrs = %{
|
|
base_callsign: "TEST",
|
|
data_type: "position",
|
|
destination: "APRS",
|
|
information_field: "!3216.46N/09647.82W>Test packet",
|
|
path: "WIDE1-1,WIDE2-1",
|
|
sender: "TEST-1",
|
|
ssid: "1",
|
|
received_at: DateTime.utc_now(),
|
|
lat: 32.2743,
|
|
lon: -96.7970,
|
|
has_position: true,
|
|
region: "32.3,-96.8",
|
|
raw_packet: "TEST-1>APRS,WIDE1-1,WIDE2-1:!3216.46N/09647.82W>Test packet"
|
|
}
|
|
|
|
assert {:ok, packet} = Packets.store_packet(attrs)
|
|
assert packet.sender == "TEST-1"
|
|
assert packet.has_position == true
|
|
assert packet.location
|
|
assert %Geo.Point{} = packet.location
|
|
assert_in_delta elem(packet.location.coordinates, 1), 32.2743, 0.0001
|
|
assert_in_delta elem(packet.location.coordinates, 0), -96.7970, 0.0001
|
|
end
|
|
|
|
test "store_packet/1 handles packet without position data" do
|
|
attrs = %{
|
|
base_callsign: "TEST",
|
|
data_type: "message",
|
|
destination: "APRS",
|
|
information_field: ":TEST-2 :Hello world",
|
|
path: "WIDE1-1",
|
|
sender: "TEST-1",
|
|
ssid: "1",
|
|
received_at: DateTime.utc_now(),
|
|
raw_packet: "TEST-1>APRS,WIDE1-1::TEST-2 :Hello world"
|
|
}
|
|
|
|
assert {:ok, packet} = Packets.store_packet(attrs)
|
|
assert packet.sender == "TEST-1"
|
|
assert packet.has_position == false
|
|
assert packet.location == nil
|
|
end
|
|
|
|
test "store_packet/1 handles coordinate validation errors gracefully" do
|
|
attrs = %{
|
|
base_callsign: "TEST",
|
|
data_type: "position",
|
|
destination: "APRS",
|
|
information_field: "test",
|
|
path: "WIDE1-1",
|
|
sender: "TEST-1",
|
|
ssid: "1",
|
|
received_at: DateTime.utc_now(),
|
|
# Invalid latitude
|
|
lat: 200.0,
|
|
lon: -96.7970,
|
|
raw_packet: "TEST-1>APRS,WIDE1-1:test"
|
|
}
|
|
|
|
# Should not crash, but should handle gracefully
|
|
assert {:ok, packet} = Packets.store_packet(attrs)
|
|
assert packet.sender == "TEST-1"
|
|
assert packet.has_position == false
|
|
assert packet.location == nil
|
|
end
|
|
|
|
test "Geo.PostGIS.Geometry cast works with Point" do
|
|
point = %Geo.Point{coordinates: {-96.7970, 32.7767}, srid: 4326}
|
|
|
|
assert {:ok, ^point} = Geometry.cast(point)
|
|
end
|
|
|
|
test "Geo.PostGIS.Geometry cast works with coordinate tuple" do
|
|
coords = {-96.7970, 32.7767}
|
|
|
|
# PostGIS geometry casting may not support tuples directly
|
|
# Instead test creating a point manually
|
|
point = %Geo.Point{coordinates: coords, srid: 4326}
|
|
assert {:ok, ^point} = Geometry.cast(point)
|
|
end
|
|
|
|
test "packet lat/lon helper functions work correctly" do
|
|
packet = %Packet{
|
|
location: %Geo.Point{coordinates: {-96.7970, 32.7767}, srid: 4326}
|
|
}
|
|
|
|
assert Packet.lat(packet) == 32.7767
|
|
assert Packet.lon(packet) == -96.7970
|
|
end
|
|
|
|
test "packet lat/lon helpers return nil for packets without location" do
|
|
packet = %Packet{location: nil}
|
|
|
|
assert Packet.lat(packet) == nil
|
|
assert Packet.lon(packet) == nil
|
|
end
|
|
end
|
|
|
|
describe "coordinate conversion and validation" do
|
|
test "handles various coordinate formats" do
|
|
# Test float coordinates
|
|
assert Packet.create_point(32.7767, -96.7970)
|
|
|
|
# Test integer coordinates
|
|
assert Packet.create_point(33, -97)
|
|
|
|
# Test boundary values
|
|
assert Packet.create_point(90.0, 180.0)
|
|
assert Packet.create_point(-90.0, -180.0)
|
|
|
|
# Test just inside boundaries
|
|
assert Packet.create_point(89.9999, 179.9999)
|
|
assert Packet.create_point(-89.9999, -179.9999)
|
|
end
|
|
|
|
test "rejects coordinates outside valid ranges" do
|
|
# Test latitude boundaries
|
|
assert Packet.create_point(90.0001, 0.0) == nil
|
|
assert Packet.create_point(-90.0001, 0.0) == nil
|
|
|
|
# Test longitude boundaries
|
|
assert Packet.create_point(0.0, 180.0001) == nil
|
|
assert Packet.create_point(0.0, -180.0001) == nil
|
|
end
|
|
|
|
test "handles edge case coordinates" do
|
|
# Test zero coordinates
|
|
assert Packet.create_point(0.0, 0.0)
|
|
|
|
# Test very small coordinates
|
|
assert Packet.create_point(0.000001, 0.000001)
|
|
|
|
# Test coordinates near boundaries
|
|
assert Packet.create_point(89.999999, 179.999999)
|
|
end
|
|
end
|
|
|
|
describe "packet storage with different position formats" do
|
|
test "stores packet with MicE-like data structure" do
|
|
# Test packet storage that would involve MicE conversion
|
|
attrs = %{
|
|
base_callsign: "TEST",
|
|
data_type: "position",
|
|
destination: "APRS",
|
|
information_field: "test",
|
|
path: "WIDE1-1",
|
|
sender: "TEST-1",
|
|
ssid: "1",
|
|
received_at: DateTime.utc_now(),
|
|
# Pre-converted coordinates
|
|
lat: 32.7767,
|
|
lon: -96.7970,
|
|
raw_packet: "TEST-1>APRS,WIDE1-1:test"
|
|
}
|
|
|
|
assert {:ok, packet} = Packets.store_packet(attrs)
|
|
assert packet.sender == "TEST-1"
|
|
assert packet.has_position == true
|
|
assert packet.location
|
|
end
|
|
|
|
test "stores packet with standard position data" do
|
|
attrs = %{
|
|
base_callsign: "TEST",
|
|
data_type: "position",
|
|
destination: "APRS",
|
|
information_field: "test",
|
|
path: "WIDE1-1",
|
|
sender: "TEST-2",
|
|
ssid: "2",
|
|
received_at: DateTime.utc_now(),
|
|
lat: 32.7767,
|
|
lon: -96.7970,
|
|
raw_packet: "TEST-2>APRS,WIDE1-1:test"
|
|
}
|
|
|
|
assert {:ok, packet} = Packets.store_packet(attrs)
|
|
assert packet.has_position == true
|
|
assert %Geo.Point{} = packet.location
|
|
end
|
|
|
|
test "stores packet without position data" do
|
|
attrs = %{
|
|
base_callsign: "TEST",
|
|
data_type: "message",
|
|
destination: "APRS",
|
|
information_field: ":TEST-2 :Hello",
|
|
path: "WIDE1-1",
|
|
sender: "TEST-3",
|
|
ssid: "3",
|
|
received_at: DateTime.utc_now(),
|
|
raw_packet: "TEST-3>APRS,WIDE1-1::TEST-2 :Hello"
|
|
}
|
|
|
|
assert {:ok, packet} = Packets.store_packet(attrs)
|
|
assert packet.has_position == false
|
|
assert packet.location == nil
|
|
end
|
|
end
|
|
end
|