LCOV - code coverage report
Current view: top level - exchangedb - test_prewire.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 104 104
Test Date: 2026-10-29 19:05:17 Functions: 100.0 % 8 8

            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_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/iterate_prewires_below_open_shards.h"
      34              : #include "exchange-database/update_to_prewire_finished.h"
      35              : #include "exchange-database/update_to_prewire_failed.h"
      36              : 
      37              : 
      38              : /**
      39              :  * Closure for #prewire_cb().
      40              :  */
      41              : struct PrewireContext
      42              : {
      43              :   /**
      44              :    * How many rows did the callback see?
      45              :    */
      46              :   unsigned int total;
      47              : 
      48              :   /**
      49              :    * Row of the first entry seen.
      50              :    */
      51              :   uint64_t first_row;
      52              : 
      53              :   /**
      54              :    * Payload of the first entry seen, owned by this struct.
      55              :    */
      56              :   char *first_buf;
      57              : 
      58              :   /**
      59              :    * Number of bytes in @e first_buf.
      60              :    */
      61              :   size_t first_buf_size;
      62              : 
      63              :   /**
      64              :    * Wire method of the first entry seen, owned by this struct.
      65              :    */
      66              :   char *first_method;
      67              : };
      68              : 
      69              : 
      70              : /**
      71              :  * Callback for #TALER_EXCHANGEDB_iterate_prewires().
      72              :  *
      73              :  * @param cls a `struct PrewireContext *`
      74              :  * @param rowid row of the entry
      75              :  * @param wire_method wire method of the entry
      76              :  * @param buf payload of the entry
      77              :  * @param buf_size number of bytes in @a buf
      78              :  */
      79              : static void
      80            7 : prewire_cb (void *cls,
      81              :             uint64_t rowid,
      82              :             const char *wire_method,
      83              :             const char *buf,
      84              :             size_t buf_size)
      85              : {
      86            7 :   struct PrewireContext *ctx = cls;
      87              : 
      88            7 :   if (0 == ctx->total++)
      89              :   {
      90            5 :     ctx->first_row = rowid;
      91            5 :     ctx->first_buf = GNUNET_memdup (buf,
      92              :                                     buf_size);
      93            5 :     ctx->first_buf_size = buf_size;
      94            5 :     ctx->first_method = GNUNET_strdup (wire_method);
      95              :   }
      96            7 : }
      97              : 
      98              : 
      99              : /**
     100              :  * Release what #prewire_cb() allocated.
     101              :  *
     102              :  * @param[in,out] ctx context to clean up
     103              :  */
     104              : static void
     105            6 : ctx_free (struct PrewireContext *ctx)
     106              : {
     107            6 :   GNUNET_free (ctx->first_buf);
     108            6 :   GNUNET_free (ctx->first_method);
     109            6 : }
     110              : 
     111              : 
     112              : /**
     113              :  * Closure for #rows_cb().
     114              :  */
     115              : struct RowsContext
     116              : {
     117              :   /**
     118              :    * Rows seen, in the order the callback saw them.
     119              :    */
     120              :   uint64_t rows[8];
     121              : 
     122              :   /**
     123              :    * How many rows did the callback see?
     124              :    */
     125              :   unsigned int total;
     126              : };
     127              : 
     128              : 
     129              : static void
     130           11 : rows_cb (void *cls,
     131              :          uint64_t rowid,
     132              :          const char *wire_method,
     133              :          const char *buf,
     134              :          size_t buf_size)
     135              : {
     136           11 :   struct RowsContext *rc = cls;
     137              : 
     138              :   (void) wire_method;
     139              :   (void) buf;
     140              :   (void) buf_size;
     141           11 :   if (rc->total < sizeof (rc->rows) / sizeof (rc->rows[0]))
     142           11 :     rc->rows[rc->total] = rowid;
     143           11 :   rc->total++;
     144           11 : }
     145              : 
     146              : 
     147              : /**
     148              :  * An empty queue yields nothing, and marking rows that do not exist does
     149              :  * nothing.
     150              :  *
     151              :  * @param pg the database context
     152              :  * @return 0 on success
     153              :  */
     154              : static int
     155            1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
     156              : {
     157            1 :   struct PrewireContext ctx = { 0 };
     158              : 
     159            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     160              :           TALER_EXCHANGEDB_iterate_prewires (pg,
     161              :                                              0,
     162              :                                              10,
     163              :                                              &prewire_cb,
     164              :                                              &ctx));
     165            1 :   FAILIF (0 != ctx.total);
     166            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     167              :           TALER_EXCHANGEDB_update_to_prewire_finished (pg,
     168              :                                                        1));
     169            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     170              :           TALER_EXCHANGEDB_update_to_prewire_failed (pg,
     171              :                                                      1));
     172            1 :   return 0;
     173              : }
     174              : 
     175              : 
     176              : /**
     177              :  * Inserted rows come back in order, with their payload intact.
     178              :  *
     179              :  * @param pg the database context
     180              :  * @return 0 on success
     181              :  */
     182              : static int
     183            1 : check_insert_and_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
     184              : {
     185              :   static const char buf1[] = "first-transfer";
     186              :   static const char buf2[] = "second-transfer";
     187              :   struct PrewireContext ctx;
     188              :   uint64_t row1;
     189              : 
     190            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     191              :           TALER_EXCHANGEDB_insert_prewire (pg,
     192              :                                            "x-taler-bank",
     193              :                                            buf1,
     194              :                                            sizeof (buf1)));
     195            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     196              :           TALER_EXCHANGEDB_insert_prewire (pg,
     197              :                                            "iban",
     198              :                                            buf2,
     199              :                                            sizeof (buf2)));
     200            1 :   memset (&ctx,
     201              :           0,
     202              :           sizeof (ctx));
     203            1 :   FAILIF (0 >=
     204              :           TALER_EXCHANGEDB_iterate_prewires (pg,
     205              :                                              0,
     206              :                                              10,
     207              :                                              &prewire_cb,
     208              :                                              &ctx));
     209            1 :   FAILIF_C (2 != ctx.total,
     210              :             ctx_free (&ctx));
     211            1 :   FAILIF_C (sizeof (buf1) != ctx.first_buf_size,
     212              :             ctx_free (&ctx));
     213            1 :   FAILIF_C (0 != memcmp (ctx.first_buf,
     214              :                          buf1,
     215              :                          sizeof (buf1)),
     216              :             ctx_free (&ctx));
     217            1 :   FAILIF_C (0 != strcmp (ctx.first_method,
     218              :                          "x-taler-bank"),
     219              :             ctx_free (&ctx));
     220            1 :   row1 = ctx.first_row;
     221            1 :   ctx_free (&ctx);
     222              : 
     223              :   /* the limit is honoured */
     224            1 :   memset (&ctx,
     225              :           0,
     226              :           sizeof (ctx));
     227            1 :   FAILIF (0 >=
     228              :           TALER_EXCHANGEDB_iterate_prewires (pg,
     229              :                                              0,
     230              :                                              1,
     231              :                                              &prewire_cb,
     232              :                                              &ctx));
     233            1 :   FAILIF_C (1 != ctx.total,
     234              :             ctx_free (&ctx));
     235            1 :   ctx_free (&ctx);
     236              : 
     237              :   /* ...and so is the starting row */
     238            1 :   memset (&ctx,
     239              :           0,
     240              :           sizeof (ctx));
     241            1 :   FAILIF (0 >=
     242              :           TALER_EXCHANGEDB_iterate_prewires (pg,
     243              :                                              row1 + 1,
     244              :                                              10,
     245              :                                              &prewire_cb,
     246              :                                              &ctx));
     247            1 :   FAILIF_C (1 != ctx.total,
     248              :             ctx_free (&ctx));
     249            1 :   FAILIF_C (0 != strcmp (ctx.first_method,
     250              :                          "iban"),
     251              :             ctx_free (&ctx));
     252            1 :   ctx_free (&ctx);
     253            1 :   return 0;
     254              : }
     255              : 
     256              : 
     257              : /**
     258              :  * Finished and failed rows drop out of the queue.
     259              :  *
     260              :  * @param pg the database context
     261              :  * @return 0 on success
     262              :  */
     263              : static int
     264            1 : check_finished_and_failed (struct TALER_EXCHANGEDB_PostgresContext *pg)
     265              : {
     266              :   struct PrewireContext ctx;
     267              :   uint64_t row1;
     268              :   uint64_t row2;
     269              : 
     270            1 :   memset (&ctx,
     271              :           0,
     272              :           sizeof (ctx));
     273            1 :   FAILIF (0 >=
     274              :           TALER_EXCHANGEDB_iterate_prewires (pg,
     275              :                                              0,
     276              :                                              10,
     277              :                                              &prewire_cb,
     278              :                                              &ctx));
     279            1 :   FAILIF_C (2 != ctx.total,
     280              :             ctx_free (&ctx));
     281            1 :   row1 = ctx.first_row;
     282            1 :   ctx_free (&ctx);
     283              : 
     284            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     285              :           TALER_EXCHANGEDB_update_to_prewire_finished (pg,
     286              :                                                        row1));
     287            1 :   memset (&ctx,
     288              :           0,
     289              :           sizeof (ctx));
     290            1 :   FAILIF (0 >=
     291              :           TALER_EXCHANGEDB_iterate_prewires (pg,
     292              :                                              0,
     293              :                                              10,
     294              :                                              &prewire_cb,
     295              :                                              &ctx));
     296            1 :   FAILIF_C (1 != ctx.total,
     297              :             ctx_free (&ctx));
     298            1 :   row2 = ctx.first_row;
     299            1 :   ctx_free (&ctx);
     300            1 :   FAILIF (row2 == row1);
     301              : 
     302            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     303              :           TALER_EXCHANGEDB_update_to_prewire_failed (pg,
     304              :                                                      row2));
     305            1 :   memset (&ctx,
     306              :           0,
     307              :           sizeof (ctx));
     308            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     309              :           TALER_EXCHANGEDB_iterate_prewires (pg,
     310              :                                              0,
     311              :                                              10,
     312              :                                              &prewire_cb,
     313              :                                              &ctx));
     314            1 :   FAILIF_C (0 != ctx.total,
     315              :             ctx_free (&ctx));
     316            1 :   ctx_free (&ctx);
     317              : 
     318              :   /* both rows are still on file, one finished and one failed */
     319            1 :   FAILIF (2 != TDB_count (pg,
     320              :                           "FROM prewire"));
     321            1 :   FAILIF (1 != TDB_count (pg,
     322              :                           "FROM prewire WHERE finished"));
     323            1 :   FAILIF (1 != TDB_count (pg,
     324              :                           "FROM prewire WHERE failed"));
     325              :   /* marking again is still reported as a row touched */
     326            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     327              :           TALER_EXCHANGEDB_update_to_prewire_finished (pg,
     328              :                                                        row1));
     329            1 :   return 0;
     330              : }
     331              : 
     332              : 
     333              : /**
     334              :  * Unfinished rows below every open shard of the job are found; rows in
     335              :  * or above an open shard, finished and failed rows, and the shards of
     336              :  * other jobs do not count.
     337              :  *
     338              :  * @param pg the database context
     339              :  * @return 0 on success
     340              :  */
     341              : static int
     342            1 : check_below_open_shards (struct TALER_EXCHANGEDB_PostgresContext *pg)
     343              : {
     344              :   static const char buf[] = "late-transfer";
     345              :   struct RowsContext rc;
     346              :   uint64_t r[4];
     347              : 
     348            5 :   for (unsigned int i = 0; i < 4; i++)
     349            4 :     FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     350              :             TALER_EXCHANGEDB_insert_prewire (pg,
     351              :                                              "iban",
     352              :                                              buf,
     353              :                                              sizeof (buf)));
     354              :   /* the rows of the earlier checks are finished or failed */
     355            1 :   memset (&rc,
     356              :           0,
     357              :           sizeof (rc));
     358            1 :   FAILIF (0 >=
     359              :           TALER_EXCHANGEDB_iterate_prewires (pg,
     360              :                                              0,
     361              :                                              10,
     362              :                                              &rows_cb,
     363              :                                              &rc));
     364            1 :   FAILIF (4 != rc.total);
     365            1 :   memcpy (r,
     366              :           rc.rows,
     367              :           sizeof (r));
     368              : 
     369              :   /* without shards, nothing is below them */
     370            1 :   memset (&rc,
     371              :           0,
     372              :           sizeof (rc));
     373            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     374              :           TALER_EXCHANGEDB_iterate_prewires_below_open_shards (pg,
     375              :                                                                "transfer",
     376              :                                                                10,
     377              :                                                                &rows_cb,
     378              :                                                                &rc));
     379            1 :   FAILIF (0 != rc.total);
     380              : 
     381              :   /* r[0] and r[1] lie in a completed shard, r[2] and r[3] in an open
     382              :      one; an open shard of another job covers all of them */
     383            1 :   FAILIF (GNUNET_OK !=
     384              :           TDB_exec (pg,
     385              :                     "INSERT INTO work_shards"
     386              :                     " (job_name,last_attempt,start_row,end_row"
     387              :                     " ,completed,progress_row)"
     388              :                     " VALUES"
     389              :                     " ('transfer',0,0,%llu,TRUE,%llu)"
     390              :                     ",('transfer',0,%llu,%llu,FALSE,%llu)"
     391              :                     ",('other',0,0,%llu,FALSE,0);",
     392              :                     (unsigned long long) r[2],
     393              :                     (unsigned long long) r[2],
     394              :                     (unsigned long long) r[2],
     395              :                     (unsigned long long) (r[3] + 10),
     396              :                     (unsigned long long) (r[3] + 1),
     397              :                     (unsigned long long) (r[3] + 10)));
     398            1 :   memset (&rc,
     399              :           0,
     400              :           sizeof (rc));
     401            1 :   FAILIF (0 >=
     402              :           TALER_EXCHANGEDB_iterate_prewires_below_open_shards (pg,
     403              :                                                                "transfer",
     404              :                                                                10,
     405              :                                                                &rows_cb,
     406              :                                                                &rc));
     407            1 :   FAILIF (2 != rc.total);
     408            1 :   FAILIF (r[0] != rc.rows[0]);
     409            1 :   FAILIF (r[1] != rc.rows[1]);
     410              : 
     411              :   /* the limit is honoured */
     412            1 :   memset (&rc,
     413              :           0,
     414              :           sizeof (rc));
     415            1 :   FAILIF (0 >=
     416              :           TALER_EXCHANGEDB_iterate_prewires_below_open_shards (pg,
     417              :                                                                "transfer",
     418              :                                                                1,
     419              :                                                                &rows_cb,
     420              :                                                                &rc));
     421            1 :   FAILIF (1 != rc.total);
     422            1 :   FAILIF (r[0] != rc.rows[0]);
     423              : 
     424              :   /* finished rows drop out */
     425            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     426              :           TALER_EXCHANGEDB_update_to_prewire_finished (pg,
     427              :                                                        r[0]));
     428            1 :   memset (&rc,
     429              :           0,
     430              :           sizeof (rc));
     431            1 :   FAILIF (0 >=
     432              :           TALER_EXCHANGEDB_iterate_prewires_below_open_shards (pg,
     433              :                                                                "transfer",
     434              :                                                                10,
     435              :                                                                &rows_cb,
     436              :                                                                &rc));
     437            1 :   FAILIF (1 != rc.total);
     438            1 :   FAILIF (r[1] != rc.rows[0]);
     439              : 
     440              :   /* with no shard of the job open, everything below the highest shard's
     441              :      end counts */
     442            1 :   FAILIF (GNUNET_OK !=
     443              :           TDB_exec (pg,
     444              :                     "UPDATE work_shards"
     445              :                     " SET completed=TRUE"
     446              :                     "    ,progress_row=end_row"
     447              :                     " WHERE job_name='transfer';"));
     448            1 :   memset (&rc,
     449              :           0,
     450              :           sizeof (rc));
     451            1 :   FAILIF (0 >=
     452              :           TALER_EXCHANGEDB_iterate_prewires_below_open_shards (pg,
     453              :                                                                "transfer",
     454              :                                                                10,
     455              :                                                                &rows_cb,
     456              :                                                                &rc));
     457            1 :   FAILIF (3 != rc.total);
     458            1 :   FAILIF (r[1] != rc.rows[0]);
     459            1 :   FAILIF (r[2] != rc.rows[1]);
     460            1 :   FAILIF (r[3] != rc.rows[2]);
     461            1 :   return 0;
     462              : }
     463              : 
     464              : 
     465              : /**
     466              :  * The checks to run, in order.
     467              :  */
     468              : static const struct TDB_Test tests[] = {
     469              :   { "prewire-empty",
     470              :     &check_empty },
     471              :   { "prewire-insert-and-iterate",
     472              :     &check_insert_and_iterate },
     473              :   { "prewire-finished-and-failed",
     474              :     &check_finished_and_failed },
     475              :   { "prewire-below-open-shards",
     476              :     &check_below_open_shards },
     477              :   { NULL, NULL }
     478              : };
     479              : 
     480              : 
     481              : int
     482            1 : main (int argc,
     483              :       char *const *argv)
     484              : {
     485            1 :   return TDB_main (argc,
     486              :                    argv,
     487              :                    "test-prewire",
     488              :                    "Tests for the exchangedb `prewire' table",
     489              :                    tests);
     490              : }
     491              : 
     492              : 
     493              : /* end of test_prewire.c */
        

Generated by: LCOV version 2.0-1