check / check (push) Canceled after 0s
The five counts the cache_stats table held are now Prometheus counters in memory, in internal/imgcache/counters.go, on the default registry that /metrics serves. The Increment methods add to them, so counting makes no database call. cache_stats, Cache.Stats, Service.Stats, CacheStats and the pending counts are gone, with their tests. The background goroutine now writes only access times, and its names say so: StartPendingAccessTimeWrites, StopPendingAccessTimeWrites and pendingaccesstimes.go. The counters count for the whole process, so the tests that check how much they rose do not call t.Parallel. Two stop tests whose write under way was a hit count now use an access time. Model: opus-5-5
253 lines
5.1 KiB
Go
253 lines
5.1 KiB
Go
package database
|
|
|
|
import (
|
|
"database/sql"
|
|
"testing"
|
|
|
|
_ "modernc.org/sqlite" // SQLite driver registration
|
|
)
|
|
|
|
// openTestDB returns a fresh in-memory SQLite database.
|
|
func openTestDB(t *testing.T) *sql.DB {
|
|
t.Helper()
|
|
|
|
db, err := sql.Open("sqlite", ":memory:")
|
|
if err != nil {
|
|
t.Fatalf("failed to open test db: %v", err)
|
|
}
|
|
|
|
t.Cleanup(func() { _ = db.Close() })
|
|
|
|
return db
|
|
}
|
|
|
|
func TestParseMigrationVersion(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
filename string
|
|
want int
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "version only",
|
|
filename: "001.sql",
|
|
want: 1,
|
|
},
|
|
{
|
|
name: "version with description",
|
|
filename: "001_initial_schema.sql",
|
|
want: 1,
|
|
},
|
|
{
|
|
name: "multi-digit version",
|
|
filename: "042_add_indexes.sql",
|
|
want: 42,
|
|
},
|
|
{
|
|
name: "long version number",
|
|
filename: "00001_long_prefix.sql",
|
|
want: 1,
|
|
},
|
|
{
|
|
name: "description with multiple underscores",
|
|
filename: "003_add_user_auth_tables.sql",
|
|
want: 3,
|
|
},
|
|
{
|
|
name: "empty filename",
|
|
filename: ".sql",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "leading underscore",
|
|
filename: "_description.sql",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "non-numeric version",
|
|
filename: "abc_migration.sql",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "mixed alphanumeric version",
|
|
filename: "001a_migration.sql",
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := ParseMigrationVersion(tt.filename)
|
|
if tt.wantErr {
|
|
if err == nil {
|
|
t.Errorf("ParseMigrationVersion(%q) expected error, got %d", tt.filename, got)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Errorf("ParseMigrationVersion(%q) unexpected error: %v", tt.filename, err)
|
|
|
|
return
|
|
}
|
|
|
|
if got != tt.want {
|
|
t.Errorf("ParseMigrationVersion(%q) = %d, want %d", tt.filename, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestApplyMigrations_CreatesSchemaAndTables(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db := openTestDB(t)
|
|
ctx := t.Context()
|
|
|
|
err := ApplyMigrations(ctx, db, nil)
|
|
if err != nil {
|
|
t.Fatalf("ApplyMigrations failed: %v", err)
|
|
}
|
|
|
|
// The schema_migrations table must exist and contain at least
|
|
// version 0 (the bootstrap) and 1 (the initial schema).
|
|
rows, err := db.QueryContext(
|
|
ctx, "SELECT version FROM schema_migrations ORDER BY version",
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("failed to query schema_migrations: %v", err)
|
|
}
|
|
|
|
defer func() { _ = rows.Close() }()
|
|
|
|
var versions []int
|
|
|
|
for rows.Next() {
|
|
var v int
|
|
|
|
scanErr := rows.Scan(&v)
|
|
if scanErr != nil {
|
|
t.Fatalf("failed to scan version: %v", scanErr)
|
|
}
|
|
|
|
versions = append(versions, v)
|
|
}
|
|
|
|
err = rows.Err()
|
|
if err != nil {
|
|
t.Fatalf("row iteration error: %v", err)
|
|
}
|
|
|
|
if len(versions) < 2 {
|
|
t.Fatalf(
|
|
"expected at least 2 migrations recorded, got %d: %v",
|
|
len(versions), versions,
|
|
)
|
|
}
|
|
|
|
if versions[0] != 0 {
|
|
t.Errorf("first recorded migration = %d, want %d", versions[0], 0)
|
|
}
|
|
|
|
if versions[1] != 1 {
|
|
t.Errorf("second recorded migration = %d, want %d", versions[1], 1)
|
|
}
|
|
|
|
// Verify that the application tables created by 001.sql exist.
|
|
tables := []string{"source_content", "negative_cache"}
|
|
for _, table := range tables {
|
|
var count int
|
|
|
|
err := db.QueryRowContext(
|
|
ctx,
|
|
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=?",
|
|
table,
|
|
).Scan(&count)
|
|
if err != nil {
|
|
t.Fatalf("failed to check for table %s: %v", table, err)
|
|
}
|
|
|
|
if count != 1 {
|
|
t.Errorf("table %s does not exist after migrations", table)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestApplyMigrations_Idempotent(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db := openTestDB(t)
|
|
ctx := t.Context()
|
|
|
|
err := ApplyMigrations(ctx, db, nil)
|
|
if err != nil {
|
|
t.Fatalf("first ApplyMigrations failed: %v", err)
|
|
}
|
|
|
|
// Running a second time must succeed without errors.
|
|
err = ApplyMigrations(ctx, db, nil)
|
|
if err != nil {
|
|
t.Fatalf("second ApplyMigrations failed: %v", err)
|
|
}
|
|
|
|
// Verify no duplicate rows in schema_migrations.
|
|
var count int
|
|
|
|
err = db.QueryRowContext(
|
|
ctx, "SELECT COUNT(*) FROM schema_migrations WHERE version = 0",
|
|
).Scan(&count)
|
|
if err != nil {
|
|
t.Fatalf("failed to count version 0 rows: %v", err)
|
|
}
|
|
|
|
if count != 1 {
|
|
t.Errorf("expected exactly 1 row for version 0, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestBootstrapMigrationsTable_FreshDatabase(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db := openTestDB(t)
|
|
ctx := t.Context()
|
|
|
|
err := bootstrapMigrationsTable(ctx, db, nil)
|
|
if err != nil {
|
|
t.Fatalf("bootstrapMigrationsTable failed: %v", err)
|
|
}
|
|
|
|
// schema_migrations table must exist.
|
|
var tableCount int
|
|
|
|
err = db.QueryRowContext(
|
|
ctx,
|
|
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='schema_migrations'",
|
|
).Scan(&tableCount)
|
|
if err != nil {
|
|
t.Fatalf("failed to check for table: %v", err)
|
|
}
|
|
|
|
if tableCount != 1 {
|
|
t.Fatalf("schema_migrations table not created")
|
|
}
|
|
|
|
// Version 0 must be recorded.
|
|
var recorded int
|
|
|
|
err = db.QueryRowContext(
|
|
ctx, "SELECT COUNT(*) FROM schema_migrations WHERE version = 0",
|
|
).Scan(&recorded)
|
|
if err != nil {
|
|
t.Fatalf("failed to check version: %v", err)
|
|
}
|
|
|
|
if recorded != 1 {
|
|
t.Errorf("expected version 0 to be recorded, got count %d", recorded)
|
|
}
|
|
}
|