@@ -4,39 +4,85 @@ import (
44 "context"
55 "crypto/rand"
66 "encoding/binary"
7+ "errors"
78 "fmt"
89 "testing"
910 "time"
1011
1112 "github.com/jackc/pgx/v5"
13+ "github.com/jackc/pgx/v5/pgconn"
1214 "github.com/stretchr/testify/require"
1315)
1416
15- // NewTestDB creates an isolated PostgreSQL database for each test.
16- // Each test gets its own database with a random name, eliminating all coordination issues.
17+ const templateDBName = "mcp_registry_test_template"
18+
19+ // ensureTemplateDB creates a template database with migrations applied
20+ // Multiple processes may call this, so we handle race conditions
21+ func ensureTemplateDB (ctx context.Context , adminConn * pgx.Conn ) error {
22+ // Check if template exists
23+ var exists bool
24+ err := adminConn .QueryRow (ctx , "SELECT EXISTS(SELECT 1 FROM pg_database WHERE datname = $1)" , templateDBName ).Scan (& exists )
25+ if err != nil {
26+ return fmt .Errorf ("failed to check template database: %w" , err )
27+ }
28+
29+ if exists {
30+ // Template already exists
31+ return nil
32+ }
33+
34+ // Create template database
35+ _ , err = adminConn .Exec (ctx , fmt .Sprintf ("CREATE DATABASE %s" , templateDBName ))
36+ if err != nil {
37+ // Ignore duplicate error - another process created it concurrently
38+ var pgErr * pgconn.PgError
39+ if errors .As (err , & pgErr ) && pgErr .Code == "23505" {
40+ return nil
41+ }
42+ return fmt .Errorf ("failed to create template database: %w" , err )
43+ }
44+
45+ // Connect to template and run migrations
46+ templateURI := fmt .Sprintf ("postgres://mcpregistry:mcpregistry@localhost:5432/%s?sslmode=disable" , templateDBName )
47+ templateDB , err := NewPostgreSQL (ctx , templateURI )
48+ if err != nil {
49+ return fmt .Errorf ("failed to connect to template database: %w" , err )
50+ }
51+ defer templateDB .Close ()
52+
53+ // Migrations run automatically in NewPostgreSQL
54+ return nil
55+ }
56+
57+ // NewTestDB creates an isolated PostgreSQL database for each test by copying a template.
58+ // The template database has migrations pre-applied, so each test is fast.
1759// Requires PostgreSQL to be running on localhost:5432 (e.g., via docker-compose).
1860func NewTestDB (t * testing.T ) Database {
1961 t .Helper ()
2062
2163 ctx , cancel := context .WithTimeout (context .Background (), 30 * time .Second )
2264 defer cancel ()
2365
66+ // Connect to postgres database
67+ adminURI := "postgres://mcpregistry:mcpregistry@localhost:5432/postgres?sslmode=disable"
68+ adminConn , err := pgx .Connect (ctx , adminURI )
69+ require .NoError (t , err , "Failed to connect to PostgreSQL. Make sure PostgreSQL is running via: docker-compose up -d postgres" )
70+ defer adminConn .Close (ctx )
71+
72+ // Ensure template database exists with migrations
73+ err = ensureTemplateDB (ctx , adminConn )
74+ require .NoError (t , err , "Failed to initialize template database" )
75+
2476 // Generate unique database name for this test
2577 var randomBytes [8 ]byte
26- _ , err : = rand .Read (randomBytes [:])
78+ _ , err = rand .Read (randomBytes [:])
2779 require .NoError (t , err , "Failed to generate random database id" )
2880 randomInt := binary .BigEndian .Uint64 (randomBytes [:])
2981 dbName := fmt .Sprintf ("test_%d" , randomInt )
3082
31- // Connect to postgres database to create test database
32- adminURI := "postgres://mcpregistry:mcpregistry@localhost:5432/postgres?sslmode=disable"
33- adminConn , err := pgx .Connect (ctx , adminURI )
34- require .NoError (t , err , "Failed to connect to PostgreSQL. Make sure PostgreSQL is running via: docker-compose up -d postgres" )
35- defer adminConn .Close (ctx )
36-
37- // Create test database
38- _ , err = adminConn .Exec (ctx , fmt .Sprintf ("CREATE DATABASE %s" , dbName ))
39- require .NoError (t , err , "Failed to create test database" )
83+ // Create test database from template (fast - just copies files)
84+ _ , err = adminConn .Exec (ctx , fmt .Sprintf ("CREATE DATABASE %s TEMPLATE %s" , dbName , templateDBName ))
85+ require .NoError (t , err , "Failed to create test database from template" )
4086
4187 // Register cleanup to drop database
4288 t .Cleanup (func () {
@@ -53,8 +99,9 @@ func NewTestDB(t *testing.T) Database {
5399 _ , _ = adminConn .Exec (cleanupCtx , fmt .Sprintf ("DROP DATABASE IF EXISTS %s" , dbName ))
54100 })
55101
56- // Connect to test database
102+ // Connect to test database (no migrations needed - copied from template)
57103 testURI := fmt .Sprintf ("postgres://mcpregistry:mcpregistry@localhost:5432/%s?sslmode=disable" , dbName )
104+
58105 db , err := NewPostgreSQL (ctx , testURI )
59106 require .NoError (t , err , "Failed to connect to test database" )
60107
0 commit comments