213 lines
6.5 KiB
TypeScript
213 lines
6.5 KiB
TypeScript
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<string, jest.Mock>).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<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',
|
|
}));
|
|
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',
|
|
}));
|
|
});
|
|
});
|