Spaces:
Running on Zero
Running on Zero
| import json | |
| from mafia.engine.reducers import add_message, create_game, resolve_night, start_discussion, submit_night_action | |
| from mafia.engine.state import Role, Team | |
| from mafia.engine.views import legal_actions_for, private_event_dict, private_view, public_view | |
| def test_public_view_does_not_leak_living_roles(): | |
| state = create_game(seed=42) | |
| view = public_view(state) | |
| serialized = json.dumps(view) | |
| assert "mafia_team" not in serialized | |
| for pid, player in state.players.items(): | |
| if player.alive: | |
| assert view["players"][pid]["revealed_role"] is None | |
| def test_mafia_private_view_knows_only_mafia_team(): | |
| state = create_game(seed=42) | |
| mafia = next(pid for pid, player in state.players.items() if player.role == Role.MAFIA) | |
| view = private_view(state, mafia) | |
| assert "mafia_team" in view["private_info"] | |
| assert len(view["private_info"]["mafia_team"]) == 2 | |
| def test_detective_result_is_private_not_public(): | |
| state = create_game(seed=44) | |
| detective = next(pid for pid, player in state.players.items() if player.role == Role.DETECTIVE) | |
| mafia = next(pid for pid, player in state.players.items() if player.role == Role.MAFIA) | |
| submit_night_action(state, detective, "check", mafia) | |
| resolve_night(state) | |
| assert mafia in private_view(state, detective)["private_info"]["investigations"] | |
| assert "is_mafia" not in json.dumps(public_view(state)) | |
| def test_dead_roles_stay_hidden_until_game_over(): | |
| state = create_game(seed=18) | |
| mafia = next(pid for pid, player in state.players.items() if player.role == Role.MAFIA) | |
| victim = next(pid for pid, player in state.players.items() if player.team == Team.TOWN) | |
| assert submit_night_action(state, mafia, "kill", victim) | |
| resolve_night(state) | |
| view = public_view(state) | |
| assert view["players"][victim]["alive"] is False | |
| assert view["players"][victim]["revealed_role"] is None | |
| assert state.players[victim].role.value not in json.dumps(view["events"]) | |
| def test_vote_start_is_moderator_owned(): | |
| state = create_game(seed=20) | |
| resolve_night(state) | |
| start_discussion(state) | |
| for pid in state.alive_players(): | |
| assert "start_vote" not in legal_actions_for(state, pid) | |
| def test_mafia_consensus_event_is_private_to_mafia(): | |
| state = create_game(seed=42) | |
| mafia = [pid for pid, player in state.players.items() if player.role == Role.MAFIA] | |
| town = [pid for pid, player in state.players.items() if player.team == Team.TOWN] | |
| submit_night_action(state, mafia[0], "kill", town[0]) | |
| submit_night_action(state, mafia[1], "kill", town[1]) | |
| resolve_night(state) | |
| event = next(event for event in state.events if event.type == "mafia_consensus") | |
| mafia_view = private_event_dict(state, event, mafia[0]) | |
| town_view = private_event_dict(state, event, town[0]) | |
| assert mafia_view is not None | |
| assert mafia_view["payload"]["private"] is True | |
| assert town_view is None | |
| def test_targeted_moderator_cue_is_private_to_target_only(): | |
| state = create_game(seed=42) | |
| target = "p2" | |
| other = "p3" | |
| add_message( | |
| state, | |
| "moderator", | |
| "Nora, give one concrete read.", | |
| speech_act="floor_cue", | |
| emotion="neutral", | |
| source="ttt", | |
| metadata={"target": target, "message_type": "evidence"}, | |
| ) | |
| event = state.events[-1] | |
| assert "Nora, give one concrete read." not in json.dumps(public_view(state)) | |
| assert private_event_dict(state, event, other) is None | |
| target_event = private_event_dict(state, event, target) | |
| assert target_event is not None | |
| assert target_event["type"] == "private_moderator_cue" | |
| assert target_event["payload"]["private"] is True | |