mirror of
https://github.com/wanderer-industries/wanderer
synced 2026-01-18 21:00:25 +00:00
701 lines
21 KiB
Elixir
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
|