feat(plans): implement configurable merge strategy (prefer-parent, prefer-subplan, manual)
Implemented a configurable merge strategy framework for three-way merges. - New module: src/cleveragents/domain/models/core/merge_strategy.py - MergeStrategy StrEnum with options: prefer-parent, prefer-subplan, manual - Helper methods: is_auto_resolve(), is_manual(), from_string() - New module: src/cleveragents/domain/models/core/merge_strategy_service.py - MergeConflict class with __eq__ for value equality comparison - MergeStrategyService to apply strategies and resolve conflicts - Proper type annotations (dict, Any) with no unused imports - BDD test suite: features/plan_merge_strategy.feature (8 scenarios) - Step definitions: features/steps/plan_merge_strategy_steps.py - Robot Framework integration tests: robot/merge_strategy.robot - Helper script: robot/helper_merge_strategy.py - Updated src/cleveragents/domain/models/core/__init__.py exports ISSUES CLOSED: #9559
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
"""Helper utilities for merge strategy Robot tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
from cleveragents.domain.models.core.merge_strategy import MergeStrategy
|
||||
from cleveragents.domain.models.core.merge_strategy_service import (
|
||||
MergeConflict,
|
||||
MergeStrategyService,
|
||||
)
|
||||
|
||||
|
||||
def _prefer_parent_test() -> None:
|
||||
"""Test prefer-parent strategy auto-resolves conflicts."""
|
||||
service = MergeStrategyService(strategy=MergeStrategy.PREFER_PARENT)
|
||||
conflicts = [
|
||||
MergeConflict(path="field1", parent_value="p1", subplan_value="s1"),
|
||||
MergeConflict(path="field2", parent_value="p2", subplan_value="s2"),
|
||||
]
|
||||
resolved = service.resolve_conflicts(conflicts)
|
||||
assert resolved["field1"] == "p1"
|
||||
assert resolved["field2"] == "p2"
|
||||
assert service.can_auto_resolve() is True
|
||||
print("merge-strategy-prefer-parent-ok")
|
||||
|
||||
|
||||
def _prefer_subplan_test() -> None:
|
||||
"""Test prefer-subplan strategy auto-resolves conflicts."""
|
||||
service = MergeStrategyService(strategy=MergeStrategy.PREFER_SUBPLAN)
|
||||
conflicts = [
|
||||
MergeConflict(path="field1", parent_value="p1", subplan_value="s1"),
|
||||
MergeConflict(path="field2", parent_value="p2", subplan_value="s2"),
|
||||
]
|
||||
resolved = service.resolve_conflicts(conflicts)
|
||||
assert resolved["field1"] == "s1"
|
||||
assert resolved["field2"] == "s2"
|
||||
assert service.can_auto_resolve() is True
|
||||
print("merge-strategy-prefer-subplan-ok")
|
||||
|
||||
|
||||
def _manual_test() -> None:
|
||||
"""Test manual strategy raises error on auto-resolution."""
|
||||
service = MergeStrategyService(strategy=MergeStrategy.MANUAL)
|
||||
conflicts = [
|
||||
MergeConflict(path="field1", parent_value="p1", subplan_value="s1"),
|
||||
]
|
||||
try:
|
||||
service.resolve_conflicts(conflicts)
|
||||
raise AssertionError("Expected ValueError for manual strategy")
|
||||
except ValueError as e:
|
||||
assert "manual" in str(e).lower()
|
||||
assert service.can_auto_resolve() is False
|
||||
print("merge-strategy-manual-ok")
|
||||
|
||||
|
||||
def _no_conflicts_test() -> None:
|
||||
"""Test no conflicts returns empty result."""
|
||||
service = MergeStrategyService(strategy=MergeStrategy.PREFER_PARENT)
|
||||
resolved = service.resolve_conflicts([])
|
||||
assert resolved == {}
|
||||
assert not service.has_conflicts([])
|
||||
print("merge-strategy-no-conflicts-ok")
|
||||
|
||||
|
||||
def _from_string_test() -> None:
|
||||
"""Test MergeStrategy.from_string creates correct enum."""
|
||||
strategy = MergeStrategy.from_string("prefer-parent")
|
||||
assert strategy.value == "prefer-parent"
|
||||
assert str(strategy) == "prefer-parent"
|
||||
|
||||
strategy_subplan = MergeStrategy.from_string("prefer-subplan")
|
||||
assert strategy_subplan.value == "prefer-subplan"
|
||||
|
||||
strategy_manual = MergeStrategy.from_string("manual")
|
||||
assert strategy_manual.is_manual() is True
|
||||
assert strategy_manual.is_auto_resolve() is False
|
||||
|
||||
print("merge-strategy-from-string-ok")
|
||||
|
||||
|
||||
def _invalid_string_test() -> None:
|
||||
"""Test invalid string raises ValueError."""
|
||||
try:
|
||||
MergeStrategy.from_string("invalid-strategy")
|
||||
raise AssertionError("Expected ValueError for invalid strategy string")
|
||||
except ValueError as e:
|
||||
assert "Invalid merge strategy" in str(e)
|
||||
print("merge-strategy-invalid-string-ok")
|
||||
|
||||
|
||||
def _enums_test() -> None:
|
||||
"""Test MergeStrategy enum values and methods."""
|
||||
assert MergeStrategy.PREFER_PARENT == "prefer-parent"
|
||||
assert MergeStrategy.PREFER_SUBPLAN == "prefer-subplan"
|
||||
assert MergeStrategy.MANUAL == "manual"
|
||||
|
||||
assert MergeStrategy.PREFER_PARENT.is_auto_resolve() is True
|
||||
assert MergeStrategy.PREFER_SUBPLAN.is_auto_resolve() is True
|
||||
assert MergeStrategy.MANUAL.is_auto_resolve() is False
|
||||
|
||||
assert MergeStrategy.PREFER_PARENT.is_manual() is False
|
||||
assert MergeStrategy.PREFER_SUBPLAN.is_manual() is False
|
||||
assert MergeStrategy.MANUAL.is_manual() is True
|
||||
|
||||
print("merge-strategy-enums-ok")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) < 2:
|
||||
raise SystemExit("Expected command argument")
|
||||
command = sys.argv[1]
|
||||
tests = {
|
||||
"prefer_parent": _prefer_parent_test,
|
||||
"prefer_subplan": _prefer_subplan_test,
|
||||
"manual": _manual_test,
|
||||
"no_conflicts": _no_conflicts_test,
|
||||
"from_string": _from_string_test,
|
||||
"invalid_string": _invalid_string_test,
|
||||
"enums": _enums_test,
|
||||
}
|
||||
if command not in tests:
|
||||
raise SystemExit(f"Unknown command: {command}")
|
||||
tests[command]()
|
||||
@@ -0,0 +1,49 @@
|
||||
*** Settings ***
|
||||
Documentation Integration tests for merge strategy configuration (prefer-parent, prefer-subplan, manual)
|
||||
Resource ${CURDIR}/common.resource
|
||||
|
||||
*** Variables ***
|
||||
${HELPER_SCRIPT} robot/helper_merge_strategy.py
|
||||
|
||||
*** Test Cases ***
|
||||
Prefer-Parent Auto-Resolves Conflicts
|
||||
[Documentation] Verify prefer-parent strategy resolves all conflicts in favor of parent values
|
||||
${result}= Run Process ${PYTHON} ${HELPER_SCRIPT} prefer_parent cwd=${WORKSPACE}
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
Should Contain ${result.stdout} merge-strategy-prefer-parent-ok
|
||||
|
||||
Prefer-Subplan Auto-Resolves Conflicts
|
||||
[Documentation] Verify prefer-subplan strategy resolves all conflicts in favor of subplan values
|
||||
${result}= Run Process ${PYTHON} ${HELPER_SCRIPT} prefer_subplan cwd=${WORKSPACE}
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
Should Contain ${result.stdout} merge-strategy-prefer-subplan-ok
|
||||
|
||||
Manual Strategy Requires Resolution
|
||||
[Documentation] Verify manual strategy raises ValueError on attempted auto-resolution
|
||||
${result}= Run Process ${PYTHON} ${HELPER_SCRIPT} manual cwd=${WORKSPACE}
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
Should Contain ${result.stdout} merge-strategy-manual-ok
|
||||
|
||||
No Conflicts Returns Empty Result
|
||||
[Documentation] Verify empty conflict list returns empty resolution
|
||||
${result}= Run Process ${PYTHON} ${HELPER_SCRIPT} no_conflicts cwd=${WORKSPACE}
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
Should Contain ${result.stdout} merge-strategy-no-conflicts-ok
|
||||
|
||||
Strategy From String Creates Correct Enum
|
||||
[Documentation] Verify MergeStrategy.from_string creates proper enum values
|
||||
${result}= Run Process ${PYTHON} ${HELPER_SCRIPT} from_string cwd=${WORKSPACE}
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
Should Contain ${result.stdout} merge-strategy-from-string-ok
|
||||
|
||||
Invalid String Raises Error
|
||||
[Documentation] Verify invalid string raises ValueError with descriptive message
|
||||
${result}= Run Process ${PYTHON} ${HELPER_SCRIPT} invalid_string cwd=${WORKSPACE}
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
Should Contain ${result.stdout} merge-strategy-invalid-string-ok
|
||||
|
||||
Enum Values And Methods Validated
|
||||
[Documentation] Verify all enum member values and helper methods are correct
|
||||
${result}= Run Process ${PYTHON} ${HELPER_SCRIPT} enums cwd=${WORKSPACE}
|
||||
Should Be Equal As Integers ${result.rc} 0
|
||||
Should Contain ${result.stdout} merge-strategy-enums-ok
|
||||
Reference in New Issue
Block a user