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
962 lines
37 KiB
Gherkin
962 lines
37 KiB
Gherkin
Feature: Consolidated Plan Model Lifecycle
|
|
Combined scenarios from: plan_hierarchy_and_failure_handler, plan_lifecycle_helpers_coverage, plan_lifecycle_service_persistence_coverage, plan_lifecycle_transitions_r2, plan_model, plan_model_coverage, plan_model_uncovered_lines
|
|
|
|
# ============================================================
|
|
# Originally from: plan_hierarchy_and_failure_handler.feature
|
|
# Feature: Plan Hierarchy and Subplan Failure Handling
|
|
# ============================================================
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A standalone plan is not considered a subplan
|
|
Given a plan that was created independently
|
|
Then the plan should not be recognized as a subplan
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A plan spawned by another plan is a subplan
|
|
Given a plan that was spawned by another plan
|
|
Then the plan should be recognized as a subplan
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A plan with no designated root is considered the root
|
|
Given a plan with no designated root
|
|
Then the plan should be recognized as a root plan
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A plan whose root is itself is considered the root
|
|
Given a plan whose designated root is itself
|
|
Then the plan should be recognized as a root plan
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A plan whose root is a different plan is not the root
|
|
Given a plan whose designated root is a different plan
|
|
Then the plan should not be recognized as a root plan
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A root plan reports a depth of zero
|
|
Given a plan with no designated root
|
|
Then the plan hierarchy depth should be 0
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A non-root plan reports a placeholder depth
|
|
Given a plan whose designated root is a different plan
|
|
Then the plan hierarchy depth should be -1
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A plan with no child work has no subplans
|
|
Given a plan that has no child work tracked
|
|
Then the plan should report having no subplans
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A plan with tracked child work has subplans
|
|
Given a plan that has tracked child work
|
|
Then the plan should report having subplans
|
|
|
|
|
|
@failure_handler
|
|
Scenario: The failure handler halts remaining work when fail-fast is enabled
|
|
Given a subplan configuration with fail-fast enabled and parallel work
|
|
And a subplan that has failed
|
|
When the failure handler evaluates whether to halt remaining work
|
|
Then the remaining work should be halted
|
|
|
|
|
|
@failure_handler
|
|
Scenario: The failure handler halts remaining work for sequential execution
|
|
Given a subplan configuration with fail-fast disabled and sequential work
|
|
And a subplan that has failed
|
|
When the failure handler evaluates whether to halt remaining work
|
|
Then the remaining work should be halted
|
|
|
|
|
|
@failure_handler
|
|
Scenario: The failure handler allows remaining work for parallel execution without fail-fast
|
|
Given a subplan configuration with fail-fast disabled and parallel work
|
|
And a subplan that has failed
|
|
When the failure handler evaluates whether to halt remaining work
|
|
Then the remaining work should continue
|
|
|
|
|
|
@failure_handler
|
|
Scenario: The failure handler allows remaining work for dependency-ordered execution without fail-fast
|
|
Given a subplan configuration with fail-fast disabled and dependency-ordered work
|
|
And a subplan that has failed
|
|
When the failure handler evaluates whether to halt remaining work
|
|
Then the remaining work should continue
|
|
|
|
|
|
@failure_handler
|
|
Scenario: The failure handler does not retry when retries are disabled
|
|
Given a subplan configuration with retries disabled
|
|
And a subplan that failed with error "ValidationError occurred"
|
|
When the failure handler evaluates whether to retry the failed work
|
|
Then the failed work should not be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: The failure handler does not retry when all attempts are exhausted
|
|
Given a subplan configuration allowing up to 2 retries
|
|
And a subplan on attempt 3 that failed with error "TimeoutError"
|
|
When the failure handler evaluates whether to retry the failed work
|
|
Then the failed work should not be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A configuration error is not eligible for retry
|
|
Given a subplan configuration allowing up to 3 retries
|
|
And a subplan on attempt 1 that failed with error "ConfigurationError: bad config"
|
|
When the failure handler evaluates whether to retry the failed work
|
|
Then the failed work should not be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: An authentication error is not eligible for retry
|
|
Given a subplan configuration allowing up to 3 retries
|
|
And a subplan on attempt 1 that failed with error "AuthenticationError: invalid token"
|
|
When the failure handler evaluates whether to retry the failed work
|
|
Then the failed work should not be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A missing resource error is not eligible for retry
|
|
Given a subplan configuration allowing up to 3 retries
|
|
And a subplan on attempt 1 that failed with error "MissingResourceError: file not found"
|
|
When the failure handler evaluates whether to retry the failed work
|
|
Then the failed work should not be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A circular dependency error is not eligible for retry
|
|
Given a subplan configuration allowing up to 3 retries
|
|
And a subplan on attempt 1 that failed with error "CircularDependencyError: cycle detected"
|
|
When the failure handler evaluates whether to retry the failed work
|
|
Then the failed work should not be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A validation error is eligible for retry
|
|
Given a subplan configuration allowing up to 3 retries
|
|
And a subplan on attempt 1 that failed with error "ValidationError: schema mismatch"
|
|
When the failure handler evaluates whether to retry the failed work
|
|
Then the failed work should be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A timeout error is eligible for retry
|
|
Given a subplan configuration allowing up to 3 retries
|
|
And a subplan on attempt 1 that failed with error "TimeoutError: deadline exceeded"
|
|
When the failure handler evaluates whether to retry the failed work
|
|
Then the failed work should be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A temporary resource error is eligible for retry
|
|
Given a subplan configuration allowing up to 3 retries
|
|
And a subplan on attempt 1 that failed with error "TemporaryResourceError: service unavailable"
|
|
When the failure handler evaluates whether to retry the failed work
|
|
Then the failed work should be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A merge conflict error is eligible for retry
|
|
Given a subplan configuration allowing up to 3 retries
|
|
And a subplan on attempt 1 that failed with error "MergeConflictError: conflicting changes"
|
|
When the failure handler evaluates whether to retry the failed work
|
|
Then the failed work should be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: An unrecognized error type is not eligible for retry
|
|
Given a subplan configuration allowing up to 3 retries
|
|
And a subplan on attempt 1 that failed with error "SomeUnknownError: unexpected"
|
|
When the failure handler evaluates whether to retry the failed work
|
|
Then the failed work should not be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A failure with no error message is not eligible for retry
|
|
Given a subplan configuration allowing up to 3 retries
|
|
And a subplan on attempt 1 that failed with no error message
|
|
When the failure handler evaluates whether to retry the failed work
|
|
Then the failed work should not be retried
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: plan_lifecycle_helpers_coverage.feature
|
|
# Feature: Plan lifecycle helper coverage
|
|
# ============================================================
|
|
|
|
Scenario: Lifecycle service helper builds a plan lifecycle service
|
|
When I call the plan lifecycle service helper
|
|
Then the lifecycle service helper should return a plan lifecycle service
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: plan_lifecycle_service_persistence_coverage.feature
|
|
# Feature: PlanLifecycleService persistence fallback coverage
|
|
# ============================================================
|
|
|
|
Scenario: get_action falls back to persistence layer when not in memory
|
|
Given a plan lifecycle service with a mock unit of work
|
|
Given an action "local/db-only-action" exists only in the persistence layer
|
|
When I call get_action with "local/db-only-action"
|
|
Then the returned action should have namespaced name "local/db-only-action"
|
|
And the action "local/db-only-action" should now be cached in memory
|
|
|
|
|
|
Scenario: get_action_by_name falls back to persistence layer when not in memory
|
|
Given a plan lifecycle service with a mock unit of work
|
|
Given an action "local/db-only-named" exists only in the persistence layer for name lookup
|
|
When I call get_action_by_name with "local/db-only-named"
|
|
Then the returned action from name lookup should have namespaced name "local/db-only-named"
|
|
And the action "local/db-only-named" should now be cached in memory after name lookup
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: plan_lifecycle_transitions_r2.feature
|
|
# Feature: PlanLifecycleService lifecycle transition branch coverage (round 2)
|
|
# ============================================================
|
|
|
|
Scenario: use_action on non-reusable action archives it after plan creation
|
|
Given r2plc-a fresh plan lifecycle service with mock UoW
|
|
Given r2plc-a non-reusable action "local/r2-oneshot" exists
|
|
When r2plc-I use the non-reusable action to create a plan
|
|
Then r2plc-the action "local/r2-oneshot" should be archived
|
|
And r2plc-a plan should have been created from the action
|
|
|
|
# -------------------------------------------------------------------
|
|
# execute_plan persisted mode (line 216 True via _commit_plan)
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
Scenario: execute_plan transitions and persists via _commit_plan
|
|
Given r2plc-a fresh plan lifecycle service with mock UoW
|
|
Given r2plc-a plan in STRATEGIZE COMPLETE state
|
|
When r2plc-I call execute_plan in persisted mode
|
|
Then r2plc-the plan should be in EXECUTE phase
|
|
And r2plc-the mock UoW should have received a plan update call
|
|
|
|
# -------------------------------------------------------------------
|
|
# apply_plan persisted mode (line 216 True via _commit_plan)
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
Scenario: apply_plan transitions and persists via _commit_plan
|
|
Given r2plc-a fresh plan lifecycle service with mock UoW
|
|
Given r2plc-a plan in EXECUTE COMPLETE state
|
|
When r2plc-I call apply_plan in persisted mode
|
|
Then r2plc-the plan should be in APPLY phase
|
|
And r2plc-the mock UoW should have received a plan update call
|
|
|
|
# -------------------------------------------------------------------
|
|
# cancel_plan persisted mode (line 216 True via _commit_plan)
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
Scenario: cancel_plan persists cancellation via _commit_plan
|
|
Given r2plc-a fresh plan lifecycle service with mock UoW
|
|
Given r2plc-a plan in STRATEGIZE phase
|
|
When r2plc-I cancel the plan with reason "user requested"
|
|
Then r2plc-the plan processing state should be "cancelled"
|
|
And r2plc-the mock UoW should have received a plan update call
|
|
|
|
# -------------------------------------------------------------------
|
|
# pause_plan and resume_plan persisted mode
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
Scenario: pause_plan persists via _commit_plan
|
|
Given r2plc-a fresh plan lifecycle service with mock UoW
|
|
Given r2plc-a plan in STRATEGIZE phase
|
|
When r2plc-I pause the plan
|
|
Then r2plc-the plan automation profile should be manual
|
|
And r2plc-the mock UoW should have received a plan update call
|
|
|
|
|
|
Scenario: resume_plan persists and auto-progresses via _commit_plan
|
|
Given r2plc-a fresh plan lifecycle service with mock UoW
|
|
Given r2plc-a plan in STRATEGIZE phase
|
|
When r2plc-I resume the plan with profile "auto"
|
|
Then r2plc-the plan automation profile should be "auto"
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: plan_model.feature
|
|
# Feature: Plan Domain Model
|
|
# ============================================================
|
|
|
|
Scenario: Parse simple name defaults to local namespace
|
|
When I parse the namespaced name "my-action"
|
|
Then the parsed namespace should be "local"
|
|
And the parsed item name should be "my-action"
|
|
And the parsed server should be empty
|
|
|
|
|
|
Scenario: Parse name with explicit local namespace
|
|
When I parse the namespaced name "local/my-action"
|
|
Then the parsed namespace should be "local"
|
|
And the parsed item name should be "my-action"
|
|
|
|
|
|
Scenario: Parse name with organization namespace
|
|
When I parse the namespaced name "myorg/my-action"
|
|
Then the parsed namespace should be "myorg"
|
|
And the parsed item name should be "my-action"
|
|
|
|
|
|
Scenario: Parse name with server qualifier
|
|
When I parse the namespaced name "prod:myorg/my-action"
|
|
Then the parsed server should be "prod"
|
|
And the parsed namespace should be "myorg"
|
|
And the parsed item name should be "my-action"
|
|
|
|
|
|
Scenario: NamespacedName string representation
|
|
Given I have a namespaced name with server "prod" namespace "myorg" and name "my-action"
|
|
Then the namespaced string representation should be "prod:myorg/my-action"
|
|
|
|
|
|
Scenario: NamespacedName without server
|
|
Given I have a namespaced name with namespace "local" and name "test-plan"
|
|
Then the namespaced string representation should be "local/test-plan"
|
|
|
|
|
|
Scenario: Invalid namespace with special characters fails
|
|
When I try to create a namespaced name with namespace "my@org" and name "test"
|
|
Then a validation error should be raised
|
|
|
|
|
|
Scenario: Empty namespace defaults to local
|
|
Given I create a namespaced name with an empty namespace and name "test-plan"
|
|
Then the parsed namespace should be "local"
|
|
|
|
# PlanPhase Tests
|
|
|
|
|
|
Scenario: Plan phases are in correct order
|
|
Then the plan phases should be in order "action, strategize, execute, apply"
|
|
|
|
|
|
Scenario: STRATEGIZE phase is the starting phase
|
|
Given I create a new plan
|
|
Then the plan phase should be "strategize"
|
|
|
|
# PlanState Tests
|
|
|
|
|
|
Scenario: New plan starts in STRATEGIZE with QUEUED processing state
|
|
Given I create a PlanModel in strategize phase
|
|
Then the plan phase should be "strategize"
|
|
And the plan processing state should be "queued"
|
|
|
|
|
|
Scenario: Strategize phase uses ProcessingState
|
|
Given I create a PlanModel in strategize phase
|
|
Then the plan processing state should be "queued"
|
|
|
|
|
|
Scenario: Plan in strategize phase defaults to QUEUED processing state
|
|
Given I create a PlanModel in strategize phase
|
|
Then the plan processing state should be "queued"
|
|
And the plan state should be "queued"
|
|
|
|
|
|
Scenario: Strategize phase defaults processing_state to QUEUED
|
|
Given I create a PlanModel in strategize phase
|
|
Then the plan processing state should be "queued"
|
|
|
|
# Plan Identity Tests
|
|
|
|
|
|
Scenario: Plan requires valid ULID
|
|
When I try to create a plan identity with invalid ULID "not-a-ulid"
|
|
Then a validation error should be raised
|
|
|
|
|
|
Scenario: Plan identity with valid ULID succeeds
|
|
When I create a plan identity with ULID "01ARZ3NDEKTSV4RRFFQ69G5FAV"
|
|
Then the plan identity should be valid
|
|
|
|
|
|
Scenario: Plan identity supports parent and root plan IDs
|
|
When I create a plan identity with parent "01ARZ3NDEKTSV4RRFFQ69G5FAV" and root "01ARZ3NDEKTSV4RRFFQ69G5FAW"
|
|
Then the parent plan ID should be "01ARZ3NDEKTSV4RRFFQ69G5FAV"
|
|
And the root plan ID should be "01ARZ3NDEKTSV4RRFFQ69G5FAW"
|
|
|
|
# Plan Lifecycle Tests
|
|
|
|
|
|
Scenario: New plan starts in STRATEGIZE phase with QUEUED state
|
|
Given I create a new plan with description "Test plan"
|
|
Then the plan phase should be "strategize"
|
|
And the plan processing state should be "queued"
|
|
And the plan should not be terminal
|
|
|
|
|
|
Scenario: Plan in STRATEGIZE with QUEUED cannot transition
|
|
Given I create a PlanModel in strategize phase
|
|
Then the plan cannot transition to next phase
|
|
|
|
|
|
Scenario: New plan in STRATEGIZE with QUEUED cannot transition
|
|
Given I create a PlanModel in strategize phase
|
|
Then the plan cannot transition to next phase
|
|
|
|
|
|
Scenario: Processing complete allows transition
|
|
Given I create a plan in execute phase with complete processing state
|
|
Then the plan can transition to next phase
|
|
|
|
|
|
Scenario: Get next phase from STRATEGIZE is EXECUTE
|
|
Given I create a new plan
|
|
Then the next phase should be "execute"
|
|
|
|
|
|
Scenario: Get next phase from APPLY is None (terminal phase)
|
|
Given I create a plan in apply phase as terminal
|
|
Then the next phase should be empty
|
|
|
|
|
|
Scenario: Unknown phase returns no next phase
|
|
Given I create a plan with an unknown phase value
|
|
Then the next phase should be empty
|
|
|
|
# Phase Transition Validation Tests
|
|
|
|
|
|
Scenario: Valid transition from STRATEGIZE to EXECUTE
|
|
Then transition from "strategize" to "execute" should be valid
|
|
|
|
|
|
Scenario: Valid transition from EXECUTE to APPLY
|
|
Then transition from "execute" to "apply" should be valid
|
|
|
|
|
|
Scenario: Valid transition from APPLY to STRATEGIZE (revert via constrained)
|
|
Then transition from "apply" to "strategize" should be valid
|
|
|
|
|
|
Scenario: Invalid transition from STRATEGIZE to APPLY
|
|
Then transition from "strategize" to "apply" should be invalid
|
|
|
|
|
|
Scenario: Valid transition from ACTION to STRATEGIZE
|
|
Then transition from "action" to "strategize" should be valid
|
|
|
|
# Error State Tests
|
|
|
|
|
|
Scenario: Plan in errored state is_errored returns True
|
|
Given I create a PlanModel in strategize phase with errored state
|
|
Then the plan should be errored
|
|
|
|
|
|
Scenario: Plan in processing state is_errored returns False
|
|
Given I create a PlanModel in strategize phase with processing state
|
|
Then the plan should not be errored
|
|
|
|
|
|
Scenario: Cancelled plan is terminal
|
|
Given I create a PlanModel in strategize phase with cancelled state
|
|
Then the plan should be terminal
|
|
|
|
# Model Validation Tests
|
|
|
|
|
|
Scenario: Plan requires description
|
|
When I try to create a plan without description
|
|
Then a validation error should be raised
|
|
|
|
|
|
Scenario: Plan description cannot be empty
|
|
When I try to create a plan with empty description
|
|
Then a validation error should be raised
|
|
|
|
|
|
Scenario: Invalid phase value raises validation error
|
|
When I try to create a plan with invalid phase value
|
|
Then a validation error should be raised
|
|
|
|
|
|
Scenario: Apply phase cannot transition to next phase
|
|
Given I create a plan in apply phase as terminal
|
|
Then the plan cannot transition to next phase
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: plan_model_coverage.feature
|
|
# Feature: Plan Model Coverage — New Fields and Helpers
|
|
# ============================================================
|
|
|
|
Scenario: Invalid name with special characters raises validation error
|
|
When I try to create a namespaced name with namespace "local" and name "test@action"
|
|
Then a validation error should be raised
|
|
|
|
# ProjectLink.validate_alias classmethod
|
|
|
|
|
|
Scenario: ProjectLink.validate_alias accepts a valid alias
|
|
When I validate the project link alias "my-service"
|
|
Then the alias should be valid
|
|
|
|
|
|
Scenario: ProjectLink.validate_alias rejects an alias starting with a digit
|
|
When I validate the project link alias "1bad-alias"
|
|
Then the alias should be invalid
|
|
|
|
|
|
Scenario: ProjectLink.validate_alias rejects an alias with uppercase
|
|
When I validate the project link alias "BadAlias"
|
|
Then the alias should be invalid
|
|
|
|
|
|
Scenario: ProjectLink.validate_alias rejects an empty alias
|
|
When I validate an empty project link alias
|
|
Then the alias should be invalid
|
|
|
|
# ProjectLink field validator — invalid alias on construction
|
|
|
|
|
|
Scenario: Creating a ProjectLink with an invalid alias raises validation error
|
|
When I try to create a project link with alias "INVALID!"
|
|
Then a validation error should be raised
|
|
|
|
|
|
Scenario: Creating a ProjectLink with a valid alias succeeds
|
|
When I create a project link with alias "api-svc"
|
|
Then the project link alias should be "api-svc"
|
|
|
|
# Plan — APPLY phase with APPLIED processing_state is terminal
|
|
|
|
|
|
Scenario: APPLY phase with APPLIED processing state is terminal
|
|
When I create a plan in apply phase with applied processing state
|
|
Then the plan processing state should be "applied"
|
|
And the plan should be terminal
|
|
|
|
# Plan — duplicate project link alias uniqueness
|
|
|
|
|
|
Scenario: Duplicate project link aliases raise validation error
|
|
When I try to create a plan with duplicate project link aliases
|
|
Then a validation error should be raised
|
|
|
|
|
|
Scenario: Distinct project link aliases are accepted
|
|
When I create a plan with distinct project link aliases
|
|
Then the plan should have 2 project links
|
|
|
|
# Plan.as_cli_dict — base fields
|
|
|
|
|
|
Scenario: as_cli_dict includes base identification fields
|
|
Given I create a plan for CLI output
|
|
When I call as_cli_dict
|
|
Then the CLI dict should contain key "plan_id"
|
|
And the CLI dict should contain key "name"
|
|
And the CLI dict should contain key "action"
|
|
And the CLI dict should contain key "phase"
|
|
And the CLI dict should contain key "state"
|
|
And the CLI dict should contain key "description"
|
|
And the CLI dict should contain key "created_at"
|
|
And the CLI dict should contain key "updated_at"
|
|
|
|
# Plan.as_cli_dict — definition_of_done
|
|
|
|
|
|
Scenario: as_cli_dict includes definition_of_done when set
|
|
Given I create a plan with definition of done "all tests pass"
|
|
When I call as_cli_dict
|
|
Then the CLI dict key "definition_of_done" should be "all tests pass"
|
|
|
|
|
|
Scenario: as_cli_dict omits definition_of_done when not set
|
|
Given I create a plan for CLI output
|
|
When I call as_cli_dict
|
|
Then the CLI dict should not contain key "definition_of_done"
|
|
|
|
# Plan.as_cli_dict — automation profile provenance
|
|
|
|
|
|
Scenario: as_cli_dict includes automation profile with provenance
|
|
Given I create a plan with automation profile "trusted" from "action"
|
|
When I call as_cli_dict
|
|
Then the CLI dict key "automation_profile" should be "trusted"
|
|
And the CLI dict key "automation_profile_source" should be "action"
|
|
|
|
|
|
Scenario: as_cli_dict omits automation profile when not set
|
|
Given I create a plan for CLI output
|
|
When I call as_cli_dict
|
|
Then the CLI dict should not contain key "automation_profile"
|
|
|
|
# Plan.as_cli_dict — project links
|
|
|
|
|
|
Scenario: as_cli_dict renders project links with alias and read_only
|
|
Given I create a plan with project links
|
|
When I call as_cli_dict
|
|
Then the CLI dict key "projects" should be a list of length 2
|
|
|
|
|
|
Scenario: as_cli_dict omits projects when none are linked
|
|
Given I create a plan for CLI output
|
|
When I call as_cli_dict
|
|
Then the CLI dict should not contain key "projects"
|
|
|
|
# Plan.as_cli_dict — invariants
|
|
|
|
|
|
Scenario: as_cli_dict renders invariants with source tags
|
|
Given I create a plan with invariants
|
|
When I call as_cli_dict
|
|
Then the CLI dict key "invariants" should be a list of length 2
|
|
|
|
|
|
Scenario: as_cli_dict omits invariants when empty
|
|
Given I create a plan for CLI output
|
|
When I call as_cli_dict
|
|
Then the CLI dict should not contain key "invariants"
|
|
|
|
# Plan.as_cli_dict — arguments
|
|
|
|
|
|
Scenario: as_cli_dict renders arguments in stable order
|
|
Given I create a plan with arguments
|
|
When I call as_cli_dict
|
|
Then the CLI dict should contain key "arguments"
|
|
And the CLI dict arguments should preserve order
|
|
|
|
|
|
Scenario: as_cli_dict omits arguments when empty
|
|
Given I create a plan for CLI output
|
|
When I call as_cli_dict
|
|
Then the CLI dict should not contain key "arguments"
|
|
|
|
# Plan.as_cli_dict — actor fields
|
|
|
|
|
|
Scenario: as_cli_dict includes strategy_actor when set
|
|
Given I create a plan with strategy actor "local/planner"
|
|
When I call as_cli_dict
|
|
Then the CLI dict key "strategy_actor" should be "local/planner"
|
|
|
|
|
|
Scenario: as_cli_dict includes execution_actor when set
|
|
Given I create a plan with execution actor "local/coder"
|
|
When I call as_cli_dict
|
|
Then the CLI dict key "execution_actor" should be "local/coder"
|
|
|
|
|
|
Scenario: as_cli_dict omits actor fields when not set
|
|
Given I create a plan for CLI output
|
|
When I call as_cli_dict
|
|
Then the CLI dict should not contain key "strategy_actor"
|
|
And the CLI dict should not contain key "execution_actor"
|
|
|
|
# Plan.as_cli_dict — error message
|
|
|
|
|
|
Scenario: as_cli_dict includes error when present
|
|
Given I create a plan with error message "sandbox timeout"
|
|
When I call as_cli_dict
|
|
Then the CLI dict key "error" should be "sandbox timeout"
|
|
|
|
|
|
Scenario: as_cli_dict omits error when not present
|
|
Given I create a plan for CLI output
|
|
When I call as_cli_dict
|
|
Then the CLI dict should not contain key "error"
|
|
|
|
# Plan.as_cli_dict — parent plan and subplan count
|
|
|
|
|
|
Scenario: as_cli_dict includes parent_plan_id for subplans
|
|
Given I create a subplan for CLI output
|
|
When I call as_cli_dict
|
|
Then the CLI dict should contain key "parent_plan_id"
|
|
|
|
|
|
Scenario: as_cli_dict includes subplan_count when subplans exist
|
|
Given I create a plan with subplan statuses
|
|
When I call as_cli_dict
|
|
Then the CLI dict key "subplan_count" should be 1
|
|
|
|
|
|
Scenario: as_cli_dict omits parent and subplan fields for root plan
|
|
Given I create a plan for CLI output
|
|
When I call as_cli_dict
|
|
Then the CLI dict should not contain key "parent_plan_id"
|
|
And the CLI dict should not contain key "subplan_count"
|
|
|
|
# ---- is_subplan / is_root_plan / depth properties ----
|
|
|
|
|
|
Scenario: Root plan is not a subplan and has depth zero
|
|
Given I create a plan for CLI output
|
|
Then the plan should not be a subplan
|
|
And the plan should be a root plan
|
|
And the plan depth should be 0
|
|
|
|
|
|
Scenario: Child plan is a subplan and has depth minus one
|
|
Given I create a child plan with a parent
|
|
Then the plan should be a subplan
|
|
And the plan should not be a root plan
|
|
And the plan depth should be -1
|
|
|
|
# ---- SubplanFailureHandler.should_stop_others ----
|
|
|
|
|
|
Scenario: Failure handler stops others when fail_fast is true
|
|
Given a subplan config with fail_fast enabled and sequential mode
|
|
And a failed subplan status
|
|
When I check should_stop_others
|
|
Then should_stop_others should be true
|
|
|
|
|
|
Scenario: Failure handler stops others in sequential mode
|
|
Given a subplan config with fail_fast disabled and sequential mode
|
|
And a failed subplan status
|
|
When I check should_stop_others
|
|
Then should_stop_others should be true
|
|
|
|
|
|
Scenario: Failure handler does not stop others in parallel mode without fail_fast
|
|
Given a subplan config with fail_fast disabled and parallel mode
|
|
And a failed subplan status
|
|
When I check should_stop_others
|
|
Then should_stop_others should be false
|
|
|
|
# ---- SubplanFailureHandler.should_retry ----
|
|
|
|
|
|
Scenario: Failure handler does not retry when retry_failed is false
|
|
Given a subplan config with retry disabled
|
|
And a failed subplan status with error "TimeoutError"
|
|
When I check should_retry
|
|
Then should_retry should be false
|
|
|
|
|
|
Scenario: Failure handler does not retry when max retries exceeded
|
|
Given a subplan config with max retries 1
|
|
And a failed subplan status on attempt 2 with error "TimeoutError"
|
|
When I check should_retry
|
|
Then should_retry should be false
|
|
|
|
|
|
Scenario: Failure handler retries retriable failure within limit
|
|
Given a subplan config with retry enabled and max retries 2
|
|
And a failed subplan status on attempt 1 with error "TimeoutError"
|
|
When I check should_retry
|
|
Then should_retry should be true
|
|
|
|
|
|
Scenario: Failure handler does not retry non-retriable error
|
|
Given a subplan config with retry enabled and max retries 2
|
|
And a failed subplan status on attempt 1 with error "ConfigurationError"
|
|
When I check should_retry
|
|
Then should_retry should be false
|
|
|
|
|
|
Scenario: Failure handler does not retry unknown error
|
|
Given a subplan config with retry enabled and max retries 2
|
|
And a failed subplan status on attempt 1 with error "SomeUnknownError"
|
|
When I check should_retry
|
|
Then should_retry should be false
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: plan_model_uncovered_lines.feature
|
|
# Feature: Plan Hierarchy and Subplan Failure Handling
|
|
# ============================================================
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A standalone plan is not considered a subplan
|
|
Given an uncovered-lines plan that was created independently
|
|
Then the uncovered-lines plan should not be recognized as a subplan
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A plan spawned by another plan is a subplan
|
|
Given an uncovered-lines plan that was spawned by another plan
|
|
Then the uncovered-lines plan should be recognized as a subplan
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A plan with no designated root is considered the root
|
|
Given an uncovered-lines plan with no designated root
|
|
Then the uncovered-lines plan should be recognized as a root plan
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A plan whose root is itself is considered the root
|
|
Given an uncovered-lines plan whose designated root is itself
|
|
Then the uncovered-lines plan should be recognized as a root plan
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A plan whose root is a different plan is not the root
|
|
Given an uncovered-lines plan whose designated root is a different plan
|
|
Then the uncovered-lines plan should not be recognized as a root plan
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A root plan reports a depth of zero
|
|
Given an uncovered-lines plan with no designated root
|
|
Then the uncovered-lines plan hierarchy depth should be 0
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A non-root plan reports a placeholder depth
|
|
Given an uncovered-lines plan whose designated root is a different plan
|
|
Then the uncovered-lines plan hierarchy depth should be -1
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A plan with no child work has no subplans
|
|
Given an uncovered-lines plan that has no child work tracked
|
|
Then the uncovered-lines plan should report having no subplans
|
|
|
|
|
|
@subplan_hierarchy
|
|
Scenario: A plan with tracked child work has subplans
|
|
Given an uncovered-lines plan that has tracked child work
|
|
Then the uncovered-lines plan should report having subplans
|
|
|
|
|
|
@failure_handler
|
|
Scenario: The failure handler halts remaining work when fail-fast is enabled
|
|
Given an uncovered-lines subplan configuration with fail-fast enabled and parallel work
|
|
And an uncovered-lines subplan that has failed
|
|
When the uncovered-lines failure handler evaluates whether to halt remaining work
|
|
Then the uncovered-lines remaining work should be halted
|
|
|
|
|
|
@failure_handler
|
|
Scenario: The failure handler halts remaining work for sequential execution
|
|
Given an uncovered-lines subplan configuration with fail-fast disabled and sequential work
|
|
And an uncovered-lines subplan that has failed
|
|
When the uncovered-lines failure handler evaluates whether to halt remaining work
|
|
Then the uncovered-lines remaining work should be halted
|
|
|
|
|
|
@failure_handler
|
|
Scenario: The failure handler allows remaining work for parallel execution without fail-fast
|
|
Given an uncovered-lines subplan configuration with fail-fast disabled and parallel work
|
|
And an uncovered-lines subplan that has failed
|
|
When the uncovered-lines failure handler evaluates whether to halt remaining work
|
|
Then the uncovered-lines remaining work should continue
|
|
|
|
|
|
@failure_handler
|
|
Scenario: The failure handler allows remaining work for dependency-ordered execution without fail-fast
|
|
Given an uncovered-lines subplan configuration with fail-fast disabled and dependency-ordered work
|
|
And an uncovered-lines subplan that has failed
|
|
When the uncovered-lines failure handler evaluates whether to halt remaining work
|
|
Then the uncovered-lines remaining work should continue
|
|
|
|
|
|
@failure_handler
|
|
Scenario: The failure handler does not retry when retries are disabled
|
|
Given an uncovered-lines subplan configuration with retries disabled
|
|
And an uncovered-lines subplan that failed with error "ValidationError occurred"
|
|
When the uncovered-lines failure handler evaluates whether to retry the failed work
|
|
Then the uncovered-lines failed work should not be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: The failure handler does not retry when all attempts are exhausted
|
|
Given an uncovered-lines subplan configuration allowing up to 2 retries
|
|
And an uncovered-lines subplan on attempt 3 that failed with error "TimeoutError"
|
|
When the uncovered-lines failure handler evaluates whether to retry the failed work
|
|
Then the uncovered-lines failed work should not be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A configuration error is not eligible for retry
|
|
Given an uncovered-lines subplan configuration allowing up to 3 retries
|
|
And an uncovered-lines subplan on attempt 1 that failed with error "ConfigurationError: bad config"
|
|
When the uncovered-lines failure handler evaluates whether to retry the failed work
|
|
Then the uncovered-lines failed work should not be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: An authentication error is not eligible for retry
|
|
Given an uncovered-lines subplan configuration allowing up to 3 retries
|
|
And an uncovered-lines subplan on attempt 1 that failed with error "AuthenticationError: invalid token"
|
|
When the uncovered-lines failure handler evaluates whether to retry the failed work
|
|
Then the uncovered-lines failed work should not be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A missing resource error is not eligible for retry
|
|
Given an uncovered-lines subplan configuration allowing up to 3 retries
|
|
And an uncovered-lines subplan on attempt 1 that failed with error "MissingResourceError: file not found"
|
|
When the uncovered-lines failure handler evaluates whether to retry the failed work
|
|
Then the uncovered-lines failed work should not be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A circular dependency error is not eligible for retry
|
|
Given an uncovered-lines subplan configuration allowing up to 3 retries
|
|
And an uncovered-lines subplan on attempt 1 that failed with error "CircularDependencyError: cycle detected"
|
|
When the uncovered-lines failure handler evaluates whether to retry the failed work
|
|
Then the uncovered-lines failed work should not be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A validation error is eligible for retry
|
|
Given an uncovered-lines subplan configuration allowing up to 3 retries
|
|
And an uncovered-lines subplan on attempt 1 that failed with error "ValidationError: schema mismatch"
|
|
When the uncovered-lines failure handler evaluates whether to retry the failed work
|
|
Then the uncovered-lines failed work should be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A timeout error is eligible for retry
|
|
Given an uncovered-lines subplan configuration allowing up to 3 retries
|
|
And an uncovered-lines subplan on attempt 1 that failed with error "TimeoutError: deadline exceeded"
|
|
When the uncovered-lines failure handler evaluates whether to retry the failed work
|
|
Then the uncovered-lines failed work should be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A temporary resource error is eligible for retry
|
|
Given an uncovered-lines subplan configuration allowing up to 3 retries
|
|
And an uncovered-lines subplan on attempt 1 that failed with error "TemporaryResourceError: service unavailable"
|
|
When the uncovered-lines failure handler evaluates whether to retry the failed work
|
|
Then the uncovered-lines failed work should be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A merge conflict error is eligible for retry
|
|
Given an uncovered-lines subplan configuration allowing up to 3 retries
|
|
And an uncovered-lines subplan on attempt 1 that failed with error "MergeConflictError: conflicting changes"
|
|
When the uncovered-lines failure handler evaluates whether to retry the failed work
|
|
Then the uncovered-lines failed work should be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: An unrecognized error type is not eligible for retry
|
|
Given an uncovered-lines subplan configuration allowing up to 3 retries
|
|
And an uncovered-lines subplan on attempt 1 that failed with error "SomeUnknownError: unexpected"
|
|
When the uncovered-lines failure handler evaluates whether to retry the failed work
|
|
Then the uncovered-lines failed work should not be retried
|
|
|
|
|
|
@failure_handler
|
|
Scenario: A failure with no error message is not eligible for retry
|
|
Given an uncovered-lines subplan configuration allowing up to 3 retries
|
|
And an uncovered-lines subplan on attempt 1 that failed with no error message
|
|
When the uncovered-lines failure handler evaluates whether to retry the failed work
|
|
Then the uncovered-lines failed work should not be retried
|