forked from HAL9000/cleveragents-core
268 lines
11 KiB
Gherkin
268 lines
11 KiB
Gherkin
Feature: Plan Domain Model
|
|
As a developer
|
|
I want a plan domain model that supports the lifecycle
|
|
So that I can track plans through Strategize -> Execute -> Apply -> Applied phases
|
|
|
|
# NamespacedName Tests
|
|
|
|
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 "strategize, execute, apply, applied"
|
|
|
|
Scenario: STRATEGIZE phase is the starting phase
|
|
Given I create a new plan
|
|
Then the plan phase should be "strategize"
|
|
|
|
# PlanState Tests
|
|
|
|
Scenario: Strategize phase uses unified state
|
|
Given I create a new plan in action phase
|
|
Then the plan state should be "queued"
|
|
|
|
Scenario: Strategize phase defaults to queued state
|
|
Given I create a plan in strategize phase
|
|
Then the plan state should be "queued"
|
|
|
|
Scenario: Strategize phase defaults state and exposes it
|
|
Given I create a plan in action phase without action state
|
|
Then the plan state should be "queued"
|
|
|
|
Scenario: Strategize phase ignores legacy action state
|
|
Given I create a plan in strategize phase with action state set and no processing state
|
|
Then the plan 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 state should be "queued"
|
|
And the plan should not be terminal
|
|
|
|
Scenario: Plan can check if it can transition to next phase
|
|
Given I create a plan in action phase with available state
|
|
Then the plan can transition to next phase
|
|
|
|
Scenario: Queued plan cannot transition to next phase
|
|
Given I create a new plan in action 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 APPLIED is None (terminal)
|
|
Given I create a plan in applied phase
|
|
Then the next phase should be empty
|
|
And the plan should be terminal
|
|
|
|
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 APPLIED
|
|
Then transition from "apply" to "applied" should be valid
|
|
|
|
Scenario: Invalid transition from STRATEGIZE to APPLY
|
|
Then transition from "strategize" to "apply" should be invalid
|
|
|
|
Scenario: No transition from APPLIED (terminal)
|
|
Then transition from "applied" to "strategize" should be invalid
|
|
|
|
# Error State Tests
|
|
|
|
Scenario: Plan in errored state is_errored returns True
|
|
Given I create a plan 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 plan in strategize phase with processing state
|
|
Then the plan should not be errored
|
|
|
|
Scenario: Applied plan is terminal
|
|
Given I create a plan in action phase with archived 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: Strategize phase accepts state
|
|
When I try to create a plan in action phase with processing state
|
|
Then the plan phase should be "strategize"
|
|
And the plan state should be "queued"
|
|
|
|
Scenario: Applied phase cannot transition to next phase
|
|
Given I create a plan in applied phase
|
|
Then the plan cannot transition to next phase
|
|
|
|
# Phase Transition Tests (spec-aligned: strategize -> execute -> apply -> applied)
|
|
|
|
Scenario: Full lifecycle transitions follow spec order
|
|
Then transition from "strategize" to "execute" should be valid
|
|
And transition from "execute" to "apply" should be valid
|
|
And transition from "apply" to "applied" should be valid
|
|
|
|
Scenario: Skipping phases is not allowed
|
|
Then transition from "strategize" to "apply" should be invalid
|
|
And transition from "strategize" to "applied" should be invalid
|
|
And transition from "execute" to "applied" should be invalid
|
|
|
|
Scenario: Backward transitions are not allowed
|
|
Then transition from "execute" to "strategize" should be invalid
|
|
And transition from "apply" to "execute" should be invalid
|
|
And transition from "applied" to "apply" should be invalid
|
|
|
|
# Automation Profile Provenance Tests
|
|
|
|
Scenario: Plan stores automation profile name at creation
|
|
Given I create a plan with automation profile "org/high-trust-profile"
|
|
Then the plan automation profile name should be "org/high-trust-profile"
|
|
And the plan model automation level should be "review_before_apply"
|
|
|
|
Scenario: Plan stores effective profile snapshot
|
|
Given I create a plan with automation profile and snapshot
|
|
Then the plan effective profile snapshot should contain "max_file_changes"
|
|
And the automation profile should be immutable after creation
|
|
|
|
Scenario: Plan as_cli_dict includes automation profile provenance
|
|
Given I create a plan with automation profile "org/high-trust-profile"
|
|
Then the cli dict should contain key "automation_profile"
|
|
And the cli dict "automation_profile" should be "org/high-trust-profile"
|
|
|
|
# Invariant Ordering Tests
|
|
|
|
Scenario: Invariants preserve insertion order
|
|
Given I create a plan with ordered invariants
|
|
Then the invariant at index 0 should have text "Global rule"
|
|
And the invariant at index 0 should have scope "global"
|
|
And the invariant at index 1 should have text "Project rule"
|
|
And the invariant at index 1 should have scope "project"
|
|
And the invariant at index 2 should have text "Plan rule"
|
|
And the invariant at index 2 should have scope "plan"
|
|
|
|
Scenario: Invariants appear in cli_dict with scope tags
|
|
Given I create a plan with ordered invariants
|
|
Then the cli dict should contain key "invariants"
|
|
And the cli dict invariants should have 3 entries
|
|
|
|
Scenario: Empty invariant text is rejected
|
|
When I try to create a plan invariant with empty text
|
|
Then a validation error should be raised
|
|
|
|
# Project Link Alias Validation Tests
|
|
|
|
Scenario: ProjectLink accepts valid alias
|
|
Given I create a project link with alias "my-project"
|
|
Then the project link alias should be "my-project"
|
|
|
|
Scenario: ProjectLink rejects alias with special characters
|
|
When I try to create a project link with alias "my@project"
|
|
Then a validation error should be raised
|
|
|
|
Scenario: ProjectLink rejects alias starting with hyphen
|
|
When I try to create a project link with alias "-invalid"
|
|
Then a validation error should be raised
|
|
|
|
Scenario: Plan rejects duplicate project link aliases
|
|
When I try to create a plan with duplicate project link aliases
|
|
Then a validation error should be raised
|
|
|
|
Scenario: ProjectLink alias is lowercased automatically
|
|
Given I create a project link with alias "MyProject"
|
|
Then the project link alias should be "myproject"
|
|
|
|
Scenario: Plan as_cli_dict includes action_name
|
|
Given I create a new plan
|
|
Then the cli dict should contain key "action_name"
|
|
And the cli dict "action_name" should be "local/test-action"
|
|
|
|
Scenario: Plan as_cli_dict renders all optional fields
|
|
Given I create a fully populated plan for cli dict testing
|
|
Then the cli dict should contain key "definition_of_done"
|
|
And the cli dict should contain key "strategy_actor"
|
|
And the cli dict should contain key "execution_actor"
|
|
And the cli dict should contain key "estimation_actor"
|
|
And the cli dict should contain key "invariant_actor"
|
|
And the cli dict should contain key "arguments"
|
|
And the cli dict should contain key "changeset_id"
|
|
And the cli dict should contain key "sandbox_refs"
|
|
And the cli dict should contain key "error_message"
|
|
And the cli dict should contain key "error_details"
|
|
And the cli dict should contain key "tags"
|
|
And the cli dict should contain key "parent_plan_id"
|
|
And the cli dict should contain key "root_plan_id"
|
|
And the cli dict should contain key "attempt"
|