LCOV - code coverage report
Current view: top level - exchangedb - test_reserves.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 135 135
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.c
      18              :  * @brief tests for the exchangedb functions whose primary table is
      19              :  *        `reserves`
      20              :  * @author Christian Grothoff
      21              :  *
      22              :  * Covers #TALER_EXCHANGEDB_get_reserve(),
      23              :  * #TALER_EXCHANGEDB_update_reserve(),
      24              :  * #TALER_EXCHANGEDB_get_reserve_balance(),
      25              :  * #TALER_EXCHANGEDB_get_reserve_close_info(),
      26              :  * #TALER_EXCHANGEDB_iterate_expired_reserves() and
      27              :  * #TALER_EXCHANGEDB_gc().
      28              :  *
      29              :  * A reserve may or may not have a `reserves_in` row: one funded by a bank
      30              :  * transfer has one, one created by a peer-to-peer merge does not.  Several
      31              :  * of these functions LEFT JOIN through it to get the originating account,
      32              :  * so both shapes are exercised.  The garbage collection check runs last,
      33              :  * as it removes rows the earlier checks created.
      34              :  */
      35              : #include "test_common.h"
      36              : #include "exchange-database/gc.h"
      37              : #include "exchange-database/get_reserve.h"
      38              : #include "exchange-database/get_reserve_balance.h"
      39              : #include "exchange-database/get_reserve_close_info.h"
      40              : #include "exchange-database/iterate_expired_reserves.h"
      41              : #include "exchange-database/update_reserve.h"
      42              : 
      43              : 
      44              : /**
      45              :  * One second in microseconds.  Timestamps read back through
      46              :  * GNUNET_PQ_result_spec_timestamp() have to be whole seconds, so the
      47              :  * checks never write a raw microsecond value that is not a multiple of
      48              :  * this.
      49              :  */
      50              : #define SECOND 1000000LLU
      51              : 
      52              : 
      53              : /**
      54              :  * Create a reserve with an explicit expiration and garbage collection
      55              :  * date, which TDB_reserve() does not offer.
      56              :  *
      57              :  * @param pg the database context
      58              :  * @param seed seed for the reserve's public key
      59              :  * @param balance balance of the reserve, e.g. "10"
      60              :  * @param expiration_us expiration date, in microseconds since the epoch
      61              :  * @param gc_us garbage collection date, in microseconds since the epoch
      62              :  * @param[out] reserve_pub set to the public key of the reserve
      63              :  */
      64              : static void
      65            5 : make_reserve (struct TALER_EXCHANGEDB_PostgresContext *pg,
      66              :               uint32_t seed,
      67              :               const char *balance,
      68              :               uint64_t expiration_us,
      69              :               uint64_t gc_us,
      70              :               struct TALER_ReservePublicKeyP *reserve_pub)
      71              : {
      72            5 :   struct TALER_Amount b = TDB_amount (balance);
      73              :   char *hex;
      74              : 
      75            5 :   TDB_fill (reserve_pub,
      76              :             sizeof (*reserve_pub),
      77              :             seed);
      78            5 :   hex = TDB_hex (reserve_pub,
      79              :                  sizeof (*reserve_pub));
      80            5 :   GNUNET_assert (GNUNET_OK ==
      81              :                  TDB_exec (pg,
      82              :                            "INSERT INTO reserves"
      83              :                            " (reserve_pub,current_balance"
      84              :                            " ,expiration_date,gc_date)"
      85              :                            " VALUES (decode('%s','hex')"
      86              :                            "        ,ROW(%llu,%u)::taler_amount"
      87              :                            "        ,%llu,%llu)"
      88              :                            " ON CONFLICT DO NOTHING;",
      89              :                            hex,
      90              :                            (unsigned long long) b.value,
      91              :                            (unsigned int) b.fraction,
      92              :                            (unsigned long long) expiration_us,
      93              :                            (unsigned long long) gc_us));
      94            5 :   GNUNET_free (hex);
      95            5 : }
      96              : 
      97              : 
      98              : /**
      99              :  * Closure for #expired_cb().
     100              :  */
     101              : struct ExpiredContext
     102              : {
     103              :   /**
     104              :    * How many reserves did the callback see?
     105              :    */
     106              :   unsigned int total;
     107              : 
     108              :   /**
     109              :    * Public key of the last reserve seen.
     110              :    */
     111              :   struct TALER_ReservePublicKeyP reserve_pub;
     112              : 
     113              :   /**
     114              :    * Balance of the last reserve seen.
     115              :    */
     116              :   struct TALER_Amount left;
     117              : 
     118              :   /**
     119              :    * Set if the last reserve was reported with an originating account.
     120              :    */
     121              :   bool have_account;
     122              : 
     123              :   /**
     124              :    * Close request row of the last reserve seen.
     125              :    */
     126              :   uint64_t close_request_row;
     127              : };
     128              : 
     129              : 
     130              : /**
     131              :  * Callback for #TALER_EXCHANGEDB_iterate_expired_reserves().
     132              :  *
     133              :  * @param cls a `struct ExpiredContext *`
     134              :  * @param reserve_pub the expired reserve
     135              :  * @param left balance left in the reserve
     136              :  * @param account_details originating account
     137              :  * @param expiration_date when the reserve expired
     138              :  * @param close_request_row row of the close request, 0 if none
     139              :  * @return #GNUNET_OK
     140              :  */
     141              : static enum GNUNET_GenericReturnValue
     142            2 : expired_cb (void *cls,
     143              :             const struct TALER_ReservePublicKeyP *reserve_pub,
     144              :             const struct TALER_Amount *left,
     145              :             const struct TALER_FullPayto account_details,
     146              :             struct GNUNET_TIME_Timestamp expiration_date,
     147              :             uint64_t close_request_row)
     148              : {
     149            2 :   struct ExpiredContext *ctx = cls;
     150              : 
     151              :   (void) expiration_date;
     152            2 :   ctx->total++;
     153            2 :   ctx->reserve_pub = *reserve_pub;
     154            2 :   ctx->left = *left;
     155            2 :   ctx->have_account = (NULL != account_details.full_payto);
     156            2 :   ctx->close_request_row = close_request_row;
     157            2 :   return GNUNET_OK;
     158              : }
     159              : 
     160              : 
     161              : /**
     162              :  * Nothing is known about a reserve that was never created.
     163              :  *
     164              :  * @param pg the database context
     165              :  * @return 0 on success
     166              :  */
     167              : static int
     168            1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
     169              : {
     170              :   struct TALER_EXCHANGEDB_Reserve reserve;
     171              :   struct TALER_Amount balance;
     172            1 :   struct TALER_FullPayto payto = { NULL };
     173            1 :   struct ExpiredContext ctx = { 0 };
     174              : 
     175            1 :   memset (&reserve,
     176              :           0,
     177              :           sizeof (reserve));
     178            1 :   TDB_FILL (reserve.pub,
     179              :             1);
     180            1 :   reserve.balance = TDB_amount ("1");
     181            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     182              :           TALER_EXCHANGEDB_get_reserve (pg,
     183              :                                         &reserve));
     184            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     185              :           TALER_EXCHANGEDB_update_reserve (pg,
     186              :                                            &reserve));
     187            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     188              :           TALER_EXCHANGEDB_get_reserve_balance (pg,
     189              :                                                 &reserve.pub,
     190              :                                                 &balance,
     191              :                                                 &payto));
     192            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     193              :           TALER_EXCHANGEDB_get_reserve_close_info (pg,
     194              :                                                    &reserve.pub,
     195              :                                                    &balance,
     196              :                                                    &payto));
     197            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     198              :           TALER_EXCHANGEDB_iterate_expired_reserves (pg,
     199              :                                                      GNUNET_TIME_timestamp_get (
     200              :                                                        ),
     201              :                                                      &expired_cb,
     202              :                                                      &ctx));
     203            1 :   FAILIF (0 != ctx.total);
     204            1 :   FAILIF (0 != TDB_count (pg,
     205              :                           "FROM reserves"));
     206            1 :   return 0;
     207              : }
     208              : 
     209              : 
     210              : /**
     211              :  * A reserve without a `reserves_in` row is found, with no origin account.
     212              :  *
     213              :  * @param pg the database context
     214              :  * @return 0 on success
     215              :  */
     216              : static int
     217            1 : check_without_origin (struct TALER_EXCHANGEDB_PostgresContext *pg)
     218              : {
     219              :   struct TALER_EXCHANGEDB_Reserve reserve;
     220              :   struct TALER_Amount balance;
     221            1 :   struct TALER_Amount expect = TDB_amount ("10");
     222            1 :   struct TALER_FullPayto payto = {
     223              :     .full_payto = (char *) "sentinel"
     224              :   };
     225              : 
     226            1 :   memset (&reserve,
     227              :           0,
     228              :           sizeof (reserve));
     229            1 :   TDB_reserve (pg,
     230              :                2,
     231              :                "10",
     232              :                &reserve.pub);
     233            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     234              :           TALER_EXCHANGEDB_get_reserve (pg,
     235              :                                         &reserve));
     236            1 :   FAILIF (0 != TALER_amount_cmp (&reserve.balance,
     237              :                                  &expect));
     238            1 :   FAILIF (GNUNET_TIME_absolute_is_zero (reserve.expiry.abs_time));
     239            1 :   FAILIF (GNUNET_TIME_absolute_is_zero (reserve.gc.abs_time));
     240              : 
     241            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     242              :           TALER_EXCHANGEDB_get_reserve_balance (pg,
     243              :                                                 &reserve.pub,
     244              :                                                 &balance,
     245              :                                                 &payto));
     246            1 :   FAILIF (0 != TALER_amount_cmp (&balance,
     247              :                                  &expect));
     248            1 :   FAILIF (NULL != payto.full_payto);
     249              : 
     250            1 :   payto.full_payto = (char *) "sentinel";
     251            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     252              :           TALER_EXCHANGEDB_get_reserve_close_info (pg,
     253              :                                                    &reserve.pub,
     254              :                                                    &balance,
     255              :                                                    &payto));
     256            1 :   FAILIF (0 != TALER_amount_cmp (&balance,
     257              :                                  &expect));
     258            1 :   FAILIF (NULL != payto.full_payto);
     259            1 :   return 0;
     260              : }
     261              : 
     262              : 
     263              : /**
     264              :  * A reserve funded by a bank transfer reports its origin account.
     265              :  *
     266              :  * @param pg the database context
     267              :  * @return 0 on success
     268              :  */
     269              : static int
     270            1 : check_with_origin (struct TALER_EXCHANGEDB_PostgresContext *pg)
     271              : {
     272              :   struct TDB_Account account;
     273              :   struct TALER_ReservePublicKeyP reserve_pub;
     274              :   struct TALER_Amount balance;
     275            1 :   struct TALER_Amount expect = TDB_amount ("7");
     276            1 :   struct TALER_FullPayto payto = { NULL };
     277              : 
     278            1 :   TDB_account (pg,
     279              :                3,
     280              :                &account);
     281            1 :   TDB_reserve_in (pg,
     282              :                   &account,
     283              :                   3,
     284              :                   "7",
     285              :                   &reserve_pub);
     286            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     287              :             TALER_EXCHANGEDB_get_reserve_balance (pg,
     288              :                                                   &reserve_pub,
     289              :                                                   &balance,
     290              :                                                   &payto),
     291              :             TDB_account_free (&account));
     292            1 :   FAILIF_C (0 != TALER_amount_cmp (&balance,
     293              :                                    &expect),
     294              :             TDB_account_free (&account));
     295            1 :   FAILIF_C (NULL == payto.full_payto,
     296              :             TDB_account_free (&account));
     297            1 :   FAILIF_C (0 != strcmp (payto.full_payto,
     298              :                          account.payto.full_payto),
     299              :             GNUNET_free (payto.full_payto); TDB_account_free (&account));
     300            1 :   GNUNET_free (payto.full_payto);
     301              : 
     302            1 :   payto.full_payto = NULL;
     303            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     304              :             TALER_EXCHANGEDB_get_reserve_close_info (pg,
     305              :                                                      &reserve_pub,
     306              :                                                      &balance,
     307              :                                                      &payto),
     308              :             TDB_account_free (&account));
     309            1 :   FAILIF_C (NULL == payto.full_payto,
     310              :             TDB_account_free (&account));
     311            1 :   FAILIF_C (0 != strcmp (payto.full_payto,
     312              :                          account.payto.full_payto),
     313              :             GNUNET_free (payto.full_payto); TDB_account_free (&account));
     314            1 :   GNUNET_free (payto.full_payto);
     315            1 :   TDB_account_free (&account);
     316            1 :   return 0;
     317              : }
     318              : 
     319              : 
     320              : /**
     321              :  * Updating a reserve writes back the balance and both dates.
     322              :  *
     323              :  * @param pg the database context
     324              :  * @return 0 on success
     325              :  */
     326              : static int
     327            1 : check_update (struct TALER_EXCHANGEDB_PostgresContext *pg)
     328              : {
     329              :   struct TALER_EXCHANGEDB_Reserve reserve;
     330              :   struct TALER_EXCHANGEDB_Reserve got;
     331            1 :   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
     332              : 
     333            1 :   memset (&reserve,
     334              :           0,
     335              :           sizeof (reserve));
     336            1 :   TDB_FILL (reserve.pub,
     337              :             2);
     338            1 :   reserve.balance = TDB_amount ("3.5");
     339            1 :   reserve.expiry = GNUNET_TIME_absolute_to_timestamp (now);
     340            1 :   reserve.gc = GNUNET_TIME_absolute_to_timestamp (
     341              :     GNUNET_TIME_absolute_add (now,
     342              :                               GNUNET_TIME_UNIT_HOURS));
     343            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     344              :           TALER_EXCHANGEDB_update_reserve (pg,
     345              :                                            &reserve));
     346            1 :   memset (&got,
     347              :           0,
     348              :           sizeof (got));
     349            1 :   got.pub = reserve.pub;
     350            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     351              :           TALER_EXCHANGEDB_get_reserve (pg,
     352              :                                         &got));
     353            1 :   FAILIF (0 != TALER_amount_cmp (&got.balance,
     354              :                                  &reserve.balance));
     355            1 :   FAILIF (GNUNET_TIME_timestamp_cmp (got.expiry,
     356              :                                      !=,
     357              :                                      reserve.expiry));
     358            1 :   FAILIF (GNUNET_TIME_timestamp_cmp (got.gc,
     359              :                                      !=,
     360              :                                      reserve.gc));
     361            1 :   return 0;
     362              : }
     363              : 
     364              : 
     365              : /**
     366              :  * Only an expired reserve that still holds money and has an originating
     367              :  * account is reported for closing.
     368              :  *
     369              :  * @param pg the database context
     370              :  * @return 0 on success
     371              :  */
     372              : static int
     373            1 : check_expired (struct TALER_EXCHANGEDB_PostgresContext *pg)
     374              : {
     375              :   struct TDB_Account account;
     376              :   struct TALER_ReservePublicKeyP funded;
     377              :   struct TALER_ReservePublicKeyP drained;
     378              :   struct ExpiredContext ctx;
     379            1 :   struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
     380            1 :   struct TALER_Amount expect = TDB_amount ("7");
     381              :   char *hex;
     382              : 
     383              :   /* nothing has expired yet: the reserves so far all expire in 2096 */
     384            1 :   memset (&ctx,
     385              :           0,
     386              :           sizeof (ctx));
     387            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     388              :           TALER_EXCHANGEDB_iterate_expired_reserves (pg,
     389              :                                                      now,
     390              :                                                      &expired_cb,
     391              :                                                      &ctx));
     392              : 
     393              :   /* expire the funded reserve from check_with_origin() */
     394            1 :   TDB_account (pg,
     395              :                3,
     396              :                &account);
     397            1 :   TDB_fill (&funded,
     398              :             sizeof (funded),
     399              :             3);
     400            1 :   hex = TDB_hex (&funded,
     401              :                  sizeof (funded));
     402            1 :   FAILIF_C (GNUNET_OK !=
     403              :             TDB_exec (pg,
     404              :                       "UPDATE reserves"
     405              :                       " SET expiration_date=1000000"
     406              :                       " WHERE reserve_pub=decode('%s','hex');",
     407              :                       hex),
     408              :             GNUNET_free (hex); TDB_account_free (&account));
     409            1 :   GNUNET_free (hex);
     410            1 :   memset (&ctx,
     411              :           0,
     412              :           sizeof (ctx));
     413            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     414              :             TALER_EXCHANGEDB_iterate_expired_reserves (pg,
     415              :                                                        now,
     416              :                                                        &expired_cb,
     417              :                                                        &ctx),
     418              :             TDB_account_free (&account));
     419            1 :   FAILIF_C (1 != ctx.total,
     420              :             TDB_account_free (&account));
     421            1 :   FAILIF_C (0 != GNUNET_memcmp (&ctx.reserve_pub,
     422              :                                 &funded),
     423              :             TDB_account_free (&account));
     424            1 :   FAILIF_C (0 != TALER_amount_cmp (&ctx.left,
     425              :                                    &expect),
     426              :             TDB_account_free (&account));
     427            1 :   FAILIF_C (! ctx.have_account,
     428              :             TDB_account_free (&account));
     429              : 
     430              :   /* an expired reserve with a zero balance is not worth closing */
     431            1 :   make_reserve (pg,
     432              :                 4,
     433              :                 "0",
     434              :                 SECOND,
     435              :                 SECOND,
     436              :                 &drained);
     437              :   {
     438            1 :     char *rhex = TDB_hex (&drained,
     439              :                           sizeof (drained));
     440            1 :     char *ahex = TDB_hex (&account.h_full,
     441              :                           sizeof (account.h_full));
     442              : 
     443            1 :     FAILIF_C (GNUNET_OK !=
     444              :               TDB_exec (pg,
     445              :                         "INSERT INTO reserves_in"
     446              :                         " (reserve_pub,wire_reference,credit"
     447              :                         " ,wire_source_h_payto"
     448              :                         " ,exchange_account_section,execution_date)"
     449              :                         " VALUES (decode('%s','hex'),44"
     450              :                         "        ,ROW(0,0)::taler_amount"
     451              :                         "        ,decode('%s','hex'),'exchange-account-1'"
     452              :                         "        ,1000000);",
     453              :                         rhex,
     454              :                         ahex),
     455              :               GNUNET_free (rhex); GNUNET_free (ahex);
     456              :               TDB_account_free (&account));
     457            1 :     GNUNET_free (rhex);
     458            1 :     GNUNET_free (ahex);
     459              :   }
     460            1 :   memset (&ctx,
     461              :           0,
     462              :           sizeof (ctx));
     463            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     464              :             TALER_EXCHANGEDB_iterate_expired_reserves (pg,
     465              :                                                        now,
     466              :                                                        &expired_cb,
     467              :                                                        &ctx),
     468              :             TDB_account_free (&account));
     469              :   /* the query returns at most one reserve, and it is still the funded one */
     470            1 :   FAILIF_C (1 != ctx.total,
     471              :             TDB_account_free (&account));
     472            1 :   FAILIF_C (0 != GNUNET_memcmp (&ctx.reserve_pub,
     473              :                                 &funded),
     474              :             TDB_account_free (&account));
     475            1 :   TDB_account_free (&account);
     476              : 
     477              :   /* an expired reserve with money but no originating account is skipped:
     478              :      it was created by a merge, so there is nowhere to wire the money back
     479              :      to.  Drain the funded one first so it cannot mask the result. */
     480            1 :   hex = TDB_hex (&funded,
     481              :                  sizeof (funded));
     482            1 :   FAILIF_C (GNUNET_OK !=
     483              :             TDB_exec (pg,
     484              :                       "UPDATE reserves"
     485              :                       " SET current_balance=ROW(0,0)::taler_amount"
     486              :                       " WHERE reserve_pub=decode('%s','hex');",
     487              :                       hex),
     488              :             GNUNET_free (hex));
     489            1 :   GNUNET_free (hex);
     490              :   {
     491              :     struct TALER_ReservePublicKeyP merged;
     492              : 
     493            1 :     make_reserve (pg,
     494              :                   5,
     495              :                   "4",
     496              :                   SECOND,
     497              :                   SECOND,
     498              :                   &merged);
     499              :   }
     500            1 :   memset (&ctx,
     501              :           0,
     502              :           sizeof (ctx));
     503            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     504              :           TALER_EXCHANGEDB_iterate_expired_reserves (pg,
     505              :                                                      now,
     506              :                                                      &expired_cb,
     507              :                                                      &ctx));
     508            1 :   FAILIF (0 != ctx.total);
     509            1 :   return 0;
     510              : }
     511              : 
     512              : 
     513              : /**
     514              :  * Garbage collection removes drained, expired reserves and leaves the
     515              :  * others alone.  Runs last.
     516              :  *
     517              :  * @param pg the database context
     518              :  * @return 0 on success
     519              :  */
     520              : static int
     521            1 : check_gc (struct TALER_EXCHANGEDB_PostgresContext *pg)
     522              : {
     523              :   struct TALER_ReservePublicKeyP old_empty;
     524              :   struct TALER_ReservePublicKeyP old_funded;
     525              :   struct TALER_ReservePublicKeyP young_empty;
     526              :   char *hex;
     527              :   uint64_t before;
     528              : 
     529              :   /* gc_date in the past and no money left: collectable */
     530            1 :   make_reserve (pg,
     531              :                 10,
     532              :                 "0",
     533              :                 SECOND,
     534              :                 SECOND,
     535              :                 &old_empty);
     536              :   /* gc_date in the past but still holding money: kept */
     537            1 :   make_reserve (pg,
     538              :                 11,
     539              :                 "2",
     540              :                 SECOND,
     541              :                 SECOND,
     542              :                 &old_funded);
     543              :   /* no money left but gc_date far in the future: kept */
     544            1 :   make_reserve (pg,
     545              :                 12,
     546              :                 "0",
     547              :                 4000000000000000LLU,
     548              :                 5000000000000000LLU,
     549              :                 &young_empty);
     550            1 :   before = TDB_count (pg,
     551              :                       "FROM reserves");
     552            1 :   FAILIF (GNUNET_OK !=
     553              :           TALER_EXCHANGEDB_gc (pg));
     554              : 
     555            1 :   hex = TDB_hex (&old_empty,
     556              :                  sizeof (old_empty));
     557            1 :   FAILIF_C (0 != TDB_count (pg,
     558              :                             "FROM reserves"
     559              :                             " WHERE reserve_pub=decode('%s','hex')",
     560              :                             hex),
     561              :             GNUNET_free (hex));
     562            1 :   GNUNET_free (hex);
     563            1 :   hex = TDB_hex (&old_funded,
     564              :                  sizeof (old_funded));
     565            1 :   FAILIF_C (1 != TDB_count (pg,
     566              :                             "FROM reserves"
     567              :                             " WHERE reserve_pub=decode('%s','hex')",
     568              :                             hex),
     569              :             GNUNET_free (hex));
     570            1 :   GNUNET_free (hex);
     571            1 :   hex = TDB_hex (&young_empty,
     572              :                  sizeof (young_empty));
     573            1 :   FAILIF_C (1 != TDB_count (pg,
     574              :                             "FROM reserves"
     575              :                             " WHERE reserve_pub=decode('%s','hex')",
     576              :                             hex),
     577              :             GNUNET_free (hex));
     578            1 :   GNUNET_free (hex);
     579            1 :   FAILIF (before <= TDB_count (pg,
     580              :                                "FROM reserves"));
     581              :   /* running it again on the same database changes nothing further */
     582            1 :   before = TDB_count (pg,
     583              :                       "FROM reserves");
     584            1 :   FAILIF (GNUNET_OK !=
     585              :           TALER_EXCHANGEDB_gc (pg));
     586            1 :   FAILIF (before != TDB_count (pg,
     587              :                                "FROM reserves"));
     588            1 :   return 0;
     589              : }
     590              : 
     591              : 
     592              : /**
     593              :  * The checks to run, in order.
     594              :  */
     595              : static const struct TDB_Test tests[] = {
     596              :   { "reserves-empty",
     597              :     &check_empty },
     598              :   { "reserves-without-origin",
     599              :     &check_without_origin },
     600              :   { "reserves-with-origin",
     601              :     &check_with_origin },
     602              :   { "reserves-update",
     603              :     &check_update },
     604              :   { "reserves-expired",
     605              :     &check_expired },
     606              :   { "reserves-gc",
     607              :     &check_gc },
     608              :   { NULL, NULL }
     609              : };
     610              : 
     611              : 
     612              : int
     613            1 : main (int argc,
     614              :       char *const *argv)
     615              : {
     616            1 :   return TDB_main (argc,
     617              :                    argv,
     618              :                    "test-reserves",
     619              :                    "Tests for the exchangedb `reserves' table",
     620              :                    tests);
     621              : }
     622              : 
     623              : 
     624              : /* end of test_reserves.c */
        

Generated by: LCOV version 2.0-1