242 lines
13 KiB
Gherkin
242 lines
13 KiB
Gherkin
@unit
|
|
Feature: ChangeSet repository edge cases and error coverage
|
|
As a developer maintaining changeset persistence
|
|
I want all error paths, validation, and edge cases covered
|
|
So that the changeset repository is reliable and well-tested
|
|
|
|
# ------ ChangeSetEntryRepository validation ------
|
|
|
|
Scenario: ChangeSetEntryRepository save_entry rejects empty changeset_id
|
|
Given I have a coverage ChangeSetEntryRepository
|
|
When I try to coverage-save an entry with empty changeset_id
|
|
Then a coverage ValueError should be raised with message "changeset_id must not be empty"
|
|
|
|
Scenario: ChangeSetEntryRepository save_entry rejects non-ChangeEntry
|
|
Given I have a coverage ChangeSetEntryRepository
|
|
When I try to coverage-save a non-ChangeEntry object
|
|
Then a coverage TypeError should be raised with message "entry must be a ChangeEntry instance"
|
|
|
|
Scenario: ChangeSetEntryRepository get_entries_for_changeset rejects empty changeset_id
|
|
Given I have a coverage ChangeSetEntryRepository
|
|
When I try to coverage-get entries for empty changeset_id
|
|
Then a coverage ValueError should be raised with message "changeset_id must not be empty"
|
|
|
|
Scenario: ChangeSetEntryRepository get_entries_for_plan rejects empty plan_id
|
|
Given I have a coverage ChangeSetEntryRepository
|
|
When I try to coverage-get entries for empty plan_id
|
|
Then a coverage ValueError should be raised with message "plan_id must not be empty"
|
|
|
|
Scenario: ChangeSetEntryRepository delete_for_changeset rejects empty changeset_id
|
|
Given I have a coverage ChangeSetEntryRepository
|
|
When I try to coverage-delete entries for empty changeset_id
|
|
Then a coverage ValueError should be raised with message "changeset_id must not be empty"
|
|
|
|
Scenario: ChangeSetEntryRepository delete_for_plan rejects empty plan_id
|
|
Given I have a coverage ChangeSetEntryRepository
|
|
When I try to coverage-delete entries for empty plan_id
|
|
Then a coverage ValueError should be raised with message "plan_id must not be empty"
|
|
|
|
# ------ ChangeSetEntryRepository database errors ------
|
|
|
|
Scenario: ChangeSetEntryRepository save_entry raises DatabaseError on OperationalError
|
|
Given I have a coverage ChangeSetEntryRepository with a broken session
|
|
When I try to coverage-save an entry that triggers an OperationalError
|
|
Then a coverage DatabaseError should be raised with message "Failed to save changeset entry"
|
|
|
|
Scenario: ChangeSetEntryRepository get_entries_for_changeset raises DatabaseError
|
|
Given I have a coverage ChangeSetEntryRepository with a broken query session
|
|
When I try to coverage-get entries for changeset "cs-broken"
|
|
Then a coverage DatabaseError should be raised with message "Failed to get entries"
|
|
|
|
Scenario: ChangeSetEntryRepository get_entries_for_plan raises DatabaseError
|
|
Given I have a coverage ChangeSetEntryRepository with a broken query session
|
|
When I try to coverage-get entries for plan "plan-broken"
|
|
Then a coverage DatabaseError should be raised with message "Failed to get entries for plan"
|
|
|
|
Scenario: ChangeSetEntryRepository delete_for_changeset raises DatabaseError
|
|
Given I have a coverage ChangeSetEntryRepository with a broken delete session
|
|
When I try to coverage-delete entries for changeset "cs-broken"
|
|
Then a coverage DatabaseError should be raised with message "Failed to delete entries"
|
|
|
|
Scenario: ChangeSetEntryRepository delete_for_plan raises DatabaseError
|
|
Given I have a coverage ChangeSetEntryRepository with a broken delete session
|
|
When I try to coverage-delete entries for plan "plan-broken"
|
|
Then a coverage DatabaseError should be raised with message "Failed to delete entries"
|
|
|
|
# ------ ChangeSetEntryRepository _to_domain edge cases ------
|
|
|
|
Scenario: ChangeSetEntryRepository _to_domain with null hashes and modes
|
|
Given I have a coverage ChangeSetEntryModel row with null hashes and modes
|
|
When I convert the coverage row to a domain ChangeEntry
|
|
Then the domain entry before_hash should be None
|
|
And the domain entry after_hash should be None
|
|
And the domain entry before_mode should be None
|
|
And the domain entry after_mode should be None
|
|
|
|
Scenario: ChangeSetEntryRepository _to_domain with populated hashes and modes
|
|
Given I have a coverage ChangeSetEntryModel row with hashes and modes
|
|
When I convert the coverage row to a domain ChangeEntry
|
|
Then the domain entry before_hash should be "abc123"
|
|
And the domain entry after_hash should be "def456"
|
|
And the domain entry before_mode should be 33188
|
|
And the domain entry after_mode should be 33261
|
|
|
|
Scenario: ChangeSetEntryRepository _to_domain with empty string hashes
|
|
Given I have a coverage ChangeSetEntryModel row with empty string hashes
|
|
When I convert the coverage row to a domain ChangeEntry
|
|
Then the domain entry before_hash should be None
|
|
And the domain entry after_hash should be None
|
|
|
|
# ------ ToolInvocationRepository validation ------
|
|
|
|
Scenario: ToolInvocationRepository save_invocation rejects non-ToolInvocation
|
|
Given I have a coverage ToolInvocationRepository
|
|
When I try to coverage-save a non-ToolInvocation object
|
|
Then a coverage TypeError should be raised with message "invocation must be a ToolInvocation instance"
|
|
|
|
Scenario: ToolInvocationRepository get_invocations_for_plan rejects empty plan_id
|
|
Given I have a coverage ToolInvocationRepository
|
|
When I try to coverage-get invocations for empty plan_id
|
|
Then a coverage ValueError should be raised with message "plan_id must not be empty"
|
|
|
|
Scenario: ToolInvocationRepository delete_for_plan rejects empty plan_id
|
|
Given I have a coverage ToolInvocationRepository
|
|
When I try to coverage-delete invocations for empty plan_id
|
|
Then a coverage ValueError should be raised with message "plan_id must not be empty"
|
|
|
|
# ------ ToolInvocationRepository database errors ------
|
|
|
|
Scenario: ToolInvocationRepository save_invocation raises DatabaseError on OperationalError
|
|
Given I have a coverage ToolInvocationRepository with a broken session
|
|
When I try to coverage-save an invocation that triggers an OperationalError
|
|
Then a coverage DatabaseError should be raised with message "Failed to save tool invocation"
|
|
|
|
Scenario: ToolInvocationRepository get_invocations_for_plan raises DatabaseError
|
|
Given I have a coverage ToolInvocationRepository with a broken query session
|
|
When I try to coverage-get invocations for plan "plan-broken"
|
|
Then a coverage DatabaseError should be raised with message "Failed to get invocations"
|
|
|
|
Scenario: ToolInvocationRepository delete_for_plan raises DatabaseError
|
|
Given I have a coverage ToolInvocationRepository with a broken delete session
|
|
When I try to coverage-delete invocations for plan "plan-broken"
|
|
Then a coverage DatabaseError should be raised with message "Failed to delete invocations"
|
|
|
|
# ------ ToolInvocationRepository save_invocation optional fields ------
|
|
|
|
Scenario: ToolInvocationRepository saves invocation with all optional fields populated
|
|
Given I have a coverage ToolInvocationRepository with real session
|
|
When I coverage-save an invocation with result, completed_at, and provider_metadata
|
|
Then the coverage-saved invocation should round-trip with all fields intact
|
|
|
|
Scenario: ToolInvocationRepository saves invocation with all optional fields as None
|
|
Given I have a coverage ToolInvocationRepository with real session
|
|
When I coverage-save an invocation with no result, no completed_at, and no provider_metadata
|
|
Then the coverage-saved invocation should round-trip with None optional fields
|
|
|
|
Scenario: ToolInvocationRepository saves invocation with changeset_id as None
|
|
Given I have a coverage ToolInvocationRepository with real session
|
|
When I coverage-save an invocation without changeset_id
|
|
Then the coverage-saved invocation for plan should be retrievable
|
|
|
|
# ------ ToolInvocationRepository _to_domain edge cases ------
|
|
|
|
Scenario: ToolInvocationRepository _to_domain with null JSON fields
|
|
Given I have a coverage ToolInvocationModel row with null JSON fields
|
|
When I convert the coverage row to a domain ToolInvocation
|
|
Then the domain invocation arguments should be an empty dict
|
|
And the domain invocation result should be None
|
|
And the domain invocation change_ids should be an empty list
|
|
And the domain invocation resource_refs should be an empty list
|
|
And the domain invocation provider_metadata should be None
|
|
|
|
Scenario: ToolInvocationRepository _to_domain with populated JSON fields
|
|
Given I have a coverage ToolInvocationModel row with populated JSON fields
|
|
When I convert the coverage row to a domain ToolInvocation
|
|
Then the domain invocation arguments should have key "path"
|
|
And the domain invocation result should have key "status"
|
|
And the domain invocation change_ids should have 2 entries
|
|
And the domain invocation resource_refs should have 1 entry
|
|
And the domain invocation provider_metadata should have key "model"
|
|
|
|
Scenario: ToolInvocationRepository _to_domain with null duration_ms and sequence_number
|
|
Given I have a coverage ToolInvocationModel row with null numeric fields
|
|
When I convert the coverage row to a domain ToolInvocation
|
|
Then the domain invocation duration_ms should be 0.0
|
|
And the domain invocation sequence_number should be 0
|
|
|
|
Scenario: ToolInvocationRepository _to_domain with null completed_at
|
|
Given I have a coverage ToolInvocationModel row with null completed_at
|
|
When I convert the coverage row to a domain ToolInvocation
|
|
Then the domain invocation completed_at should be None
|
|
|
|
Scenario: ToolInvocationRepository _to_domain with populated completed_at
|
|
Given I have a coverage ToolInvocationModel row with populated completed_at
|
|
When I convert the coverage row to a domain ToolInvocation
|
|
Then the domain invocation completed_at should be a datetime
|
|
|
|
# ------ SqliteChangeSetStore edge cases ------
|
|
|
|
Scenario: SqliteChangeSetStore get returns None for empty changeset_id
|
|
Given I have a coverage sqlite changeset store
|
|
When I coverage-get a changeset with empty string
|
|
Then the coverage-get result should be None
|
|
|
|
Scenario: SqliteChangeSetStore get returns empty SpecChangeSet for known ID without entries
|
|
Given I have a coverage sqlite changeset store
|
|
When I coverage-start a changeset for plan "plan-known"
|
|
And I coverage-get the started changeset
|
|
Then the coverage-get result should be a SpecChangeSet with 0 entries
|
|
And the coverage-get result plan_id should be "plan-known"
|
|
|
|
Scenario: SqliteChangeSetStore get returns None for unknown ID not in plan_map
|
|
Given I have a coverage sqlite changeset store
|
|
When I coverage-get a changeset with id "totally-unknown-id"
|
|
Then the coverage-get result should be None
|
|
|
|
Scenario: SqliteChangeSetStore get returns SpecChangeSet with entries
|
|
Given I have a coverage sqlite changeset store
|
|
When I coverage-start a changeset for plan "plan-with-entries"
|
|
And I coverage-record a create entry in the started changeset
|
|
And I coverage-get the started changeset
|
|
Then the coverage-get result should be a SpecChangeSet with 1 entries
|
|
And the coverage-get result plan_id should be "plan-with-entries"
|
|
|
|
Scenario: SqliteChangeSetStore record rejects empty changeset_id
|
|
Given I have a coverage sqlite changeset store
|
|
When I try to coverage-record an entry with empty changeset_id
|
|
Then a coverage ValueError should be raised with message "changeset_id must not be empty"
|
|
|
|
Scenario: SqliteChangeSetStore get_for_plan returns empty list for empty plan_id
|
|
Given I have a coverage sqlite changeset store
|
|
When I coverage-get_for_plan with empty plan_id
|
|
Then the coverage-get_for_plan result should be an empty list
|
|
|
|
Scenario: SqliteChangeSetStore get_for_plan returns empty list when no entries exist
|
|
Given I have a coverage sqlite changeset store
|
|
When I coverage-get_for_plan with plan_id "nonexistent-plan"
|
|
Then the coverage-get_for_plan result should be an empty list
|
|
|
|
Scenario: SqliteChangeSetStore summarize returns empty dict for empty changeset_id
|
|
Given I have a coverage sqlite changeset store
|
|
When I coverage-summarize changeset with empty string
|
|
Then the coverage-summarize result should be an empty dict
|
|
|
|
Scenario: SqliteChangeSetStore summarize returns summary for valid changeset
|
|
Given I have a coverage sqlite changeset store
|
|
When I coverage-start a changeset for plan "plan-sum"
|
|
And I coverage-record a create entry in the started changeset
|
|
And I coverage-summarize the started changeset
|
|
Then the coverage-summarize result total should be 1
|
|
|
|
Scenario: SqliteChangeSetStore delete_for_plan rejects empty plan_id
|
|
Given I have a coverage sqlite changeset store
|
|
When I try to coverage-delete_for_plan with empty plan_id
|
|
Then a coverage ValueError should be raised with message "plan_id must not be empty"
|
|
|
|
Scenario: SqliteChangeSetStore delete_for_plan returns count
|
|
Given I have a coverage sqlite changeset store
|
|
When I coverage-start a changeset for plan "plan-del-cov"
|
|
And I coverage-record a create entry in the started changeset
|
|
And I coverage-delete_for_plan "plan-del-cov"
|
|
Then the coverage-delete count should be 1
|