LCOV - code coverage report
Current view: top level - exchangedb - test_reserves_in.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 148 148
Test Date: 2026-09-11 18:55:36 Functions: 100.0 % 9 9

            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_reserves_in.c
      18              :  * @brief tests for the exchangedb functions whose primary table is
      19              :  *        `reserves_in`
      20              :  * @author Christian Grothoff
      21              :  *
      22              :  * Covers #TALER_EXCHANGEDB_do_import_credits() (for its reserve half; the
      23              :  * KYC-auth half is in test_kycauths_in.c),
      24              :  * #TALER_EXCHANGEDB_get_reserve_origin(),
      25              :  * #TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id(),
      26              :  * #TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id_by_account() and
      27              :  * #TALER_EXCHANGEDB_iterate_exchange_credit_transfers().
      28              :  *
      29              :  * do_import_credits() creates the `wire_targets` and `reserves` rows it
      30              :  * needs on its own, so the only fixture the checks have to set up is a work
      31              :  * shard for the batch to advance.
      32              :  */
      33              : #include "test_common.h"
      34              : #include "exchange-database/begin_shard.h"
      35              : #include "exchange-database/do_import_credits.h"
      36              : #include "exchange-database/get_reserve_origin.h"
      37              : #include "exchange-database/iterate_exchange_credit_transfers.h"
      38              : #include "exchange-database/iterate_reserves_in_above_serial_id.h"
      39              : #include "exchange-database/iterate_reserves_in_above_serial_id_by_account.h"
      40              : 
      41              : 
      42              : /**
      43              :  * Account the first two transfers come from.
      44              :  */
      45              : #define PAYTO_A "payto://x-taler-bank/localhost/ri-a?receiver-name=A"
      46              : 
      47              : /**
      48              :  * Account the third transfer comes from.
      49              :  */
      50              : #define PAYTO_B "payto://x-taler-bank/localhost/ri-b?receiver-name=B"
      51              : 
      52              : 
      53              : /**
      54              :  * Closure for #reserve_in_cb().
      55              :  */
      56              : struct InContext
      57              : {
      58              :   /**
      59              :    * How many rows did the callback see?
      60              :    */
      61              :   unsigned int total;
      62              : 
      63              :   /**
      64              :    * Stop after this many rows; 0 for no limit.
      65              :    */
      66              :   unsigned int stop_after;
      67              : 
      68              :   /**
      69              :    * Reserve we are looking for, NULL to match nothing.
      70              :    */
      71              :   const struct TALER_ReservePublicKeyP *reserve_pub;
      72              : 
      73              :   /**
      74              :    * How many times did we see @e reserve_pub?
      75              :    */
      76              :   unsigned int matched;
      77              : 
      78              :   /**
      79              :    * Credit reported for @e reserve_pub.
      80              :    */
      81              :   struct TALER_Amount credit;
      82              : 
      83              :   /**
      84              :    * Row reported for @e reserve_pub.
      85              :    */
      86              :   uint64_t rowid;
      87              : 
      88              :   /**
      89              :    * Wire reference reported for @e reserve_pub.
      90              :    */
      91              :   uint64_t wire_reference;
      92              : 
      93              :   /**
      94              :    * Sender account reported for @e reserve_pub, owned by this struct.
      95              :    */
      96              :   char *sender;
      97              : };
      98              : 
      99              : 
     100              : /**
     101              :  * Callback for the `reserves_in` iterators.
     102              :  *
     103              :  * @param cls a `struct InContext *`
     104              :  * @param rowid row of the transfer
     105              :  * @param reserve_pub reserve that was credited
     106              :  * @param credit amount that was credited
     107              :  * @param sender_account_details account the money came from
     108              :  * @param wire_reference bank's identifier for the transfer
     109              :  * @param execution_date when the transfer was made
     110              :  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to stop
     111              :  */
     112              : static enum GNUNET_GenericReturnValue
     113           10 : reserve_in_cb (void *cls,
     114              :                uint64_t rowid,
     115              :                const struct TALER_ReservePublicKeyP *reserve_pub,
     116              :                const struct TALER_Amount *credit,
     117              :                const struct TALER_FullPayto sender_account_details,
     118              :                uint64_t wire_reference,
     119              :                struct GNUNET_TIME_Timestamp execution_date)
     120              : {
     121           10 :   struct InContext *ctx = cls;
     122              : 
     123              :   (void) execution_date;
     124           10 :   ctx->total++;
     125           10 :   if ( (NULL != ctx->reserve_pub) &&
     126            4 :        (0 == GNUNET_memcmp (reserve_pub,
     127              :                             ctx->reserve_pub)) )
     128              :   {
     129            2 :     ctx->matched++;
     130            2 :     ctx->credit = *credit;
     131            2 :     ctx->rowid = rowid;
     132            2 :     ctx->wire_reference = wire_reference;
     133            2 :     GNUNET_free (ctx->sender);
     134            2 :     ctx->sender = (NULL == sender_account_details.full_payto)
     135              :       ? NULL
     136            2 :       : GNUNET_strdup (sender_account_details.full_payto);
     137              :   }
     138           10 :   if ( (0 != ctx->stop_after) &&
     139            1 :        (ctx->total >= ctx->stop_after) )
     140            1 :     return GNUNET_SYSERR;
     141            9 :   return GNUNET_OK;
     142              : }
     143              : 
     144              : 
     145              : /**
     146              :  * Closure for #transfer_cb().
     147              :  */
     148              : struct TransferContext
     149              : {
     150              :   /**
     151              :    * How many transfers did the callback see?
     152              :    */
     153              :   unsigned int total;
     154              : 
     155              :   /**
     156              :    * Row of the first transfer seen.
     157              :    */
     158              :   uint64_t first_row;
     159              : 
     160              :   /**
     161              :    * Sum of the whole-unit parts of the amounts seen.
     162              :    */
     163              :   uint64_t value_sum;
     164              : };
     165              : 
     166              : 
     167              : /**
     168              :  * Callback for #TALER_EXCHANGEDB_iterate_exchange_credit_transfers().
     169              :  *
     170              :  * @param cls a `struct TransferContext *`
     171              :  * @param row_id row of the transfer
     172              :  * @param payto_uri account the money came from
     173              :  * @param execution_time when the transfer was made
     174              :  * @param amount amount that was transferred
     175              :  */
     176              : static void
     177           11 : transfer_cb (void *cls,
     178              :              uint64_t row_id,
     179              :              const char *payto_uri,
     180              :              struct GNUNET_TIME_Absolute execution_time,
     181              :              const struct TALER_Amount *amount)
     182              : {
     183           11 :   struct TransferContext *ctx = cls;
     184              : 
     185              :   (void) payto_uri;
     186              :   (void) execution_time;
     187           11 :   if (0 == ctx->total++)
     188            5 :     ctx->first_row = row_id;
     189           11 :   ctx->value_sum += amount->value;
     190           11 : }
     191              : 
     192              : 
     193              : /**
     194              :  * Import one wire transfer into a reserve, advancing a work shard of its
     195              :  * own along with it.
     196              :  *
     197              :  * @param pg the database context
     198              :  * @param job_name job whose shard to open and advance
     199              :  * @param account_name exchange bank account the transfer arrived at
     200              :  * @param payto account the money came from
     201              :  * @param seed seed for the reserve's public key
     202              :  * @param amount amount that was transferred, e.g. "10"
     203              :  * @param[out] reserve_pub set to the credited reserve
     204              :  * @param[out] status set to the per-transfer status
     205              :  * @return transaction status of the import
     206              :  */
     207              : static enum GNUNET_DB_QueryStatus
     208            4 : import_one (struct TALER_EXCHANGEDB_PostgresContext *pg,
     209              :             const char *job_name,
     210              :             const char *account_name,
     211              :             const char *payto,
     212              :             uint32_t seed,
     213              :             const char *amount,
     214              :             struct TALER_ReservePublicKeyP *reserve_pub,
     215              :             enum GNUNET_DB_QueryStatus *status)
     216              : {
     217            4 :   struct TALER_Amount balance = TDB_amount (amount);
     218              :   struct TALER_EXCHANGEDB_ReserveInInfo info;
     219              :   struct TALER_EXCHANGEDB_CreditBatch batch;
     220              :   uint64_t start;
     221              :   uint64_t end;
     222              :   uint64_t progress;
     223              : 
     224            4 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
     225              :                  TALER_EXCHANGEDB_begin_shard (pg,
     226              :                                                job_name,
     227              :                                                GNUNET_TIME_UNIT_HOURS,
     228              :                                                1024,
     229              :                                                &start,
     230              :                                                &end,
     231              :                                                &progress));
     232            4 :   TDB_fill (reserve_pub,
     233              :             sizeof (*reserve_pub),
     234              :             seed);
     235            4 :   memset (&info,
     236              :           0,
     237              :           sizeof (info));
     238            4 :   info.reserve_pub = reserve_pub;
     239            4 :   info.balance = &balance;
     240            4 :   info.execution_time = GNUNET_TIME_timestamp_get ();
     241            4 :   info.sender_account_details.full_payto = (char *) payto;
     242            4 :   info.wire_reference = start + 1;
     243            4 :   memset (&batch,
     244              :           0,
     245              :           sizeof (batch));
     246            4 :   batch.exchange_account_name = account_name;
     247            4 :   batch.reserves = &info;
     248            4 :   batch.reserves_length = 1;
     249            4 :   batch.job_name = job_name;
     250            4 :   batch.shard_start = start;
     251            4 :   batch.shard_end = end;
     252            4 :   batch.progress_row = end;
     253            4 :   batch.lease = GNUNET_TIME_UNIT_HOURS;
     254            4 :   return TALER_EXCHANGEDB_do_import_credits (pg,
     255              :                                              &batch,
     256              :                                              status);
     257              : }
     258              : 
     259              : 
     260              : /**
     261              :  * Nothing is known while the table is empty.
     262              :  *
     263              :  * @param pg the database context
     264              :  * @return 0 on success
     265              :  */
     266              : static int
     267            1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
     268              : {
     269              :   struct TALER_ReservePublicKeyP reserve_pub;
     270              :   struct TALER_FullPaytoHashP h_payto;
     271            1 :   struct TALER_FullPayto payto = { NULL };
     272            1 :   struct InContext ctx = { 0 };
     273            1 :   struct TransferContext tctx = { 0 };
     274            1 :   struct TALER_Amount zero = TDB_amount ("0");
     275              : 
     276            1 :   TDB_FILL (reserve_pub,
     277              :             1);
     278            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     279              :           TALER_EXCHANGEDB_get_reserve_origin (pg,
     280              :                                                &reserve_pub,
     281              :                                                &h_payto,
     282              :                                                &payto));
     283            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     284              :           TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id (pg,
     285              :                                                                 0,
     286              :                                                                 &reserve_in_cb,
     287              :                                                                 &ctx));
     288            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     289              :           TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id_by_account (
     290              :             pg,
     291              :             "exchange-account-1",
     292              :             0,
     293              :             &reserve_in_cb,
     294              :             &ctx));
     295            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     296              :           TALER_EXCHANGEDB_iterate_exchange_credit_transfers (pg,
     297              :                                                               &zero,
     298              :                                                               0,
     299              :                                                               10,
     300              :                                                               NULL,
     301              :                                                               &transfer_cb,
     302              :                                                               &tctx));
     303            1 :   FAILIF (0 != ctx.total);
     304            1 :   FAILIF (0 != tctx.total);
     305            1 :   return 0;
     306              : }
     307              : 
     308              : 
     309              : /**
     310              :  * Importing a transfer creates the reserve, the account and the
     311              :  * `reserves_in` row, and doing it again is a no-op.
     312              :  *
     313              :  * @param pg the database context
     314              :  * @return 0 on success
     315              :  */
     316              : static int
     317            1 : check_import (struct TALER_EXCHANGEDB_PostgresContext *pg)
     318              : {
     319              :   struct TALER_ReservePublicKeyP reserve_pub;
     320              :   struct TALER_FullPaytoHashP h_payto;
     321            1 :   struct TALER_FullPayto payto = { NULL };
     322            1 :   struct TALER_FullPayto expect = {
     323              :     .full_payto = (char *) PAYTO_A
     324              :   };
     325              :   struct TALER_FullPaytoHashP expect_h;
     326            1 :   struct TALER_Amount expect_amount = TDB_amount ("10");
     327              :   enum GNUNET_DB_QueryStatus status;
     328              : 
     329            1 :   FAILIF (0 >
     330              :           import_one (pg,
     331              :                       "ri-job-a",
     332              :                       "exchange-account-1",
     333              :                       PAYTO_A,
     334              :                       10,
     335              :                       "10",
     336              :                       &reserve_pub,
     337              :                       &status));
     338            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != status);
     339            1 :   FAILIF (1 != TDB_count (pg,
     340              :                           "FROM reserves_in"));
     341            1 :   FAILIF (1 != TDB_count (pg,
     342              :                           "FROM reserves"));
     343              : 
     344            1 :   TALER_full_payto_hash (expect,
     345              :                          &expect_h);
     346            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     347              :           TALER_EXCHANGEDB_get_reserve_origin (pg,
     348              :                                                &reserve_pub,
     349              :                                                &h_payto,
     350              :                                                &payto));
     351            1 :   FAILIF (0 != GNUNET_memcmp (&h_payto,
     352              :                               &expect_h));
     353            1 :   FAILIF (0 != strcmp (payto.full_payto,
     354              :                        PAYTO_A));
     355            1 :   GNUNET_free (payto.full_payto);
     356              : 
     357              :   /* the same transfer again: the row is already there */
     358              :   {
     359              :     struct TALER_ReservePublicKeyP again;
     360              : 
     361            1 :     FAILIF (0 >
     362              :             import_one (pg,
     363              :                         "ri-job-a",
     364              :                         "exchange-account-1",
     365              :                         PAYTO_A,
     366              :                         10,
     367              :                         "10",
     368              :                         &again,
     369              :                         &status));
     370            1 :     FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != status);
     371            1 :     FAILIF (1 != TDB_count (pg,
     372              :                             "FROM reserves_in"));
     373              :   }
     374              : 
     375              :   {
     376              :     struct InContext ctx;
     377              : 
     378            1 :     memset (&ctx,
     379              :             0,
     380              :             sizeof (ctx));
     381            1 :     ctx.reserve_pub = &reserve_pub;
     382            1 :     FAILIF (0 >=
     383              :             TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id (
     384              :               pg,
     385              :               0,
     386              :               &reserve_in_cb,
     387              :               &ctx));
     388            1 :     FAILIF_C (1 != ctx.matched,
     389              :               GNUNET_free (ctx.sender));
     390            1 :     FAILIF_C (0 != TALER_amount_cmp (&ctx.credit,
     391              :                                      &expect_amount),
     392              :               GNUNET_free (ctx.sender));
     393            1 :     FAILIF_C (0 != strcmp (ctx.sender,
     394              :                            PAYTO_A),
     395              :               GNUNET_free (ctx.sender));
     396            1 :     GNUNET_free (ctx.sender);
     397              :   }
     398            1 :   return 0;
     399              : }
     400              : 
     401              : 
     402              : /**
     403              :  * With several transfers on file the iterators filter by serial and by
     404              :  * exchange bank account, and honour an aborting callback.
     405              :  *
     406              :  * @param pg the database context
     407              :  * @return 0 on success
     408              :  */
     409              : static int
     410            1 : check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
     411              : {
     412              :   struct TALER_ReservePublicKeyP r2;
     413              :   struct TALER_ReservePublicKeyP r3;
     414              :   struct InContext ctx;
     415              :   enum GNUNET_DB_QueryStatus status;
     416              : 
     417              :   /* a second transfer from the same account to the same exchange account */
     418            1 :   FAILIF (0 >
     419              :           import_one (pg,
     420              :                       "ri-job-a",
     421              :                       "exchange-account-1",
     422              :                       PAYTO_A,
     423              :                       11,
     424              :                       "2",
     425              :                       &r2,
     426              :                       &status));
     427            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != status);
     428              :   /* and one from a different account to a different exchange account */
     429            1 :   FAILIF (0 >
     430              :           import_one (pg,
     431              :                       "ri-job-b",
     432              :                       "exchange-account-2",
     433              :                       PAYTO_B,
     434              :                       12,
     435              :                       "5",
     436              :                       &r3,
     437              :                       &status));
     438            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != status);
     439            1 :   FAILIF (3 != TDB_count (pg,
     440              :                           "FROM reserves_in"));
     441              : 
     442            1 :   memset (&ctx,
     443              :           0,
     444              :           sizeof (ctx));
     445            1 :   ctx.reserve_pub = &r3;
     446            1 :   FAILIF (3 !=
     447              :           TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id (pg,
     448              :                                                                 0,
     449              :                                                                 &reserve_in_cb,
     450              :                                                                 &ctx));
     451            1 :   FAILIF_C (1 != ctx.matched,
     452              :             GNUNET_free (ctx.sender));
     453            1 :   FAILIF_C (0 != strcmp (ctx.sender,
     454              :                          PAYTO_B),
     455              :             GNUNET_free (ctx.sender));
     456            1 :   GNUNET_free (ctx.sender);
     457              : 
     458              :   /* by exchange bank account */
     459            1 :   memset (&ctx,
     460              :           0,
     461              :           sizeof (ctx));
     462            1 :   FAILIF (2 !=
     463              :           TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id_by_account (
     464              :             pg,
     465              :             "exchange-account-1",
     466              :             0,
     467              :             &reserve_in_cb,
     468              :             &ctx));
     469            1 :   GNUNET_free (ctx.sender);
     470            1 :   memset (&ctx,
     471              :           0,
     472              :           sizeof (ctx));
     473            1 :   FAILIF (1 !=
     474              :           TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id_by_account (
     475              :             pg,
     476              :             "exchange-account-2",
     477              :             0,
     478              :             &reserve_in_cb,
     479              :             &ctx));
     480            1 :   GNUNET_free (ctx.sender);
     481            1 :   memset (&ctx,
     482              :           0,
     483              :           sizeof (ctx));
     484            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     485              :           TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id_by_account (
     486              :             pg,
     487              :             "exchange-account-does-not-exist",
     488              :             0,
     489              :             &reserve_in_cb,
     490              :             &ctx));
     491            1 :   FAILIF (0 != ctx.total);
     492              : 
     493              :   /* A callback that gives up stops the iteration but is not an error: the
     494              :      status stays the number of rows the query matched, not the number the
     495              :      callback was shown. */
     496            1 :   memset (&ctx,
     497              :           0,
     498              :           sizeof (ctx));
     499            1 :   ctx.stop_after = 1;
     500            1 :   FAILIF (3 !=
     501              :           TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id (pg,
     502              :                                                                 0,
     503              :                                                                 &reserve_in_cb,
     504              :                                                                 &ctx));
     505            1 :   FAILIF (1 != ctx.total);
     506            1 :   GNUNET_free (ctx.sender);
     507            1 :   return 0;
     508              : }
     509              : 
     510              : 
     511              : /**
     512              :  * The serial number bound of the plain iterator excludes earlier rows.
     513              :  * The bound is inclusive, so passing the row of the second transfer
     514              :  * leaves the first one out and nothing else.
     515              :  *
     516              :  * @param pg the database context
     517              :  * @return 0 on success
     518              :  */
     519              : static int
     520            1 : check_serial_bound (struct TALER_EXCHANGEDB_PostgresContext *pg)
     521              : {
     522              :   struct InContext ctx;
     523              : 
     524            1 :   FAILIF (3 != TDB_count (pg,
     525              :                           "FROM reserves_in"));
     526            1 :   memset (&ctx,
     527              :           0,
     528              :           sizeof (ctx));
     529            1 :   FAILIF (2 !=
     530              :           TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id (pg,
     531              :                                                                 2,
     532              :                                                                 &reserve_in_cb,
     533              :                                                                 &ctx));
     534            1 :   GNUNET_free (ctx.sender);
     535            1 :   memset (&ctx,
     536              :           0,
     537              :           sizeof (ctx));
     538            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     539              :           TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id (pg,
     540              :                                                                 1000,
     541              :                                                                 &reserve_in_cb,
     542              :                                                                 &ctx));
     543            1 :   GNUNET_free (ctx.sender);
     544            1 :   return 0;
     545              : }
     546              : 
     547              : 
     548              : /**
     549              :  * The AML view of the same rows filters by amount and by account, and can
     550              :  * walk the table backwards.
     551              :  *
     552              :  * @param pg the database context
     553              :  * @return 0 on success
     554              :  */
     555              : static int
     556            1 : check_credit_transfers (struct TALER_EXCHANGEDB_PostgresContext *pg)
     557              : {
     558              :   struct TALER_NormalizedPayto npayto;
     559              :   struct TALER_NormalizedPaytoHashP h_payto;
     560            1 :   struct TALER_FullPayto payto_a = {
     561              :     .full_payto = (char *) PAYTO_A
     562              :   };
     563            1 :   struct TALER_Amount zero = TDB_amount ("0");
     564            1 :   struct TALER_Amount five = TDB_amount ("5");
     565            1 :   struct TALER_Amount huge = TDB_amount ("1000");
     566              :   struct TransferContext ctx;
     567              :   uint64_t lowest;
     568              : 
     569              :   /* everything, ascending */
     570            1 :   memset (&ctx,
     571              :           0,
     572              :           sizeof (ctx));
     573            1 :   FAILIF (3 !=
     574              :           TALER_EXCHANGEDB_iterate_exchange_credit_transfers (pg,
     575              :                                                               &zero,
     576              :                                                               0,
     577              :                                                               10,
     578              :                                                               NULL,
     579              :                                                               &transfer_cb,
     580              :                                                               &ctx));
     581            1 :   FAILIF (3 != ctx.total);
     582            1 :   FAILIF (10 + 2 + 5 != ctx.value_sum);
     583            1 :   lowest = ctx.first_row;
     584              : 
     585              :   /* the amount threshold drops the EUR:2 transfer */
     586            1 :   memset (&ctx,
     587              :           0,
     588              :           sizeof (ctx));
     589            1 :   FAILIF (2 !=
     590              :           TALER_EXCHANGEDB_iterate_exchange_credit_transfers (pg,
     591              :                                                               &five,
     592              :                                                               0,
     593              :                                                               10,
     594              :                                                               NULL,
     595              :                                                               &transfer_cb,
     596              :                                                               &ctx));
     597            1 :   FAILIF (10 + 5 != ctx.value_sum);
     598              : 
     599              :   /* a threshold nobody meets */
     600            1 :   memset (&ctx,
     601              :           0,
     602              :           sizeof (ctx));
     603            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     604              :           TALER_EXCHANGEDB_iterate_exchange_credit_transfers (pg,
     605              :                                                               &huge,
     606              :                                                               0,
     607              :                                                               10,
     608              :                                                               NULL,
     609              :                                                               &transfer_cb,
     610              :                                                               &ctx));
     611            1 :   FAILIF (0 != ctx.total);
     612              : 
     613              :   /* filtering by account keeps the two transfers from PAYTO_A */
     614            1 :   npayto = TALER_payto_normalize (payto_a);
     615            1 :   GNUNET_assert (NULL != npayto.normalized_payto);
     616            1 :   TALER_normalized_payto_hash (npayto,
     617              :                                &h_payto);
     618            1 :   GNUNET_free (npayto.normalized_payto);
     619            1 :   memset (&ctx,
     620              :           0,
     621              :           sizeof (ctx));
     622            1 :   FAILIF (2 !=
     623              :           TALER_EXCHANGEDB_iterate_exchange_credit_transfers (pg,
     624              :                                                               &zero,
     625              :                                                               0,
     626              :                                                               10,
     627              :                                                               &h_payto,
     628              :                                                               &transfer_cb,
     629              :                                                               &ctx));
     630            1 :   FAILIF (10 + 2 != ctx.value_sum);
     631              : 
     632              :   /* the limit caps the result set... */
     633            1 :   memset (&ctx,
     634              :           0,
     635              :           sizeof (ctx));
     636            1 :   FAILIF (1 !=
     637              :           TALER_EXCHANGEDB_iterate_exchange_credit_transfers (pg,
     638              :                                                               &zero,
     639              :                                                               0,
     640              :                                                               1,
     641              :                                                               NULL,
     642              :                                                               &transfer_cb,
     643              :                                                               &ctx));
     644            1 :   FAILIF (lowest != ctx.first_row);
     645              : 
     646              :   /* ...and a negative limit walks the table the other way, starting from
     647              :      the offset rather than from the beginning */
     648            1 :   memset (&ctx,
     649              :           0,
     650              :           sizeof (ctx));
     651            1 :   FAILIF (3 !=
     652              :           TALER_EXCHANGEDB_iterate_exchange_credit_transfers (pg,
     653              :                                                               &zero,
     654              :                                                               1000,
     655              :                                                               -10,
     656              :                                                               NULL,
     657              :                                                               &transfer_cb,
     658              :                                                               &ctx));
     659            1 :   FAILIF (lowest == ctx.first_row);
     660            1 :   FAILIF (10 + 2 + 5 != ctx.value_sum);
     661            1 :   return 0;
     662              : }
     663              : 
     664              : 
     665              : /**
     666              :  * The checks to run, in order.
     667              :  */
     668              : static const struct TDB_Test tests[] = {
     669              :   { "reserves-in-empty",
     670              :     &check_empty },
     671              :   { "reserves-in-import",
     672              :     &check_import },
     673              :   { "reserves-in-iterate",
     674              :     &check_iterate },
     675              :   { "reserves-in-serial-bound",
     676              :     &check_serial_bound },
     677              :   { "reserves-in-credit-transfers",
     678              :     &check_credit_transfers },
     679              :   { NULL, NULL }
     680              : };
     681              : 
     682              : 
     683              : int
     684            1 : main (int argc,
     685              :       char *const *argv)
     686              : {
     687            1 :   return TDB_main (argc,
     688              :                    argv,
     689              :                    "test-reserves-in",
     690              :                    "Tests for the exchangedb `reserves_in' table",
     691              :                    tests);
     692              : }
     693              : 
     694              : 
     695              : /* end of test_reserves_in.c */
        

Generated by: LCOV version 2.0-1