exchange

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

test_prewire.c (9460B)


      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_prewire.c
     18  * @brief tests for the exchangedb functions whose primary table is `prewire`
     19  * @author Christian Grothoff
     20  *
     21  * Covers #TALER_EXCHANGEDB_insert_prewire(),
     22  * #TALER_EXCHANGEDB_iterate_prewires(),
     23  * #TALER_EXCHANGEDB_update_to_prewire_finished() and
     24  * #TALER_EXCHANGEDB_update_to_prewire_failed().
     25  *
     26  * `prewire` has no foreign keys.  The table is a work queue: the iterator
     27  * only reports rows that are neither finished nor failed, which is what the
     28  * checks below are about.
     29  */
     30 #include "test_common.h"
     31 #include "exchange-database/insert_prewire.h"
     32 #include "exchange-database/iterate_prewires.h"
     33 #include "exchange-database/update_to_prewire_finished.h"
     34 #include "exchange-database/update_to_prewire_failed.h"
     35 
     36 
     37 /**
     38  * Closure for #prewire_cb().
     39  */
     40 struct PrewireContext
     41 {
     42   /**
     43    * How many rows did the callback see?
     44    */
     45   unsigned int total;
     46 
     47   /**
     48    * Row of the first entry seen.
     49    */
     50   uint64_t first_row;
     51 
     52   /**
     53    * Payload of the first entry seen, owned by this struct.
     54    */
     55   char *first_buf;
     56 
     57   /**
     58    * Number of bytes in @e first_buf.
     59    */
     60   size_t first_buf_size;
     61 
     62   /**
     63    * Wire method of the first entry seen, owned by this struct.
     64    */
     65   char *first_method;
     66 };
     67 
     68 
     69 /**
     70  * Callback for #TALER_EXCHANGEDB_iterate_prewires().
     71  *
     72  * @param cls a `struct PrewireContext *`
     73  * @param rowid row of the entry
     74  * @param wire_method wire method of the entry
     75  * @param buf payload of the entry
     76  * @param buf_size number of bytes in @a buf
     77  */
     78 static void
     79 prewire_cb (void *cls,
     80             uint64_t rowid,
     81             const char *wire_method,
     82             const char *buf,
     83             size_t buf_size)
     84 {
     85   struct PrewireContext *ctx = cls;
     86 
     87   if (0 == ctx->total++)
     88   {
     89     ctx->first_row = rowid;
     90     ctx->first_buf = GNUNET_memdup (buf,
     91                                     buf_size);
     92     ctx->first_buf_size = buf_size;
     93     ctx->first_method = GNUNET_strdup (wire_method);
     94   }
     95 }
     96 
     97 
     98 /**
     99  * Release what #prewire_cb() allocated.
    100  *
    101  * @param[in,out] ctx context to clean up
    102  */
    103 static void
    104 ctx_free (struct PrewireContext *ctx)
    105 {
    106   GNUNET_free (ctx->first_buf);
    107   GNUNET_free (ctx->first_method);
    108 }
    109 
    110 
    111 /**
    112  * An empty queue yields nothing, and marking rows that do not exist does
    113  * nothing.
    114  *
    115  * @param pg the database context
    116  * @return 0 on success
    117  */
    118 static int
    119 check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
    120 {
    121   struct PrewireContext ctx = { 0 };
    122 
    123   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    124           TALER_EXCHANGEDB_iterate_prewires (pg,
    125                                              0,
    126                                              10,
    127                                              &prewire_cb,
    128                                              &ctx));
    129   FAILIF (0 != ctx.total);
    130   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    131           TALER_EXCHANGEDB_update_to_prewire_finished (pg,
    132                                                        1));
    133   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    134           TALER_EXCHANGEDB_update_to_prewire_failed (pg,
    135                                                      1));
    136   return 0;
    137 }
    138 
    139 
    140 /**
    141  * Inserted rows come back in order, with their payload intact.
    142  *
    143  * @param pg the database context
    144  * @return 0 on success
    145  */
    146 static int
    147 check_insert_and_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
    148 {
    149   static const char buf1[] = "first-transfer";
    150   static const char buf2[] = "second-transfer";
    151   struct PrewireContext ctx;
    152   uint64_t row1;
    153 
    154   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    155           TALER_EXCHANGEDB_insert_prewire (pg,
    156                                            "x-taler-bank",
    157                                            buf1,
    158                                            sizeof (buf1)));
    159   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    160           TALER_EXCHANGEDB_insert_prewire (pg,
    161                                            "iban",
    162                                            buf2,
    163                                            sizeof (buf2)));
    164   memset (&ctx,
    165           0,
    166           sizeof (ctx));
    167   FAILIF (0 >=
    168           TALER_EXCHANGEDB_iterate_prewires (pg,
    169                                              0,
    170                                              10,
    171                                              &prewire_cb,
    172                                              &ctx));
    173   FAILIF_C (2 != ctx.total,
    174             ctx_free (&ctx));
    175   FAILIF_C (sizeof (buf1) != ctx.first_buf_size,
    176             ctx_free (&ctx));
    177   FAILIF_C (0 != memcmp (ctx.first_buf,
    178                          buf1,
    179                          sizeof (buf1)),
    180             ctx_free (&ctx));
    181   FAILIF_C (0 != strcmp (ctx.first_method,
    182                          "x-taler-bank"),
    183             ctx_free (&ctx));
    184   row1 = ctx.first_row;
    185   ctx_free (&ctx);
    186 
    187   /* the limit is honoured */
    188   memset (&ctx,
    189           0,
    190           sizeof (ctx));
    191   FAILIF (0 >=
    192           TALER_EXCHANGEDB_iterate_prewires (pg,
    193                                              0,
    194                                              1,
    195                                              &prewire_cb,
    196                                              &ctx));
    197   FAILIF_C (1 != ctx.total,
    198             ctx_free (&ctx));
    199   ctx_free (&ctx);
    200 
    201   /* ...and so is the starting row */
    202   memset (&ctx,
    203           0,
    204           sizeof (ctx));
    205   FAILIF (0 >=
    206           TALER_EXCHANGEDB_iterate_prewires (pg,
    207                                              row1 + 1,
    208                                              10,
    209                                              &prewire_cb,
    210                                              &ctx));
    211   FAILIF_C (1 != ctx.total,
    212             ctx_free (&ctx));
    213   FAILIF_C (0 != strcmp (ctx.first_method,
    214                          "iban"),
    215             ctx_free (&ctx));
    216   ctx_free (&ctx);
    217   return 0;
    218 }
    219 
    220 
    221 /**
    222  * Finished and failed rows drop out of the queue.
    223  *
    224  * @param pg the database context
    225  * @return 0 on success
    226  */
    227 static int
    228 check_finished_and_failed (struct TALER_EXCHANGEDB_PostgresContext *pg)
    229 {
    230   struct PrewireContext ctx;
    231   uint64_t row1;
    232   uint64_t row2;
    233 
    234   memset (&ctx,
    235           0,
    236           sizeof (ctx));
    237   FAILIF (0 >=
    238           TALER_EXCHANGEDB_iterate_prewires (pg,
    239                                              0,
    240                                              10,
    241                                              &prewire_cb,
    242                                              &ctx));
    243   FAILIF_C (2 != ctx.total,
    244             ctx_free (&ctx));
    245   row1 = ctx.first_row;
    246   ctx_free (&ctx);
    247 
    248   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    249           TALER_EXCHANGEDB_update_to_prewire_finished (pg,
    250                                                        row1));
    251   memset (&ctx,
    252           0,
    253           sizeof (ctx));
    254   FAILIF (0 >=
    255           TALER_EXCHANGEDB_iterate_prewires (pg,
    256                                              0,
    257                                              10,
    258                                              &prewire_cb,
    259                                              &ctx));
    260   FAILIF_C (1 != ctx.total,
    261             ctx_free (&ctx));
    262   row2 = ctx.first_row;
    263   ctx_free (&ctx);
    264   FAILIF (row2 == row1);
    265 
    266   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    267           TALER_EXCHANGEDB_update_to_prewire_failed (pg,
    268                                                      row2));
    269   memset (&ctx,
    270           0,
    271           sizeof (ctx));
    272   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    273           TALER_EXCHANGEDB_iterate_prewires (pg,
    274                                              0,
    275                                              10,
    276                                              &prewire_cb,
    277                                              &ctx));
    278   FAILIF_C (0 != ctx.total,
    279             ctx_free (&ctx));
    280   ctx_free (&ctx);
    281 
    282   /* both rows are still on file, one finished and one failed */
    283   FAILIF (2 != TDB_count (pg,
    284                           "FROM prewire"));
    285   FAILIF (1 != TDB_count (pg,
    286                           "FROM prewire WHERE finished"));
    287   FAILIF (1 != TDB_count (pg,
    288                           "FROM prewire WHERE failed"));
    289   /* marking again is still reported as a row touched */
    290   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    291           TALER_EXCHANGEDB_update_to_prewire_finished (pg,
    292                                                        row1));
    293   return 0;
    294 }
    295 
    296 
    297 /**
    298  * The checks to run, in order.
    299  */
    300 static const struct TDB_Test tests[] = {
    301   { "prewire-empty",
    302     &check_empty },
    303   { "prewire-insert-and-iterate",
    304     &check_insert_and_iterate },
    305   { "prewire-finished-and-failed",
    306     &check_finished_and_failed },
    307   { NULL, NULL }
    308 };
    309 
    310 
    311 int
    312 main (int argc,
    313       char *const *argv)
    314 {
    315   return TDB_main (argc,
    316                    argv,
    317                    "test-prewire",
    318                    "Tests for the exchangedb `prewire' table",
    319                    tests);
    320 }
    321 
    322 
    323 /* end of test_prewire.c */