feat: use postgres
This commit is contained in:
@@ -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},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user