test(M01-B): 验证脱敏旧库迁移兼容

This commit is contained in:
Codex
2026-06-18 10:06:26 +08:00
parent 5117ae27d6
commit 1192dcb6b2
13 changed files with 5512 additions and 41 deletions
+12
View File
@@ -11,6 +11,7 @@ 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 coreTables = [
'qipai_schema_migrations',
@@ -60,5 +61,16 @@ 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);
console.log('PASS: M01-B migration contract is present.');
@@ -1,9 +1,14 @@
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { loadConfig } from '../dist/config.js';
import { closeMySqlPool, createMySqlPool } from '../dist/db/mysql.js';
import { LegacyReadRepository } from '../dist/db/legacy-read-repository.js';
import {
executeMigrationPlan,
loadMigrationPlan
loadMigrationPlan,
splitSqlStatements
} from '../dist/db/migration-runner.js';
const expectedTables = [
@@ -18,6 +23,7 @@ const expectedTables = [
'qipai_stores',
'qipai_tenants'
];
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
async function readCoreTables(pool) {
const placeholders = expectedTables.map(() => '?').join(', ');
@@ -42,6 +48,37 @@ async function readMigrationVersion(pool) {
return rows;
}
async function loadLegacyFixture(pool) {
const fixtureSql = await readFile(
resolve(repoRoot, 'database/fixtures/2026061801_m01b_legacy_schema.sql'),
'utf8'
);
const statements = splitSqlStatements(fixtureSql);
for (const statement of statements) {
await pool.query(statement);
}
return statements.length;
}
async function assertLegacyCompatibility(pool) {
const repository = new LegacyReadRepository(pool);
const stores = await repository.listStores({ tenantId: 1 });
const rooms = await repository.listRooms({ tenantId: 1, parentId: 101 });
const orders = await repository.listOrders({ tenantId: 1, parentId: 1001 });
const devices = await repository.listDevices({ tenantId: 1, parentId: 1001 });
assert.deepEqual(stores.map((record) => record.legacyId), [101]);
assert.deepEqual(rooms.map((record) => record.legacyId), [1001, 1002]);
assert.deepEqual(devices.map((record) => record.code), ['SANITIZED-DEVICE-001']);
assert.equal(orders.length, 1);
assert.equal(orders[0].code, 'LEGACY-SANITIZED-001');
assert.equal(orders[0].totalAmountCents, 2580);
assert.equal(orders[0].paidAmountCents, 2000);
assert.equal(orders[0].renewalAmountCents, 580);
assert.equal(orders[0].groupAmountCents, 0);
assert.equal(orders[0].refundAmountCents, null);
}
const config = loadConfig();
assert.equal(config.mysql.passwordConfigured, true, 'Live migration test requires a temporary password.');
assert.match(
@@ -58,6 +95,10 @@ const plans = {
const pool = createMySqlPool(config);
try {
const legacyFixtureStatements = await loadLegacyFixture(pool);
await assertLegacyCompatibility(pool);
console.log('PASS: sanitized legacy fixture is readable before migration.');
await executeMigrationPlan(pool, plans.up);
await executeMigrationPlan(pool, plans.verify);
assert.deepEqual(await readCoreTables(pool), expectedTables);
@@ -65,10 +106,12 @@ try {
version: '2026061601',
name: 'm01b_core_schema'
}]);
await assertLegacyCompatibility(pool);
console.log('PASS: first up and verify completed.');
await executeMigrationPlan(pool, plans.down);
assert.deepEqual(await readCoreTables(pool), []);
await assertLegacyCompatibility(pool);
console.log('PASS: down removed all M01-B core tables.');
await executeMigrationPlan(pool, plans.up);
@@ -78,6 +121,7 @@ try {
version: '2026061601',
name: 'm01b_core_schema'
}]);
await assertLegacyCompatibility(pool);
console.log('PASS: second up and verify restored the schema.');
console.log(JSON.stringify({
@@ -93,7 +137,16 @@ try {
up: plans.up.statements.length,
verify: plans.verify.statements.length,
down: plans.down.statements.length
}
},
legacyFixtureStatements,
legacyCompatibilityChecks: [
'stores',
'rooms',
'orders',
'devices',
'tenant isolation',
'decimal cents'
]
}, null, 2));
} finally {
await closeMySqlPool(pool);