mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-12 18:56:01 +00:00
597 lines
18 KiB
Elixir
597 lines
18 KiB
Elixir
defmodule WandererAppWeb.MapSystemSignatureAPIControllerTest do
|
|
use WandererAppWeb.ApiCase
|
|
|
|
alias WandererAppWeb.Factory
|
|
|
|
describe "GET /api/maps/:map_identifier/signatures" do
|
|
setup :setup_map_authentication
|
|
|
|
test "returns all signatures for a map", %{conn: conn, map: map} do
|
|
conn = get(conn, ~p"/api/maps/#{map.slug}/signatures")
|
|
|
|
assert %{"data" => data} = json_response(conn, 200)
|
|
assert is_list(data)
|
|
end
|
|
|
|
test "returns empty list when no signatures exist", %{conn: conn, map: map} do
|
|
conn = get(conn, ~p"/api/maps/#{map.slug}/signatures")
|
|
|
|
assert %{"data" => []} = json_response(conn, 200)
|
|
end
|
|
|
|
test "returns 401 without authentication" do
|
|
map = Factory.insert(:map)
|
|
|
|
conn = build_conn()
|
|
conn = get(conn, ~p"/api/maps/#{map.slug}/signatures")
|
|
|
|
assert json_response(conn, 401)
|
|
end
|
|
end
|
|
|
|
describe "GET /api/maps/:map_identifier/signatures/:id" do
|
|
setup :setup_map_authentication
|
|
|
|
test "returns signature when it exists and belongs to the map", %{conn: conn, map: map} do
|
|
# Create a system for the map
|
|
system = Factory.insert(:map_system, %{map_id: map.id, solar_system_id: 30_000_142})
|
|
|
|
# Create a signature for this system
|
|
signature =
|
|
Factory.insert(:map_system_signature, %{
|
|
system_id: system.id,
|
|
eve_id: "ABC-123",
|
|
character_eve_id: "123456789",
|
|
name: "Test Signature"
|
|
})
|
|
|
|
conn = get(conn, ~p"/api/maps/#{map.slug}/signatures/#{signature.id}")
|
|
|
|
assert %{"data" => data} = json_response(conn, 200)
|
|
assert data["id"] == signature.id
|
|
assert data["eve_id"] == "ABC-123"
|
|
assert data["name"] == "Test Signature"
|
|
end
|
|
|
|
test "returns 404 when signature exists but belongs to different map", %{conn: conn, map: map} do
|
|
# Create a different map and system
|
|
other_map = Factory.insert(:map)
|
|
|
|
other_system =
|
|
Factory.insert(:map_system, %{map_id: other_map.id, solar_system_id: 30_000_143})
|
|
|
|
signature = Factory.insert(:map_system_signature, %{system_id: other_system.id})
|
|
|
|
conn = get(conn, ~p"/api/maps/#{map.slug}/signatures/#{signature.id}")
|
|
|
|
assert %{"error" => error} = json_response(conn, 404)
|
|
assert error == "Signature not found"
|
|
end
|
|
|
|
test "returns 404 for non-existent signature", %{conn: conn, map: map} do
|
|
non_existent_id = Ecto.UUID.generate()
|
|
|
|
conn = get(conn, ~p"/api/maps/#{map.slug}/signatures/#{non_existent_id}")
|
|
|
|
assert %{"error" => error} = json_response(conn, 404)
|
|
assert error == "Signature not found"
|
|
end
|
|
|
|
test "returns error for invalid signature ID format", %{conn: conn, map: map} do
|
|
conn = get(conn, ~p"/api/maps/#{map.slug}/signatures/invalid-uuid")
|
|
|
|
# Should return 404 for malformed UUID
|
|
assert %{"error" => _error} = json_response(conn, 404)
|
|
end
|
|
|
|
test "returns 401 without authentication" do
|
|
map = Factory.insert(:map)
|
|
signature_id = Ecto.UUID.generate()
|
|
|
|
conn = build_conn()
|
|
conn = get(conn, ~p"/api/maps/#{map.slug}/signatures/#{signature_id}")
|
|
|
|
assert json_response(conn, 401)
|
|
end
|
|
end
|
|
|
|
describe "POST /api/maps/:map_identifier/signatures" do
|
|
setup :setup_map_authentication
|
|
|
|
test "creates a new signature with valid parameters", %{conn: conn, map: map} do
|
|
signature_params = %{
|
|
"solar_system_id" => 30_000_142,
|
|
"eve_id" => "ABC-123",
|
|
"character_eve_id" => "123456789",
|
|
"name" => "Test Signature",
|
|
"description" => "Test description",
|
|
"type" => "Wormhole",
|
|
"kind" => "cosmic_signature",
|
|
"group" => "wormhole",
|
|
"custom_info" => "Fresh"
|
|
}
|
|
|
|
conn = post(conn, ~p"/api/maps/#{map.slug}/signatures", signature_params)
|
|
|
|
# Should either create successfully or return an error
|
|
response =
|
|
case conn.status do
|
|
201 -> json_response(conn, 201)
|
|
422 -> json_response(conn, 422)
|
|
_ -> flunk("Unexpected status code: #{inspect(conn.status)}")
|
|
end
|
|
|
|
case response do
|
|
%{"data" => _data} ->
|
|
assert true
|
|
|
|
%{"error" => _error} ->
|
|
assert true
|
|
end
|
|
end
|
|
|
|
test "handles signature creation with minimal required fields", %{conn: conn, map: map} do
|
|
minimal_params = %{
|
|
"solar_system_id" => 30_000_143,
|
|
"eve_id" => "XYZ-456",
|
|
"character_eve_id" => "987654321"
|
|
}
|
|
|
|
conn = post(conn, ~p"/api/maps/#{map.slug}/signatures", minimal_params)
|
|
|
|
# Should handle minimal params
|
|
response =
|
|
case conn.status do
|
|
201 -> json_response(conn, 201)
|
|
422 -> json_response(conn, 422)
|
|
_ -> flunk("Unexpected status code: #{inspect(conn.status)}")
|
|
end
|
|
|
|
assert Map.has_key?(response, "data") or Map.has_key?(response, "error")
|
|
end
|
|
|
|
test "handles signature creation with all optional fields", %{conn: conn, map: map} do
|
|
complete_params = %{
|
|
"solar_system_id" => 30_000_144,
|
|
"eve_id" => "DEF-789",
|
|
"character_eve_id" => "456789123",
|
|
"name" => "Complete Signature",
|
|
"description" => "Complete description",
|
|
"type" => "Data Site",
|
|
"linked_system_id" => 30_000_142,
|
|
"kind" => "cosmic_signature",
|
|
"group" => "data",
|
|
"custom_info" => "High value",
|
|
"updated" => 1
|
|
}
|
|
|
|
conn = post(conn, ~p"/api/maps/#{map.slug}/signatures", complete_params)
|
|
|
|
response =
|
|
case conn.status do
|
|
201 -> json_response(conn, 201)
|
|
422 -> json_response(conn, 422)
|
|
_ -> flunk("Unexpected status code: #{inspect(conn.status)}")
|
|
end
|
|
|
|
assert Map.has_key?(response, "data") or Map.has_key?(response, "error")
|
|
end
|
|
|
|
test "returns 401 without authentication" do
|
|
map = Factory.insert(:map)
|
|
|
|
signature_params = %{
|
|
"solar_system_id" => 30_000_145,
|
|
"eve_id" => "ABC-123",
|
|
"character_eve_id" => "123456789"
|
|
}
|
|
|
|
conn = build_conn()
|
|
conn = post(conn, ~p"/api/maps/#{map.slug}/signatures", signature_params)
|
|
|
|
assert json_response(conn, 401)
|
|
end
|
|
end
|
|
|
|
describe "PUT /api/maps/:map_identifier/signatures/:id" do
|
|
setup :setup_map_authentication
|
|
|
|
test "updates an existing signature", %{conn: conn, map: map} do
|
|
signature_id = Ecto.UUID.generate()
|
|
|
|
update_params = %{
|
|
"name" => "Updated Signature",
|
|
"description" => "Updated description",
|
|
"type" => "Updated Type",
|
|
"custom_info" => "Updated info"
|
|
}
|
|
|
|
conn = put(conn, ~p"/api/maps/#{map.slug}/signatures/#{signature_id}", update_params)
|
|
|
|
# Should return updated signature or error
|
|
response =
|
|
case conn.status do
|
|
200 -> json_response(conn, 200)
|
|
422 -> json_response(conn, 422)
|
|
_ -> flunk("Unexpected status code: #{inspect(conn.status)}")
|
|
end
|
|
|
|
case response do
|
|
%{"data" => _data} ->
|
|
assert true
|
|
|
|
%{"error" => _error} ->
|
|
assert true
|
|
end
|
|
end
|
|
|
|
test "handles partial updates", %{conn: conn, map: map} do
|
|
signature_id = Ecto.UUID.generate()
|
|
|
|
partial_params = %{
|
|
"name" => "Partially Updated"
|
|
}
|
|
|
|
conn = put(conn, ~p"/api/maps/#{map.slug}/signatures/#{signature_id}", partial_params)
|
|
|
|
response =
|
|
case conn.status do
|
|
200 -> json_response(conn, 200)
|
|
422 -> json_response(conn, 422)
|
|
_ -> flunk("Unexpected status code: #{inspect(conn.status)}")
|
|
end
|
|
|
|
assert Map.has_key?(response, "data") or Map.has_key?(response, "error")
|
|
end
|
|
|
|
test "updates with null values for optional fields", %{conn: conn, map: map} do
|
|
signature_id = Ecto.UUID.generate()
|
|
|
|
update_params = %{
|
|
"name" => nil,
|
|
"description" => nil,
|
|
"custom_info" => nil
|
|
}
|
|
|
|
conn = put(conn, ~p"/api/maps/#{map.slug}/signatures/#{signature_id}", update_params)
|
|
|
|
response =
|
|
case conn.status do
|
|
200 -> json_response(conn, 200)
|
|
422 -> json_response(conn, 422)
|
|
_ -> flunk("Unexpected status code: #{inspect(conn.status)}")
|
|
end
|
|
|
|
assert Map.has_key?(response, "data") or Map.has_key?(response, "error")
|
|
end
|
|
|
|
test "handles update with invalid signature ID", %{conn: conn, map: map} do
|
|
update_params = %{
|
|
"name" => "Updated Signature"
|
|
}
|
|
|
|
conn = put(conn, ~p"/api/maps/#{map.slug}/signatures/invalid-uuid", update_params)
|
|
|
|
# Should handle invalid UUID gracefully
|
|
response =
|
|
case conn.status do
|
|
200 -> json_response(conn, 200)
|
|
422 -> json_response(conn, 422)
|
|
_ -> flunk("Unexpected status code: #{inspect(conn.status)}")
|
|
end
|
|
|
|
assert Map.has_key?(response, "data") or Map.has_key?(response, "error")
|
|
end
|
|
|
|
test "returns 401 without authentication" do
|
|
map = Factory.insert(:map)
|
|
signature_id = Ecto.UUID.generate()
|
|
|
|
update_params = %{
|
|
"name" => "Updated Signature"
|
|
}
|
|
|
|
conn = build_conn()
|
|
conn = put(conn, ~p"/api/maps/#{map.slug}/signatures/#{signature_id}", update_params)
|
|
|
|
assert json_response(conn, 401)
|
|
end
|
|
end
|
|
|
|
describe "DELETE /api/maps/:map_identifier/signatures/:id" do
|
|
setup :setup_map_authentication
|
|
|
|
test "deletes an existing signature", %{conn: conn, map: map} do
|
|
signature_id = Ecto.UUID.generate()
|
|
|
|
conn = delete(conn, ~p"/api/maps/#{map.slug}/signatures/#{signature_id}")
|
|
|
|
# Should return 204 No Content or error
|
|
case conn.status do
|
|
204 ->
|
|
assert conn.resp_body == ""
|
|
|
|
422 ->
|
|
assert %{"error" => _error} = json_response(conn, 422)
|
|
|
|
_ ->
|
|
assert false, "Unexpected status code: #{conn.status}"
|
|
end
|
|
end
|
|
|
|
test "handles deletion of non-existent signature", %{conn: conn, map: map} do
|
|
non_existent_id = Ecto.UUID.generate()
|
|
|
|
conn = delete(conn, ~p"/api/maps/#{map.slug}/signatures/#{non_existent_id}")
|
|
|
|
# Should handle gracefully
|
|
case conn.status do
|
|
204 ->
|
|
assert conn.resp_body == ""
|
|
|
|
422 ->
|
|
assert %{"error" => _error} = json_response(conn, 422)
|
|
|
|
_ ->
|
|
assert false, "Unexpected status code: #{conn.status}"
|
|
end
|
|
end
|
|
|
|
test "handles invalid signature ID format", %{conn: conn, map: map} do
|
|
conn = delete(conn, ~p"/api/maps/#{map.slug}/signatures/invalid-uuid")
|
|
|
|
case conn.status do
|
|
204 ->
|
|
assert conn.resp_body == ""
|
|
|
|
422 ->
|
|
assert %{"error" => _error} = json_response(conn, 422)
|
|
|
|
_ ->
|
|
assert false, "Unexpected status code: #{conn.status}"
|
|
end
|
|
end
|
|
|
|
test "returns 401 without authentication" do
|
|
map = Factory.insert(:map)
|
|
signature_id = Ecto.UUID.generate()
|
|
|
|
conn = build_conn()
|
|
conn = delete(conn, ~p"/api/maps/#{map.slug}/signatures/#{signature_id}")
|
|
|
|
assert json_response(conn, 401)
|
|
end
|
|
end
|
|
|
|
describe "parameter validation" do
|
|
setup :setup_map_authentication
|
|
|
|
test "validates signature ID format in show", %{conn: conn, map: map} do
|
|
invalid_ids = [
|
|
"",
|
|
"not-a-uuid",
|
|
"123",
|
|
"invalid-format-here"
|
|
]
|
|
|
|
for invalid_id <- invalid_ids do
|
|
conn = get(conn, ~p"/api/maps/#{map.slug}/signatures/#{invalid_id}")
|
|
|
|
# Should handle invalid IDs gracefully
|
|
response =
|
|
case conn.status do
|
|
200 -> json_response(conn, 200)
|
|
404 -> json_response(conn, 404)
|
|
422 -> json_response(conn, 422)
|
|
_ -> flunk("Unexpected status code: #{inspect(conn.status)}")
|
|
end
|
|
|
|
assert Map.has_key?(response, "data") or Map.has_key?(response, "error")
|
|
end
|
|
end
|
|
|
|
test "validates signature creation with invalid data types", %{conn: conn, map: map} do
|
|
invalid_params = [
|
|
%{"solar_system_id" => "not-an-integer", "eve_id" => "ABC", "character_eve_id" => "123"},
|
|
%{"solar_system_id" => 30_000_142, "eve_id" => 123, "character_eve_id" => "123"},
|
|
%{"solar_system_id" => 30_000_142, "eve_id" => "ABC", "character_eve_id" => 123},
|
|
%{
|
|
"solar_system_id" => 30_000_142,
|
|
"eve_id" => "ABC",
|
|
"character_eve_id" => "123",
|
|
"linked_system_id" => "not-an-integer"
|
|
}
|
|
]
|
|
|
|
for params <- invalid_params do
|
|
conn = post(conn, ~p"/api/maps/#{map.slug}/signatures", params)
|
|
|
|
# Should handle validation errors
|
|
response =
|
|
case conn.status do
|
|
201 -> json_response(conn, 201)
|
|
422 -> json_response(conn, 422)
|
|
_ -> flunk("Unexpected status code: #{inspect(conn.status)}")
|
|
end
|
|
|
|
assert Map.has_key?(response, "data") or Map.has_key?(response, "error")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "edge cases" do
|
|
setup :setup_map_authentication
|
|
|
|
test "handles very long signature names and descriptions", %{conn: conn, map: map} do
|
|
long_string = String.duplicate("a", 1000)
|
|
|
|
long_params = %{
|
|
"solar_system_id" => 30_000_146,
|
|
"eve_id" => "LONG-123",
|
|
"character_eve_id" => "123456789",
|
|
"name" => long_string,
|
|
"description" => long_string,
|
|
"custom_info" => long_string
|
|
}
|
|
|
|
conn = post(conn, ~p"/api/maps/#{map.slug}/signatures", long_params)
|
|
|
|
response =
|
|
case conn.status do
|
|
201 -> json_response(conn, 201)
|
|
422 -> json_response(conn, 422)
|
|
_ -> flunk("Unexpected status code: #{inspect(conn.status)}")
|
|
end
|
|
|
|
assert Map.has_key?(response, "data") or Map.has_key?(response, "error")
|
|
end
|
|
|
|
test "handles special characters in signature data", %{conn: conn, map: map} do
|
|
special_params = %{
|
|
"solar_system_id" => 30_000_147,
|
|
"eve_id" => "ABC-123",
|
|
"character_eve_id" => "123456789",
|
|
"name" => "Special chars: àáâãäåæçèéêë",
|
|
"description" => "Unicode: 🚀🌟⭐",
|
|
"custom_info" => "Mixed: abc123!@#$%^&*()"
|
|
}
|
|
|
|
conn = post(conn, ~p"/api/maps/#{map.slug}/signatures", special_params)
|
|
|
|
response =
|
|
case conn.status do
|
|
201 -> json_response(conn, 201)
|
|
422 -> json_response(conn, 422)
|
|
_ -> flunk("Unexpected status code: #{inspect(conn.status)}")
|
|
end
|
|
|
|
assert Map.has_key?(response, "data") or Map.has_key?(response, "error")
|
|
end
|
|
|
|
test "handles empty string values", %{conn: conn, map: map} do
|
|
empty_params = %{
|
|
"solar_system_id" => 30_000_148,
|
|
"eve_id" => "",
|
|
"character_eve_id" => "",
|
|
"name" => "",
|
|
"description" => "",
|
|
"type" => "",
|
|
"kind" => "",
|
|
"group" => "",
|
|
"custom_info" => ""
|
|
}
|
|
|
|
conn = post(conn, ~p"/api/maps/#{map.slug}/signatures", empty_params)
|
|
|
|
response =
|
|
case conn.status do
|
|
201 -> json_response(conn, 201)
|
|
422 -> json_response(conn, 422)
|
|
_ -> flunk("Unexpected status code: #{inspect(conn.status)}")
|
|
end
|
|
|
|
assert Map.has_key?(response, "data") or Map.has_key?(response, "error")
|
|
end
|
|
end
|
|
|
|
describe "authentication and authorization" do
|
|
test "all endpoints require authentication" do
|
|
map = Factory.insert(:map)
|
|
signature_id = Ecto.UUID.generate()
|
|
|
|
endpoints = [
|
|
{:get, ~p"/api/maps/#{map.slug}/signatures"},
|
|
{:get, ~p"/api/maps/#{map.slug}/signatures/#{signature_id}"},
|
|
{:post, ~p"/api/maps/#{map.slug}/signatures"},
|
|
{:put, ~p"/api/maps/#{map.slug}/signatures/#{signature_id}"},
|
|
{:delete, ~p"/api/maps/#{map.slug}/signatures/#{signature_id}"}
|
|
]
|
|
|
|
for {method, path} <- endpoints do
|
|
conn = build_conn()
|
|
|
|
conn =
|
|
case method do
|
|
:get -> get(conn, path)
|
|
:post -> post(conn, path, %{})
|
|
:put -> put(conn, path, %{})
|
|
:delete -> delete(conn, path)
|
|
end
|
|
|
|
assert json_response(conn, 401)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "OpenAPI schema compliance" do
|
|
setup :setup_map_authentication
|
|
|
|
test "responses match expected structure", %{conn: conn, map: map} do
|
|
# Test index endpoint response structure
|
|
conn = get(conn, ~p"/api/maps/#{map.slug}/signatures")
|
|
|
|
case json_response(conn, 200) do
|
|
%{"data" => data} ->
|
|
assert is_list(data)
|
|
# If signatures exist, they should have the expected structure
|
|
if length(data) > 0 do
|
|
signature = List.first(data)
|
|
assert Map.has_key?(signature, "id")
|
|
assert Map.has_key?(signature, "solar_system_id")
|
|
assert Map.has_key?(signature, "eve_id")
|
|
assert Map.has_key?(signature, "character_eve_id")
|
|
end
|
|
|
|
_ ->
|
|
assert false, "Expected data wrapper"
|
|
end
|
|
end
|
|
|
|
test "error responses have consistent structure", %{conn: conn, map: map} do
|
|
# Test error response from non-existent signature
|
|
non_existent_id = Ecto.UUID.generate()
|
|
conn = get(conn, ~p"/api/maps/#{map.slug}/signatures/#{non_existent_id}")
|
|
|
|
case json_response(conn, 404) do
|
|
%{"error" => error} ->
|
|
assert is_binary(error)
|
|
assert error == "Signature not found"
|
|
|
|
_ ->
|
|
assert false, "Expected error field in response"
|
|
end
|
|
end
|
|
|
|
test "created signature response structure", %{conn: conn, map: map} do
|
|
signature_params = %{
|
|
"solar_system_id" => 30_000_149,
|
|
"eve_id" => "TEST-001",
|
|
"character_eve_id" => "123456789",
|
|
"name" => "Test Signature"
|
|
}
|
|
|
|
conn = post(conn, ~p"/api/maps/#{map.slug}/signatures", signature_params)
|
|
|
|
response =
|
|
case conn.status do
|
|
201 -> json_response(conn, 201)
|
|
422 -> json_response(conn, 422)
|
|
_ -> flunk("Unexpected status code: #{inspect(conn.status)}")
|
|
end
|
|
|
|
case response do
|
|
%{"data" => data} ->
|
|
# Should have signature structure
|
|
assert Map.has_key?(data, "id") or Map.has_key?(data, "solar_system_id")
|
|
|
|
%{"error" => _error} ->
|
|
# Error response is also valid
|
|
assert true
|
|
|
|
_ ->
|
|
assert false, "Unexpected response structure"
|
|
end
|
|
end
|
|
end
|
|
end
|