test(storage): add cross-dialect storage parity suite - #935
Conversation
Introduce pkg/storage/storagetest: a behavioral suite any storage.Storage implementation must pass, driven through a per-dialect Harness. Seeds it with the settings and apply-log families moved from mysqlstore; remaining families migrate incrementally as the Postgres store lands.
There was a problem hiding this comment.
Pull request overview
Adds a new pkg/storage/storagetest package that defines a cross-dialect behavioral parity suite for storage.Storage implementations, and migrates existing MySQL storage integration tests (settings + apply logs) to run through that suite via a MySQL harness adapter.
Changes:
- Introduce
storagetest.Harnessplus shared test fixtures (CreateLock,CreateApply*) to standardize parity testing across storage implementations. - Add parity suites for
SettingsStoreandApplyLogStore(storagetest.TestSettings,storagetest.TestApplyLogs). - Replace MySQL-specific settings/apply-log integration tests with a single
parity_test.gothat runs the shared suite againstmysqlstore.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/storage/storagetest/storagetest.go | Defines the harness interface and shared fixture helpers for parity tests. |
| pkg/storage/storagetest/settings.go | Adds the SettingsStore parity suite (CRUD + unreachable-DB error paths). |
| pkg/storage/storagetest/apply_logs.go | Adds the ApplyLogStore parity suite (bounded newest-window ordering + filtering). |
| pkg/storage/mysqlstore/parity_test.go | Wires MySQL’s existing integration fixture into the parity suite via a harness adapter. |
| pkg/storage/mysqlstore/settings_test.go | Removed in favor of running the shared parity suite. |
| pkg/storage/mysqlstore/apply_logs_test.go | Removed in favor of running the shared parity suite. |
Suppressed comments (1)
pkg/storage/storagetest/settings.go:101
- Similar to the Set test,
Getcan return(nil, nil)if theSetdidn't persist the value. Accessingsetting.Valuewould panic and make the failure harder to diagnose; addrequire.NotNilbefore dereferencing.
setting, err := store.Settings().Get(ctx, "rate_limits")
require.NoError(t, err)
require.Equal(t, jsonValue, setting.Value)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The suite asserted apply-log insertion-order tie-breaks and settings key ordering that only the MySQL implementation guaranteed — a spec-compliant Postgres store could flake or false-pass. Promote both orderings into the interface contract (and add the missing id tie-break to GetByApply), make the settings List oracle independent of insertion order, and close the coverage gaps: GetByApply parity, apply-log DB-error family, a single Run(t, h) entry point, and mysqlstore fixture helpers delegating to storagetest so the two suites cannot drift.
…ntract Get legally returns (nil, nil) for a missing row, so the parity subtests that dereference the result now assert non-nil first, and the ErrSettingNotFound behavior the suite asserts is documented on SettingsStore.Delete.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
🤖 Adversarial correctness review, requested by Armand and performed by his agent. Reviewed at head dc3120f. Verdict: clean — no fix-before-merge findings. The moved coverage is a strict superset of what it replaces, and the one production change is exactly as narrow as advertised. The core risk in a test-move PR like this is silent coverage loss — a behavior the old package-local tests pinned that the new suite no longer exercises. I audited both moved families against the deleted files at the merge base. Settings is 1:1 (all eight behaviors: Get/Set/List/Delete/JSONValues plus the three DB-error paths), and its
Verified solid: the production change is confined to This review was generated by Claude Code (claude-fable-5). |
aparajon
left a comment
There was a problem hiding this comment.
🤖 Approving on Armand's behalf after the adversarial correctness review above (no fix-before-merge findings). This stamp was left by Claude Code (claude-fable-5).
Summary
Adds
pkg/storage/storagetest— a behavioral parity suite that anystorage.Storageimplementation must pass. The suite asserts on the storage interface contract (typed results, typed errors, ordering, state transitions), never on SQL text or dialect-specific behavior, so it can validate a future non-MySQL state store slice by slice as it lands.What
storagetest.Harnessinterface: implementations supply a clean bootstrapped store (NewStorage) and a store on an unusable connection (NewUnreachableStorage) for error-path coverage.storagetest.Run(t, h)entry point running every parity family, so an implementation cannot silently opt out of part of the contract; per-family entry points remain exported for bring-up. Shared interface-level fixture helpers (CreateLock,CreateApply*) back both the suite andmysqlstore's own tests, which delegate to them so the two can't drift.mysqlstoreinto the suite;mysqlstoreruns them through a thin harness adapter over its existing testcontainer fixture (parity_test.go).created_atascending with ties broken by ascending id (added the missing tie-break toGetByApply), and settingsListreturns keys ascending. New parity coverage:GetByApplyand the apply-log DB-error family.Why
The suite is the oracle for the upcoming Postgres state store: each storage sub-interface implementation lands validated against exactly the behaviors the MySQL store already guarantees. Remaining test families migrate incrementally in follow-up PRs, ahead of or alongside the store slices they validate.