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

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

Parameters:

  • make_store (#call)

    invoked once per contract to provide isolation; returns a fresh SessionStore (or duck-typed adapter).

  • skip_optional (Array<String>) (defaults to: [])

    optional method names to skip. Contracts for an optional method are also skipped automatically when the store does not override it.

  • check_uuid_dedupe (Boolean) (defaults to: false)

    additionally assert the ADVISORY uuid-dedupe recommendation: re-appending a batch that overlaps a prior write (exactly what the mirror batcher's retry can produce) must not duplicate entries sharing a uuid. The SessionStore contract phrases this as a "should" and the shipped reference adapters deliberately skip it (duplicates largely self-heal through last-wins summary folds, though resumed transcripts can still show repeated turns), so this defaults to off. Turn it on if your adapter dedupes.

Raises:



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