Skip to content

Commit 2eb7756

Browse files
committed
test: add additional tests
1 parent 2ce4a07 commit 2eb7756

9 files changed

Lines changed: 1885 additions & 0 deletions
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
using System.Text;
2+
using NetCore8583.Extensions;
3+
using NetCore8583.Parse;
4+
using Xunit;
5+
6+
namespace NetCore8583.Test.Parse
7+
{
8+
/// <summary>
9+
/// Unit tests for <see cref="AmountParseInfo"/>.
10+
///
11+
/// ASCII format: 12 decimal digit characters representing cents (divide by 100 to get amount).
12+
/// e.g. "000000001000" → 10.00
13+
///
14+
/// Binary format: 6 BCD bytes encoding 12 digits with an implied decimal point after digit 10.
15+
/// e.g. {0x00,0x00,0x00,0x00,0x10,0x00} → "0000000010.00" → 10.00
16+
/// </summary>
17+
public class TestAmountParseInfo
18+
{
19+
private static sbyte[] Ascii(string s) => s.GetSignedBytes(Encoding.ASCII);
20+
21+
// ═══════════════════════════════════════════════════════════════════════
22+
// Parse (ASCII)
23+
// ═══════════════════════════════════════════════════════════════════════
24+
25+
[Fact]
26+
public void Parse_ReturnsCorrectAmount()
27+
{
28+
var fpi = new AmountParseInfo();
29+
var val = fpi.Parse(1, Ascii("000000001000"), 0, null);
30+
Assert.Equal(IsoType.AMOUNT, val.Type);
31+
Assert.Equal(10.00m, (decimal) val.Value);
32+
}
33+
34+
[Fact]
35+
public void Parse_OneCent()
36+
{
37+
var fpi = new AmountParseInfo();
38+
var val = fpi.Parse(1, Ascii("000000000001"), 0, null);
39+
Assert.Equal(0.01m, (decimal) val.Value);
40+
}
41+
42+
[Fact]
43+
public void Parse_ZeroAmount()
44+
{
45+
var fpi = new AmountParseInfo();
46+
var val = fpi.Parse(1, Ascii("000000000000"), 0, null);
47+
Assert.Equal(0.00m, (decimal) val.Value);
48+
}
49+
50+
[Fact]
51+
public void Parse_LargeAmount()
52+
{
53+
var fpi = new AmountParseInfo();
54+
var val = fpi.Parse(1, Ascii("999999999999"), 0, null);
55+
Assert.Equal(9999999999.99m, (decimal) val.Value);
56+
}
57+
58+
[Fact]
59+
public void Parse_WithOffset()
60+
{
61+
var fpi = new AmountParseInfo();
62+
// 4 bytes prefix + 12 bytes of amount
63+
var buf = Ascii("XXXX000000005000");
64+
var val = fpi.Parse(1, buf, 4, null);
65+
Assert.Equal(50.00m, (decimal) val.Value);
66+
}
67+
68+
[Fact]
69+
public void Parse_NegativePosition_Throws()
70+
{
71+
var fpi = new AmountParseInfo();
72+
Assert.Throws<ParseException>(() => fpi.Parse(1, Ascii("000000001000"), -1, null));
73+
}
74+
75+
[Fact]
76+
public void Parse_InsufficientData_Throws()
77+
{
78+
var fpi = new AmountParseInfo();
79+
Assert.Throws<ParseException>(() => fpi.Parse(1, Ascii("00000010"), 0, null));
80+
}
81+
82+
[Fact]
83+
public void Parse_InsufficientDataFromOffset_Throws()
84+
{
85+
var fpi = new AmountParseInfo();
86+
// only 5 chars after offset 7 → not enough for 12
87+
Assert.Throws<ParseException>(() => fpi.Parse(1, Ascii("000000012345"), 7, null));
88+
}
89+
90+
[Fact]
91+
public void Parse_InvalidFormat_Throws()
92+
{
93+
var fpi = new AmountParseInfo();
94+
Assert.Throws<ParseException>(() => fpi.Parse(1, Ascii("00000000ABCD"), 0, null));
95+
}
96+
97+
[Fact]
98+
public void Parse_HasCorrectLength()
99+
{
100+
var fpi = new AmountParseInfo();
101+
var val = fpi.Parse(1, Ascii("000000002500"), 0, null);
102+
Assert.Equal(12, val.Length);
103+
}
104+
105+
// ═══════════════════════════════════════════════════════════════════════
106+
// ParseBinary (BCD)
107+
// ═══════════════════════════════════════════════════════════════════════
108+
109+
[Fact]
110+
public void ParseBinary_TenDollars()
111+
{
112+
// BCD for 12 digits "000000001000" → 6 bytes {0x00,0x00,0x00,0x00,0x10,0x00}
113+
// → "0000000010.00" → 10.00
114+
var fpi = new AmountParseInfo();
115+
var buf = new sbyte[] { 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 };
116+
var val = fpi.ParseBinary(1, buf, 0, null);
117+
Assert.Equal(IsoType.AMOUNT, val.Type);
118+
Assert.Equal(10.00m, (decimal) val.Value);
119+
}
120+
121+
[Fact]
122+
public void ParseBinary_OneCent()
123+
{
124+
// BCD "000000000001" → {0x00,0x00,0x00,0x00,0x00,0x01} → "0000000000.01" → 0.01
125+
var fpi = new AmountParseInfo();
126+
var buf = new sbyte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
127+
var val = fpi.ParseBinary(1, buf, 0, null);
128+
Assert.Equal(0.01m, (decimal) val.Value);
129+
}
130+
131+
[Fact]
132+
public void ParseBinary_ZeroAmount()
133+
{
134+
var fpi = new AmountParseInfo();
135+
var buf = new sbyte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
136+
var val = fpi.ParseBinary(1, buf, 0, null);
137+
Assert.Equal(0.00m, (decimal) val.Value);
138+
}
139+
140+
[Fact]
141+
public void ParseBinary_WithOffset()
142+
{
143+
// 2 bytes of garbage then BCD for $25.00: {0x00,0x00,0x00,0x00,0x25,0x00}
144+
// → "0000000025.00" → 25.00
145+
var fpi = new AmountParseInfo();
146+
var buf = new sbyte[] { 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00 };
147+
var val = fpi.ParseBinary(1, buf, 2, null);
148+
Assert.Equal(25.00m, (decimal) val.Value);
149+
}
150+
151+
[Fact]
152+
public void ParseBinary_LargeAmount()
153+
{
154+
// BCD "999999999999" → {0x99,0x99,0x99,0x99,0x99,0x99} → "9999999999.99" → 9999999999.99
155+
var fpi = new AmountParseInfo();
156+
var buf = new sbyte[] { unchecked((sbyte)0x99), unchecked((sbyte)0x99), unchecked((sbyte)0x99), unchecked((sbyte)0x99), unchecked((sbyte)0x99), unchecked((sbyte)0x99) };
157+
var val = fpi.ParseBinary(1, buf, 0, null);
158+
Assert.Equal(9999999999.99m, (decimal) val.Value);
159+
}
160+
}
161+
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
using System;
2+
using System.IO;
3+
using System.Text;
4+
using NetCore8583.Codecs;
5+
using NetCore8583.Extensions;
6+
using NetCore8583.Parse;
7+
using Xunit;
8+
9+
namespace NetCore8583.Test.Parse
10+
{
11+
/// <summary>
12+
/// Unit tests for <see cref="ConfigParser"/> focusing on:
13+
/// - <c>ConfigureFromClasspathConfig</c>: file-path-based configuration
14+
/// - <c>ConfigureFromDefault</c>: loading the default "n8583.xml"
15+
/// - <c>CreateDefault</c>: factory creation from default config
16+
/// - Error paths: missing files, invalid XML, malformed message types
17+
/// </summary>
18+
public class TestConfigParserUnit
19+
{
20+
// ═══════════════════════════════════════════════════════════════════════
21+
// ConfigureFromClasspathConfig
22+
// ═══════════════════════════════════════════════════════════════════════
23+
24+
[Fact]
25+
public void ConfigureFromClasspathConfig_ValidConfig_ConfiguresHeaders()
26+
{
27+
var mfact = new MessageFactory<IsoMessage>();
28+
ConfigParser.ConfigureFromClasspathConfig(mfact, "/Resources/config.xml");
29+
30+
Assert.NotNull(mfact.GetIsoHeader(0x800));
31+
Assert.NotNull(mfact.GetIsoHeader(0x810));
32+
}
33+
34+
[Fact]
35+
public void ConfigureFromClasspathConfig_ValidConfig_ConfiguresTemplates()
36+
{
37+
var mfact = new MessageFactory<IsoMessage>();
38+
ConfigParser.ConfigureFromClasspathConfig(mfact, "/Resources/config.xml");
39+
40+
var m200 = mfact.GetMessageTemplate(0x200);
41+
Assert.NotNull(m200);
42+
Assert.True(m200.HasField(3));
43+
Assert.True(m200.HasField(32));
44+
Assert.True(m200.HasField(49));
45+
}
46+
47+
[Fact]
48+
public void ConfigureFromClasspathConfig_ValidConfig_ConfiguresParseGuides()
49+
{
50+
var mfact = new MessageFactory<IsoMessage>();
51+
ConfigParser.ConfigureFromClasspathConfig(mfact, "/Resources/config.xml");
52+
53+
// Parse a 0800 message with known fields
54+
var s800 = "0800201080000000000012345611251125";
55+
var m = mfact.ParseMessage(s800.GetSignedBytes(), 0);
56+
Assert.NotNull(m);
57+
Assert.True(m.HasField(3));
58+
Assert.True(m.HasField(12));
59+
Assert.True(m.HasField(17));
60+
}
61+
62+
[Fact]
63+
public void ConfigureFromClasspathConfig_ExtendsTemplate_InheritsFields()
64+
{
65+
var mfact = new MessageFactory<IsoMessage>();
66+
ConfigParser.ConfigureFromClasspathConfig(mfact, "/Resources/config.xml");
67+
68+
var m200 = mfact.GetMessageTemplate(0x200);
69+
var m400 = mfact.GetMessageTemplate(0x400);
70+
Assert.NotNull(m400);
71+
72+
// 0400 extends 0200 — inherited fields should match
73+
for (var i = 2; i < 89; i++)
74+
{
75+
var v = m200.GetField(i);
76+
if (v == null)
77+
Assert.False(m400.HasField(i));
78+
else
79+
Assert.True(m400.HasField(i));
80+
}
81+
}
82+
83+
[Fact]
84+
public void ConfigureFromClasspathConfig_ExtendsTemplate_ExcludesFields()
85+
{
86+
var mfact = new MessageFactory<IsoMessage>();
87+
ConfigParser.ConfigureFromClasspathConfig(mfact, "/Resources/config.xml");
88+
89+
var m200 = mfact.GetMessageTemplate(0x200);
90+
var m400 = mfact.GetMessageTemplate(0x400);
91+
92+
// field 102 is excluded in 0400 but present in 0200
93+
Assert.True(m200.HasField(102));
94+
Assert.False(m400.HasField(102));
95+
96+
// field 90 is added only in 0400
97+
Assert.False(m200.HasField(90));
98+
Assert.True(m400.HasField(90));
99+
}
100+
101+
[Fact]
102+
public void ConfigureFromClasspathConfig_BinaryHeader_IsSetCorrectly()
103+
{
104+
var mfact = new MessageFactory<IsoMessage>();
105+
ConfigParser.ConfigureFromClasspathConfig(mfact, "/Resources/config.xml");
106+
107+
// 0280 has a binary header (binary="true" with "ffffffff")
108+
var binHeader = mfact.GetBinaryIsoHeader(0x280);
109+
Assert.NotNull(binHeader);
110+
Assert.Equal(4, binHeader.Length);
111+
Assert.Equal(unchecked((sbyte) 0xff), binHeader[0]);
112+
}
113+
114+
[Fact]
115+
public void ConfigureFromClasspathConfig_ExtendsParseGuide_InheritsAndExcludesFields()
116+
{
117+
var mfact = new MessageFactory<IsoMessage>();
118+
ConfigParser.ConfigureFromClasspathConfig(mfact, "/Resources/config.xml");
119+
120+
var s810 = "08102010000002000000123456112500";
121+
var m = mfact.ParseMessage(s810.GetSignedBytes(), 0);
122+
Assert.NotNull(m);
123+
// 0810 extends 0800 but excludes field 17 and adds field 39
124+
Assert.False(m.HasField(17));
125+
Assert.True(m.HasField(39));
126+
}
127+
128+
[Fact]
129+
public void ConfigureFromClasspathConfig_FileNotFound_ThrowsIoException()
130+
{
131+
var mfact = new MessageFactory<IsoMessage>();
132+
Assert.Throws<IOException>(() =>
133+
ConfigParser.ConfigureFromClasspathConfig(mfact, "/Resources/nonexistent.xml"));
134+
}
135+
136+
[Fact]
137+
public void ConfigureFromClasspathConfig_CanBeCalledMultipleTimes()
138+
{
139+
// Calling configure multiple times should not throw
140+
var mfact = new MessageFactory<IsoMessage>();
141+
ConfigParser.ConfigureFromClasspathConfig(mfact, "/Resources/config.xml");
142+
ConfigParser.ConfigureFromClasspathConfig(mfact, "/Resources/config.xml");
143+
Assert.NotNull(mfact.GetIsoHeader(0x800));
144+
}
145+
146+
// ═══════════════════════════════════════════════════════════════════════
147+
// ConfigureFromDefault
148+
// ═══════════════════════════════════════════════════════════════════════
149+
150+
[Fact]
151+
public void ConfigureFromDefault_WhenFileNotFound_ThrowsFileNotFoundException()
152+
{
153+
// "n8583.xml" does not exist in the test output directory
154+
var mfact = new MessageFactory<IsoMessage>();
155+
Assert.Throws<FileNotFoundException>(() => ConfigParser.ConfigureFromDefault(mfact));
156+
}
157+
158+
// ═══════════════════════════════════════════════════════════════════════
159+
// CreateDefault
160+
// ═══════════════════════════════════════════════════════════════════════
161+
162+
[Fact]
163+
public void CreateDefault_WhenFileNotFound_Throws()
164+
{
165+
// Same as ConfigureFromDefault — n8583.xml doesn't exist
166+
Assert.ThrowsAny<Exception>(() => ConfigParser.CreateDefault());
167+
}
168+
169+
// ═══════════════════════════════════════════════════════════════════════
170+
// Composite XML configs (reuse of composites.xml)
171+
// ═══════════════════════════════════════════════════════════════════════
172+
173+
[Fact]
174+
public void ConfigureFromClasspathConfig_CompositeConfig_ParsesCompositeFields()
175+
{
176+
var mfact = new MessageFactory<IsoMessage>();
177+
ConfigParser.ConfigureFromClasspathConfig(mfact, "/Resources/composites.xml");
178+
179+
var m = mfact.ParseMessage("01000040000000000000016one 03two12345.".GetSignedBytes(), 0);
180+
Assert.NotNull(m);
181+
var f = (CompositeField) m.GetObjectValue(10);
182+
Assert.NotNull(f);
183+
Assert.Equal(4, f.Values.Count);
184+
}
185+
186+
[Fact]
187+
public void ConfigureFromClasspathConfig_IssueConfig_ParsesExtendedParseGuide()
188+
{
189+
var mfact = new MessageFactory<IsoMessage>();
190+
ConfigParser.ConfigureFromClasspathConfig(mfact, "/Resources/issue34.xml");
191+
192+
var m200 = "0200422000000880800001X1231235959123456101010202020TERMINAL484";
193+
var m = mfact.ParseMessage(m200.GetSignedBytes(), 0);
194+
Assert.NotNull(m);
195+
Assert.Equal("X", m.GetObjectValue(2));
196+
Assert.Equal("123456", m.GetObjectValue(11));
197+
}
198+
}
199+
}

0 commit comments

Comments
 (0)