package store import ( "context" "testing" "time" "github.com/jackc/pgx/v5/pgconn" ) func TestPostgresPoolConfigSetsDiagnosticAndConnectTimeout(t *testing.T) { config, err := postgresPoolConfig("postgresql://gateway:password@localhost:5432/gateway?sslmode=disable") if err != nil { t.Fatalf("parse PostgreSQL pool config: %v", err) } if config.ConnConfig.ConnectTimeout != 5*time.Second { t.Fatalf("connect timeout = %s, want 5s", config.ConnConfig.ConnectTimeout) } if applicationName := config.ConnConfig.RuntimeParams["application_name"]; applicationName != "easyai-ai-gateway" { t.Fatalf("application_name = %q, want easyai-ai-gateway", applicationName) } } func TestPostgresPoolConfigRejectsMalformedURL(t *testing.T) { if _, err := postgresPoolConfig("://malformed"); err == nil { t.Fatal("expected malformed PostgreSQL URL to fail") } } func TestPostgresPoolConfigUsesExplicitMinimumIdleConnections(t *testing.T) { config, err := postgresPoolConfig( "postgresql://gateway:password@localhost:5432/gateway?sslmode=disable", 24, ) if err != nil { t.Fatalf("parse PostgreSQL pool config: %v", err) } if config.MaxConns != 24 || config.MinIdleConns != 0 { t.Fatalf( "pool bounds max=%d minIdle=%d, want 24/0", config.MaxConns, config.MinIdleConns, ) } warmed, err := postgresPoolConfigWithOptions( "postgresql://gateway:password@localhost:5432/gateway?sslmode=disable", PostgresPoolOptions{MaxConns: 24, MinIdleConns: 4}, ) if err != nil { t.Fatalf("parse warmed PostgreSQL pool config: %v", err) } if warmed.MinIdleConns != 4 { t.Fatalf("warmed pool minIdle=%d, want 4", warmed.MinIdleConns) } } func TestPostgresPoolConfigSetsBoundedTransactionTimeouts(t *testing.T) { config, err := postgresPoolConfigWithOptions( "postgresql://gateway:password@localhost:5432/gateway?sslmode=disable", PostgresPoolOptions{ MaxConns: 32, MinIdleConns: 4, IdleInTransactionTimeout: 60 * time.Second, LockTimeout: 30 * time.Second, }, ) if err != nil { t.Fatalf("parse PostgreSQL pool config: %v", err) } if got := config.ConnConfig.RuntimeParams["idle_in_transaction_session_timeout"]; got != "60000ms" { t.Fatalf("idle transaction timeout = %q, want 60000ms", got) } if got := config.ConnConfig.RuntimeParams["lock_timeout"]; got != "30000ms" { t.Fatalf("lock timeout = %q, want 30000ms", got) } if config.MaxConns != 32 || config.MinIdleConns != 4 { t.Fatalf("pool bounds max=%d minIdle=%d, want 32/4", config.MaxConns, config.MinIdleConns) } } func TestIsPostgresUnavailableClassifiesConnectivityFailures(t *testing.T) { for _, testCase := range []struct { name string err error }{ {name: "deadline", err: context.DeadlineExceeded}, {name: "connection exception", err: &pgconn.PgError{Code: "08006"}}, {name: "too many connections", err: &pgconn.PgError{Code: "53300"}}, {name: "cannot connect now", err: &pgconn.PgError{Code: "57P03"}}, } { t.Run(testCase.name, func(t *testing.T) { if !IsPostgresUnavailable(testCase.err) { t.Fatalf("error %v was not classified as PostgreSQL unavailable", testCase.err) } }) } if IsPostgresUnavailable(&pgconn.PgError{Code: "42601"}) { t.Fatal("SQL syntax error was incorrectly classified as PostgreSQL unavailable") } } func TestIsPostgresLockTimeout(t *testing.T) { if !IsPostgresLockTimeout(&pgconn.PgError{Code: "55P03"}) { t.Fatal("lock timeout was not classified") } if IsPostgresLockTimeout(&pgconn.PgError{Code: "57014"}) { t.Fatal("query cancellation was classified as lock timeout") } }