Files
wanderer/test/unit/character_api_controller_test.exs
guarzo d8acfa5c05
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions
refactor: standalone unit tests (#278)
2025-03-18 21:37:52 +04:00

335 lines
11 KiB
Elixir

# Standalone test for the CharacterAPIController
#
# This file can be run directly with:
# elixir test/standalone/character_api_controller_test.exs
#
# It doesn't require any database connections or external dependencies.
# Start ExUnit
ExUnit.start()
defmodule CharacterAPIControllerTest do
use ExUnit.Case
# Mock modules to simulate the behavior of the controller's dependencies
defmodule MockUtil do
def require_param(params, key) do
case params[key] do
nil -> {:error, "Missing required param: #{key}"}
"" -> {:error, "Param #{key} cannot be empty"}
val -> {:ok, val}
end
end
def parse_int(str) do
case Integer.parse(str) do
{num, ""} -> {:ok, num}
_ -> {:error, "Invalid integer for param id=#{str}"}
end
end
def parse_bool(str) do
case str do
"true" -> {:ok, true}
"false" -> {:ok, false}
_ -> {:error, "Invalid boolean value: #{str}"}
end
end
end
defmodule MockCharacterRepo do
# In-memory storage for character tracking data
def init_storage do
:ets.new(:character_tracking, [:set, :public, :named_table])
# Initialize with some test data
:ets.insert(:character_tracking, {"user1", [
%{eve_id: "123456", name: "Character One", tracked: true, followed: true},
%{eve_id: "234567", name: "Character Two", tracked: true, followed: false},
%{eve_id: "345678", name: "Character Three", tracked: false, followed: false}
]})
:ets.insert(:character_tracking, {"user2", [
%{eve_id: "456789", name: "Character Four", tracked: true, followed: true}
]})
end
def get_tracking_data(user_id) do
case :ets.lookup(:character_tracking, user_id) do
[{^user_id, data}] -> {:ok, data}
[] -> {:ok, []}
end
end
def update_tracking_data(user_id, new_data) do
:ets.insert(:character_tracking, {user_id, new_data})
{:ok, new_data}
end
def toggle_character_follow(user_id, character_id, follow_state) do
case get_tracking_data(user_id) do
{:ok, data} ->
# Find the character and update its followed state
updated_data = Enum.map(data, fn char ->
if char.eve_id == character_id do
%{char | followed: follow_state}
else
char
end
end)
# Update the storage
update_tracking_data(user_id, updated_data)
# Return the updated character
updated_char = Enum.find(updated_data, fn char -> char.eve_id == character_id end)
{:ok, updated_char}
error -> error
end
end
def toggle_character_track(user_id, character_id, track_state) do
case get_tracking_data(user_id) do
{:ok, data} ->
# Find the character and update its tracked state
updated_data = Enum.map(data, fn char ->
if char.eve_id == character_id do
%{char | tracked: track_state}
else
char
end
end)
# Update the storage
update_tracking_data(user_id, updated_data)
# Return the updated character
updated_char = Enum.find(updated_data, fn char -> char.eve_id == character_id end)
{:ok, updated_char}
error -> error
end
end
end
defmodule MockTrackingUtils do
def check_tracking_consistency(tracking_data) do
# Log warnings for characters that are followed but not tracked
inconsistent_chars = Enum.filter(tracking_data, fn char ->
char[:followed] == true && char[:tracked] == false
end)
if length(inconsistent_chars) > 0 do
Enum.each(inconsistent_chars, fn char ->
eve_id = Map.get(char, :eve_id, "unknown")
name = Map.get(char, :name, "Unknown Character")
IO.puts("WARNING: Inconsistent state detected - Character (ID: #{eve_id}, Name: #{name}) is followed but not tracked")
end)
end
# Return the original data unchanged
tracking_data
end
end
# Mock controller that uses our mock dependencies
defmodule MockCharacterAPIController do
# Simplified version of toggle_follow from CharacterAPIController
def toggle_follow(params, user_id) do
with {:ok, character_id} <- MockUtil.require_param(params, "character_id"),
{:ok, follow_str} <- MockUtil.require_param(params, "follow"),
{:ok, follow} <- MockUtil.parse_bool(follow_str) do
case MockCharacterRepo.toggle_character_follow(user_id, character_id, follow) do
{:ok, updated_char} ->
# Get all tracking data to check consistency
{:ok, all_tracking} = MockCharacterRepo.get_tracking_data(user_id)
# Check for inconsistencies (characters followed but not tracked)
MockTrackingUtils.check_tracking_consistency(all_tracking)
# Return the updated character
{:ok, %{data: updated_char}}
{:error, reason} ->
{:error, :internal_server_error, "Failed to update character: #{reason}"}
end
else
{:error, msg} ->
{:error, :bad_request, msg}
end
end
# Simplified version of toggle_track from CharacterAPIController
def toggle_track(params, user_id) do
with {:ok, character_id} <- MockUtil.require_param(params, "character_id"),
{:ok, track_str} <- MockUtil.require_param(params, "track"),
{:ok, track} <- MockUtil.parse_bool(track_str) do
# If we're untracking a character, we should also unfollow it
result = if track == false do
# First unfollow if needed
MockCharacterRepo.toggle_character_follow(user_id, character_id, false)
# Then untrack
MockCharacterRepo.toggle_character_track(user_id, character_id, false)
else
# Just track
MockCharacterRepo.toggle_character_track(user_id, character_id, true)
end
case result do
{:ok, updated_char} ->
# Get all tracking data to check consistency
{:ok, all_tracking} = MockCharacterRepo.get_tracking_data(user_id)
# Check for inconsistencies (characters followed but not tracked)
MockTrackingUtils.check_tracking_consistency(all_tracking)
# Return the updated character
{:ok, %{data: updated_char}}
{:error, reason} ->
{:error, :internal_server_error, "Failed to update character: #{reason}"}
end
else
{:error, msg} ->
{:error, :bad_request, msg}
end
end
# Simplified version of list_tracking from CharacterAPIController
def list_tracking(user_id) do
case MockCharacterRepo.get_tracking_data(user_id) do
{:ok, tracking_data} ->
# Check for inconsistencies
checked_data = MockTrackingUtils.check_tracking_consistency(tracking_data)
# Return the data
{:ok, %{data: checked_data}}
{:error, reason} ->
{:error, :internal_server_error, "Failed to get tracking data: #{reason}"}
end
end
end
# Setup for tests
setup do
# Initialize the mock storage
MockCharacterRepo.init_storage()
:ok
end
describe "toggle_follow/2" do
test "follows a character successfully" do
params = %{"character_id" => "345678", "follow" => "true"}
result = MockCharacterAPIController.toggle_follow(params, "user1")
assert {:ok, %{data: data}} = result
assert data.eve_id == "345678"
assert data.name == "Character Three"
assert data.followed == true
assert data.tracked == false
# This should have created an inconsistency (followed but not tracked)
# The check_tracking_consistency function should have logged a warning
end
test "unfollows a character successfully" do
params = %{"character_id" => "123456", "follow" => "false"}
result = MockCharacterAPIController.toggle_follow(params, "user1")
assert {:ok, %{data: data}} = result
assert data.eve_id == "123456"
assert data.followed == false
assert data.tracked == true
end
test "returns error when character_id is missing" do
params = %{"follow" => "true"}
result = MockCharacterAPIController.toggle_follow(params, "user1")
assert {:error, :bad_request, message} = result
assert message == "Missing required param: character_id"
end
test "returns error when follow is not a valid boolean" do
params = %{"character_id" => "123456", "follow" => "not-a-boolean"}
result = MockCharacterAPIController.toggle_follow(params, "user1")
assert {:error, :bad_request, message} = result
assert message =~ "Invalid boolean value"
end
end
describe "toggle_track/2" do
test "tracks a character successfully" do
params = %{"character_id" => "345678", "track" => "true"}
result = MockCharacterAPIController.toggle_track(params, "user1")
assert {:ok, %{data: data}} = result
assert data.eve_id == "345678"
assert data.tracked == true
end
test "untracks and unfollows a character" do
# First, make sure the character is followed
follow_params = %{"character_id" => "123456", "follow" => "true"}
MockCharacterAPIController.toggle_follow(follow_params, "user1")
# Now untrack the character
params = %{"character_id" => "123456", "track" => "false"}
result = MockCharacterAPIController.toggle_track(params, "user1")
assert {:ok, %{data: data}} = result
assert data.eve_id == "123456"
assert data.tracked == false
assert data.followed == false # Should also be unfollowed
end
test "returns error when character_id is missing" do
params = %{"track" => "true"}
result = MockCharacterAPIController.toggle_track(params, "user1")
assert {:error, :bad_request, message} = result
assert message == "Missing required param: character_id"
end
test "returns error when track is not a valid boolean" do
params = %{"character_id" => "123456", "track" => "not-a-boolean"}
result = MockCharacterAPIController.toggle_track(params, "user1")
assert {:error, :bad_request, message} = result
assert message =~ "Invalid boolean value"
end
end
describe "list_tracking/1" do
test "returns tracking data for a user" do
result = MockCharacterAPIController.list_tracking("user1")
assert {:ok, %{data: data}} = result
assert length(data) == 3
# Check that the data contains the expected characters
char_one = Enum.find(data, fn char -> char.eve_id == "123456" end)
assert char_one.name == "Character One"
assert char_one.tracked == true
assert char_one.followed == true
char_two = Enum.find(data, fn char -> char.eve_id == "234567" end)
assert char_two.name == "Character Two"
assert char_two.tracked == true
assert char_two.followed == false
end
test "returns empty list for user with no tracking data" do
result = MockCharacterAPIController.list_tracking("non-existent-user")
assert {:ok, %{data: data}} = result
assert data == []
end
end
end