easypg

package
v0.0.0-...-7169321 Latest Latest
Warning

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

Go to latest
Published: Jan 1, 2025 License: Apache-2.0 Imports: 23 Imported by: 4

Documentation

Overview

Package easypg is a database library for applications that use PostgreSQL. It imports the libpq SQL driver and integrates github.com/golang-migrate/migrate for data definition.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AssertDBContent

func AssertDBContent(t *testing.T, db *sql.DB, fixtureFile string)

AssertDBContent makes a dump of the database contents (as a sequence of INSERT statements) and runs diff(1) against the given file, producing a test error if these two are different from each other.

func Connect

func Connect(dbURL url.URL, cfg Configuration) (*sql.DB, error)

Connect connects to a Postgres database.

The given URL must be a libpq connection URL, see: <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>

We recommend constructing the URL with func URLFrom.

func ConnectForTest

func ConnectForTest(t *testing.T, cfg Configuration, opts ...TestSetupOption) *sql.DB

ConnectForTest connects to the test database server managed by func WithTestDB(). Any number of TestSetupOption arguments can be given to reset and prepare the database for the test run.

Each test will run in its own separate database (whose name is the same as the test name), so it is safe to mark tests as t.Parallel() to run multiple tests within the same package concurrently.

func NewTracker

func NewTracker(t *testing.T, db *sql.DB) (*Tracker, Assertable)

NewTracker creates a new Tracker.

Since the initial creation involves taking a snapshot, this snapshot is returned as a second value. This is an optimization, since it is often desired to assert on the full DB contents when creating the tracker. Calling Tracker.DBContent() directly after NewTracker() would do a useless second snapshot.

func URLFrom

func URLFrom(parts URLParts) (url.URL, error)

URLFrom constructs a libpq connection URL from the provided parts. The parts are typically retrieved from environment variables, for example:

dbURL := must.Return(easypg.URLFrom(easypg.URLParts {
	HostName:          osext.GetenvOrDefault("FOOBAR_DB_HOSTNAME", "localhost"),
	Port:              osext.GetenvOrDefault("FOOBAR_DB_PORT", "5432"),
	UserName:          osext.GetenvOrDefault("FOOBAR_DB_USERNAME", "postgres"),
	Password:          os.Getenv("FOOBAR_DB_PASSWORD"),
	ConnectionOptions: os.Getenv("FOOBAR_DB_CONNECTION_OPTIONS"),
	DatabaseName:      osext.GetenvOrDefault("FOOBAR_DB_NAME", "foobar"),
}))
db := must.Return(easypg.Connect(dbURL, easypg.Configuration{ ... }))

We provide URLFrom() as a separate function, instead of just putting the fields of URLParts into the Configuration struct, to accommodate applications that may want to accept a fully-formed postgres:// URL from outside instead of building it up from individual parts.

func WithTestDB

func WithTestDB(m *testing.M, action func() int) int

WithTestDB spawns a PostgreSQL database for the duration of a `go test` run. Its data directory, configuration and logs are stored in the ".testdb" directory below the repository root.

How to interact with the test database:

  • To inspect it manually, use one of the helper scripts in the ".testdb" directory, e.g. ".testdb/psql.sh".
  • It is currently not supported to run tests for multiple packages concurrently, so make sure to run "go test" with "-p 1".
  • The "/.testdb" directory should be added to your repository's .gitignore rules.

This function takes a testing.M because it is supposed to be called from TestMain(). This is required to ensure that its cleanup phase shuts down the database server after all tests have been executed. Add a TestMain() like this to each package that needs to interact with the test database:

func TestMain(m *testing.M) {
	easypg.WithTestDB(m, func() int { return m.Run() })
}

This function will fail when running as root (which might happen in some Docker containers), because PostgreSQL refuses to run as UID 0.

Types

type Assertable

type Assertable struct {
	// contains filtered or unexported fields
}

Assertable contains a set of SQL statements. Instances are produced by methods on type Tracker.

func (Assertable) AssertEmpty

func (a Assertable) AssertEmpty()

AssertEmpty is a shorthand for AssertEqual("").

func (Assertable) AssertEqual

func (a Assertable) AssertEqual(expected string)

AssertEqual compares the set of SQL statements to those in the given string literal. A test error is generated in case of differences. This assertion is lenient with regards to whitespace to enable callers to format their string literals in a way that fits nicely in the surrounding code.

func (Assertable) AssertEqualToFile

func (a Assertable) AssertEqualToFile(fixtureFile string)

AssertEqualToFile compares the set of SQL statements to those in the given file. A test error is generated in case of differences.

func (Assertable) AssertEqualf

func (a Assertable) AssertEqualf(format string, args ...any)

AssertEqualf is a shorthand for AssertEqual(fmt.Sprintf(...)).

func (Assertable) Ignore

func (a Assertable) Ignore()

Ignore is a no-op. It is commonly used like `tr.DBChanges().Ignore()`, to clarify that a certain set of DB changes is not asserted on.

type Configuration

type Configuration struct {
	// (required) The schema migrations, in Postgres syntax. See above for details.
	Migrations map[string]string
	// (optional) If not empty, use this database/sql driver instead of "postgres".
	// This is useful e.g. when using github.com/majewsky/sqlproxy.
	OverrideDriverName string
}

Configuration contains settings for Init(). The field Migrations needs to have keys matching the filename format expected by github.com/golang-migrate/migrate (see documentation there for details), for example:

cfg.Migrations = map[string]string{
    "001_initial.up.sql": `
        CREATE TABLE things (
            id   BIGSERIAL NOT NULL PRIMARY KEY,
            name TEXT NOT NULL,
        );
    `,
    "001_initial.down.sql": `
        DROP TABLE things;
    `,
}

type TestSetupOption

type TestSetupOption func(*testSetupParams)

TestSetupOption is an optional behavior that can be given to ConnectForTest().

func ClearContentsWith

func ClearContentsWith(sqlStatement string) TestSetupOption

ClearContentsWith is a TestSetupOption that removes records from the DB using the provided SQL statement. If provided, this runs directly after connecting, before any other setup phase. The provided SQL statement is executed repeatedly, until result.RowsAffected() == 0 is observed.

Prefer ClearTables() over this, and only use this if ClearTables() does not work.

func ClearTables

func ClearTables(tableNames ...string) TestSetupOption

ClearTables is a TestSetupOption that removes all rows from the given tables.

This option only works for tables that can be cleared with `DELETE FROM <table>`. If specific setups like "ON DELETE RESTRICT" constraints make that impossible, use ClearContentsWith() to provide a specialized clearing method that runs before ClearTables().

func LoadSQLFile

func LoadSQLFile(path string) TestSetupOption

LoadSQLFile is a TestSetupOption that loads a file containing SQL statements and executes them all. Every SQL statement must be on a single line.

This executes after any ClearTables() options, but before any ResetPrimaryKeys() options.

func OverrideDatabaseName

func OverrideDatabaseName(dbName string) TestSetupOption

OverrideDatabaseName is a TestSetupOption that picks a different database name than the default of t.Name().

This is only necessary if a single test needs to use multiple database connections at the same time, e.g. to simulate two separate deployments of the application next to each other.

func ResetPrimaryKeys

func ResetPrimaryKeys(tableNames ...string) TestSetupOption

ResetPrimaryKeys is a TestSetupOption that resets the sequences for the "id" column of the given tables to start at 1 again (or if there are entries in the table, to start right after the entry with the highest ID).

type Tracker

type Tracker struct {
	// contains filtered or unexported fields
}

Tracker keeps a copy of the database contents and allows for checking the database contents (or changes made to them) during tests.

func (*Tracker) DBChanges

func (t *Tracker) DBChanges() Assertable

DBChanges produces a diff of the current database contents against the state at the last Tracker call, as a sequence of INSERT/UPDATE/DELETE statements on which test assertions can be executed.

func (*Tracker) DBContent

func (t *Tracker) DBContent() Assertable

DBContent produces a dump of the current database contents, as a sequence of INSERT statements on which test assertions can be executed.

type URLParts

type URLParts struct {
	HostName               string            // required
	Port                   string            // optional (default value = 5432 for postgres:// scheme)
	UserName               string            // required
	Password               string            // optional
	ConnectionOptions      string            // optional (usually used for options coming in via config)
	ExtraConnectionOptions map[string]string // optional (usually used for options coming in via code)
	DatabaseName           string            // required
}

URLParts contains the arguments for func URLFrom(), see documentation over there.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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