@@ -0,0 +1,162 @@
|
||||
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
// The deployment contract per process (decision AT-D20): which Postgres role each process may
|
||||
// hold, and what happens when it is handed one it may not.
|
||||
|
||||
import { describe, expect, it } from "vitest"
|
||||
import { loadAppEnv, roleOf } from "../config/src/env.js"
|
||||
import { assertBoundByRowLevelSecurity, assertOwnsSchema } from "../config/src/postgres.js"
|
||||
|
||||
const base = {
|
||||
CONUSAI_API_URL: "https://api.example.test/api",
|
||||
CONUSAI_API_TOKEN: "token",
|
||||
CONUSAI_PROJECT_ID: "42",
|
||||
CONUSAI_ORG_ID: "9",
|
||||
CONUSAI_ORG_SLUG: "acme",
|
||||
POSTGRES_URL: "postgresql://app_app:secret@db:5432/app",
|
||||
POSTGRES_READER_URL: "postgresql://app_reader:secret@db:5432/app",
|
||||
}
|
||||
|
||||
describe("the deployment contract", () => {
|
||||
it("gives web and worker the runtime role and the global reader", () => {
|
||||
for (const service of ["web", "worker"] as const) {
|
||||
const env = loadAppEnv(service, { ...base })
|
||||
expect(env.postgres?.runtime).toContain("app_app")
|
||||
expect(env.postgres?.reader).toContain("app_reader")
|
||||
expect(env.postgres?.owner).toBeUndefined()
|
||||
}
|
||||
})
|
||||
|
||||
it("refuses to start web or worker with the owner's URL", () => {
|
||||
for (const service of ["web", "worker"] as const) {
|
||||
expect(() =>
|
||||
loadAppEnv(service, { ...base, POSTGRES_MIGRATE_URL: "postgresql://app_owner:s@db:5432/app" }),
|
||||
).toThrow(/POSTGRES_MIGRATE_URL is set for the \w+ process/)
|
||||
}
|
||||
})
|
||||
|
||||
it("requires the platform organization slug for production web and worker usage billing, but not migrate", () => {
|
||||
const { CONUSAI_ORG_SLUG: _slug, ...withoutSlug } = base
|
||||
for (const service of ["web", "worker"] as const) {
|
||||
expect(() => loadAppEnv(service, { ...withoutSlug })).toThrow(/CONUSAI_ORG_SLUG/)
|
||||
}
|
||||
expect(() =>
|
||||
loadAppEnv("migrate", {
|
||||
...withoutSlug,
|
||||
POSTGRES_MIGRATE_URL: "postgresql://app_owner:secret@db:5432/app",
|
||||
}),
|
||||
).not.toThrow()
|
||||
})
|
||||
|
||||
it("refuses the migrate job without the owner's URL, naming postgres.migrate", () => {
|
||||
expect(() => loadAppEnv("migrate", { ...base })).toThrow(/declares postgres\.migrate/)
|
||||
})
|
||||
|
||||
it("refuses the migrate job when two of the three URLs name one role", () => {
|
||||
expect(() => loadAppEnv("migrate", { ...base, POSTGRES_MIGRATE_URL: base.POSTGRES_URL })).toThrow(
|
||||
/three separate logins/,
|
||||
)
|
||||
})
|
||||
|
||||
it("names the Postgres service to link when a URL is missing", () => {
|
||||
const { POSTGRES_URL: _dropped, ...without } = base
|
||||
expect(() => loadAppEnv("web", without)).toThrow(/link a Postgres service/)
|
||||
})
|
||||
|
||||
it("refuses a runtime URL and a reader URL that name one login", () => {
|
||||
expect(() => loadAppEnv("web", { ...base, POSTGRES_READER_URL: base.POSTGRES_URL })).toThrow(/name the same role/)
|
||||
})
|
||||
|
||||
it("refuses a role name that is not a plain identifier", () => {
|
||||
expect(roleOf("postgresql://app_reader:s@db:5432/app")).toBe("app_reader")
|
||||
expect(() => roleOf("postgresql://app%2Dreader:s@db:5432/app")).toThrow(/not a plain identifier/)
|
||||
})
|
||||
|
||||
it("loads Convex server credentials and refuses missing or misplaced credentials", () => {
|
||||
const convex = {
|
||||
...base,
|
||||
CONVEX_SELF_HOSTED_URL: "http://convex:3210",
|
||||
CONVEX_SELF_HOSTED_ADMIN_KEY: "server-only-test-key",
|
||||
}
|
||||
const loaded = loadAppEnv("web", convex, "convex")
|
||||
expect(loaded.convex).toEqual({ url: "http://convex:3210", adminKey: "server-only-test-key" })
|
||||
expect(loaded).not.toHaveProperty("postgres")
|
||||
const { CONVEX_SELF_HOSTED_ADMIN_KEY: _key, ...missingKey } = convex
|
||||
expect(() => loadAppEnv("web", missingKey, "convex")).toThrow(/CONVEX_SELF_HOSTED_ADMIN_KEY/)
|
||||
expect(() => loadAppEnv("migrate", convex, "convex")).toThrow(/not supported by the Convex app runtime/)
|
||||
})
|
||||
|
||||
it("gives every process of the mariadb profile the one MARIADB_URL, and web and worker the queue's REDIS_URL", () => {
|
||||
const { POSTGRES_URL: _p, POSTGRES_READER_URL: _r, ...platform } = base
|
||||
const mariadb = {
|
||||
...platform,
|
||||
MARIADB_URL: "mysql://app:secret@mariadb:3306/app",
|
||||
DATABASE_URL: "mysql://app:secret@mariadb:3306/app",
|
||||
REDIS_URL: "redis://valkey:6379",
|
||||
}
|
||||
for (const service of ["web", "worker"] as const) {
|
||||
const loaded = loadAppEnv(service, mariadb, "mariadb")
|
||||
expect(loaded.mariadb).toEqual({ url: mariadb.MARIADB_URL })
|
||||
expect(loaded.redis).toEqual({ url: "redis://valkey:6379" })
|
||||
expect(loaded).not.toHaveProperty("postgres")
|
||||
const { REDIS_URL: _q, ...noQueue } = mariadb
|
||||
expect(() => loadAppEnv(service, noQueue, "mariadb")).toThrow(/REDIS_URL is not set/)
|
||||
}
|
||||
const migrate = loadAppEnv("migrate", { ...mariadb, REDIS_URL: "" }, "mariadb")
|
||||
expect(migrate.mariadb?.url).toBe(mariadb.MARIADB_URL)
|
||||
expect(migrate).not.toHaveProperty("redis")
|
||||
const { MARIADB_URL: _m, ...noStore } = mariadb
|
||||
expect(() => loadAppEnv("web", noStore, "mariadb")).toThrow(/MARIADB_URL is not set: link a MariaDB service/)
|
||||
})
|
||||
|
||||
it("gives every process of the mongodb profile the one MONGODB_URL and refuses one without it", () => {
|
||||
const { POSTGRES_URL: _p, POSTGRES_READER_URL: _r, ...platform } = base
|
||||
const mongodb = {
|
||||
...platform,
|
||||
MONGODB_URL: "mongodb://app:secret@mongodb:27017/app?directConnection=true",
|
||||
REDIS_URL: "redis://valkey:6379",
|
||||
}
|
||||
const web = loadAppEnv("web", mongodb, "mongodb")
|
||||
expect(web.mongodb).toEqual({ url: mongodb.MONGODB_URL })
|
||||
expect(web).not.toHaveProperty("mariadb")
|
||||
expect(loadAppEnv("migrate", mongodb, "mongodb")).not.toHaveProperty("redis")
|
||||
const { MONGODB_URL: _m, ...noStore } = mongodb
|
||||
expect(() => loadAppEnv("worker", noStore, "mongodb")).toThrow(/MONGODB_URL/)
|
||||
})
|
||||
})
|
||||
|
||||
describe("the role the database answers with", () => {
|
||||
const facts = {
|
||||
role: "app_app",
|
||||
ownedTables: 0,
|
||||
bypassesRowLevelSecurity: false,
|
||||
isSuperuser: false,
|
||||
ownsSchema: false,
|
||||
}
|
||||
|
||||
it("accepts a role that owns nothing and bypasses nothing", () => {
|
||||
expect(assertBoundByRowLevelSecurity(facts, "POSTGRES_URL")).toBe(facts)
|
||||
})
|
||||
|
||||
it("refuses a connection that owns a table, bypasses row-level security or is a superuser", () => {
|
||||
expect(() => assertBoundByRowLevelSecurity({ ...facts, ownedTables: 3 }, "POSTGRES_URL")).toThrow(/owns 3 table/)
|
||||
expect(() => assertBoundByRowLevelSecurity({ ...facts, bypassesRowLevelSecurity: true }, "POSTGRES_URL")).toThrow(
|
||||
/BYPASSRLS/,
|
||||
)
|
||||
expect(() => assertBoundByRowLevelSecurity({ ...facts, isSuperuser: true }, "POSTGRES_READER_URL")).toThrow(
|
||||
/superuser/,
|
||||
)
|
||||
})
|
||||
|
||||
it("refuses a migrate job that does not own schema public", () => {
|
||||
expect(() => assertOwnsSchema(facts, "POSTGRES_MIGRATE_URL")).toThrow(/does not own schema public/)
|
||||
expect(assertOwnsSchema({ ...facts, role: "app_owner", ownsSchema: true }, "POSTGRES_MIGRATE_URL").ownsSchema).toBe(
|
||||
true,
|
||||
)
|
||||
})
|
||||
|
||||
it("names the variable whose connection is wrong", () => {
|
||||
expect(() => assertBoundByRowLevelSecurity({ ...facts, ownedTables: 1 }, "POSTGRES_READER_URL")).toThrow(
|
||||
/POSTGRES_READER_URL/,
|
||||
)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user