import { BadRequestException } from '@nestjs/common'; import { GatewayEventsController } from './gateway-events.controller'; describe('GatewayEventsController protocol logging', () => { const sendChain = { handleSubmitResult: jest.fn(), handleSubmitSegmentResult: jest.fn(), handleReceipt: jest.fn(), intakeReceipt: jest.fn(), }; const protocolLogs = { record: jest.fn(), }; const metrics = { beginCmppInboundStage: jest.fn().mockReturnValue(1n), finishCmppInboundStage: jest.fn(), }; const controller = new GatewayEventsController( sendChain as never, {} as never, protocolLogs as never, { recordEvent: jest.fn() } as never, metrics 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('persists each supplier segment response without adding a duplicate protocol log', async () => { sendChain.handleSubmitSegmentResult.mockResolvedValue({ accepted: true }); const body = { messageId: 'MSG-LONG-1', channelId: 'channel-1', submitId: 'submit-1', segmentIndex: 2, segmentTotal: 3, gatewayMessageId: '456', sequenceId: 8, submitStatus: 'accepted' as const, }; await expect(controller.submitSegmentResult(body)).resolves.toEqual({ accepted: true }); expect(sendChain.handleSubmitSegmentResult).toHaveBeenCalledWith(body); expect(protocolLogs.record).not.toHaveBeenCalled(); }); it('logs a supplier receipt only after it is durably accepted by the inbox', async () => { sendChain.intakeReceipt.mockResolvedValue({ accepted: true, inboxId: 'inbox-1', status: 'pending' }); const body = { messageId: 'receipt-456', channelId: 'channel-1', connectionId: 'channel-1:0', gatewayMessageId: '456', phoneNumber: '13127620092', receiptStatus: 'undelivered' as const, rawStatus: 'UNDELIV', }; await expect(controller.receiptIntake(body)).resolves.toEqual({ accepted: true, inboxId: 'inbox-1', status: 'pending', }); expect(protocolLogs.record).toHaveBeenCalledWith(expect.objectContaining({ direction: 'channel_to_platform', eventType: 'deliver_receipt', channelId: 'channel-1', messageId: 'receipt-456', gatewayMessageId: '456', status: 'success', })); }); it('records a failed inbound total without swallowing the service error', async () => { const failure = new Error('inbound failed'); (sendChain as Record).submitInboundMessage = jest.fn().mockRejectedValue(failure); await expect(controller.submitInbound({ account: '607532', phoneNumber: '13127620092', content: 'failed', sequenceId: 142, })).rejects.toBe(failure); expect(metrics.finishCmppInboundStage).toHaveBeenCalledWith(1n, 'total', 'error'); }); 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 }); expect(controller.protocolLog({ protocol: 'cmpp', direction: 'platform_to_client', eventType: 'deliver_receipt', status: 'success', messageId: 'MSG-1', })).toEqual({ accepted: true }); expect(controller.protocolLog({ protocol: 'cmpp', direction: 'platform_to_client', eventType: 'deliver_uplink', status: 'success', messageId: 'MSG-1', })).toEqual({ accepted: true }); expect(controller.protocolLog({ protocol: 'cmpp', direction: 'client_to_platform', eventType: 'deliver_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).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', })); expect(metrics.finishCmppInboundStage).toHaveBeenCalledWith(1n, 'total', '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', })); }); });