Files
wanderer/test/integration/api/map_system_signature_api_controller_test.exs

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