137 lines
3.1 KiB
Go
137 lines
3.1 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
)
|
|
|
|
// ExpectedUserVersion is the schema version after all migrations have been applied.
|
|
// Increment this when adding a new migration file.
|
|
const ExpectedUserVersion = 1
|
|
|
|
func TestSchema(t *testing.T) {
|
|
var (
|
|
ctx = t.Context()
|
|
db = createDB(t)
|
|
)
|
|
defer db.Close()
|
|
|
|
t.Run("InitialVersion", func(t *testing.T) {
|
|
testInitialVersion(t, ctx, db)
|
|
})
|
|
|
|
if err := db.ApplySchema(ctx, nil); err != nil {
|
|
t.Fatalf("ApplySchema: %v", err)
|
|
}
|
|
|
|
t.Run("ApplySchema", func(t *testing.T) {
|
|
testApplySchema(t, ctx, db)
|
|
})
|
|
t.Run("ApplySchemaIdempotent", func(t *testing.T) {
|
|
testSchemaIdempotent(t, ctx, db)
|
|
})
|
|
t.Run("ApplySchemaTablesExist", func(t *testing.T) {
|
|
testTablesExist(t, ctx, db)
|
|
})
|
|
t.Run("ApplySchemaIndexesExist", func(t *testing.T) {
|
|
testSchemaIndexes(t, ctx, db)
|
|
})
|
|
}
|
|
|
|
func testInitialVersion(t *testing.T, ctx context.Context, db *SQLite) {
|
|
var version int
|
|
err := db.Reader.QueryRowContext(
|
|
ctx,
|
|
"PRAGMA user_version",
|
|
).Scan(&version)
|
|
if err != nil {
|
|
t.Fatalf("PRAGMA user_version: %v", err)
|
|
}
|
|
if version != 0 {
|
|
t.Fatalf("expected initial version 0, got %d", version)
|
|
}
|
|
}
|
|
|
|
func testApplySchema(t *testing.T, ctx context.Context, db *SQLite) {
|
|
var version int
|
|
err := db.Reader.QueryRowContext(
|
|
ctx,
|
|
"PRAGMA user_version",
|
|
).Scan(&version)
|
|
if err != nil {
|
|
t.Fatalf("PRAGMA user_version: %v", err)
|
|
}
|
|
if version != ExpectedUserVersion {
|
|
t.Fatalf("expected version %d, got %d", ExpectedUserVersion, version)
|
|
}
|
|
}
|
|
|
|
func testTablesExist(t *testing.T, ctx context.Context, db *SQLite) {
|
|
expected := []string{
|
|
"approvals",
|
|
"conversations",
|
|
"messages",
|
|
"sessions",
|
|
"users",
|
|
}
|
|
for _, table := range expected {
|
|
var exists bool
|
|
err := db.Reader.QueryRowContext(
|
|
ctx,
|
|
"SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1)",
|
|
table,
|
|
).Scan(&exists)
|
|
if err != nil {
|
|
t.Fatalf("query sqlite_master for %s: %v", table, err)
|
|
}
|
|
if !exists {
|
|
t.Errorf("table %s does not exist", table)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testSchemaIdempotent(t *testing.T, ctx context.Context, db *SQLite) {
|
|
// Apply schema again; should succeed without error.
|
|
if err := db.ApplySchema(ctx, nil); err != nil {
|
|
t.Fatalf("second ApplySchema: %v", err)
|
|
}
|
|
|
|
var version int
|
|
err := db.Reader.QueryRowContext(
|
|
ctx,
|
|
"PRAGMA user_version",
|
|
).Scan(&version)
|
|
if err != nil {
|
|
t.Fatalf("PRAGMA user_version: %v", err)
|
|
}
|
|
if version != ExpectedUserVersion {
|
|
t.Fatalf("expected version %d, got %d", ExpectedUserVersion, version)
|
|
}
|
|
}
|
|
|
|
func testSchemaIndexes(t *testing.T, ctx context.Context, db *SQLite) {
|
|
expected := []string{
|
|
"idx_approvals_message",
|
|
"idx_approvals_pending",
|
|
"idx_conv_user",
|
|
"idx_msg_conv",
|
|
"idx_msg_conv_seq",
|
|
"idx_sessions_active",
|
|
"idx_users_deactivated",
|
|
}
|
|
for _, idx := range expected {
|
|
var exists bool
|
|
err := db.Reader.QueryRowContext(
|
|
ctx,
|
|
"SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type = 'index' AND name = ? LIMIT 1)",
|
|
idx,
|
|
).Scan(&exists)
|
|
if err != nil {
|
|
t.Fatalf("query sqlite_master for index %s: %v", idx, err)
|
|
}
|
|
if !exists {
|
|
t.Errorf("index %s does not exist", idx)
|
|
}
|
|
}
|
|
}
|