fix: reconcile shared-channel receipts and protocol logs

This commit is contained in:
hectorzhao
2026-07-24 12:52:06 +08:00
parent 3997349c21
commit ca12f14b00
14 changed files with 1110 additions and 43 deletions
@@ -0,0 +1,119 @@
import { BadRequestException } from '@nestjs/common';
import { GatewayEventsController } from './gateway-events.controller';
describe('GatewayEventsController protocol logging', () => {
const sendChain = {
handleSubmitResult: jest.fn(),
handleReceipt: jest.fn(),
};
const protocolLogs = {
record: jest.fn(),
};
const controller = new GatewayEventsController(sendChain as never, {} as never, protocolLogs as never);
beforeEach(() => {
jest.clearAllMocks();
});
it('does not duplicate a supplier SubmitResp from the internal aggregate callback', async () => {
sendChain.handleSubmitResult.mockResolvedValue({ accepted: true });
const body = {
messageId: 'MSG-1',
channelId: 'channel-1',
gatewayMessageId: '123',
sequenceId: 7,
submitStatus: 'accepted' as const,
};
await controller.submitResult(body);
expect(protocolLogs.record).not.toHaveBeenCalled();
});
it('accepts only safe outbound Gateway packet events', () => {
expect(controller.protocolLog({
protocol: 'cmpp',
direction: 'platform_to_channel',
eventType: 'submit',
status: 'success',
messageId: 'MSG-1',
})).toEqual({ accepted: true });
expect(protocolLogs.record).toHaveBeenCalledTimes(1);
expect(() => controller.protocolLog({
protocol: 'cmpp',
direction: 'client_to_platform',
eventType: 'submit',
status: 'success',
})).toThrow(BadRequestException);
expect(controller.protocolLog({
protocol: 'cmpp',
direction: 'channel_to_platform',
eventType: 'submit_resp',
status: 'success',
messageId: 'MSG-1',
})).toEqual({ accepted: true });
expect(controller.protocolLog({
protocol: 'cmpp',
direction: 'platform_to_client',
eventType: 'submit_resp',
status: 'success',
messageId: 'MSG-1',
})).toEqual({ accepted: true });
});
it('enriches an enterprise Submit packet with identifiers returned by the real service', async () => {
(sendChain as Record<string, jest.Mock>).submitInboundMessage = jest.fn().mockResolvedValue({
accepted: true,
tenantId: 'tenant-1',
applicationId: 'app-1',
messageId: 'MSG-1',
status: 'fragment_pending',
});
await controller.submitInbound({
account: '607532',
phoneNumber: '13127620092',
content: 'fragment',
sequenceId: 141,
});
expect(protocolLogs.record).toHaveBeenCalledWith(expect.objectContaining({
direction: 'client_to_platform',
eventType: 'submit',
tenantId: 'tenant-1',
applicationId: 'app-1',
messageId: 'MSG-1',
status: 'success',
}));
});
it('replaces a fallback receipt identifier with the resolved main message identifier', async () => {
sendChain.handleReceipt.mockResolvedValue({
tenantId: 'tenant-1',
applicationId: 'app-1',
messageId: 'MSG-LONG-1',
});
await controller.receipt({
messageId: 'receipt-736070230367350788',
channelId: 'channel-copy',
gatewayMessageId: '736070230367350788',
phoneNumber: '13127620092',
receiptStatus: 'delivered',
rawStatus: 'DELIVRD',
});
expect(protocolLogs.record).toHaveBeenCalledWith(expect.objectContaining({
direction: 'channel_to_platform',
eventType: 'deliver_receipt',
channelId: 'channel-copy',
messageId: 'MSG-LONG-1',
gatewayMessageId: '736070230367350788',
tenantId: 'tenant-1',
applicationId: 'app-1',
status: 'success',
}));
});
});