add pytest
This commit is contained in:
56
tests/conftest.py
Normal file
56
tests/conftest.py
Normal file
@@ -0,0 +1,56 @@
|
||||
import pytest
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from src.classes.map import Map
|
||||
from src.classes.tile import TileType
|
||||
from src.classes.world import World
|
||||
from src.classes.calendar import Month, Year, create_month_stamp
|
||||
from src.classes.avatar import Avatar, Gender
|
||||
from src.classes.age import Age
|
||||
from src.classes.cultivation import Realm
|
||||
from src.utils.id_generator import get_avatar_id
|
||||
from src.classes.name import get_random_name
|
||||
|
||||
@pytest.fixture
|
||||
def base_map():
|
||||
"""创建一个 10x10 的全平原地图"""
|
||||
width, height = 10, 10
|
||||
game_map = Map(width=width, height=height)
|
||||
for x in range(width):
|
||||
for y in range(height):
|
||||
game_map.create_tile(x, y, TileType.PLAIN)
|
||||
return game_map
|
||||
|
||||
@pytest.fixture
|
||||
def base_world(base_map):
|
||||
"""创建一个基于 base_map 的世界,时间为 Year 1, Jan"""
|
||||
return World(map=base_map, month_stamp=create_month_stamp(Year(1), Month.JANUARY))
|
||||
|
||||
from src.classes.root import Root
|
||||
from src.classes.alignment import Alignment
|
||||
|
||||
@pytest.fixture
|
||||
def dummy_avatar(base_world):
|
||||
"""创建一个位于 (0,0) 的标准男性练气期角色"""
|
||||
# 确保ID生成器重置或不冲突 (get_avatar_id 是随机UUID通常没问题)
|
||||
av = Avatar(
|
||||
world=base_world,
|
||||
name="TestDummy",
|
||||
id=get_avatar_id(),
|
||||
birth_month_stamp=create_month_stamp(Year(2000), Month.JANUARY),
|
||||
age=Age(20, Realm.Qi_Refinement),
|
||||
gender=Gender.MALE,
|
||||
pos_x=0,
|
||||
pos_y=0,
|
||||
root=Root.GOLD, # 固定灵根
|
||||
personas=[], # 清空特质,避免随机效果
|
||||
alignment=Alignment.RIGHTEOUS # 固定阵营
|
||||
)
|
||||
|
||||
# 赋予一个 Mock 武器,防止 get_avatar_info 报错
|
||||
av.weapon = MagicMock()
|
||||
av.weapon.get_detailed_info.return_value = "测试木剑(凡品)"
|
||||
av.weapon_proficiency = 0.0
|
||||
|
||||
return av
|
||||
|
||||
@@ -1,32 +1,13 @@
|
||||
from src.utils.id_generator import get_avatar_id
|
||||
from src.classes.avatar import Avatar, Gender
|
||||
from src.classes.calendar import Month, Year, MonthStamp, create_month_stamp
|
||||
from src.classes.world import World
|
||||
from src.classes.map import Map
|
||||
from src.classes.tile import TileType
|
||||
from src.classes.age import Age
|
||||
from src.classes.cultivation import Realm
|
||||
from src.classes.name import get_random_name
|
||||
|
||||
def test_basic():
|
||||
from src.classes.avatar import Avatar
|
||||
# test_basic is now simplified using fixtures
|
||||
def test_basic(base_world, dummy_avatar):
|
||||
"""
|
||||
测试整个基础代码能不能run起来
|
||||
使用 conftest.py 中的 fixtures 简化设置
|
||||
"""
|
||||
map = Map(width=2, height=2)
|
||||
for x in range(2):
|
||||
for y in range(2):
|
||||
map.create_tile(x, y, TileType.PLAIN)
|
||||
|
||||
world = World(map=map, month_stamp=create_month_stamp(Year(1), Month.JANUARY))
|
||||
|
||||
avatar = Avatar(
|
||||
world=world,
|
||||
name=get_random_name(Gender.MALE),
|
||||
id=get_avatar_id(),
|
||||
birth_month_stamp=create_month_stamp(Year(2000), Month.JANUARY),
|
||||
age=Age(20, Realm.Qi_Refinement),
|
||||
gender=Gender.MALE
|
||||
)
|
||||
|
||||
|
||||
|
||||
# fixtures 已经创建了 map, world, avatar
|
||||
assert base_world.map.width == 10
|
||||
assert base_world.map.height == 10
|
||||
|
||||
assert dummy_avatar.world == base_world
|
||||
assert dummy_avatar.age.age == 20
|
||||
|
||||
@@ -16,11 +16,11 @@ from src.sim.load.load_game import load_game
|
||||
from src.utils.id_generator import get_avatar_id
|
||||
from src.utils.config import CONFIG
|
||||
|
||||
# Helper to create a simple map
|
||||
def create_simple_map():
|
||||
m = Map(width=5, height=5) # Slightly larger to be safe
|
||||
for x in range(5):
|
||||
for y in range(5):
|
||||
# Helper to create a simple map (aligned with conftest base_map logic)
|
||||
def create_test_map():
|
||||
m = Map(width=10, height=10)
|
||||
for x in range(10):
|
||||
for y in range(10):
|
||||
m.create_tile(x, y, TileType.PLAIN)
|
||||
return m
|
||||
|
||||
@@ -37,7 +37,7 @@ def test_save_load_cycle(temp_save_dir):
|
||||
"""
|
||||
# 1. Setup World
|
||||
# Create a deterministic map for testing
|
||||
game_map = create_simple_map()
|
||||
game_map = create_test_map()
|
||||
|
||||
# Set a specific time
|
||||
start_year = Year(100)
|
||||
@@ -45,6 +45,7 @@ def test_save_load_cycle(temp_save_dir):
|
||||
month_stamp = create_month_stamp(start_year, start_month)
|
||||
|
||||
world = World(map=game_map, month_stamp=month_stamp)
|
||||
|
||||
|
||||
# 2. Add an Avatar
|
||||
avatar_id = get_avatar_id()
|
||||
@@ -92,7 +93,7 @@ def test_save_load_cycle(temp_save_dir):
|
||||
# but since it's inside, we rely on sys.modules or patch the target module path.
|
||||
# The import in load_game.py is: from src.run.load_map import load_cultivation_world_map
|
||||
|
||||
with patch('src.run.load_map.load_cultivation_world_map', return_value=create_simple_map()):
|
||||
with patch('src.run.load_map.load_cultivation_world_map', return_value=create_test_map()):
|
||||
# We also need to be careful about 'sects_by_id' if we had sects, but we don't.
|
||||
loaded_world, loaded_sim, loaded_sects = load_game(save_path)
|
||||
|
||||
@@ -126,7 +127,7 @@ def test_save_load_with_relations(temp_save_dir):
|
||||
"""
|
||||
Test saving and loading avatars with relationships.
|
||||
"""
|
||||
game_map = create_simple_map()
|
||||
game_map = create_test_map()
|
||||
world = World(map=game_map, month_stamp=create_month_stamp(Year(1), Month.JANUARY))
|
||||
|
||||
# Create two avatars
|
||||
@@ -148,7 +149,7 @@ def test_save_load_with_relations(temp_save_dir):
|
||||
save_path = temp_save_dir / "test_relation.json"
|
||||
save_game(world, sim, [], save_path)
|
||||
|
||||
with patch('src.run.load_map.load_cultivation_world_map', return_value=create_simple_map()):
|
||||
with patch('src.run.load_map.load_cultivation_world_map', return_value=create_test_map()):
|
||||
l_world, _, _ = load_game(save_path)
|
||||
|
||||
l_av1 = l_world.avatar_manager.avatars[av1.id]
|
||||
|
||||
@@ -48,17 +48,13 @@ def test_auto_promote():
|
||||
assert should_auto_promote(Realm.Qi_Refinement, Realm.Qi_Refinement) == False
|
||||
|
||||
|
||||
def test_avatar_sect_rank_assignment():
|
||||
|
||||
def test_avatar_sect_rank_assignment(base_world):
|
||||
"""测试avatar创建时宗门职位分配"""
|
||||
from src.run.load_map import load_cultivation_world_map
|
||||
game_map = load_cultivation_world_map()
|
||||
world = World(
|
||||
map=game_map,
|
||||
month_stamp=MonthStamp(100 * 12),
|
||||
)
|
||||
# 使用 base_world fixture,不需要 load_cultivation_world_map
|
||||
|
||||
# 创建多个avatar
|
||||
avatars_dict = make_avatars(world, count=20, current_month_stamp=MonthStamp(100 * 12))
|
||||
avatars_dict = make_avatars(base_world, count=20, current_month_stamp=MonthStamp(100 * 12))
|
||||
avatars = list(avatars_dict.values())
|
||||
|
||||
# 检查所有有宗门的avatar都有职位
|
||||
@@ -75,17 +71,11 @@ def test_avatar_sect_rank_assignment():
|
||||
assert avatar.sect_rank is None, f"{avatar.name} 散修不应该有职位"
|
||||
|
||||
|
||||
def test_patriarch_uniqueness():
|
||||
def test_patriarch_uniqueness(base_world):
|
||||
"""测试每个宗门只有一个掌门"""
|
||||
from src.run.load_map import load_cultivation_world_map
|
||||
game_map = load_cultivation_world_map()
|
||||
world = World(
|
||||
map=game_map,
|
||||
month_stamp=MonthStamp(100 * 12),
|
||||
)
|
||||
|
||||
# 创建足够多的avatar
|
||||
avatars_dict = make_avatars(world, count=50, current_month_stamp=MonthStamp(100 * 12))
|
||||
avatars_dict = make_avatars(base_world, count=50, current_month_stamp=MonthStamp(100 * 12))
|
||||
avatars = list(avatars_dict.values())
|
||||
|
||||
# 统计每个宗门的掌门数量
|
||||
@@ -102,16 +92,10 @@ def test_patriarch_uniqueness():
|
||||
assert len(patriarchs) <= 1, f"宗门 {sect_id} 有多个掌门: {patriarchs}"
|
||||
|
||||
|
||||
def test_sect_str_display():
|
||||
def test_sect_str_display(base_world):
|
||||
"""测试宗门信息显示"""
|
||||
from src.run.load_map import load_cultivation_world_map
|
||||
game_map = load_cultivation_world_map()
|
||||
world = World(
|
||||
map=game_map,
|
||||
month_stamp=MonthStamp(100 * 12),
|
||||
)
|
||||
|
||||
avatars_dict = make_avatars(world, count=20, current_month_stamp=MonthStamp(100 * 12))
|
||||
avatars_dict = make_avatars(base_world, count=20, current_month_stamp=MonthStamp(100 * 12))
|
||||
avatars = list(avatars_dict.values())
|
||||
|
||||
for avatar in avatars:
|
||||
@@ -127,16 +111,10 @@ def test_sect_str_display():
|
||||
assert rank_name in sect_str
|
||||
|
||||
|
||||
def test_cultivation_breakthrough_promotion():
|
||||
def test_cultivation_breakthrough_promotion(base_world):
|
||||
"""测试突破境界后自动晋升"""
|
||||
from src.run.load_map import load_cultivation_world_map
|
||||
game_map = load_cultivation_world_map()
|
||||
world = World(
|
||||
map=game_map,
|
||||
month_stamp=MonthStamp(100 * 12),
|
||||
)
|
||||
|
||||
avatars_dict = make_avatars(world, count=10, current_month_stamp=MonthStamp(100 * 12))
|
||||
avatars_dict = make_avatars(base_world, count=10, current_month_stamp=MonthStamp(100 * 12))
|
||||
avatars = list(avatars_dict.values())
|
||||
|
||||
# 找一个练气期的宗门弟子
|
||||
@@ -160,6 +138,7 @@ def test_cultivation_breakthrough_promotion():
|
||||
assert target_avatar.sect_rank > old_rank
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
|
||||
|
||||
@@ -1,54 +1,48 @@
|
||||
import random
|
||||
import asyncio
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from src.sim.simulator import Simulator
|
||||
from src.classes.avatar import Avatar, Gender
|
||||
from src.classes.calendar import Month, Year, MonthStamp, create_month_stamp
|
||||
from src.classes.world import World
|
||||
from src.classes.map import Map
|
||||
from src.classes.action.move_to_direction import MoveToDirection
|
||||
from src.classes.tile import TileType
|
||||
from src.classes.action import Move
|
||||
from src.classes.name import get_random_name
|
||||
from src.classes.age import Age
|
||||
from src.classes.cultivation import Realm
|
||||
from src.classes.action_runtime import ActionInstance
|
||||
|
||||
def test_simulator_step_moves_avatar_and_sets_tile(base_world, dummy_avatar):
|
||||
# Set initial position
|
||||
dummy_avatar.pos_x = 1
|
||||
dummy_avatar.pos_y = 1
|
||||
# Ensure tile is updated to initial position (fixture puts it at 0,0)
|
||||
dummy_avatar.tile = base_world.map.get_tile(1, 1)
|
||||
|
||||
def test_simulator_step_moves_avatar_and_sets_tile():
|
||||
# 固定随机种子,确保决定的移动是可预测的
|
||||
random.seed(0)
|
||||
sim = Simulator(base_world)
|
||||
base_world.avatar_manager.avatars[dummy_avatar.id] = dummy_avatar
|
||||
|
||||
# 构建 3x3 地图并填充地块
|
||||
game_map = Map(width=3, height=3)
|
||||
for x in range(3):
|
||||
for y in range(3):
|
||||
game_map.create_tile(x, y, TileType.PLAIN)
|
||||
# Manually assign a MoveToDirection action to avoid relying on LLM
|
||||
action = MoveToDirection(dummy_avatar, base_world)
|
||||
# "East" means x + 1
|
||||
direction = "East"
|
||||
action.start(direction=direction) # Initialize start_monthstamp etc.
|
||||
|
||||
# Wrap in ActionInstance
|
||||
dummy_avatar.current_action = ActionInstance(action=action, params={"direction": direction})
|
||||
|
||||
world = World(map=game_map, month_stamp=create_month_stamp(Year(1), Month.JANUARY))
|
||||
# Mock LLM to avoid external calls or errors
|
||||
with patch("src.sim.simulator.llm_ai") as mock_ai:
|
||||
mock_ai.decide = MagicMock(return_value={})
|
||||
|
||||
print(f"DEBUG: Before step: pos_x={dummy_avatar.pos_x}")
|
||||
# Run step synchronously
|
||||
asyncio.run(sim.step())
|
||||
print(f"DEBUG: After step: pos_x={dummy_avatar.pos_x}")
|
||||
print(f"DEBUG: move_step_length={getattr(dummy_avatar, 'move_step_length', 'Not set')}")
|
||||
print(f"DEBUG: effects={dummy_avatar.effects}")
|
||||
|
||||
# 将角色放在地图中心,避免越界
|
||||
avatar = Avatar(
|
||||
world=world,
|
||||
name=get_random_name(Gender.MALE),
|
||||
id="1",
|
||||
birth_month_stamp=create_month_stamp(Year(2000), Month.JANUARY),
|
||||
age=Age(20, Realm.Qi_Refinement),
|
||||
gender=Gender.MALE,
|
||||
pos_x=1,
|
||||
pos_y=1,
|
||||
)
|
||||
|
||||
|
||||
sim = Simulator(world)
|
||||
world.avatar_manager.avatars["1"] = avatar
|
||||
|
||||
# 执行一步模拟
|
||||
sim.step()
|
||||
|
||||
# 断言位置在边界内
|
||||
assert 0 <= avatar.pos_x < game_map.width
|
||||
assert 0 <= avatar.pos_y < game_map.height
|
||||
|
||||
# 断言 tile 已正确设置且与位置一致
|
||||
assert avatar.tile is not None
|
||||
assert avatar.tile.x == avatar.pos_x
|
||||
assert avatar.tile.y == avatar.pos_y
|
||||
# Assert moved East (x increased by move_step_length)
|
||||
# Current move step for Qi Refinement is 2
|
||||
assert dummy_avatar.pos_x == 3
|
||||
assert dummy_avatar.pos_y == 1
|
||||
|
||||
# Assert tile is updated
|
||||
assert dummy_avatar.tile is not None
|
||||
assert dummy_avatar.tile.x == 3
|
||||
assert dummy_avatar.tile.y == 1
|
||||
|
||||
Reference in New Issue
Block a user