// Package dbtest gives each test a database of its own and runs raw // SQL on it. Only tests may import it. package dbtest import ( "crypto/rand" "database/sql" "testing" _ "modernc.org/sqlite" // registers the sqlite driver ) // NewURL returns the URL of a new, empty in-memory database for one // test. SQLite deletes an in-memory database when its last connection // closes. A pool closes a connection when a query on it is cancelled // while it runs. So this function holds a connection of its own open // until the test ends. func NewURL(tb testing.TB) string { tb.Helper() // A random name, so that no other test can share the database. dbURL := "file:" + rand.Text() + "?mode=memory&cache=shared" pool, err := sql.Open("sqlite", dbURL) if err != nil { tb.Fatalf("open test database: %v", err) } tb.Cleanup(func() { _ = pool.Close() }) conn, err := pool.Conn(tb.Context()) if err != nil { tb.Fatalf("connect to test database: %v", err) } tb.Cleanup(func() { _ = conn.Close() }) return dbURL } // Exec runs one raw SQL statement on the database at dbURL, over a // connection of its own. A test uses it for what the methods of // db.Database cannot do, such as creating a trigger that makes a // write fail. func Exec(tb testing.TB, dbURL, statement string) { tb.Helper() pool, err := sql.Open("sqlite", dbURL) if err != nil { tb.Fatalf("open test database: %v", err) } defer func() { _ = pool.Close() }() _, err = pool.ExecContext(tb.Context(), statement) if err != nil { tb.Fatalf("run %q: %v", statement, err) } }