Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: ci

on:
pull_request:
push:
branches:
- main
- zefan-todo-1
- zefan-todo-2
- zefan-todo-3
- zefan-todo-4
- zefan-todo-7
- zefan-todo-8
- zefan-todo-10

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Compile Python sources
run: python -m py_compile main.py src/*.py tests/*.py

- name: Run unit tests
run: python -m unittest discover -s tests -v
116 changes: 116 additions & 0 deletions tests/test_cli_smoke.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from __future__ import annotations

import subprocess
import sys
import tempfile
import unittest
from pathlib import Path

from src.manifest import load_run_manifest
from src.utils import build_run_paths, ensure_run_layout, initialize_memory, write_text


REPO_ROOT = Path(__file__).resolve().parent.parent


class CliSmokeTests(unittest.TestCase):
def test_cli_new_run_abort_creates_cancelled_manifest(self) -> None:
with tempfile.TemporaryDirectory() as tmp_dir:
runs_dir = Path(tmp_dir) / "runs"

result = subprocess.run(
[
sys.executable,
"main.py",
"--fake-operator",
"--goal",
"CLI smoke abort goal",
"--runs-dir",
str(runs_dir),
],
cwd=REPO_ROOT,
input="6\n",
text=True,
capture_output=True,
)

self.assertEqual(result.returncode, 0, msg=result.stderr)
run_roots = sorted(path for path in runs_dir.iterdir() if path.is_dir())
self.assertEqual(len(run_roots), 1)
manifest = load_run_manifest(run_roots[0] / "run_manifest.json")
self.assertIsNotNone(manifest)
assert manifest is not None
self.assertEqual(manifest.run_status, "cancelled")
self.assertEqual(manifest.current_stage_slug, "01_literature_survey")

def test_cli_rejects_redo_and_rollback_together(self) -> None:
with tempfile.TemporaryDirectory() as tmp_dir:
runs_dir = Path(tmp_dir) / "runs"
run_root = runs_dir / "demo_run"
paths = build_run_paths(run_root)
ensure_run_layout(paths)
write_text(paths.user_input, "CLI conflict goal")
initialize_memory(paths, "CLI conflict goal")

result = subprocess.run(
[
sys.executable,
"main.py",
"--fake-operator",
"--resume-run",
"demo_run",
"--redo-stage",
"6",
"--rollback-stage",
"5",
"--runs-dir",
str(runs_dir),
],
cwd=REPO_ROOT,
text=True,
capture_output=True,
)

self.assertEqual(result.returncode, 1)
self.assertIn("mutually exclusive", result.stderr)

def test_cli_resume_latest_targets_newest_run_directory(self) -> None:
with tempfile.TemporaryDirectory() as tmp_dir:
runs_dir = Path(tmp_dir) / "runs"
older = runs_dir / "20260331_010101"
newer = runs_dir / "20260331_020202"
for run_root, goal in ((older, "older goal"), (newer, "newer goal")):
paths = build_run_paths(run_root)
ensure_run_layout(paths)
write_text(paths.user_input, goal)
initialize_memory(paths, goal)

result = subprocess.run(
[
sys.executable,
"main.py",
"--fake-operator",
"--resume-run",
"latest",
"--redo-stage",
"1",
"--runs-dir",
str(runs_dir),
],
cwd=REPO_ROOT,
input="6\n",
text=True,
capture_output=True,
)

self.assertEqual(result.returncode, 0, msg=result.stderr)
self.assertFalse((older / "run_manifest.json").exists())
manifest = load_run_manifest(newer / "run_manifest.json")
self.assertIsNotNone(manifest)
assert manifest is not None
self.assertEqual(manifest.run_status, "cancelled")
self.assertEqual(manifest.current_stage_slug, "01_literature_survey")


if __name__ == "__main__":
unittest.main()
Loading
Loading