forked from HAL9000/cleveragents-core
f2f7aa5dc9
Implement multiple Stage A/B/E/SEC milestones for the v3 lifecycle system: - Stage A5.3+A5.4: Add LifecycleActionModel and LifecyclePlanModel SQLAlchemy models with to_domain()/from_domain() conversion methods - Stage A5.6: Implement ActionRepository with full CRUD, namespace/state queries, referential integrity checks, and retry decorator - Stage E1: Add subplan domain models (ExecutionMode, SubplanMergeStrategy, SubplanConfig, SubplanStatus, SubplanAttempt, SubplanFailureHandler) with computed properties on Plan (is_subplan, is_root_plan, depth, has_subplans) - Stage A6: Add AutomationLevel enum (MANUAL, REVIEW_BEFORE_APPLY, FULL_AUTOMATION), settings integration, PlanLifecycleService auto-progression, pause/resume, and CLI commands (--automation-level, set-automation-level) - Stage SEC1: Remove eval()/exec() from stream_router.py, replace with named operation and transform registries; code blocks and unregistered transforms now raise StreamRoutingError - Add langchain-anthropic dependency - Update BDD tests for security changes and relax ADR directory requirement
97 lines
5.6 KiB
Gherkin
97 lines
5.6 KiB
Gherkin
@phase1 @domain @repository @action_repository @error_handling
|
||
Feature: Action Repository Transient Database Error Handling
|
||
As a system operating under unstable database conditions
|
||
I want the action repository to wrap transient database failures in a domain-specific error
|
||
So that callers receive a consistent DatabaseError regardless of the underlying driver exception
|
||
|
||
Background:
|
||
Given a clean in-memory database with the lifecycle schema
|
||
And an action repository whose session raises OperationalError on query
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# ActionRepository.create – non-unique IntegrityError branch (line 769→771)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
@action_create @transient_error
|
||
Scenario: A non-unique integrity violation during create is wrapped in DatabaseError
|
||
Given an action repository whose session raises a non-unique IntegrityError on flush
|
||
And a valid action domain object named "local/integrity-fail"
|
||
When the action is saved and a database error is expected
|
||
Then a DatabaseError should be raised with message containing "Failed to create action"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# ActionRepository.create – OperationalError branch (lines 772-774)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
@action_create @transient_error
|
||
Scenario: An operational error during create is wrapped in DatabaseError
|
||
Given an action repository whose session raises OperationalError on flush
|
||
And a valid action domain object named "local/create-op-fail"
|
||
When the action is saved and a database error is expected
|
||
Then a DatabaseError should be raised with message containing "Failed to create action"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# ActionRepository.get_by_id – OperationalError branch (lines 793-794)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
@action_read @transient_error
|
||
Scenario: An operational error during get-by-id is wrapped in DatabaseError
|
||
When an action is retrieved by identifier and a database error is expected
|
||
Then a DatabaseError should be raised with message containing "Failed to get action"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# ActionRepository.get_by_name – OperationalError branch (lines 811-812)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
@action_read @transient_error
|
||
Scenario: An operational error during get-by-name is wrapped in DatabaseError
|
||
When an action is retrieved by name "local/broken" and a database error is expected
|
||
Then a DatabaseError should be raised with message containing "Failed to get action by name"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# ActionRepository.get_by_namespace – OperationalError branch (lines 840-841)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
@action_list @transient_error
|
||
Scenario: An operational error during namespace listing is wrapped in DatabaseError
|
||
When actions in namespace "local" are listed and a database error is expected
|
||
Then a DatabaseError should be raised with message containing "Failed to list actions in namespace"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# ActionRepository.get_by_state – OperationalError branch (lines 864-865)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
@action_list @transient_error
|
||
Scenario: An operational error during state listing is wrapped in DatabaseError
|
||
When actions in state "available" are listed and a database error is expected
|
||
Then a DatabaseError should be raised with message containing "Failed to list actions by state"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# ActionRepository.update – OperationalError branch (lines 924-925)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
@action_update @transient_error
|
||
Scenario: An operational error during update is wrapped in DatabaseError
|
||
Given a saved action named "local/update-target" in a healthy repository
|
||
And the repository session is replaced with one that raises OperationalError on query
|
||
When the saved action is updated and a database error is expected
|
||
Then a DatabaseError should be raised with message containing "Failed to update action"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# ActionRepository.list_available – OperationalError branch (lines 951-952)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
@action_list @transient_error
|
||
Scenario: An operational error during list-available is wrapped in DatabaseError
|
||
When available actions are listed and a database error is expected
|
||
Then a DatabaseError should be raised with message containing "Failed to list available actions"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# ActionRepository.delete – OperationalError branch (lines 996-998)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
@action_delete @transient_error
|
||
Scenario: An operational error during delete is wrapped in DatabaseError
|
||
When an action is deleted by identifier and a database error is expected
|
||
Then a DatabaseError should be raised with message containing "Failed to delete action"
|