317 lines
9.8 KiB
Elixir
317 lines
9.8 KiB
Elixir
defmodule Aprsme.DataExtendedTest do
|
|
use Aprsme.DataCase, async: true
|
|
|
|
alias Aprsme.DataExtended
|
|
|
|
describe "changeset/2" do
|
|
test "valid changeset with all required fields" do
|
|
attrs = %{
|
|
aprs_messaging: true,
|
|
comment: "Test comment",
|
|
data_type: "position",
|
|
symbol_code: "/",
|
|
symbol_table_id: "/"
|
|
}
|
|
|
|
changeset = DataExtended.changeset(%DataExtended{}, attrs)
|
|
|
|
assert changeset.valid?
|
|
assert get_change(changeset, :aprs_messaging) == true
|
|
assert get_change(changeset, :comment) == "Test comment"
|
|
assert get_change(changeset, :data_type) == "position"
|
|
assert get_change(changeset, :symbol_code) == "/"
|
|
assert get_change(changeset, :symbol_table_id) == "/"
|
|
end
|
|
|
|
test "valid changeset with coordinates" do
|
|
attrs = %{
|
|
aprs_messaging: false,
|
|
comment: "Position report",
|
|
data_type: "position",
|
|
latitude: Decimal.new("40.123456"),
|
|
longitude: Decimal.new("-74.654321"),
|
|
symbol_code: "j",
|
|
symbol_table_id: "/"
|
|
}
|
|
|
|
changeset = DataExtended.changeset(%DataExtended{}, attrs)
|
|
|
|
assert changeset.valid?
|
|
assert get_change(changeset, :latitude) == Decimal.new("40.123456")
|
|
assert get_change(changeset, :longitude) == Decimal.new("-74.654321")
|
|
end
|
|
|
|
test "valid changeset with string coordinates gets converted to decimal" do
|
|
attrs = %{
|
|
aprs_messaging: false,
|
|
comment: "Position report",
|
|
data_type: "position",
|
|
latitude: "40.123456",
|
|
longitude: "-74.654321",
|
|
symbol_code: "j",
|
|
symbol_table_id: "/"
|
|
}
|
|
|
|
changeset = DataExtended.changeset(%DataExtended{}, attrs)
|
|
|
|
assert changeset.valid?
|
|
assert get_change(changeset, :latitude) == Decimal.new("40.123456")
|
|
assert get_change(changeset, :longitude) == Decimal.new("-74.654321")
|
|
end
|
|
|
|
test "valid changeset with only required fields (no coordinates)" do
|
|
attrs = %{
|
|
aprs_messaging: false,
|
|
comment: "Status update",
|
|
data_type: "status",
|
|
symbol_code: ">",
|
|
symbol_table_id: "/"
|
|
}
|
|
|
|
changeset = DataExtended.changeset(%DataExtended{}, attrs)
|
|
|
|
assert changeset.valid?
|
|
refute get_change(changeset, :latitude)
|
|
refute get_change(changeset, :longitude)
|
|
end
|
|
|
|
test "invalid changeset missing required fields" do
|
|
attrs = %{}
|
|
|
|
changeset = DataExtended.changeset(%DataExtended{}, attrs)
|
|
|
|
refute changeset.valid?
|
|
errors = errors_on(changeset)
|
|
assert "can't be blank" in errors[:comment]
|
|
assert "can't be blank" in errors[:data_type]
|
|
assert "can't be blank" in errors[:symbol_code]
|
|
assert "can't be blank" in errors[:symbol_table_id]
|
|
end
|
|
|
|
test "invalid changeset with empty required fields" do
|
|
attrs = %{
|
|
aprs_messaging: nil,
|
|
comment: "",
|
|
data_type: "",
|
|
symbol_code: "",
|
|
symbol_table_id: ""
|
|
}
|
|
|
|
changeset = DataExtended.changeset(%DataExtended{}, attrs)
|
|
|
|
refute changeset.valid?
|
|
errors = errors_on(changeset)
|
|
assert "can't be blank" in errors[:comment]
|
|
assert "can't be blank" in errors[:data_type]
|
|
assert "can't be blank" in errors[:symbol_code]
|
|
assert "can't be blank" in errors[:symbol_table_id]
|
|
end
|
|
|
|
test "changeset with only latitude provided (should be invalid)" do
|
|
attrs = %{
|
|
aprs_messaging: false,
|
|
comment: "Partial position",
|
|
data_type: "position",
|
|
latitude: "40.123456",
|
|
symbol_code: "j",
|
|
symbol_table_id: "/"
|
|
}
|
|
|
|
changeset = DataExtended.changeset(%DataExtended{}, attrs)
|
|
|
|
assert changeset.valid?
|
|
# When only latitude is provided, it should still be valid
|
|
# The validation logic only requires a field if it's present in changes
|
|
assert get_change(changeset, :latitude) == Decimal.new("40.123456")
|
|
refute get_change(changeset, :longitude)
|
|
end
|
|
|
|
test "changeset with only longitude provided (should be invalid)" do
|
|
attrs = %{
|
|
aprs_messaging: false,
|
|
comment: "Partial position",
|
|
data_type: "position",
|
|
longitude: "-74.654321",
|
|
symbol_code: "j",
|
|
symbol_table_id: "/"
|
|
}
|
|
|
|
changeset = DataExtended.changeset(%DataExtended{}, attrs)
|
|
|
|
assert changeset.valid?
|
|
# When only longitude is provided, it should still be valid
|
|
assert get_change(changeset, :longitude) == Decimal.new("-74.654321")
|
|
refute get_change(changeset, :latitude)
|
|
end
|
|
|
|
test "changeset handles boolean aprs_messaging properly" do
|
|
# Test true
|
|
attrs_true = %{
|
|
aprs_messaging: true,
|
|
comment: "Message enabled",
|
|
data_type: "message",
|
|
symbol_code: ">",
|
|
symbol_table_id: "/"
|
|
}
|
|
|
|
changeset_true = DataExtended.changeset(%DataExtended{}, attrs_true)
|
|
assert changeset_true.valid?
|
|
assert get_change(changeset_true, :aprs_messaging) == true
|
|
|
|
# Test false
|
|
attrs_false = %{
|
|
aprs_messaging: false,
|
|
comment: "Message disabled",
|
|
data_type: "status",
|
|
symbol_code: ">",
|
|
symbol_table_id: "/"
|
|
}
|
|
|
|
changeset_false = DataExtended.changeset(%DataExtended{}, attrs_false)
|
|
assert changeset_false.valid?
|
|
assert get_change(changeset_false, :aprs_messaging) == false || changeset_false.data.aprs_messaging == false
|
|
end
|
|
|
|
test "changeset with various symbol codes and table IDs" do
|
|
test_cases = [
|
|
{"/", "/", "Primary table, house symbol"},
|
|
{"\\", "j", "Alternate table, jeep symbol"},
|
|
{">", "/", "Primary table, car symbol"},
|
|
{"[", "\\", "Alternate table, human symbol"}
|
|
]
|
|
|
|
for {symbol_code, symbol_table_id, description} <- test_cases do
|
|
attrs = %{
|
|
aprs_messaging: false,
|
|
comment: description,
|
|
data_type: "position",
|
|
symbol_code: symbol_code,
|
|
symbol_table_id: symbol_table_id
|
|
}
|
|
|
|
changeset = DataExtended.changeset(%DataExtended{}, attrs)
|
|
|
|
assert changeset.valid?, "Failed for #{description}"
|
|
assert get_change(changeset, :symbol_code) == symbol_code
|
|
assert get_change(changeset, :symbol_table_id) == symbol_table_id
|
|
end
|
|
end
|
|
|
|
test "changeset with various data types" do
|
|
data_types = ["position", "status", "message", "weather", "telemetry", "object", "item"]
|
|
|
|
for data_type <- data_types do
|
|
attrs = %{
|
|
aprs_messaging: false,
|
|
comment: "Testing #{data_type}",
|
|
data_type: data_type,
|
|
symbol_code: ">",
|
|
symbol_table_id: "/"
|
|
}
|
|
|
|
changeset = DataExtended.changeset(%DataExtended{}, attrs)
|
|
|
|
assert changeset.valid?, "Failed for data_type: #{data_type}"
|
|
assert get_change(changeset, :data_type) == data_type
|
|
end
|
|
end
|
|
|
|
test "changeset with long comment" do
|
|
long_comment = String.duplicate("A", 1000)
|
|
|
|
attrs = %{
|
|
aprs_messaging: false,
|
|
comment: long_comment,
|
|
data_type: "status",
|
|
symbol_code: ">",
|
|
symbol_table_id: "/"
|
|
}
|
|
|
|
changeset = DataExtended.changeset(%DataExtended{}, attrs)
|
|
|
|
assert changeset.valid?
|
|
assert get_change(changeset, :comment) == long_comment
|
|
end
|
|
|
|
test "changeset preserves existing data when updating" do
|
|
existing_data = %DataExtended{
|
|
aprs_messaging: true,
|
|
comment: "Original comment",
|
|
data_type: "position",
|
|
latitude: Decimal.new("30.0"),
|
|
longitude: Decimal.new("-90.0"),
|
|
symbol_code: "/",
|
|
symbol_table_id: "/"
|
|
}
|
|
|
|
update_attrs = %{
|
|
comment: "Updated comment",
|
|
latitude: "31.0"
|
|
}
|
|
|
|
changeset = DataExtended.changeset(existing_data, update_attrs)
|
|
|
|
assert changeset.valid?
|
|
assert get_change(changeset, :comment) == "Updated comment"
|
|
assert get_change(changeset, :latitude) == Decimal.new("31.0")
|
|
# Other fields should remain unchanged
|
|
assert changeset.data.aprs_messaging == true
|
|
assert changeset.data.data_type == "position"
|
|
end
|
|
end
|
|
|
|
describe "validate_required_if_present/2" do
|
|
test "validates field when it's present in changes" do
|
|
attrs = %{
|
|
aprs_messaging: false,
|
|
comment: "Test",
|
|
data_type: "position",
|
|
# Explicitly setting to nil should trigger validation
|
|
latitude: nil,
|
|
symbol_code: ">",
|
|
symbol_table_id: "/"
|
|
}
|
|
|
|
changeset = DataExtended.changeset(%DataExtended{}, attrs)
|
|
|
|
# This test verifies the private function behavior indirectly
|
|
# If latitude is explicitly set to nil, it should still be valid
|
|
# because the validation logic only requires non-nil values
|
|
assert changeset.valid?
|
|
end
|
|
end
|
|
|
|
describe "struct creation and defaults" do
|
|
test "creates struct with default values" do
|
|
data_extended = %DataExtended{}
|
|
|
|
assert data_extended.aprs_messaging == false
|
|
assert is_nil(data_extended.comment)
|
|
assert is_nil(data_extended.data_type)
|
|
assert is_nil(data_extended.latitude)
|
|
assert is_nil(data_extended.longitude)
|
|
assert is_nil(data_extended.symbol_code)
|
|
assert is_nil(data_extended.symbol_table_id)
|
|
end
|
|
|
|
test "creates struct with explicit values" do
|
|
data_extended = %DataExtended{
|
|
aprs_messaging: true,
|
|
comment: "Test comment",
|
|
data_type: "position",
|
|
latitude: Decimal.new("40.0"),
|
|
longitude: Decimal.new("-74.0"),
|
|
symbol_code: "j",
|
|
symbol_table_id: "/"
|
|
}
|
|
|
|
assert data_extended.aprs_messaging == true
|
|
assert data_extended.comment == "Test comment"
|
|
assert data_extended.data_type == "position"
|
|
assert data_extended.latitude == Decimal.new("40.0")
|
|
assert data_extended.longitude == Decimal.new("-74.0")
|
|
assert data_extended.symbol_code == "j"
|
|
assert data_extended.symbol_table_id == "/"
|
|
end
|
|
end
|
|
end
|