// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0 // Disposable real MongoDB evidence; ACCOUNT_TEST_MONGODB_URL must name a replica set. // Each run owns a fresh database and removes only that database afterwards. import { randomUUID } from "node:crypto" import { readFileSync } from "node:fs" import { dialectModuleMigrationFiles } from "@app/config" import { kernelModules } from "@app/modules" import { buildRegistry, newId } from "@conusai-kit/core" import { connectMongoDataSource, type MongoCommand, mongoCommandsMigration, runMongoMigrations, type StoredDoc, } from "@conusai-kit/data-mongo" import { makeTenant } from "@conusai-kit/testing" import { MongoClient } from "mongodb" import { describe, expect, it } from "vitest" import { DOCUMENTS_ENTITIES, documents, documentsModule, FAILURE_CODES } from "../index.js" const url = process.env.ACCOUNT_TEST_MONGODB_URL const migration = (name: string) => mongoCommandsMigration( `documents/${name}`, JSON.parse( readFileSync(new URL(`../adapters/mongo/migrations/${name}.json`, import.meta.url), "utf8"), ) as MongoCommand[], ) describe.skipIf(!url)("Documents forward MongoDB validator evolution", () => { it("preserves existing rows and indexes, persists exhaustion, and retains enum and tenant refusals", async () => { const client = await new MongoClient(url ?? "").connect() const database = `documents_forward_${randomUUID().replaceAll("-", "")}` const db = client.db(database) const data = await connectMongoDataSource(client, { database, entities: DOCUMENTS_ENTITIES }) try { const discovered = await dialectModuleMigrationFiles( buildRegistry([...kernelModules, documentsModule]), "mongodb", ) expect(discovered.filter(({ name }) => name.startsWith("documents/")).map(({ name }) => name)).toEqual([ "documents/0001_documents", "documents/0002_skipped_pages", "documents/0003_nullable_failure_code", "documents/0004_processing_exhausted", ]) const history = ["0001_documents", "0002_skipped_pages", "0003_nullable_failure_code"].map(migration) await runMongoMigrations(db, history) const tenant = makeTenant() const repo = data.forScope(tenant.project).of(documents) const input = { fileId: newId(), filename: "synthetic.pdf", status: "processing" as const, ocrJobId: null, pageCount: null, proposedCount: null, failureCode: null, skippedPages: null, finishedAt: null, } const row = await repo.insert(input) const rows = db.collection("documents_document") const before = await rows.findOne({ _id: row.id }) const indexes = await rows.indexes() const [collectionBefore] = await db.listCollections({ name: "documents_document" }).toArray() const terminal = { status: "failed" as const, failureCode: "processing_exhausted" as const } // The current typed model accepts this transition, but the applied historical validator refuses it. await expect(repo.update(row.id, terminal, row.version)).rejects.toThrow(/[Dd]ocument failed validation/) expect(await rows.findOne({ _id: row.id })).toEqual(before) const forward = migration("0004_processing_exhausted") expect(await runMongoMigrations(db, [...history, forward])).toEqual({ applied: [forward.name], skipped: history.map(({ name }) => name), }) expect(await rows.findOne({ _id: row.id })).toEqual(before) expect(await rows.indexes()).toEqual(indexes) const [collectionAfter] = await db.listCollections({ name: "documents_document" }).toArray() if (!collectionBefore || !("info" in collectionBefore) || !collectionAfter || !("info" in collectionAfter)) throw new Error("missing collection metadata") expect(collectionBefore.info?.uuid).toBeDefined() expect(collectionAfter.info?.uuid).toEqual(collectionBefore.info?.uuid) const failed = await repo.update(row.id, terminal, row.version) expect(await repo.get(row.id)).toEqual(failed) expect(failed).toMatchObject({ ...terminal, filename: input.filename, fileId: input.fileId }) expect(await rows.countDocuments()).toBe(1) // All current codes and null remain usable; no blanket weakening of the enum. for (const failureCode of [...FAILURE_CODES, null]) { await repo.insert({ ...input, fileId: newId(), status: "failed", failureCode }) } await expect(rows.updateOne({ _id: row.id }, { $set: { failure_code: "unknown_failure" } })).rejects.toThrow( /[Dd]ocument failed validation/, ) // Zod still rejects an unknown code before an adapter write too. await expect(repo.update(row.id, { failureCode: "unknown_failure" as never }, failed.version)).rejects.toThrow() for (const scope of [ makeTenant().project, { ...tenant.project, workspaceId: tenant.siblingWorkspace.workspaceId }, tenant.siblingProject, ]) { const other = data.forScope(scope).of(documents) expect(await other.get(row.id)).toBeNull() await expect(other.update(row.id, { status: "processing" }, failed.version)).rejects.toThrow() } expect(await repo.get(row.id)).toEqual(failed) const stored = await rows.find().sort({ _id: 1 }).toArray() expect(await runMongoMigrations(db, [...history, forward])).toEqual({ applied: [], skipped: [...history, forward].map(({ name }) => name), }) expect(await rows.find().sort({ _id: 1 }).toArray()).toEqual(stored) } finally { await db.dropDatabase() await data.close() } }) })