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 ¶
- func AssertDBContent(t *testing.T, db *sql.DB, fixtureFile string)
- func Connect(dbURL url.URL, cfg Configuration) (*sql.DB, error)
- func ConnectForTest(t *testing.T, cfg Configuration, opts ...TestSetupOption) *sql.DB
- func NewTracker(t *testing.T, db *sql.DB) (*Tracker, Assertable)
- func URLFrom(parts URLParts) (url.URL, error)
- func WithTestDB(m *testing.M, action func() int) int
- type Assertable
- type Configuration
- type TestSetupOption
- type Tracker
- type URLParts
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AssertDBContent ¶
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 ¶
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 ¶
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 ¶
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 ¶
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.