@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