import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const backendRoot = dirname(dirname(fileURLToPath(import.meta.url))); const repoRoot = dirname(backendRoot); const read = (path) => readFileSync(join(repoRoot, path), 'utf8'); const upSql = read('database/migrations/2026061601_m01b_core_schema.up.sql'); const downSql = read('database/migrations/2026061601_m01b_core_schema.down.sql'); const verifySql = read('database/migrations/2026061601_m01b_core_schema.verify.sql'); const seedSql = read('database/seeds/2026061601_m01b_minimal_seed.sql'); const legacyFixtureSql = read('database/fixtures/2026061801_m01b_legacy_schema.sql'); const asyncUpSql = read('database/migrations/2026061802_m01c_async_tasks.up.sql'); const asyncDownSql = read('database/migrations/2026061802_m01c_async_tasks.down.sql'); const asyncVerifySql = read('database/migrations/2026061802_m01c_async_tasks.verify.sql'); const tenantAppsUpSql = read('database/migrations/2026061803_m02a_tenant_apps.up.sql'); const tenantAppsDownSql = read('database/migrations/2026061803_m02a_tenant_apps.down.sql'); const tenantAppsVerifySql = read('database/migrations/2026061803_m02a_tenant_apps.verify.sql'); const coreTables = [ 'qipai_schema_migrations', 'qipai_tenants', 'qipai_stores', 'qipai_rooms', 'qipai_members', 'qipai_orders', 'qipai_payments', 'qipai_devices', 'qipai_audit_logs', 'qipai_legacy_table_mappings' ]; for (const table of coreTables) { assert.match(upSql, new RegExp(`CREATE TABLE IF NOT EXISTS ${table}\\b`)); assert.match(downSql, new RegExp(`DROP TABLE IF EXISTS ${table}\\b`)); assert.match(verifySql, new RegExp(`'${table}'`)); } for (const table of [ 'qipai_stores', 'qipai_rooms', 'qipai_members', 'qipai_orders', 'qipai_payments', 'qipai_devices', 'qipai_audit_logs' ]) { const block = upSql.slice(upSql.indexOf(`CREATE TABLE IF NOT EXISTS ${table}`)); assert.match(block, /\btenant_id BIGINT UNSIGNED NOT NULL\b/); } for (const amountColumn of [ 'base_price_cents', 'balance_cents', 'total_amount_cents', 'paid_amount_cents', 'amount_cents' ]) { assert.match(upSql, new RegExp(`\\b${amountColumn}\\b`)); } assert.doesNotMatch(upSql, /\bDECIMAL\b/i); assert.match(upSql, /DATETIME\(3\)/); assert.match(upSql, /legacy_store_id/); assert.match(upSql, /legacy_order_id/); assert.match(seedSql, /INSERT IGNORE INTO qipai_legacy_table_mappings/); assert.match(seedSql, /member_order_info/); for (const table of [ 'member_store_info', 'member_room_info', 'member_order_info', 'member_device_info' ]) { assert.match(legacyFixtureSql, new RegExp(`CREATE TABLE ${table}`)); } assert.match(legacyFixtureSql, /DECIMAL\(10,\s*2\)/); assert.match(legacyFixtureSql, /LEGACY-SANITIZED-001/); assert.doesNotMatch(legacyFixtureSql, /(?:https?:\/\/|@|password|secret|token)/i); for (const table of ['qipai_outbox_events', 'qipai_async_tasks']) { assert.match(asyncUpSql, new RegExp(`CREATE TABLE IF NOT EXISTS ${table}\\b`)); assert.match(asyncDownSql, new RegExp(`DROP TABLE IF EXISTS ${table}\\b`)); assert.match(asyncVerifySql, new RegExp(`'${table}'`)); } assert.match(asyncUpSql, /UNIQUE KEY uq_qipai_outbox_events_idempotency/); assert.match(asyncUpSql, /UNIQUE KEY uq_qipai_async_tasks_idempotency/); assert.match(asyncUpSql, /lease_expires_at DATETIME\(3\)/); assert.match(asyncUpSql, /COMPENSATION_REQUIRED|status VARCHAR/); for (const table of ['qipai_platform_apps', 'qipai_tenant_apps', 'qipai_tenant_configs']) { assert.match(tenantAppsUpSql, new RegExp(`CREATE TABLE IF NOT EXISTS ${table}\\b`)); assert.match(tenantAppsDownSql, new RegExp(`DROP TABLE IF EXISTS ${table}\\b`)); assert.match(tenantAppsVerifySql, new RegExp(`'${table}'`)); } assert.match(tenantAppsUpSql, /UNIQUE KEY uq_qipai_tenant_apps_tenant_app \(tenant_id, platform_app_id\)/); assert.match(tenantAppsUpSql, /UNIQUE KEY uq_qipai_tenant_configs_tenant_app \(tenant_id, platform_app_id\)/); assert.match(tenantAppsUpSql, /brand_name VARCHAR/); assert.match(tenantAppsUpSql, /theme_color VARCHAR/); console.log('PASS: M01-B through M02-A migration contracts are present.');