import { existsSync, readFileSync } from 'node:fs'; import { join } from 'node:path'; const migrationPath = join( process.cwd(), 'prisma', 'migrations', '20260703000000_v22_partitioned_domain_schema', 'migration.sql', ); function readMigrationSql() { if (!existsSync(migrationPath)) { throw new Error(`Missing V2.2 migration: ${migrationPath}`); } return readFileSync(migrationPath, 'utf8'); } function expectPartitionedTable( sql: string, table: string, method: 'HASH' | 'RANGE', key: string, primaryKey: string[], ) { const quotedPrimaryKey = primaryKey.map((column) => `"${column}"`).join(', '); expect(sql).toMatch( new RegExp( `CREATE TABLE "${table}"[\\s\\S]*CONSTRAINT "${table}_pkey" PRIMARY KEY \\(${quotedPrimaryKey}\\)[\\s\\S]*PARTITION BY ${method} \\("${key}"\\);`, ), ); } function expectHashPartitions(sql: string, table: string, count: number) { const matches = sql.match( new RegExp(`PARTITION OF "${table}" FOR VALUES WITH \\(MODULUS ${count}, REMAINDER`, 'g'), ); expect(matches).toHaveLength(count); } describe('V2.2 partitioned domain schema migration', () => { it('keeps every hash partitioned business table primary key anchored by the partition key', () => { const sql = readMigrationSql(); expectPartitionedTable(sql, 'requirements', 'HASH', 'product_id', ['id', 'product_id']); expectPartitionedTable(sql, 'dev_tasks', 'HASH', 'version_id', ['id', 'version_id']); expectPartitionedTable(sql, 'test_cases', 'HASH', 'version_id', ['id', 'version_id']); expectPartitionedTable(sql, 'bugs', 'HASH', 'version_id', ['id', 'version_id']); expectHashPartitions(sql, 'requirements', 16); expectHashPartitions(sql, 'dev_tasks', 16); expectHashPartitions(sql, 'test_cases', 16); expectHashPartitions(sql, 'bugs', 16); }); it('includes partition keys in business unique constraints', () => { const sql = readMigrationSql(); expect(sql).toMatch(/CONSTRAINT "requirements_product_id_code_key" UNIQUE \("product_id", "code"\)/); expect(sql).toMatch(/CONSTRAINT "dev_tasks_version_id_code_key" UNIQUE \("version_id", "code"\)/); expect(sql).toMatch(/CONSTRAINT "test_cases_version_id_code_key" UNIQUE \("version_id", "code"\)/); expect(sql).toMatch(/CONSTRAINT "bugs_version_id_code_key" UNIQUE \("version_id", "code"\)/); }); it('uses composite foreign keys when a referenced table is partitioned', () => { const sql = readMigrationSql(); expect(sql).toMatch( /FOREIGN KEY \("requirement_id", "requirement_product_id"\) REFERENCES "requirements"\("id", "product_id"\)/, ); expect(sql).toMatch( /FOREIGN KEY \("test_case_id", "test_case_version_id"\) REFERENCES "test_cases"\("id", "version_id"\)/, ); }); it('range partitions append-only evidence and snapshot tables by created_at', () => { const sql = readMigrationSql(); expectPartitionedTable(sql, 'work_activities', 'RANGE', 'created_at', ['id', 'created_at']); expectPartitionedTable(sql, 'task_worklogs', 'RANGE', 'created_at', ['id', 'created_at']); expectPartitionedTable(sql, 'overtime_records', 'RANGE', 'created_at', ['id', 'created_at']); expectPartitionedTable(sql, 'xiaobao_risk_snapshots', 'RANGE', 'created_at', ['id', 'created_at']); expectPartitionedTable(sql, 'ai_logs', 'RANGE', 'created_at', ['id', 'created_at']); for (const table of [ 'work_activities', 'task_worklogs', 'overtime_records', 'xiaobao_risk_snapshots', 'ai_logs', ]) { expect(sql).toMatch(new RegExp(`CREATE TABLE "${table}_default" PARTITION OF "${table}" DEFAULT;`)); } }); it('keeps Xiaobao current summaries as one row per version instead of partitioned history', () => { const sql = readMigrationSql(); expect(sql).toMatch( /CREATE TABLE "xiaobao_risk_summaries"[\s\S]*CONSTRAINT "xiaobao_risk_summaries_pkey" PRIMARY KEY \("version_id"\)/, ); expect(sql).not.toMatch(/CREATE TABLE "xiaobao_risk_summaries"[\s\S]*PARTITION BY/); }); });