diff --git a/jpos/src/main/resources/packager/cmf-858.xml b/jpos/src/main/resources/packager/cmf-858.xml
index fdf67d2a55..e6b4db859a 100644
--- a/jpos/src/main/resources/packager/cmf-858.xml
+++ b/jpos/src/main/resources/packager/cmf-858.xml
@@ -763,7 +763,7 @@
name="Reserved for ISO use"
pad="false"
class="org.jpos.iso.IFB_LLLLBINARY"/>
-
-
-
+ class="org.jpos.iso.IFB_LLLLBINARY"/>
.
+ */
+
+package org.jpos.iso.packager;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.jpos.iso.IFB_LLLBINARY;
+import org.jpos.iso.IFB_LLLLBINARY;
+import org.jpos.iso.ISOBinaryField;
+import org.jpos.iso.ISOFieldPackager;
+import org.jpos.iso.ISOMsg;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class CMFFieldDefinitionsTest {
+ @ParameterizedTest
+ @ValueSource(strings = {"cmf.xml", "cmfv3.xml", "cmf-858.xml"})
+ void topLevelFieldIdsAreUnique(String resource) throws Exception {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ try (InputStream input = resource(resource)) {
+ Element root = factory.newDocumentBuilder().parse(input).getDocumentElement();
+ Set ids = new HashSet<>();
+ for (Node child = root.getFirstChild(); child != null; child = child.getNextSibling()) {
+ if (child instanceof Element field && field.hasAttribute("id"))
+ assertTrue(ids.add(field.getAttribute("id")),
+ resource + ": duplicate field " + field.getAttribute("id"));
+ }
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"cmf.xml", "cmfv3.xml", "cmf-858.xml"})
+ void reservedAndKeyManagementFieldsRoundTripAtTheirLimits(String resource) throws Exception {
+ GenericPackager packager;
+ try (InputStream input = resource(resource)) {
+ packager = new GenericPackager(input);
+ }
+ ISOFieldPackager reserved = packager.getFieldPackager(91);
+ ISOFieldPackager keys = packager.getFieldPackager(96);
+ assertInstanceOf(IFB_LLLLBINARY.class, reserved);
+ assertEquals(9999, reserved.getLength());
+ assertInstanceOf(IFB_LLLBINARY.class, keys);
+ assertEquals(999, keys.getLength());
+
+ byte[] reservedData = new byte[9999];
+ byte[] keyData = new byte[999];
+ Arrays.fill(reservedData, (byte) 0xA5);
+ Arrays.fill(keyData, (byte) 0x5A);
+ assertArrayEquals(new byte[] {(byte) 0x99, (byte) 0x99},
+ Arrays.copyOf(reserved.pack(new ISOBinaryField(91, reservedData)), 2));
+ assertArrayEquals(new byte[] {0x09, (byte) 0x99},
+ Arrays.copyOf(keys.pack(new ISOBinaryField(96, keyData)), 2));
+
+ ISOMsg message = new ISOMsg();
+ message.setPackager(packager);
+ message.setMTI("1200");
+ message.set(91, reservedData);
+ message.set(96, keyData);
+ byte[] packed = message.pack();
+ ISOMsg unpacked = new ISOMsg();
+ unpacked.setPackager(packager);
+ assertEquals(packed.length, unpacked.unpack(packed));
+ assertArrayEquals(reservedData, unpacked.getBytes(91));
+ assertArrayEquals(keyData, unpacked.getBytes(96));
+ }
+
+ private InputStream resource(String name) {
+ InputStream input = getClass().getResourceAsStream("/packager/" + name);
+ assertNotNull(input, name);
+ return input;
+ }
+}