247 lines
9.5 KiB
Gherkin
247 lines
9.5 KiB
Gherkin
@phase1 @domain @plan @coverage
|
|
Feature: Plan Model Coverage — New Fields and Helpers
|
|
As a developer
|
|
I want full test coverage of the plan model's new features
|
|
So that ProjectLink validation, as_cli_dict output, and edge-case validators are reliable
|
|
|
|
# NamespacedName — invalid name characters
|
|
|
|
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
|