import assert from 'node:assert/strict'; import { LegacyReadRepository } from '../dist/db/legacy-read-repository.js'; const calls = []; const fakePool = { async query(sql, parameters) { calls.push({ sql, parameters }); const isOrderQuery = /FROM `member_order_info`/.test(sql); return [[{ legacyId: '7', tenantId: '2', parentId: '3', name: 'Room A', code: 'A01', status: 'OPEN', startAt: null, endAt: null, totalAmountDecimal: isOrderQuery ? '25.80' : null, paidAmountDecimal: isOrderQuery ? '20.00' : null, renewalAmountDecimal: isOrderQuery ? '5.8' : null, groupAmountDecimal: isOrderQuery ? '0.00' : null, refundAmountDecimal: null }], []]; } }; const repository = new LegacyReadRepository(fakePool); const rooms = await repository.listRooms({ tenantId: 2, parentId: 3, limit: 25 }); assert.equal(rooms.length, 1); assert.equal(rooms[0].totalAmountCents, null); assert.equal(rooms[0].paidAmountCents, null); assert.equal(rooms[0].renewalAmountCents, null); assert.equal(rooms[0].groupAmountCents, null); assert.equal(rooms[0].refundAmountCents, null); assert.equal(calls.length, 1); assert.match(calls[0].sql, /FROM `member_room_info`/); assert.match(calls[0].sql, /`tenant_id` = \?/); assert.match(calls[0].sql, /`store_id` = \?/); assert.match(calls[0].sql, /ORDER BY `id` ASC LIMIT \?/); assert.deepEqual(calls[0].parameters, [2, 3, 25]); assert.doesNotMatch(calls[0].sql, /\b(?:INSERT|UPDATE|DELETE|REPLACE)\b/i); const orders = await repository.listOrders({ tenantId: 2, parentId: 3, limit: 10 }); 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); assert.match(calls[1].sql, /`price` AS `totalAmountDecimal`/); assert.match(calls[1].sql, /`pay_price` AS `paidAmountDecimal`/); assert.match(calls[1].sql, /`refund_price` AS `refundAmountDecimal`/); await assert.rejects( () => repository.listStores({ tenantId: 2, limit: 501 }), /between 1 and 500/ ); const unsafeRepository = new LegacyReadRepository(fakePool, { stores: { table: 'member_store_info; DROP TABLE users', idColumn: 'id', tenantColumn: 'tenant_id' }, rooms: { table: 'member_room_info', idColumn: 'id', tenantColumn: 'tenant_id' }, orders: { table: 'member_order_info', idColumn: 'id', tenantColumn: 'tenant_id' }, devices: { table: 'member_device_info', idColumn: 'id', tenantColumn: 'tenant_id' } }); await assert.rejects( () => unsafeRepository.listStores({ tenantId: 2 }), /Unsafe legacy SQL identifier/ ); const invalidMoneyRepository = new LegacyReadRepository({ async query() { return [[{ legacyId: '8', tenantId: '2', parentId: '3', name: null, code: 'ORDER-8', status: 'PAID', startAt: null, endAt: null, totalAmountDecimal: '1.001', paidAmountDecimal: '1.00', renewalAmountDecimal: '0.00', groupAmountDecimal: '0.00', refundAmountDecimal: '0.00' }], []]; } }); await assert.rejects( () => invalidMoneyRepository.listOrders({ tenantId: 2 }), /member_order_info\.price/ ); const missingRequiredMoneyRepository = new LegacyReadRepository({ async query() { return [[{ legacyId: '9', tenantId: '2', parentId: '3', name: null, code: 'ORDER-9', status: 'PAID', startAt: null, endAt: null, totalAmountDecimal: null, paidAmountDecimal: null, renewalAmountDecimal: null, groupAmountDecimal: null, refundAmountDecimal: null }], []]; } }); await assert.rejects( () => missingRequiredMoneyRepository.listOrders({ tenantId: 2 }), /member_order_info\.price.*cannot be null/ ); console.log('PASS: legacy read-only repository contracts are present.');