exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

test_auditors.c (11542B)


      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_auditors.c
     18  * @brief tests for the exchangedb functions whose primary table is `auditors`
     19  * @author Christian Grothoff
     20  *
     21  * Covers #TALER_EXCHANGEDB_insert_auditor(),
     22  * #TALER_EXCHANGEDB_update_auditor(),
     23  * #TALER_EXCHANGEDB_get_auditor_status(),
     24  * #TALER_EXCHANGEDB_get_auditor_timestamp() and
     25  * #TALER_EXCHANGEDB_iterate_active_auditors().
     26  *
     27  * `auditors` has no foreign keys, so the checks need no fixtures.
     28  */
     29 #include "test_common.h"
     30 #include "exchange-database/insert_auditor.h"
     31 #include "exchange-database/update_auditor.h"
     32 #include "exchange-database/get_auditor_status.h"
     33 #include "exchange-database/get_auditor_timestamp.h"
     34 #include "exchange-database/iterate_active_auditors.h"
     35 
     36 
     37 /**
     38  * Nothing is known about an auditor that was never inserted.
     39  *
     40  * @param pg the database context
     41  * @return 0 on success
     42  */
     43 static int
     44 check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
     45 {
     46   struct TALER_AuditorPublicKeyP auditor_pub;
     47   struct GNUNET_TIME_Timestamp last_date;
     48   char *url = NULL;
     49   bool enabled = true;
     50 
     51   TDB_FILL (auditor_pub,
     52             1);
     53   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     54           TALER_EXCHANGEDB_get_auditor_status (pg,
     55                                                &auditor_pub,
     56                                                &url,
     57                                                &enabled));
     58   FAILIF (NULL != url);
     59   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     60           TALER_EXCHANGEDB_get_auditor_timestamp (pg,
     61                                                   &auditor_pub,
     62                                                   &last_date));
     63   /* updating an auditor that is not on file does not create one */
     64   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     65           TALER_EXCHANGEDB_update_auditor (pg,
     66                                            &auditor_pub,
     67                                            "https://auditor.example/",
     68                                            "Nobody",
     69                                            GNUNET_TIME_timestamp_get (),
     70                                            true));
     71   FAILIF (0 != TDB_count (pg,
     72                           "FROM auditors"));
     73   return 0;
     74 }
     75 
     76 
     77 /**
     78  * Closure for #count_auditors_cb().
     79  */
     80 struct CountContext
     81 {
     82   /**
     83    * Auditor we are looking for.
     84    */
     85   const struct TALER_AuditorPublicKeyP *auditor_pub;
     86 
     87   /**
     88    * How many auditors did the callback see in total?
     89    */
     90   unsigned int total;
     91 
     92   /**
     93    * How many times did we see @e auditor_pub?
     94    */
     95   unsigned int matched;
     96 
     97   /**
     98    * URL reported for @e auditor_pub, owned by this struct.
     99    */
    100   char *url;
    101 
    102   /**
    103    * Name reported for @e auditor_pub, owned by this struct.
    104    */
    105   char *name;
    106 };
    107 
    108 
    109 /**
    110  * Callback for #TALER_EXCHANGEDB_iterate_active_auditors().
    111  *
    112  * @param cls a `struct CountContext *`
    113  * @param auditor_pub public key of the auditor
    114  * @param auditor_url base URL of the auditor
    115  * @param auditor_name human-readable name of the auditor
    116  */
    117 static void
    118 count_auditors_cb (void *cls,
    119                    const struct TALER_AuditorPublicKeyP *auditor_pub,
    120                    const char *auditor_url,
    121                    const char *auditor_name)
    122 {
    123   struct CountContext *ctx = cls;
    124 
    125   ctx->total++;
    126   if (0 != GNUNET_memcmp (auditor_pub,
    127                           ctx->auditor_pub))
    128     return;
    129   ctx->matched++;
    130   GNUNET_free (ctx->url);
    131   GNUNET_free (ctx->name);
    132   ctx->url = GNUNET_strdup (auditor_url);
    133   ctx->name = GNUNET_strdup (auditor_name);
    134 }
    135 
    136 
    137 /**
    138  * Insert an auditor, read it back, then disable and re-enable it.
    139  *
    140  * @param pg the database context
    141  * @return 0 on success
    142  */
    143 static int
    144 check_insert_update (struct TALER_EXCHANGEDB_PostgresContext *pg)
    145 {
    146   struct TALER_AuditorPublicKeyP auditor_pub;
    147   struct GNUNET_TIME_Timestamp start;
    148   struct GNUNET_TIME_Timestamp later;
    149   struct GNUNET_TIME_Timestamp last_date;
    150   struct CountContext ctx;
    151   char *url = NULL;
    152   bool enabled = false;
    153 
    154   TDB_FILL (auditor_pub,
    155             2);
    156   start = GNUNET_TIME_timestamp_get ();
    157   later = GNUNET_TIME_absolute_to_timestamp (
    158     GNUNET_TIME_absolute_add (start.abs_time,
    159                               GNUNET_TIME_UNIT_HOURS));
    160   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    161           TALER_EXCHANGEDB_insert_auditor (pg,
    162                                            &auditor_pub,
    163                                            "https://auditor.example/",
    164                                            "The Auditor",
    165                                            start));
    166   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    167           TALER_EXCHANGEDB_get_auditor_status (pg,
    168                                                &auditor_pub,
    169                                                &url,
    170                                                &enabled));
    171   FAILIF (NULL == url);
    172   FAILIF_C (0 != strcmp (url,
    173                          "https://auditor.example/"),
    174             GNUNET_free (url));
    175   GNUNET_free (url);
    176   /* a fresh auditor is active */
    177   FAILIF (! enabled);
    178   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    179           TALER_EXCHANGEDB_get_auditor_timestamp (pg,
    180                                                   &auditor_pub,
    181                                                   &last_date));
    182   FAILIF (GNUNET_TIME_timestamp_cmp (last_date,
    183                                      !=,
    184                                      start));
    185 
    186   memset (&ctx,
    187           0,
    188           sizeof (ctx));
    189   ctx.auditor_pub = &auditor_pub;
    190   FAILIF (0 >=
    191           TALER_EXCHANGEDB_iterate_active_auditors (pg,
    192                                                     &count_auditors_cb,
    193                                                     &ctx));
    194   FAILIF_C (1 != ctx.matched,
    195             GNUNET_free (ctx.url); GNUNET_free (ctx.name));
    196   FAILIF_C (0 != strcmp (ctx.name,
    197                          "The Auditor"),
    198             GNUNET_free (ctx.url); GNUNET_free (ctx.name));
    199   GNUNET_free (ctx.url);
    200   GNUNET_free (ctx.name);
    201 
    202   /* disabling drops the auditor from the active iterator */
    203   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    204           TALER_EXCHANGEDB_update_auditor (pg,
    205                                            &auditor_pub,
    206                                            "https://auditor2.example/",
    207                                            "The Other Auditor",
    208                                            later,
    209                                            false));
    210   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    211           TALER_EXCHANGEDB_get_auditor_status (pg,
    212                                                &auditor_pub,
    213                                                &url,
    214                                                &enabled));
    215   FAILIF_C (0 != strcmp (url,
    216                          "https://auditor2.example/"),
    217             GNUNET_free (url));
    218   GNUNET_free (url);
    219   FAILIF (enabled);
    220   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    221           TALER_EXCHANGEDB_get_auditor_timestamp (pg,
    222                                                   &auditor_pub,
    223                                                   &last_date));
    224   FAILIF (GNUNET_TIME_timestamp_cmp (last_date,
    225                                      !=,
    226                                      later));
    227   memset (&ctx,
    228           0,
    229           sizeof (ctx));
    230   ctx.auditor_pub = &auditor_pub;
    231   FAILIF (0 >
    232           TALER_EXCHANGEDB_iterate_active_auditors (pg,
    233                                                     &count_auditors_cb,
    234                                                     &ctx));
    235   FAILIF (0 != ctx.matched);
    236 
    237   /* ...and re-enabling brings it back */
    238   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    239           TALER_EXCHANGEDB_update_auditor (pg,
    240                                            &auditor_pub,
    241                                            "https://auditor2.example/",
    242                                            "The Other Auditor",
    243                                            later,
    244                                            true));
    245   memset (&ctx,
    246           0,
    247           sizeof (ctx));
    248   ctx.auditor_pub = &auditor_pub;
    249   FAILIF (0 >=
    250           TALER_EXCHANGEDB_iterate_active_auditors (pg,
    251                                                     &count_auditors_cb,
    252                                                     &ctx));
    253   FAILIF_C (1 != ctx.matched,
    254             GNUNET_free (ctx.url); GNUNET_free (ctx.name));
    255   GNUNET_free (ctx.url);
    256   GNUNET_free (ctx.name);
    257   return 0;
    258 }
    259 
    260 
    261 /**
    262  * A second auditor is independent of the first, and only the active ones
    263  * are iterated.
    264  *
    265  * @param pg the database context
    266  * @return 0 on success
    267  */
    268 static int
    269 check_two_auditors (struct TALER_EXCHANGEDB_PostgresContext *pg)
    270 {
    271   struct TALER_AuditorPublicKeyP a2;
    272   struct TALER_AuditorPublicKeyP a3;
    273   struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
    274   struct CountContext ctx;
    275 
    276   TDB_FILL (a2,
    277             3);
    278   TDB_FILL (a3,
    279             4);
    280   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    281           TALER_EXCHANGEDB_insert_auditor (pg,
    282                                            &a2,
    283                                            "https://a2.example/",
    284                                            "A2",
    285                                            now));
    286   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    287           TALER_EXCHANGEDB_insert_auditor (pg,
    288                                            &a3,
    289                                            "https://a3.example/",
    290                                            "A3",
    291                                            now));
    292   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    293           TALER_EXCHANGEDB_update_auditor (pg,
    294                                            &a3,
    295                                            "https://a3.example/",
    296                                            "A3",
    297                                            now,
    298                                            false));
    299   memset (&ctx,
    300           0,
    301           sizeof (ctx));
    302   ctx.auditor_pub = &a3;
    303   FAILIF (0 >=
    304           TALER_EXCHANGEDB_iterate_active_auditors (pg,
    305                                                     &count_auditors_cb,
    306                                                     &ctx));
    307   FAILIF (0 != ctx.matched);
    308   FAILIF (ctx.total !=
    309           TDB_count (pg,
    310                      "FROM auditors WHERE is_active"));
    311   FAILIF (ctx.total ==
    312           TDB_count (pg,
    313                      "FROM auditors"));
    314   return 0;
    315 }
    316 
    317 
    318 /**
    319  * The checks to run, in order.
    320  */
    321 static const struct TDB_Test tests[] = {
    322   { "auditors-empty",
    323     &check_empty },
    324   { "auditors-insert-update",
    325     &check_insert_update },
    326   { "auditors-two-auditors",
    327     &check_two_auditors },
    328   { NULL, NULL }
    329 };
    330 
    331 
    332 int
    333 main (int argc,
    334       char *const *argv)
    335 {
    336   return TDB_main (argc,
    337                    argv,
    338                    "test-auditors",
    339                    "Tests for the exchangedb `auditors' table",
    340                    tests);
    341 }
    342 
    343 
    344 /* end of test_auditors.c */