Files
Echo-Tavern/tests/test_card_modes.py
2026-03-08 09:16:44 +08:00

271 lines
9.7 KiB
Python

from __future__ import annotations
import json
import pytest
def test_non_fog_card_keeps_custom_opening_scene(room_manager, persistence):
card = persistence.get_game_card('sunken_arcade')
assert card is not None
created = room_manager.create_room('Host', game_card=card)
room_id = created['room_id']
host_id = created['player_id']
room_manager.start_game(room_id, host_id)
snapshot = room_manager.get_snapshot(room_id)
assert snapshot['game_card']['card_id'] == 'sunken_arcade'
assert snapshot['game_state']['scene_id'] == 'opening'
assert '沉没游乐城' in snapshot['game_state']['global_flags']['game_card_title']
assert isinstance(snapshot['game_state']['global_flags'].get('card_content_pack'), dict)
def test_game_ended_blocks_chat(room_manager):
created = room_manager.create_room('Host')
room_id = created['room_id']
host_id = created['player_id']
room_manager.start_game(room_id, host_id)
room = room_manager.persistence.get_room(room_id)
state = room['game_state']
state['game_phase'] = 'ended'
room_manager.state_manager.set_room_state(room_id, 'ended', state)
with pytest.raises(ValueError, match='game_ended'):
room_manager.submit_chat(room_id, host_id, 'hello')
def test_custom_card_end_conditions_can_end_game(room_manager, persistence):
custom_card = persistence.create_game_card(
title='战斗测试卡',
subtitle='',
summary='用于验证自定义结束条件',
cover_url='/static/img/cards/fogharbor.svg',
opening_text='战斗即将开始。',
starter_goals=['提交一次战斗行动'],
state_schema={
'metrics': [
{'key': 'hp', 'label': '队伍生命', 'min': 0, 'max': 100, 'initial': 50},
{'key': 'rage', 'label': '敌方怒气', 'min': 0, 'max': 100, 'initial': 10},
],
'ui': {
'status_tracks': [
{'key': 'hp', 'label': '队伍生命', 'min': 0, 'max': 100, 'color': '#ff5a4d'},
{'key': 'rage', 'label': '敌方怒气', 'min': 0, 'max': 100, 'color': '#6aa3ff'},
]
},
},
content_pack={
'end_conditions': [
{
'id': 'team_wiped',
'metric': 'hp',
'op': '<=',
'value': 0,
'ending_state': 'defeat',
'ending_summary': '生命归零,队伍倒下。',
}
]
},
creator_user_id='test-user',
)
created = room_manager.create_room('Host', game_card=custom_card)
room_id = created['room_id']
host_id = created['player_id']
room_manager.start_game(room_id, host_id)
room_manager.deepseek_client.chat_json = lambda *args, **kwargs: {
'ok': True,
'content': json.dumps(
{
'narration': '敌方重击命中,队伍承受严重伤害。',
'npc_dialogues': [],
'state_updates': {
'danger_delta': 0,
'add_clues': [],
'set_flags': {'hp_delta': -80},
'npc_changes': {},
'inventory_add': {},
},
'private_player_updates': [],
'choices': ['紧急撤离'],
'should_end_scene': False,
'should_end_game': False,
'ending_summary': None,
},
ensure_ascii=False,
),
}
room_manager.submit_action(room_id, host_id, '发起正面进攻')
room_manager.resolve_round(room_id, host_id)
snapshot = room_manager.get_snapshot(room_id)
state = snapshot['game_state']
assert state['game_phase'] == 'ended'
assert state['ending_state'] == 'defeat'
assert state['global_flags'].get('ending_summary') == '生命归零,队伍倒下。'
def test_custom_card_scope_supports_per_player_metrics_and_shared_inventory(room_manager, persistence):
card = persistence.create_game_card(
title='作用域测试卡',
subtitle='',
summary='验证 shared/per_player 作用域',
cover_url='/static/img/cards/fogharbor.svg',
opening_text='你们踏入测试场。',
starter_goals=['提交行动'],
state_schema={
'metrics': [
{'key': 'hp', 'label': '生命', 'min': 0, 'max': 100, 'initial': 50},
],
'data_scope': {
'metrics': 'per_player',
'inventory': 'shared',
'clues': 'shared',
},
'visibility': {
'show_all_player_metrics': True,
'show_all_player_inventory': True,
},
'ui': {
'status_tracks': [
{'key': 'hp', 'label': '生命', 'min': 0, 'max': 100, 'color': '#ff5a4d'},
]
},
},
content_pack={},
creator_user_id='scope-user',
)
created = room_manager.create_room('Host', game_card=card)
room_id = created['room_id']
host_id = created['player_id']
joined, _ = room_manager.join_room(room_id, 'Mate')
mate_id = joined['player_id']
room_manager.set_ready(room_id, mate_id, True)
room_manager.start_game(room_id, host_id)
room_manager.deepseek_client.chat_json = lambda *args, **kwargs: {
'ok': True,
'content': json.dumps(
{
'narration': '敌人命中主攻位,队伍拿到补给。',
'npc_dialogues': [],
'state_updates': {
'danger_delta': 0,
'add_clues': [],
'set_flags': {'hp_delta': -10},
'npc_changes': {},
'inventory_add': {host_id: ['repair_kit']},
},
'private_player_updates': [],
'choices': ['继续推进'],
'should_end_scene': False,
'should_end_game': False,
'ending_summary': None,
},
ensure_ascii=False,
),
}
room_manager.submit_action(room_id, host_id, '吸引火力')
room_manager.resolve_round(room_id, host_id)
snapshot = room_manager.get_snapshot(room_id)
game_state = snapshot['game_state']
by_player = game_state['global_flags'].get('metric_values_by_player', {})
assert by_player[host_id]['hp'] == 40
assert by_player[mate_id]['hp'] == 50
assert 'repair_kit' in game_state['global_flags'].get('shared_inventory', [])
def test_inventory_remove_consumes_used_item(room_manager):
created = room_manager.create_room('Host')
room_id = created['room_id']
host_id = created['player_id']
room_manager.start_game(room_id, host_id)
room = room_manager.persistence.get_room(room_id)
state = room['game_state']
state['inventory_by_player'][host_id] = ['silver_bell', 'torn_manifest']
room_manager.state_manager.set_room_state(room_id, 'running', state)
room_manager.deepseek_client.chat_json = lambda *args, **kwargs: {
'ok': True,
'content': json.dumps(
{
'narration': '你敲响银铃吸引注意,铃体裂开彻底损毁。',
'npc_dialogues': [],
'state_updates': {
'danger_delta': 0,
'add_clues': [],
'set_flags': {},
'npc_changes': {},
'inventory_add': {},
'inventory_remove': {host_id: ['silver_bell']},
},
'private_player_updates': [],
'choices': ['继续调查'],
'should_end_scene': False,
'should_end_game': False,
'ending_summary': None,
},
ensure_ascii=False,
),
}
room_manager.submit_action(room_id, host_id, '使用银铃引开旅店老板')
room_manager.resolve_round(room_id, host_id)
snapshot = room_manager.get_snapshot(room_id)
inventory = snapshot['game_state']['inventory_by_player'][host_id]
assert 'silver_bell' not in inventory
assert 'torn_manifest' in inventory
def test_inventory_kept_when_ai_does_not_emit_remove(room_manager):
created = room_manager.create_room('Host')
room_id = created['room_id']
host_id = created['player_id']
room_manager.start_game(room_id, host_id)
room = room_manager.persistence.get_room(room_id)
state = room['game_state']
state['inventory_by_player'][host_id] = ['silver_bell']
room_manager.state_manager.set_room_state(room_id, 'running', state)
room_manager.deepseek_client.chat_json = lambda *args, **kwargs: {
'ok': True,
'content': json.dumps(
{
'narration': '你使用银铃制造声响,守卫被成功引开。',
'npc_dialogues': [],
'state_updates': {
'danger_delta': 0,
'add_clues': [],
'set_flags': {},
'npc_changes': {},
'inventory_add': {},
'inventory_remove': {},
},
'private_player_updates': [],
'choices': ['继续潜入'],
'should_end_scene': False,
'should_end_game': False,
'ending_summary': None,
},
ensure_ascii=False,
),
}
room_manager.submit_action(room_id, host_id, '使用银铃吸引注意')
room_manager.resolve_round(room_id, host_id)
snapshot = room_manager.get_snapshot(room_id)
inventory = snapshot['game_state']['inventory_by_player'][host_id]
assert 'silver_bell' in inventory