import assert from 'node:assert/strict'; import { randomUUID } from 'node:crypto'; import { createRequire } from 'node:module'; const url = new URL(process.env.SIGNATURE_TEST_DATABASE_URL || ''); assert(['localhost', '127.0.0.1'].includes(url.hostname) && url.pathname.startsWith('/cmpp_qa_signature_')); process.env.DATABASE_URL = url.toString(); process.env.NODE_ENV = 'test'; const require = createRequire(new URL('../../api/package.json', import.meta.url)); require('reflect-metadata'); const { PrismaService } = require('./dist/prisma/prisma.service'); const { completionDatabase } = require('./dist/send-chain/completion-context'); const { SendDownstreamDeliveryService } = require('./dist/send-chain/send-downstream-delivery.service'); const { OpenApiService } = require('./dist/open-api/open-api.service'); const { resolveUplinkMatch } = require('./dist/send-chain/uplink-matching'); const db = new PrismaService(), proxy = completionDatabase(db), prefix = randomUUID().slice(0, 8); function delivery(openApi) { let service; const facade = { resolveUplinkMatch: (event, channel) => resolveUplinkMatch(proxy, event, channel), queueAndTryDownstreamDelivery: (data) => service.queueAndTryDownstreamDelivery(data), postGatewayControl: () => { throw new Error('External delivery is forbidden in QA'); }, }; service = new SendDownstreamDeliveryService(proxy, undefined, openApi, facade, {}); return service; } try { const tenant = await db.tenant.create({ data: { name: '上行验收', code: prefix } }); const apps = []; for (const suffix of ['a', 'b']) apps.push( await db.smsApplication.create({ data: { tenantId: tenant.id, name: '应用' + suffix, cmppAccount: prefix + suffix, cmppEnterpriseCode: '000001', secretHash: 'isolated', interfaceEnabled: true, }, }), ); const channel = await db.smsChannel.create({ data: { name: '仅入库隔离通道', code: prefix, srcId: '1069', gatewayHost: '127.0.0.1', gatewayPort: 1, account: 'isolated', passwordCipher: 'not-a-secret', carriers: ['mobile'], status: 'disabled', }, }); const received = new Date(), sent = new Date(received.getTime() - 60_000); for (const [phone, index] of [ ['13800000101', 0], ['13800000101', 0], ['13800000102', 0], ['13800000102', 1], ]) { const m = await db.smsMessageRecord.create({ data: { messageId: randomUUID(), tenantId: tenant.id, applicationId: apps[index].id, phoneNumber: phone, content: '隔离上行匹配测试', }, }); await db.smsSubmitRecord.create({ data: { messageRecordId: m.id, channelId: channel.id, submitId: randomUUID(), submitStatus: 'accepted', submittedAt: sent, }, }); } for (const app of apps) { await db.smsApplicationHttpConfig.create({ data: { applicationId: app.id, enabled: true, sendEnabled: false } }); await db.httpWebhookEndpoint.create({ data: { applicationId: app.id, eventType: 'uplink', url: 'http://127.0.0.1:1/never-called', secretEncrypted: 'isolated-no-delivery', secretLast4: 'test', }, }); } const service = delivery(new OpenApiService(db, undefined)); const event = { eventId: randomUUID(), channelId: channel.id, phoneNumber: '13800000101', destId: '10690001', content: 'TD', receivedAt: received.toISOString(), gatewayMessageId: 'supplier-mo-id', }; const copies = await Promise.all([service.handleUplink(event), service.handleUplink(event)]); assert.equal(copies[0].id, copies[1].id); assert.equal(copies[0].messageId, null); assert.equal(copies[0].messageRecordId, null); assert.equal(copies[0].gatewayMessageId, 'supplier-mo-id'); assert.equal(await db.cmppDownstreamDelivery.count({ where: { dedupeKey: 'uplink:' + copies[0].id } }), 1); assert.equal(await db.httpWebhookEvent.count({ where: { uplinkMessageId: copies[0].id } }), 1); console.log('PASS 重复事件原子入库、无唯一原短信不伪造编号、CMPP与HTTP通知意图各一份'); const ambiguous = await service.handleUplink({ ...event, eventId: randomUUID(), phoneNumber: '13800000102' }); assert.equal(ambiguous.matchStatus, 'ambiguous'); const candidates = await db.smsUplinkMatchCandidate.findMany({ where: { uplinkMessageId: ambiguous.id } }); assert.equal(candidates.length, 2); const claims = await Promise.allSettled(candidates.map((c) => service.claimUplinkMatchCandidate(ambiguous.id, c.id))); assert.equal(claims.filter((r) => r.status === 'fulfilled').length, 1); assert.equal(await db.cmppDownstreamDelivery.count({ where: { dedupeKey: 'uplink:' + ambiguous.id } }), 1); assert.equal( await db.smsUplinkMatchCandidate.count({ where: { uplinkMessageId: ambiguous.id, status: 'claimed' } }), 1, ); console.log('PASS 并发认领仅一个应用成功、候选及通知同事务'); const failedEvent = { ...event, eventId: randomUUID() }; await assert.rejects( delivery({ queueWebhookEvent: async () => { throw new Error('injected notification storage failure'); }, }).handleUplink(failedEvent), /injected/, ); assert.equal(await db.smsUplinkMessage.count({ where: { eventId: failedEvent.eventId } }), 0); await service.handleUplink(failedEvent); assert.equal(await db.smsUplinkMessage.count({ where: { eventId: failedEvent.eventId } }), 1); console.log('PASS 通知存储故障回滚上行、重试恢复,不调用外部发送'); } finally { await db.onModuleDestroy(); }