Skip to content

Commit 67d7739

Browse files
committed
Fix tests and make them much simpler
1 parent ad6a1fe commit 67d7739

8 files changed

Lines changed: 66 additions & 75 deletions

File tree

codeclash/__init__.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
__version__ = "0.0.0"
22

33
# Optional viewer import - only if Flask dependencies are available
4-
try:
5-
from . import viewer
4+
from pathlib import Path
65

7-
__all__ = ["viewer"]
6+
try:
7+
from . import viewer # noqa: F401
88
except ImportError:
9-
__all__ = []
9+
pass
10+
11+
12+
CONFIG_DIR = Path(__file__).resolve().parent.parent / "configs"

codeclash/agents/minisweagent.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from minisweagent.agents.default import AgentConfig, DefaultAgent
1111
from minisweagent.environments.docker import DockerEnvironment
1212
from minisweagent.models import get_model
13+
from minisweagent.models.test_models import DeterministicModel
1314
from minisweagent.run.utils.save import save_traj
1415
from rich.console import Console
1516

@@ -69,7 +70,11 @@ def __init__(self, config: dict, environment: DockerEnvironment, game_context: G
6970
super().__init__(config, environment=environment, game_context=game_context)
7071

7172
def run(self):
72-
model = get_model(config=self.config["config"]["model"])
73+
# temporary workaround around https://github.com/SWE-agent/mini-swe-agent/issues/477
74+
if "DeterministicModel" not in self.config["config"]["model"].get("model_class", ""):
75+
model = get_model(config=self.config["config"]["model"])
76+
else:
77+
model = DeterministicModel(outputs=self.config["config"]["model"]["outputs"])
7378
self.agent = ClashAgent(
7479
model=model,
7580
env=self.environment,

codeclash/tournaments/abstract.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import getpass
2+
import os
23
import time
34
import traceback
45
from pathlib import Path
@@ -13,7 +14,6 @@ def __init__(self, config: dict, *, name: str, **kwargs):
1314
self.config: dict = config
1415
self.name: str = name
1516
self.tournament_id: str = f"{self.name}.{config['game']['name']}.{time.strftime('%y%m%d%H%M%S')}"
16-
self.local_output_dir: Path = (DIR_LOGS / getpass.getuser() / self.tournament_id).resolve()
1717
self._metadata: dict = {
1818
"name": self.name,
1919
"tournament_id": self.tournament_id,
@@ -22,6 +22,13 @@ def __init__(self, config: dict, *, name: str, **kwargs):
2222
}
2323
self.logger = get_logger(self.name, log_path=self.local_output_dir / "tournament.log", emoji="🏆")
2424

25+
@property
26+
def local_output_dir(self) -> Path:
27+
base_dir = DIR_LOGS
28+
if "PYTEST_CURRENT_TEST" in os.environ:
29+
base_dir = Path("/tmp/codeclash")
30+
return (base_dir / getpass.getuser() / self.tournament_id).resolve()
31+
2532
def get_metadata(self) -> dict:
2633
return self._metadata
2734

configs/models/instant_submit.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
model_name: instant_submit
2+
model_class: minisweagent.models.test_models.DeterministicModel
3+
outputs:
4+
- "echo COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
game:
2+
name: BattleSnake
3+
sims_per_round: 1
4+
args:
5+
width: 11
6+
height: 11
7+
browser: false
8+
tournament:
9+
rounds: 1
10+
evaluate_matrix: false
11+
players:
12+
- agent: mini
13+
name: p1
14+
config:
15+
agent: !include mini/default.yaml
16+
model: !include models/instant_submit.yaml
17+
- agent: dummy
18+
name: p2
19+
prompts:
20+
game_description: "asdf"

main.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,24 @@
33

44
import yaml
55

6+
from codeclash import CONFIG_DIR
67
from codeclash.tournaments.pvp import PvpTournament
78
from codeclash.utils.yaml_utils import resolve_includes
89

910

1011
def main(config_path: Path, *, cleanup: bool = False, push_agent: bool = False):
1112
yaml_content = config_path.read_text()
12-
preprocessed_yaml = resolve_includes(yaml_content, base_dir=config_path.parent)
13+
preprocessed_yaml = resolve_includes(yaml_content, base_dir=CONFIG_DIR)
1314
config = yaml.safe_load(preprocessed_yaml)
1415
training = PvpTournament(config, cleanup=cleanup, push_agent=push_agent)
1516
training.run()
1617

1718

18-
if __name__ == "__main__":
19+
def main_cli(argv: list[str] | None = None):
1920
parser = argparse.ArgumentParser(description="CodeClash")
2021
parser.add_argument(
2122
"config_path",
2223
type=Path,
23-
default="configs/battlesnake.yaml",
2424
help="Path to the config file.",
2525
)
2626
parser.add_argument(
@@ -35,5 +35,9 @@ def main(config_path: Path, *, cleanup: bool = False, push_agent: bool = False):
3535
action="store_true",
3636
help="If set, push each agent's codebase to a new repository after running.",
3737
)
38-
args = parser.parse_args()
38+
args = parser.parse_args(argv)
3939
main(**vars(args))
40+
41+
42+
if __name__ == "__main__":
43+
main_cli()

main_single_player.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,24 @@
33

44
import yaml
55

6+
from codeclash import CONFIG_DIR
67
from codeclash.tournaments.single_player import SinglePlayerTraining
78
from codeclash.utils.yaml_utils import resolve_includes
89

910

1011
def main(config_path: Path, cleanup: bool = False):
1112
yaml_content = config_path.read_text()
12-
preprocessed_yaml = resolve_includes(yaml_content, base_dir=config_path.parent)
13+
preprocessed_yaml = resolve_includes(yaml_content, base_dir=CONFIG_DIR)
1314
config = yaml.safe_load(preprocessed_yaml)
1415
training = SinglePlayerTraining(config, cleanup=cleanup)
1516
training.run()
1617

1718

18-
if __name__ == "__main__":
19+
def main_cli(argv: list[str] | None = None):
1920
parser = argparse.ArgumentParser(description="CodeClash")
2021
parser.add_argument(
2122
"config_path",
2223
type=Path,
23-
default="configs/battlesnake.yaml",
2424
help="Path to the config file.",
2525
)
2626
parser.add_argument(
@@ -29,5 +29,9 @@ def main(config_path: Path, cleanup: bool = False):
2929
action="store_true",
3030
help="If set, do not clean up the game environment after running.",
3131
)
32-
args = parser.parse_args()
32+
args = parser.parse_args(argv)
3333
main(**vars(args))
34+
35+
36+
if __name__ == "__main__":
37+
main_cli()

tests/test_integration.py

Lines changed: 5 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,66 +5,10 @@
55
using DeterministicModel instead of real LLM models.
66
"""
77

8-
import tempfile
9-
from pathlib import Path
10-
from unittest.mock import patch
8+
from codeclash import CONFIG_DIR
9+
from main import main_cli
1110

12-
import yaml
13-
from minisweagent.models.test_models import DeterministicModel
1411

15-
from main import main
16-
17-
18-
def test_main_battlesnake_integration():
19-
"""
20-
Integration test for main.py with configs/battlesnake.yaml.
21-
Success criterion: execution completes without exceptions.
22-
"""
23-
# Create a temporary config file with DeterministicModel settings
24-
config_path = "configs/battlesnake.yaml"
25-
26-
# Read the original config
27-
with open(config_path) as f:
28-
config = yaml.safe_load(f)
29-
30-
# Create a temporary directory for test artifacts
31-
with tempfile.TemporaryDirectory() as temp_dir:
32-
temp_config_path = Path(temp_dir) / "test_battlesnake.yaml"
33-
34-
# Reduce rounds to 1 for faster testing
35-
config["tournament"]["rounds"] = 1
36-
37-
# Write the modified config
38-
with open(temp_config_path, "w") as f:
39-
yaml.dump(config, f)
40-
41-
def mock_get_agent(original_get_agent):
42-
"""Wrapper to replace agent models with DeterministicModel"""
43-
44-
def wrapper(config, game_context, environment):
45-
agent = original_get_agent(config, game_context, environment)
46-
print("In wrapper, got agent of type ", type(agent))
47-
48-
# Replace model if the agent has one (specifically for MiniSWEAgent)
49-
if hasattr(agent, "agent") and hasattr(agent.agent, "model"):
50-
print(f"Replacing model for agent {agent.name}")
51-
# Create DeterministicModel with the specified command
52-
deterministic_model = DeterministicModel(
53-
outputs=["```bash\necho 'COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT'\n```"]
54-
)
55-
agent.agent.model = deterministic_model
56-
57-
return agent
58-
59-
return wrapper
60-
61-
# Import the get_agent function and patch it where it's used in the tournaments
62-
from codeclash.agents import get_agent
63-
64-
# Run the main function with cleanup enabled
65-
with patch(
66-
"codeclash.tournaments.pvp.get_agent",
67-
side_effect=mock_get_agent(get_agent),
68-
):
69-
# This should complete without raising any exceptions
70-
main(temp_config_path, cleanup=True, push_agent=False)
12+
def test_pvp_battlesnake():
13+
config_path = CONFIG_DIR / "test_configs" / "battlesnake_pvp_test.yaml"
14+
main_cli(["-c", str(config_path)])

0 commit comments

Comments
 (0)