Make the internal/handlers tests fast (closes #113) #116

Merged
clawbot merged 1 commits from issue-113-fast-handlers-tests into next 2026-10-07 00:13:42 +02:00
+75 -1
View File
@@ -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