Dialect parity
runDialectParity — a fixed scenario suite that proves a SQL backend behaves identically to the others. For dialect-package authors and apps with hand-written portable SQL.
When you need this
Most apps never touch this page. The framework's DSL already hides every cross-dialect difference, and a unit test on the in-memory store proves your handler logic. Reach for runDialectParity only when you have code that talks to a real database in a dialect-specific way:
- You're authoring a new
@voltro/sql-*dialect package and need to prove it before plugging it intoloadDialect(). - Your app drops to
unsafe()SQL or custom store logic that must stay portable across the backends you ship on.
runDialectParity
runDialectParity(fixture) registers a vitest suite (describe('dialect parity: <name>')) that runs a fixed scenario set against the database your fixture stands up — and asserts identical behaviour on each. It's exported from the @voltro/testing/dialect subpath — the only part of @voltro/testing that imports vitest, so makeTestContext consumers carry no vitest peer. The scenarios cover the contract the DSL relies on:
- DDL idempotency (re-applying the schema is a no-op)
- insert / query round-trip, update post-image, delete
onChangefires insert + update + delete eventstransactionalrolls back on throw (and drops queued change events) / commits and drains events in orderupsert(insert then update-on-conflict, no duplicate)updateManywith a predicate, and the atomic compare-and-set (the wakeup-claim pattern)retryFilterreturns a stable'retry' | 'noRetry'decision
The harness intentionally does not bundle docker setup — that lives next to the dialect, so the parity packages' devDeps stay minimal. Your fixture owns the lifecycle (scratch docker Postgres, temp-file SQLite, …).
// sql-postgres/__tests__/parity.test.ts
import { runDialectParity, applySchema } from '@voltro/testing/dialect'
import { postgresDialect } from '../src/index'
runDialectParity({
dialect: postgresDialect,
name: 'postgres (scratch docker)',
setup: async () => { /* stand up a clean scratch database */ },
teardown: async () => { /* drop it */ },
make: async () => ({
store: /* the live DataStore for this scenario */,
migrate: (tables) => /* applySchema(tables, dialect.id) against the same runtime */,
dispose: async () => { /* close store, drop scratch state */ },
}),
})The fixture contract
DialectFixture:
| Field | Meaning |
|---|---|
dialect |
The SqlDialect under test (its id, retryFilter, etc.). |
name? |
Label for the test output (defaults to dialect.id). |
setup |
Runs before each scenario — give it a clean slate. |
teardown |
Runs after each scenario. |
make |
Returns a DialectFixtureSession for the scenario. |
DialectFixtureSession:
| Field | Meaning |
|---|---|
store |
The live DataStore the scenarios drive. |
migrate(tables) |
Apply applySchema(tables, dialect.id) against the same runtime the store uses, so DDL and DML hit the same database. |
dispose() |
Tear the session down (close store, drop scratch DB). |
applySchema is re-exported from @voltro/testing/dialect (the same subpath as runDialectParity, not the root entry) so a fixture can build its migrate without pulling @voltro/database/sql separately. Once a dialect green-checks the harness, it's ready for loadDialect().