LCOV - code coverage report
Current view: top level - auditordb - test_auditordb_regression.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 81.1 % 90 73
Test Date: 2026-09-09 15:11:34 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 auditordb/test_auditordb_regression.c
      18              :  * @brief regression tests for individual auditor DB operations
      19              :  * @author Christian Grothoff
      20              :  *
      21              :  * Each test function in this file pins down the behaviour of one
      22              :  * auditor DB operation that was found to be wrong.  Keep them small
      23              :  * and independent: the file is meant to grow one function per fixed
      24              :  * defect.
      25              :  */
      26              : #include <gnunet/gnunet_db_lib.h>
      27              : #include "auditordb_lib.h"
      28              : #include "auditor-database/create_tables.h"
      29              : #include "auditor-database/drop_tables.h"
      30              : #include "auditor-database/get_auditor_progress.h"
      31              : #include "auditor-database/get_balance.h"
      32              : #include "auditor-database/insert_auditor_progress.h"
      33              : #include "auditor-database/insert_balance.h"
      34              : #include "auditor-database/insert_purse_not_closed_inconsistencies.h"
      35              : #include "auditor-database/iterate_purse_not_closed_inconsistencies.h"
      36              : #include "auditor-database/preflight.h"
      37              : #include "auditor-database/start.h"
      38              : 
      39              : /**
      40              :  * Currency we use, must match CURRENCY in "test-auditor-db-postgres.conf".
      41              :  */
      42              : #define CURRENCY "EUR"
      43              : 
      44              : /**
      45              :  * Complain and fail the test if @a cond is true.
      46              :  */
      47              : #define FAILIF(cond)                            \
      48              :         do {                                    \
      49              :           if (! (cond)) break;                  \
      50              :           GNUNET_break (0);                     \
      51              :           return GNUNET_SYSERR;                 \
      52              :         } while (0)
      53              : 
      54              : 
      55              : /**
      56              :  * Global result from the testcase.
      57              :  */
      58              : static int result = -1;
      59              : 
      60              : /**
      61              :  * Database connection under test.
      62              :  */
      63              : static struct TALER_AUDITORDB_PostgresContext *pg;
      64              : 
      65              : 
      66              : /**
      67              :  * Parse @a str into @a amount, asserting success.
      68              :  *
      69              :  * @param str amount to parse, without currency
      70              :  * @param[out] amount where to write the result
      71              :  */
      72              : static void
      73            4 : amount (const char *str,
      74              :         struct TALER_Amount *amount)
      75              : {
      76              :   char buf[128];
      77              : 
      78            4 :   GNUNET_snprintf (buf,
      79              :                    sizeof (buf),
      80              :                    "%s:%s",
      81              :                    CURRENCY,
      82              :                    str);
      83            4 :   GNUNET_assert (GNUNET_OK ==
      84              :                  TALER_string_to_amount (buf,
      85              :                                          amount));
      86            4 : }
      87              : 
      88              : 
      89              : /**
      90              :  * A-1: neither TALER_AUDITORDB_get_balance() nor
      91              :  * TALER_AUDITORDB_get_auditor_progress() can report "key not on file"
      92              :  * (auditor_do_get_balance() emits one row per key either way), so an
      93              :  * auditor helper cannot know whether to insert or to update.  Both
      94              :  * operations must therefore store the value they are given no matter
      95              :  * which one the caller picks.
      96              :  *
      97              :  * @return #GNUNET_OK on success
      98              :  */
      99              : static enum GNUNET_GenericReturnValue
     100            1 : test_checkpoint_upsert (void)
     101              : {
     102              :   struct TALER_Amount one;
     103              :   struct TALER_Amount two;
     104              :   struct TALER_Amount got;
     105              :   uint64_t off;
     106              : 
     107            1 :   amount ("1",
     108              :           &one);
     109            1 :   amount ("2",
     110              :           &two);
     111              : 
     112              :   /* insert_balance() on a key that does not exist yet must create it. */
     113            1 :   FAILIF (0 >
     114              :           TALER_AUDITORDB_insert_balance (pg,
     115              :                                           "a1-update-only",
     116              :                                           &one,
     117              :                                           NULL));
     118            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     119              :           TALER_AUDITORDB_get_balance (pg,
     120              :                                        "a1-update-only",
     121              :                                        &got,
     122              :                                        NULL));
     123            1 :   FAILIF (0 !=
     124              :           TALER_amount_cmp (&got,
     125              :                             &one));
     126              : 
     127              :   /* insert_balance() on a key that already exists must overwrite it. */
     128            1 :   FAILIF (0 >
     129              :           TALER_AUDITORDB_insert_balance (pg,
     130              :                                           "a1-insert-twice",
     131              :                                           &one,
     132              :                                           NULL));
     133            1 :   FAILIF (0 >
     134              :           TALER_AUDITORDB_insert_balance (pg,
     135              :                                           "a1-insert-twice",
     136              :                                           &two,
     137              :                                           NULL));
     138            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     139              :           TALER_AUDITORDB_get_balance (pg,
     140              :                                        "a1-insert-twice",
     141              :                                        &got,
     142              :                                        NULL));
     143            1 :   FAILIF (0 !=
     144              :           TALER_amount_cmp (&got,
     145              :                             &two));
     146              : 
     147              :   /* Same two properties for the progress points. */
     148            1 :   FAILIF (0 >
     149              :           TALER_AUDITORDB_insert_auditor_progress (pg,
     150              :                                                    "a1-update-only",
     151              :                                                    42,
     152              :                                                    NULL));
     153            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     154              :           TALER_AUDITORDB_get_auditor_progress (pg,
     155              :                                                 "a1-update-only",
     156              :                                                 &off,
     157              :                                                 NULL));
     158            1 :   FAILIF (42 != off);
     159              : 
     160            1 :   FAILIF (0 >
     161              :           TALER_AUDITORDB_insert_auditor_progress (pg,
     162              :                                                    "a1-insert-twice",
     163              :                                                    42,
     164              :                                                    NULL));
     165            1 :   FAILIF (0 >
     166              :           TALER_AUDITORDB_insert_auditor_progress (pg,
     167              :                                                    "a1-insert-twice",
     168              :                                                    43,
     169              :                                                    NULL));
     170            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     171              :           TALER_AUDITORDB_get_auditor_progress (pg,
     172              :                                                 "a1-insert-twice",
     173              :                                                 &off,
     174              :                                                 NULL));
     175            1 :   FAILIF (43 != off);
     176            1 :   return GNUNET_OK;
     177              : }
     178              : 
     179              : 
     180              : /**
     181              :  * Callback for #test_purse_not_closed_dedup(), keeps the last row seen.
     182              :  *
     183              :  * @param cls a `struct TALER_AUDITORDB_PurseNotClosedInconsistencies *`
     184              :  * @param dc the row
     185              :  * @return #GNUNET_OK
     186              :  */
     187              : static enum GNUNET_GenericReturnValue
     188            1 : purse_not_closed_cb (
     189              :   void *cls,
     190              :   const struct TALER_AUDITORDB_PurseNotClosedInconsistencies *dc)
     191              : {
     192            1 :   struct TALER_AUDITORDB_PurseNotClosedInconsistencies *last = cls;
     193              : 
     194            1 :   *last = *dc;
     195            1 :   return GNUNET_OK;
     196              : }
     197              : 
     198              : 
     199              : /**
     200              :  * A-5: the purse auditor re-reports every expired purse on every round.
     201              :  * Without dedup on purse_pub the report table grows without bound.
     202              :  *
     203              :  * @return #GNUNET_OK on success
     204              :  */
     205              : static enum GNUNET_GenericReturnValue
     206            1 : test_purse_not_closed_dedup (void)
     207              : {
     208            1 :   struct TALER_AUDITORDB_PurseNotClosedInconsistencies pnc = {
     209            1 :     .expiration_date = GNUNET_TIME_absolute_get ()
     210              :   };
     211            1 :   struct TALER_AUDITORDB_PurseNotClosedInconsistencies last = { 0 };
     212              :   struct TALER_Amount three;
     213              : 
     214            1 :   GNUNET_CRYPTO_random_block (&pnc.purse_pub,
     215              :                               sizeof (pnc.purse_pub));
     216            1 :   amount ("1",
     217              :           &pnc.amount);
     218            1 :   amount ("3",
     219              :           &three);
     220            4 :   for (unsigned int round = 0; round < 3; round++)
     221              :   {
     222            3 :     if (2 == round)
     223            1 :       pnc.amount = three;
     224            3 :     FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     225              :             TALER_AUDITORDB_insert_purse_not_closed_inconsistencies (pg,
     226              :                                                                      &pnc));
     227              :   }
     228              :   /* Three rounds, one purse, one row -- carrying the latest amount. */
     229            1 :   FAILIF (1 !=
     230              :           TALER_AUDITORDB_iterate_purse_not_closed_inconsistencies (
     231              :             pg,
     232              :             1024,
     233              :             0,
     234              :             true,
     235              :             &purse_not_closed_cb,
     236              :             &last));
     237            1 :   FAILIF (0 !=
     238              :           TALER_amount_cmp (&last.amount,
     239              :                             &three));
     240            1 :   return GNUNET_OK;
     241              : }
     242              : 
     243              : 
     244              : /**
     245              :  * Main function that will be run by the scheduler.
     246              :  *
     247              :  * @param cls closure with the configuration
     248              :  */
     249              : static void
     250            1 : run (void *cls)
     251              : {
     252            1 :   struct GNUNET_CONFIGURATION_Handle *cfg = cls;
     253              : 
     254            1 :   if (NULL ==
     255            1 :       (pg = TALER_AUDITORDB_connect (cfg)))
     256              :   {
     257            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     258              :                 "Failed to connect to database\n");
     259            0 :     result = 77;
     260            0 :     return;
     261              :   }
     262            1 :   GNUNET_assert (GNUNET_OK ==
     263              :                  TALER_AUDITORDB_preflight (pg));
     264            1 :   (void) TALER_AUDITORDB_drop_tables (pg,
     265              :                                       GNUNET_YES);
     266            1 :   if (GNUNET_OK !=
     267            1 :       TALER_AUDITORDB_create_tables (pg,
     268              :                                      false,
     269              :                                      0))
     270              :   {
     271            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     272              :                 "Failed to 'create_tables'\n");
     273            0 :     result = 77;
     274            0 :     goto unload;
     275              :   }
     276            1 :   if (GNUNET_SYSERR ==
     277            1 :       TALER_AUDITORDB_preflight (pg))
     278              :   {
     279            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     280              :                 "Failed preflight check\n");
     281            0 :     result = 77;
     282            0 :     goto drop;
     283              :   }
     284            1 :   if (GNUNET_OK !=
     285            1 :       TALER_AUDITORDB_start (pg,
     286              :                              "test-auditordb-regression"))
     287              :   {
     288            0 :     GNUNET_break (0);
     289            0 :     goto drop;
     290              :   }
     291            1 :   if (GNUNET_OK !=
     292            1 :       test_checkpoint_upsert ())
     293            0 :     goto rollback;
     294            1 :   if (GNUNET_OK !=
     295            1 :       test_purse_not_closed_dedup ())
     296            0 :     goto rollback;
     297            1 :   result = 0;
     298            1 :   GNUNET_break (0 <=
     299              :                 TALER_AUDITORDB_commit (pg));
     300            1 :   goto drop;
     301            0 : rollback:
     302            0 :   TALER_AUDITORDB_rollback (pg);
     303            1 : drop:
     304            1 :   GNUNET_break (GNUNET_OK ==
     305              :                 TALER_AUDITORDB_drop_tables (pg,
     306              :                                              GNUNET_YES));
     307            1 : unload:
     308            1 :   TALER_AUDITORDB_disconnect (pg);
     309            1 :   pg = NULL;
     310              : }
     311              : 
     312              : 
     313              : int
     314            1 : main (int argc,
     315              :       char *const argv[])
     316              : {
     317              :   struct GNUNET_CONFIGURATION_Handle *cfg;
     318              : 
     319              :   (void) argc;
     320            1 :   result = -1;
     321            1 :   GNUNET_log_setup (argv[0],
     322              :                     "WARNING",
     323              :                     NULL);
     324            1 :   cfg = GNUNET_CONFIGURATION_create (TALER_AUDITOR_project_data ());
     325            1 :   if (GNUNET_OK !=
     326            1 :       GNUNET_CONFIGURATION_parse (cfg,
     327              :                                   "test-auditor-db-postgres.conf"))
     328              :   {
     329            0 :     GNUNET_break (0);
     330            0 :     return 2;
     331              :   }
     332            1 :   GNUNET_SCHEDULER_run (&run,
     333              :                         cfg);
     334            1 :   GNUNET_CONFIGURATION_destroy (cfg);
     335            1 :   return result;
     336              : }
        

Generated by: LCOV version 2.0-1