Files
template-app/test/dialect/stores.test.ts
T
2026-11-14 09:56:45 +00:00

131 lines
5.6 KiB
TypeScript

// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// The web shell's stores of the mariadb and mongodb profiles (apps/web/src/server/dialect/) on the
// real engines: the readiness probes answer, the migration level the migrate job wrote is what
// readiness expects, the MongoDB store refuses a standalone server at boot (AT-D15), and the BullMQ
// queue enqueues on Valkey under the app's own prefix.
//
// It needs ACCOUNT_TEST_MARIADB_URL, ACCOUNT_TEST_MONGODB_URL (a replica set) and ACCOUNT_TEST_REDIS_URL,
// the databases test/dialect/account.test.ts migrated; without them each block is skipped by name.
import { randomUUID } from "node:crypto"
import { type AppEnv, expectedDialectMigrations, liveCaptureMigrations, loadAppEnv } from "@app/config"
import { notes } from "@app/modules/notes"
import { buildRegistry } from "@conusai-kit/core"
import { entitiesOf } from "@conusai-kit/data"
import { KIT_JOBS_ENTITIES } from "@conusai-kit/jobs-pgboss"
import { Redis } from "ioredis"
import { describe, expect, it } from "vitest"
import { dialectStores, queueSlug } from "../../apps/web/src/server/dialect/stores"
import { engineFor } from "../../apps/worker/src/dialect/runtime.js"
import { freshDialectModules } from "./fixture.js"
const redisUrl = process.env.ACCOUNT_TEST_REDIS_URL
const platform = {
NODE_ENV: "test",
CONUSAI_API_URL: "https://api.example.test/api",
CONUSAI_API_TOKEN: "token",
CONUSAI_PROJECT_ID: "w06-stores",
}
const ENGINES = [
[
"mariadb",
{ MARIADB_URL: process.env.ACCOUNT_TEST_MARIADB_URL, DATABASE_URL: process.env.ACCOUNT_TEST_MARIADB_URL },
],
["mongodb", { MONGODB_URL: process.env.ACCOUNT_TEST_MONGODB_URL }],
] as const
for (const [profile, store] of ENGINES) {
const registry = buildRegistry(freshDialectModules(profile))
const url = Object.values(store)[0]
describe.skipIf(!url || !redisUrl)(`the web shell's ${profile} store`, () => {
it("answers readiness at the migrate job's level and enqueues on its own BullMQ prefix", async () => {
const raw = { ...platform, ...store, REDIS_URL: redisUrl }
await engineFor(profile).migrate(raw, registry)
const env: AppEnv = loadAppEnv("web", raw, profile)
const stores = dialectStores(
env,
[...entitiesOf(registry, profile), ...KIT_JOBS_ENTITIES],
liveCaptureMigrations(registry, profile),
)
if (!stores) throw new Error("no dialect store")
try {
await stores.probes.boot()
await stores.probes.store()
const applied = await stores.probes.applied()
for (const name of await expectedDialectMigrations(registry, profile))
expect(applied.has(name), name).toBe(true)
// A request's enqueue reaches Valkey under this app's prefix.
const id = await stores
.queue()
.enqueue(
{ level: "org", orgId: "00000000-0000-4000-8000-000000000001", actor: { userId: "u" } },
"files.recover",
{ probe: true },
)
expect(id).toBeTruthy()
const redis = new Redis(redisUrl as string)
try {
const keys = await redis.keys(`kit-jobs-${queueSlug(env)}*`)
expect(keys.length).toBeGreaterThan(0)
} finally {
await redis.quit()
}
// The live log: the migrate job prepared notes_note (MariaDB row triggers, MongoDB pre-images),
// so a note written through the repository is in the log, ids only, in its own organization.
{
const log = stores.liveLog?.()
if (!log) throw new Error(`the ${profile} store has no live log`)
await log.start()
const scope = {
level: "project",
orgId: randomUUID(),
workspaceId: randomUUID(),
projectId: randomUUID(),
actor: { userId: randomUUID() },
} as const
const since = profile === "mongodb" ? (await log.head(scope.orgId)).position : { txid: 0n, seq: 0n }
const note = await stores.data
.forScope(scope)
.of(notes)
.insert({ title: "live", body: "b", createdBy: scope.actor.userId })
let rows: Awaited<ReturnType<typeof log.read>>["final"] = []
for (let attempt = 0; attempt < 50 && rows.length === 0; attempt++) {
const read = await log.read(scope.orgId, since, 10)
rows = [...read.final, ...read.early]
if (rows.length === 0) await new Promise((resolve) => setTimeout(resolve, 100))
}
expect(rows.map((row) => [row.entity, row.id, row.op, row.scope.ws])).toEqual([
["notes.note", note.id, "u", scope.workspaceId],
])
expect((await log.read(randomUUID(), since, 10)).final).toEqual([])
await log.close()
}
} finally {
await stores.close()
}
}, 60_000)
})
}
describe.skipIf(!process.env.ACCOUNT_TEST_MONGODB_STANDALONE_URL)(
"the web shell's mongodb store on a standalone server",
() => {
it("refuses to boot with the typed replica_set error (AT-D15)", async () => {
const env = loadAppEnv(
"web",
{ ...platform, MONGODB_URL: process.env.ACCOUNT_TEST_MONGODB_STANDALONE_URL, REDIS_URL: "redis://127.0.0.1:1" },
"mongodb",
)
const registry = buildRegistry(freshDialectModules("mongodb"))
const stores = dialectStores(env, entitiesOf(registry, "mongodb"))
if (!stores) throw new Error("no dialect store")
try {
await expect(stores.probes.boot()).rejects.toMatchObject({ name: "MongoTopologyError", setting: "replica_set" })
} finally {
await stores.close()
}
}, 60_000)
},
)