rippled
Loading...
Searching...
No Matches
LoanSetTests.cpp
1// Auto-generated unit tests for transaction LoanSet
2
3
4#include <gtest/gtest.h>
5
6#include <protocol_autogen/TestHelpers.h>
7
8#include <xrpl/protocol/SecretKey.h>
9#include <xrpl/protocol/Seed.h>
10#include <xrpl/protocol/STTx.h>
11#include <xrpl/protocol_autogen/transactions/LoanSet.h>
12#include <xrpl/protocol_autogen/transactions/AccountSet.h>
13
14#include <string>
15
16namespace xrpl::transactions {
17
18// 1 & 4) Set fields via builder setters, build, then read them back via
19// wrapper getters. After build(), validate() should succeed.
20TEST(TransactionsLoanSetTests, BuilderSettersRoundTrip)
21{
22 // Generate a deterministic keypair for signing
23 auto const [publicKey, secretKey] =
25
26 // Common transaction fields
27 auto const accountValue = calcAccountID(publicKey);
28 std::uint32_t const sequenceValue = 1;
29 auto const feeValue = canonical_AMOUNT();
30
31 // Transaction-specific field values
32 auto const loanBrokerIDValue = canonical_UINT256();
33 auto const dataValue = canonical_VL();
34 auto const counterpartyValue = canonical_ACCOUNT();
35 auto const counterpartySignatureValue = canonical_OBJECT();
36 auto const loanOriginationFeeValue = canonical_NUMBER();
37 auto const loanServiceFeeValue = canonical_NUMBER();
38 auto const latePaymentFeeValue = canonical_NUMBER();
39 auto const closePaymentFeeValue = canonical_NUMBER();
40 auto const overpaymentFeeValue = canonical_UINT32();
41 auto const interestRateValue = canonical_UINT32();
42 auto const lateInterestRateValue = canonical_UINT32();
43 auto const closeInterestRateValue = canonical_UINT32();
44 auto const overpaymentInterestRateValue = canonical_UINT32();
45 auto const principalRequestedValue = canonical_NUMBER();
46 auto const paymentTotalValue = canonical_UINT32();
47 auto const paymentIntervalValue = canonical_UINT32();
48 auto const gracePeriodValue = canonical_UINT32();
49
50 LoanSetBuilder builder{
51 accountValue,
52 loanBrokerIDValue,
53 principalRequestedValue,
54 sequenceValue,
55 feeValue
56 };
57
58 // Set optional fields
59 builder.setData(dataValue);
60 builder.setCounterparty(counterpartyValue);
61 builder.setCounterpartySignature(counterpartySignatureValue);
62 builder.setLoanOriginationFee(loanOriginationFeeValue);
63 builder.setLoanServiceFee(loanServiceFeeValue);
64 builder.setLatePaymentFee(latePaymentFeeValue);
65 builder.setClosePaymentFee(closePaymentFeeValue);
66 builder.setOverpaymentFee(overpaymentFeeValue);
67 builder.setInterestRate(interestRateValue);
68 builder.setLateInterestRate(lateInterestRateValue);
69 builder.setCloseInterestRate(closeInterestRateValue);
70 builder.setOverpaymentInterestRate(overpaymentInterestRateValue);
71 builder.setPaymentTotal(paymentTotalValue);
72 builder.setPaymentInterval(paymentIntervalValue);
73 builder.setGracePeriod(gracePeriodValue);
74
75 auto tx = builder.build(publicKey, secretKey);
76
77 std::string reason;
78 EXPECT_TRUE(tx.validate(reason)) << reason;
79
80 // Verify signing was applied
81 EXPECT_FALSE(tx.getSigningPubKey().empty());
82 EXPECT_TRUE(tx.hasTxnSignature());
83
84 // Verify common fields
85 EXPECT_EQ(tx.getAccount(), accountValue);
86 EXPECT_EQ(tx.getSequence(), sequenceValue);
87 EXPECT_EQ(tx.getFee(), feeValue);
88
89 // Verify required fields
90 {
91 auto const& expected = loanBrokerIDValue;
92 auto const actual = tx.getLoanBrokerID();
93 expectEqualField(expected, actual, "sfLoanBrokerID");
94 }
95
96 {
97 auto const& expected = principalRequestedValue;
98 auto const actual = tx.getPrincipalRequested();
99 expectEqualField(expected, actual, "sfPrincipalRequested");
100 }
101
102 // Verify optional fields
103 {
104 auto const& expected = dataValue;
105 auto const actualOpt = tx.getData();
106 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present";
107 expectEqualField(expected, *actualOpt, "sfData");
108 EXPECT_TRUE(tx.hasData());
109 }
110
111 {
112 auto const& expected = counterpartyValue;
113 auto const actualOpt = tx.getCounterparty();
114 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterparty should be present";
115 expectEqualField(expected, *actualOpt, "sfCounterparty");
116 EXPECT_TRUE(tx.hasCounterparty());
117 }
118
119 {
120 auto const& expected = counterpartySignatureValue;
121 auto const actualOpt = tx.getCounterpartySignature();
122 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterpartySignature should be present";
123 expectEqualField(expected, *actualOpt, "sfCounterpartySignature");
124 EXPECT_TRUE(tx.hasCounterpartySignature());
125 }
126
127 {
128 auto const& expected = loanOriginationFeeValue;
129 auto const actualOpt = tx.getLoanOriginationFee();
130 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanOriginationFee should be present";
131 expectEqualField(expected, *actualOpt, "sfLoanOriginationFee");
132 EXPECT_TRUE(tx.hasLoanOriginationFee());
133 }
134
135 {
136 auto const& expected = loanServiceFeeValue;
137 auto const actualOpt = tx.getLoanServiceFee();
138 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanServiceFee should be present";
139 expectEqualField(expected, *actualOpt, "sfLoanServiceFee");
140 EXPECT_TRUE(tx.hasLoanServiceFee());
141 }
142
143 {
144 auto const& expected = latePaymentFeeValue;
145 auto const actualOpt = tx.getLatePaymentFee();
146 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLatePaymentFee should be present";
147 expectEqualField(expected, *actualOpt, "sfLatePaymentFee");
148 EXPECT_TRUE(tx.hasLatePaymentFee());
149 }
150
151 {
152 auto const& expected = closePaymentFeeValue;
153 auto const actualOpt = tx.getClosePaymentFee();
154 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfClosePaymentFee should be present";
155 expectEqualField(expected, *actualOpt, "sfClosePaymentFee");
156 EXPECT_TRUE(tx.hasClosePaymentFee());
157 }
158
159 {
160 auto const& expected = overpaymentFeeValue;
161 auto const actualOpt = tx.getOverpaymentFee();
162 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOverpaymentFee should be present";
163 expectEqualField(expected, *actualOpt, "sfOverpaymentFee");
164 EXPECT_TRUE(tx.hasOverpaymentFee());
165 }
166
167 {
168 auto const& expected = interestRateValue;
169 auto const actualOpt = tx.getInterestRate();
170 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInterestRate should be present";
171 expectEqualField(expected, *actualOpt, "sfInterestRate");
172 EXPECT_TRUE(tx.hasInterestRate());
173 }
174
175 {
176 auto const& expected = lateInterestRateValue;
177 auto const actualOpt = tx.getLateInterestRate();
178 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLateInterestRate should be present";
179 expectEqualField(expected, *actualOpt, "sfLateInterestRate");
180 EXPECT_TRUE(tx.hasLateInterestRate());
181 }
182
183 {
184 auto const& expected = closeInterestRateValue;
185 auto const actualOpt = tx.getCloseInterestRate();
186 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCloseInterestRate should be present";
187 expectEqualField(expected, *actualOpt, "sfCloseInterestRate");
188 EXPECT_TRUE(tx.hasCloseInterestRate());
189 }
190
191 {
192 auto const& expected = overpaymentInterestRateValue;
193 auto const actualOpt = tx.getOverpaymentInterestRate();
194 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOverpaymentInterestRate should be present";
195 expectEqualField(expected, *actualOpt, "sfOverpaymentInterestRate");
196 EXPECT_TRUE(tx.hasOverpaymentInterestRate());
197 }
198
199 {
200 auto const& expected = paymentTotalValue;
201 auto const actualOpt = tx.getPaymentTotal();
202 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaymentTotal should be present";
203 expectEqualField(expected, *actualOpt, "sfPaymentTotal");
204 EXPECT_TRUE(tx.hasPaymentTotal());
205 }
206
207 {
208 auto const& expected = paymentIntervalValue;
209 auto const actualOpt = tx.getPaymentInterval();
210 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaymentInterval should be present";
211 expectEqualField(expected, *actualOpt, "sfPaymentInterval");
212 EXPECT_TRUE(tx.hasPaymentInterval());
213 }
214
215 {
216 auto const& expected = gracePeriodValue;
217 auto const actualOpt = tx.getGracePeriod();
218 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfGracePeriod should be present";
219 expectEqualField(expected, *actualOpt, "sfGracePeriod");
220 EXPECT_TRUE(tx.hasGracePeriod());
221 }
222
223}
224
225// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper,
226// and verify all fields match.
227TEST(TransactionsLoanSetTests, BuilderFromStTxRoundTrip)
228{
229 // Generate a deterministic keypair for signing
230 auto const [publicKey, secretKey] =
231 generateKeyPair(KeyType::secp256k1, generateSeed("testLoanSetFromTx"));
232
233 // Common transaction fields
234 auto const accountValue = calcAccountID(publicKey);
235 std::uint32_t const sequenceValue = 2;
236 auto const feeValue = canonical_AMOUNT();
237
238 // Transaction-specific field values
239 auto const loanBrokerIDValue = canonical_UINT256();
240 auto const dataValue = canonical_VL();
241 auto const counterpartyValue = canonical_ACCOUNT();
242 auto const counterpartySignatureValue = canonical_OBJECT();
243 auto const loanOriginationFeeValue = canonical_NUMBER();
244 auto const loanServiceFeeValue = canonical_NUMBER();
245 auto const latePaymentFeeValue = canonical_NUMBER();
246 auto const closePaymentFeeValue = canonical_NUMBER();
247 auto const overpaymentFeeValue = canonical_UINT32();
248 auto const interestRateValue = canonical_UINT32();
249 auto const lateInterestRateValue = canonical_UINT32();
250 auto const closeInterestRateValue = canonical_UINT32();
251 auto const overpaymentInterestRateValue = canonical_UINT32();
252 auto const principalRequestedValue = canonical_NUMBER();
253 auto const paymentTotalValue = canonical_UINT32();
254 auto const paymentIntervalValue = canonical_UINT32();
255 auto const gracePeriodValue = canonical_UINT32();
256
257 // Build an initial transaction
258 LoanSetBuilder initialBuilder{
259 accountValue,
260 loanBrokerIDValue,
261 principalRequestedValue,
262 sequenceValue,
263 feeValue
264 };
265
266 initialBuilder.setData(dataValue);
267 initialBuilder.setCounterparty(counterpartyValue);
268 initialBuilder.setCounterpartySignature(counterpartySignatureValue);
269 initialBuilder.setLoanOriginationFee(loanOriginationFeeValue);
270 initialBuilder.setLoanServiceFee(loanServiceFeeValue);
271 initialBuilder.setLatePaymentFee(latePaymentFeeValue);
272 initialBuilder.setClosePaymentFee(closePaymentFeeValue);
273 initialBuilder.setOverpaymentFee(overpaymentFeeValue);
274 initialBuilder.setInterestRate(interestRateValue);
275 initialBuilder.setLateInterestRate(lateInterestRateValue);
276 initialBuilder.setCloseInterestRate(closeInterestRateValue);
277 initialBuilder.setOverpaymentInterestRate(overpaymentInterestRateValue);
278 initialBuilder.setPaymentTotal(paymentTotalValue);
279 initialBuilder.setPaymentInterval(paymentIntervalValue);
280 initialBuilder.setGracePeriod(gracePeriodValue);
281
282 auto initialTx = initialBuilder.build(publicKey, secretKey);
283
284 // Create builder from existing STTx
285 LoanSetBuilder builderFromTx{initialTx.getSTTx()};
286
287 auto rebuiltTx = builderFromTx.build(publicKey, secretKey);
288
289 std::string reason;
290 EXPECT_TRUE(rebuiltTx.validate(reason)) << reason;
291
292 // Verify common fields
293 EXPECT_EQ(rebuiltTx.getAccount(), accountValue);
294 EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue);
295 EXPECT_EQ(rebuiltTx.getFee(), feeValue);
296
297 // Verify required fields
298 {
299 auto const& expected = loanBrokerIDValue;
300 auto const actual = rebuiltTx.getLoanBrokerID();
301 expectEqualField(expected, actual, "sfLoanBrokerID");
302 }
303
304 {
305 auto const& expected = principalRequestedValue;
306 auto const actual = rebuiltTx.getPrincipalRequested();
307 expectEqualField(expected, actual, "sfPrincipalRequested");
308 }
309
310 // Verify optional fields
311 {
312 auto const& expected = dataValue;
313 auto const actualOpt = rebuiltTx.getData();
314 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfData should be present";
315 expectEqualField(expected, *actualOpt, "sfData");
316 }
317
318 {
319 auto const& expected = counterpartyValue;
320 auto const actualOpt = rebuiltTx.getCounterparty();
321 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterparty should be present";
322 expectEqualField(expected, *actualOpt, "sfCounterparty");
323 }
324
325 {
326 auto const& expected = counterpartySignatureValue;
327 auto const actualOpt = rebuiltTx.getCounterpartySignature();
328 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCounterpartySignature should be present";
329 expectEqualField(expected, *actualOpt, "sfCounterpartySignature");
330 }
331
332 {
333 auto const& expected = loanOriginationFeeValue;
334 auto const actualOpt = rebuiltTx.getLoanOriginationFee();
335 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanOriginationFee should be present";
336 expectEqualField(expected, *actualOpt, "sfLoanOriginationFee");
337 }
338
339 {
340 auto const& expected = loanServiceFeeValue;
341 auto const actualOpt = rebuiltTx.getLoanServiceFee();
342 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLoanServiceFee should be present";
343 expectEqualField(expected, *actualOpt, "sfLoanServiceFee");
344 }
345
346 {
347 auto const& expected = latePaymentFeeValue;
348 auto const actualOpt = rebuiltTx.getLatePaymentFee();
349 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLatePaymentFee should be present";
350 expectEqualField(expected, *actualOpt, "sfLatePaymentFee");
351 }
352
353 {
354 auto const& expected = closePaymentFeeValue;
355 auto const actualOpt = rebuiltTx.getClosePaymentFee();
356 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfClosePaymentFee should be present";
357 expectEqualField(expected, *actualOpt, "sfClosePaymentFee");
358 }
359
360 {
361 auto const& expected = overpaymentFeeValue;
362 auto const actualOpt = rebuiltTx.getOverpaymentFee();
363 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOverpaymentFee should be present";
364 expectEqualField(expected, *actualOpt, "sfOverpaymentFee");
365 }
366
367 {
368 auto const& expected = interestRateValue;
369 auto const actualOpt = rebuiltTx.getInterestRate();
370 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfInterestRate should be present";
371 expectEqualField(expected, *actualOpt, "sfInterestRate");
372 }
373
374 {
375 auto const& expected = lateInterestRateValue;
376 auto const actualOpt = rebuiltTx.getLateInterestRate();
377 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfLateInterestRate should be present";
378 expectEqualField(expected, *actualOpt, "sfLateInterestRate");
379 }
380
381 {
382 auto const& expected = closeInterestRateValue;
383 auto const actualOpt = rebuiltTx.getCloseInterestRate();
384 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCloseInterestRate should be present";
385 expectEqualField(expected, *actualOpt, "sfCloseInterestRate");
386 }
387
388 {
389 auto const& expected = overpaymentInterestRateValue;
390 auto const actualOpt = rebuiltTx.getOverpaymentInterestRate();
391 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfOverpaymentInterestRate should be present";
392 expectEqualField(expected, *actualOpt, "sfOverpaymentInterestRate");
393 }
394
395 {
396 auto const& expected = paymentTotalValue;
397 auto const actualOpt = rebuiltTx.getPaymentTotal();
398 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaymentTotal should be present";
399 expectEqualField(expected, *actualOpt, "sfPaymentTotal");
400 }
401
402 {
403 auto const& expected = paymentIntervalValue;
404 auto const actualOpt = rebuiltTx.getPaymentInterval();
405 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPaymentInterval should be present";
406 expectEqualField(expected, *actualOpt, "sfPaymentInterval");
407 }
408
409 {
410 auto const& expected = gracePeriodValue;
411 auto const actualOpt = rebuiltTx.getGracePeriod();
412 ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfGracePeriod should be present";
413 expectEqualField(expected, *actualOpt, "sfGracePeriod");
414 }
415
416}
417
418// 3) Verify wrapper throws when constructed from wrong transaction type.
419TEST(TransactionsLoanSetTests, WrapperThrowsOnWrongTxType)
420{
421 // Build a valid transaction of a different type
422 auto const [pk, sk] =
424 auto const account = calcAccountID(pk);
425
426 AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()};
427 auto wrongTx = wrongBuilder.build(pk, sk);
428
429 EXPECT_THROW(LoanSet{wrongTx.getSTTx()}, std::runtime_error);
430}
431
432// 4) Verify builder throws when constructed from wrong transaction type.
433TEST(TransactionsLoanSetTests, BuilderThrowsOnWrongTxType)
434{
435 // Build a valid transaction of a different type
436 auto const [pk, sk] =
437 generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder"));
438 auto const account = calcAccountID(pk);
439
440 AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()};
441 auto wrongTx = wrongBuilder.build(pk, sk);
442
443 EXPECT_THROW(LoanSetBuilder{wrongTx.getSTTx()}, std::runtime_error);
444}
445
446// 5) Build with only required fields and verify optional fields return nullopt.
447TEST(TransactionsLoanSetTests, OptionalFieldsReturnNullopt)
448{
449 // Generate a deterministic keypair for signing
450 auto const [publicKey, secretKey] =
451 generateKeyPair(KeyType::secp256k1, generateSeed("testLoanSetNullopt"));
452
453 // Common transaction fields
454 auto const accountValue = calcAccountID(publicKey);
455 std::uint32_t const sequenceValue = 3;
456 auto const feeValue = canonical_AMOUNT();
457
458 // Transaction-specific required field values
459 auto const loanBrokerIDValue = canonical_UINT256();
460 auto const principalRequestedValue = canonical_NUMBER();
461
462 LoanSetBuilder builder{
463 accountValue,
464 loanBrokerIDValue,
465 principalRequestedValue,
466 sequenceValue,
467 feeValue
468 };
469
470 // Do NOT set optional fields
471
472 auto tx = builder.build(publicKey, secretKey);
473
474 // Verify optional fields are not present
475 EXPECT_FALSE(tx.hasData());
476 EXPECT_FALSE(tx.getData().has_value());
477 EXPECT_FALSE(tx.hasCounterparty());
478 EXPECT_FALSE(tx.getCounterparty().has_value());
479 EXPECT_FALSE(tx.hasCounterpartySignature());
480 EXPECT_FALSE(tx.getCounterpartySignature().has_value());
481 EXPECT_FALSE(tx.hasLoanOriginationFee());
482 EXPECT_FALSE(tx.getLoanOriginationFee().has_value());
483 EXPECT_FALSE(tx.hasLoanServiceFee());
484 EXPECT_FALSE(tx.getLoanServiceFee().has_value());
485 EXPECT_FALSE(tx.hasLatePaymentFee());
486 EXPECT_FALSE(tx.getLatePaymentFee().has_value());
487 EXPECT_FALSE(tx.hasClosePaymentFee());
488 EXPECT_FALSE(tx.getClosePaymentFee().has_value());
489 EXPECT_FALSE(tx.hasOverpaymentFee());
490 EXPECT_FALSE(tx.getOverpaymentFee().has_value());
491 EXPECT_FALSE(tx.hasInterestRate());
492 EXPECT_FALSE(tx.getInterestRate().has_value());
493 EXPECT_FALSE(tx.hasLateInterestRate());
494 EXPECT_FALSE(tx.getLateInterestRate().has_value());
495 EXPECT_FALSE(tx.hasCloseInterestRate());
496 EXPECT_FALSE(tx.getCloseInterestRate().has_value());
497 EXPECT_FALSE(tx.hasOverpaymentInterestRate());
498 EXPECT_FALSE(tx.getOverpaymentInterestRate().has_value());
499 EXPECT_FALSE(tx.hasPaymentTotal());
500 EXPECT_FALSE(tx.getPaymentTotal().has_value());
501 EXPECT_FALSE(tx.hasPaymentInterval());
502 EXPECT_FALSE(tx.getPaymentInterval().has_value());
503 EXPECT_FALSE(tx.hasGracePeriod());
504 EXPECT_FALSE(tx.getGracePeriod().has_value());
505}
506
507}
LoanSet build(PublicKey const &publicKey, SecretKey const &secretKey)
Build and return the LoanSet wrapper.
std::shared_ptr< STTx const > getSTTx() const
Get the underlying STTx object.
TEST(TransactionsAccountDeleteTests, BuilderSettersRoundTrip)
Seed generateSeed(std::string const &passPhrase)
Generate a seed deterministically.
Definition Seed.cpp:57
std::pair< PublicKey, SecretKey > generateKeyPair(KeyType type, Seed const &seed)
Generate a key pair deterministically.
AccountID calcAccountID(PublicKey const &pk)
void expectEqualField(T const &expected, T const &actual, char const *fieldName)