LCOV - code coverage report
Current view: top level - exchangedb - test_reserves_open_requests.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 114 114
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_open_requests.c
      18              :  * @brief tests for the exchangedb functions whose primary table is
      19              :  *        `reserves_open_requests`
      20              :  * @author Christian Grothoff
      21              :  *
      22              :  * Covers #TALER_EXCHANGEDB_do_reserve_open() and
      23              :  * #TALER_EXCHANGEDB_iterate_reserve_open_above_serial_id().
      24              :  *
      25              :  * The table references `reserves`, which TDB_reserve() creates.  The
      26              :  * interesting cases of do_reserve_open() are the ones where it declines to
      27              :  * do the work: an unknown reserve, a payment that does not cover the cost,
      28              :  * a reserve that cannot pay its share, and a request that asks for nothing
      29              :  * that is not already granted.  Only the success path writes the table.
      30              :  */
      31              : #include "test_common.h"
      32              : #include "exchange-database/do_reserve_open.h"
      33              : #include "exchange-database/get_reserve.h"
      34              : #include "exchange-database/iterate_reserve_open_above_serial_id.h"
      35              : 
      36              : 
      37              : /**
      38              :  * A year in microseconds, as exchange_do_reserve_open() counts it.
      39              :  */
      40              : #define YEAR_US 31536000000000LLU
      41              : 
      42              : 
      43              : /**
      44              :  * Closure for #open_cb().
      45              :  */
      46              : struct OpenContext
      47              : {
      48              :   /**
      49              :    * How many rows did the callback see?
      50              :    */
      51              :   unsigned int total;
      52              : 
      53              :   /**
      54              :    * Stop after this many rows; 0 for no limit.
      55              :    */
      56              :   unsigned int stop_after;
      57              : 
      58              :   /**
      59              :    * Reserve we are looking for, NULL to match nothing.
      60              :    */
      61              :   const struct TALER_ReservePublicKeyP *reserve_pub;
      62              : 
      63              :   /**
      64              :    * How many times did we see @e reserve_pub?
      65              :    */
      66              :   unsigned int matched;
      67              : 
      68              :   /**
      69              :    * Reserve payment reported for @e reserve_pub.
      70              :    */
      71              :   struct TALER_Amount reserve_payment;
      72              : 
      73              :   /**
      74              :    * Purse limit reported for @e reserve_pub.
      75              :    */
      76              :   uint32_t purse_limit;
      77              : 
      78              :   /**
      79              :    * Signature reported for @e reserve_pub.
      80              :    */
      81              :   struct TALER_ReserveSignatureP reserve_sig;
      82              : 
      83              :   /**
      84              :    * Expiration reported for @e reserve_pub.
      85              :    */
      86              :   struct GNUNET_TIME_Timestamp expiration;
      87              : 
      88              :   /**
      89              :    * Request timestamp reported for @e reserve_pub.
      90              :    */
      91              :   struct GNUNET_TIME_Timestamp request_timestamp;
      92              : };
      93              : 
      94              : 
      95              : /**
      96              :  * Callback for #TALER_EXCHANGEDB_iterate_reserve_open_above_serial_id().
      97              :  *
      98              :  * @param cls a `struct OpenContext *`
      99              :  * @param rowid row of the request
     100              :  * @param reserve_payment how much the reserve itself paid
     101              :  * @param request_timestamp when the request was made
     102              :  * @param reserve_expiration until when the reserve is now open
     103              :  * @param purse_limit how many purses were asked for
     104              :  * @param reserve_pub the reserve in question
     105              :  * @param reserve_sig signature affirming the request
     106              :  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to stop
     107              :  */
     108              : static enum GNUNET_GenericReturnValue
     109            4 : open_cb (void *cls,
     110              :          uint64_t rowid,
     111              :          const struct TALER_Amount *reserve_payment,
     112              :          struct GNUNET_TIME_Timestamp request_timestamp,
     113              :          struct GNUNET_TIME_Timestamp reserve_expiration,
     114              :          uint32_t purse_limit,
     115              :          const struct TALER_ReservePublicKeyP *reserve_pub,
     116              :          const struct TALER_ReserveSignatureP *reserve_sig)
     117              : {
     118            4 :   struct OpenContext *ctx = cls;
     119              : 
     120              :   (void) rowid;
     121            4 :   ctx->total++;
     122            4 :   if ( (NULL != ctx->reserve_pub) &&
     123            1 :        (0 == GNUNET_memcmp (reserve_pub,
     124              :                             ctx->reserve_pub)) )
     125              :   {
     126            1 :     ctx->matched++;
     127            1 :     ctx->reserve_payment = *reserve_payment;
     128            1 :     ctx->purse_limit = purse_limit;
     129            1 :     ctx->reserve_sig = *reserve_sig;
     130            1 :     ctx->expiration = reserve_expiration;
     131            1 :     ctx->request_timestamp = request_timestamp;
     132              :   }
     133            4 :   if ( (0 != ctx->stop_after) &&
     134            1 :        (ctx->total >= ctx->stop_after) )
     135            1 :     return GNUNET_SYSERR;
     136            3 :   return GNUNET_OK;
     137              : }
     138              : 
     139              : 
     140              : /**
     141              :  * Ask to keep a reserve open.
     142              :  *
     143              :  * @param pg the database context
     144              :  * @param reserve_pub reserve to open
     145              :  * @param seed seed for the reserve signature
     146              :  * @param total_paid total the client is paying, e.g. "1"
     147              :  * @param reserve_payment share of that taken from the reserve, e.g. "1"
     148              :  * @param purse_limit how many purses the client wants
     149              :  * @param desired_expiration until when the client wants the reserve open
     150              :  * @param now when the client made the request
     151              :  * @param open_fee annual fee, e.g. "1"
     152              :  * @param[out] no_funds set if the reserve cannot pay its share
     153              :  * @param[out] open_cost set to what the operation costs
     154              :  * @param[out] final_expiration set to the expiration that was granted
     155              :  * @return transaction status
     156              :  */
     157              : static enum GNUNET_DB_QueryStatus
     158            7 : open_reserve (struct TALER_EXCHANGEDB_PostgresContext *pg,
     159              :               const struct TALER_ReservePublicKeyP *reserve_pub,
     160              :               uint32_t seed,
     161              :               const char *total_paid,
     162              :               const char *reserve_payment,
     163              :               uint32_t purse_limit,
     164              :               struct GNUNET_TIME_Timestamp desired_expiration,
     165              :               struct GNUNET_TIME_Timestamp now,
     166              :               const char *open_fee,
     167              :               bool *no_funds,
     168              :               struct TALER_Amount *open_cost,
     169              :               struct GNUNET_TIME_Timestamp *final_expiration)
     170              : {
     171              :   struct TALER_ReserveSignatureP reserve_sig;
     172            7 :   struct TALER_Amount paid = TDB_amount (total_paid);
     173            7 :   struct TALER_Amount payment = TDB_amount (reserve_payment);
     174            7 :   struct TALER_Amount fee = TDB_amount (open_fee);
     175              :   struct TALER_Amount balance;
     176              : 
     177            7 :   TDB_fill (&reserve_sig,
     178              :             sizeof (reserve_sig),
     179              :             seed);
     180            7 :   return TALER_EXCHANGEDB_do_reserve_open (pg,
     181              :                                            reserve_pub,
     182              :                                            &paid,
     183              :                                            &payment,
     184              :                                            purse_limit,
     185              :                                            &reserve_sig,
     186              :                                            desired_expiration,
     187              :                                            now,
     188              :                                            &fee,
     189              :                                            no_funds,
     190              :                                            &balance,
     191              :                                            open_cost,
     192              :                                            final_expiration);
     193              : }
     194              : 
     195              : 
     196              : /**
     197              :  * Build a timestamp from a number of seconds since the epoch.
     198              :  *
     199              :  * @param secs seconds since the epoch
     200              :  * @return the timestamp
     201              :  */
     202              : static struct GNUNET_TIME_Timestamp
     203           12 : ts (uint64_t secs)
     204              : {
     205           12 :   struct GNUNET_TIME_Absolute abs = {
     206           12 :     .abs_value_us = secs * 1000LLU * 1000LLU
     207              :   };
     208              : 
     209           12 :   return GNUNET_TIME_absolute_to_timestamp (abs);
     210              : }
     211              : 
     212              : 
     213              : /**
     214              :  * A reserve that does not exist cannot be opened, and nothing is on file.
     215              :  *
     216              :  * @param pg the database context
     217              :  * @return 0 on success
     218              :  */
     219              : static int
     220            1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
     221              : {
     222              :   struct TALER_ReservePublicKeyP reserve_pub;
     223              :   struct TALER_Amount open_cost;
     224              :   struct GNUNET_TIME_Timestamp final_expiration;
     225            1 :   struct OpenContext ctx = { 0 };
     226            1 :   bool no_funds = false;
     227              : 
     228            1 :   TDB_FILL (reserve_pub,
     229              :             1);
     230            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     231              :           open_reserve (pg,
     232              :                         &reserve_pub,
     233              :                         1,
     234              :                         "1",
     235              :                         "1",
     236              :                         1,
     237              :                         ts (2000000000),
     238              :                         ts (1600000000),
     239              :                         "1",
     240              :                         &no_funds,
     241              :                         &open_cost,
     242              :                         &final_expiration));
     243            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     244              :           TALER_EXCHANGEDB_iterate_reserve_open_above_serial_id (
     245              :             pg,
     246              :             0,
     247              :             &open_cb,
     248              :             &ctx));
     249            1 :   FAILIF (0 != ctx.total);
     250            1 :   FAILIF (0 != TDB_count (pg,
     251              :                           "FROM reserves_open_requests"));
     252            1 :   return 0;
     253              : }
     254              : 
     255              : 
     256              : /**
     257              :  * Opening a reserve charges it, extends it and records the request.
     258              :  *
     259              :  * @param pg the database context
     260              :  * @return 0 on success
     261              :  */
     262              : static int
     263            1 : check_open (struct TALER_EXCHANGEDB_PostgresContext *pg)
     264              : {
     265              :   struct TALER_ReservePublicKeyP reserve_pub;
     266              :   struct TALER_EXCHANGEDB_Reserve reserve;
     267              :   struct TALER_Amount open_cost;
     268            1 :   struct TALER_Amount expect_cost = TDB_amount ("1");
     269            1 :   struct TALER_Amount expect_balance = TDB_amount ("9");
     270              :   struct TALER_ReserveSignatureP expect_sig;
     271              :   struct GNUNET_TIME_Timestamp final_expiration;
     272            1 :   struct GNUNET_TIME_Timestamp now = ts (1600000000);
     273              :   struct OpenContext ctx;
     274            1 :   bool no_funds = true;
     275              : 
     276            1 :   TDB_reserve (pg,
     277              :                10,
     278              :                "10",
     279              :                &reserve_pub);
     280              :   /* TDB_reserve() gives the reserve an expiration far in the future, which
     281              :      would leave nothing for this call to do; pull it back to `now'. */
     282              :   {
     283            1 :     char *hex = TDB_hex (&reserve_pub,
     284              :                          sizeof (reserve_pub));
     285              : 
     286            1 :     FAILIF_C (GNUNET_OK !=
     287              :               TDB_exec (pg,
     288              :                         "UPDATE reserves"
     289              :                         " SET expiration_date=%llu"
     290              :                         " WHERE reserve_pub=decode('%s','hex');",
     291              :                         (unsigned long long) now.abs_time.abs_value_us,
     292              :                         hex),
     293              :               GNUNET_free (hex));
     294            1 :     GNUNET_free (hex);
     295              :   }
     296              : 
     297              :   /* one year of extension at an annual fee of EUR:1 */
     298            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     299              :           open_reserve (pg,
     300              :                         &reserve_pub,
     301              :                         10,
     302              :                         "1",
     303              :                         "1",
     304              :                         1,
     305              :                         ts (1600000000 + YEAR_US / 1000000),
     306              :                         now,
     307              :                         "1",
     308              :                         &no_funds,
     309              :                         &open_cost,
     310              :                         &final_expiration));
     311            1 :   FAILIF (no_funds);
     312            1 :   FAILIF (0 != TALER_amount_cmp (&open_cost,
     313              :                                  &expect_cost));
     314            1 :   FAILIF (final_expiration.abs_time.abs_value_us !=
     315              :           now.abs_time.abs_value_us + YEAR_US);
     316              : 
     317              :   /* the reserve paid for it */
     318            1 :   memset (&reserve,
     319              :           0,
     320              :           sizeof (reserve));
     321            1 :   reserve.pub = reserve_pub;
     322            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     323              :           TALER_EXCHANGEDB_get_reserve (pg,
     324              :                                         &reserve));
     325            1 :   FAILIF (0 != TALER_amount_cmp (&reserve.balance,
     326              :                                  &expect_balance));
     327            1 :   FAILIF (GNUNET_TIME_timestamp_cmp (reserve.expiry,
     328              :                                      !=,
     329              :                                      final_expiration));
     330              : 
     331              :   /* ...and the request itself was recorded */
     332            1 :   TDB_FILL (expect_sig,
     333              :             10);
     334            1 :   memset (&ctx,
     335              :           0,
     336              :           sizeof (ctx));
     337            1 :   ctx.reserve_pub = &reserve_pub;
     338            1 :   FAILIF (0 >=
     339              :           TALER_EXCHANGEDB_iterate_reserve_open_above_serial_id (
     340              :             pg,
     341              :             0,
     342              :             &open_cb,
     343              :             &ctx));
     344            1 :   FAILIF (1 != ctx.matched);
     345            1 :   FAILIF (0 != GNUNET_memcmp (&ctx.reserve_sig,
     346              :                               &expect_sig));
     347            1 :   FAILIF (0 != TALER_amount_cmp (&ctx.reserve_payment,
     348              :                                  &expect_cost));
     349            1 :   FAILIF (1 != ctx.purse_limit);
     350            1 :   FAILIF (GNUNET_TIME_timestamp_cmp (ctx.expiration,
     351              :                                      !=,
     352              :                                      final_expiration));
     353            1 :   FAILIF (GNUNET_TIME_timestamp_cmp (ctx.request_timestamp,
     354              :                                      !=,
     355              :                                      now));
     356              : 
     357              :   /* a retry of the very same request is absorbed */
     358            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     359              :           open_reserve (pg,
     360              :                         &reserve_pub,
     361              :                         10,
     362              :                         "1",
     363              :                         "1",
     364              :                         1,
     365              :                         ts (1600000000 + YEAR_US / 1000000),
     366              :                         now,
     367              :                         "1",
     368              :                         &no_funds,
     369              :                         &open_cost,
     370              :                         &final_expiration));
     371            1 :   FAILIF (1 != TDB_count (pg,
     372              :                           "FROM reserves_open_requests"));
     373            1 :   return 0;
     374              : }
     375              : 
     376              : 
     377              : /**
     378              :  * A request that asks for nothing beyond what the reserve already has
     379              :  * costs nothing and is not recorded.
     380              :  *
     381              :  * @param pg the database context
     382              :  * @return 0 on success
     383              :  */
     384              : static int
     385            1 : check_nothing_to_do (struct TALER_EXCHANGEDB_PostgresContext *pg)
     386              : {
     387              :   struct TALER_ReservePublicKeyP reserve_pub;
     388              :   struct TALER_Amount open_cost;
     389            1 :   struct TALER_Amount zero = TDB_amount ("0");
     390              :   struct GNUNET_TIME_Timestamp final_expiration;
     391            1 :   bool no_funds = true;
     392              :   uint64_t before;
     393              : 
     394            1 :   TDB_reserve (pg,
     395              :                11,
     396              :                "10",
     397              :                &reserve_pub);
     398            1 :   before = TDB_count (pg,
     399              :                       "FROM reserves_open_requests");
     400              :   /* The reserve already runs to 2096 and no additional purses are asked
     401              :      for, so there is nothing left to grant.  Note that asking for even one
     402              :      purse would *not* be a no-op: a fresh reserve has purses_allowed=0. */
     403            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     404              :           open_reserve (pg,
     405              :                         &reserve_pub,
     406              :                         11,
     407              :                         "1",
     408              :                         "1",
     409              :                         0,
     410              :                         ts (1600000000),
     411              :                         ts (1600000000),
     412              :                         "1",
     413              :                         &no_funds,
     414              :                         &open_cost,
     415              :                         &final_expiration));
     416            1 :   FAILIF (no_funds);
     417            1 :   FAILIF (0 != TALER_amount_cmp (&open_cost,
     418              :                                  &zero));
     419            1 :   FAILIF (before != TDB_count (pg,
     420              :                                "FROM reserves_open_requests"));
     421            1 :   return 0;
     422              : }
     423              : 
     424              : 
     425              : /**
     426              :  * Too little paid, or too little in the reserve, both decline the work.
     427              :  *
     428              :  * @param pg the database context
     429              :  * @return 0 on success
     430              :  */
     431              : static int
     432            1 : check_insufficient (struct TALER_EXCHANGEDB_PostgresContext *pg)
     433              : {
     434              :   struct TALER_ReservePublicKeyP reserve_pub;
     435              :   struct TALER_Amount open_cost;
     436            1 :   struct TALER_Amount expect_cost = TDB_amount ("2");
     437              :   struct GNUNET_TIME_Timestamp final_expiration;
     438            1 :   struct GNUNET_TIME_Timestamp now = ts (1600000000);
     439            1 :   bool no_funds = false;
     440              :   uint64_t before;
     441              : 
     442            1 :   TDB_reserve (pg,
     443              :                12,
     444              :                "1",
     445              :                &reserve_pub);
     446              :   {
     447            1 :     char *hex = TDB_hex (&reserve_pub,
     448              :                          sizeof (reserve_pub));
     449              : 
     450            1 :     FAILIF_C (GNUNET_OK !=
     451              :               TDB_exec (pg,
     452              :                         "UPDATE reserves"
     453              :                         " SET expiration_date=%llu"
     454              :                         " WHERE reserve_pub=decode('%s','hex');",
     455              :                         (unsigned long long) now.abs_time.abs_value_us,
     456              :                         hex),
     457              :               GNUNET_free (hex));
     458            1 :     GNUNET_free (hex);
     459              :   }
     460            1 :   before = TDB_count (pg,
     461              :                       "FROM reserves_open_requests");
     462              : 
     463              :   /* two years at EUR:1 a year, but only EUR:1 offered */
     464            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     465              :           open_reserve (pg,
     466              :                         &reserve_pub,
     467              :                         12,
     468              :                         "1",
     469              :                         "1",
     470              :                         1,
     471              :                         ts (1600000000 + 2 * (YEAR_US / 1000000)),
     472              :                         now,
     473              :                         "1",
     474              :                         &no_funds,
     475              :                         &open_cost,
     476              :                         &final_expiration));
     477            1 :   FAILIF (no_funds);
     478            1 :   FAILIF (0 != TALER_amount_cmp (&open_cost,
     479              :                                  &expect_cost));
     480            1 :   FAILIF (before != TDB_count (pg,
     481              :                                "FROM reserves_open_requests"));
     482              : 
     483              :   /* enough offered, but the reserve holds only EUR:1 of the EUR:2 */
     484            1 :   no_funds = false;
     485            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     486              :           open_reserve (pg,
     487              :                         &reserve_pub,
     488              :                         12,
     489              :                         "2",
     490              :                         "2",
     491              :                         1,
     492              :                         ts (1600000000 + 2 * (YEAR_US / 1000000)),
     493              :                         now,
     494              :                         "1",
     495              :                         &no_funds,
     496              :                         &open_cost,
     497              :                         &final_expiration));
     498            1 :   FAILIF (! no_funds);
     499            1 :   FAILIF (before != TDB_count (pg,
     500              :                                "FROM reserves_open_requests"));
     501            1 :   return 0;
     502              : }
     503              : 
     504              : 
     505              : /**
     506              :  * The iterator's serial bound and its abort return behave as documented.
     507              :  *
     508              :  * @param pg the database context
     509              :  * @return 0 on success
     510              :  */
     511              : static int
     512            1 : check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
     513              : {
     514              :   struct TALER_ReservePublicKeyP reserve_pub;
     515              :   struct TALER_Amount open_cost;
     516              :   struct GNUNET_TIME_Timestamp final_expiration;
     517            1 :   struct GNUNET_TIME_Timestamp now = ts (1600000000);
     518              :   struct OpenContext ctx;
     519            1 :   bool no_funds = true;
     520              : 
     521              :   /* a second recorded request, from another reserve */
     522            1 :   TDB_reserve (pg,
     523              :                13,
     524              :                "10",
     525              :                &reserve_pub);
     526              :   {
     527            1 :     char *hex = TDB_hex (&reserve_pub,
     528              :                          sizeof (reserve_pub));
     529              : 
     530            1 :     FAILIF_C (GNUNET_OK !=
     531              :               TDB_exec (pg,
     532              :                         "UPDATE reserves"
     533              :                         " SET expiration_date=%llu"
     534              :                         " WHERE reserve_pub=decode('%s','hex');",
     535              :                         (unsigned long long) now.abs_time.abs_value_us,
     536              :                         hex),
     537              :               GNUNET_free (hex));
     538            1 :     GNUNET_free (hex);
     539              :   }
     540            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     541              :           open_reserve (pg,
     542              :                         &reserve_pub,
     543              :                         13,
     544              :                         "1",
     545              :                         "1",
     546              :                         1,
     547              :                         ts (1600000000 + YEAR_US / 1000000),
     548              :                         now,
     549              :                         "1",
     550              :                         &no_funds,
     551              :                         &open_cost,
     552              :                         &final_expiration));
     553            1 :   FAILIF (2 != TDB_count (pg,
     554              :                           "FROM reserves_open_requests"));
     555              : 
     556            1 :   memset (&ctx,
     557              :           0,
     558              :           sizeof (ctx));
     559            1 :   FAILIF (2 !=
     560              :           TALER_EXCHANGEDB_iterate_reserve_open_above_serial_id (
     561              :             pg,
     562              :             0,
     563              :             &open_cb,
     564              :             &ctx));
     565            1 :   memset (&ctx,
     566              :           0,
     567              :           sizeof (ctx));
     568            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     569              :           TALER_EXCHANGEDB_iterate_reserve_open_above_serial_id (
     570              :             pg,
     571              :             1000,
     572              :             &open_cb,
     573              :             &ctx));
     574            1 :   FAILIF (0 != ctx.total);
     575              : 
     576            1 :   memset (&ctx,
     577              :           0,
     578              :           sizeof (ctx));
     579            1 :   ctx.stop_after = 1;
     580            1 :   FAILIF (2 !=
     581              :           TALER_EXCHANGEDB_iterate_reserve_open_above_serial_id (
     582              :             pg,
     583              :             0,
     584              :             &open_cb,
     585              :             &ctx));
     586            1 :   FAILIF (1 != ctx.total);
     587            1 :   return 0;
     588              : }
     589              : 
     590              : 
     591              : /**
     592              :  * The checks to run, in order.
     593              :  */
     594              : static const struct TDB_Test tests[] = {
     595              :   { "reserves-open-requests-empty",
     596              :     &check_empty },
     597              :   { "reserves-open-requests-open",
     598              :     &check_open },
     599              :   { "reserves-open-requests-nothing-to-do",
     600              :     &check_nothing_to_do },
     601              :   { "reserves-open-requests-insufficient",
     602              :     &check_insufficient },
     603              :   { "reserves-open-requests-iterate",
     604              :     &check_iterate },
     605              :   { NULL, NULL }
     606              : };
     607              : 
     608              : 
     609              : int
     610            1 : main (int argc,
     611              :       char *const *argv)
     612              : {
     613            1 :   return TDB_main (argc,
     614              :                    argv,
     615              :                    "test-reserves-open-requests",
     616              :                    "Tests for the exchangedb `reserves_open_requests' table",
     617              :                    tests);
     618              : }
     619              : 
     620              : 
     621              : /* end of test_reserves_open_requests.c */
        

Generated by: LCOV version 2.0-1