4ad51561fc
Cap time.sleep and asyncio.sleep globally at 10ms in before_all to eliminate retry/backoff waits (tenacity wait_fixed, retry_auto_debug exponential backoff). Save originals as time._original_sleep and asyncio._original_sleep for tests that need real wall-clock delays (CircuitBreaker recovery, debounce timers, validation timeouts). Enhance template-DB patch: add "db." prefix to _SCENARIO_DB_PREFIXES and check db_path.stat().st_size > 0 so 0-byte auto-created SQLite files receive the template copy instead of falling through to real migrations. Replace subprocess.run CLI invocations with typer.testing.CliRunner in module_coverage, main_coverage_complete, and coverage_extras step files, eliminating ~6s Python cold-start overhead per call. Switch plan_persistence and action_persistence to in-memory SQLite by default; only cross-restart scenarios use file-based via an explicit Given step. Replace MigrationRunner.run_migrations with Base.metadata.create_all in plan_service_steps for freshly created in-memory engines. Removed leftover debug comment in environment.py. Total tier runtime: 565s -> 21s (96% reduction), 20 features all under 5s behave-internal time. ISSUES CLOSED: #480
130 lines
6.6 KiB
Gherkin
130 lines
6.6 KiB
Gherkin
Feature: Plan persistence via LifecyclePlanRepository
|
|
As a developer
|
|
I want plans to persist correctly through the LifecyclePlanRepository
|
|
So that plan lifecycle state is durable across operations
|
|
|
|
Background:
|
|
Given a fresh in-memory plan persistence database
|
|
And a prerequisite action "local/persist-action" exists in the database
|
|
|
|
# Create scenarios
|
|
Scenario: Create a plan and retrieve it by ID
|
|
Given a new lifecycle plan with ID "01HV000000000000000000PP01"
|
|
When I persist the plan via the plan repository
|
|
Then I can retrieve the plan by ID "01HV000000000000000000PP01"
|
|
And the persisted plan description should be "Persistence test plan"
|
|
|
|
Scenario: Create a plan preserves all identity fields
|
|
Given a new lifecycle plan with parent tree ID "01HV000000000000000000PP02" parent "01HV000000000000000000PP01" root "01HV000000000000000000PP01"
|
|
When I persist the plan via the plan repository
|
|
Then I can retrieve the plan by ID "01HV000000000000000000PP02"
|
|
And the plan parent_plan_id should be "01HV000000000000000000PP01"
|
|
And the plan root_plan_id should be "01HV000000000000000000PP01"
|
|
|
|
# Phase and state transition scenarios
|
|
Scenario: Plan persists phase transition from action to strategize
|
|
Given a persisted plan in phase "action" with state "queued"
|
|
When I update the plan phase to "strategize" with state "queued"
|
|
Then the plan persistence phase should be "strategize"
|
|
And the persisted plan processing_state should be "queued"
|
|
|
|
Scenario: Plan persists phase transition from strategize to execute
|
|
Given a persisted plan in phase "strategize" with state "complete"
|
|
When I update the plan phase to "execute" with state "queued"
|
|
Then the plan persistence phase should be "execute"
|
|
And the persisted plan processing_state should be "queued"
|
|
|
|
Scenario: Plan persists phase transition from execute to apply
|
|
Given a persisted plan in phase "execute" with state "complete"
|
|
When I update the plan phase to "apply" with state "queued"
|
|
Then the plan persistence phase should be "apply"
|
|
And the persisted plan processing_state should be "queued"
|
|
|
|
Scenario: Plan persists processing state transition to processing
|
|
Given a persisted plan in phase "strategize" with state "queued"
|
|
When I update the plan phase to "strategize" with state "processing"
|
|
Then the persisted plan processing_state should be "processing"
|
|
|
|
Scenario: Plan persists terminal applied state
|
|
Given a persisted plan in phase "apply" with state "queued"
|
|
When I update the plan phase to "apply" with state "applied"
|
|
Then the persisted plan processing_state should be "applied"
|
|
|
|
Scenario: Plan persists terminal constrained state
|
|
Given a persisted plan in phase "apply" with state "queued"
|
|
When I update the plan phase to "apply" with state "constrained"
|
|
Then the persisted plan processing_state should be "constrained"
|
|
|
|
Scenario: Plan persists terminal errored state
|
|
Given a persisted plan in phase "apply" with state "queued"
|
|
When I update the plan phase to "apply" with state "errored"
|
|
Then the persisted plan processing_state should be "errored"
|
|
|
|
Scenario: Plan persists terminal cancelled state
|
|
Given a persisted plan in phase "strategize" with state "queued"
|
|
When I update the plan phase to "strategize" with state "cancelled"
|
|
Then the persisted plan processing_state should be "cancelled"
|
|
|
|
# List filter scenarios
|
|
Scenario: List plans filtered by phase
|
|
Given 3 persisted plans in phase "strategize"
|
|
And 2 persisted plans in phase "execute"
|
|
When I list persisted plans filtered by phase "strategize"
|
|
Then I should get 3 plans in the list
|
|
|
|
Scenario: List plans filtered by processing state
|
|
Given 2 persisted plans with processing state "queued"
|
|
And 1 persisted plan with processing state "processing"
|
|
When I list plans filtered by processing_state "queued"
|
|
Then I should get 2 plans in the list
|
|
|
|
Scenario: List plans filtered by action name
|
|
Given 2 persisted plans linked to action "local/persist-action"
|
|
When I list plans filtered by action_name "local/persist-action"
|
|
Then I should get 2 plans in the list
|
|
|
|
# Plan tree link scenarios
|
|
Scenario: Plan tree preserves parent-child relationships
|
|
Given a parent plan with ID "01HV0000000000000000TREE01"
|
|
And a child plan with ID "01HV0000000000000000TREE02" under parent "01HV0000000000000000TREE01"
|
|
When I retrieve the child plan "01HV0000000000000000TREE02"
|
|
Then the child plan should reference parent "01HV0000000000000000TREE01"
|
|
And the child plan should reference root "01HV0000000000000000TREE01"
|
|
|
|
Scenario: Plan tree preserves three-level hierarchy
|
|
Given a root plan with ID "01HV0000000000000000R00T01"
|
|
And a mid plan with ID "01HV0000000000000000M0D001" under root "01HV0000000000000000R00T01"
|
|
And a leaf plan with ID "01HV0000000000000000KEAF01" under parent "01HV0000000000000000M0D001" and root "01HV0000000000000000R00T01"
|
|
When I retrieve the leaf plan "01HV0000000000000000KEAF01"
|
|
Then the leaf plan parent should be "01HV0000000000000000M0D001"
|
|
And the leaf plan root should be "01HV0000000000000000R00T01"
|
|
|
|
# Cross-restart scenarios (require file-based SQLite to survive reconnection)
|
|
Scenario: Plan status persists across database reconnection
|
|
Given the plan persistence database is file-based
|
|
And a prerequisite action "local/persist-action" exists in the database
|
|
And a persisted plan in phase "execute" with state "processing"
|
|
When I close and reopen the persistence database
|
|
Then the plan should still be in phase "execute" with state "processing"
|
|
|
|
Scenario: Plan with project links persists across reconnection
|
|
Given the plan persistence database is file-based
|
|
And a prerequisite action "local/persist-action" exists in the database
|
|
And a persisted plan with project links "proj-alpha" and "proj-beta"
|
|
When I close and reopen the persistence database
|
|
Then the plan should still have 2 project links
|
|
|
|
Scenario: Plan with arguments persists across reconnection
|
|
Given the plan persistence database is file-based
|
|
And a prerequisite action "local/persist-action" exists in the database
|
|
And a persisted plan with arguments "target" and "coverage"
|
|
When I close and reopen the persistence database
|
|
Then the plan should still have 2 arguments in order
|
|
|
|
Scenario: Plan with invariants persists across reconnection
|
|
Given the plan persistence database is file-based
|
|
And a prerequisite action "local/persist-action" exists in the database
|
|
And a persisted plan with invariant "No breaking changes"
|
|
When I close and reopen the persistence database
|
|
Then the plan should still have invariant "No breaking changes"
|