#!/usr/bin/env python3 """Atomic-write, resume, failure-retention, and validation tests.""" from pathlib import Path import sys import tempfile import unittest import numpy as np CODE_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(CODE_ROOT)) from experiments.io import TrialPayload # noqa: E402 from experiments.plan import build_trial_plan # noqa: E402 from experiments.runner import run_trial_plan # noqa: E402 from experiments.validate import validate_batch # noqa: E402 def runner_specification(): return { "study_id": "runner_contract", "split": "pilot", "root_seed": 17, "replicates": 2, "methods": ["a", "b"], "trajectories": ["trajectory_001"], "factors": {"delay_ms": [0]}, } def successful_executor(trial): method_offset = 0.0 if trial["method"]["method_id"] == "a" else 1.0 time = np.arange(5, dtype=float) * 0.002 return TrialPayload( samples={ "time": time, "signal": np.full((5, 2), method_offset, dtype=float), }, events=[{"sample_index": 2, "event": "marker"}], metadata={"executor": "unit-test"}, ) class ExperimentRunnerContractTest(unittest.TestCase): def test_dry_run_writes_nothing(self): plan = build_trial_plan(runner_specification()) with tempfile.TemporaryDirectory() as temporary: batch = Path(temporary) / "batch" report = run_trial_plan(plan, batch, dry_run=True) self.assertEqual(report["trial_count"], 4) self.assertFalse(batch.exists()) def test_atomic_run_validate_and_resume(self): plan = build_trial_plan(runner_specification()) with tempfile.TemporaryDirectory() as temporary: batch = Path(temporary) / "batch" first = run_trial_plan(plan, batch, successful_executor) self.assertEqual(first["completed_trials"], 4) self.assertEqual(first["failed_trials"], 0) validation = validate_batch(batch) self.assertTrue(validation["valid"], validation["errors"]) self.assertEqual(validation["completed_trials"], 4) self.assertFalse( any(path.name.startswith(".") for path in (batch / "raw").iterdir()) ) second = run_trial_plan(plan, batch, successful_executor, resume=True) self.assertEqual(second["completed_trials"], 0) self.assertEqual(second["skipped_trials"], 4) def test_failure_is_retained_without_committing_trial(self): plan = build_trial_plan(runner_specification()) def failing_executor(_trial): raise RuntimeError("intentional failure") with tempfile.TemporaryDirectory() as temporary: batch = Path(temporary) / "batch" report = run_trial_plan( plan, batch, failing_executor, max_trials=1, ) self.assertEqual(report["failed_trials"], 1) self.assertFalse((batch / "raw" / plan["trials"][0]["trial_id"]).exists()) failure_files = list((batch / "failures").rglob("*.json")) self.assertEqual(len(failure_files), 1) if __name__ == "__main__": unittest.main()