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

            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_purse_merges.c
      18              :  * @brief tests for the exchangedb functions whose primary table is
      19              :  *        `purse_merges`
      20              :  * @author Christian Grothoff
      21              :  *
      22              :  * Covers #TALER_EXCHANGEDB_do_purse_merge(),
      23              :  * #TALER_EXCHANGEDB_get_purse_merge(),
      24              :  * #TALER_EXCHANGEDB_iterate_purse_merges_above_serial_id(),
      25              :  * #TALER_EXCHANGEDB_iterate_wallet_merges().
      26              :  *
      27              :  * A merge is the exchange acting on the merge capability key: it moves a
      28              :  * paid-up purse into a reserve.  do_purse_merge() therefore declines when
      29              :  * the purse is not paid up and when the named partner exchange is unknown,
      30              :  * and reports a conflict when the purse was already merged elsewhere.
      31              :  */
      32              : #include "test_common.h"
      33              : #include "exchange-database/do_purse_deposit.h"
      34              : #include "exchange-database/do_purse_merge.h"
      35              : #include "exchange-database/get_purse_merge.h"
      36              : #include "exchange-database/get_reserve.h"
      37              : #include "exchange-database/iterate_purse_merges_above_serial_id.h"
      38              : #include "exchange-database/iterate_wallet_merges.h"
      39              : 
      40              : 
      41              : /**
      42              :  * Denomination the checks pay purses with.
      43              :  */
      44              : static struct TDB_Denom denom;
      45              : 
      46              : 
      47              : /**
      48              :  * Wallet account the reserve of the checks belongs to.
      49              :  */
      50              : static struct TDB_Account account;
      51              : 
      52              : 
      53              : /**
      54              :  * Build a timestamp from a number of seconds since the epoch.
      55              :  *
      56              :  * @param secs seconds since the epoch
      57              :  * @return the timestamp
      58              :  */
      59              : static struct GNUNET_TIME_Timestamp
      60            8 : ts (uint64_t secs)
      61              : {
      62            8 :   struct GNUNET_TIME_Absolute abs = {
      63            8 :     .abs_value_us = secs * 1000LLU * 1000LLU
      64              :   };
      65              : 
      66            8 :   return GNUNET_TIME_absolute_to_timestamp (abs);
      67              : }
      68              : 
      69              : 
      70              : /**
      71              :  * Closure for #merge_cb().
      72              :  */
      73              : struct MergeContext
      74              : {
      75              :   /**
      76              :    * How many rows did the callback see?
      77              :    */
      78              :   unsigned int total;
      79              : 
      80              :   /**
      81              :    * Stop after this many rows; 0 for no limit.
      82              :    */
      83              :   unsigned int stop_after;
      84              : 
      85              :   /**
      86              :    * Purse we are looking for, NULL to match nothing.
      87              :    */
      88              :   const struct TALER_PurseContractPublicKeyP *purse_pub;
      89              : 
      90              :   /**
      91              :    * How many times did we see it?
      92              :    */
      93              :   unsigned int matched;
      94              : 
      95              :   /**
      96              :    * Reserve reported for it.
      97              :    */
      98              :   struct TALER_ReservePublicKeyP reserve_pub;
      99              : 
     100              :   /**
     101              :    * Amount reported for it.
     102              :    */
     103              :   struct TALER_Amount amount;
     104              : 
     105              :   /**
     106              :    * Was a partner exchange reported for it?
     107              :    */
     108              :   bool have_partner;
     109              : };
     110              : 
     111              : 
     112              : /**
     113              :  * Callback for
     114              :  * #TALER_EXCHANGEDB_iterate_purse_merges_above_serial_id().
     115              :  *
     116              :  * @param cls a `struct MergeContext *`
     117              :  * @param rowid row of the merge
     118              :  * @param partner_base_url partner exchange, NULL for us
     119              :  * @param amount target amount of the purse
     120              :  * @param balance balance of the purse
     121              :  * @param flags flags of the purse
     122              :  * @param merge_pub merge capability key
     123              :  * @param reserve_pub reserve the purse went into
     124              :  * @param merge_sig signature affirming the merge
     125              :  * @param purse_pub the purse
     126              :  * @param merge_timestamp when it was merged
     127              :  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to stop
     128              :  */
     129              : static enum GNUNET_GenericReturnValue
     130            2 : merge_cb (void *cls,
     131              :           uint64_t rowid,
     132              :           const char *partner_base_url,
     133              :           const struct TALER_Amount *amount,
     134              :           const struct TALER_Amount *balance,
     135              :           enum TALER_WalletAccountMergeFlags flags,
     136              :           const struct TALER_PurseMergePublicKeyP *merge_pub,
     137              :           const struct TALER_ReservePublicKeyP *reserve_pub,
     138              :           const struct TALER_PurseMergeSignatureP *merge_sig,
     139              :           const struct TALER_PurseContractPublicKeyP *purse_pub,
     140              :           struct GNUNET_TIME_Timestamp merge_timestamp)
     141              : {
     142            2 :   struct MergeContext *ctx = cls;
     143              : 
     144              :   (void) rowid;
     145              :   (void) balance;
     146              :   (void) flags;
     147              :   (void) merge_pub;
     148              :   (void) merge_sig;
     149              :   (void) merge_timestamp;
     150            2 :   ctx->total++;
     151            2 :   if ( (NULL != ctx->purse_pub) &&
     152            1 :        (0 == GNUNET_memcmp (purse_pub,
     153              :                             ctx->purse_pub)) )
     154              :   {
     155            1 :     ctx->matched++;
     156            1 :     ctx->reserve_pub = *reserve_pub;
     157            1 :     ctx->amount = *amount;
     158            1 :     ctx->have_partner = (NULL != partner_base_url);
     159              :   }
     160            2 :   if ( (0 != ctx->stop_after) &&
     161            1 :        (ctx->total >= ctx->stop_after) )
     162            1 :     return GNUNET_SYSERR;
     163            1 :   return GNUNET_OK;
     164              : }
     165              : 
     166              : 
     167              : /**
     168              :  * Closure for #transfer_cb() and #amount_cb().
     169              :  */
     170              : struct SumContext
     171              : {
     172              :   /**
     173              :    * How many entries did the callback see?
     174              :    */
     175              :   unsigned int total;
     176              : 
     177              :   /**
     178              :    * Sum of the whole-unit parts of the amounts seen.
     179              :    */
     180              :   uint64_t value_sum;
     181              : 
     182              : };
     183              : 
     184              : 
     185              : /**
     186              :  * Callback for #TALER_EXCHANGEDB_iterate_wallet_merges().
     187              :  *
     188              :  * @param cls a `struct SumContext *`
     189              :  * @param row_id row of the merge
     190              :  * @param payto_uri wallet account involved
     191              :  * @param execution_time when the merge happened
     192              :  * @param amount how much was merged
     193              :  */
     194              : static void
     195            3 : transfer_cb (void *cls,
     196              :              uint64_t row_id,
     197              :              const char *payto_uri,
     198              :              struct GNUNET_TIME_Absolute execution_time,
     199              :              const struct TALER_Amount *amount)
     200              : {
     201            3 :   struct SumContext *ctx = cls;
     202              : 
     203              :   (void) row_id;
     204              :   (void) payto_uri;
     205              :   (void) execution_time;
     206            3 :   ctx->total++;
     207            3 :   ctx->value_sum += amount->value;
     208            3 : }
     209              : 
     210              : 
     211              : /**
     212              :  * Outcome of a merge.
     213              :  */
     214              : struct MergeStatus
     215              : {
     216              :   /**
     217              :    * Was the named partner exchange unknown?
     218              :    */
     219              :   bool no_partner;
     220              : 
     221              :   /**
     222              :    * Was the purse not paid up?
     223              :    */
     224              :   bool no_balance;
     225              : 
     226              :   /**
     227              :    * Was the purse already merged elsewhere?
     228              :    */
     229              :   bool in_conflict;
     230              : };
     231              : 
     232              : 
     233              : /**
     234              :  * Merge a purse into a reserve.
     235              :  *
     236              :  * @param pg the database context
     237              :  * @param purse purse to merge
     238              :  * @param reserve_pub reserve to credit
     239              :  * @param h_payto normalized account of the reserve's wallet
     240              :  * @param seed seed for the merge and reserve signatures
     241              :  * @param partner_url partner exchange, NULL for us
     242              :  * @param[out] st set to the outcome
     243              :  * @return transaction status
     244              :  */
     245              : static enum GNUNET_DB_QueryStatus
     246            4 : run_merge (struct TALER_EXCHANGEDB_PostgresContext *pg,
     247              :            const struct TDB_Purse *purse,
     248              :            const struct TALER_ReservePublicKeyP *reserve_pub,
     249              :            uint32_t seed,
     250              :            const char *partner_url,
     251              :            struct MergeStatus *st)
     252              : {
     253              :   struct TALER_PurseMergeSignatureP merge_sig;
     254              :   struct TALER_ReserveSignatureP reserve_sig;
     255              : 
     256            4 :   TDB_fill (&merge_sig,
     257              :             sizeof (merge_sig),
     258              :             seed);
     259            4 :   TDB_fill (&reserve_sig,
     260              :             sizeof (reserve_sig),
     261              :             seed);
     262            4 :   memset (st,
     263              :           0,
     264              :           sizeof (*st));
     265            4 :   return TALER_EXCHANGEDB_do_purse_merge (pg,
     266              :                                           &purse->purse_pub,
     267              :                                           &merge_sig,
     268              :                                           ts (1600000000),
     269              :                                           &reserve_sig,
     270              :                                           partner_url,
     271              :                                           reserve_pub,
     272              :                                           &st->no_partner,
     273              :                                           &st->no_balance,
     274              :                                           &st->in_conflict);
     275              : }
     276              : 
     277              : 
     278              : /**
     279              :  * Pay a purse up to its target amount.
     280              :  *
     281              :  * @param pg the database context
     282              :  * @param purse purse to fill
     283              :  * @param seed seed for the coin
     284              :  * @param amount how much to pay in, e.g. "5"
     285              :  */
     286              : static void
     287            2 : fill_purse (struct TALER_EXCHANGEDB_PostgresContext *pg,
     288              :             const struct TDB_Purse *purse,
     289              :             uint32_t seed,
     290              :             const char *amount)
     291              : {
     292              :   struct TALER_CoinPublicInfo coin;
     293              :   struct TALER_CoinSpendSignatureP coin_sig;
     294            2 :   struct TALER_Amount a = TDB_amount (amount);
     295              :   bool balance_ok;
     296              :   bool too_late;
     297              :   bool conflict;
     298              : 
     299            2 :   TDB_coin (pg,
     300              :             &denom,
     301              :             seed,
     302              :             &coin,
     303              :             NULL);
     304            2 :   TDB_fill (&coin_sig,
     305              :             sizeof (coin_sig),
     306              :             seed);
     307            2 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
     308              :                  TALER_EXCHANGEDB_do_purse_deposit (pg,
     309              :                                                     &purse->purse_pub,
     310              :                                                     &coin.coin_pub,
     311              :                                                     &a,
     312              :                                                     &coin_sig,
     313              :                                                     &a,
     314              :                                                     &balance_ok,
     315              :                                                     &too_late,
     316              :                                                     &conflict));
     317            2 :   GNUNET_assert (balance_ok);
     318            2 :   TDB_coin_free (&coin);
     319            2 : }
     320              : 
     321              : 
     322              : /**
     323              :  * Nothing is reported while the table is empty.
     324              :  *
     325              :  * @param pg the database context
     326              :  * @return 0 on success
     327              :  */
     328              : static int
     329            1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
     330              : {
     331              :   struct TALER_PurseContractPublicKeyP purse_pub;
     332              :   struct TALER_PurseMergeSignatureP merge_sig;
     333              :   struct GNUNET_TIME_Timestamp merge_timestamp;
     334              :   struct TALER_ReservePublicKeyP reserve_pub;
     335            1 :   struct MergeContext ctx = { 0 };
     336            1 :   struct SumContext sctx = { 0 };
     337            1 :   struct TALER_Amount zero = TDB_amount ("0");
     338            1 :   char *partner_url = NULL;
     339              :   bool refunded;
     340              : 
     341            1 :   TDB_denom (pg,
     342              :              10,
     343              :              "5",
     344              :              "0.1",
     345              :              &denom);
     346            1 :   TDB_account (pg,
     347              :                10,
     348              :                &account);
     349            1 :   TDB_FILL (purse_pub,
     350              :             1);
     351            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     352              :           TALER_EXCHANGEDB_get_purse_merge (pg,
     353              :                                             &purse_pub,
     354              :                                             &merge_sig,
     355              :                                             &merge_timestamp,
     356              :                                             &partner_url,
     357              :                                             &reserve_pub,
     358              :                                             &refunded));
     359            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     360              :           TALER_EXCHANGEDB_iterate_purse_merges_above_serial_id (
     361              :             pg,
     362              :             0,
     363              :             &merge_cb,
     364              :             &ctx));
     365            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     366              :           TALER_EXCHANGEDB_iterate_wallet_merges (pg,
     367              :                                                   &zero,
     368              :                                                   0,
     369              :                                                   10,
     370              :                                                   NULL,
     371              :                                                   &transfer_cb,
     372              :                                                   &sctx));
     373            1 :   FAILIF (0 != ctx.total);
     374            1 :   FAILIF (0 != sctx.total);
     375            1 :   return 0;
     376              : }
     377              : 
     378              : 
     379              : /**
     380              :  * A purse that has not been paid up cannot be merged.
     381              :  *
     382              :  * @param pg the database context
     383              :  * @return 0 on success
     384              :  */
     385              : static int
     386            1 : check_no_balance (struct TALER_EXCHANGEDB_PostgresContext *pg)
     387              : {
     388              :   struct TDB_Purse purse;
     389              :   struct TALER_ReservePublicKeyP reserve_pub;
     390              :   struct MergeStatus st;
     391              : 
     392            1 :   TDB_purse (pg,
     393              :              10,
     394              :              "5",
     395              :              ts (1700000000),
     396              :              &purse);
     397            1 :   TDB_reserve (pg,
     398              :                10,
     399              :                "0",
     400              :                &reserve_pub);
     401              :   /* only EUR:1 of the EUR:5 has been paid in */
     402            1 :   fill_purse (pg,
     403              :               &purse,
     404              :               20,
     405              :               "1");
     406            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     407              :           run_merge (pg,
     408              :                      &purse,
     409              :                      &reserve_pub,
     410              :                      10,
     411              :                      NULL,
     412              :                      &st));
     413            1 :   FAILIF (! st.no_balance);
     414            1 :   FAILIF (st.no_partner);
     415            1 :   FAILIF (st.in_conflict);
     416            1 :   FAILIF (0 != TDB_count (pg,
     417              :                           "FROM purse_merges"));
     418            1 :   return 0;
     419              : }
     420              : 
     421              : 
     422              : /**
     423              :  * A partner exchange the exchange does not know cannot receive a merge.
     424              :  *
     425              :  * @param pg the database context
     426              :  * @return 0 on success
     427              :  */
     428              : static int
     429            1 : check_no_partner (struct TALER_EXCHANGEDB_PostgresContext *pg)
     430              : {
     431              :   struct TDB_Purse purse;
     432              :   struct TALER_ReservePublicKeyP reserve_pub;
     433              :   struct MergeStatus st;
     434              : 
     435            1 :   TDB_purse (pg,
     436              :              10,
     437              :              "5",
     438              :              ts (1700000000),
     439              :              &purse);
     440            1 :   TDB_fill (&reserve_pub,
     441              :             sizeof (reserve_pub),
     442              :             10);
     443            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     444              :           run_merge (pg,
     445              :                      &purse,
     446              :                      &reserve_pub,
     447              :                      10,
     448              :                      "https://partner.example/",
     449              :                      &st));
     450            1 :   FAILIF (! st.no_partner);
     451            1 :   FAILIF (0 != TDB_count (pg,
     452              :                           "FROM purse_merges"));
     453            1 :   return 0;
     454              : }
     455              : 
     456              : 
     457              : /**
     458              :  * A paid-up purse is merged into the reserve, which is credited.
     459              :  *
     460              :  * @param pg the database context
     461              :  * @return 0 on success
     462              :  */
     463              : static int
     464            1 : check_merge (struct TALER_EXCHANGEDB_PostgresContext *pg)
     465              : {
     466              :   struct TDB_Purse purse;
     467              :   struct TALER_ReservePublicKeyP reserve_pub;
     468              :   struct TALER_ReservePublicKeyP got_reserve;
     469              :   struct TALER_EXCHANGEDB_Reserve reserve;
     470              :   struct MergeStatus st;
     471              :   struct MergeContext ctx;
     472              :   struct TALER_PurseMergeSignatureP merge_sig;
     473              :   struct TALER_PurseMergeSignatureP expect_sig;
     474              :   struct GNUNET_TIME_Timestamp merge_timestamp;
     475            1 :   struct TALER_Amount expect_balance = TDB_amount ("5");
     476            1 :   char *partner_url = NULL;
     477            1 :   bool refunded = true;
     478              : 
     479            1 :   TDB_purse (pg,
     480              :              10,
     481              :              "5",
     482              :              ts (1700000000),
     483              :              &purse);
     484            1 :   TDB_fill (&reserve_pub,
     485              :             sizeof (reserve_pub),
     486              :             10);
     487              :   /* pay in the remaining EUR:4 */
     488            1 :   fill_purse (pg,
     489              :               &purse,
     490              :               21,
     491              :               "4");
     492            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     493              :           run_merge (pg,
     494              :                      &purse,
     495              :                      &reserve_pub,
     496              :                      10,
     497              :                      NULL,
     498              :                      &st));
     499            1 :   FAILIF (st.no_balance);
     500            1 :   FAILIF (st.no_partner);
     501            1 :   FAILIF (st.in_conflict);
     502            1 :   FAILIF (1 != TDB_count (pg,
     503              :                           "FROM purse_merges"));
     504              : 
     505              :   /* the reserve got the money */
     506            1 :   memset (&reserve,
     507              :           0,
     508              :           sizeof (reserve));
     509            1 :   reserve.pub = reserve_pub;
     510            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     511              :           TALER_EXCHANGEDB_get_reserve (pg,
     512              :                                         &reserve));
     513            1 :   FAILIF (0 != TALER_amount_cmp (&reserve.balance,
     514              :                                  &expect_balance));
     515              : 
     516            1 :   TDB_FILL (expect_sig,
     517              :             10);
     518            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     519              :           TALER_EXCHANGEDB_get_purse_merge (pg,
     520              :                                             &purse.purse_pub,
     521              :                                             &merge_sig,
     522              :                                             &merge_timestamp,
     523              :                                             &partner_url,
     524              :                                             &got_reserve,
     525              :                                             &refunded));
     526            1 :   FAILIF (0 != GNUNET_memcmp (&merge_sig,
     527              :                               &expect_sig));
     528            1 :   FAILIF (0 != GNUNET_memcmp (&got_reserve,
     529              :                               &reserve_pub));
     530            1 :   FAILIF (GNUNET_TIME_timestamp_cmp (merge_timestamp,
     531              :                                      !=,
     532              :                                      ts (1600000000)));
     533            1 :   FAILIF (refunded);
     534              :   /* the purse stayed with us, so there is no partner exchange */
     535            1 :   FAILIF (NULL != partner_url);
     536              : 
     537            1 :   memset (&ctx,
     538              :           0,
     539              :           sizeof (ctx));
     540            1 :   ctx.purse_pub = &purse.purse_pub;
     541            1 :   FAILIF (0 >=
     542              :           TALER_EXCHANGEDB_iterate_purse_merges_above_serial_id (
     543              :             pg,
     544              :             0,
     545              :             &merge_cb,
     546              :             &ctx));
     547            1 :   FAILIF (1 != ctx.matched);
     548            1 :   FAILIF (0 != GNUNET_memcmp (&ctx.reserve_pub,
     549              :                               &reserve_pub));
     550            1 :   FAILIF (0 != TALER_amount_cmp (&ctx.amount,
     551              :                                  &expect_balance));
     552            1 :   FAILIF (ctx.have_partner);
     553              : 
     554              :   /* merging the same purse into a different reserve is a conflict */
     555              :   {
     556              :     struct TALER_ReservePublicKeyP other;
     557              : 
     558            1 :     TDB_reserve (pg,
     559              :                  12,
     560              :                  "0",
     561              :                  &other);
     562            1 :     FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     563              :             run_merge (pg,
     564              :                        &purse,
     565              :                        &other,
     566              :                        12,
     567              :                        NULL,
     568              :                        &st));
     569            1 :     FAILIF (! st.in_conflict);
     570            1 :     FAILIF (1 != TDB_count (pg,
     571              :                             "FROM purse_merges"));
     572              :   }
     573            1 :   return 0;
     574              : }
     575              : 
     576              : 
     577              : /**
     578              :  * The wallet's view of its merges reports the purse once the reserve is
     579              :  * tied to a wallet account.
     580              :  *
     581              :  * @param pg the database context
     582              :  * @return 0 on success
     583              :  */
     584              : static int
     585            1 : check_wallet_merges (struct TALER_EXCHANGEDB_PostgresContext *pg)
     586              : {
     587              :   struct TALER_ReservePublicKeyP reserve_pub;
     588            1 :   struct TALER_Amount zero = TDB_amount ("0");
     589            1 :   struct TALER_Amount huge = TDB_amount ("1000");
     590              :   struct SumContext ctx;
     591              :   char *rhex;
     592              :   char *nhex;
     593              : 
     594              :   /* Until the reserve is known to belong to a wallet account, the view
     595              :      has nothing to join against. */
     596            1 :   memset (&ctx,
     597              :           0,
     598              :           sizeof (ctx));
     599            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     600              :           TALER_EXCHANGEDB_iterate_wallet_merges (pg,
     601              :                                                   &zero,
     602              :                                                   0,
     603              :                                                   10,
     604              :                                                   NULL,
     605              :                                                   &transfer_cb,
     606              :                                                   &ctx));
     607            1 :   FAILIF (0 != ctx.total);
     608              : 
     609            1 :   TDB_fill (&reserve_pub,
     610              :             sizeof (reserve_pub),
     611              :             10);
     612            1 :   rhex = TDB_hex (&reserve_pub,
     613              :                   sizeof (reserve_pub));
     614            1 :   nhex = TDB_hex (&account.h_normalized,
     615              :                   sizeof (account.h_normalized));
     616            1 :   FAILIF_C (GNUNET_OK !=
     617              :             TDB_exec (pg,
     618              :                       "UPDATE kyc_targets"
     619              :                       " SET target_pub=decode('%s','hex')"
     620              :                       "    ,is_wallet=TRUE"
     621              :                       " WHERE h_normalized_payto=decode('%s','hex');",
     622              :                       rhex,
     623              :                       nhex),
     624              :             GNUNET_free (rhex); GNUNET_free (nhex));
     625            1 :   GNUNET_free (rhex);
     626            1 :   GNUNET_free (nhex);
     627              : 
     628            1 :   memset (&ctx,
     629              :           0,
     630              :           sizeof (ctx));
     631            1 :   FAILIF (1 !=
     632              :           TALER_EXCHANGEDB_iterate_wallet_merges (pg,
     633              :                                                   &zero,
     634              :                                                   0,
     635              :                                                   10,
     636              :                                                   NULL,
     637              :                                                   &transfer_cb,
     638              :                                                   &ctx));
     639            1 :   FAILIF (5 != ctx.value_sum);
     640              : 
     641              :   /* filtering by that very account keeps it */
     642            1 :   memset (&ctx,
     643              :           0,
     644              :           sizeof (ctx));
     645            1 :   FAILIF (1 !=
     646              :           TALER_EXCHANGEDB_iterate_wallet_merges (pg,
     647              :                                                   &zero,
     648              :                                                   0,
     649              :                                                   10,
     650              :                                                   &account.h_normalized,
     651              :                                                   &transfer_cb,
     652              :                                                   &ctx));
     653            1 :   FAILIF (1 != ctx.total);
     654              : 
     655              :   /* a threshold nobody meets drops it */
     656            1 :   memset (&ctx,
     657              :           0,
     658              :           sizeof (ctx));
     659            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     660              :           TALER_EXCHANGEDB_iterate_wallet_merges (pg,
     661              :                                                   &huge,
     662              :                                                   0,
     663              :                                                   10,
     664              :                                                   NULL,
     665              :                                                   &transfer_cb,
     666              :                                                   &ctx));
     667            1 :   FAILIF (0 != ctx.total);
     668              : 
     669              :   /* and the negative limit walks the table the other way */
     670            1 :   memset (&ctx,
     671              :           0,
     672              :           sizeof (ctx));
     673            1 :   FAILIF (1 !=
     674              :           TALER_EXCHANGEDB_iterate_wallet_merges (pg,
     675              :                                                   &zero,
     676              :                                                   1000,
     677              :                                                   -10,
     678              :                                                   NULL,
     679              :                                                   &transfer_cb,
     680              :                                                   &ctx));
     681            1 :   FAILIF (1 != ctx.total);
     682            1 :   return 0;
     683              : }
     684              : 
     685              : 
     686              : /**
     687              :  * The iterators' bounds and abort return behave as documented.
     688              :  *
     689              :  * @param pg the database context
     690              :  * @return 0 on success
     691              :  */
     692              : static int
     693            1 : check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
     694              : {
     695              :   struct MergeContext ctx;
     696              : 
     697            1 :   memset (&ctx,
     698              :           0,
     699              :           sizeof (ctx));
     700            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     701              :           TALER_EXCHANGEDB_iterate_purse_merges_above_serial_id (
     702              :             pg,
     703              :             1000,
     704              :             &merge_cb,
     705              :             &ctx));
     706            1 :   FAILIF (0 != ctx.total);
     707            1 :   memset (&ctx,
     708              :           0,
     709              :           sizeof (ctx));
     710            1 :   ctx.stop_after = 1;
     711            1 :   FAILIF (1 !=
     712              :           TALER_EXCHANGEDB_iterate_purse_merges_above_serial_id (
     713              :             pg,
     714              :             0,
     715              :             &merge_cb,
     716              :             &ctx));
     717            1 :   FAILIF (1 != ctx.total);
     718            1 :   return 0;
     719              : }
     720              : 
     721              : 
     722              : /**
     723              :  * The checks to run, in order.
     724              :  */
     725              : static const struct TDB_Test tests[] = {
     726              :   { "purse-merges-empty",
     727              :     &check_empty },
     728              :   { "purse-merges-no-balance",
     729              :     &check_no_balance },
     730              :   { "purse-merges-no-partner",
     731              :     &check_no_partner },
     732              :   { "purse-merges-merge",
     733              :     &check_merge },
     734              :   { "purse-merges-wallet-merges",
     735              :     &check_wallet_merges },
     736              :   { "purse-merges-iterate",
     737              :     &check_iterate },
     738              :   { NULL, NULL }
     739              : };
     740              : 
     741              : 
     742              : int
     743            1 : main (int argc,
     744              :       char *const *argv)
     745              : {
     746              :   int ret;
     747              : 
     748            1 :   ret = TDB_main (argc,
     749              :                   argv,
     750              :                   "test-purse-merges",
     751              :                   "Tests for the exchangedb `purse_merges' table",
     752              :                   tests);
     753            1 :   TDB_account_free (&account);
     754            1 :   TDB_denom_free (&denom);
     755            1 :   return ret;
     756              : }
     757              : 
     758              : 
     759              : /* end of test_purse_merges.c */
        

Generated by: LCOV version 2.0-1