Files
cleveragents-core/features/lock_service_coverage.feature
T
freemo c9abb45adf
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 18s
CI / security (pull_request) Successful in 31s
CI / typecheck (pull_request) Successful in 55s
CI / integration_tests (pull_request) Successful in 3m28s
CI / unit_tests (pull_request) Successful in 10m38s
CI / docker (pull_request) Successful in 16s
CI / benchmark-regression (pull_request) Successful in 20m39s
CI / coverage (pull_request) Successful in 41m22s
CI / lint (push) Successful in 13s
CI / quality (push) Successful in 17s
CI / build (push) Successful in 22s
CI / typecheck (push) Successful in 29s
CI / security (push) Successful in 29s
CI / benchmark-regression (push) Has been skipped
CI / integration_tests (push) Successful in 2m40s
CI / unit_tests (push) Successful in 11m4s
CI / docker (push) Successful in 1m5s
CI / benchmark-publish (push) Successful in 12m10s
CI / coverage (push) Successful in 1h7m46s
test(coverage): add Behave BDD scenarios for 9 under-covered modules
Added 246 new BDD scenarios across 9 feature files to improve unit test
coverage for modules that were either entirely untested or had significant
coverage gaps:

- lock_service_coverage.feature (27 scenarios): validation branches,
  TTL boundaries, re-entrant acquisition, rollback on exceptions
- plan_apply_service_coverage.feature (54 scenarios): operation labels,
  diff rendering (plain/rich/json), artifact building, validation gate,
  changeset resolution and cleanup
- plan_executor_coverage.feature (51 scenarios): step parsing, execute
  actor integration, strategize/execute guards, stub retry/recovery,
  decision tree construction
- skill_cli_coverage_r3.feature (22 scenarios): tools refresh, list/show
  JSON fallback, capability summary errors, remove confirmation
- changeset_repository_coverage.feature (39 scenarios): entry/tool repos
  validation, database error wrapping, domain conversion, SQLite store
  CRUD operations
- repositories_coverage.feature (20 scenarios): get_by_name/namespace
  errors, list_available filters, delete with ActionInUseError, plan
  update with invariants/processing_state/error_details
- sandbox_copy_on_write_coverage.feature (12 scenarios): create OSError
  wrapping, get_path state transitions, commit edge cases, rollback
  errors, cleanup with missing paths
- bridge_coverage.feature (8 scenarios): __del__ suppression, async task
  cancellation, execute_graph message type handling, stream config,
  state checkpointer
- plan_cli_coverage.feature (13 scenarios): legacy apply/list/cd paths,
  use-action with estimation/invariant actors, lifecycle-apply guards,
  status errors, error recovery display

All 246 scenarios (1105 steps) pass. Step definitions use unique prefixes
to prevent ambiguous step conflicts with existing tests.

ISSUES CLOSED: #467
2026-02-27 13:47:42 -05:00

176 lines
8.6 KiB
Gherkin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Feature: LockService coverage uncovered branches and edge cases
As a developer
I want full coverage of every branch in lock_service.py
So that no edge case is left untested
Background:
Given I have a fresh lock service with in-memory database
# ------------------------------------------------------------------
# Validation: release method
# ------------------------------------------------------------------
Scenario: Release rejects empty resource_type
When I try to release with an empty resource_type
Then a lock_service validation error should be raised with message containing "resource_type must not be empty"
Scenario: Release rejects invalid resource_type
When I try to release with resource_type "widget" owner "o" resource_id "r1"
Then a lock_service validation error should be raised with message containing "resource_type must be one of"
Scenario: Release rejects empty resource_id
When I try to release with an empty resource_id
Then a lock_service validation error should be raised with message containing "resource_id must not be empty"
# ------------------------------------------------------------------
# Validation: renew method
# ------------------------------------------------------------------
Scenario: Renew rejects empty resource_type
When I try to renew with an empty resource_type
Then a lock_service validation error should be raised with message containing "resource_type must not be empty"
Scenario: Renew rejects invalid resource_type
When I try to renew with resource_type "widget" owner "o" resource_id "r1"
Then a lock_service validation error should be raised with message containing "resource_type must be one of"
Scenario: Renew rejects empty resource_id
When I try to renew with an empty resource_id
Then a lock_service validation error should be raised with message containing "resource_id must not be empty"
Scenario: Renew rejects TTL below minimum
When I try to renew with TTL 2 owner "o" resource_type "plan" resource_id "r1"
Then a lock_service validation error should be raised with message containing "ttl_seconds must be >="
Scenario: Renew rejects TTL above maximum
When I try to renew with TTL 7200 owner "o" resource_type "plan" resource_id "r1"
Then a lock_service validation error should be raised with message containing "ttl_seconds must be <="
# ------------------------------------------------------------------
# Validation: is_locked method
# ------------------------------------------------------------------
Scenario: is_locked rejects empty resource_type
When I try to check is_locked with an empty resource_type
Then a lock_service validation error should be raised with message containing "resource_type must not be empty"
Scenario: is_locked rejects invalid resource_type
When I try to check is_locked with resource_type "widget" resource_id "r1"
Then a lock_service validation error should be raised with message containing "resource_type must be one of"
Scenario: is_locked rejects empty resource_id
When I try to check is_locked with an empty resource_id
Then a lock_service validation error should be raised with message containing "resource_id must not be empty"
# ------------------------------------------------------------------
# TTL boundary values on acquire
# ------------------------------------------------------------------
Scenario: Acquire with explicit minimum TTL succeeds
When I acquire a lock with TTL 5 owner "owner-min" resource_type "plan" resource_id "p-min"
Then the lock_service result should be true
Scenario: Acquire with explicit maximum TTL succeeds
When I acquire a lock with TTL 3600 owner "owner-max" resource_type "plan" resource_id "p-max"
Then the lock_service result should be true
Scenario: Acquire with custom valid TTL succeeds
When I acquire a lock with TTL 120 owner "owner-cust" resource_type "project" resource_id "proj-cust"
Then the lock_service result should be true
# ------------------------------------------------------------------
# Re-entrant acquisition on project resource
# ------------------------------------------------------------------
Scenario: Same owner re-acquires a project lock
Given owner "owner-a" already holds a project lock on "proj-re"
When I acquire a lock with TTL 300 owner "owner-a" resource_type "project" resource_id "proj-re"
Then the lock_service result should be true
# ------------------------------------------------------------------
# Release by wrong owner returns False
# ------------------------------------------------------------------
Scenario: Release returns false when owner does not match
Given owner "owner-a" already holds a plan lock on "plan-wrong"
When I try to release with resource_type "plan" owner "wrong-owner" resource_id "plan-wrong"
Then the lock_service result should be false
# ------------------------------------------------------------------
# is_locked on expired lock returns False
# ------------------------------------------------------------------
Scenario: is_locked returns false for an expired lock
Given an expired lock exists for owner "old" on plan "plan-exp"
When I check is_locked for resource_type "plan" resource_id "plan-exp"
Then the lock_service result should be false
# ------------------------------------------------------------------
# Zero-count paths (no log emitted)
# ------------------------------------------------------------------
Scenario: release_all_for_owner returns zero when owner has no locks
When I release all locks for owner_id "ghost"
Then the lock_service integer result should be 0
Scenario: cleanup_expired returns zero when no expired locks exist
When I run cleanup of expired locks
Then the lock_service integer result should be 0
Scenario: count_stale_locks returns zero when no stale locks exist
When I count stale locks via the service
Then the lock_service integer result should be 0
# ------------------------------------------------------------------
# Exception rollback paths (mocked session)
# ------------------------------------------------------------------
Scenario: Acquire rolls back on unexpected exception
Given a lock service whose session raises an unexpected error on execute
When I try to acquire and expect a RuntimeError
Then a RuntimeError should have been raised
And the session should have been rolled back
Scenario: Release rolls back on unexpected exception
Given a lock service whose session raises an unexpected error on execute
When I try to release and expect a RuntimeError
Then a RuntimeError should have been raised
And the session should have been rolled back
Scenario: Renew rolls back on unexpected exception
Given a lock service whose session raises an unexpected error on execute
When I try to renew and expect a RuntimeError
Then a RuntimeError should have been raised
And the session should have been rolled back
Scenario: release_all_for_owner rolls back on unexpected exception
Given a lock service whose session raises an unexpected error on execute
When I try to release_all_for_owner and expect a RuntimeError
Then a RuntimeError should have been raised
And the session should have been rolled back
Scenario: cleanup_expired rolls back on unexpected exception
Given a lock service whose session raises an unexpected error on execute
When I try to cleanup_expired and expect a RuntimeError
Then a RuntimeError should have been raised
And the session should have been rolled back
# ------------------------------------------------------------------
# Acquire conflict rollback path
# ------------------------------------------------------------------
Scenario: Acquire rolls back the session on LockConflictError
Given a lock service with a real database and owner "holder" holds plan "plan-conf"
When I try to acquire plan "plan-conf" as "intruder" and capture the conflict error
Then a LockConflictError should have been raised
And the conflict owner should be "holder"
# ------------------------------------------------------------------
# Renew LockExpiredError rollback path
# ------------------------------------------------------------------
Scenario: Renew rolls back the session on LockExpiredError
Given a lock service with a real database and an expired lock for "exp-owner" on plan "plan-exp-r"
When I try to renew plan "plan-exp-r" as "exp-owner" and capture the expired error
Then a LockExpiredError should have been raised