@@ -0,0 +1,103 @@
|
||||
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
// Disposable local PostgreSQL for the app's actual claim tests. No SDK-private test code.
|
||||
import { execFileSync } from "node:child_process"
|
||||
import type { AnyEntityDef } from "@conusai-kit/data"
|
||||
import { readerMigration, runMigrationsFromEnv, schemaMigration } from "@conusai-kit/data-kysely"
|
||||
import { Pool } from "pg"
|
||||
|
||||
export interface Postgres {
|
||||
ownerUrl: string
|
||||
runtimeUrl: string
|
||||
readerUrl: string
|
||||
stop(): Promise<void>
|
||||
}
|
||||
const docker = (...args: string[]) =>
|
||||
execFileSync("docker", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim()
|
||||
|
||||
export async function startPostgres(): Promise<Postgres> {
|
||||
const name = `agent-claim-${crypto.randomUUID()}`
|
||||
const container = docker(
|
||||
"run",
|
||||
"--detach",
|
||||
"--rm",
|
||||
"--name",
|
||||
name,
|
||||
"--label",
|
||||
"conusai.test=app-agent-claim",
|
||||
"--publish",
|
||||
"127.0.0.1::5432",
|
||||
"--env",
|
||||
"POSTGRES_USER=owner",
|
||||
"--env",
|
||||
"POSTGRES_PASSWORD=owner",
|
||||
"--env",
|
||||
"POSTGRES_DB=agent",
|
||||
"pgvector/pgvector:pg18",
|
||||
)
|
||||
const stop = async () => {
|
||||
docker("rm", "--force", "--volumes", container)
|
||||
}
|
||||
let owner: Pool | undefined
|
||||
try {
|
||||
// Docker Desktop can return before the random published port is visible. An empty
|
||||
// port makes pg silently use 5432, so wait for the actual mapping before connecting.
|
||||
const mappingDeadline = Date.now() + 10_000
|
||||
let port = ""
|
||||
while (!/^[1-9][0-9]*$/.test(port)) {
|
||||
port = docker("port", container, "5432/tcp").split(":").at(-1) ?? ""
|
||||
if (/^[1-9][0-9]*$/.test(port)) break
|
||||
if (Date.now() >= mappingDeadline) throw new Error("Postgres test port was not published")
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
}
|
||||
const ownerUrl = `postgresql://owner:owner@127.0.0.1:${port}/agent`
|
||||
owner = new Pool({ connectionString: ownerUrl, max: 1, connectionTimeoutMillis: 1000 })
|
||||
const deadline = Date.now() + 60_000
|
||||
while (true) {
|
||||
try {
|
||||
await owner.query("select 1")
|
||||
break
|
||||
} catch (error) {
|
||||
if (Date.now() >= deadline) throw error
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
}
|
||||
}
|
||||
await owner.query("create role app_runtime login password 'runtime' nosuperuser nobypassrls")
|
||||
await owner.query("create role app_reader login password 'reader' nosuperuser nobypassrls")
|
||||
return {
|
||||
ownerUrl,
|
||||
runtimeUrl: ownerUrl.replace("owner:owner@", "app_runtime:runtime@"),
|
||||
readerUrl: ownerUrl.replace("owner:owner@", "app_reader:reader@"),
|
||||
stop,
|
||||
}
|
||||
} catch (error) {
|
||||
await stop()
|
||||
throw error
|
||||
} finally {
|
||||
await owner?.end()
|
||||
}
|
||||
}
|
||||
|
||||
export async function migrate(postgres: Postgres, entities: readonly AnyEntityDef[]) {
|
||||
await runMigrationsFromEnv({ POSTGRES_MIGRATE_URL: postgres.ownerUrl }, [
|
||||
schemaMigration(entities),
|
||||
readerMigration("app_reader", []),
|
||||
])
|
||||
const owner = new Pool({ connectionString: postgres.ownerUrl, max: 1 })
|
||||
try {
|
||||
await owner.query("grant usage on schema public to app_runtime, app_reader")
|
||||
await owner.query("grant select, insert, update, delete on all tables in schema public to app_runtime")
|
||||
} finally {
|
||||
await owner.end()
|
||||
}
|
||||
}
|
||||
|
||||
export async function grantPgBoss(postgres: Postgres) {
|
||||
const owner = new Pool({ connectionString: postgres.ownerUrl, max: 1 })
|
||||
try {
|
||||
await owner.query("grant usage on schema pgboss to app_runtime")
|
||||
await owner.query("grant select, insert, update, delete on all tables in schema pgboss to app_runtime")
|
||||
await owner.query("grant execute on all functions in schema pgboss to app_runtime")
|
||||
} finally {
|
||||
await owner.end()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user