dreamland / tests /test_player.py
panda-lsy
feat: per-user data isolation, fix model name, clean gitignore
820bd65
Raw
History Blame Contribute Delete
6.6 kB
"""Tests for Player model and save slot system."""
import tempfile
from pathlib import Path
from unittest.mock import patch
from core.player import (
Player, load_player, save_player, get_active_slot, set_active_slot,
list_save_slots, switch_slot, delete_slot, rename_player,
_slot_path, _get_slots_dir, _get_active_slot_file,
)
def _make_temp_player(tmpdir):
"""Create a Player with temp paths."""
p = Player()
p.name = "测试角色"
p.level = 3
p.xp = 150
p.xp_to_next = 300
return p
def test_player_xp_and_level_up():
"""Test XP addition and level-up mechanics."""
p = Player()
p.level = 1
p.xp = 0
p.xp_to_next = 100
# Add XP without leveling
result = p.add_xp(50)
assert result is None
assert p.xp == 50
# Add XP that triggers level up
result = p.add_xp(60)
assert result is not None
assert "Lv.2" in result
assert p.level == 2
assert p.xp == 10 # 110 - 100
assert p.xp_to_next == 200 # level * 100
assert p.max_hp == 110 # +10
assert p.hp == 110 # Full heal on level up
def test_player_hp_mp():
"""Test HP/MP heal and damage."""
p = Player()
p.hp = 50
p.max_hp = 100
p.mp = 30
p.max_mp = 100
p.heal(hp=20, mp=10)
assert p.hp == 70
assert p.mp == 40
# Can't exceed max
p.heal(hp=100, mp=100)
assert p.hp == 100
assert p.mp == 100
p.take_damage(80)
assert p.hp == 20
# Can't go below 0
p.take_damage(999)
assert p.hp == 0
def test_player_inventory():
"""Test item add/remove/has."""
p = Player()
p.add_item("水晶碎片", "发光的碎片", "key")
assert p.has_item("水晶碎片")
assert p.inventory_count == 1
# Stack
p.add_item("水晶碎片", "发光的碎片", "key")
assert p.inventory_count == 2
# Remove one
assert p.remove_item("水晶碎片")
assert p.inventory_count == 1
# Remove last
assert p.remove_item("水晶碎片")
assert not p.has_item("水晶碎片")
assert p.inventory_count == 0
# Remove non-existent
assert not p.remove_item("不存在的物品")
def test_player_abilities():
"""Test ability learning."""
p = Player()
p.learn_ability("飞行能力", "寻找水晶碎片")
assert len(p.abilities) == 1
assert p.abilities[0]["name"] == "飞行能力"
# Don't duplicate
p.learn_ability("飞行能力", "other")
assert len(p.abilities) == 1
def test_player_quest_completion():
"""Test quest completion tracking."""
p = Player()
assert not p.is_quest_completed("q1")
p.complete_quest("q1")
assert p.is_quest_completed("q1")
# Don't duplicate
p.complete_quest("q1")
assert p.completed_quests.count("q1") == 1
def test_player_serialization():
"""Test to_dict/from_dict roundtrip."""
p = Player()
p.name = "存档测试"
p.level = 5
p.stats["智慧"] = 18
p.add_item("星辰之书", "古老典籍", "key")
p.learn_ability("深海呼吸", "寻找命运之书")
p.complete_quest("q_abc")
d = p.to_dict()
p2 = Player.from_dict(d)
assert p2.name == "存档测试"
assert p2.level == 5
assert p2.stats["智慧"] == 18
assert len(p2.inventory) == 1
assert p2.inventory[0]["name"] == "星辰之书"
assert len(p2.abilities) == 1
assert p2.completed_quests == ["q_abc"]
def test_player_percent_properties():
"""Test HP/MP/XP percent calculations."""
p = Player()
p.hp = 75
p.max_hp = 100
p.mp = 50
p.max_mp = 100
p.xp = 30
p.xp_to_next = 100
assert p.hp_percent == 75
assert p.mp_percent == 50
assert p.xp_percent == 30
def test_save_slot_persistence():
"""Test save/load with slot system."""
with tempfile.TemporaryDirectory() as tmpdir:
with patch("core.player.PLAYER_FILE", Path(tmpdir) / "player.json"), \
patch("core.player._get_slots_dir", lambda: Path(tmpdir) / "saves"), \
patch("core.player._get_active_slot_file", lambda: Path(tmpdir) / "active_slot.txt"):
# Save to slot 0
p = Player()
p.name = "存档A"
p.level = 3
save_player(p)
# Load from slot 0
loaded = load_player()
assert loaded.name == "存档A"
assert loaded.level == 3
# Switch to slot 1
p2 = switch_slot(1)
assert p2.name == "梦境行者" # Default new player
p2.name = "存档B"
p2.level = 7
save_player(p2)
# List slots
slots = list_save_slots()
assert len(slots) == 5
active_slots = [s for s in slots if s["level"] > 0]
assert len(active_slots) == 2
# Switch back to slot 0
p_back = switch_slot(0)
assert p_back.name == "存档A"
assert p_back.level == 3
# Current active should be 0
assert get_active_slot() == 0
def test_save_slot_delete():
"""Test deleting a save slot."""
with tempfile.TemporaryDirectory() as tmpdir:
with patch("core.player.PLAYER_FILE", Path(tmpdir) / "player.json"), \
patch("core.player._get_slots_dir", lambda: Path(tmpdir) / "saves"), \
patch("core.player._get_active_slot_file", lambda: Path(tmpdir) / "active_slot.txt"):
p = Player()
p.name = "待删除"
save_player(p)
assert load_player().name == "待删除"
delete_slot(0)
# Should get default player after delete
fresh = load_player()
assert fresh.name == "梦境行者"
def test_rename_player():
"""Test renaming the current player."""
with tempfile.TemporaryDirectory() as tmpdir:
with patch("core.player.PLAYER_FILE", Path(tmpdir) / "player.json"), \
patch("core.player._get_slots_dir", lambda: Path(tmpdir) / "saves"), \
patch("core.player._get_active_slot_file", lambda: Path(tmpdir) / "active_slot.txt"):
save_player(Player())
rename_player("新名字")
assert load_player().name == "新名字"
if __name__ == "__main__":
test_player_xp_and_level_up()
test_player_hp_mp()
test_player_inventory()
test_player_abilities()
test_player_quest_completion()
test_player_serialization()
test_player_percent_properties()
test_save_slot_persistence()
test_save_slot_delete()
test_rename_player()
print("All player tests passed!")