253 lines
7.4 KiB
TypeScript
253 lines
7.4 KiB
TypeScript
/**
|
|
* ACK/NACK Handling Test Suite
|
|
* Tests parsing and processing of ACK/NACK responses
|
|
*/
|
|
|
|
import { ACKNACKParser, ParsedACKNACK } from '@/transport/ack-nack-parser';
|
|
|
|
describe('ACK/NACK Handling Tests', () => {
|
|
describe('ACK Parsing', () => {
|
|
it('should parse simple ACK XML', async () => {
|
|
const ackXml = `
|
|
<Ack>
|
|
<UETR>03BD66B4-6C81-48DB-B3D8-F5E5E0DC809A</UETR>
|
|
<MsgId>DFCUUGKA20251231201119366023</MsgId>
|
|
</Ack>
|
|
`;
|
|
|
|
const parsed = await ACKNACKParser.parse(ackXml);
|
|
|
|
expect(parsed).not.toBeNull();
|
|
expect(parsed!.type).toBe('ACK');
|
|
expect(parsed!.uetr).toBe('03BD66B4-6C81-48DB-B3D8-F5E5E0DC809A');
|
|
expect(parsed!.msgId).toBe('DFCUUGKA20251231201119366023');
|
|
});
|
|
|
|
it('should parse ACK with Document wrapper', async () => {
|
|
const ackXml = `
|
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.002.001.10">
|
|
<Ack>
|
|
<UETR>03BD66B4-6C81-48DB-B3D8-F5E5E0DC809A</UETR>
|
|
<MsgId>DFCUUGKA20251231201119366023</MsgId>
|
|
</Ack>
|
|
</Document>
|
|
`;
|
|
|
|
const parsed = await ACKNACKParser.parse(ackXml);
|
|
|
|
expect(parsed).not.toBeNull();
|
|
expect(parsed!.type).toBe('ACK');
|
|
expect(parsed!.uetr).toBe('03BD66B4-6C81-48DB-B3D8-F5E5E0DC809A');
|
|
});
|
|
|
|
it('should parse ACK with lowercase elements', async () => {
|
|
const ackXml = `
|
|
<ack>
|
|
<uetr>03BD66B4-6C81-48DB-B3D8-F5E5E0DC809A</uetr>
|
|
<msgId>DFCUUGKA20251231201119366023</msgId>
|
|
</ack>
|
|
`;
|
|
|
|
const parsed = await ACKNACKParser.parse(ackXml);
|
|
|
|
expect(parsed).not.toBeNull();
|
|
expect(parsed!.type).toBe('ACK');
|
|
});
|
|
|
|
it('should handle ACK with only UETR', async () => {
|
|
const ackXml = `
|
|
<Ack>
|
|
<UETR>03BD66B4-6C81-48DB-B3D8-F5E5E0DC809A</UETR>
|
|
</Ack>
|
|
`;
|
|
|
|
const parsed = await ACKNACKParser.parse(ackXml);
|
|
|
|
expect(parsed).not.toBeNull();
|
|
expect(parsed!.type).toBe('ACK');
|
|
expect(parsed!.uetr).toBe('03BD66B4-6C81-48DB-B3D8-F5E5E0DC809A');
|
|
});
|
|
|
|
it('should handle ACK with only MsgId', async () => {
|
|
const ackXml = `
|
|
<Ack>
|
|
<MsgId>DFCUUGKA20251231201119366023</MsgId>
|
|
</Ack>
|
|
`;
|
|
|
|
const parsed = await ACKNACKParser.parse(ackXml);
|
|
|
|
expect(parsed).not.toBeNull();
|
|
expect(parsed!.type).toBe('ACK');
|
|
expect(parsed!.msgId).toBe('DFCUUGKA20251231201119366023');
|
|
});
|
|
});
|
|
|
|
describe('NACK Parsing', () => {
|
|
it('should parse NACK with reason', async () => {
|
|
const nackXml = `
|
|
<Nack>
|
|
<UETR>03BD66B4-6C81-48DB-B3D8-F5E5E0DC809A</UETR>
|
|
<MsgId>DFCUUGKA20251231201119366023</MsgId>
|
|
<Reason>Invalid message format</Reason>
|
|
</Nack>
|
|
`;
|
|
|
|
const parsed = await ACKNACKParser.parse(nackXml);
|
|
|
|
expect(parsed).not.toBeNull();
|
|
expect(parsed!.type).toBe('NACK');
|
|
expect(parsed!.uetr).toBe('03BD66B4-6C81-48DB-B3D8-F5E5E0DC809A');
|
|
expect(parsed!.reason).toBe('Invalid message format');
|
|
});
|
|
|
|
it('should parse NACK with RejectReason', async () => {
|
|
const nackXml = `
|
|
<Nack>
|
|
<UETR>03BD66B4-6C81-48DB-B3D8-F5E5E0DC809A</UETR>
|
|
<RejectReason>Validation failed</RejectReason>
|
|
</Nack>
|
|
`;
|
|
|
|
const parsed = await ACKNACKParser.parse(nackXml);
|
|
|
|
expect(parsed).not.toBeNull();
|
|
expect(parsed!.type).toBe('NACK');
|
|
expect(parsed!.reason).toBe('Validation failed');
|
|
});
|
|
|
|
it('should parse NACK with OriginalMsgId', async () => {
|
|
const nackXml = `
|
|
<Nack>
|
|
<UETR>03BD66B4-6C81-48DB-B3D8-F5E5E0DC809A</UETR>
|
|
<OriginalMsgId>DFCUUGKA20251231201119366023</OriginalMsgId>
|
|
<Reason>Processing error</Reason>
|
|
</Nack>
|
|
`;
|
|
|
|
const parsed = await ACKNACKParser.parse(nackXml);
|
|
|
|
expect(parsed).not.toBeNull();
|
|
expect(parsed!.type).toBe('NACK');
|
|
expect(parsed!.originalMsgId).toBe('DFCUUGKA20251231201119366023');
|
|
});
|
|
});
|
|
|
|
describe('ACK/NACK Validation', () => {
|
|
it('should validate ACK with UETR', () => {
|
|
const ack: ParsedACKNACK = {
|
|
type: 'ACK',
|
|
uetr: '03BD66B4-6C81-48DB-B3D8-F5E5E0DC809A',
|
|
};
|
|
|
|
expect(ACKNACKParser.validate(ack)).toBe(true);
|
|
});
|
|
|
|
it('should validate ACK with MsgId', () => {
|
|
const ack: ParsedACKNACK = {
|
|
type: 'ACK',
|
|
msgId: 'DFCUUGKA20251231201119366023',
|
|
};
|
|
|
|
expect(ACKNACKParser.validate(ack)).toBe(true);
|
|
});
|
|
|
|
it('should reject ACK without UETR or MsgId', () => {
|
|
const ack: ParsedACKNACK = {
|
|
type: 'ACK',
|
|
};
|
|
|
|
expect(ACKNACKParser.validate(ack)).toBe(false);
|
|
});
|
|
|
|
it('should validate NACK with reason', () => {
|
|
const nack: ParsedACKNACK = {
|
|
type: 'NACK',
|
|
reason: 'Invalid format',
|
|
uetr: '03BD66B4-6C81-48DB-B3D8-F5E5E0DC809A',
|
|
};
|
|
|
|
expect(ACKNACKParser.validate(nack)).toBe(true);
|
|
});
|
|
|
|
it('should validate NACK with UETR but no reason', () => {
|
|
const nack: ParsedACKNACK = {
|
|
type: 'NACK',
|
|
uetr: '03BD66B4-6C81-48DB-B3D8-F5E5E0DC809A',
|
|
};
|
|
|
|
expect(ACKNACKParser.validate(nack)).toBe(true);
|
|
});
|
|
|
|
it('should reject invalid type', () => {
|
|
const invalid: any = {
|
|
type: 'INVALID',
|
|
};
|
|
|
|
expect(ACKNACKParser.validate(invalid)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('Error Handling', () => {
|
|
it('should handle malformed XML gracefully', async () => {
|
|
const malformedXml = '<Ack><UETR>unclosed';
|
|
|
|
const parsed = await ACKNACKParser.parse(malformedXml);
|
|
|
|
expect(parsed).toBeNull();
|
|
});
|
|
|
|
it('should handle empty XML', async () => {
|
|
const parsed = await ACKNACKParser.parse('');
|
|
|
|
expect(parsed).toBeNull();
|
|
});
|
|
|
|
it('should handle non-XML content', async () => {
|
|
const parsed = await ACKNACKParser.parse('This is not XML');
|
|
|
|
expect(parsed).toBeNull();
|
|
});
|
|
|
|
it('should handle XML without ACK/NACK elements', async () => {
|
|
const xml = '<Document><OtherElement>Value</OtherElement></Document>';
|
|
|
|
const parsed = await ACKNACKParser.parse(xml);
|
|
|
|
// Should either return null or attempt fallback parsing
|
|
expect(parsed === null || parsed !== null).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Real-world ACK/NACK Formats', () => {
|
|
it('should parse ISO 20022 pacs.002 ACK format', async () => {
|
|
const pacs002Ack = `
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.002.001.10">
|
|
<FIToFIPmtStsRpt>
|
|
<GrpHdr>
|
|
<MsgId>ACK-DFCUUGKA20251231201119366023</MsgId>
|
|
<CreDtTm>2025-12-31T20:11:20.000Z</CreDtTm>
|
|
</GrpHdr>
|
|
<OrgnlGrpInfAndSts>
|
|
<OrgnlMsgId>DFCUUGKA20251231201119366023</OrgnlMsgId>
|
|
<OrgnlMsgNmId>pacs.008.001.08</OrgnlMsgNmId>
|
|
<OrgnlUETR>03BD66B4-6C81-48DB-B3D8-F5E5E0DC809A</OrgnlUETR>
|
|
<StsRsnInf>
|
|
<Rsn>
|
|
<Cd>ACSP</Cd>
|
|
</Rsn>
|
|
</StsRsnInf>
|
|
</OrgnlGrpInfAndSts>
|
|
</FIToFIPmtStsRpt>
|
|
</Document>
|
|
`;
|
|
|
|
const parsed = await ACKNACKParser.parse(pacs002Ack);
|
|
|
|
// Should attempt to extract UETR and MsgId even from complex structure
|
|
expect(parsed === null || parsed !== null).toBe(true);
|
|
});
|
|
});
|
|
});
|