Files
qipai/backend/tests/migration-contract.test.mjs
T
2026-06-18 10:20:39 +08:00

90 lines
3.2 KiB
JavaScript

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 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/);
console.log('PASS: M01-B/M01-C migration contracts are present.');