Documentation
¶
Overview ¶
Package pgcleaner puts a PostgreSQL database back to its baseline after a test, the way Ruby's DatabaseCleaner does: pick a strategy, start a session, clean it.
Three strategies are built in:
- Transaction runs the whole test inside one transaction and rolls it back. The fastest, but the test must use the *sql.DB it is given: another connection (a server in another process, a second pool) cannot see its rows.
- Truncation commits normally and runs TRUNCATE on the tables afterwards.
- Deletion commits normally and runs DELETE on the tables afterwards, children before parents.
Null cleans nothing. The package knows nothing about an ORM: it hands out a *database/sql.DB and runs one setup callback (migrations, fixtures) per process.
Index ¶
- func DefaultGuard(database string) bool
- func ScratchDatabase(t testing.TB, adminURL string) string
- type Cleaner
- func (c *Cleaner) CleanWith(ctx context.Context, s Strategy) error
- func (c *Cleaner) Cleaning(ctx context.Context, fn func(db *sql.DB) error) error
- func (c *Cleaner) Close() error
- func (c *Cleaner) DB(t testing.TB) *sql.DB
- func (c *Cleaner) Start(ctx context.Context) (*Session, error)
- func (c *Cleaner) URL(ctx context.Context) (string, error)
- type Option
- func WithDriver(name string) Option
- func WithGuard(allow func(database string) bool) Option
- func WithSetup(fn func(ctx context.Context, db *sql.DB) error) Option
- func WithStrategy(s Strategy) Option
- func WithTemplate(version string, build func(ctx context.Context, db *sql.DB, url string) error) Option
- type Session
- type Strategy
- type TableOption
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DefaultGuard ¶
DefaultGuard allows a database whose name contains "test", "e2e" or "scratch". Wiping a database is the one irreversible thing this package does, so a URL that still points at a development database is refused rather than emptied.
func ScratchDatabase ¶
ScratchDatabase creates an empty, uniquely named database on the server behind adminURL and returns its URL; the database is dropped when the test ends. It is for code that needs a database with no schema at all (migrations on a fresh instance), which a pre-migrated one cannot give. The role in adminURL must be able to create databases.
Types ¶
type Cleaner ¶
type Cleaner struct {
// contains filtered or unexported fields
}
Cleaner starts cleaning sessions against one database. Build it once per test process (a package variable next to TestMain) and share it between tests.
func New ¶
New returns a Cleaner for the database at url. Nothing connects until the first session starts.
func (*Cleaner) CleanWith ¶
CleanWith cleans once with the given strategy, whatever the Cleaner's own is. The usual use is a Truncation before the suite, then Transaction per test.
func (*Cleaner) Cleaning ¶
Cleaning runs fn in a session and cleans it afterwards, even when fn fails.
func (*Cleaner) Close ¶
Close releases the shared pool the committing strategies use and the worker database lease. A Cleaner that only ran Transaction on its own database holds nothing to close.
func (*Cleaner) DB ¶
DB starts a session for the test and cleans it when the test ends. A failure to start or to clean fails the test.
func (*Cleaner) URL ¶ added in v0.2.0
URL is the URL of the database the Cleaner works on: the one it was built with, or, with WithTemplate, this process's worker database. It starts what is needed (the lease, the setup) if that has not happened yet.
type Option ¶
type Option func(*Cleaner)
Option tunes a Cleaner.
func WithDriver ¶
WithDriver names the database/sql driver the URL is opened with. The default is "pgx", registered by this package; any other driver must be imported by the caller.
func WithGuard ¶
WithGuard replaces the check that the database may be wiped, run before Truncation and Deletion touch it (see DefaultGuard). A nil guard allows every database.
func WithSetup ¶
WithSetup registers the callback that brings the database to its baseline: migrate the schema, load the fixtures. It runs once per Cleaner, on a committing connection, before the first session starts. Its error fails every session.
func WithStrategy ¶
WithStrategy sets the strategy Cleaner.Start and Cleaner.DB use. The default is Transaction.
func WithTemplate ¶ added in v0.2.0
func WithTemplate(version string, build func(ctx context.Context, db *sql.DB, url string) error) Option
WithTemplate makes the Cleaner work on a database of its own instead of the one in its URL, so that test processes (one per package under `go test ./...`) run in parallel without sharing rows or locks.
Two kinds of database are kept next to the one in the URL, called base:
- base_tpl_<hash of version>, the template (a base too long for a 63-byte name is shortened, with a hash of it). It is built once per version, by whichever process comes first and under an advisory lock, by running build on an empty database; others wait for it. A template that exists is always complete. Older versions are dropped.
- base_w<i>, the workers. A process leases the lowest free index with a session advisory lock, which the server releases when the process ends, however it ends: there is no cleanup hook to forget, and the number of databases stays bounded by the number of concurrent processes. A worker is cloned from the template when it is missing or was cloned from another version, and is otherwise reused as it is.
version names everything that decides what build produces: bump it when the schema changes (a hash of the migration files, say). build gets the template database and its URL, for a tool that opens its own connection.
What differs per process belongs in WithSetup, which runs on the worker every time (fixtures with relative dates, for one): it also wipes whatever an earlier process left. The database to hand to code that opens its own connection is Cleaner.URL.
The role needs the right to create databases. A Cleaner whose first session starts inside a testing/synctest bubble must be closed inside it, or never: a test process just ends.
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session is one test's view of the database, from Cleaner.Start to Session.Clean.
type Strategy ¶
type Strategy interface {
// contains filtered or unexported methods
}
Strategy is how a session undoes a test's changes.
func Deletion ¶
func Deletion(opts ...TableOption) Strategy
Deletion empties the tables with DELETE after the test, children before parents. It is the cheaper choice when a test touches few of many tables, and the one that works without the TRUNCATE privilege. Sequences are not reset. A foreign-key cycle among the cleaned tables is an error: use Truncation there.
Migration bookkeeping tables are never cleaned (see Except).
func Null ¶
func Null() Strategy
Null cleans nothing: the session is the committing pool, untouched afterwards.
func Transaction ¶
func Transaction() Strategy
Transaction rolls the test back. Every session owns one connection with one open transaction; the code under test sees it as an ordinary *sql.DB, and its own Begin/Commit/Rollback become savepoints, so nested transactions work and still vanish with the session. The connection is limited to one: a second would start a second transaction that does not see the first one's writes.
func Truncation ¶
func Truncation(opts ...TableOption) Strategy
Truncation empties the tables with TRUNCATE ... RESTART IDENTITY CASCADE after the test. Rows are committed, so any connection sees them (a server under test in another process, a second pool), at the price of a slower clean and sessions that must not run in parallel against one database.
Migration bookkeeping tables are never truncated (see Except).
type TableOption ¶
type TableOption func(*filter)
TableOption narrows the tables Truncation and Deletion clean.
func Except ¶
func Except(tables ...string) TableOption
Except leaves these tables alone, on top of the migration tables.