139 lines
4.1 KiB
Go
139 lines
4.1 KiB
Go
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},
|
|
}
|
|
}
|