ConusAI vendor v0.1.0-beta.114
ci / gate (push) Waiting to run

This commit is contained in:
ConusAI vendor v0.1.0-beta.114
2026-11-14 09:56:45 +00:00
commit 1ce8d23c35
867 changed files with 122104 additions and 0 deletions
+114
View File
@@ -0,0 +1,114 @@
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// The account module's data-subject rights on the mariadb and mongodb profiles: the same store
// (modules/account/adapters/store.ts) over the profile's real data source, with the kernel's and the
// modules' committed dialect migrations applied first. A person's export holds their rows and nobody
// else's, and their deletion erases them and leaves another person's account as it was.
//
// It needs the engines: ACCOUNT_TEST_MARIADB_URL (MariaDB LTS) and ACCOUNT_TEST_MONGODB_URL (MongoDB 8 as
// a single-node replica set, AT-D15), each naming a database of its own that the test may fill. Without
// them each block is skipped by name; `scripts/vendor/test-template.sh app --profile <name>` starts both.
import { type DialectProfile, expectedDialectMigrations } from "@app/config"
import { sessions } from "@conusai-kit/auth"
import { buildRegistry } from "@conusai-kit/core"
import { entitiesOf } from "@conusai-kit/data"
import { KIT_JOBS_ENTITIES } from "@conusai-kit/jobs-pgboss"
import { orgMemberships, users } from "@conusai-kit/tenancy"
import { afterAll, beforeAll, describe, expect, it } from "vitest"
import type { DialectData } from "../../apps/worker/src/dialect/engine.js"
import { engineFor } from "../../apps/worker/src/dialect/runtime.js"
import { makeApp, type Person } from "../../modules/account/__tests__/harness.js"
import type { AccountExport, DeletionView } from "../../modules/account/index.js"
import { accountDeletions } from "../../modules/account/index.js"
import { freshDialectModules } from "./fixture.js"
const ENGINES: readonly [DialectProfile, string, string | undefined][] = [
["mariadb", "MARIADB_URL", process.env.ACCOUNT_TEST_MARIADB_URL],
["mongodb", "MONGODB_URL", process.env.ACCOUNT_TEST_MONGODB_URL],
]
/** What the migrate job of the profile applies (apps/worker/src/dialect/), on the engine under test. */
async function migrate(
profile: DialectProfile,
env: Record<string, string>,
registry: ReturnType<typeof buildRegistry>,
): Promise<string[]> {
const outcome = await engineFor(profile).migrate(env, registry)
return [...outcome.applied, ...outcome.skipped]
}
async function orgOf(person: Person, slug: string): Promise<string> {
const created = (await person.call().callByName("tenancy.org.create", { slug, name: slug })) as { orgId: string }
return created.orgId
}
for (const [profile, variable, url] of ENGINES) {
const modules = freshDialectModules(profile)
const registry = buildRegistry(modules)
describe.skipIf(!url)(`account data-subject rights on ${profile} (${variable})`, () => {
let data: DialectData
const env = { [variable]: url ?? "" }
beforeAll(async () => {
// The migrate job's whole list applies on the engine, and it is the list readiness expects.
expect((await migrate(profile, env, registry)).sort()).toEqual(
(await expectedDialectMigrations(registry, profile)).sort(),
)
const entities = [...entitiesOf(registry, profile), ...KIT_JOBS_ENTITIES]
expect(entities.map(({ table }) => table)).toEqual(
expect.arrayContaining(["account_deletion", "account_session_revocation"]),
)
data = await engineFor(profile).connect(env, entities)
}, 120_000)
afterAll(async () => {
await data?.close()
})
it("exports the person's own rows and deletes the account, leaving another person's as it was", async () => {
const app = makeApp({ data, modules })
const tag = Math.random().toString(36).slice(2, 8)
const ada = await app.signUp(`ada-${tag}@example.test`)
const grace = await app.signUp(`grace-${tag}@example.test`)
const adaOrg = await orgOf(ada, `solo-${tag}`)
await orgOf(grace, `other-${tag}`)
const exported = (await ada.call().callByName("account.data.export", {})) as AccountExport
expect(exported).toMatchObject({ subject: ada.actor.userId, complete: true, failed: [] })
expect(exported.sections.account).toMatchObject({
status: "ok",
data: { profile: { userId: ada.actor.userId, email: ada.email }, organizations: [{ slug: `solo-${tag}` }] },
})
// Negative control: nothing of the other person is in it.
expect(JSON.stringify(exported)).not.toContain(grace.email)
expect(JSON.stringify(exported)).not.toContain(`other-${tag}`)
const view = (await ada.call().callByName("account.deletion.request", { confirm: ada.email })) as DeletionView
expect(view).toMatchObject({ state: "completed", pending: [], abandoned: [] })
expect(await app.data.global(users).get(ada.actor.userId)).toBeNull()
expect(
await app.data.forScope({ level: "org", orgId: adaOrg, actor: ada.actor }).of(orgMemberships).count(),
).toBe(0)
const adaSessions = (
await app.data.global(sessions).list({ where: [{ field: "userId", op: "eq", value: ada.actor.userId }] })
).items
expect(adaSessions).toEqual([])
const ledger = (
await app.data
.global(accountDeletions)
.list({ where: [{ field: "userId", op: "eq", value: ada.actor.userId }] })
).items
expect(ledger).toMatchObject([{ state: "completed" }])
expect(JSON.stringify(ledger)).not.toContain(ada.email)
// The other person keeps everything: their user row, their membership and their export.
expect((await app.data.global(users).get(grace.actor.userId))?.email).toBe(grace.email)
const graceExport = (await grace.call().callByName("account.data.export", {})) as AccountExport
expect(graceExport.sections.account).toMatchObject({
status: "ok",
data: { organizations: [{ slug: `other-${tag}` }] },
})
}, 60_000)
})
}