Skip to content

Commit f31a847

Browse files
authored
Add tests (#14)
1 parent 95c33c3 commit f31a847

File tree

7 files changed

+125
-17
lines changed

7 files changed

+125
-17
lines changed

.github/workflows/test.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# .github/workflows/python-tests.yml
2+
3+
# A descriptive name for your workflow
4+
name: Run Python Tests
5+
6+
# This section defines when the workflow will run
7+
on:
8+
# Run on pushes to the main branch
9+
push:
10+
branches: [ main ]
11+
# Run on pull requests that target the main branch
12+
pull_request:
13+
branches: [ main ]
14+
15+
# This section defines the actual work to be done
16+
jobs:
17+
# We define a single job called "build"
18+
build:
19+
# Use the latest version of Ubuntu as the operating system for our runner
20+
runs-on: ubuntu-latest
21+
22+
# This strategy will run the job against multiple Python versions
23+
strategy:
24+
matrix:
25+
python-version: ["3.11", "3.12"]
26+
27+
# These are the sequential steps the job will perform
28+
steps:
29+
# Step 1: Check out your repository code so the workflow can access it
30+
- name: Checkout repository
31+
uses: actions/checkout@v4
32+
33+
# Step 2: Set up the specific version of Python from our matrix
34+
- name: Set up Python ${{ matrix.python-version }}
35+
uses: actions/setup-python@v5
36+
with:
37+
python-version: ${{ matrix.python-version }}
38+
39+
# Step 3: Install dependencies
40+
# This step should mirror your local setup instructions
41+
- name: Install dependencies
42+
run: |
43+
python -m pip install --upgrade pip
44+
pip install -r requirements.txt
45+
pip install -e .
46+
47+
# Step 4: Run pytest
48+
# We use "python -m pytest" to ensure pathing is handled correctly
49+
- name: Run tests with pytest
50+
run: |
51+
python -m pytest

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ source venv/bin/activate
5454

5555
# This installs the project dependcies into venv Python
5656
pip install -r requirements.txt
57+
pip install -e .
5758
```
5859

5960
Once you have activated the virtual environment and installed requirements, you can run the below commands to run the simulation and backend service.
@@ -81,6 +82,11 @@ uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
8182
```
8283
Then open: http://localhost:8000/docs for interactive Swagger API.
8384

85+
### 🔍 Run tests
86+
```bash
87+
pytest
88+
```
89+
8490

8591
## ✅ Features
8692
- Character model with paranoia, goodwill, intrigue, and disallowed locations

engine/simulation.py

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,33 +33,45 @@ def simulate_day(game_state: GameState):
3333
print(f"🏁 Game Over — {game_state.game_result.replace('_', ' ').title()}")
3434
game_state.day += 1
3535

36-
def main():
37-
parser = argparse.ArgumentParser(description="Run Tragedy Looper simulation.")
38-
parser.add_argument("script_name", help="Name of the script YAML file (without extension)")
39-
parser.add_argument("actions_file", help="Name of the actions YAML file (without extension)")
40-
41-
args = parser.parse_args()
42-
43-
# Assuming your YAML files live in a folder like 'scripts/' and have a '.yaml' extension
44-
script_path = f"scripts/{args.script_name}/script.yaml"
45-
actions_path = f"scripts/{args.script_name}/{args.actions_file}.yaml"
46-
36+
def run_full_simulation(script_path: str, actions_path: str) -> GameState:
37+
"""
38+
Runs a full game simulation and returns the final state.
39+
This function is designed to be testable.
40+
"""
4741
game_state = create_starting_game_state(script_path, actions_path)
42+
4843
while game_state.game_result is None:
4944
if game_state.day > game_state.days_per_loop:
50-
# We've finished a loop. Time to check for victory and reset.
51-
check_victory(game_state) # Final check at loop end
45+
check_victory(game_state)
5246
if game_state.game_result is None:
5347
if game_state.loop_count >= game_state.max_loops:
54-
# We've run out of loops, mastermind wins.
55-
print("⏰ The protagonists have run out of time!")
5648
game_state.game_result = "mastermind_win"
5749
else:
58-
# Reset for the next loop
5950
reset_for_new_loop(game_state)
6051

6152
if game_state.game_result is None:
6253
simulate_day(game_state)
54+
55+
# After the loop, do one final check on the very last day's state
56+
check_victory(game_state)
57+
return game_state
58+
59+
def main():
60+
parser = argparse.ArgumentParser(description="Run Tragedy Looper simulation.")
61+
parser.add_argument("script_name", help="Name of the script YAML file (without extension)")
62+
parser.add_argument("actions_file", help="Name of the actions YAML file (without extension)")
63+
64+
args = parser.parse_args()
65+
66+
# Assuming your YAML files live in a folder like 'scripts/' and have a '.yaml' extension
67+
script_path = f"scripts/{args.script_name}/script.yaml"
68+
actions_path = f"scripts/{args.script_name}/{args.actions_file}.yaml"
69+
70+
final_state = run_full_simulation(script_path, actions_path)
71+
print("\n--- Final Results ---")
72+
if final_state.game_result:
73+
print(f"🏁 Game Over — {final_state.game_result.replace('_', ' ').title()}")
74+
final_state.print_characters()
6375

6476

6577
if __name__ == "__main__":

pyproject.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# pyproject.toml
2+
3+
[build-system]
4+
requires = ["setuptools>=61.0"]
5+
build-backend = "setuptools.build_meta"
6+
7+
[project]
8+
name = "disastercycler_engine"
9+
version = "0.1.0"
10+
11+
# --- ADD THIS SECTION ---
12+
[tool.setuptools]
13+
packages = ["engine", "app"]

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pyyaml
22
fastapi
33
uvicorn
4-
pydantic
4+
pydantic
5+
pytest

run_tests.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/bash
2+
3+
pytest

tests/test_scenarios.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from engine.simulation import run_full_simulation
2+
from engine.models import RoleType
3+
4+
def test_friend_dies_at_end_of_loop():
5+
"""
6+
Tests the scenario where the Friend dies on the last day,
7+
resulting in a win for the Mastermind.
8+
"""
9+
# 1. Arrange: Define the input files for this specific scenario
10+
script_path = "scripts/testing_roles/script.yaml"
11+
actions_path = "scripts/testing_roles/actions_fd.yaml" # You create this file
12+
13+
# 2. Act: Run the simulation and get the final state
14+
final_state = run_full_simulation(script_path, actions_path)
15+
16+
# 3. Assert: Automatically check if the outcome is what we expect
17+
assert final_state.game_result == "mastermind_win"
18+
19+
# You can add more specific assertions too!
20+
friend_char = next(c for c in final_state.characters if c.role == RoleType.FRIEND)
21+
assert not friend_char.alive
22+
assert RoleType.FRIEND in final_state.revealed_roles

0 commit comments

Comments
 (0)