check / check (push) Canceled after 0s
A source is now one source_content row and one file named by its SourceKey, the SHA-256 of its host, path and query, made as the variant key is. A store writes the file, then upserts the row, and removes the file when the row cannot be written; an eviction deletes the row, then the file, with no lock. source_metadata, the JSON files under cache/metadata/, MetadataStorage, the content lock, the eviction transaction and its test hook are gone, as is the reconciliation pass that dropped a source row whose file is missing, so TestReconciliationReadsAPageAtATime now counts 15 rows read, not 20. Two source URLs serving the same bytes store them twice. The tests of the removed sharing and lock go with them; the two eviction cancellation tests no longer use the hook. Model: opus-5-5
256 lines
5.2 KiB
Go
256 lines
5.2 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", "output_content",
|
|
"request_cache", "negative_cache", "cache_stats",
|
|
}
|
|
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)
|
|
}
|
|
}
|