from __future__ import annotations import pytest from pydantic import ValidationError from gap_pipeline.models import ( DiffusedProof, MethodPlan, ProofDAG, ReplacementSpec, ) def test_valid_dag_and_plan(dag_dict: dict, plan_dict: dict) -> None: dag = ProofDAG.model_validate(dag_dict) assert [node.node_id for node in dag.topological_nodes()] == ["n1", "n2"] assert dag.leaf_node_ids() == ["n1"] plan = MethodPlan.model_validate(plan_dict) plan.validate_against(dag) def test_cycle_is_rejected(dag_dict: dict) -> None: dag_dict["nodes"][0]["dependencies"] = ["n2"] with pytest.raises(ValidationError, match="cycle"): ProofDAG.model_validate(dag_dict) def test_node_disconnected_from_terminal_is_rejected(dag_dict: dict) -> None: dag_dict["nodes"].append( { "node_id": "orphan", "claim": "unused claim", "derivation": "unused derivation", "dependencies": [], "source_span": "", "mathematical_objects": [], } ) with pytest.raises(ValidationError, match="disconnected"): ProofDAG.model_validate(dag_dict) def test_method_reordering_is_rejected(dag_dict: dict, plan_dict: dict) -> None: dag = ProofDAG.model_validate(dag_dict) plan_dict["steps"].reverse() plan = MethodPlan.model_validate(plan_dict) with pytest.raises(ValueError, match="topological order"): plan.validate_against(dag) def test_replacement_must_target_leaf( dag_dict: dict, replacement_dict: dict ) -> None: dag = ProofDAG.model_validate(dag_dict) replacement_dict["target_node_id"] = "n2" replacement = ReplacementSpec.model_validate(replacement_dict) with pytest.raises(ValueError, match="not a DAG leaf"): replacement.validate_against(dag) def test_diffusion_cannot_change_dag_dependencies( dag_dict: dict, plan_dict: dict, diffused_dict: dict, ) -> None: dag = ProofDAG.model_validate(dag_dict) plan = MethodPlan.model_validate(plan_dict) diffused_dict["node_instantiations"][1]["dependencies"] = [] diffused = DiffusedProof.model_validate(diffused_dict) with pytest.raises(ValueError, match="changes dependencies"): diffused.validate_against(dag, plan)