storetest

package
v1.9.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 32 Imported by: 0

Documentation

Overview

Package storetest provides a conformance suite every store.Store implementation must pass, plus constructors for each backend.

Memory and SQLite run in process and need no setup. Postgres and Mongo read a DSN from the environment and skip when it is absent, so `go test ./...` stays runnable with nothing installed.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewMemory

func NewMemory(t *testing.T) ledgerstore.Store

NewMemory returns an in-memory store. It never skips.

func NewMongo

func NewMongo(t *testing.T) ledgerstore.Store

NewMongo returns a migrated MongoDB store, or skips the test when LEDGER_TEST_MONGO_URI is unset.

func NewPostgres

func NewPostgres(t *testing.T) ledgerstore.Store

NewPostgres returns a migrated PostgreSQL store, or skips the test when LEDGER_TEST_POSTGRES_DSN is unset.

The DSN's database is migrated in place and not torn down, so point it at a scratch database and not at anything you care about.

func NewSQLite

func NewSQLite(t *testing.T) ledgerstore.Store

NewSQLite returns a migrated SQLite store backed by a file under the test's temp directory.

":memory:" cannot be used here: sqlitedriver opens the database through database/sql's pooled *sql.DB, and modernc.org/sqlite treats ":memory:" as a fresh, independent database on every new connection the pool opens. A concurrency test that expects two goroutines' connections to see each other's writes (a unique-constraint race, for instance) would silently pass or fail for the wrong reason against ":memory:" - the two writes would land in two different databases and never conflict at all. A file under t.TempDir() is shared by every connection the pool opens, and a busy_timeout pragma makes a losing writer wait for the lock instead of failing immediately with SQLITE_BUSY. The DSN query parameter form (rather than a one-off PRAGMA exec after Open) matters too: modernc's driver re-parses the DSN for every new pooled connection, so this is the only way to guarantee busy_timeout is set on connections opened later in the pool's life, not just the first one.

This deliberately does NOT set _txlock=immediate. An earlier version of RedeemCoupon opened its transaction with a SELECT before writing, which only takes a SHARED lock going in; two such transactions racing the same coupon could both hold SHARED and then both try to upgrade to RESERVED at their first write, which SQLite resolves as SQLITE_BUSY immediately rather than queuing the upgrade behind busy_timeout. That version relied on _txlock=immediate here to mask the problem - but the documented production DSN (docs/content/docs/stores/sqlite.mdx) does not set it, so the tests were passing against a configuration production does not run. RedeemCoupon's transactions now open with a write (the application INSERT) as their first statement instead, which takes the RESERVED lock up front with no later upgrade to fail, so this harness runs the same DSN shape production does and still exercises the real lock behavior.

func Run

func Run(t *testing.T, newStore func(t *testing.T) ledgerstore.Store)

Run executes the conformance suite against a store. newStore is called once per subtest so each starts from a clean database.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL