const { spawnSync } = require("node:child_process"); const { createHash, randomBytes } = require("node:crypto"); const path = require("node:path"); const testUrl = process.env.DEPLOY_INIT_TEST_DATABASE_URL; const configurationModels = [ "expenseType", "bankInstitution", "approvalRule", "documentSequence", "documentCategory", "workflowDefinition", "leaveType", "leavePolicy", "featureFlag", "holidayCalendar", "holidayCalendarDay", "attendancePolicy", "budgetControlRule", "department", "employee", "employeeRole", "workspaceSetting", ]; describe.skipIf(!testUrl)("real init against an explicitly supplied empty synthetic database", () => { let prisma; let pool; let environment; const originalUrl = process.env.DATABASE_URL; beforeAll(async () => { const parsed = new URL(testUrl); if (parsed.hostname !== "127.0.0.1" || !/^\/hyb_seed_test_[a-z0-9]+$/.test(parsed.pathname)) { throw new Error("synthetic_loopback_database_required"); } process.env.DATABASE_URL = testUrl; ({ prisma, pool } = require("../../../src/prismaClient").createPrismaClient()); const tables = await prisma.$queryRaw`SELECT count(*)::int AS total FROM information_schema.tables WHERE table_schema = 'public'`; expect(tables[0].total).toBe(0); environment = { PATH: process.env.PATH, NODE_ENV: "test", DATABASE_URL: testUrl, JWT_SECRET: randomBytes(32).toString("hex"), TRIALRUN_ADMIN_EMAIL: "seed-test@example.invalid", TRIALRUN_ADMIN_PHONE: "19900000000", TRIALRUN_ADMIN_PASSWORD: randomBytes(24).toString("hex") + "Aa1!", TRIALRUN_ADMIN_NAME: "Synthetic Admin", TRIALRUN_ADMIN_EMPLOYEE_NO: "SEEDTEST", TRIALRUN_EXTRA_FORCE_ON_FLAGS: "", }; }); afterAll(async () => { try { if (prisma) await prisma.$disconnect(); } finally { if (pool) await pool.end(); if (originalUrl === undefined) delete process.env.DATABASE_URL; else process.env.DATABASE_URL = originalUrl; } }); function run(mode) { return spawnSync(process.execPath, ["src/scripts/deploy-init.js"], { cwd: path.resolve(__dirname, "../../.."), env: { ...environment, DEPLOY_DATA_MODE: mode }, encoding: "utf8", timeout: 120000, maxBuffer: 4 * 1024 * 1024, }); } async function snapshot() { const hashes = {}; for (const model of configurationModels) { const rows = await prisma[model].findMany(); const serialized = rows.map((row) => JSON.stringify(row)).sort(); hashes[model] = createHash("sha256").update(JSON.stringify(serialized)).digest("hex"); } return hashes; } it("initializes an empty database and preserves customized configuration through repeated existing runs and missing-config failures", async () => { const initialized = run("initialize"); expect(initialized.status).toBe(0); expect(initialized.stdout).toContain('"status":"INIT_PASS"'); const flag = await prisma.featureFlag.findFirstOrThrow(); await prisma.featureFlag.update({ where: { id: flag.id }, data: { override_mode: "force_off", rollout_type: "department_allowlist", department_ids: [], version: 77, note: "synthetic customization" }, }); const workflow = await prisma.workflowDefinition.findFirstOrThrow({ where: { entity_type: "reimbursement" } }); await prisma.workflowDefinition.update({ where: { id: workflow.id }, data: { version: 42, name: "Synthetic custom workflow" } }); const holiday = await prisma.holidayCalendarDay.findFirstOrThrow(); await prisma.holidayCalendarDay.update({ where: { id: holiday.id }, data: { name: "Synthetic custom holiday" } }); const expected = await snapshot(); const migrations = await prisma.$queryRaw`SELECT migration_name FROM "_prisma_migrations" WHERE finished_at IS NOT NULL ORDER BY migration_name`; expect(migrations.length).toBeGreaterThan(0); for (const attempt of [1, 2]) { const existing = run("existing"); expect(existing.status, "existing attempt " + attempt).toBe(0); expect(existing.stdout).toContain('"status":"INIT_PASS"'); expect(existing.stdout).not.toContain('"stage":"seed:'); expect(await snapshot()).toEqual(expected); } for (const [model, field, where] of [ ["expenseType", "enabled", {}], ["approvalRule", "enabled", {}], ["workflowDefinition", "is_active", { entity_type: "reimbursement" }], ]) { const enabled = await prisma[model].findMany({ where: { ...where, [field]: true }, select: { id: true } }); await prisma[model].updateMany({ where, data: { [field]: false } }); const beforeFailure = await snapshot(); const failed = run("existing"); expect(failed.status).toBe(1); expect(failed.stdout).toContain("REQUIRED_CONFIGURATION_MISSING"); expect(failed.stdout).not.toContain('"status":"INIT_PASS"'); expect(await snapshot()).toEqual(beforeFailure); await prisma[model].updateMany({ where: { id: { in: enabled.map((row) => row.id) } }, data: { [field]: true } }); } expect(await prisma.$queryRaw`SELECT migration_name FROM "_prisma_migrations" WHERE finished_at IS NOT NULL ORDER BY migration_name`).toEqual(migrations); }, 240000); });