79 lines
3.2 KiB
JavaScript
79 lines
3.2 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { existsSync, readFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
|
|
const root = process.cwd();
|
|
const read = (path) => readFileSync(join(root, path), 'utf8');
|
|
|
|
for (const suffix of ['up', 'down', 'verify']) {
|
|
assert.ok(
|
|
existsSync(join(root, 'database/migrations/2026081006_m09c_cleaning_settlement_integrity.' + suffix + '.sql')),
|
|
'M09-C ' + suffix + ' migration is missing'
|
|
);
|
|
}
|
|
|
|
const migration = read('database/migrations/2026081006_m09c_cleaning_settlement_integrity.up.sql');
|
|
for (const pattern of [
|
|
'qipai_cleaning_settlement_reversals',
|
|
'qipai_cleaning_settlement_events',
|
|
'amount_delta_cents BIGINT',
|
|
'active_share_key',
|
|
'uq_qipai_cleaning_settlement_active_share',
|
|
'payout_request_no',
|
|
'uq_qipai_cleaning_settlement_payout_request',
|
|
"actor_type IN ('USER', 'SYSTEM')"
|
|
]) assert.ok(migration.includes(pattern), 'migration is missing ' + pattern);
|
|
|
|
const repository = read('backend/src/cleaning/cleaning-task-repository.ts');
|
|
for (const pattern of [
|
|
'CLEANING_SETTLEMENT_CONCURRENTLY_CLAIMED',
|
|
'assertTaskSettlementMutable',
|
|
'beginSettlementPayout',
|
|
'cancelSettlement',
|
|
'recordSettlementEventWithConnection',
|
|
"status = 'SETTLED'",
|
|
'exportSettlements',
|
|
'querySettlementRows'
|
|
]) assert.ok(repository.includes(pattern), 'repository is missing ' + pattern);
|
|
assert.match(repository, /payout_state IN \('PROCESSING', 'WAIT_USER_CONFIRM'\)/);
|
|
assert.match(repository, /reversed_at IS NULL/);
|
|
|
|
const payout = read('backend/src/cleaning/cleaning-payout-service.ts');
|
|
assert.match(payout, /await this\.repository\.beginSettlementPayout/);
|
|
assert.match(payout, /actorType: 'SYSTEM'/);
|
|
assert.match(payout, /payout_request_no AS payoutRequestNo/);
|
|
assert.ok(payout.includes('CLP$' + '{settlementId}'), 'stable payout request id is missing');
|
|
|
|
const routes = read('backend/src/routes/cleaning.ts');
|
|
assert.match(routes, /settlements\/export/);
|
|
assert.match(routes, /settlements\/:settlementId\/cancel/);
|
|
assert.match(routes, /manualOverride: true/);
|
|
|
|
const admin = read('admin/src/components/CleaningSettlementsPanel.vue');
|
|
for (const pattern of [
|
|
'exportCleaningSettlements',
|
|
'canCancelSettlement',
|
|
'openManualPaid',
|
|
'detailDialog.detail.reversals',
|
|
'detailDialog.detail.events',
|
|
'payoutState: props.filters.payoutState',
|
|
"['', 'FAIL'].includes(row.payoutState)",
|
|
'金额(分)'
|
|
]) assert.ok(admin.includes(pattern), 'admin settlement UI is missing ' + pattern);
|
|
|
|
for (const [file, pattern] of [
|
|
['admin/src/components/CleaningStatisticsPanel.vue', '待结算(分)'],
|
|
['admin/src/components/CleaningTasksPanel.vue', '金额(分)'],
|
|
['admin/src/components/CleaningWorkspace.vue', '金额(分)']
|
|
]) assert.ok(read(file).includes(pattern), file + ' must export monetary values as integer cents');
|
|
|
|
const mysqlTest = read('backend/tests/mysql-migration-roundtrip.test.mjs');
|
|
for (const pattern of [
|
|
'm09c-generate-one',
|
|
'CLEANING_MEMBER_SETTLEMENT_LOCKED',
|
|
'm09c-cancel-processing',
|
|
'collaborative task settles only after every member share is paid'
|
|
]) assert.ok(mysqlTest.includes(pattern), 'live MySQL test is missing ' + pattern);
|
|
|
|
console.log('PASS: M09-C settlement integrity, reversal, payout and shared-query gates are present.');
|