Files
wanderer/test/unit/controllers/map_system_api_controller_test.exs
2025-11-24 11:33:08 +01:00

701 lines
21 KiB
Elixir

defmodule WandererAppWeb.MapSystemAPIControllerTest do
use WandererAppWeb.ConnCase, async: false
alias WandererAppWeb.MapSystemAPIController
# Helper function to handle controller results that may be error tuples in unit tests
defp assert_controller_result(result, expected_statuses \\ [200, 400, 404, 422, 500]) do
case result do
%Plug.Conn{} ->
assert result.status in expected_statuses
result
{:error, _} ->
# Error tuples are acceptable in unit tests without full context
:ok
end
end
describe "parameter validation and core functions" do
test "index lists systems and connections" do
map_id = Ecto.UUID.generate()
conn = build_conn() |> assign(:map_id, map_id)
result = MapSystemAPIController.index(conn, %{})
case result do
%Plug.Conn{} ->
assert result.status in [200, 500]
if result.status == 200 do
response = json_response(result, 200)
assert Map.has_key?(response, "data")
assert Map.has_key?(response["data"], "systems")
assert Map.has_key?(response["data"], "connections")
end
{:error, _} ->
# Error tuples are acceptable in unit tests
:ok
end
end
test "show validates system ID parameter" do
map_id = Ecto.UUID.generate()
conn = build_conn() |> assign(:map_id, map_id)
# Test with valid system ID
params_valid = %{"id" => "30000142"}
result_valid = MapSystemAPIController.show(conn, params_valid)
# Can return error tuple if system not found (which is expected in unit test)
case result_valid do
%Plug.Conn{} -> assert result_valid.status in [200, 404, 500]
# Expected in unit test without real data
{:error, :not_found} -> :ok
# Other errors are acceptable in unit tests
{:error, _} -> :ok
end
# Test with invalid system ID
params_invalid = %{"id" => "invalid"}
result_invalid = MapSystemAPIController.show(conn, params_invalid)
case result_invalid do
%Plug.Conn{} -> assert result_invalid.status in [400, 404, 500]
# Expected for invalid parameters
{:error, _} -> :ok
end
end
test "create handles single system creation" do
map_id = Ecto.UUID.generate()
char_id = "123456789"
conn = build_conn() |> assign(:map_id, map_id) |> assign(:owner_character_id, char_id)
# Test with valid single system parameters
params_valid = %{
"solar_system_id" => 30_000_142,
"position_x" => 100,
"position_y" => 200
}
result_valid = MapSystemAPIController.create(conn, params_valid)
# Can return error tuple if missing required context (expected in unit test)
case result_valid do
%Plug.Conn{} -> assert result_valid.status in [200, 400, 500]
# Expected in unit test without full context
{:error, :missing_params} -> :ok
# Other errors are acceptable in unit tests
{:error, _} -> :ok
end
# Test with missing position parameters
params_missing_pos = %{
"solar_system_id" => 30_000_142
}
result_missing = MapSystemAPIController.create(conn, params_missing_pos)
case result_missing do
%Plug.Conn{} ->
assert result_missing.status in [400, 422, 500]
if result_missing.status == 400 do
response = json_response(result_missing, 400)
assert Map.has_key?(response, "error")
end
{:error, _} ->
# Error tuples are acceptable in unit tests
:ok
end
end
test "create handles batch operations" do
map_id = Ecto.UUID.generate()
char_id = "123456789"
conn = build_conn() |> assign(:map_id, map_id) |> assign(:owner_character_id, char_id)
# Test with valid batch parameters
params_batch = %{
"systems" => [
%{
"solar_system_id" => 30_000_142,
"position_x" => 100,
"position_y" => 200
}
],
"connections" => [
%{
"solar_system_source" => 30_000_142,
"solar_system_target" => 30_000_143
}
]
}
result_batch = MapSystemAPIController.create(conn, params_batch)
assert_controller_result(result_batch)
# Test with empty arrays
params_empty = %{
"systems" => [],
"connections" => []
}
result_empty = MapSystemAPIController.create(conn, params_empty)
case result_empty do
%Plug.Conn{} -> assert result_empty.status in [200, 400, 500]
# Error tuples are acceptable in unit tests
{:error, _} -> :ok
end
end
test "create validates array parameters for batch" do
map_id = Ecto.UUID.generate()
char_id = "123456789"
conn = build_conn() |> assign(:map_id, map_id) |> assign(:owner_character_id, char_id)
# Test with invalid systems parameter (not array)
params_invalid_systems = %{
"systems" => "not_an_array",
"connections" => []
}
result_invalid_systems = MapSystemAPIController.create(conn, params_invalid_systems)
case result_invalid_systems do
%Plug.Conn{} ->
assert result_invalid_systems.status in [400, 422, 500]
if result_invalid_systems.status == 400 do
response = json_response(result_invalid_systems, 400)
assert is_binary(response["error"])
end
{:error, _} ->
# Error tuples are acceptable in unit tests
:ok
end
# Test with invalid connections parameter (not array)
params_invalid_connections = %{
"systems" => [],
"connections" => "not_an_array"
}
result_invalid_connections = MapSystemAPIController.create(conn, params_invalid_connections)
case result_invalid_connections do
%Plug.Conn{} ->
assert result_invalid_connections.status in [400, 422, 500]
if result_invalid_connections.status == 400 do
response = json_response(result_invalid_connections, 400)
assert is_binary(response["error"])
end
{:error, _} ->
# Error tuples are acceptable in unit tests
:ok
end
end
test "create handles malformed single system requests" do
map_id = Ecto.UUID.generate()
char_id = "123456789"
conn = build_conn() |> assign(:map_id, map_id) |> assign(:owner_character_id, char_id)
# Test with position parameters but no solar_system_id
params_malformed = %{
"position_x" => 100,
"position_y" => 200
}
result_malformed = MapSystemAPIController.create(conn, params_malformed)
case result_malformed do
%Plug.Conn{} ->
assert result_malformed.status in [400, 422, 500]
if result_malformed.status == 400 do
response = json_response(result_malformed, 400)
assert is_binary(response["error"])
end
{:error, _} ->
# Error tuples are acceptable in unit tests
:ok
end
end
test "update validates system ID and parameters" do
map_id = Ecto.UUID.generate()
char_id = "123456789"
conn = build_conn() |> assign(:map_id, map_id) |> assign(:owner_character_id, char_id)
# Test with valid system ID
params_valid = %{"id" => "30000142", "position_x" => 150}
result_valid = MapSystemAPIController.update(conn, params_valid)
assert_controller_result(result_valid)
# Test with invalid system ID
params_invalid = %{"id" => "invalid", "position_x" => 150}
result_invalid = MapSystemAPIController.update(conn, params_invalid)
assert_controller_result(result_invalid)
end
test "delete handles batch deletion" do
map_id = Ecto.UUID.generate()
conn = build_conn() |> assign(:map_id, map_id)
# Test with system and connection IDs
params = %{
"system_ids" => [30_000_142, 30_000_143],
"connection_ids" => [Ecto.UUID.generate()]
}
result = MapSystemAPIController.delete(conn, params)
case result do
%Plug.Conn{} ->
if result.status == 200 do
response = json_response(result, 200)
assert Map.has_key?(response, "data")
assert Map.has_key?(response["data"], "deleted_count")
end
{:error, _} ->
# Error tuples are acceptable in unit tests
:ok
end
end
test "delete_single handles individual system deletion" do
map_id = Ecto.UUID.generate()
conn = build_conn() |> assign(:map_id, map_id)
# Test with valid system ID
params_valid = %{"id" => "30000142"}
result_valid = MapSystemAPIController.delete_single(conn, params_valid)
assert_controller_result(result_valid)
# Test with invalid system ID
params_invalid = %{"id" => "invalid"}
result_invalid = MapSystemAPIController.delete_single(conn, params_invalid)
assert_controller_result(result_invalid)
end
end
describe "parameter parsing and edge cases" do
test "create_single_system handles invalid solar_system_id" do
map_id = Ecto.UUID.generate()
char_id = "123456789"
base_conn = build_conn() |> assign(:map_id, map_id) |> assign(:owner_character_id, char_id)
# Test invalid solar_system_id formats
invalid_system_ids = ["invalid", "", nil, -1]
Enum.each(invalid_system_ids, fn solar_system_id ->
params = %{
"solar_system_id" => solar_system_id,
"position_x" => 100,
"position_y" => 200
}
result = MapSystemAPIController.create(base_conn, params)
case result do
%Plug.Conn{} ->
# Should handle invalid IDs gracefully
assert result.status in [400, 422, 500]
{:error, _} ->
# Error tuples are acceptable in unit tests
:ok
end
end)
end
test "handles different parameter combinations for batch create" do
map_id = Ecto.UUID.generate()
char_id = "123456789"
conn = build_conn() |> assign(:map_id, map_id) |> assign(:owner_character_id, char_id)
# Test various parameter combinations
param_combinations = [
%{"systems" => [], "connections" => []},
%{
"systems" => [
%{"solar_system_id" => 30_000_142, "position_x" => 100, "position_y" => 200}
]
},
%{
"connections" => [
%{"solar_system_source" => 30_000_142, "solar_system_target" => 30_000_143}
]
},
# Empty parameters
%{},
# Unexpected field
%{"other_field" => "value"}
]
Enum.each(param_combinations, fn params ->
result = MapSystemAPIController.create(conn, params)
assert_controller_result(result)
end)
end
test "delete handles empty and invalid arrays" do
map_id = Ecto.UUID.generate()
conn = build_conn() |> assign(:map_id, map_id)
# Test with empty arrays
params_empty = %{
"system_ids" => [],
"connection_ids" => []
}
result_empty = MapSystemAPIController.delete(conn, params_empty)
assert_controller_result(result_empty)
# Test with missing fields
params_missing = %{}
result_missing = MapSystemAPIController.delete(conn, params_missing)
assert_controller_result(result_missing)
# Test with malformed IDs
params_malformed = %{
"system_ids" => ["invalid", "", nil],
"connection_ids" => ["invalid-uuid", ""]
}
result_malformed = MapSystemAPIController.delete(conn, params_malformed)
assert_controller_result(result_malformed)
end
test "update extracts parameters correctly" do
map_id = Ecto.UUID.generate()
char_id = "123456789"
conn = build_conn() |> assign(:map_id, map_id) |> assign(:owner_character_id, char_id)
# Test with various update parameters
update_param_combinations = [
%{"id" => "30000142", "position_x" => 100},
%{"id" => "30000142", "position_y" => 200},
%{"id" => "30000142", "status" => 1},
%{"id" => "30000142", "visible" => true},
%{"id" => "30000142", "description" => "test"},
%{"id" => "30000142", "tag" => "test-tag"},
%{"id" => "30000142", "locked" => false},
%{"id" => "30000142", "temporary_name" => "temp"},
%{"id" => "30000142", "labels" => "label1,label2"},
# No update fields
%{"id" => "30000142"}
]
Enum.each(update_param_combinations, fn params ->
result = MapSystemAPIController.update(conn, params)
assert_controller_result(result)
end)
end
test "handles missing assigns gracefully" do
conn = build_conn()
# Should fail due to missing map_id assign
assert_raise(FunctionClauseError, fn ->
MapSystemAPIController.index(conn, %{})
end)
assert_raise(FunctionClauseError, fn ->
MapSystemAPIController.show(conn, %{"id" => "30000142"})
end)
end
end
describe "error handling scenarios" do
test "create handles various error conditions" do
map_id = Ecto.UUID.generate()
char_id = "123456789"
conn = build_conn() |> assign(:map_id, map_id) |> assign(:owner_character_id, char_id)
# Test malformed single system requests
malformed_single_params = [
%{"solar_system_id" => "invalid", "position_x" => 100, "position_y" => 200},
%{"solar_system_id" => nil, "position_x" => 100, "position_y" => 200},
%{"solar_system_id" => "", "position_x" => 100, "position_y" => 200}
]
Enum.each(malformed_single_params, fn params ->
result = MapSystemAPIController.create(conn, params)
case result do
%Plug.Conn{} ->
assert result.status in [400, 422, 500]
{:error, _} ->
# Error tuples are acceptable in unit tests
:ok
end
end)
end
test "delete_system_id and delete_connection_id helper functions" do
# These are tested indirectly through the delete function
map_id = Ecto.UUID.generate()
conn = build_conn() |> assign(:map_id, map_id)
# Test with various ID formats
test_ids = [
# Valid integer ID
30_000_142,
# Valid string ID
"30000142",
# Invalid string
"invalid",
# Empty string
"",
# Nil value
nil
]
Enum.each(test_ids, fn id ->
params = %{
"system_ids" => [id],
"connection_ids" => []
}
result = MapSystemAPIController.delete(conn, params)
assert_controller_result(result)
end)
end
test "handles invalid update parameters" do
map_id = Ecto.UUID.generate()
char_id = "123456789"
conn = build_conn() |> assign(:map_id, map_id) |> assign(:owner_character_id, char_id)
# Test with various invalid parameters
invalid_updates = [
%{"id" => "", "position_x" => 100},
%{"id" => nil, "position_x" => 100},
%{"id" => "invalid", "position_x" => "invalid"},
%{"id" => "30000142", "status" => "invalid"},
%{"id" => "30000142", "visible" => "invalid"}
]
Enum.each(invalid_updates, fn params ->
result = MapSystemAPIController.update(conn, params)
assert_controller_result(result)
end)
end
test "delete_single handles various error conditions" do
map_id = Ecto.UUID.generate()
conn = build_conn() |> assign(:map_id, map_id)
# Test with various system ID formats
system_id_formats = [
# Valid
"30000142",
# Invalid string
"invalid",
# Empty
"",
# Nil
nil,
# Negative
"-1",
# Zero
"0"
]
Enum.each(system_id_formats, fn id ->
params = %{"id" => id}
result = MapSystemAPIController.delete_single(conn, params)
assert_controller_result(result)
end)
end
end
describe "response structure validation" do
test "index returns consistent response structure" do
map_id = Ecto.UUID.generate()
conn = build_conn() |> assign(:map_id, map_id)
result = MapSystemAPIController.index(conn, %{})
assert_controller_result(result)
if result.status == 200 do
response = json_response(result, 200)
assert Map.has_key?(response, "data")
assert is_map(response["data"])
assert Map.has_key?(response["data"], "systems")
assert Map.has_key?(response["data"], "connections")
assert is_list(response["data"]["systems"])
assert is_list(response["data"]["connections"])
end
end
test "show returns proper response structure" do
map_id = Ecto.UUID.generate()
conn = build_conn() |> assign(:map_id, map_id)
result = MapSystemAPIController.show(conn, %{"id" => "30000142"})
case result do
%Plug.Conn{} ->
# Should have JSON response
assert result.resp_body != ""
{:error, _} ->
# Error tuples are acceptable in unit tests
:ok
end
end
test "create returns proper response structures" do
map_id = Ecto.UUID.generate()
char_id = "123456789"
conn = build_conn() |> assign(:map_id, map_id) |> assign(:owner_character_id, char_id)
# Test single system creation response
params_single = %{
"solar_system_id" => 30_000_142,
"position_x" => 100,
"position_y" => 200
}
result_single = MapSystemAPIController.create(conn, params_single)
case result_single do
%Plug.Conn{} ->
assert result_single.resp_body != ""
{:error, _} ->
# Error tuples are acceptable in unit tests
:ok
end
# Test batch operation response
params_batch = %{
"systems" => [],
"connections" => []
}
result_batch = MapSystemAPIController.create(conn, params_batch)
case result_batch do
%Plug.Conn{} ->
assert result_batch.resp_body != ""
{:error, _} ->
# Error tuples are acceptable in unit tests
:ok
end
end
test "update returns proper response structure" do
map_id = Ecto.UUID.generate()
char_id = "123456789"
conn = build_conn() |> assign(:map_id, map_id) |> assign(:owner_character_id, char_id)
result = MapSystemAPIController.update(conn, %{"id" => "30000142", "position_x" => 150})
case result do
%Plug.Conn{} ->
assert result.resp_body != ""
{:error, _} ->
# Error tuples are acceptable in unit tests
:ok
end
end
test "delete returns proper response structure" do
map_id = Ecto.UUID.generate()
conn = build_conn() |> assign(:map_id, map_id)
result = MapSystemAPIController.delete(conn, %{"system_ids" => [], "connection_ids" => []})
assert_controller_result(result)
if result.status == 200 do
response = json_response(result, 200)
assert Map.has_key?(response, "data")
assert Map.has_key?(response["data"], "deleted_count")
assert is_integer(response["data"]["deleted_count"])
end
end
test "delete_single returns proper response structure" do
map_id = Ecto.UUID.generate()
conn = build_conn() |> assign(:map_id, map_id)
result = MapSystemAPIController.delete_single(conn, %{"id" => "30000142"})
case result do
%Plug.Conn{} ->
# Should have JSON response
assert result.resp_body != ""
response = Jason.decode!(result.resp_body)
assert Map.has_key?(response, "data")
assert Map.has_key?(response["data"], "deleted")
{:error, _} ->
# Error tuples are acceptable in unit tests
:ok
end
end
test "error responses have consistent structure" do
map_id = Ecto.UUID.generate()
char_id = "123456789"
conn = build_conn() |> assign(:map_id, map_id) |> assign(:owner_character_id, char_id)
# Test error response from create
params_error = %{
"solar_system_id" => 30_000_142
# Missing position_x and position_y
}
result_error = MapSystemAPIController.create(conn, params_error)
case result_error do
%Plug.Conn{} ->
assert result_error.status in [400, 422, 500]
if result_error.status == 400 do
response = json_response(result_error, 400)
assert Map.has_key?(response, "error")
assert is_binary(response["error"])
end
{:error, _} ->
# Error tuples are acceptable in unit tests
:ok
end
end
end
describe "legacy endpoint compatibility" do
test "list_systems delegates to index" do
map_id = Ecto.UUID.generate()
conn = build_conn() |> assign(:map_id, map_id)
# The list_systems function delegates to index, so it should behave the same
result = MapSystemAPIController.list_systems(conn, %{})
case result do
%Plug.Conn{} ->
assert result.status in [200, 500]
{:error, _} ->
# Error tuples are acceptable in unit tests
:ok
end
end
end
end