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