"""Tests for parser module — JSONL trace and YAML spec parsing.""" from pathlib import Path import pytest from scripts.parser import ( ComplianceSpec, Detector, ObservationEvent, Step, parse_spec, parse_trace, ) FIXTURES = Path(__file__).parent.parent / "fixtures" class TestParseTrace: def test_parses_compliant_trace(self) -> None: events = parse_trace(FIXTURES / "compliant_trace.jsonl") assert len(events) == 5 assert all(isinstance(e, ObservationEvent) for e in events) def test_events_sorted_by_timestamp(self) -> None: events = parse_trace(FIXTURES / "compliant_trace.jsonl") timestamps = [e.timestamp for e in events] assert timestamps == sorted(timestamps) def test_event_fields(self) -> None: events = parse_trace(FIXTURES / "compliant_trace.jsonl") first = events[0] assert first.tool == "Write" assert first.session == "sess-001" assert "test_fib.py" in first.input assert first.output == "File created" def test_parses_noncompliant_trace(self) -> None: events = parse_trace(FIXTURES / "noncompliant_trace.jsonl") assert len(events) == 3 assert "src/fib.py" in events[0].input def test_empty_file_returns_empty_list(self, tmp_path: Path) -> None: empty = tmp_path / "empty.jsonl" empty.write_text("") events = parse_trace(empty) assert events == [] def test_nonexistent_file_raises(self) -> None: with pytest.raises(FileNotFoundError): parse_trace(Path("/nonexistent/trace.jsonl")) class TestParseSpec: def test_parses_tdd_spec(self) -> None: spec = parse_spec(FIXTURES / "tdd_spec.yaml") assert isinstance(spec, ComplianceSpec) assert spec.id == "tdd-workflow" assert len(spec.steps) == 5 def test_step_fields(self) -> None: spec = parse_spec(FIXTURES / "tdd_spec.yaml") first = spec.steps[0] assert isinstance(first, Step) assert first.id == "write_test" assert first.required is True assert isinstance(first.detector, Detector) assert "test file" in first.detector.description assert first.detector.before_step == "write_impl" def test_optional_detector_fields(self) -> None: spec = parse_spec(FIXTURES / "tdd_spec.yaml") write_test = spec.steps[0] assert write_test.detector.after_step is None run_test_red = spec.steps[1] assert run_test_red.detector.after_step == "write_test" assert run_test_red.detector.before_step == "write_impl" def test_scoring_threshold(self) -> None: spec = parse_spec(FIXTURES / "tdd_spec.yaml") assert spec.threshold_promote_to_hook == 0.6 def test_required_vs_optional_steps(self) -> None: spec = parse_spec(FIXTURES / "tdd_spec.yaml") required = [s for s in spec.steps if s.required] optional = [s for s in spec.steps if not s.required] assert len(required) == 4 assert len(optional) == 1 assert optional[0].id == "refactor"