feat: use postgres

This commit is contained in:
Ilia Denisov
2026-04-26 20:34:39 +02:00
committed by GitHub
parent 48b0056b49
commit fe829285a6
365 changed files with 29223 additions and 24049 deletions
@@ -0,0 +1,138 @@
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},
}
}