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

Generated by: LCOV version 2.0-1