update history pytest

This commit is contained in:
bridge
2026-01-12 00:20:15 +08:00
parent 57cf5ca51a
commit 287f9d2ae4
9 changed files with 131 additions and 50 deletions

View File

@@ -73,18 +73,21 @@ def mock_llm_managers():
with patch("src.sim.simulator.llm_ai") as mock_ai, \
patch("src.sim.simulator.process_avatar_long_term_objective", new_callable=AsyncMock) as mock_lto, \
patch("src.classes.nickname.process_avatar_nickname", new_callable=AsyncMock) as mock_nick, \
patch("src.classes.relation_resolver.RelationResolver.run_batch", new_callable=AsyncMock) as mock_rr:
patch("src.classes.relation_resolver.RelationResolver.run_batch", new_callable=AsyncMock) as mock_rr, \
patch("src.classes.history.HistoryManager.apply_history_influence", new_callable=AsyncMock) as mock_hist:
mock_ai.decide = AsyncMock(return_value={})
mock_lto.return_value = None
mock_nick.return_value = None
mock_rr.return_value = []
mock_hist.return_value = None
yield {
"ai": mock_ai,
"lto": mock_lto,
"nick": mock_nick,
"rr": mock_rr
"rr": mock_rr,
"hist": mock_hist
}
# --- Shared Helpers for Item Creation ---

View File

@@ -89,8 +89,8 @@ async def test_history_influence(base_world):
# Mock _read_csv to return dummy string
manager._read_csv = MagicMock(return_value="dummy,csv,content")
# Mock call_llm_with_template
with patch("src.classes.history.call_llm_with_template", new_callable=AsyncMock) as mock_llm:
# Mock call_llm_with_task_name
with patch("src.classes.history.call_llm_with_task_name", new_callable=AsyncMock) as mock_llm:
mock_llm.return_value = mock_response
# --- Execute ---

View File

@@ -67,7 +67,7 @@ class TestInitStatusEndpoint:
def test_init_status_in_progress(self, client, reset_game_instance):
"""Test init-status during initialization."""
game_instance["init_status"] = "in_progress"
game_instance["init_phase"] = 2
game_instance["init_phase"] = 3
game_instance["init_phase_name"] = "initializing_sects"
game_instance["init_progress"] = 33
game_instance["init_start_time"] = time.time() - 5 # 5 seconds ago
@@ -77,7 +77,7 @@ class TestInitStatusEndpoint:
data = response.json()
assert data["status"] == "in_progress"
assert data["phase"] == 2
assert data["phase"] == 3
assert data["phase_name"] == "initializing_sects"
assert data["progress"] == 33
assert data["elapsed_seconds"] >= 5
@@ -85,7 +85,7 @@ class TestInitStatusEndpoint:
def test_init_status_ready(self, client, reset_game_instance):
"""Test init-status when initialization is complete."""
game_instance["init_status"] = "ready"
game_instance["init_phase"] = 5
game_instance["init_phase"] = 6
game_instance["init_phase_name"] = "generating_initial_events"
game_instance["init_progress"] = 100
@@ -127,29 +127,30 @@ class TestUpdateInitProgress:
def test_update_progress_with_phase_name(self, reset_game_instance):
"""Test updating progress with explicit phase name."""
update_init_progress(2, "initializing_sects")
update_init_progress(3, "initializing_sects")
assert game_instance["init_phase"] == 2
assert game_instance["init_phase"] == 3
assert game_instance["init_phase_name"] == "initializing_sects"
assert game_instance["init_progress"] == 20
assert game_instance["init_progress"] == 40
def test_update_progress_without_phase_name(self, reset_game_instance):
"""Test updating progress uses default phase name from mapping."""
update_init_progress(3)
update_init_progress(4)
assert game_instance["init_phase"] == 3
assert game_instance["init_phase"] == 4
assert game_instance["init_phase_name"] == "generating_avatars"
assert game_instance["init_progress"] == 30
assert game_instance["init_progress"] == 55
def test_all_phase_names_mapped(self):
"""Test all phases have corresponding names."""
expected_phases = {
0: "scanning_assets",
1: "loading_map",
2: "initializing_sects",
3: "generating_avatars",
4: "checking_llm",
5: "generating_initial_events",
2: "processing_history",
3: "initializing_sects",
4: "generating_avatars",
5: "checking_llm",
6: "generating_initial_events",
}
assert INIT_PHASE_NAMES == expected_phases
@@ -165,7 +166,8 @@ class TestNewGameEndpoint:
"init_npc_num": 10,
"sect_num": 2,
"protagonist": "none",
"npc_awakening_rate_per_month": 0.01
"npc_awakening_rate_per_month": 0.01,
"world_history": "Some history"
}
response = client.post("/api/game/start", json=payload)
@@ -221,7 +223,7 @@ class TestReinitEndpoint:
game_instance["sim"] = MagicMock()
game_instance["init_status"] = "error"
game_instance["init_error"] = "Some error"
game_instance["init_phase"] = 3
game_instance["init_phase"] = 4
game_instance["init_progress"] = 50
with patch.object(main, 'init_game_async', new_callable=AsyncMock):
@@ -262,7 +264,7 @@ class TestMapAndStateAPIDuringInit:
game_instance["world"] = mock_world
game_instance["init_status"] = "in_progress"
game_instance["init_phase"] = 4
game_instance["init_phase"] = 5
game_instance["init_phase_name"] = "checking_llm"
# The /api/map endpoint should work.
@@ -280,7 +282,7 @@ class TestMapAndStateAPIDuringInit:
game_instance["world"] = mock_world
game_instance["init_status"] = "in_progress"
game_instance["init_phase"] = 5
game_instance["init_phase"] = 6
game_instance["init_phase_name"] = "generating_initial_events"
response = client.get("/api/state")
@@ -291,7 +293,7 @@ class TestInitGameAsync:
"""Tests for the async initialization flow."""
@pytest.mark.asyncio
async def test_init_sets_status_to_in_progress(self, reset_game_instance):
async def test_init_sets_status_to_in_progress(self, reset_game_instance, mock_llm_managers):
"""Test initialization sets status to in_progress immediately."""
with patch.object(main, 'scan_avatar_assets'), \
patch.object(main, 'load_cultivation_world_map') as mock_load_map, \
@@ -317,7 +319,7 @@ class TestInitGameAsync:
await task # Let it complete.
@pytest.mark.asyncio
async def test_init_error_sets_error_status(self, reset_game_instance):
async def test_init_error_sets_error_status(self, reset_game_instance, mock_llm_managers):
"""Test initialization error sets status to error."""
with patch.object(main, 'scan_avatar_assets', side_effect=Exception("Test error")):
await main.init_game_async()
@@ -326,7 +328,7 @@ class TestInitGameAsync:
assert "Test error" in game_instance["init_error"]
@pytest.mark.asyncio
async def test_init_completes_with_ready_status(self, reset_game_instance):
async def test_init_completes_with_ready_status(self, reset_game_instance, mock_llm_managers):
"""Test successful initialization sets status to ready."""
with patch.object(main, 'scan_avatar_assets'), \
patch.object(main, 'load_cultivation_world_map') as mock_load_map, \
@@ -355,7 +357,7 @@ class TestInitGameAsync:
assert game_instance["init_progress"] == 100
@pytest.mark.asyncio
async def test_init_records_llm_failure(self, reset_game_instance):
async def test_init_records_llm_failure(self, reset_game_instance, mock_llm_managers):
"""Test LLM check failure is recorded but doesn't stop initialization."""
with patch.object(main, 'scan_avatar_assets'), \
patch.object(main, 'load_cultivation_world_map') as mock_load_map, \
@@ -387,7 +389,47 @@ class TestInitGameAsync:
assert game_instance["llm_error_message"] == "API key invalid"
@pytest.mark.asyncio
async def test_init_pauses_after_initial_events(self, reset_game_instance):
async def test_init_calls_history_manager(self, reset_game_instance, mock_llm_managers):
"""Test initialization calls HistoryManager when history is present."""
with patch.object(main, 'scan_avatar_assets'), \
patch.object(main, 'load_cultivation_world_map') as mock_load_map, \
patch.object(main, 'check_llm_connectivity', return_value=(True, "")), \
patch('src.server.main.World') as mock_world_class, \
patch('src.server.main.Simulator') as mock_sim_class, \
patch('src.server.main.sects_by_id', {}), \
patch('src.server.main.CONFIG') as mock_config, \
patch('src.server.main.HistoryManager') as mock_history_class:
mock_config.game.sect_num = 0
mock_config.game.init_npc_num = 0
mock_config.avatar.protagonist = "none"
mock_config.game.world_history = "Ancient times..."
mock_map = MagicMock()
mock_load_map.return_value = mock_map
mock_world = MagicMock()
mock_world.avatar_manager.avatars = {}
mock_world_class.create_with_db.return_value = mock_world
mock_sim = MagicMock()
mock_sim.step = AsyncMock()
mock_sim_class.return_value = mock_sim
# Use the mock from fixture if available, but here we patch HistoryManager class specifically
# to verify constructor call.
mock_history_mgr = MagicMock()
# We want to verify that apply_history_influence is called.
# Even if mock_llm_managers mocks the underlying method on the real class,
# here we mock the whole class, so we get a fresh mock instance.
mock_history_mgr.apply_history_influence = AsyncMock()
mock_history_class.return_value = mock_history_mgr
await main.init_game_async()
mock_history_class.assert_called_once_with(mock_world)
mock_history_mgr.apply_history_influence.assert_called_once_with("Ancient times...")
@pytest.mark.asyncio
async def test_init_pauses_after_initial_events(self, reset_game_instance, mock_llm_managers):
"""Test game is paused after generating initial events."""
with patch.object(main, 'scan_avatar_assets'), \
patch.object(main, 'load_cultivation_world_map') as mock_load_map, \