Initial commit: add .gitignore and README

This commit is contained in:
defiQUG
2026-02-09 21:51:45 -08:00
commit 929fe6f6b6
240 changed files with 40977 additions and 0 deletions

View 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 &amp; Special: "quotes" &lt;tags&gt;</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('&amp;');
});
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:');
});
});
});