87 lines
3.2 KiB
Python
87 lines
3.2 KiB
Python
from __future__ import annotations
|
|
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from engine.artifact_manager import ArtifactManager
|
|
from engine.models import (
|
|
ArtifactSpec,
|
|
BudgetSpec,
|
|
ConstraintSpec,
|
|
LoggingSpec,
|
|
MutationSpec,
|
|
ObjectiveSpec,
|
|
PolicySpec,
|
|
RunnerSpec,
|
|
ScorerParseSpec,
|
|
ScorerSpec,
|
|
TaskSpec,
|
|
)
|
|
from engine.mutation_engine import MutationValidationError, validate_candidate_changes
|
|
|
|
|
|
def _make_task(root_dir: Path, allowed_file_types: list[str], max_changed_lines: int) -> TaskSpec:
|
|
return TaskSpec(
|
|
id="mutation-test",
|
|
description="Mutation validation fixture.",
|
|
artifacts=ArtifactSpec(include=["fixtures/*"], exclude=[], max_files_per_iteration=10),
|
|
mutation=MutationSpec(
|
|
mode="direct_edit",
|
|
allowed_file_types=allowed_file_types,
|
|
max_changed_lines=max_changed_lines,
|
|
),
|
|
runner=RunnerSpec(command="python -c \"print('runner ok')\"", cwd=".", timeout_seconds=30),
|
|
scorer=ScorerSpec(
|
|
type="command",
|
|
command="python -c \"print('{\\\"score\\\": 1.0, \\\"metrics\\\": {}}')\"",
|
|
parse=ScorerParseSpec(format="json", score_field="score", metrics_field="metrics"),
|
|
),
|
|
objective=ObjectiveSpec(primary_metric="score", direction="maximize"),
|
|
constraints=[],
|
|
policy=PolicySpec(keep_if="better_primary", tie_breakers=[], on_failure="discard"),
|
|
budget=BudgetSpec(max_iterations=1, max_failures=1),
|
|
logging=LoggingSpec(results_file="work/results.jsonl", candidate_dir="work/candidates"),
|
|
root_dir=root_dir,
|
|
)
|
|
|
|
|
|
class MutationEngineTest(unittest.TestCase):
|
|
def test_rejects_too_many_changed_lines(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root_dir = Path(tmp)
|
|
fixture_dir = root_dir / "fixtures"
|
|
fixture_dir.mkdir(parents=True)
|
|
target = fixture_dir / "note.md"
|
|
target.write_text("line 1\nline 2\n", encoding="utf-8")
|
|
|
|
task = _make_task(root_dir, allowed_file_types=[".md"], max_changed_lines=1)
|
|
snapshot = ArtifactManager(task).snapshot()
|
|
target.write_text("line 1\nline 2\nline 3\n", encoding="utf-8")
|
|
|
|
with self.assertRaises(MutationValidationError) as ctx:
|
|
validate_candidate_changes(task, snapshot)
|
|
|
|
self.assertIn("changed lines", str(ctx.exception))
|
|
|
|
def test_rejects_disallowed_extension(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root_dir = Path(tmp)
|
|
fixture_dir = root_dir / "fixtures"
|
|
fixture_dir.mkdir(parents=True)
|
|
target = fixture_dir / "note.txt"
|
|
target.write_text("line 1\n", encoding="utf-8")
|
|
|
|
task = _make_task(root_dir, allowed_file_types=[".md"], max_changed_lines=10)
|
|
snapshot = ArtifactManager(task).snapshot()
|
|
target.write_text("line 1 changed\n", encoding="utf-8")
|
|
|
|
with self.assertRaises(MutationValidationError) as ctx:
|
|
validate_candidate_changes(task, snapshot)
|
|
|
|
self.assertIn("disallowed file type", str(ctx.exception))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|