Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2026 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file exchangedb/test_aggregation_tracking.c
18 : * @brief tests for the exchangedb functions whose primary table is
19 : * `aggregation_tracking`
20 : * @author Christian Grothoff
21 : *
22 : * Covers #TALER_EXCHANGEDB_do_aggregate(),
23 : * #TALER_EXCHANGEDB_get_pending_aggregation(),
24 : * #TALER_EXCHANGEDB_get_transfer_by_deposit(),
25 : * #TALER_EXCHANGEDB_iterate_aggregation_wtids_above_serial_id(),
26 : * #TALER_EXCHANGEDB_iterate_aggregations_above_serial_id() and
27 : * #TALER_EXCHANGEDB_iterate_wire_transfers().
28 : *
29 : * `aggregation_tracking` records which deposit went into which wire
30 : * transfer. do_aggregate() writes it, netting deposits against refunds and
31 : * charging the deposit fee only for coins that were not refunded in full --
32 : * the checks below pin that arithmetic down.
33 : */
34 : #include "test_common.h"
35 : #include "exchange-database/do_aggregate.h"
36 : #include "exchange-database/do_refund.h"
37 : #include "exchange-database/get_pending_aggregation.h"
38 : #include "exchange-database/get_transfer_by_deposit.h"
39 : #include "exchange-database/insert_wire_out.h"
40 : #include "exchange-database/iterate_aggregation_wtids_above_serial_id.h"
41 : #include "exchange-database/iterate_aggregations_above_serial_id.h"
42 : #include "exchange-database/iterate_wire_transfers.h"
43 :
44 :
45 : /**
46 : * Account the merchants of the checks are paid at.
47 : */
48 : static struct TDB_Account account;
49 :
50 :
51 : /**
52 : * Denomination the checks deposit.
53 : */
54 : static struct TDB_Denom denom;
55 :
56 :
57 : /**
58 : * Build a timestamp from a number of seconds since the epoch.
59 : *
60 : * @param secs seconds since the epoch
61 : * @return the timestamp
62 : */
63 : static struct GNUNET_TIME_Timestamp
64 5 : ts (uint64_t secs)
65 : {
66 5 : struct GNUNET_TIME_Absolute abs = {
67 5 : .abs_value_us = secs * 1000LLU * 1000LLU
68 : };
69 :
70 5 : return GNUNET_TIME_absolute_to_timestamp (abs);
71 : }
72 :
73 :
74 : /**
75 : * Closure for #wtid_cb().
76 : */
77 : struct WtidContext
78 : {
79 : /**
80 : * How many rows did the callback see?
81 : */
82 : unsigned int total;
83 :
84 : /**
85 : * Wire transfer we are looking for, NULL to match nothing.
86 : */
87 : const struct TALER_WireTransferIdentifierRawP *wtid;
88 :
89 : /**
90 : * How many times did we see it?
91 : */
92 : unsigned int matched;
93 :
94 : /**
95 : * Was the last matching row reported as pending?
96 : */
97 : bool pending;
98 : };
99 :
100 :
101 : /**
102 : * Callback for
103 : * #TALER_EXCHANGEDB_iterate_aggregation_wtids_above_serial_id().
104 : *
105 : * @param cls a `struct WtidContext *`
106 : * @param rowid row of the tracking entry
107 : * @param wtid wire transfer the deposit was aggregated into
108 : * @param wire_target_h_payto where the transfer goes
109 : * @param pending whether the transfer has not been made yet
110 : * @return #GNUNET_OK
111 : */
112 : static enum GNUNET_GenericReturnValue
113 3 : wtid_cb (void *cls,
114 : uint64_t rowid,
115 : const struct TALER_WireTransferIdentifierRawP *wtid,
116 : const struct TALER_FullPaytoHashP *wire_target_h_payto,
117 : bool pending)
118 : {
119 3 : struct WtidContext *ctx = cls;
120 :
121 : (void) rowid;
122 : (void) wire_target_h_payto;
123 3 : ctx->total++;
124 3 : if ( (NULL != ctx->wtid) &&
125 3 : (0 == GNUNET_memcmp (wtid,
126 : ctx->wtid)) )
127 : {
128 2 : ctx->matched++;
129 2 : ctx->pending = pending;
130 : }
131 3 : return GNUNET_OK;
132 : }
133 :
134 :
135 : /**
136 : * Closure for #aggregation_cb() and #transfer_cb().
137 : */
138 : struct SumContext
139 : {
140 : /**
141 : * How many rows did the callback see?
142 : */
143 : unsigned int total;
144 :
145 : /**
146 : * Sum of the whole-unit parts of the amounts seen.
147 : */
148 : uint64_t value_sum;
149 :
150 : /**
151 : * Row of the first entry seen.
152 : */
153 : uint64_t first_row;
154 : };
155 :
156 :
157 : /**
158 : * Callback for #TALER_EXCHANGEDB_iterate_aggregations_above_serial_id().
159 : *
160 : * @param cls a `struct SumContext *`
161 : * @param amount how much was aggregated
162 : * @param tracking_serial_id row of the tracking entry
163 : * @param batch_deposit_serial_id deposit that was aggregated
164 : */
165 : static void
166 2 : aggregation_cb (void *cls,
167 : const struct TALER_Amount *amount,
168 : uint64_t tracking_serial_id,
169 : uint64_t batch_deposit_serial_id)
170 : {
171 2 : struct SumContext *ctx = cls;
172 :
173 : (void) batch_deposit_serial_id;
174 2 : if (0 == ctx->total++)
175 1 : ctx->first_row = tracking_serial_id;
176 2 : ctx->value_sum += amount->value;
177 2 : }
178 :
179 :
180 : /**
181 : * Callback for #TALER_EXCHANGEDB_iterate_wire_transfers().
182 : *
183 : * @param cls a `struct SumContext *`
184 : * @param rowid row of the tracking entry
185 : * @param merchant_pub merchant that was paid
186 : * @param account_payto_uri account of the merchant
187 : * @param h_payto hash of that account
188 : * @param exchange_payto_uri account of the exchange
189 : * @param exec_time when the transfer was made
190 : * @param h_contract_terms contract that was paid
191 : * @param denom_pub denomination of the coin
192 : * @param coin_pub the coin that was deposited
193 : * @param coin_value how much the coin contributed
194 : * @param coin_fee how much the exchange charged for it
195 : */
196 : static void
197 1 : transfer_cb (void *cls,
198 : uint64_t rowid,
199 : const struct TALER_MerchantPublicKeyP *merchant_pub,
200 : const struct TALER_FullPayto account_payto_uri,
201 : const struct TALER_FullPaytoHashP *h_payto,
202 : const struct TALER_FullPayto exchange_payto_uri,
203 : struct GNUNET_TIME_Timestamp exec_time,
204 : const struct TALER_PrivateContractHashP *h_contract_terms,
205 : const struct TALER_DenominationPublicKey *denom_pub,
206 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
207 : const struct TALER_Amount *coin_value,
208 : const struct TALER_Amount *coin_fee)
209 : {
210 1 : struct SumContext *ctx = cls;
211 :
212 : (void) merchant_pub;
213 : (void) account_payto_uri;
214 : (void) h_payto;
215 : (void) exchange_payto_uri;
216 : (void) exec_time;
217 : (void) h_contract_terms;
218 : (void) denom_pub;
219 : (void) coin_pub;
220 : (void) coin_fee;
221 1 : if (0 == ctx->total++)
222 1 : ctx->first_row = rowid;
223 1 : ctx->value_sum += coin_value->value;
224 1 : }
225 :
226 :
227 : /**
228 : * Nothing is reported while the table is empty, and aggregating an
229 : * account with no deposits comes out at zero.
230 : *
231 : * @param pg the database context
232 : * @return 0 on success
233 : */
234 : static int
235 1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
236 : {
237 : struct TALER_MerchantPublicKeyP merchant_pub;
238 : struct TALER_WireTransferIdentifierRawP wtid;
239 : struct TALER_FullPaytoHashP h_payto;
240 1 : struct TALER_FullPayto payto_uri = { NULL };
241 : struct TALER_Amount total;
242 1 : struct TALER_Amount zero = TDB_amount ("0");
243 : struct TALER_Amount deposited;
244 : struct TALER_Amount refunded;
245 : struct TALER_Amount fee;
246 1 : struct WtidContext ctx = { 0 };
247 1 : struct SumContext sctx = { 0 };
248 :
249 1 : TDB_denom (pg,
250 : 10,
251 : "5",
252 : "0.1",
253 : &denom);
254 1 : TDB_account (pg,
255 : 10,
256 : &account);
257 1 : TDB_FILL (merchant_pub,
258 : 1);
259 1 : TDB_FILL (wtid,
260 : 1);
261 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
262 : TALER_EXCHANGEDB_do_aggregate (pg,
263 : &account.h_full,
264 : &merchant_pub,
265 : &wtid,
266 : &total));
267 1 : FAILIF (0 != TALER_amount_cmp (&total,
268 : &zero));
269 1 : FAILIF (0 != TDB_count (pg,
270 : "FROM aggregation_tracking"));
271 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
272 : TALER_EXCHANGEDB_get_pending_aggregation (pg,
273 : &wtid,
274 : &h_payto,
275 : &payto_uri,
276 : &deposited,
277 : &refunded,
278 : &fee));
279 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
280 : TALER_EXCHANGEDB_iterate_aggregation_wtids_above_serial_id (
281 : pg,
282 : 0,
283 : &wtid_cb,
284 : &ctx));
285 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
286 : TALER_EXCHANGEDB_iterate_aggregations_above_serial_id (
287 : pg,
288 : 0,
289 : &aggregation_cb,
290 : &sctx));
291 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
292 : TALER_EXCHANGEDB_iterate_wire_transfers (pg,
293 : &wtid,
294 : &transfer_cb,
295 : &sctx));
296 1 : FAILIF (0 != ctx.total);
297 1 : FAILIF (0 != sctx.total);
298 1 : return 0;
299 : }
300 :
301 :
302 : /**
303 : * Aggregating a deposit records it and nets out the deposit fee.
304 : *
305 : * @param pg the database context
306 : * @return 0 on success
307 : */
308 : static int
309 1 : check_aggregate (struct TALER_EXCHANGEDB_PostgresContext *pg)
310 : {
311 : struct TALER_CoinPublicInfo coin;
312 : struct TDB_Deposit dep;
313 : struct TALER_WireTransferIdentifierRawP wtid;
314 : struct TALER_FullPaytoHashP h_payto;
315 1 : struct TALER_FullPayto payto_uri = { NULL };
316 : struct TALER_Amount total;
317 : struct TALER_Amount deposited;
318 : struct TALER_Amount refunded;
319 : struct TALER_Amount fee;
320 1 : struct TALER_Amount expect_total = TDB_amount ("0.9");
321 1 : struct TALER_Amount expect_deposited = TDB_amount ("1");
322 1 : struct TALER_Amount expect_refunded = TDB_amount ("0");
323 1 : struct TALER_Amount expect_fee = TDB_amount ("0.1");
324 : struct WtidContext ctx;
325 :
326 1 : TDB_coin (pg,
327 : &denom,
328 : 20,
329 : &coin,
330 : NULL);
331 1 : TDB_deposit (pg,
332 : &account,
333 : &coin,
334 : 20,
335 : "1",
336 : "0.1",
337 : ts (1600000000),
338 : ts (1600000000),
339 : &dep);
340 1 : TDB_FILL (wtid,
341 : 20);
342 1 : FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
343 : TALER_EXCHANGEDB_do_aggregate (pg,
344 : &account.h_full,
345 : &dep.merchant_pub,
346 : &wtid,
347 : &total),
348 : TDB_coin_free (&coin));
349 1 : FAILIF_C (0 != TALER_amount_cmp (&total,
350 : &expect_total),
351 : TDB_coin_free (&coin));
352 1 : FAILIF_C (1 != TDB_count (pg,
353 : "FROM aggregation_tracking"),
354 : TDB_coin_free (&coin));
355 : /* the deposit is now marked as done */
356 1 : FAILIF_C (1 != TDB_count (pg,
357 : "FROM batch_deposits WHERE done"),
358 : TDB_coin_free (&coin));
359 :
360 : /* the transfer has not been made yet, so it is pending */
361 1 : FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
362 : TALER_EXCHANGEDB_get_pending_aggregation (pg,
363 : &wtid,
364 : &h_payto,
365 : &payto_uri,
366 : &deposited,
367 : &refunded,
368 : &fee),
369 : TDB_coin_free (&coin));
370 1 : FAILIF_C (0 != GNUNET_memcmp (&h_payto,
371 : &account.h_full),
372 : GNUNET_free (payto_uri.full_payto); TDB_coin_free (&coin));
373 1 : FAILIF_C (0 != strcmp (payto_uri.full_payto,
374 : account.payto.full_payto),
375 : GNUNET_free (payto_uri.full_payto); TDB_coin_free (&coin));
376 1 : GNUNET_free (payto_uri.full_payto);
377 1 : FAILIF_C (0 != TALER_amount_cmp (&deposited,
378 : &expect_deposited),
379 : TDB_coin_free (&coin));
380 1 : FAILIF_C (0 != TALER_amount_cmp (&refunded,
381 : &expect_refunded),
382 : TDB_coin_free (&coin));
383 1 : FAILIF_C (0 != TALER_amount_cmp (&fee,
384 : &expect_fee),
385 : TDB_coin_free (&coin));
386 :
387 1 : memset (&ctx,
388 : 0,
389 : sizeof (ctx));
390 1 : ctx.wtid = &wtid;
391 1 : FAILIF_C (0 >=
392 : TALER_EXCHANGEDB_iterate_aggregation_wtids_above_serial_id (
393 : pg,
394 : 0,
395 : &wtid_cb,
396 : &ctx),
397 : TDB_coin_free (&coin));
398 1 : FAILIF_C (1 != ctx.matched,
399 : TDB_coin_free (&coin));
400 1 : FAILIF_C (! ctx.pending,
401 : TDB_coin_free (&coin));
402 1 : TDB_coin_free (&coin);
403 1 : return 0;
404 : }
405 :
406 :
407 : /**
408 : * A deposit that was refunded in full is aggregated at zero and is not
409 : * charged a deposit fee.
410 : *
411 : * @param pg the database context
412 : * @return 0 on success
413 : */
414 : static int
415 1 : check_full_refund (struct TALER_EXCHANGEDB_PostgresContext *pg)
416 : {
417 : struct TALER_CoinPublicInfo coin;
418 : struct TDB_Deposit dep;
419 : struct TALER_EXCHANGEDB_Refund refund;
420 1 : struct TALER_Amount deposit_fee = TDB_amount ("0.1");
421 : struct TALER_WireTransferIdentifierRawP wtid;
422 : struct TALER_Amount total;
423 1 : struct TALER_Amount zero = TDB_amount ("0");
424 : bool not_found;
425 : bool refund_ok;
426 : bool gone;
427 : bool conflict;
428 :
429 1 : TDB_coin (pg,
430 : &denom,
431 : 21,
432 : &coin,
433 : NULL);
434 1 : TDB_deposit (pg,
435 : &account,
436 : &coin,
437 : 21,
438 : "1",
439 : "0.1",
440 : ts (1600000000),
441 : ts (1600000000),
442 : &dep);
443 1 : memset (&refund,
444 : 0,
445 : sizeof (refund));
446 1 : refund.coin = coin;
447 1 : refund.details.merchant_pub = dep.merchant_pub;
448 1 : TDB_FILL (refund.details.merchant_sig,
449 : 21);
450 1 : refund.details.h_contract_terms = dep.h_contract_terms;
451 1 : refund.details.rtransaction_id = 1;
452 1 : refund.details.refund_amount = TDB_amount ("1");
453 1 : refund.details.refund_fee = TDB_amount ("0");
454 1 : FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
455 : TALER_EXCHANGEDB_do_refund (pg,
456 : &refund,
457 : &deposit_fee,
458 : 0,
459 : ¬_found,
460 : &refund_ok,
461 : &gone,
462 : &conflict),
463 : TDB_coin_free (&coin));
464 1 : FAILIF_C (! refund_ok,
465 : TDB_coin_free (&coin));
466 :
467 1 : TDB_FILL (wtid,
468 : 21);
469 1 : FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
470 : TALER_EXCHANGEDB_do_aggregate (pg,
471 : &account.h_full,
472 : &dep.merchant_pub,
473 : &wtid,
474 : &total),
475 : TDB_coin_free (&coin));
476 : /* deposit EUR:1 minus refund EUR:1, and no deposit fee on a coin that
477 : was refunded in full */
478 1 : FAILIF_C (0 != TALER_amount_cmp (&total,
479 : &zero),
480 : TDB_coin_free (&coin));
481 1 : TDB_coin_free (&coin);
482 1 : return 0;
483 : }
484 :
485 :
486 : /**
487 : * Executing the wire transfer takes the aggregation out of "pending".
488 : *
489 : * @param pg the database context
490 : * @return 0 on success
491 : */
492 : static int
493 1 : check_executed (struct TALER_EXCHANGEDB_PostgresContext *pg)
494 : {
495 : struct TALER_WireTransferIdentifierRawP wtid;
496 : struct TALER_FullPaytoHashP h_payto;
497 1 : struct TALER_FullPayto payto_uri = { NULL };
498 1 : struct TALER_FullPayto exchange_payto = {
499 : .full_payto = (char *) "payto://x-taler-bank/localhost/exchange"
500 : };
501 1 : struct TALER_Amount amount = TDB_amount ("0.9");
502 : struct TALER_Amount deposited;
503 : struct TALER_Amount refunded;
504 : struct TALER_Amount fee;
505 : struct WtidContext ctx;
506 :
507 1 : TDB_FILL (wtid,
508 : 20);
509 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
510 : TALER_EXCHANGEDB_insert_wire_out (pg,
511 : ts (1600003600),
512 : &wtid,
513 : &account.h_full,
514 : "exchange-account-1",
515 : exchange_payto,
516 : &amount,
517 : NULL));
518 : /* the aggregation is no longer pending... */
519 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
520 : TALER_EXCHANGEDB_get_pending_aggregation (pg,
521 : &wtid,
522 : &h_payto,
523 : &payto_uri,
524 : &deposited,
525 : &refunded,
526 : &fee));
527 : /* ...and the wtid iterator says so too */
528 1 : memset (&ctx,
529 : 0,
530 : sizeof (ctx));
531 1 : ctx.wtid = &wtid;
532 1 : FAILIF (0 >=
533 : TALER_EXCHANGEDB_iterate_aggregation_wtids_above_serial_id (
534 : pg,
535 : 0,
536 : &wtid_cb,
537 : &ctx));
538 1 : FAILIF (1 != ctx.matched);
539 1 : FAILIF (ctx.pending);
540 1 : return 0;
541 : }
542 :
543 :
544 : /**
545 : * The deposit can be traced to the wire transfer it went into, and the
546 : * transfer back to the deposits it paid.
547 : *
548 : * @param pg the database context
549 : * @return 0 on success
550 : */
551 : static int
552 1 : check_transfer_by_deposit (struct TALER_EXCHANGEDB_PostgresContext *pg)
553 : {
554 : struct TALER_CoinSpendPublicKeyP coin_pub;
555 : struct TDB_Deposit dep;
556 : struct TALER_WireTransferIdentifierRawP wtid;
557 : struct TALER_WireTransferIdentifierRawP got_wtid;
558 : struct GNUNET_TIME_Timestamp exec_time;
559 : struct TALER_Amount amount_with_fee;
560 : struct TALER_Amount deposit_fee;
561 1 : struct TALER_Amount expect_amount = TDB_amount ("1");
562 : struct TALER_EXCHANGEDB_KycStatus kyc;
563 : union TALER_AccountPublicKeyP account_pub;
564 : struct SumContext sctx;
565 1 : bool pending = true;
566 :
567 : /* the deposit of check_aggregate() */
568 1 : TDB_FILL (coin_pub,
569 : 20);
570 1 : TDB_FILL (wtid,
571 : 20);
572 1 : memset (&dep,
573 : 0,
574 : sizeof (dep));
575 1 : TDB_FILL (dep.merchant_pub,
576 : 20);
577 1 : TDB_FILL (dep.h_contract_terms,
578 : 20);
579 1 : TDB_FILL (dep.wire_salt,
580 : 20);
581 1 : TALER_merchant_wire_signature_hash (account.payto,
582 : &dep.wire_salt,
583 : &dep.h_wire);
584 1 : memset (&kyc,
585 : 0,
586 : sizeof (kyc));
587 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
588 : TALER_EXCHANGEDB_get_transfer_by_deposit (pg,
589 : &dep.h_contract_terms,
590 : &dep.h_wire,
591 : &coin_pub,
592 : &dep.merchant_pub,
593 : &pending,
594 : &got_wtid,
595 : &exec_time,
596 : &amount_with_fee,
597 : &deposit_fee,
598 : &kyc,
599 : &account_pub));
600 1 : FAILIF (pending);
601 1 : FAILIF (0 != GNUNET_memcmp (&got_wtid,
602 : &wtid));
603 1 : FAILIF (0 != TALER_amount_cmp (&amount_with_fee,
604 : &expect_amount));
605 :
606 : /* a contract nobody deposited is not found */
607 : {
608 : struct TALER_PrivateContractHashP other;
609 :
610 1 : TDB_FILL (other,
611 : 97);
612 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
613 : TALER_EXCHANGEDB_get_transfer_by_deposit (pg,
614 : &other,
615 : &dep.h_wire,
616 : &coin_pub,
617 : &dep.merchant_pub,
618 : &pending,
619 : &got_wtid,
620 : &exec_time,
621 : &amount_with_fee,
622 : &deposit_fee,
623 : &kyc,
624 : &account_pub));
625 : }
626 :
627 : /* and the transfer lists the coins it paid for */
628 1 : memset (&sctx,
629 : 0,
630 : sizeof (sctx));
631 1 : FAILIF (0 >=
632 : TALER_EXCHANGEDB_iterate_wire_transfers (pg,
633 : &wtid,
634 : &transfer_cb,
635 : &sctx));
636 1 : FAILIF (1 != sctx.total);
637 1 : FAILIF (1 != sctx.value_sum);
638 : {
639 : struct TALER_WireTransferIdentifierRawP other;
640 :
641 1 : TDB_FILL (other,
642 : 96);
643 1 : memset (&sctx,
644 : 0,
645 : sizeof (sctx));
646 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
647 : TALER_EXCHANGEDB_iterate_wire_transfers (pg,
648 : &other,
649 : &transfer_cb,
650 : &sctx));
651 1 : FAILIF (0 != sctx.total);
652 : }
653 1 : return 0;
654 : }
655 :
656 :
657 : /**
658 : * The aggregation iterator walks the table by serial.
659 : *
660 : * @param pg the database context
661 : * @return 0 on success
662 : */
663 : static int
664 1 : check_iterate_aggregations (struct TALER_EXCHANGEDB_PostgresContext *pg)
665 : {
666 : struct SumContext ctx;
667 :
668 1 : memset (&ctx,
669 : 0,
670 : sizeof (ctx));
671 1 : FAILIF (2 !=
672 : TALER_EXCHANGEDB_iterate_aggregations_above_serial_id (
673 : pg,
674 : 0,
675 : &aggregation_cb,
676 : &ctx));
677 1 : FAILIF (2 != ctx.total);
678 1 : memset (&ctx,
679 : 0,
680 : sizeof (ctx));
681 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
682 : TALER_EXCHANGEDB_iterate_aggregations_above_serial_id (
683 : pg,
684 : 1000,
685 : &aggregation_cb,
686 : &ctx));
687 1 : FAILIF (0 != ctx.total);
688 1 : return 0;
689 : }
690 :
691 :
692 : /**
693 : * The checks to run, in order.
694 : */
695 : static const struct TDB_Test tests[] = {
696 : { "aggregation-tracking-empty",
697 : &check_empty },
698 : { "aggregation-tracking-aggregate",
699 : &check_aggregate },
700 : { "aggregation-tracking-full-refund",
701 : &check_full_refund },
702 : { "aggregation-tracking-executed",
703 : &check_executed },
704 : { "aggregation-tracking-transfer-by-deposit",
705 : &check_transfer_by_deposit },
706 : { "aggregation-tracking-iterate-aggregations",
707 : &check_iterate_aggregations },
708 : { NULL, NULL }
709 : };
710 :
711 :
712 : int
713 1 : main (int argc,
714 : char *const *argv)
715 : {
716 : int ret;
717 :
718 1 : ret = TDB_main (argc,
719 : argv,
720 : "test-aggregation-tracking",
721 : "Tests for the exchangedb `aggregation_tracking' table",
722 : tests);
723 1 : TDB_account_free (&account);
724 1 : TDB_denom_free (&denom);
725 1 : return ret;
726 : }
727 :
728 :
729 : /* end of test_aggregation_tracking.c */
|