package harness import ( "context" "net/url" "testing" "time" "galaxy/postgres" "github.com/stretchr/testify/require" ) func TestPostgresContainerRoundTrip(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) t.Cleanup(cancel) rt := StartPostgresContainer(t) require.NoError(t, rt.EnsureRoleAndSchema(ctx, "smoke_schema", "smoke_role", "smoke_pass")) cfg := postgres.DefaultConfig() cfg.PrimaryDSN = rt.DSNForSchema("smoke_schema", "smoke_role") cfg.OperationTimeout = 5 * time.Second db, err := postgres.OpenPrimary(ctx, cfg) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, db.Close()) }) require.NoError(t, postgres.Ping(ctx, db, cfg.OperationTimeout)) _, err = db.ExecContext(ctx, `CREATE TABLE notes (id serial PRIMARY KEY, body text NOT NULL)`) require.NoError(t, err) var insertedID int64 require.NoError(t, db.QueryRowContext(ctx, `INSERT INTO notes (body) VALUES ($1) RETURNING id`, "hello").Scan(&insertedID)) require.Greater(t, insertedID, int64(0)) var body string require.NoError(t, db.QueryRowContext(ctx, `SELECT body FROM notes WHERE id = $1`, insertedID).Scan(&body)) require.Equal(t, "hello", body) // search_path is honoured: the unqualified table created above resolved // inside smoke_schema. var schemaName string require.NoError(t, db.QueryRowContext(ctx, `SELECT table_schema FROM information_schema.tables WHERE table_name = 'notes'`, ).Scan(&schemaName)) require.Equal(t, "smoke_schema", schemaName) } func TestEnsureRoleAndSchemaIsIdempotent(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) t.Cleanup(cancel) rt := StartPostgresContainer(t) require.NoError(t, rt.EnsureRoleAndSchema(ctx, "schema_x", "role_x", "pass_x")) require.NoError(t, rt.EnsureRoleAndSchema(ctx, "schema_x", "role_x", "pass_x")) } func TestEnsureRoleAndSchemaSupportsReservedWordIdentifiers(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) t.Cleanup(cancel) rt := StartPostgresContainer(t) // `user` is a SQL reserved word; identifier quoting must keep this working. require.NoError(t, rt.EnsureRoleAndSchema(ctx, "user", "userservice", "secret")) cfg := postgres.DefaultConfig() cfg.PrimaryDSN = rt.DSNForSchema("user", "userservice") cfg.OperationTimeout = 5 * time.Second db, err := postgres.OpenPrimary(ctx, cfg) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, db.Close()) }) require.NoError(t, postgres.Ping(ctx, db, cfg.OperationTimeout)) } func TestWithPostgresBuildsPrimaryDSNEnv(t *testing.T) { t.Parallel() rt := newRuntimeForTest("127.0.0.1", "55432", "galaxy_integration", "userservice", "s3cr3t!") env := WithPostgres(rt, "USERSERVICE", "user", "userservice") require.Len(t, env, 1) dsn, ok := env["USERSERVICE_POSTGRES_PRIMARY_DSN"] require.True(t, ok, "missing USERSERVICE_POSTGRES_PRIMARY_DSN entry") parsed, err := url.Parse(dsn) require.NoError(t, err) require.Equal(t, "postgres", parsed.Scheme) require.Equal(t, "127.0.0.1:55432", parsed.Host) require.Equal(t, "/galaxy_integration", parsed.Path) require.Equal(t, "userservice", parsed.User.Username()) password, hasPassword := parsed.User.Password() require.True(t, hasPassword) require.Equal(t, "s3cr3t!", password) query := parsed.Query() require.Equal(t, "user", query.Get("search_path")) require.Equal(t, "disable", query.Get("sslmode")) } func TestDSNForSchemaPanicsWithoutCredentials(t *testing.T) { t.Parallel() rt := newRuntimeForTest("127.0.0.1", "55432", "galaxy_integration", "userservice", "secret") require.PanicsWithValue(t, `harness: DSNForSchema called for role "unknown" with no credentials; call EnsureRoleAndSchema first`, func() { _ = rt.DSNForSchema("user", "unknown") }, ) } // newRuntimeForTest builds a PostgresRuntime without spinning a container. // It exists only to exercise the pure DSN/env-builder paths. func newRuntimeForTest(host, port, database, role, password string) *PostgresRuntime { return &PostgresRuntime{ host: host, port: port, database: database, creds: map[string]string{role: password}, } }