Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,18 @@ jobs:
- 3306:3306

steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e
with:
go-version: ${{ matrix.go-version }}
cache: true

- name: Cache Go tools
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: ~/go/bin
key: ${{ runner.os }}-go-tools-${{ hashFiles('.github/workflows/ci.yml') }}
Expand Down Expand Up @@ -93,7 +93,7 @@ jobs:
GOMAXPROCS=$RACE_CORES go test -race github.com/bashhack/testdb/...

- name: Upload coverage report
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9
with:
name: go-coverage-report-${{ matrix.os }}
path: ./coverage.html
Expand Down
12 changes: 8 additions & 4 deletions mysql/concurrency_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,13 @@ func TestTemplateSurvivesKilledProcess(t *testing.T) {
return err == nil
})
users := qualify(name, bookkeepingPrefix+"users")
readyTable := qualify(name, bookkeepingPrefix+"ready")
readyPattern := strings.ReplaceAll(readyPrefix, "_", `\_`) + `\_%`
// The pool records a database before cloning into it, so wait for the
// databases themselves, not the records; a kill that lands mid-clone
// is a fine case for the sweep, but the assertion below needs both
// databases to exist
waitUntil(t, 30*time.Second, "holder's pool to fill", func() bool {
return scanInt(t, admin, "SELECT COUNT(*) FROM "+readyTable) == 2
return schemasLike(t, admin, readyPattern) == 2
})
if n := scanInt(t, admin, "SELECT COUNT(*) FROM "+users); n != 1 {
t.Fatalf("%d template users registered while the holder runs, want 1\n%s", n, holderOut.String())
Expand All @@ -187,7 +191,7 @@ func TestTemplateSurvivesKilledProcess(t *testing.T) {
waitUntil(t, 10*time.Second, "the killed process's session to end", func() bool {
return scanInt(t, admin, "SELECT COUNT(*) FROM information_schema.PROCESSLIST WHERE id = ?", holderConn) == 0
})
if n := schemasLike(t, admin, strings.ReplaceAll(readyPrefix, "_", `\_`)+`\_%`); n != 2 {
if n := schemasLike(t, admin, readyPattern); n != 2 {
t.Fatalf("expected the dead process's 2 prepared databases to still exist before the sweep, found %d", n)
}

Expand All @@ -208,7 +212,7 @@ func TestTemplateSurvivesKilledProcess(t *testing.T) {
if schemaExists(t, admin, name) {
t.Errorf("template %s was left behind: a dead process kept it alive", name)
}
if n := schemasLike(t, admin, strings.ReplaceAll(readyPrefix, "_", `\_`)+`\_%`); n != 0 {
if n := schemasLike(t, admin, readyPattern); n != 0 {
t.Errorf("%d prepared databases of the dead process were not swept", n)
}
}
101 changes: 101 additions & 0 deletions mysql/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package mysql_test

import (
"context"
"database/sql"
"fmt"
"log"
"os"
"testing"

"github.com/bashhack/testdb"
"github.com/bashhack/testdb/mysql"
)

// These examples show the MySQL API, which mirrors postgres's. They are
// compile-checked but not run: a runnable example cannot skip when no
// MySQL server is reachable, and the MySQL tests in this module skip
// rather than fail on a machine that only runs PostgreSQL.

// Example_setup is the common case: an isolated database with the schema
// applied, cleaned up when the test ends.
func Example_setup() {
var t testing.TB // in a test this is your *testing.T

db := mysql.Setup(t, testdb.WithMigrator(mysql.SQLFiles("./schema")))

_, err := db.ExecContext(context.Background(),
"INSERT INTO users (email) VALUES (?)", "test@example.com")
if err != nil {
t.Fatalf("Insert failed: %v", err)
}
// No cleanup to write: the database is dropped via t.Cleanup
}

// Example_customInitializer hands the database to your own type, for
// sqlx, GORM, or an application wrapper.
func Example_customInitializer() {
var t testing.TB

db := mysql.New(t, &appDBInitializer{},
testdb.WithMigrator(mysql.LoadSQLFile("./schema.sql")))

appDB := db.Entity().(*appDB)
fmt.Println(appDB.Name)
}

// appDB stands in for an application's own database type.
type appDB struct {
*sql.DB
Name string
}

type appDBInitializer struct{}

func (appDBInitializer) InitializeTestDatabase(ctx context.Context, dsn string) (any, error) {
db, err := sql.Open("mysql", dsn)
if err != nil {
return nil, err
}
return &appDB{DB: db, Name: "app"}, nil
}

// Example_template builds the schema once in TestMain and gives every
// test a prepared clone, which is where MySQL's speed comes from.
func Example_template() {
// func TestMain(m *testing.M) {
ctx := context.Background()

tpl, err := mysql.EnsureTemplate(ctx, "app_schema",
mysql.SQLFiles("./schema"),
mysql.WithReadyDatabases(8)) // clones kept prepared ahead of tests
if err != nil {
log.Fatal(err)
}

var m *testing.M
code := m.Run()

if err := tpl.Release(ctx); err != nil {
log.Print(err)
}
os.Exit(code)
// }
//
// func TestOrders(t *testing.T) {
// db := mysql.Setup(t, testdb.WithTemplate("app_schema")) // ~6 ms
// }
}

// Example_keepOnFailure keeps a failed test's database and logs a mysql
// command line to open it.
func Example_keepOnFailure() {
var t testing.TB

db := mysql.Setup(t, testdb.WithKeepOnFailure())
_ = db
// On failure the test log shows:
// testdb: kept database test_1761100000_a1b2c3d4 for inspection:
// mysql --host='localhost' --port=3306 --user='root' --password='root' 'test_1761100000_a1b2c3d4'
// The same switch for a whole run: TESTDB_KEEP_ON_FAILURE=1 go test ./...
}
Loading