f808abff86
Fix JSON syntax errors in .devcontainer/devcontainer.json (removed
invalid JS-style // comments) and .devcontainer/opencode.json (removed
90+ trailing commas). Apply auto-fixes for end-of-file and trailing
whitespace issues across 100+ files. Fix SIM105 ruff violations in
benchmarks/core_circuit_breaker_bench.py (use contextlib.suppress).
Note: The security fix from issue #7478 (validate_path startswith bypass)
was already delivered to master in commit e18ac5f2. This PR as currently
structured is non-atomic (35 commits across 10+ issues) and needs
significant restructure before merge. This commit only addresses the
CI/pre-commit failures.
ISSUES CLOSED: #7478
912 lines
43 KiB
Gherkin
912 lines
43 KiB
Gherkin
Feature: Consolidated Correction
|
||
Combined scenarios from: correction_flows, correction_model, correction_service_coverage_r2, correction_service_new_coverage
|
||
|
||
# ============================================================
|
||
# Originally from: correction_flows.feature
|
||
# Feature: Correction flows — revert and append
|
||
# ============================================================
|
||
|
||
Scenario: Simple revert invalidates target decision
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And a decision tree where "D1" has no children
|
||
When I execute the revert correction
|
||
Then the correction flow result status should be "applied"
|
||
And the reverted decisions should contain "D1"
|
||
|
||
|
||
Scenario: Revert deep subtree invalidates all descendants
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And a multi-parent decision tree with "D1" children "D2,D3" and "D2" children "D4,D5"
|
||
When I execute the revert correction
|
||
Then the reverted decisions should contain "D1"
|
||
And the reverted decisions should contain "D2"
|
||
And the reverted decisions should contain "D3"
|
||
And the reverted decisions should contain "D4"
|
||
And the reverted decisions should contain "D5"
|
||
|
||
|
||
Scenario: Revert archives artifacts for each affected decision
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And a simple decision tree where "D1" has children "D2"
|
||
When I execute the revert correction
|
||
Then the archived artifacts should include "D1.artifact"
|
||
And the archived artifacts should include "D2.artifact"
|
||
|
||
|
||
Scenario: Revert correction transitions through expected statuses
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
Then the correction flow status should be "pending"
|
||
When I execute the revert correction with a decision tree where "D1" has no children
|
||
Then the correction flow status should be "applied"
|
||
|
||
# ───────────────────────────────────────────────────────────
|
||
# Append scenarios
|
||
# ───────────────────────────────────────────────────────────
|
||
|
||
|
||
Scenario: Simple append spawns child plan
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in append mode
|
||
When I execute the append correction
|
||
Then the correction flow result status should be "applied"
|
||
And the result should have a spawned child plan id
|
||
|
||
|
||
Scenario: Append with guidance preserves guidance on request
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in append mode with guidance "Fix auth module"
|
||
When I execute the append correction
|
||
Then the correction flow guidance should be "Fix auth module"
|
||
|
||
|
||
Scenario: Append creates new decision in result
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in append mode
|
||
When I execute the append correction
|
||
Then the result new decisions should not be empty
|
||
|
||
# ───────────────────────────────────────────────────────────
|
||
# Impact analysis
|
||
# ───────────────────────────────────────────────────────────
|
||
|
||
|
||
Scenario: Impact analysis with BFS subtree traversal
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And a multi-parent decision tree with "D1" children "D2,D3" and "D3" children "D6"
|
||
When I analyze the impact
|
||
Then the affected decisions should be "D1,D2,D3,D6"
|
||
|
||
|
||
Scenario: Impact analysis risk level low for small subtree
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And a simple decision tree where "D1" has children "D2"
|
||
When I analyze the impact
|
||
Then the risk level should be "low"
|
||
|
||
|
||
Scenario: Impact analysis risk level medium for moderate subtree
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And a decision tree with 7 nodes rooted at "D1"
|
||
When I analyze the impact
|
||
Then the risk level should be "medium"
|
||
|
||
|
||
Scenario: Impact analysis risk level high for large subtree
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And a decision tree with 15 nodes rooted at "D1"
|
||
When I analyze the impact
|
||
Then the risk level should be "high"
|
||
|
||
|
||
Scenario: Impact analysis on leaf node returns only the target
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "LEAF" in revert mode
|
||
And an empty decision tree
|
||
When I analyze the impact
|
||
Then the affected decisions should be "LEAF"
|
||
|
||
# ───────────────────────────────────────────────────────────
|
||
# Dry-run
|
||
# ───────────────────────────────────────────────────────────
|
||
|
||
|
||
Scenario: Dry-run report generation for revert
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And a simple decision tree where "D1" has children "D2,D3"
|
||
When I generate a dry-run report
|
||
Then the report mode should be "revert"
|
||
And the report decisions to invalidate should contain "D1"
|
||
And the report estimated recompute time should be greater than 0
|
||
|
||
|
||
Scenario: Dry-run report generates warnings for high risk
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And a decision tree with 15 nodes rooted at "D1"
|
||
When I generate a dry-run report
|
||
Then the report warnings should contain "High risk"
|
||
|
||
|
||
Scenario: Dry-run report generates warnings for medium risk
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And a decision tree with 7 nodes rooted at "D1"
|
||
When I generate a dry-run report
|
||
Then the report warnings should contain "Medium risk"
|
||
|
||
|
||
Scenario: Dry-run report for append has no decisions to invalidate
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in append mode
|
||
When I generate a dry-run report
|
||
Then the report decisions to invalidate should be empty
|
||
|
||
# ───────────────────────────────────────────────────────────
|
||
# Error handling
|
||
# ───────────────────────────────────────────────────────────
|
||
|
||
|
||
Scenario: Get correction with invalid id raises not found
|
||
Given a correction flow service
|
||
When I try to get correction flow "nonexistent-id"
|
||
Then a correction flow resource not found error should be raised
|
||
|
||
|
||
Scenario: Execute already applied correction raises validation error
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And I execute the revert correction with a decision tree where "D1" has no children
|
||
When I try to execute the revert correction again
|
||
Then a validation error should be raised for status
|
||
|
||
|
||
Scenario: Execute already cancelled correction raises validation error
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And I cancel the correction flow
|
||
When I try to execute the revert correction again
|
||
Then a validation error should be raised for status
|
||
|
||
|
||
Scenario: Cancel already applied correction raises validation error
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And I execute the revert correction with a decision tree where "D1" has no children
|
||
When I try to cancel the correction flow
|
||
Then a validation error should be raised for status
|
||
|
||
# ───────────────────────────────────────────────────────────
|
||
# Status transitions
|
||
# ───────────────────────────────────────────────────────────
|
||
|
||
|
||
Scenario: Correction status transitions from pending to applied on revert
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
When I execute the revert correction with a decision tree where "D1" has no children
|
||
Then the correction flow status should be "applied"
|
||
|
||
|
||
Scenario: Correction status transitions from pending to cancelled
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
When I cancel the correction flow
|
||
Then the correction flow status should be "cancelled"
|
||
|
||
# ───────────────────────────────────────────────────────────
|
||
# Multi-correction & listing
|
||
# ───────────────────────────────────────────────────────────
|
||
|
||
|
||
Scenario: Multiple corrections on same plan are listed correctly
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And a correction request for plan "P1" targeting decision "D2" in append mode
|
||
When I list correction flows for plan "P1"
|
||
Then the correction list should have 2 items
|
||
|
||
|
||
Scenario: List corrections filters by plan id
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And a correction request for plan "P2" targeting decision "D2" in append mode
|
||
When I list correction flows for plan "P1"
|
||
Then the correction list should have 1 items
|
||
|
||
# ───────────────────────────────────────────────────────────
|
||
# Cancellation
|
||
# ───────────────────────────────────────────────────────────
|
||
|
||
|
||
Scenario: Cancel pending correction succeeds
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
When I cancel the correction flow
|
||
Then the correction flow status should be "cancelled"
|
||
|
||
# ───────────────────────────────────────────────────────────
|
||
# Attempt tracking
|
||
# ───────────────────────────────────────────────────────────
|
||
|
||
|
||
Scenario: Execution records an attempt
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
When I execute the revert correction with a decision tree where "D1" has no children
|
||
Then the attempt list should have 1 entries
|
||
And the first attempt should be successful
|
||
|
||
|
||
Scenario: Append execution records an attempt
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in append mode
|
||
When I execute the append correction
|
||
Then the attempt list should have 1 entries
|
||
|
||
# ───────────────────────────────────────────────────────────
|
||
# Model validation
|
||
# ───────────────────────────────────────────────────────────
|
||
|
||
|
||
Scenario: CorrectionRequest rejects empty plan_id
|
||
Given a correction flow service
|
||
When I try to create a correction with empty plan_id
|
||
Then a validation error should be raised for empty field
|
||
|
||
|
||
Scenario: CorrectionRequest rejects empty target_decision_id
|
||
Given a correction flow service
|
||
When I try to create a correction with empty target_decision_id
|
||
Then a validation error should be raised for empty field
|
||
|
||
|
||
Scenario: CorrectionImpact rejects invalid risk level
|
||
When I try to create a correction impact with risk level "critical"
|
||
Then a correction flow pydantic validation error should be raised
|
||
|
||
|
||
Scenario: CorrectionDryRunReport contains all expected fields
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And a simple decision tree where "D1" has children "D2"
|
||
When I generate a dry-run report
|
||
Then the report should have a correction_id
|
||
And the report should have an impact object
|
||
And the report should have a warnings list
|
||
|
||
|
||
Scenario: Execute correction dispatches to revert
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in revert mode
|
||
And a decision tree where "D1" has no children
|
||
When I execute correction via dispatch
|
||
Then the correction flow result status should be "applied"
|
||
And the reverted decisions should contain "D1"
|
||
|
||
|
||
Scenario: Execute correction dispatches to append
|
||
Given a correction flow service
|
||
And a correction request for plan "P1" targeting decision "D1" in append mode
|
||
When I execute correction via dispatch
|
||
Then the correction flow result status should be "applied"
|
||
And the result should have a spawned child plan id
|
||
|
||
|
||
# ============================================================
|
||
# Originally from: correction_model.feature
|
||
# Feature: Correction Model
|
||
# ============================================================
|
||
|
||
Scenario: Create correction request in revert mode
|
||
Given a correction service
|
||
When I request a correction for plan "plan-1" decision "DEC-001" in revert mode with guidance "Use FastAPI instead"
|
||
Then the correction should be created
|
||
And the correction mode should be "revert"
|
||
And the correction status should be "pending"
|
||
And the correction plan_id should be "plan-1"
|
||
And the correction target_decision_id should be "DEC-001"
|
||
And the correction guidance should be "Use FastAPI instead"
|
||
|
||
|
||
Scenario: Create correction request in append mode
|
||
Given a correction service
|
||
When I request a correction for plan "plan-2" decision "DEC-002" in append mode with guidance "Add caching layer"
|
||
Then the correction should be created
|
||
And the correction mode should be "append"
|
||
And the correction status should be "pending"
|
||
|
||
|
||
Scenario: Create dry-run correction request
|
||
Given a correction service
|
||
When I request a dry-run correction for plan "plan-1" decision "DEC-001" in revert mode with guidance "Use SQLite"
|
||
Then the correction should be created
|
||
And the correction dry_run should be true
|
||
|
||
# ── Impact Analysis ──────────────────────────────────────────
|
||
|
||
|
||
Scenario: Dry-run returns impact analysis
|
||
Given a correction service
|
||
And a correction request for plan "plan-1" decision "DEC-001" in revert mode with guidance "Change framework"
|
||
When I analyze the correction impact
|
||
Then the impact should list affected decisions
|
||
And the impact risk level should be "low"
|
||
And the correction status should be "analyzing"
|
||
|
||
# ── Cancellation ─────────────────────────────────────────────
|
||
|
||
|
||
Scenario: Cancel a pending correction
|
||
Given a correction service
|
||
And a correction request for plan "plan-1" decision "DEC-001" in revert mode with guidance "Change approach"
|
||
When I cancel the correction
|
||
Then the correction status should be "cancelled"
|
||
|
||
|
||
Scenario: Cancel an already-cancelled correction fails
|
||
Given a correction service
|
||
And a cancelled correction for plan "plan-1" decision "DEC-001"
|
||
When I try to cancel the correction
|
||
Then a validation error should be raised with message containing "cancelled"
|
||
|
||
|
||
Scenario: Cancel an applied correction fails
|
||
Given a correction service
|
||
And an applied correction for plan "plan-1" decision "DEC-001"
|
||
When I try to cancel the correction
|
||
Then a validation error should be raised with message containing "applied"
|
||
|
||
# ── Listing ──────────────────────────────────────────────────
|
||
|
||
|
||
Scenario: List corrections by plan
|
||
Given a correction service
|
||
And a correction request for plan "plan-1" decision "DEC-001" in revert mode with guidance "Fix endpoint"
|
||
And a correction request for plan "plan-1" decision "DEC-002" in append mode with guidance "Add logging"
|
||
And a correction request for plan "plan-2" decision "DEC-003" in revert mode with guidance "Change DB"
|
||
When I list corrections for plan "plan-1"
|
||
Then I should get 2 corrections
|
||
When I list corrections for plan "plan-2"
|
||
Then I should get 1 corrections
|
||
When I list all corrections
|
||
Then I should get 3 corrections
|
||
|
||
# ── Retrieval ────────────────────────────────────────────────
|
||
|
||
|
||
Scenario: Get correction by ID
|
||
Given a correction service
|
||
And a correction request for plan "plan-1" decision "DEC-001" in revert mode with guidance "Test retrieval"
|
||
When I get the correction by its ID
|
||
Then the correction should be found
|
||
And the correction guidance should be "Test retrieval"
|
||
|
||
|
||
Scenario: Get non-existent correction raises error
|
||
Given a correction service
|
||
When I try to get correction "nonexistent-id"
|
||
Then a resource not found error should be raised
|
||
|
||
# ── Validation ───────────────────────────────────────────────
|
||
|
||
|
||
Scenario: Invalid decision ID rejection
|
||
Given a correction service
|
||
When I try to request a correction with empty decision ID
|
||
Then a validation error should be raised with message containing "decision_id"
|
||
|
||
|
||
Scenario: Invalid plan ID rejection
|
||
Given a correction service
|
||
When I try to request a correction with empty plan ID
|
||
Then a validation error should be raised with message containing "plan_id"
|
||
|
||
|
||
Scenario: Empty guidance is accepted
|
||
Given a correction service
|
||
When I try to request a correction with empty guidance
|
||
Then the correction should be created
|
||
|
||
|
||
Scenario: Whitespace-only guidance is accepted
|
||
Given a correction service
|
||
When I try to request a correction with whitespace-only guidance
|
||
Then the correction should be created
|
||
|
||
# ── Status Transitions ───────────────────────────────────────
|
||
|
||
|
||
Scenario: Correction status transitions through execution
|
||
Given a correction service
|
||
And a correction request for plan "plan-1" decision "DEC-001" in revert mode with guidance "Execute me"
|
||
Then the correction status should be "pending"
|
||
When I execute the correction
|
||
Then the correction status should be "applied"
|
||
|
||
|
||
Scenario: Execute already-applied correction fails
|
||
Given a correction service
|
||
And an applied correction for plan "plan-1" decision "DEC-001"
|
||
When I try to execute the correction
|
||
Then a validation error should be raised with message containing "applied"
|
||
|
||
|
||
Scenario: Execute already-cancelled correction fails
|
||
Given a correction service
|
||
And a cancelled correction for plan "plan-1" decision "DEC-001"
|
||
When I try to execute the correction
|
||
Then a validation error should be raised with message containing "cancelled"
|
||
|
||
# ── Execution Result ─────────────────────────────────────────
|
||
|
||
|
||
Scenario: Revert execution produces reverted decisions
|
||
Given a correction service
|
||
And a correction request for plan "plan-1" decision "DEC-001" in revert mode with guidance "Revert this"
|
||
When I execute the correction
|
||
Then the result should have reverted decisions
|
||
And the result status should be "applied"
|
||
|
||
|
||
Scenario: Append execution produces no reverted decisions
|
||
Given a correction service
|
||
And a correction request for plan "plan-1" decision "DEC-001" in append mode with guidance "Append this"
|
||
When I execute the correction
|
||
Then the result should have no reverted decisions
|
||
And the result status should be "applied"
|
||
|
||
# ── Attempts ─────────────────────────────────────────────────
|
||
|
||
|
||
Scenario: Execution records an attempt
|
||
Given a correction service
|
||
And a correction request for plan "plan-1" decision "DEC-001" in revert mode with guidance "Track attempt"
|
||
When I execute the correction
|
||
Then there should be 1 attempt recorded
|
||
And the attempt should be successful
|
||
|
||
# ── Model Validation ─────────────────────────────────────────
|
||
|
||
|
||
Scenario: CorrectionRequest validates non-empty plan_id
|
||
When I try to create a CorrectionRequest with empty plan_id
|
||
Then a pydantic validation error should be raised
|
||
|
||
|
||
Scenario: CorrectionRequest validates non-empty target_decision_id
|
||
When I try to create a CorrectionRequest with empty target_decision_id
|
||
Then a pydantic validation error should be raised
|
||
|
||
|
||
Scenario: CorrectionRequest accepts empty guidance
|
||
When I try to create a CorrectionRequest with empty guidance
|
||
Then the correction should be created
|
||
|
||
|
||
Scenario: CorrectionImpact validates risk_level
|
||
When I try to create a CorrectionImpact with invalid risk_level "extreme"
|
||
Then a pydantic validation error should be raised
|
||
|
||
|
||
Scenario: CorrectionImpact accepts valid risk levels
|
||
When I create a CorrectionImpact with risk_level "low"
|
||
Then the impact should be created
|
||
When I create a CorrectionImpact with risk_level "medium"
|
||
Then the impact should be created
|
||
When I create a CorrectionImpact with risk_level "high"
|
||
Then the impact should be created
|
||
|
||
# ── Enum Tests ───────────────────────────────────────────────
|
||
|
||
|
||
Scenario: CorrectionMode enum values
|
||
Then CorrectionMode.REVERT should equal "revert"
|
||
And CorrectionMode.APPEND should equal "append"
|
||
|
||
|
||
Scenario: CorrectionStatus enum values
|
||
Then CorrectionStatus.PENDING should equal "pending"
|
||
And CorrectionStatus.ANALYZING should equal "analyzing"
|
||
And CorrectionStatus.EXECUTING should equal "executing"
|
||
And CorrectionStatus.APPLIED should equal "applied"
|
||
And CorrectionStatus.FAILED should equal "failed"
|
||
And CorrectionStatus.CANCELLED should equal "cancelled"
|
||
|
||
# ── CorrectionResult Model ──────────────────────────────────
|
||
|
||
|
||
Scenario: CorrectionResult with defaults
|
||
When I create a CorrectionResult with correction_id "corr-1" and status "applied"
|
||
Then the result should have empty new_decisions
|
||
And the result should have empty reverted_decisions
|
||
And the result error_message should be none
|
||
|
||
# ── CorrectionAttempt Model ─────────────────────────────────
|
||
|
||
|
||
Scenario: CorrectionAttempt default values
|
||
When I create a CorrectionAttempt for correction "corr-1"
|
||
Then the attempt success should be false
|
||
And the attempt completed_at should be none
|
||
And the attempt details should be empty
|
||
|
||
|
||
# ============================================================
|
||
# Originally from: correction_service_coverage_r2.feature
|
||
# Feature: CorrectionService exception-handling coverage (round 2)
|
||
# ============================================================
|
||
|
||
Scenario: execute_revert catches internal error and returns FAILED result
|
||
Given r2corr-a fresh correction service
|
||
Given r2corr-a stored revert correction for plan "P1" targeting "D1"
|
||
And r2corr-analyze_impact is patched to raise RuntimeError "boom in analyze"
|
||
When r2corr-I execute revert on the stored correction
|
||
Then r2corr-the revert result status should be "failed"
|
||
And r2corr-the revert result error_message should contain "boom in analyze"
|
||
And r2corr-the stored correction status should be "failed"
|
||
And r2corr-the first attempt should have success false
|
||
And r2corr-the first attempt details should contain error key
|
||
And r2corr-the first attempt details error should contain "boom in analyze"
|
||
And r2corr-the first attempt completed_at should be set
|
||
And r2corr-the result should be stored in the service results dict
|
||
|
||
|
||
Scenario: execute_revert exception path preserves attempt count
|
||
Given r2corr-a fresh correction service
|
||
Given r2corr-a stored revert correction for plan "P1" targeting "D2"
|
||
And r2corr-analyze_impact is patched to raise ValueError "bad value"
|
||
When r2corr-I execute revert on the stored correction
|
||
Then r2corr-the attempts for the stored correction should have 1 entry
|
||
And r2corr-the first attempt should have success false
|
||
|
||
|
||
Scenario: execute_revert exception with TypeError message
|
||
Given r2corr-a fresh correction service
|
||
Given r2corr-a stored revert correction for plan "P1" targeting "D3"
|
||
And r2corr-analyze_impact is patched to raise TypeError "wrong type"
|
||
When r2corr-I execute revert on the stored correction
|
||
Then r2corr-the revert result status should be "failed"
|
||
And r2corr-the revert result error_message should contain "wrong type"
|
||
And r2corr-the stored correction status should be "failed"
|
||
|
||
# -------------------------------------------------------------------
|
||
# execute_append – exception path (lines 320-328)
|
||
# -------------------------------------------------------------------
|
||
|
||
|
||
Scenario: execute_append catches internal error and returns FAILED result
|
||
Given r2corr-a fresh correction service
|
||
Given r2corr-a stored append correction for plan "P1" targeting "A1"
|
||
And r2corr-ULID is patched to raise RuntimeError "ulid broken"
|
||
When r2corr-I execute append on the stored correction
|
||
Then r2corr-the append result status should be "failed"
|
||
And r2corr-the append result error_message should contain "ulid broken"
|
||
And r2corr-the stored correction status should be "failed"
|
||
And r2corr-the first attempt should have success false
|
||
And r2corr-the first attempt details should contain error key
|
||
And r2corr-the first attempt details error should contain "ulid broken"
|
||
And r2corr-the first attempt completed_at should be set
|
||
And r2corr-the append result should be stored in the service results dict
|
||
|
||
|
||
Scenario: execute_append exception path preserves attempt count
|
||
Given r2corr-a fresh correction service
|
||
Given r2corr-a stored append correction for plan "P1" targeting "A2"
|
||
And r2corr-ULID is patched to raise ValueError "ulid value error"
|
||
When r2corr-I execute append on the stored correction
|
||
Then r2corr-the attempts for the stored correction should have 1 entry
|
||
And r2corr-the first attempt should have success false
|
||
|
||
|
||
Scenario: execute_append exception with OSError message
|
||
Given r2corr-a fresh correction service
|
||
Given r2corr-a stored append correction for plan "P1" targeting "A3"
|
||
And r2corr-ULID is patched to raise OSError "system failure"
|
||
When r2corr-I execute append on the stored correction
|
||
Then r2corr-the append result status should be "failed"
|
||
And r2corr-the append result error_message should contain "system failure"
|
||
And r2corr-the stored correction status should be "failed"
|
||
|
||
# -------------------------------------------------------------------
|
||
# execute_correction dispatch into exception paths
|
||
# -------------------------------------------------------------------
|
||
|
||
|
||
Scenario: dispatch to revert hits exception path when analyze_impact fails
|
||
Given r2corr-a fresh correction service
|
||
Given r2corr-a stored revert correction for plan "P1" targeting "DR1"
|
||
And r2corr-analyze_impact is patched to raise RuntimeError "dispatch revert boom"
|
||
When r2corr-I dispatch execute correction on the stored correction
|
||
Then r2corr-the dispatch result status should be "failed"
|
||
And r2corr-the dispatch result error_message should contain "dispatch revert boom"
|
||
|
||
|
||
Scenario: dispatch to append hits exception path when ULID fails
|
||
Given r2corr-a fresh correction service
|
||
Given r2corr-a stored append correction for plan "P1" targeting "DA1"
|
||
And r2corr-ULID is patched to raise RuntimeError "dispatch append boom"
|
||
When r2corr-I dispatch execute correction on the stored correction
|
||
Then r2corr-the dispatch result status should be "failed"
|
||
And r2corr-the dispatch result error_message should contain "dispatch append boom"
|
||
|
||
|
||
# ============================================================
|
||
# Originally from: correction_service_new_coverage.feature
|
||
# Feature: CorrectionService comprehensive coverage
|
||
# ============================================================
|
||
|
||
Scenario: Create a revert correction with valid parameters
|
||
Given a fresh correction service instance
|
||
When I create a correction for plan "PX1" targeting "DX1" in revert mode
|
||
Then the new correction should be stored in the service
|
||
And the new correction plan_id should equal "PX1"
|
||
And the new correction target_decision_id should equal "DX1"
|
||
And the new correction mode should equal "revert"
|
||
And the new correction status should equal "pending"
|
||
And the new correction dry_run should be false
|
||
|
||
|
||
Scenario: Create correction with empty plan_id raises ValidationError
|
||
Given a fresh correction service instance
|
||
When I attempt to create a correction with an empty plan_id
|
||
Then a service ValidationError should have been raised
|
||
|
||
|
||
Scenario: Create correction with empty target_decision_id raises ValidationError
|
||
Given a fresh correction service instance
|
||
When I attempt to create a correction with an empty target_decision_id
|
||
Then a service ValidationError should have been raised
|
||
|
||
|
||
Scenario: Create correction with guidance and dry_run flag
|
||
Given a fresh correction service instance
|
||
When I create a correction for plan "PX1" targeting "DX1" in append mode with guidance "Refactor auth" and dry_run true
|
||
Then the new correction guidance should equal "Refactor auth"
|
||
And the new correction dry_run should be true
|
||
And the new correction mode should equal "append"
|
||
|
||
# ── analyze_impact ───────────────────────────────────────────
|
||
|
||
|
||
Scenario: Analyze impact with no children yields single node and low risk
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "DX1"
|
||
When I analyze impact with an empty decision tree
|
||
Then the impact affected decisions should be exactly "DX1"
|
||
And the impact risk level should equal "low"
|
||
And the impact rollback tier should equal "full"
|
||
And the impact estimated cost should equal 1.5
|
||
And the impact artifacts to archive should be exactly "DX1.artifact"
|
||
And the impact affected files should be exactly "DX1.py"
|
||
|
||
|
||
Scenario: Analyze impact with deep BFS tree traversal
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "A"
|
||
When I analyze impact with tree "A->B,C;B->D,E;C->F"
|
||
Then the impact affected decisions should be exactly "A,B,C,D,E,F"
|
||
And the impact risk level should equal "medium"
|
||
|
||
|
||
Scenario: Analyze impact with medium risk subtree of 4 nodes
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "R"
|
||
When I analyze impact with tree "R->C1,C2,C3,C4"
|
||
Then the impact risk level should equal "medium"
|
||
And the impact affected decisions count should be 5
|
||
|
||
|
||
Scenario: Analyze impact with high risk subtree of 11 or more nodes
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "R"
|
||
When I analyze impact with a chain tree of 12 nodes rooted at "R"
|
||
Then the impact risk level should equal "high"
|
||
And the impact affected decisions count should be 12
|
||
|
||
|
||
Scenario: Analyze impact with non-existent correction raises ResourceNotFoundError
|
||
Given a fresh correction service instance
|
||
When I attempt to analyze impact for correction "bogus-id-999"
|
||
Then a service ResourceNotFoundError should have been raised
|
||
|
||
|
||
Scenario: Analyze impact sets status to analyzing
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "DX1"
|
||
When I analyze impact with an empty decision tree
|
||
Then the stored correction status should be "analyzing"
|
||
|
||
|
||
Scenario: Analyze impact for append mode yields append_only rollback tier
|
||
Given a fresh correction service instance
|
||
And a stored append correction for plan "PX1" targeting "DX1"
|
||
When I analyze impact with an empty decision tree
|
||
Then the impact rollback tier should equal "append_only"
|
||
|
||
# ── generate_dry_run_report ──────────────────────────────────
|
||
|
||
|
||
Scenario: Dry-run report for revert with high risk includes high-risk warning
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "R"
|
||
When I generate a dry-run report with a chain tree of 15 nodes rooted at "R"
|
||
Then the dry-run report mode should equal "revert"
|
||
And the dry-run report warnings should include "High risk"
|
||
And the dry-run report warnings should include "Revert will invalidate"
|
||
And the dry-run report decisions to invalidate should not be empty
|
||
And the dry-run report estimated recompute time should be 30.0
|
||
|
||
|
||
Scenario: Dry-run report for revert with medium risk includes medium-risk warning
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "R"
|
||
When I generate a dry-run report with a chain tree of 7 nodes rooted at "R"
|
||
Then the dry-run report warnings should include "Medium risk"
|
||
And the dry-run report warnings should include "Revert will invalidate"
|
||
|
||
|
||
Scenario: Dry-run report for append has empty decisions to invalidate
|
||
Given a fresh correction service instance
|
||
And a stored append correction for plan "PX1" targeting "DX1"
|
||
When I generate a dry-run report with an empty decision tree
|
||
Then the dry-run report decisions to invalidate should be empty
|
||
And the dry-run report mode should equal "append"
|
||
|
||
# ── execute_revert ───────────────────────────────────────────
|
||
|
||
|
||
Scenario: Execute revert with simple tree returns applied result
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "DX1"
|
||
When I execute revert with tree "DX1->DX2"
|
||
Then the revert result status should equal "applied"
|
||
And the revert result reverted decisions should include "DX1"
|
||
And the revert result reverted decisions should include "DX2"
|
||
And the revert result archived artifacts should include "DX1.artifact"
|
||
And the revert result archived artifacts should include "DX2.artifact"
|
||
|
||
|
||
Scenario: Execute revert records a successful attempt with completed_at
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "DX1"
|
||
When I execute revert with an empty decision tree
|
||
Then the service attempts for the correction should have 1 entry
|
||
And the first attempt should have success true
|
||
And the first attempt completed_at should not be none
|
||
|
||
# ── execute_append ───────────────────────────────────────────
|
||
|
||
|
||
Scenario: Execute append creates a child plan and new decision
|
||
Given a fresh correction service instance
|
||
And a stored append correction for plan "PX1" targeting "DX1"
|
||
When I execute append for the stored correction
|
||
Then the append result status should equal "applied"
|
||
And the append result spawned_child_plan_id should not be none
|
||
And the append result new decisions should not be empty
|
||
|
||
# ── execute_correction dispatch ──────────────────────────────
|
||
|
||
|
||
Scenario: Execute correction dispatches to revert for REVERT mode
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "DX1"
|
||
When I execute correction via the dispatch method with tree "DX1->DX2"
|
||
Then the dispatched result status should equal "applied"
|
||
And the dispatched result reverted decisions should include "DX1"
|
||
|
||
|
||
Scenario: Execute correction dispatches to append for APPEND mode
|
||
Given a fresh correction service instance
|
||
And a stored append correction for plan "PX1" targeting "DX1"
|
||
When I execute correction via the dispatch method with no tree
|
||
Then the dispatched result status should equal "applied"
|
||
And the dispatched result spawned_child_plan_id should not be none
|
||
|
||
# ── get_correction ───────────────────────────────────────────
|
||
|
||
|
||
Scenario: Get correction with valid id returns the request
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "DX1"
|
||
When I get the stored correction by its id
|
||
Then the retrieved correction plan_id should equal "PX1"
|
||
And the retrieved correction target_decision_id should equal "DX1"
|
||
|
||
|
||
Scenario: Get correction with invalid id raises ResourceNotFoundError
|
||
Given a fresh correction service instance
|
||
When I attempt to get correction with id "nonexistent-xyz"
|
||
Then a service ResourceNotFoundError should have been raised
|
||
|
||
# ── list_corrections ─────────────────────────────────────────
|
||
|
||
|
||
Scenario: List corrections without filter returns all corrections
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "DX1"
|
||
And a stored append correction for plan "PX2" targeting "DX2"
|
||
When I list all service corrections without filter
|
||
Then the service corrections list should have 2 items
|
||
|
||
|
||
Scenario: List corrections with plan_id filter returns only matching
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "DX1"
|
||
And a stored append correction for plan "PX1" targeting "DX2"
|
||
And a stored revert correction for plan "PX2" targeting "DX3"
|
||
When I list service corrections for plan "PX1"
|
||
Then the service corrections list should have 2 items
|
||
When I list service corrections for plan "PX2"
|
||
Then the service corrections list should have 1 items
|
||
|
||
# ── list_attempts ────────────────────────────────────────────
|
||
|
||
|
||
Scenario: List attempts for a valid correction returns attempts
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "DX1"
|
||
And the stored correction has been reverted with an empty tree
|
||
When I list attempts for the stored correction
|
||
Then the attempts list should have 1 entry
|
||
|
||
# ── cancel_correction ────────────────────────────────────────
|
||
|
||
|
||
Scenario: Cancel correction in pending status succeeds
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "DX1"
|
||
When I cancel the stored correction
|
||
Then the stored correction status should be "cancelled"
|
||
|
||
|
||
Scenario: Cancel correction in applied status raises ValidationError
|
||
Given a fresh correction service instance
|
||
And a stored revert correction for plan "PX1" targeting "DX1"
|
||
And the stored correction has been reverted with an empty tree
|
||
When I attempt to cancel the stored correction
|
||
Then a service ValidationError should have been raised
|
||
|
||
# ── _classify_risk boundary tests ────────────────────────────
|
||
|
||
|
||
Scenario: Classify risk at exact boundary values
|
||
Given a fresh correction service instance
|
||
Then classifying risk for 0 affected nodes should return "low"
|
||
And classifying risk for 1 affected nodes should return "low"
|
||
And classifying risk for 3 affected nodes should return "low"
|
||
And classifying risk for 4 affected nodes should return "medium"
|
||
And classifying risk for 10 affected nodes should return "medium"
|
||
And classifying risk for 11 affected nodes should return "high"
|
||
And classifying risk for 100 affected nodes should return "high"
|
||
|
||
# ── _compute_affected_subtree BFS order ──────────────────────
|
||
|
||
|
||
Scenario: Compute affected subtree follows BFS order
|
||
Given a fresh correction service instance
|
||
When I compute affected subtree for "A" with tree "A->B,C;B->D;C->E"
|
||
Then the affected subtree should be exactly "A,B,C,D,E" in order
|
||
|
||
|
||
Scenario: Compute affected subtree with no children returns only root
|
||
Given a fresh correction service instance
|
||
When I compute affected subtree for "LEAF" with an empty tree
|
||
Then the affected subtree should be exactly "LEAF" in order
|
||
|
||
|
||
Scenario: Compute affected subtree with wide tree
|
||
Given a fresh correction service instance
|
||
When I compute affected subtree for "R" with tree "R->A,B,C,D,E"
|
||
Then the affected subtree should be exactly "R,A,B,C,D,E" in order
|
||
And the affected subtree count should be 6
|