Module: ClaudeAgentSDK::Testing
- Defined in:
- lib/claude_agent_sdk/testing/session_store_conformance.rb
Overview
Test helpers shipped in the gem for third-party SessionStore adapter authors.
Defined Under Namespace
Classes: ConformanceError
Constant Summary collapse
- OPTIONAL_METHODS =
%w[list_sessions list_session_summaries delete list_subkeys].freeze
Class Method Summary collapse
-
.run_session_store_conformance(make_store, skip_optional: [], check_uuid_dedupe: false) ⇒ Object
Assert the 16 SessionStore behavioral contracts against an adapter.
Class Method Details
.run_session_store_conformance(make_store, skip_optional: [], check_uuid_dedupe: false) ⇒ Object
Assert the 16 SessionStore behavioral contracts against an adapter.
Contracts 1-14 mirror the Python SDK's run_session_store_conformance.
Contract 16 (Ruby extension) locks one-row-per-session list_sessions
under multiple appends — the naive one-row-per-append implementation
passed every other contract and then showed N duplicate sessions in
pickers. Contract 15 is a Ruby SDK extension locking empty-subpath delete
coherence ('' == no subpath, the same addressing append/load already
use in every implementation in both SDKs); it runs only for stores
implementing #delete, and skip_optional: %w excludes the
delete contracts wholesale. Note: a store ported 1:1 from Python's
reference patterns that gates its delete cascade on
subpath is not None will fail contract 15 — that pattern orphans
subkeys (a known upstream incoherence; Python's own postgres example
passes while its redis/s3 examples fail).
Framework-agnostic: raises ConformanceError on the first violated contract, otherwise returns nil. Call it from any test framework, e.g.
it 'conforms' do
ClaudeAgentSDK::Testing.run_session_store_conformance(-> { MyStore.new })
end
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/claude_agent_sdk/testing/session_store_conformance.rb', line 52 def run_session_store_conformance(make_store, skip_optional: [], check_uuid_dedupe: false) skip_optional = skip_optional.map(&:to_s) invalid = skip_optional - OPTIONAL_METHODS raise ConformanceError, "unknown optional methods in skip_optional: #{invalid}" unless invalid.empty? fresh = -> { make_store.call } probe = fresh.call has_list_sessions = optional?(probe, 'list_sessions', skip_optional) has_list_summaries = optional?(probe, 'list_session_summaries', skip_optional) has_delete = optional?(probe, 'delete', skip_optional) has_list_subkeys = optional?(probe, 'list_subkeys', skip_optional) check_append_and_load(fresh, has_list_sessions) check_list_sessions(fresh) if has_list_sessions check_list_session_summaries(fresh, has_list_sessions, has_delete) if has_list_summaries check_delete(fresh, has_list_subkeys, has_list_sessions) if has_delete check_list_subkeys(fresh) if has_list_subkeys check_uuid_dedupe_contract(fresh) if check_uuid_dedupe nil end |