import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import { join } from 'node:path'; const root = process.cwd(); const read = (path) => readFileSync(join(root, path), 'utf8'); const orderService = read('backend/src/products/product-order-service.ts'); for (const pattern of [ 'reconcileForManagement', 'buildProductOrderReconciliation', 'qipai_product_inventory_ledger', 'qipai_product_storage_records', 'PAYMENT_CAPTURED_AMOUNT', 'REFUND_SUCCEEDED_AMOUNT', 'INVENTORY_RESERVATION_BALANCE', 'INVENTORY_SALE_BALANCE', 'STORAGE_ITEM_BALANCE' ]) assert.ok(orderService.includes(pattern), `product reconciliation is missing ${pattern}`); const orderRoutes = read('backend/src/routes/product-orders.ts'); assert.match(orderRoutes, /product-orders\/:orderId\/reconciliation/); assert.match(orderRoutes, /service\.reconcileForManagement/); assert.match(orderRoutes, /capabilities\.includes\('platform\.manage'\)/); const inventoryRoute = read('backend/src/routes/inventory.ts'); const inventoryService = read('backend/src/inventory/inventory-service.ts'); assert.match(inventoryRoute, /capabilities\.includes\('platform\.manage'\)/); assert.match(inventoryService, /capabilities\.includes\('platform\.manage'\)/); const api = read('admin/src/api.ts'); const types = read('admin/src/types.ts'); const panel = read('admin/src/components/ProductOperationsPanel.vue'); assert.match(api, /getProductOrderReconciliation/); assert.match(types, /interface ProductOrderReconciliation/); for (const pattern of [ '逐单对账', '一致性检查', '库存不可变流水', '关联寄存', '支付流水', '退款流水', '不可变事件' ]) assert.ok(panel.includes(pattern), `product operations reconciliation is missing ${pattern}`); const reconciliationTest = read('backend/tests/product-reconciliation.test.mjs'); for (const pattern of [ 'detects amount, payment, refund, inventory and storage drift', "inventoryStatus: 'RELEASED'", 'reservationBalance', 'STORAGE_ITEM_BALANCE' ]) assert.ok(reconciliationTest.includes(pattern), `reconciliation unit test is missing ${pattern}`); const routeTest = read('backend/tests/product-order-route.test.mjs'); const inventoryRouteTest = read('backend/tests/inventory-route.test.mjs'); const inventoryServiceTest = read('backend/tests/inventory-service.test.mjs'); for (const source of [routeTest, inventoryRouteTest, inventoryServiceTest]) { assert.match(source, /platform\.manage/); assert.match(source, /storeIds: \[\]/); } const mysqlTest = read('backend/tests/mysql-migration-roundtrip.test.mjs'); for (const pattern of [ 'completedReconciliation', 'storageReconciliation', 'platformActor', 'reconcileForManagement', 'netRevenueCents', 'recordRemainingQuantity' ]) assert.ok(mysqlTest.includes(pattern), `live MySQL reconciliation is missing ${pattern}`); console.log( 'PASS: M09 regression reconciliation, platform scope, immutable ledgers and drift gates are present.' );