"""Tests for openjarvis.learning.spec_search.plan.prompt_diff module.""" from __future__ import annotations from pathlib import Path class TestChangedLineRatio: """Tests for changed_line_ratio().""" def test_identical_strings(self) -> None: from openjarvis.learning.spec_search.plan.prompt_diff import ( changed_line_ratio, ) assert changed_line_ratio("hello\nworld\n", "hello\nworld\n") == 0.0 def test_completely_different(self) -> None: from openjarvis.learning.spec_search.plan.prompt_diff import ( changed_line_ratio, ) ratio = changed_line_ratio("aaa\nbbb\nccc\n", "xxx\nyyy\nzzz\n") assert ratio == 1.0 def test_partial_change(self) -> None: from openjarvis.learning.spec_search.plan.prompt_diff import ( changed_line_ratio, ) original = "line1\nline2\nline3\nline4\n" modified = "line1\nchanged\nline3\nline4\n" ratio = changed_line_ratio(original, modified) # 1 out of 4 lines changed assert 0.2 <= ratio <= 0.35 def test_empty_original(self) -> None: from openjarvis.learning.spec_search.plan.prompt_diff import ( changed_line_ratio, ) # Adding to empty = 100% change ratio = changed_line_ratio("", "new content\n") assert ratio == 1.0 def test_empty_both(self) -> None: from openjarvis.learning.spec_search.plan.prompt_diff import ( changed_line_ratio, ) assert changed_line_ratio("", "") == 0.0 class TestApplyUnifiedDiff: """Tests for apply_unified_diff().""" def test_applies_simple_patch(self) -> None: from openjarvis.learning.spec_search.plan.prompt_diff import ( apply_unified_diff, ) original = "line1\nline2\nline3\n" diff = ( "--- a/prompt.md\n" "+++ b/prompt.md\n" "@@ -1,3 +1,3 @@\n" " line1\n" "-line2\n" "+changed_line2\n" " line3\n" ) result = apply_unified_diff(original, diff) assert result == "line1\nchanged_line2\nline3\n" def test_returns_none_on_bad_diff(self) -> None: from openjarvis.learning.spec_search.plan.prompt_diff import ( apply_unified_diff, ) result = apply_unified_diff("hello\n", "not a valid diff") assert result is None class TestMaybeDowngradeToReplace: """Tests for maybe_downgrade_to_replace().""" def test_non_patch_op_passes_through(self) -> None: from openjarvis.learning.spec_search.models import ( Edit, EditOp, EditPillar, EditRiskTier, ) from openjarvis.learning.spec_search.plan.prompt_diff import ( maybe_downgrade_to_replace, ) edit = Edit( id="edit-001", pillar=EditPillar.INTELLIGENCE, op=EditOp.SET_MODEL_FOR_QUERY_CLASS, target="routing.math", payload={"model": "qwen2.5-coder:14b"}, rationale="test", expected_improvement="cluster-001", risk_tier=EditRiskTier.AUTO, ) result = maybe_downgrade_to_replace(edit, prompt_reader=lambda t: "") assert result.op == EditOp.SET_MODEL_FOR_QUERY_CLASS def test_small_diff_stays_patch(self, tmp_path: Path) -> None: from openjarvis.learning.spec_search.models import ( Edit, EditOp, EditPillar, EditRiskTier, ) from openjarvis.learning.spec_search.plan.prompt_diff import ( maybe_downgrade_to_replace, ) original = ( "line1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\nline9\nline10\n" ) diff = ( "--- a/prompt.md\n" "+++ b/prompt.md\n" "@@ -2,1 +2,1 @@\n" " line1\n" "-line2\n" "+changed\n" " line3\n" ) edit = Edit( id="edit-001", pillar=EditPillar.AGENT, op=EditOp.PATCH_SYSTEM_PROMPT, target="agents.simple.system_prompt", payload={"diff": diff}, rationale="Small fix", expected_improvement="cluster-001", risk_tier=EditRiskTier.REVIEW, ) result = maybe_downgrade_to_replace(edit, prompt_reader=lambda t: original) assert result.op == EditOp.PATCH_SYSTEM_PROMPT def test_large_diff_downgrades_to_replace(self) -> None: from openjarvis.learning.spec_search.models import ( Edit, EditOp, EditPillar, EditRiskTier, ) from openjarvis.learning.spec_search.plan.prompt_diff import ( maybe_downgrade_to_replace, ) original = "old1\nold2\nold3\nold4\n" diff = ( "--- a/prompt.md\n" "+++ b/prompt.md\n" "@@ -1,4 +1,4 @@\n" "-old1\n" "-old2\n" "-old3\n" "-old4\n" "+new1\n" "+new2\n" "+new3\n" "+new4\n" ) edit = Edit( id="edit-002", pillar=EditPillar.AGENT, op=EditOp.PATCH_SYSTEM_PROMPT, target="agents.simple.system_prompt", payload={"diff": diff}, rationale="Major rewrite", expected_improvement="cluster-001", risk_tier=EditRiskTier.REVIEW, ) result = maybe_downgrade_to_replace(edit, prompt_reader=lambda t: original) assert result.op == EditOp.REPLACE_SYSTEM_PROMPT assert "new_content" in result.payload assert "new1" in result.payload["new_content"] def test_bad_diff_downgrades_to_replace_with_raw(self) -> None: from openjarvis.learning.spec_search.models import ( Edit, EditOp, EditPillar, EditRiskTier, ) from openjarvis.learning.spec_search.plan.prompt_diff import ( maybe_downgrade_to_replace, ) edit = Edit( id="edit-003", pillar=EditPillar.AGENT, op=EditOp.PATCH_SYSTEM_PROMPT, target="agents.simple.system_prompt", payload={"diff": "this is not a valid unified diff"}, rationale="Bad diff", expected_improvement="cluster-001", risk_tier=EditRiskTier.REVIEW, ) result = maybe_downgrade_to_replace( edit, prompt_reader=lambda t: "original content\n" ) # Can't apply the diff, so it should downgrade assert result.op == EditOp.REPLACE_SYSTEM_PROMPT