@padosoft/react-native-ecr17 0.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/Ecr17.podspec +39 -39
  2. package/README.md +348 -348
  3. package/android/CMakeLists.txt +41 -41
  4. package/android/build.gradle +148 -148
  5. package/android/fix-prefab.gradle +50 -50
  6. package/android/gradle.properties +5 -5
  7. package/android/src/main/AndroidManifest.xml +2 -2
  8. package/android/src/main/cpp/cpp-adapter.cpp +8 -8
  9. package/android/src/main/java/com/margelo/nitro/ecr17/HybridEcr17Transport.kt +233 -233
  10. package/android/src/main/java/com/padosoft/ecr17/Ecr17Package.kt +30 -30
  11. package/cpp/Ecr17.hpp +1 -1
  12. package/cpp/Ecr17Client/HybridEcr17Client.cpp +598 -598
  13. package/cpp/Ecr17Client/HybridEcr17Client.hpp +85 -85
  14. package/cpp/Ecr17Protocol/Ecr17Protocol.cpp +277 -277
  15. package/cpp/Ecr17Protocol/Ecr17Protocol.hpp +103 -103
  16. package/cpp/Ecr17Response/Ecr17Response.cpp +155 -155
  17. package/cpp/Ecr17Response/Ecr17Response.hpp +113 -113
  18. package/cpp/Lcr/Lcr.cpp +42 -42
  19. package/cpp/Lcr/Lcr.hpp +21 -21
  20. package/cpp/PacketCodec/PacketCodec.cpp +145 -145
  21. package/cpp/PacketCodec/PacketCodec.hpp +47 -47
  22. package/cpp/Session/Ecr17Session.cpp +260 -260
  23. package/cpp/Session/Ecr17Session.hpp +97 -97
  24. package/cpp/Session/RetryPolicy.hpp +23 -23
  25. package/cpp/Transport/FakeTransport.hpp +95 -95
  26. package/cpp/Transport/NativeTransportAdapter.cpp +42 -42
  27. package/cpp/Transport/NativeTransportAdapter.hpp +32 -32
  28. package/cpp/Transport/Transport.hpp +30 -30
  29. package/cpp/tests/CMakeLists.txt +55 -55
  30. package/cpp/tests/PosixTcpTransport.hpp +105 -105
  31. package/cpp/tests/stubs/LrcMode.hpp +25 -25
  32. package/cpp/tests/test_flows.cpp +148 -148
  33. package/cpp/tests/test_integration_terminal.cpp +72 -72
  34. package/cpp/tests/test_lrc.cpp +66 -66
  35. package/cpp/tests/test_packet_codec.cpp +164 -164
  36. package/cpp/tests/test_protocol.cpp +102 -102
  37. package/cpp/tests/test_protocol_commands.cpp +190 -190
  38. package/cpp/tests/test_response.cpp +164 -164
  39. package/cpp/tests/test_retry_policy.cpp +28 -28
  40. package/cpp/tests/test_session.cpp +262 -262
  41. package/ios/HybridEcr17Transport.swift +103 -103
  42. package/lib/commonjs/index.js +50 -0
  43. package/lib/commonjs/index.js.map +1 -0
  44. package/lib/commonjs/package.json +1 -0
  45. package/lib/commonjs/specs/client.nitro.js +17 -0
  46. package/lib/commonjs/specs/client.nitro.js.map +1 -0
  47. package/lib/commonjs/specs/transport.nitro.js +6 -0
  48. package/lib/commonjs/specs/transport.nitro.js.map +1 -0
  49. package/lib/commonjs/types/client.js +2 -0
  50. package/lib/commonjs/types/client.js.map +1 -0
  51. package/lib/commonjs/utils/client.js +13 -0
  52. package/lib/commonjs/utils/client.js.map +1 -0
  53. package/lib/module/index.js +7 -0
  54. package/lib/module/index.js.map +1 -0
  55. package/lib/module/specs/client.nitro.js +13 -0
  56. package/lib/module/specs/client.nitro.js.map +1 -0
  57. package/lib/module/specs/transport.nitro.js +4 -0
  58. package/lib/module/specs/transport.nitro.js.map +1 -0
  59. package/lib/module/types/client.js +2 -0
  60. package/lib/module/types/client.js.map +1 -0
  61. package/lib/module/utils/client.js +9 -0
  62. package/lib/module/utils/client.js.map +1 -0
  63. package/lib/typescript/src/index.d.ts +5 -0
  64. package/lib/typescript/src/index.d.ts.map +1 -0
  65. package/lib/typescript/src/specs/client.nitro.d.ts +63 -0
  66. package/lib/typescript/src/specs/client.nitro.d.ts.map +1 -0
  67. package/lib/typescript/src/specs/transport.nitro.d.ts +13 -0
  68. package/lib/typescript/src/specs/transport.nitro.d.ts.map +1 -0
  69. package/lib/typescript/src/types/client.d.ts +138 -0
  70. package/lib/typescript/src/types/client.d.ts.map +1 -0
  71. package/lib/typescript/src/utils/client.d.ts +3 -0
  72. package/lib/typescript/src/utils/client.d.ts.map +1 -0
  73. package/nitro.json +30 -30
  74. package/package.json +4 -4
  75. package/react-native.config.js +18 -18
  76. package/src/index.ts +4 -4
  77. package/src/specs/client.nitro.ts +102 -102
  78. package/src/specs/transport.nitro.ts +25 -25
  79. package/src/types/client.ts +196 -196
  80. package/src/utils/client.ts +10 -10
@@ -1,164 +1,164 @@
1
- // Tests for the ECR17 response parsers. Payloads are synthesized field-by-field
2
- // at the exact 1-based offsets from the spec response tables in docs/. Helpers
3
- // guarantee each field's width so offsets can't drift from a miscounted space.
4
-
5
- #include <gtest/gtest.h>
6
-
7
- #include <string>
8
-
9
- #include "Ecr17Response/Ecr17Response.hpp"
10
-
11
- using namespace margelo::nitro::ecr17;
12
-
13
- namespace {
14
-
15
- // Left-justified field, right-padded with spaces to `width` (alpha fields).
16
- std::string a(const std::string& value, size_t width) {
17
- std::string s = value;
18
- s.resize(width, ' ');
19
- return s;
20
- }
21
-
22
- // Right-justified numeric field, left-padded with '0' to `width`.
23
- std::string n(const std::string& value, size_t width) {
24
- return std::string(width - value.size(), '0') + value;
25
- }
26
-
27
- } // namespace
28
-
29
- TEST(Response, PaymentPositive) {
30
- std::string p = a("12345678", 8) + "0" + "E" + "00" + // header + result
31
- n("4111111111", 19) + // PAN(19)
32
- a("ICC", 3) + a("ABC123", 6) + "2111520" + // txType authCode dateTime
33
- "2" + // cardType
34
- a("ACQ", 11) + n("42", 6) + n("99", 6); // acquirer STAN idOnline
35
- PaymentResponse r = Ecr17Response::parsePayment(p);
36
- EXPECT_EQ(r.outcome, Outcome::Ok);
37
- EXPECT_EQ(r.resultCode, "00");
38
- EXPECT_EQ(r.pan, n("4111111111", 19));
39
- EXPECT_EQ(r.transactionType, "ICC");
40
- EXPECT_EQ(r.authCode, "ABC123");
41
- EXPECT_EQ(r.hostDateTime, "2111520");
42
- EXPECT_EQ(r.cardType, "2");
43
- EXPECT_EQ(r.acquirerId, "ACQ");
44
- EXPECT_EQ(r.stan, "000042");
45
- EXPECT_EQ(r.onlineId, "000099");
46
- EXPECT_FALSE(r.currency.applied);
47
- }
48
-
49
- TEST(Response, PaymentNegative) {
50
- std::string p = a("12345678", 8) + "0" + "E" + "01" + a("CARTA RIFIUTATA", 24) +
51
- n("", 11) + // reserved 37-47
52
- "3" + a("AC2", 11) + n("7", 6) + n("3", 6);
53
- PaymentResponse r = Ecr17Response::parsePayment(p);
54
- EXPECT_EQ(r.outcome, Outcome::Ko);
55
- EXPECT_EQ(r.resultCode, "01");
56
- EXPECT_EQ(r.errorDescription, "CARTA RIFIUTATA");
57
- EXPECT_EQ(r.cardType, "3");
58
- EXPECT_EQ(r.stan, "000007");
59
- }
60
-
61
- TEST(Response, PaymentWithCurrencyExchange) {
62
- std::string base = a("12345678", 8) + "0" + "V" + "00" + n("4111111111", 19) + a("ICC", 3) +
63
- a("ABC123", 6) + "2111520" + "2" + a("ACQ", 11) + n("42", 6) + n("99", 6);
64
- // actionCode(3) origAmount(8) flag(1) rate(8) ccy(3) amount(12) precision(1)
65
- std::string p = base + "000" + n("650", 8) + "1" + n("12345", 8) + "USD" + n("650", 12) + "2";
66
- PaymentResponse r = Ecr17Response::parsePayment(p);
67
- EXPECT_EQ(r.outcome, Outcome::Ok);
68
- EXPECT_TRUE(r.currency.applied);
69
- EXPECT_EQ(r.currency.rate, "00012345");
70
- EXPECT_EQ(r.currency.currencyCode, "USD");
71
- EXPECT_EQ(r.currency.amount, "000000000650");
72
- EXPECT_EQ(r.currency.precision, "2");
73
- }
74
-
75
- TEST(Response, Status) {
76
- std::string p = a("12345678", 8) + "0" + "s" + n("", 10) + // reserved 11-20
77
- "0102251530" + "2" + "V1.2.3"; // dateTime status sw
78
- StatusResponse r = Ecr17Response::parseStatus(p);
79
- EXPECT_EQ(r.terminalId, "12345678");
80
- EXPECT_EQ(r.dateTimeRaw, "0102251530");
81
- EXPECT_EQ(r.status, 2);
82
- EXPECT_EQ(r.softwareRelease, "V1.2.3");
83
- }
84
-
85
- TEST(Response, Totals) {
86
- std::string p = a("12345678", 8) + "0" + "T" + "00" + n("123456", 16) + n("", 6);
87
- TotalsResponse r = Ecr17Response::parseTotals(p);
88
- EXPECT_EQ(r.outcome, Outcome::Ok);
89
- EXPECT_EQ(r.posTotal, n("123456", 16));
90
- }
91
-
92
- TEST(Response, ClosePositive) {
93
- std::string p = a("12345678", 8) + "0" + "C" + "00" + n("1000", 16) + n("1000", 16);
94
- CloseResponse r = Ecr17Response::parseClose(p);
95
- EXPECT_EQ(r.outcome, Outcome::Ok);
96
- EXPECT_EQ(r.posTotal, n("1000", 16));
97
- EXPECT_EQ(r.hostTotal, n("1000", 16));
98
- }
99
-
100
- TEST(Response, CloseNegative) {
101
- std::string p = a("12345678", 8) + "0" + "C" + "01" + a("SBILANCIO", 19) + "100";
102
- CloseResponse r = Ecr17Response::parseClose(p);
103
- EXPECT_EQ(r.outcome, Outcome::Ko);
104
- EXPECT_EQ(r.errorDescription, "SBILANCIO");
105
- EXPECT_EQ(r.actionCode, "100");
106
- }
107
-
108
- TEST(Response, PreAuthPositive) {
109
- std::string p = a("12345678", 8) + "0" + "e" + "00" + n("4111111111", 19) + a("CLI", 3) +
110
- a("AUTH01", 6) + n("50000", 8) + n("123", 9) + "000" + "2111520";
111
- PreAuthResponse r = Ecr17Response::parsePreAuth(p);
112
- EXPECT_EQ(r.outcome, Outcome::Ok);
113
- EXPECT_EQ(r.transactionType, "CLI");
114
- EXPECT_EQ(r.authCode, "AUTH01");
115
- EXPECT_EQ(r.preAuthorizedAmount, "00050000");
116
- EXPECT_EQ(r.preAuthCode, "000000123");
117
- EXPECT_EQ(r.hostDateTime, "2111520");
118
- }
119
-
120
- // Regression: on an approved pre-auth the amount field occupies positions 41-48,
121
- // so its last digit sits exactly where cardType would be read. An amount ending
122
- // in 1/2/3 must NOT be surfaced as debit/credit/other. cardType is only
123
- // meaningful for the KO layout.
124
- TEST(Response, PreAuthPositiveDoesNotLeakAmountDigitAsCardType) {
125
- std::string p = a("12345678", 8) + "0" + "e" + "00" + n("4111111111", 19) + a("CLI", 3) +
126
- a("AUTH01", 6) + n("50001", 8) + n("123", 9) + "000" + "2111520";
127
- PreAuthResponse r = Ecr17Response::parsePreAuth(p);
128
- EXPECT_EQ(r.outcome, Outcome::Ok);
129
- EXPECT_EQ(r.preAuthorizedAmount, "00050001"); // ends in '1'
130
- EXPECT_EQ(r.cardType, ""); // must stay empty, not "1"
131
- }
132
-
133
- TEST(Response, Vas) {
134
- std::string xml =
135
- "<ecrres><p k=\"RESPID\">0</p><p k=\"RESPMSG\">OK-APPROVED</p>"
136
- "<p k=\"ORDER_ID\">ABC123</p></ecrres>";
137
- // header(10) reserved(4) concatFlag(1) idMessage(3) filler-to-pos27(8) xml
138
- std::string p = a("12345678", 8) + "0" + "K" + n("", 4) + "0" + "001" + n("", 8) + xml;
139
- VasResponse r = Ecr17Response::parseVas(p);
140
- EXPECT_FALSE(r.moreMessages);
141
- EXPECT_EQ(r.idMessage, "001");
142
- EXPECT_EQ(r.responseId, "0");
143
- EXPECT_EQ(r.responseMessage, "OK-APPROVED");
144
- EXPECT_EQ(r.orderId, "ABC123");
145
- EXPECT_EQ(r.rawXml, xml);
146
- }
147
-
148
- TEST(Response, DefensiveOnShortOrEmptyPayload) {
149
- PaymentResponse r = Ecr17Response::parsePayment("");
150
- EXPECT_EQ(r.outcome, Outcome::Unknown);
151
- EXPECT_EQ(r.resultCode, "");
152
- EXPECT_EQ(r.pan, "");
153
-
154
- StatusResponse s = Ecr17Response::parseStatus("123"); // truncated, must not crash
155
- EXPECT_EQ(s.status, -1);
156
- }
157
-
158
- TEST(Response, OutcomeMapping) {
159
- EXPECT_EQ(outcomeFromCode("00"), Outcome::Ok);
160
- EXPECT_EQ(outcomeFromCode("01"), Outcome::Ko);
161
- EXPECT_EQ(outcomeFromCode("05"), Outcome::CardNotPresent);
162
- EXPECT_EQ(outcomeFromCode("09"), Outcome::UnknownTag);
163
- EXPECT_EQ(outcomeFromCode("zz"), Outcome::Unknown);
164
- }
1
+ // Tests for the ECR17 response parsers. Payloads are synthesized field-by-field
2
+ // at the exact 1-based offsets from the spec response tables in docs/. Helpers
3
+ // guarantee each field's width so offsets can't drift from a miscounted space.
4
+
5
+ #include <gtest/gtest.h>
6
+
7
+ #include <string>
8
+
9
+ #include "Ecr17Response/Ecr17Response.hpp"
10
+
11
+ using namespace margelo::nitro::ecr17;
12
+
13
+ namespace {
14
+
15
+ // Left-justified field, right-padded with spaces to `width` (alpha fields).
16
+ std::string a(const std::string& value, size_t width) {
17
+ std::string s = value;
18
+ s.resize(width, ' ');
19
+ return s;
20
+ }
21
+
22
+ // Right-justified numeric field, left-padded with '0' to `width`.
23
+ std::string n(const std::string& value, size_t width) {
24
+ return std::string(width - value.size(), '0') + value;
25
+ }
26
+
27
+ } // namespace
28
+
29
+ TEST(Response, PaymentPositive) {
30
+ std::string p = a("12345678", 8) + "0" + "E" + "00" + // header + result
31
+ n("4111111111", 19) + // PAN(19)
32
+ a("ICC", 3) + a("ABC123", 6) + "2111520" + // txType authCode dateTime
33
+ "2" + // cardType
34
+ a("ACQ", 11) + n("42", 6) + n("99", 6); // acquirer STAN idOnline
35
+ PaymentResponse r = Ecr17Response::parsePayment(p);
36
+ EXPECT_EQ(r.outcome, Outcome::Ok);
37
+ EXPECT_EQ(r.resultCode, "00");
38
+ EXPECT_EQ(r.pan, n("4111111111", 19));
39
+ EXPECT_EQ(r.transactionType, "ICC");
40
+ EXPECT_EQ(r.authCode, "ABC123");
41
+ EXPECT_EQ(r.hostDateTime, "2111520");
42
+ EXPECT_EQ(r.cardType, "2");
43
+ EXPECT_EQ(r.acquirerId, "ACQ");
44
+ EXPECT_EQ(r.stan, "000042");
45
+ EXPECT_EQ(r.onlineId, "000099");
46
+ EXPECT_FALSE(r.currency.applied);
47
+ }
48
+
49
+ TEST(Response, PaymentNegative) {
50
+ std::string p = a("12345678", 8) + "0" + "E" + "01" + a("CARTA RIFIUTATA", 24) +
51
+ n("", 11) + // reserved 37-47
52
+ "3" + a("AC2", 11) + n("7", 6) + n("3", 6);
53
+ PaymentResponse r = Ecr17Response::parsePayment(p);
54
+ EXPECT_EQ(r.outcome, Outcome::Ko);
55
+ EXPECT_EQ(r.resultCode, "01");
56
+ EXPECT_EQ(r.errorDescription, "CARTA RIFIUTATA");
57
+ EXPECT_EQ(r.cardType, "3");
58
+ EXPECT_EQ(r.stan, "000007");
59
+ }
60
+
61
+ TEST(Response, PaymentWithCurrencyExchange) {
62
+ std::string base = a("12345678", 8) + "0" + "V" + "00" + n("4111111111", 19) + a("ICC", 3) +
63
+ a("ABC123", 6) + "2111520" + "2" + a("ACQ", 11) + n("42", 6) + n("99", 6);
64
+ // actionCode(3) origAmount(8) flag(1) rate(8) ccy(3) amount(12) precision(1)
65
+ std::string p = base + "000" + n("650", 8) + "1" + n("12345", 8) + "USD" + n("650", 12) + "2";
66
+ PaymentResponse r = Ecr17Response::parsePayment(p);
67
+ EXPECT_EQ(r.outcome, Outcome::Ok);
68
+ EXPECT_TRUE(r.currency.applied);
69
+ EXPECT_EQ(r.currency.rate, "00012345");
70
+ EXPECT_EQ(r.currency.currencyCode, "USD");
71
+ EXPECT_EQ(r.currency.amount, "000000000650");
72
+ EXPECT_EQ(r.currency.precision, "2");
73
+ }
74
+
75
+ TEST(Response, Status) {
76
+ std::string p = a("12345678", 8) + "0" + "s" + n("", 10) + // reserved 11-20
77
+ "0102251530" + "2" + "V1.2.3"; // dateTime status sw
78
+ StatusResponse r = Ecr17Response::parseStatus(p);
79
+ EXPECT_EQ(r.terminalId, "12345678");
80
+ EXPECT_EQ(r.dateTimeRaw, "0102251530");
81
+ EXPECT_EQ(r.status, 2);
82
+ EXPECT_EQ(r.softwareRelease, "V1.2.3");
83
+ }
84
+
85
+ TEST(Response, Totals) {
86
+ std::string p = a("12345678", 8) + "0" + "T" + "00" + n("123456", 16) + n("", 6);
87
+ TotalsResponse r = Ecr17Response::parseTotals(p);
88
+ EXPECT_EQ(r.outcome, Outcome::Ok);
89
+ EXPECT_EQ(r.posTotal, n("123456", 16));
90
+ }
91
+
92
+ TEST(Response, ClosePositive) {
93
+ std::string p = a("12345678", 8) + "0" + "C" + "00" + n("1000", 16) + n("1000", 16);
94
+ CloseResponse r = Ecr17Response::parseClose(p);
95
+ EXPECT_EQ(r.outcome, Outcome::Ok);
96
+ EXPECT_EQ(r.posTotal, n("1000", 16));
97
+ EXPECT_EQ(r.hostTotal, n("1000", 16));
98
+ }
99
+
100
+ TEST(Response, CloseNegative) {
101
+ std::string p = a("12345678", 8) + "0" + "C" + "01" + a("SBILANCIO", 19) + "100";
102
+ CloseResponse r = Ecr17Response::parseClose(p);
103
+ EXPECT_EQ(r.outcome, Outcome::Ko);
104
+ EXPECT_EQ(r.errorDescription, "SBILANCIO");
105
+ EXPECT_EQ(r.actionCode, "100");
106
+ }
107
+
108
+ TEST(Response, PreAuthPositive) {
109
+ std::string p = a("12345678", 8) + "0" + "e" + "00" + n("4111111111", 19) + a("CLI", 3) +
110
+ a("AUTH01", 6) + n("50000", 8) + n("123", 9) + "000" + "2111520";
111
+ PreAuthResponse r = Ecr17Response::parsePreAuth(p);
112
+ EXPECT_EQ(r.outcome, Outcome::Ok);
113
+ EXPECT_EQ(r.transactionType, "CLI");
114
+ EXPECT_EQ(r.authCode, "AUTH01");
115
+ EXPECT_EQ(r.preAuthorizedAmount, "00050000");
116
+ EXPECT_EQ(r.preAuthCode, "000000123");
117
+ EXPECT_EQ(r.hostDateTime, "2111520");
118
+ }
119
+
120
+ // Regression: on an approved pre-auth the amount field occupies positions 41-48,
121
+ // so its last digit sits exactly where cardType would be read. An amount ending
122
+ // in 1/2/3 must NOT be surfaced as debit/credit/other. cardType is only
123
+ // meaningful for the KO layout.
124
+ TEST(Response, PreAuthPositiveDoesNotLeakAmountDigitAsCardType) {
125
+ std::string p = a("12345678", 8) + "0" + "e" + "00" + n("4111111111", 19) + a("CLI", 3) +
126
+ a("AUTH01", 6) + n("50001", 8) + n("123", 9) + "000" + "2111520";
127
+ PreAuthResponse r = Ecr17Response::parsePreAuth(p);
128
+ EXPECT_EQ(r.outcome, Outcome::Ok);
129
+ EXPECT_EQ(r.preAuthorizedAmount, "00050001"); // ends in '1'
130
+ EXPECT_EQ(r.cardType, ""); // must stay empty, not "1"
131
+ }
132
+
133
+ TEST(Response, Vas) {
134
+ std::string xml =
135
+ "<ecrres><p k=\"RESPID\">0</p><p k=\"RESPMSG\">OK-APPROVED</p>"
136
+ "<p k=\"ORDER_ID\">ABC123</p></ecrres>";
137
+ // header(10) reserved(4) concatFlag(1) idMessage(3) filler-to-pos27(8) xml
138
+ std::string p = a("12345678", 8) + "0" + "K" + n("", 4) + "0" + "001" + n("", 8) + xml;
139
+ VasResponse r = Ecr17Response::parseVas(p);
140
+ EXPECT_FALSE(r.moreMessages);
141
+ EXPECT_EQ(r.idMessage, "001");
142
+ EXPECT_EQ(r.responseId, "0");
143
+ EXPECT_EQ(r.responseMessage, "OK-APPROVED");
144
+ EXPECT_EQ(r.orderId, "ABC123");
145
+ EXPECT_EQ(r.rawXml, xml);
146
+ }
147
+
148
+ TEST(Response, DefensiveOnShortOrEmptyPayload) {
149
+ PaymentResponse r = Ecr17Response::parsePayment("");
150
+ EXPECT_EQ(r.outcome, Outcome::Unknown);
151
+ EXPECT_EQ(r.resultCode, "");
152
+ EXPECT_EQ(r.pan, "");
153
+
154
+ StatusResponse s = Ecr17Response::parseStatus("123"); // truncated, must not crash
155
+ EXPECT_EQ(s.status, -1);
156
+ }
157
+
158
+ TEST(Response, OutcomeMapping) {
159
+ EXPECT_EQ(outcomeFromCode("00"), Outcome::Ok);
160
+ EXPECT_EQ(outcomeFromCode("01"), Outcome::Ko);
161
+ EXPECT_EQ(outcomeFromCode("05"), Outcome::CardNotPresent);
162
+ EXPECT_EQ(outcomeFromCode("09"), Outcome::UnknownTag);
163
+ EXPECT_EQ(outcomeFromCode("zz"), Outcome::Unknown);
164
+ }
@@ -1,28 +1,28 @@
1
- // Money-critical safety tests for the auto-reconnect retry decision.
2
- // The terminal handles real payments, so a financial command must NEVER be
3
- // blindly re-sent after a connection drop (double-charge risk).
4
-
5
- #include <gtest/gtest.h>
6
-
7
- #include "Session/RetryPolicy.hpp"
8
-
9
- using margelo::nitro::ecr17::shouldRetryAfterReconnect;
10
-
11
- // A financial command (safeToRetry == false) must never be retried, regardless
12
- // of autoReconnect / drop state. This is the invariant that prevents double
13
- // charging; recovery is via sendLastResult ('G'), not a re-send.
14
- TEST(RetryPolicy, FinancialCommandIsNeverRetried) {
15
- EXPECT_FALSE(shouldRetryAfterReconnect(/*autoReconnect=*/true, /*dropped=*/true, /*safe=*/false));
16
- EXPECT_FALSE(shouldRetryAfterReconnect(/*autoReconnect=*/false, /*dropped=*/true, /*safe=*/false));
17
- EXPECT_FALSE(shouldRetryAfterReconnect(/*autoReconnect=*/true, /*dropped=*/false, /*safe=*/false));
18
- EXPECT_FALSE(shouldRetryAfterReconnect(/*autoReconnect=*/false, /*dropped=*/false, /*safe=*/false));
19
- }
20
-
21
- // A safe/idempotent command is retried ONLY when autoReconnect is on AND the
22
- // transport actually dropped.
23
- TEST(RetryPolicy, SafeCommandRetriedOnlyOnReconnectAfterDrop) {
24
- EXPECT_TRUE(shouldRetryAfterReconnect(/*autoReconnect=*/true, /*dropped=*/true, /*safe=*/true));
25
- EXPECT_FALSE(shouldRetryAfterReconnect(/*autoReconnect=*/false, /*dropped=*/true, /*safe=*/true));
26
- EXPECT_FALSE(shouldRetryAfterReconnect(/*autoReconnect=*/true, /*dropped=*/false, /*safe=*/true));
27
- EXPECT_FALSE(shouldRetryAfterReconnect(/*autoReconnect=*/false, /*dropped=*/false, /*safe=*/true));
28
- }
1
+ // Money-critical safety tests for the auto-reconnect retry decision.
2
+ // The terminal handles real payments, so a financial command must NEVER be
3
+ // blindly re-sent after a connection drop (double-charge risk).
4
+
5
+ #include <gtest/gtest.h>
6
+
7
+ #include "Session/RetryPolicy.hpp"
8
+
9
+ using margelo::nitro::ecr17::shouldRetryAfterReconnect;
10
+
11
+ // A financial command (safeToRetry == false) must never be retried, regardless
12
+ // of autoReconnect / drop state. This is the invariant that prevents double
13
+ // charging; recovery is via sendLastResult ('G'), not a re-send.
14
+ TEST(RetryPolicy, FinancialCommandIsNeverRetried) {
15
+ EXPECT_FALSE(shouldRetryAfterReconnect(/*autoReconnect=*/true, /*dropped=*/true, /*safe=*/false));
16
+ EXPECT_FALSE(shouldRetryAfterReconnect(/*autoReconnect=*/false, /*dropped=*/true, /*safe=*/false));
17
+ EXPECT_FALSE(shouldRetryAfterReconnect(/*autoReconnect=*/true, /*dropped=*/false, /*safe=*/false));
18
+ EXPECT_FALSE(shouldRetryAfterReconnect(/*autoReconnect=*/false, /*dropped=*/false, /*safe=*/false));
19
+ }
20
+
21
+ // A safe/idempotent command is retried ONLY when autoReconnect is on AND the
22
+ // transport actually dropped.
23
+ TEST(RetryPolicy, SafeCommandRetriedOnlyOnReconnectAfterDrop) {
24
+ EXPECT_TRUE(shouldRetryAfterReconnect(/*autoReconnect=*/true, /*dropped=*/true, /*safe=*/true));
25
+ EXPECT_FALSE(shouldRetryAfterReconnect(/*autoReconnect=*/false, /*dropped=*/true, /*safe=*/true));
26
+ EXPECT_FALSE(shouldRetryAfterReconnect(/*autoReconnect=*/true, /*dropped=*/false, /*safe=*/true));
27
+ EXPECT_FALSE(shouldRetryAfterReconnect(/*autoReconnect=*/false, /*dropped=*/false, /*safe=*/true));
28
+ }