Initial commit: add .gitignore and README
This commit is contained in:
303
tests/property-based/exports/format-edge-cases.test.ts
Normal file
303
tests/property-based/exports/format-edge-cases.test.ts
Normal file
@@ -0,0 +1,303 @@
|
||||
/**
|
||||
* Property-Based Tests for Export Format Edge Cases
|
||||
*
|
||||
* Tests for edge cases in format generation, delimiters, encoding, etc.
|
||||
*/
|
||||
|
||||
import { RJEContainer } from '@/exports/containers/rje-container';
|
||||
import { RawISOContainer } from '@/exports/containers/raw-iso-container';
|
||||
import { XMLV2Container } from '@/exports/containers/xmlv2-container';
|
||||
import { ISOMessage, MessageType, MessageStatus } from '@/models/message';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
describe('Export Format Edge Cases', () => {
|
||||
const createTestMessage = (xmlContent?: string): ISOMessage => {
|
||||
return {
|
||||
id: uuidv4(),
|
||||
paymentId: uuidv4(),
|
||||
messageType: MessageType.PACS_008,
|
||||
uetr: uuidv4(),
|
||||
msgId: 'MSG-TEST',
|
||||
xmlContent: xmlContent || `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.10">
|
||||
<FIToFICstmrCdtTrf>
|
||||
<GrpHdr>
|
||||
<MsgId>MSG-TEST</MsgId>
|
||||
</GrpHdr>
|
||||
</FIToFICstmrCdtTrf>
|
||||
</Document>`,
|
||||
xmlHash: 'test-hash',
|
||||
status: MessageStatus.VALIDATED,
|
||||
createdAt: new Date(),
|
||||
};
|
||||
};
|
||||
|
||||
describe('RJE Format Edge Cases', () => {
|
||||
it('should handle empty message list in batch', async () => {
|
||||
const result = await RJEContainer.exportBatch([]);
|
||||
expect(result).toBe('');
|
||||
});
|
||||
|
||||
it('should handle single message in batch (no delimiter)', async () => {
|
||||
const message = createTestMessage();
|
||||
const result = await RJEContainer.exportBatch([message]);
|
||||
|
||||
// Single message should not have $ delimiter
|
||||
expect(result).not.toContain('$');
|
||||
});
|
||||
|
||||
it('should ensure no trailing $ in batch', async () => {
|
||||
const messages = [createTestMessage(), createTestMessage(), createTestMessage()];
|
||||
const result = await RJEContainer.exportBatch(messages);
|
||||
|
||||
// Count $ delimiters (should be 2 for 3 messages)
|
||||
const delimiterCount = (result.match(/\$/g) || []).length;
|
||||
expect(delimiterCount).toBe(2);
|
||||
|
||||
// Last character should not be $
|
||||
expect(result.trim().endsWith('$')).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle CRLF in message content', async () => {
|
||||
const message = createTestMessage();
|
||||
const result = await RJEContainer.exportMessage(message);
|
||||
|
||||
// Should contain CRLF
|
||||
expect(result).toContain('\r\n');
|
||||
});
|
||||
|
||||
it('should handle very long UETR in Block 3', async () => {
|
||||
const message = createTestMessage();
|
||||
const longUetr = uuidv4() + uuidv4(); // Double length UUID
|
||||
const identityMap = {
|
||||
paymentId: message.paymentId,
|
||||
uetr: longUetr,
|
||||
ledgerJournalIds: [],
|
||||
internalTransactionIds: [],
|
||||
};
|
||||
|
||||
const result = await RJEContainer.exportMessage(message, identityMap);
|
||||
// Should still include UETR (may be truncated)
|
||||
expect(result).toContain(':121:');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Raw ISO Format Edge Cases', () => {
|
||||
it('should handle XML with special characters', async () => {
|
||||
const xmlWithSpecialChars = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.10">
|
||||
<FIToFICstmrCdtTrf>
|
||||
<GrpHdr>
|
||||
<MsgId>MSG-SPECIAL</MsgId>
|
||||
</GrpHdr>
|
||||
<CdtTrfTxInf>
|
||||
<RmtInf>
|
||||
<Ustrd>Test & Special: "quotes" <tags></Ustrd>
|
||||
</RmtInf>
|
||||
</CdtTrfTxInf>
|
||||
</FIToFICstmrCdtTrf>
|
||||
</Document>`;
|
||||
|
||||
const message = createTestMessage(xmlWithSpecialChars);
|
||||
const result = await RawISOContainer.exportMessage(message);
|
||||
|
||||
// Should preserve XML structure
|
||||
expect(result).toContain('urn:iso:std:iso:20022');
|
||||
expect(result).toContain('&');
|
||||
});
|
||||
|
||||
it('should handle empty batch', async () => {
|
||||
const result = await RawISOContainer.exportBatch([]);
|
||||
expect(result).toBe('');
|
||||
});
|
||||
|
||||
it('should handle messages with missing UETR', async () => {
|
||||
const xmlWithoutUETR = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.10">
|
||||
<FIToFICstmrCdtTrf>
|
||||
<GrpHdr>
|
||||
<MsgId>MSG-NO-UETR</MsgId>
|
||||
</GrpHdr>
|
||||
<CdtTrfTxInf>
|
||||
<PmtId>
|
||||
<EndToEndId>E2E-123</EndToEndId>
|
||||
</PmtId>
|
||||
</CdtTrfTxInf>
|
||||
</FIToFICstmrCdtTrf>
|
||||
</Document>`;
|
||||
|
||||
const message = createTestMessage(xmlWithoutUETR);
|
||||
const uetr = uuidv4();
|
||||
const identityMap = {
|
||||
paymentId: message.paymentId,
|
||||
uetr,
|
||||
ledgerJournalIds: [],
|
||||
internalTransactionIds: [],
|
||||
};
|
||||
|
||||
const result = await RawISOContainer.exportMessage(message, identityMap, {
|
||||
ensureUETR: true,
|
||||
});
|
||||
|
||||
// Should add UETR
|
||||
expect(result).toContain(uetr);
|
||||
});
|
||||
|
||||
it('should handle line ending normalization edge cases', async () => {
|
||||
const message = createTestMessage();
|
||||
|
||||
// Test LF only
|
||||
const lfResult = await RawISOContainer.exportMessage(message, undefined, {
|
||||
lineEnding: 'LF',
|
||||
});
|
||||
expect(lfResult).not.toContain('\r\n');
|
||||
|
||||
// Test CRLF
|
||||
const crlfResult = await RawISOContainer.exportMessage(message, undefined, {
|
||||
lineEnding: 'CRLF',
|
||||
});
|
||||
expect(crlfResult).toContain('\r\n');
|
||||
});
|
||||
});
|
||||
|
||||
describe('XML v2 Format Edge Cases', () => {
|
||||
it('should handle empty message list in batch', async () => {
|
||||
const result = await XMLV2Container.exportBatch([]);
|
||||
expect(result).toContain('BatchPDU');
|
||||
expect(result).toContain('<MessageCount>0</MessageCount>');
|
||||
});
|
||||
|
||||
it('should handle Base64 encoding option', async () => {
|
||||
const message = createTestMessage();
|
||||
const result = await XMLV2Container.exportMessage(message, undefined, {
|
||||
base64EncodeMT: true,
|
||||
});
|
||||
|
||||
// Should contain MessageBlock (Base64 encoding is internal)
|
||||
expect(result).toContain('MessageBlock');
|
||||
});
|
||||
|
||||
it('should handle missing Alliance Header option', async () => {
|
||||
const message = createTestMessage();
|
||||
const result = await XMLV2Container.exportMessage(message, undefined, {
|
||||
includeAllianceHeader: false,
|
||||
});
|
||||
|
||||
// Should still contain MessageBlock
|
||||
expect(result).toContain('MessageBlock');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Encoding Edge Cases', () => {
|
||||
it('should handle UTF-8 characters correctly', async () => {
|
||||
const xmlWithUnicode = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.10">
|
||||
<FIToFICstmrCdtTrf>
|
||||
<GrpHdr>
|
||||
<MsgId>MSG-UNICODE</MsgId>
|
||||
</GrpHdr>
|
||||
<CdtTrfTxInf>
|
||||
<Cdtr>
|
||||
<Nm>Test 测试 テスト</Nm>
|
||||
</Cdtr>
|
||||
</CdtTrfTxInf>
|
||||
</FIToFICstmrCdtTrf>
|
||||
</Document>`;
|
||||
|
||||
const message = createTestMessage(xmlWithUnicode);
|
||||
const result = await RawISOContainer.exportMessage(message);
|
||||
|
||||
// Should preserve UTF-8 characters
|
||||
expect(result).toContain('测试');
|
||||
expect(result).toContain('テスト');
|
||||
});
|
||||
|
||||
it('should handle very long XML content', async () => {
|
||||
// Create XML with very long remittance info
|
||||
const longRemittance = 'A'.repeat(10000);
|
||||
const xmlWithLongContent = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.10">
|
||||
<FIToFICstmrCdtTrf>
|
||||
<GrpHdr>
|
||||
<MsgId>MSG-LONG</MsgId>
|
||||
</GrpHdr>
|
||||
<CdtTrfTxInf>
|
||||
<RmtInf>
|
||||
<Ustrd>${longRemittance}</Ustrd>
|
||||
</RmtInf>
|
||||
</CdtTrfTxInf>
|
||||
</FIToFICstmrCdtTrf>
|
||||
</Document>`;
|
||||
|
||||
const message = createTestMessage(xmlWithLongContent);
|
||||
const result = await RawISOContainer.exportMessage(message);
|
||||
|
||||
// Should handle long content
|
||||
expect(result.length).toBeGreaterThan(10000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Delimiter Edge Cases', () => {
|
||||
it('should handle $ character in message content (RJE)', async () => {
|
||||
// Create message with $ in content
|
||||
const xmlWithDollar = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.10">
|
||||
<FIToFICstmrCdtTrf>
|
||||
<GrpHdr>
|
||||
<MsgId>MSG-DOLLAR</MsgId>
|
||||
</GrpHdr>
|
||||
<CdtTrfTxInf>
|
||||
<RmtInf>
|
||||
<Ustrd>Amount: $1000.00</Ustrd>
|
||||
</RmtInf>
|
||||
</CdtTrfTxInf>
|
||||
</FIToFICstmrCdtTrf>
|
||||
</Document>`;
|
||||
|
||||
const message = createTestMessage(xmlWithDollar);
|
||||
const result = await RJEContainer.exportMessage(message);
|
||||
|
||||
// Should still be valid RJE format
|
||||
expect(result).toContain('{1:');
|
||||
expect(result).toContain('{2:');
|
||||
});
|
||||
|
||||
it('should properly separate messages with $ delimiter', async () => {
|
||||
const messages = [createTestMessage(), createTestMessage()];
|
||||
const result = await RJEContainer.exportBatch(messages);
|
||||
|
||||
// Should have exactly one $ delimiter for 2 messages
|
||||
const parts = result.split('$');
|
||||
expect(parts.length).toBe(2);
|
||||
expect(parts[0].trim().length).toBeGreaterThan(0);
|
||||
expect(parts[1].trim().length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Field Truncation Edge Cases', () => {
|
||||
it('should handle very long account numbers', async () => {
|
||||
const message = createTestMessage();
|
||||
const identityMap = {
|
||||
paymentId: message.paymentId,
|
||||
uetr: message.uetr,
|
||||
ledgerJournalIds: [],
|
||||
internalTransactionIds: [],
|
||||
mur: 'A'.repeat(200), // Very long MUR
|
||||
};
|
||||
|
||||
const result = await RJEContainer.exportMessage(message, identityMap);
|
||||
// Should handle long fields (may be truncated in actual implementation)
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
|
||||
it('should handle very long BIC codes', async () => {
|
||||
const message = createTestMessage();
|
||||
// RJE Block 2 has fixed 12-character receiver field
|
||||
const result = await RJEContainer.exportMessage(message);
|
||||
|
||||
// Should still generate valid Block 2
|
||||
expect(result).toContain('{2:');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user