Make the internal/handlers tests fast (closes #113) #116
@@ -6,7 +6,10 @@ package handlers_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -22,6 +25,7 @@ import (
|
|||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
"go.uber.org/fx/fxtest"
|
"go.uber.org/fx/fxtest"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"modernc.org/sqlite"
|
||||||
"sneak.berlin/go/neoirc/internal/broker"
|
"sneak.berlin/go/neoirc/internal/broker"
|
||||||
"sneak.berlin/go/neoirc/internal/config"
|
"sneak.berlin/go/neoirc/internal/config"
|
||||||
"sneak.berlin/go/neoirc/internal/db"
|
"sneak.berlin/go/neoirc/internal/db"
|
||||||
@@ -37,9 +41,76 @@ import (
|
|||||||
"sneak.berlin/go/neoirc/pkg/irc"
|
"sneak.berlin/go/neoirc/pkg/irc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// templateDBURL names the in-memory database TestMain migrates once
|
||||||
|
// and every test copies; it lasts while TestMain holds it open.
|
||||||
|
const templateDBURL = "file:template?mode=memory&cache=shared"
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
db.SetBcryptCost(bcrypt.MinCost)
|
db.SetBcryptCost(bcrypt.MinCost)
|
||||||
os.Exit(m.Run())
|
|
||||||
|
template, err := sql.Open("sqlite", templateDBURL)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.NewTestDatabaseFromConn(template).
|
||||||
|
RunMigrations(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
code := m.Run()
|
||||||
|
|
||||||
|
_ = template.Close()
|
||||||
|
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyTemplateDB creates the in-memory database dbURL as a copy of
|
||||||
|
// the migrated template, so a test does not migrate the schema
|
||||||
|
// again. The copy lasts until the test ends.
|
||||||
|
func copyTemplateDB(t *testing.T, dbURL string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
database, err := sql.Open("sqlite", dbURL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("open test db: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Cleanup(func() { _ = database.Close() })
|
||||||
|
|
||||||
|
conn, err := database.Conn(t.Context())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("connect test db: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() { _ = conn.Close() }()
|
||||||
|
|
||||||
|
err = conn.Raw(func(driverConn any) error {
|
||||||
|
restorer, ok := driverConn.(interface {
|
||||||
|
NewRestore(srcURI string) (*sqlite.Backup, error)
|
||||||
|
})
|
||||||
|
if !ok {
|
||||||
|
return errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
restore, restoreErr := restorer.NewRestore(templateDBURL)
|
||||||
|
if restoreErr != nil {
|
||||||
|
return fmt.Errorf("start restore: %w", restoreErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, restoreErr = restore.Step(-1)
|
||||||
|
if restoreErr != nil {
|
||||||
|
_ = restore.Finish()
|
||||||
|
|
||||||
|
return fmt.Errorf("restore: %w", restoreErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return restore.Finish()
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("copy template db: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -84,6 +155,7 @@ func newTestServerWith(
|
|||||||
"file:test_%p?mode=memory&cache=shared",
|
"file:test_%p?mode=memory&cache=shared",
|
||||||
t,
|
t,
|
||||||
)
|
)
|
||||||
|
copyTemplateDB(t, dbURL)
|
||||||
|
|
||||||
var srv *server.Server
|
var srv *server.Server
|
||||||
|
|
||||||
@@ -109,6 +181,8 @@ func newTestServerWith(
|
|||||||
|
|
||||||
cfg.DBURL = dbURL
|
cfg.DBURL = dbURL
|
||||||
cfg.Port = 0
|
cfg.Port = 0
|
||||||
|
// One line: each line is a database write per new session.
|
||||||
|
cfg.MOTD = "neoirc test server"
|
||||||
cfg.HashcashBits = hashcashBits
|
cfg.HashcashBits = hashcashBits
|
||||||
cfg.OperName = operName
|
cfg.OperName = operName
|
||||||
cfg.OperPassword = operPassword
|
cfg.OperPassword = operPassword
|
||||||
|
|||||||
Reference in New Issue
Block a user