Help us shape the future of Go! Take the 2026 Go Developer Survey

testutils

package
v0.18.0 Latest Latest
Warning

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

Go to latest
Published: Oct 6, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package testutils contains some common utilities used exclusively by the test suite.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CreateUniqueTestDatabase

func CreateUniqueTestDatabase(t *testing.T) (string, *sql.DB)

CreateUniqueTestDatabase creates a unique database for a test and returns both the database name and a *sql.DB connection scoped to that database. The connection and database are automatically cleaned up when the test finishes.

func DSN

func DSN() string

func DSNForDatabase

func DSNForDatabase(dbName string) string

DSNForDatabase returns a DSN for a specific database name

func EvenOddHasher added in v0.10.2

func EvenOddHasher(colAny any) (uint64, error)

EvenOddHasher is a test hash function that shards assuming -80 and 80- shards. even goes to -80, odd goes to 80-

func RequireNoEffectiveTLS added in v0.18.0

func RequireNoEffectiveTLS(t *testing.T, dsn, description string)

RequireNoEffectiveTLS asserts that a DSN yields a connection with no TLS.

It deliberately does not assert the DSN omits "tls=". DISABLED writes tls=false, because the driver applies verified TLS to an RDS address whenever the DSN asks for nothing — so an omitted parameter is how DISABLED silently becomes a TLS connection, while an explicit "false" is how it stays off. What matters is the setting the driver ends up with, which is what this reads.

It lives here rather than in dbconn's tests because pkg/migration asserts the same property, and two copies of "what counts as no TLS" would let one of them be strengthened while the other silently stayed weak.

func RunSQL

func RunSQL(t *testing.T, stmt string)

func RunSQLInDatabase

func RunSQLInDatabase(t *testing.T, dbName, stmt string)

RunSQLInDatabase runs SQL in a specific database

func RunSQLInDatabaseAsRoot added in v0.18.0

func RunSQLInDatabaseAsRoot(t *testing.T, dbName, stmt string)

RunSQLInDatabaseAsRoot runs SQL in a specific database as the root user, with the password from MYSQL_DSN (CI gives root and the test user the same password). Use it for statements the test user is not granted, such as CREATE VIEW or CREATE ROUTINE (compose/bootstrap.sql lists its grants).

func SanitizeIdentifier added in v0.18.0

func SanitizeIdentifier(s string) string

SanitizeIdentifier lowercases s and replaces every character outside [a-z0-9_] with '_', so the result can be used as a MySQL identifier without quoting. Use it to derive schema or table names from t.Name().

func SkipBeforeMySQLVersion added in v0.18.0

func SkipBeforeMySQLVersion(t *testing.T, minVersion, reason string)

SkipBeforeMySQLVersion skips the test when the server's version() is older than minVersion (e.g. "8.0.33"), giving reason in the skip message. Use it for behavior an old server gets wrong and that is not worth special-casing, not to hide a failure a supported server should pass.

func SkipFromMySQLVersion added in v0.18.0

func SkipFromMySQLVersion(t *testing.T, fromVersion, reason string)

SkipFromMySQLVersion skips the test when the server's version() is fromVersion (e.g. "9.7.0") or newer, giving reason in the skip message. Use it for behavior a newer server changed or gets wrong and that is not worth special-casing, not to hide a failure a supported server should pass.

func SkipUnlessVectorSupported added in v0.17.0

func SkipUnlessVectorSupported(t *testing.T)

SkipUnlessVectorSupported skips the test unless the server understands the VECTOR data type (MySQL 9.7+). It probes for the feature rather than parsing version(), so a fork or a future version that renames itself still gets the coverage. The probe result is cached for the life of the test binary.

Only an "unknown function" answer from the server counts as unsupported. Anything else — an unreachable host, a bad DSN, an auth failure, any other server error — fails the test instead, so a broken environment cannot masquerade as a whole suite of quietly skipped tests.

func WaitForReplicaHealthy added in v0.13.0

func WaitForReplicaHealthy(t *testing.T, dsn string, timeout time.Duration)

WaitForReplicaHealthy polls SHOW REPLICA STATUS until both the IO and SQL threads report Yes, or the timeout elapses. On timeout it fails the test with an infra-attribution message so a broken CI replica setup is clearly distinguishable from a Spirit migration bug.

Types

type OnLogHandler added in v0.18.0

type OnLogHandler struct {
	slog.Handler
	// contains filtered or unexported fields
}

OnLogHandler wraps a slog.Handler and calls fn, once, the first time a record with message msg is handled, before passing the record on. Tests use it to act at a precise point in code that has no other hook, such as the log line a runner writes as Run starts.

func NewOnLogHandler added in v0.18.0

func NewOnLogHandler(h slog.Handler, msg string, fn func()) *OnLogHandler

NewOnLogHandler returns an OnLogHandler that wraps h.

func (*OnLogHandler) Handle added in v0.18.0

func (h *OnLogHandler) Handle(ctx context.Context, rec slog.Record) error

Handle calls fn the first time msg is logged, then passes rec on. fn runs on the logging goroutine and may itself log.

func (*OnLogHandler) WithAttrs added in v0.18.0

func (h *OnLogHandler) WithAttrs(attrs []slog.Attr) slog.Handler

WithAttrs keeps the hook on loggers derived with With.

func (*OnLogHandler) WithGroup added in v0.18.0

func (h *OnLogHandler) WithGroup(name string) slog.Handler

WithGroup keeps the hook on loggers derived with WithGroup.

type TestTable added in v0.13.0

type TestTable struct {
	Name string
	DB   *sql.DB
}

TestTable manages a test table's lifecycle: creation, cleanup, and provides a DB connection for verification queries after migration.

func NewTestTable added in v0.13.0

func NewTestTable(t *testing.T, name string, createSQL string) *TestTable

NewTestTable creates a test table and registers cleanup to drop it and all Spirit artifacts (_new, _old, _chkpnt) when the test finishes.

Example:

tt := testutils.NewTestTable(t, "mytable",
    `CREATE TABLE mytable (
        id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(255) NOT NULL
    )`)

// Use tt.DB for verification queries after migration
var count int
tt.DB.QueryRowContext(t.Context(), "SELECT COUNT(*) FROM mytable").Scan(&count)

func (*TestTable) SeedRows added in v0.13.0

func (tt *TestTable) SeedRows(t *testing.T, insertSelectSQL string, targetRows int)

SeedRows populates the table by doubling rows until reaching approximately targetRows. The insertSelectSQL should be an INSERT INTO ... SELECT statement WITHOUT a FROM clause. SeedRows appends "FROM dual" for the initial insert, then "FROM <table>" for each doubling iteration.

Example:

// Simple seeding — produces ~4096 identical rows (different auto-increment IDs)
tt.SeedRows(t, "INSERT INTO mytable (name, val) SELECT 'seed', 1", 4096)

// With SQL functions — each row gets unique random data
tt.SeedRows(t, "INSERT INTO mytable (pad) SELECT RANDOM_BYTES(1024)", 100000)

Jump to

Keyboard shortcuts

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