Files
wanderer/test/unit/controllers/map_api_controller_test.exs
2025-07-12 22:28:59 +00:00

548 lines
18 KiB
Elixir

defmodule WandererAppWeb.MapAPIControllerTest do
use WandererAppWeb.ConnCase
alias WandererAppWeb.MapAPIController
describe "parameter validation and helper functions" do
test "list_tracked_characters validates missing map parameters" do
conn = build_conn()
params = %{}
result = MapAPIController.list_tracked_characters(conn, params)
# Should return bad request error
assert json_response(result, 400)
response = json_response(result, 400)
assert Map.has_key?(response, "error")
end
test "show_tracked_characters handles valid map_id in assigns" do
map_id = Ecto.UUID.generate()
conn = build_conn() |> assign(:map_id, map_id)
result = MapAPIController.show_tracked_characters(conn, %{})
# Should handle the call without crashing
assert %Plug.Conn{} = result
# Response depends on underlying data
assert result.status in [200, 500]
end
test "show_structure_timers validates parameters" do
conn = build_conn()
# Test with missing parameters
result_empty = MapAPIController.show_structure_timers(conn, %{})
assert json_response(result_empty, 400)
# Test with valid map_id
map_id = Ecto.UUID.generate()
result_valid = MapAPIController.show_structure_timers(conn, %{"map_id" => map_id})
assert %Plug.Conn{} = result_valid
# Response depends on underlying data
assert result_valid.status in [200, 400, 404, 500]
# Test with valid slug
result_slug = MapAPIController.show_structure_timers(conn, %{"slug" => "test-map"})
assert %Plug.Conn{} = result_slug
assert result_slug.status in [200, 400, 404, 500]
end
test "show_structure_timers handles system_id parameter" do
map_id = Ecto.UUID.generate()
conn = build_conn()
# Test with valid system_id
params_valid = %{"map_id" => map_id, "system_id" => "30000142"}
result_valid = MapAPIController.show_structure_timers(conn, params_valid)
assert %Plug.Conn{} = result_valid
# Test with invalid system_id
params_invalid = %{"map_id" => map_id, "system_id" => "invalid"}
result_invalid = MapAPIController.show_structure_timers(conn, params_invalid)
assert json_response(result_invalid, 400)
response = json_response(result_invalid, 400)
assert Map.has_key?(response, "error")
assert String.contains?(response["error"], "system_id must be int")
end
test "list_systems_kills validates parameters and handles hours parameter" do
conn = build_conn()
# Test with missing parameters
result_empty = MapAPIController.list_systems_kills(conn, %{})
assert json_response(result_empty, 400)
# Test with valid map_id
map_id = Ecto.UUID.generate()
result_valid = MapAPIController.list_systems_kills(conn, %{"map_id" => map_id})
assert %Plug.Conn{} = result_valid
# Test with hours parameter
result_hours =
MapAPIController.list_systems_kills(conn, %{"map_id" => map_id, "hours" => "24"})
assert %Plug.Conn{} = result_hours
# Test with invalid hours parameter
result_invalid_hours =
MapAPIController.list_systems_kills(conn, %{"map_id" => map_id, "hours" => "invalid"})
assert json_response(result_invalid_hours, 400)
# Test with legacy parameter names
result_legacy1 =
MapAPIController.list_systems_kills(conn, %{"map_id" => map_id, "hours_ago" => "12"})
assert %Plug.Conn{} = result_legacy1
result_legacy2 =
MapAPIController.list_systems_kills(conn, %{"map_id" => map_id, "hour_ago" => "6"})
assert %Plug.Conn{} = result_legacy2
end
test "character_activity validates parameters and handles days parameter" do
conn = build_conn()
# Test with missing parameters
result_empty = MapAPIController.character_activity(conn, %{})
assert json_response(result_empty, 400)
# Test with valid map_id
map_id = Ecto.UUID.generate()
result_valid = MapAPIController.character_activity(conn, %{"map_id" => map_id})
assert %Plug.Conn{} = result_valid
# Test with days parameter
result_days =
MapAPIController.character_activity(conn, %{"map_id" => map_id, "days" => "7"})
assert %Plug.Conn{} = result_days
# Test with invalid days parameter
result_invalid_days =
MapAPIController.character_activity(conn, %{"map_id" => map_id, "days" => "invalid"})
assert json_response(result_invalid_days, 400)
# Test with zero days (should be invalid)
result_zero_days =
MapAPIController.character_activity(conn, %{"map_id" => map_id, "days" => "0"})
assert json_response(result_zero_days, 400)
end
test "user_characters validates parameters" do
conn = build_conn()
# Test with missing parameters
result_empty = MapAPIController.user_characters(conn, %{})
assert json_response(result_empty, 400)
# Test with valid map_id
map_id = Ecto.UUID.generate()
result_valid = MapAPIController.user_characters(conn, %{"map_id" => map_id})
assert %Plug.Conn{} = result_valid
# Test with slug parameter
result_slug = MapAPIController.user_characters(conn, %{"slug" => "test-map"})
assert %Plug.Conn{} = result_slug
end
test "show_user_characters handles valid map_id in assigns" do
map_id = Ecto.UUID.generate()
conn = build_conn() |> assign(:map_id, map_id)
result = MapAPIController.show_user_characters(conn, %{})
# Should handle the call without crashing
assert %Plug.Conn{} = result
# Response depends on underlying data
assert result.status in [200, 500]
end
test "list_connections validates parameters" do
conn = build_conn()
# Test with missing parameters
result_empty = MapAPIController.list_connections(conn, %{})
assert json_response(result_empty, 400)
# Test with valid map_id
map_id = Ecto.UUID.generate()
result_valid = MapAPIController.list_connections(conn, %{"map_id" => map_id})
assert %Plug.Conn{} = result_valid
# Test with slug parameter
result_slug = MapAPIController.list_connections(conn, %{"slug" => "test-map"})
assert %Plug.Conn{} = result_slug
end
test "toggle_webhooks validates parameters and authorization" do
conn = build_conn()
# Test with missing enabled parameter - expects FunctionClauseError
assert_raise(FunctionClauseError, fn ->
MapAPIController.toggle_webhooks(conn, %{"map_id" => "test-map"})
end)
# Test with valid boolean values
test_cases = [
%{"map_id" => "test-map", "enabled" => true},
%{"map_id" => "test-map", "enabled" => false},
%{"map_id" => "test-map", "enabled" => "true"},
%{"map_id" => "test-map", "enabled" => "false"},
%{"map_id" => "test-map", "enabled" => "invalid"}
]
Enum.each(test_cases, fn params ->
result = MapAPIController.toggle_webhooks(conn, params)
assert %Plug.Conn{} = result
# Response depends on application configuration and data
assert result.status in [200, 400, 403, 404, 503]
end)
end
end
describe "parameter parsing and edge cases" do
test "handles various map identifier formats" do
conn = build_conn()
# Test UUID format
uuid = Ecto.UUID.generate()
result_uuid = MapAPIController.list_connections(conn, %{"map_id" => uuid})
assert %Plug.Conn{} = result_uuid
# Test slug format
result_slug = MapAPIController.list_connections(conn, %{"slug" => "my-test-map"})
assert %Plug.Conn{} = result_slug
# Test invalid formats
result_invalid = MapAPIController.list_connections(conn, %{"map_id" => "invalid-format"})
assert %Plug.Conn{} = result_invalid
end
test "handles parameter combinations for structure timers" do
conn = build_conn()
map_id = Ecto.UUID.generate()
# Test various parameter combinations
param_combinations = [
%{"map_id" => map_id},
%{"slug" => "test-map"},
%{"map_id" => map_id, "system_id" => "30000142"},
%{"slug" => "test-map", "system_id" => "30000143"},
%{"map_id" => map_id, "system_id" => "0"},
%{"map_id" => map_id, "system_id" => "-1"}
]
Enum.each(param_combinations, fn params ->
result = MapAPIController.show_structure_timers(conn, params)
assert %Plug.Conn{} = result
# Each combination should be handled
assert result.status in [200, 400, 404, 500]
end)
end
test "handles different time parameter formats for kills" do
conn = build_conn()
map_id = Ecto.UUID.generate()
# Test different hour formats
hour_formats = [
"1",
"24",
"168",
"0",
"-1",
"invalid",
"",
"1.5",
"abc"
]
Enum.each(hour_formats, fn hours ->
params = %{"map_id" => map_id, "hours" => hours}
result = MapAPIController.list_systems_kills(conn, params)
assert %Plug.Conn{} = result
# Each format should be handled
assert result.status in [200, 400, 404, 500]
end)
end
test "handles different day parameter formats for character activity" do
conn = build_conn()
map_id = Ecto.UUID.generate()
# Test different day formats
day_formats = [
"1",
"7",
"30",
"365",
"0",
"-1",
"invalid",
"",
"1.5",
"abc"
]
Enum.each(day_formats, fn days ->
params = %{"map_id" => map_id, "days" => days}
result = MapAPIController.character_activity(conn, params)
assert %Plug.Conn{} = result
# Each format should be handled
assert result.status in [200, 400, 500]
end)
end
test "handles map_identifier parameter normalization" do
conn = build_conn()
# Test the parameter that gets normalized in character_activity
param_formats = [
%{"map_identifier" => Ecto.UUID.generate()},
%{"map_identifier" => "test-slug"},
%{"map_identifier" => "invalid-format"},
%{"map_identifier" => ""},
%{"map_identifier" => nil}
]
Enum.each(param_formats, fn params ->
result = MapAPIController.character_activity(conn, params)
assert %Plug.Conn{} = result
# Each format should be handled
assert result.status in [200, 400, 500]
end)
end
end
describe "error handling scenarios" do
test "handles empty and nil parameters gracefully" do
conn = build_conn()
# Test all endpoints with empty parameters
endpoints = [
&MapAPIController.list_tracked_characters/2,
&MapAPIController.show_structure_timers/2,
&MapAPIController.list_systems_kills/2,
&MapAPIController.character_activity/2,
&MapAPIController.user_characters/2,
&MapAPIController.list_connections/2
]
Enum.each(endpoints, fn endpoint ->
result = endpoint.(conn, %{})
assert %Plug.Conn{} = result
# Should handle empty params gracefully
assert result.status in [200, 400, 404, 500]
end)
end
test "handles malformed parameter values" do
conn = build_conn()
# Test with various malformed values
malformed_params = [
%{"map_id" => []},
%{"map_id" => %{}},
%{"slug" => []},
%{"slug" => %{}},
%{"system_id" => []},
%{"hours" => []},
%{"days" => []},
%{"enabled" => []}
]
Enum.each(malformed_params, fn params ->
# Test structure timers endpoint as it has multiple parameter types
result = MapAPIController.show_structure_timers(conn, params)
case result do
%Plug.Conn{} ->
# Should handle malformed params gracefully
assert result.status in [200, 400, 404, 500]
{:error, _} ->
:ok
end
end)
end
test "handles webhook toggle with various enabled values" do
conn = build_conn()
map_id = "test-map"
# Test different enabled parameter formats
enabled_values = [
true,
false,
"true",
"false",
"1",
"0",
"yes",
"no",
nil,
"",
"invalid",
[],
%{},
123,
-1,
0.5
]
Enum.each(enabled_values, fn enabled ->
params = %{"map_id" => map_id, "enabled" => enabled}
result = MapAPIController.toggle_webhooks(conn, params)
assert %Plug.Conn{} = result
# Each value should be handled
assert result.status in [200, 400, 403, 404, 503]
end)
end
test "handles requests with assigns and without assigns" do
map_id = Ecto.UUID.generate()
# Test with assigns
conn_with_assigns = build_conn() |> assign(:map_id, map_id)
result_with = MapAPIController.show_tracked_characters(conn_with_assigns, %{})
assert %Plug.Conn{} = result_with
# Test with assigns including current_character
character = %{id: "char123"}
conn_with_char = build_conn() |> assign(:current_character, character)
result_with_char =
MapAPIController.show_user_characters(conn_with_char |> assign(:map_id, map_id), %{})
assert %Plug.Conn{} = result_with_char
end
end
describe "response structure validation" do
test "endpoints return consistent response structures" do
conn = build_conn()
map_id = Ecto.UUID.generate()
# Test endpoints that should return data wrapper format
endpoints_with_params = [
{&MapAPIController.list_tracked_characters/2, %{"map_id" => map_id}},
{&MapAPIController.show_structure_timers/2, %{"map_id" => map_id}},
{&MapAPIController.list_systems_kills/2, %{"map_id" => map_id}},
{&MapAPIController.character_activity/2, %{"map_id" => map_id}},
{&MapAPIController.user_characters/2, %{"map_id" => map_id}},
{&MapAPIController.list_connections/2, %{"map_id" => map_id}}
]
Enum.each(endpoints_with_params, fn {endpoint, params} ->
result = endpoint.(conn, params)
assert %Plug.Conn{} = result
# If successful, should have proper JSON structure
if result.status == 200 do
response = json_response(result, 200)
assert Map.has_key?(response, "data")
end
# If error, should have error field
if result.status >= 400 do
response = Jason.decode!(result.resp_body)
assert Map.has_key?(response, "error")
end
end)
end
test "webhook toggle returns proper response structure" do
conn = build_conn()
params = %{"map_id" => "test-map", "enabled" => true}
result = MapAPIController.toggle_webhooks(conn, params)
assert %Plug.Conn{} = result
# Should return JSON response
assert result.resp_body != ""
response = Jason.decode!(result.resp_body)
# Response should have either webhooks_enabled or error field
assert Map.has_key?(response, "webhooks_enabled") or Map.has_key?(response, "error")
end
end
describe "OpenAPI schema compliance" do
test "endpoints handle documented parameter combinations" do
conn = build_conn()
map_id = Ecto.UUID.generate()
# Test parameter combinations mentioned in OpenAPI specs
test_combinations = [
# list_tracked_characters
{&MapAPIController.list_tracked_characters/2, %{"map_id" => map_id}},
{&MapAPIController.list_tracked_characters/2, %{"slug" => "test-map"}},
# show_structure_timers
{&MapAPIController.show_structure_timers/2, %{"map_id" => map_id}},
{&MapAPIController.show_structure_timers/2, %{"slug" => "test-map"}},
{&MapAPIController.show_structure_timers/2,
%{"map_id" => map_id, "system_id" => "30000142"}},
# list_systems_kills
{&MapAPIController.list_systems_kills/2, %{"map_id" => map_id}},
{&MapAPIController.list_systems_kills/2, %{"slug" => "test-map"}},
{&MapAPIController.list_systems_kills/2, %{"map_id" => map_id, "hours" => "24"}},
# character_activity
{&MapAPIController.character_activity/2, %{"map_id" => map_id}},
{&MapAPIController.character_activity/2, %{"slug" => "test-map"}},
{&MapAPIController.character_activity/2, %{"map_id" => map_id, "days" => "7"}},
# user_characters
{&MapAPIController.user_characters/2, %{"map_id" => map_id}},
{&MapAPIController.user_characters/2, %{"slug" => "test-map"}},
# list_connections
{&MapAPIController.list_connections/2, %{"map_id" => map_id}},
{&MapAPIController.list_connections/2, %{"slug" => "test-map"}}
]
Enum.each(test_combinations, fn {endpoint, params} ->
try do
result = endpoint.(conn, params)
assert %Plug.Conn{} = result
# Each documented combination should be handled
assert result.status in [200, 400, 404, 500]
catch
# Some endpoints may have unhandled error cases in unit tests
_, _ -> :ok
rescue
# Some endpoints may throw MatchError with missing resources
MatchError -> :ok
end
end)
end
test "error responses match documented status codes" do
conn = build_conn()
# Test bad request scenarios (400)
bad_request_tests = [
{&MapAPIController.list_tracked_characters/2, %{}},
{&MapAPIController.show_structure_timers/2, %{}},
{&MapAPIController.list_systems_kills/2, %{}},
{&MapAPIController.character_activity/2, %{}},
{&MapAPIController.user_characters/2, %{}},
{&MapAPIController.list_connections/2, %{}}
]
Enum.each(bad_request_tests, fn {endpoint, params} ->
result = endpoint.(conn, params)
assert %Plug.Conn{} = result
assert result.status == 400
end)
end
end
end