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

            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_denominations.c
      18              :  * @brief tests for the exchangedb functions whose primary table is
      19              :  *        `denominations`
      20              :  * @author Christian Grothoff
      21              :  *
      22              :  * Covers #TALER_EXCHANGEDB_insert_denomination_info(),
      23              :  * #TALER_EXCHANGEDB_get_denomination_info(),
      24              :  * #TALER_EXCHANGEDB_get_denomination_by_serial(),
      25              :  * #TALER_EXCHANGEDB_get_denomination_meta(),
      26              :  * #TALER_EXCHANGEDB_iterate_denomination_info() and
      27              :  * #TALER_EXCHANGEDB_iterate_denominations().
      28              :  *
      29              :  * `denominations` has no foreign keys, so the checks need no fixtures.  The
      30              :  * empty-table checks run first, before any denomination exists.
      31              :  */
      32              : #include "test_common.h"
      33              : #include "exchange-database/insert_denomination_info.h"
      34              : #include "exchange-database/get_denomination_info.h"
      35              : #include "exchange-database/get_denomination_by_serial.h"
      36              : #include "exchange-database/get_denomination_meta.h"
      37              : #include "exchange-database/iterate_denomination_info.h"
      38              : #include "exchange-database/iterate_denominations.h"
      39              : 
      40              : 
      41              : /**
      42              :  * Check that nothing is found while the table is still empty.
      43              :  *
      44              :  * @param pg the database context
      45              :  * @return 0 on success
      46              :  */
      47              : static int
      48            1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
      49              : {
      50              :   struct TALER_DenominationHashP h_denom_pub;
      51              :   struct TALER_EXCHANGEDB_DenominationKeyInformation issue;
      52              :   struct TALER_EXCHANGEDB_DenominationKeyMetaData meta;
      53              :   uint64_t serial;
      54              : 
      55            1 :   TDB_FILL (h_denom_pub,
      56              :             1);
      57            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
      58              :           TALER_EXCHANGEDB_get_denomination_info (pg,
      59              :                                                   &h_denom_pub,
      60              :                                                   &serial,
      61              :                                                   &issue));
      62            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
      63              :           TALER_EXCHANGEDB_get_denomination_by_serial (pg,
      64              :                                                        42,
      65              :                                                        &issue));
      66            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
      67              :           TALER_EXCHANGEDB_get_denomination_meta (pg,
      68              :                                                   &h_denom_pub,
      69              :                                                   &meta));
      70            1 :   return 0;
      71              : }
      72              : 
      73              : 
      74              : /**
      75              :  * Closure for #count_info_cb() and #count_denoms_cb().
      76              :  */
      77              : struct CountContext
      78              : {
      79              :   /**
      80              :    * Denomination we are looking for.
      81              :    */
      82              :   const struct TDB_Denom *denom;
      83              : 
      84              :   /**
      85              :    * How many rows did the callback see in total?
      86              :    */
      87              :   unsigned int total;
      88              : 
      89              :   /**
      90              :    * How many times did we see @e denom?
      91              :    */
      92              :   unsigned int matched;
      93              : 
      94              :   /**
      95              :    * Set to true if @e denom was reported with data that does not match
      96              :    * what we inserted.
      97              :    */
      98              :   bool mismatch;
      99              : 
     100              :   /**
     101              :    * Recoup flag reported for @e denom.
     102              :    */
     103              :   bool recoup_possible;
     104              : 
     105              :   /**
     106              :    * Row reported for @e denom.
     107              :    */
     108              :   uint64_t serial;
     109              : };
     110              : 
     111              : 
     112              : /**
     113              :  * Callback for #TALER_EXCHANGEDB_iterate_denomination_info().
     114              :  *
     115              :  * @param cls a `struct CountContext *`
     116              :  * @param denom_serial table row of the denomination
     117              :  * @param denom_pub public key of the denomination
     118              :  * @param issue information about the denomination
     119              :  */
     120              : static void
     121            3 : count_info_cb (void *cls,
     122              :                uint64_t denom_serial,
     123              :                const struct TALER_DenominationPublicKey *denom_pub,
     124              :                const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue)
     125              : {
     126            3 :   struct CountContext *ctx = cls;
     127              : 
     128            3 :   ctx->total++;
     129            3 :   if (0 != GNUNET_memcmp (&issue->denom_hash,
     130              :                           &ctx->denom->h_denom_pub))
     131            2 :     return;
     132            1 :   ctx->matched++;
     133            1 :   ctx->serial = denom_serial;
     134            1 :   if ( (0 != TALER_denom_pub_cmp (denom_pub,
     135            2 :                                   &ctx->denom->pub)) ||
     136            1 :        (0 != TALER_amount_cmp (&issue->value,
     137            2 :                                &ctx->denom->issue.value)) ||
     138            1 :        (0 != TALER_amount_cmp (&issue->fees.deposit,
     139            1 :                                &ctx->denom->issue.fees.deposit)) )
     140            0 :     ctx->mismatch = true;
     141              : }
     142              : 
     143              : 
     144              : /**
     145              :  * Callback for #TALER_EXCHANGEDB_iterate_denominations().
     146              :  *
     147              :  * @param cls a `struct CountContext *`
     148              :  * @param denom_pub public key of the denomination
     149              :  * @param h_denom_pub hash of @a denom_pub
     150              :  * @param meta meta data of the denomination
     151              :  * @param master_sig master signature over the denomination
     152              :  * @param recoup_possible true if the denomination was revoked
     153              :  */
     154              : static void
     155            3 : count_denoms_cb (void *cls,
     156              :                  const struct TALER_DenominationPublicKey *denom_pub,
     157              :                  const struct TALER_DenominationHashP *h_denom_pub,
     158              :                  const struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta,
     159              :                  const struct TALER_MasterSignatureP *master_sig,
     160              :                  bool recoup_possible)
     161              : {
     162            3 :   struct CountContext *ctx = cls;
     163              : 
     164            3 :   ctx->total++;
     165            3 :   if (0 != GNUNET_memcmp (h_denom_pub,
     166              :                           &ctx->denom->h_denom_pub))
     167            2 :     return;
     168            1 :   ctx->matched++;
     169            1 :   ctx->serial = meta->serial;
     170            1 :   ctx->recoup_possible = recoup_possible;
     171            1 :   if ( (0 != TALER_denom_pub_cmp (denom_pub,
     172            1 :                                   &ctx->denom->pub)) ||
     173            1 :        (0 != GNUNET_memcmp (master_sig,
     174            1 :                             &ctx->denom->issue.signature)) ||
     175            1 :        (0 != TALER_amount_cmp (&meta->value,
     176            1 :                                &ctx->denom->issue.value)) ||
     177            1 :        (GNUNET_TIME_timestamp_cmp (meta->start,
     178              :                                    !=,
     179              :                                    ctx->denom->issue.start)) )
     180            0 :     ctx->mismatch = true;
     181              : }
     182              : 
     183              : 
     184              : /**
     185              :  * Insert a denomination and read it back through every lookup the
     186              :  * table offers.
     187              :  *
     188              :  * @param pg the database context
     189              :  * @return 0 on success
     190              :  */
     191              : static int
     192            1 : check_insert_and_lookup (struct TALER_EXCHANGEDB_PostgresContext *pg)
     193              : {
     194              :   struct TDB_Denom denom;
     195              :   struct TALER_EXCHANGEDB_DenominationKeyInformation issue;
     196              :   struct TALER_EXCHANGEDB_DenominationKeyMetaData meta;
     197              :   struct TALER_DenominationHashP other;
     198              :   uint64_t serial;
     199              : 
     200              :   /* TDB_denom() inserts and looks up the row it created. */
     201            1 :   TDB_denom (pg,
     202              :              10,
     203              :              "5",
     204              :              "0.1",
     205              :              &denom);
     206            1 :   memset (&issue,
     207              :           0,
     208              :           sizeof (issue));
     209            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     210              :             TALER_EXCHANGEDB_get_denomination_info (pg,
     211              :                                                     &denom.h_denom_pub,
     212              :                                                     &serial,
     213              :                                                     &issue),
     214              :             TDB_denom_free (&denom));
     215            1 :   FAILIF_C (0 != GNUNET_memcmp (&issue,
     216              :                                 &denom.issue),
     217              :             TDB_denom_free (&denom));
     218            1 :   FAILIF_C (serial != denom.serial,
     219              :             TDB_denom_free (&denom));
     220              : 
     221              :   /* the denom_serial output is optional */
     222            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     223              :             TALER_EXCHANGEDB_get_denomination_info (pg,
     224              :                                                     &denom.h_denom_pub,
     225              :                                                     NULL,
     226              :                                                     &issue),
     227              :             TDB_denom_free (&denom));
     228              : 
     229            1 :   memset (&issue,
     230              :           0,
     231              :           sizeof (issue));
     232            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     233              :             TALER_EXCHANGEDB_get_denomination_by_serial (pg,
     234              :                                                          serial,
     235              :                                                          &issue),
     236              :             TDB_denom_free (&denom));
     237            1 :   FAILIF_C (0 != GNUNET_memcmp (&issue,
     238              :                                 &denom.issue),
     239              :             TDB_denom_free (&denom));
     240              : 
     241            1 :   memset (&meta,
     242              :           0,
     243              :           sizeof (meta));
     244            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     245              :             TALER_EXCHANGEDB_get_denomination_meta (pg,
     246              :                                                     &denom.h_denom_pub,
     247              :                                                     &meta),
     248              :             TDB_denom_free (&denom));
     249            1 :   FAILIF_C (meta.serial != serial,
     250              :             TDB_denom_free (&denom));
     251            1 :   FAILIF_C (0 != TALER_amount_cmp (&meta.value,
     252              :                                    &denom.issue.value),
     253              :             TDB_denom_free (&denom));
     254            1 :   FAILIF_C (0 != TALER_amount_cmp (&meta.fees.refund,
     255              :                                    &denom.issue.fees.refund),
     256              :             TDB_denom_free (&denom));
     257            1 :   FAILIF_C (GNUNET_TIME_timestamp_cmp (meta.expire_legal,
     258              :                                        !=,
     259              :                                        denom.issue.expire_legal),
     260              :             TDB_denom_free (&denom));
     261            1 :   FAILIF_C (meta.age_mask.bits != denom.issue.age_mask.bits,
     262              :             TDB_denom_free (&denom));
     263              : 
     264              :   /* ...and a denomination that was never inserted is still not found. */
     265            1 :   TDB_FILL (other,
     266              :             99);
     267            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     268              :             TALER_EXCHANGEDB_get_denomination_info (pg,
     269              :                                                     &other,
     270              :                                                     &serial,
     271              :                                                     &issue),
     272              :             TDB_denom_free (&denom));
     273            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     274              :             TALER_EXCHANGEDB_get_denomination_meta (pg,
     275              :                                                     &other,
     276              :                                                     &meta),
     277              :             TDB_denom_free (&denom));
     278            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     279              :             TALER_EXCHANGEDB_get_denomination_by_serial (pg,
     280              :                                                          denom.serial + 1000,
     281              :                                                          &issue),
     282              :             TDB_denom_free (&denom));
     283            1 :   TDB_denom_free (&denom);
     284            1 :   return 0;
     285              : }
     286              : 
     287              : 
     288              : /**
     289              :  * Both iterators must report every denomination in the table.
     290              :  *
     291              :  * @param pg the database context
     292              :  * @return 0 on success
     293              :  */
     294              : static int
     295            1 : check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
     296              : {
     297              :   struct TDB_Denom d1;
     298              :   struct TDB_Denom d2;
     299              :   struct CountContext ctx;
     300              :   unsigned int seen;
     301              : 
     302            1 :   TDB_denom (pg,
     303              :              20,
     304              :              "1",
     305              :              "0.01",
     306              :              &d1);
     307            1 :   TDB_denom (pg,
     308              :              21,
     309              :              "2",
     310              :              "0.02",
     311              :              &d2);
     312            1 :   memset (&ctx,
     313              :           0,
     314              :           sizeof (ctx));
     315            1 :   ctx.denom = &d2;
     316            1 :   FAILIF_C (0 >=
     317              :             TALER_EXCHANGEDB_iterate_denomination_info (pg,
     318              :                                                         &count_info_cb,
     319              :                                                         &ctx),
     320              :             TDB_denom_free (&d1); TDB_denom_free (&d2));
     321            1 :   FAILIF_C (1 != ctx.matched,
     322              :             TDB_denom_free (&d1); TDB_denom_free (&d2));
     323            1 :   FAILIF_C (ctx.mismatch,
     324              :             TDB_denom_free (&d1); TDB_denom_free (&d2));
     325            1 :   FAILIF_C (ctx.serial != d2.serial,
     326              :             TDB_denom_free (&d1); TDB_denom_free (&d2));
     327            1 :   seen = ctx.total;
     328            1 :   FAILIF_C (seen < 2,
     329              :             TDB_denom_free (&d1); TDB_denom_free (&d2));
     330              : 
     331            1 :   memset (&ctx,
     332              :           0,
     333              :           sizeof (ctx));
     334            1 :   ctx.denom = &d1;
     335            1 :   FAILIF_C (0 >=
     336              :             TALER_EXCHANGEDB_iterate_denominations (pg,
     337              :                                                     &count_denoms_cb,
     338              :                                                     &ctx),
     339              :             TDB_denom_free (&d1); TDB_denom_free (&d2));
     340            1 :   FAILIF_C (1 != ctx.matched,
     341              :             TDB_denom_free (&d1); TDB_denom_free (&d2));
     342            1 :   FAILIF_C (ctx.mismatch,
     343              :             TDB_denom_free (&d1); TDB_denom_free (&d2));
     344            1 :   FAILIF_C (ctx.serial != d1.serial,
     345              :             TDB_denom_free (&d1); TDB_denom_free (&d2));
     346              :   /* nothing was revoked in this binary */
     347            1 :   FAILIF_C (ctx.recoup_possible,
     348              :             TDB_denom_free (&d1); TDB_denom_free (&d2));
     349              :   /* both iterators walk the same table, so they must agree on its size */
     350            1 :   FAILIF_C (ctx.total != seen,
     351              :             TDB_denom_free (&d1); TDB_denom_free (&d2));
     352            1 :   TDB_denom_free (&d1);
     353            1 :   TDB_denom_free (&d2);
     354            1 :   return 0;
     355              : }
     356              : 
     357              : 
     358              : /**
     359              :  * The checks to run, in order.
     360              :  */
     361              : static const struct TDB_Test tests[] = {
     362              :   { "denominations-empty",
     363              :     &check_empty },
     364              :   { "denominations-insert-and-lookup",
     365              :     &check_insert_and_lookup },
     366              :   { "denominations-iterate",
     367              :     &check_iterate },
     368              :   { NULL, NULL }
     369              : };
     370              : 
     371              : 
     372              : int
     373            1 : main (int argc,
     374              :       char *const *argv)
     375              : {
     376            1 :   return TDB_main (argc,
     377              :                    argv,
     378              :                    "test-denominations",
     379              :                    "Tests for the exchangedb `denominations' table",
     380              :                    tests);
     381              : }
     382              : 
     383              : 
     384              : /* end of test_denominations.c */
        

Generated by: LCOV version 2.0-1