LCOV - code coverage report
Current view: top level - exchangedb - test_profit_drains.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 65 65
Test Date: 2026-09-11 18:55:36 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_profit_drains.c
      18              :  * @brief tests for the exchangedb functions whose primary table is
      19              :  *        `profit_drains`
      20              :  * @author Christian Grothoff
      21              :  *
      22              :  * Covers #TALER_EXCHANGEDB_insert_profit_drain(),
      23              :  * #TALER_EXCHANGEDB_get_profit_drain(),
      24              :  * #TALER_EXCHANGEDB_get_pending_profit_drain() and
      25              :  * #TALER_EXCHANGEDB_update_to_profit_drain_finished().
      26              :  *
      27              :  * `profit_drains` has no foreign keys.  The pending lookup picks the
      28              :  * oldest not-yet-executed request, so the checks insert two out of order
      29              :  * and drain them one by one.
      30              :  */
      31              : #include "test_common.h"
      32              : #include "exchange-database/insert_profit_drain.h"
      33              : #include "exchange-database/get_profit_drain.h"
      34              : #include "exchange-database/get_pending_profit_drain.h"
      35              : #include "exchange-database/update_to_profit_drain_finished.h"
      36              : 
      37              : 
      38              : /**
      39              :  * Build a timestamp from a number of seconds since the epoch.
      40              :  *
      41              :  * @param secs seconds since the epoch
      42              :  * @return the timestamp
      43              :  */
      44              : static struct GNUNET_TIME_Timestamp
      45            3 : ts (uint64_t secs)
      46              : {
      47            3 :   struct GNUNET_TIME_Absolute abs = {
      48            3 :     .abs_value_us = secs * 1000LLU * 1000LLU
      49              :   };
      50              : 
      51            3 :   return GNUNET_TIME_absolute_to_timestamp (abs);
      52              : }
      53              : 
      54              : 
      55              : /**
      56              :  * Insert a drain request.
      57              :  *
      58              :  * @param pg the database context
      59              :  * @param seed seed for the wire transfer identifier and signature
      60              :  * @param when when was the request made
      61              :  * @param amount amount to drain, e.g. "10"
      62              :  * @param[out] wtid set to the wire transfer identifier used
      63              :  * @return transaction status
      64              :  */
      65              : static enum GNUNET_DB_QueryStatus
      66            2 : add_drain (struct TALER_EXCHANGEDB_PostgresContext *pg,
      67              :            uint32_t seed,
      68              :            uint64_t when,
      69              :            const char *amount,
      70              :            struct TALER_WireTransferIdentifierRawP *wtid)
      71              : {
      72            2 :   struct TALER_FullPayto payto = {
      73              :     .full_payto = (char *) "payto://x-taler-bank/localhost/profits"
      74              :   };
      75            2 :   struct TALER_Amount a = TDB_amount (amount);
      76              :   struct TALER_MasterSignatureP master_sig;
      77              : 
      78            2 :   TDB_fill (wtid,
      79              :             sizeof (*wtid),
      80              :             seed);
      81            2 :   TDB_FILL (master_sig,
      82              :             seed);
      83            2 :   return TALER_EXCHANGEDB_insert_profit_drain (pg,
      84              :                                                wtid,
      85              :                                                "exchange-account-1",
      86              :                                                payto,
      87              :                                                ts (when),
      88              :                                                &a,
      89              :                                                &master_sig);
      90              : }
      91              : 
      92              : 
      93              : /**
      94              :  * Nothing is pending and nothing is found while the table is empty.
      95              :  *
      96              :  * @param pg the database context
      97              :  * @return 0 on success
      98              :  */
      99              : static int
     100            1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
     101              : {
     102              :   struct TALER_WireTransferIdentifierRawP wtid;
     103              :   struct TALER_FullPayto payto_uri;
     104              :   struct GNUNET_TIME_Timestamp request_timestamp;
     105              :   struct TALER_Amount amount;
     106              :   struct TALER_MasterSignatureP master_sig;
     107            1 :   char *account_section = NULL;
     108              :   uint64_t serial;
     109              : 
     110            1 :   TDB_FILL (wtid,
     111              :             1);
     112            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     113              :           TALER_EXCHANGEDB_get_profit_drain (pg,
     114              :                                              &wtid,
     115              :                                              &serial,
     116              :                                              &account_section,
     117              :                                              &payto_uri,
     118              :                                              &request_timestamp,
     119              :                                              &amount,
     120              :                                              &master_sig));
     121            1 :   FAILIF (NULL != account_section);
     122            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     123              :           TALER_EXCHANGEDB_get_pending_profit_drain (pg,
     124              :                                                      &serial,
     125              :                                                      &wtid,
     126              :                                                      &account_section,
     127              :                                                      &payto_uri,
     128              :                                                      &request_timestamp,
     129              :                                                      &amount,
     130              :                                                      &master_sig));
     131            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     132              :           TALER_EXCHANGEDB_update_to_profit_drain_finished (pg,
     133              :                                                             1));
     134            1 :   return 0;
     135              : }
     136              : 
     137              : 
     138              : /**
     139              :  * A drain request is stored, found by its wire transfer identifier and
     140              :  * reported as pending.
     141              :  *
     142              :  * @param pg the database context
     143              :  * @return 0 on success
     144              :  */
     145              : static int
     146            1 : check_insert_and_lookup (struct TALER_EXCHANGEDB_PostgresContext *pg)
     147              : {
     148              :   struct TALER_WireTransferIdentifierRawP wtid;
     149              :   struct TALER_WireTransferIdentifierRawP unknown;
     150              :   struct TALER_WireTransferIdentifierRawP got_wtid;
     151            1 :   struct TALER_FullPayto payto_uri = { NULL };
     152              :   struct GNUNET_TIME_Timestamp request_timestamp;
     153              :   struct TALER_Amount amount;
     154            1 :   struct TALER_Amount expect = TDB_amount ("10");
     155              :   struct TALER_MasterSignatureP master_sig;
     156              :   struct TALER_MasterSignatureP expect_sig;
     157            1 :   char *account_section = NULL;
     158            1 :   uint64_t serial = 0;
     159              : 
     160            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     161              :           add_drain (pg,
     162              :                      10,
     163              :                      1600000000,
     164              :                      "10",
     165              :                      &wtid));
     166            1 :   TDB_FILL (expect_sig,
     167              :             10);
     168            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     169              :           TALER_EXCHANGEDB_get_profit_drain (pg,
     170              :                                              &wtid,
     171              :                                              &serial,
     172              :                                              &account_section,
     173              :                                              &payto_uri,
     174              :                                              &request_timestamp,
     175              :                                              &amount,
     176              :                                              &master_sig));
     177            1 :   FAILIF (0 == serial);
     178            1 :   FAILIF (0 != strcmp (account_section,
     179              :                        "exchange-account-1"));
     180            1 :   GNUNET_free (account_section);
     181            1 :   FAILIF (0 != strcmp (payto_uri.full_payto,
     182              :                        "payto://x-taler-bank/localhost/profits"));
     183            1 :   GNUNET_free (payto_uri.full_payto);
     184            1 :   FAILIF (0 != TALER_amount_cmp (&amount,
     185              :                                  &expect));
     186            1 :   FAILIF (GNUNET_TIME_timestamp_cmp (request_timestamp,
     187              :                                      !=,
     188              :                                      ts (1600000000)));
     189            1 :   FAILIF (0 != GNUNET_memcmp (&master_sig,
     190              :                               &expect_sig));
     191              : 
     192              :   /* a wire transfer identifier we never used is not found */
     193            1 :   TDB_FILL (unknown,
     194              :             99);
     195            1 :   account_section = NULL;
     196            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     197              :           TALER_EXCHANGEDB_get_profit_drain (pg,
     198              :                                              &unknown,
     199              :                                              &serial,
     200              :                                              &account_section,
     201              :                                              &payto_uri,
     202              :                                              &request_timestamp,
     203              :                                              &amount,
     204              :                                              &master_sig));
     205              : 
     206            1 :   account_section = NULL;
     207            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     208              :           TALER_EXCHANGEDB_get_pending_profit_drain (pg,
     209              :                                                      &serial,
     210              :                                                      &got_wtid,
     211              :                                                      &account_section,
     212              :                                                      &payto_uri,
     213              :                                                      &request_timestamp,
     214              :                                                      &amount,
     215              :                                                      &master_sig));
     216            1 :   GNUNET_free (account_section);
     217            1 :   GNUNET_free (payto_uri.full_payto);
     218            1 :   FAILIF (0 != GNUNET_memcmp (&got_wtid,
     219              :                               &wtid));
     220            1 :   return 0;
     221              : }
     222              : 
     223              : 
     224              : /**
     225              :  * The oldest unexecuted request is the pending one, and marking it
     226              :  * executed hands the next one over.
     227              :  *
     228              :  * @param pg the database context
     229              :  * @return 0 on success
     230              :  */
     231              : static int
     232            1 : check_pending_order (struct TALER_EXCHANGEDB_PostgresContext *pg)
     233              : {
     234              :   struct TALER_WireTransferIdentifierRawP older;
     235              :   struct TALER_WireTransferIdentifierRawP got_wtid;
     236            1 :   struct TALER_FullPayto payto_uri = { NULL };
     237              :   struct GNUNET_TIME_Timestamp request_timestamp;
     238              :   struct TALER_Amount amount;
     239              :   struct TALER_MasterSignatureP master_sig;
     240            1 :   char *account_section = NULL;
     241            1 :   uint64_t serial = 0;
     242              : 
     243              :   /* inserted second, but with an earlier trigger date */
     244            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     245              :           add_drain (pg,
     246              :                      11,
     247              :                      1500000000,
     248              :                      "3",
     249              :                      &older));
     250            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     251              :           TALER_EXCHANGEDB_get_pending_profit_drain (pg,
     252              :                                                      &serial,
     253              :                                                      &got_wtid,
     254              :                                                      &account_section,
     255              :                                                      &payto_uri,
     256              :                                                      &request_timestamp,
     257              :                                                      &amount,
     258              :                                                      &master_sig));
     259            1 :   GNUNET_free (account_section);
     260            1 :   GNUNET_free (payto_uri.full_payto);
     261            1 :   FAILIF (0 != GNUNET_memcmp (&got_wtid,
     262              :                               &older));
     263              : 
     264              :   /* draining it hands over the remaining one */
     265            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     266              :           TALER_EXCHANGEDB_update_to_profit_drain_finished (pg,
     267              :                                                             serial));
     268            1 :   account_section = NULL;
     269            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     270              :           TALER_EXCHANGEDB_get_pending_profit_drain (pg,
     271              :                                                      &serial,
     272              :                                                      &got_wtid,
     273              :                                                      &account_section,
     274              :                                                      &payto_uri,
     275              :                                                      &request_timestamp,
     276              :                                                      &amount,
     277              :                                                      &master_sig));
     278            1 :   GNUNET_free (account_section);
     279            1 :   GNUNET_free (payto_uri.full_payto);
     280            1 :   FAILIF (0 == GNUNET_memcmp (&got_wtid,
     281              :                               &older));
     282              : 
     283              :   /* and once both are executed, nothing is pending */
     284            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     285              :           TALER_EXCHANGEDB_update_to_profit_drain_finished (pg,
     286              :                                                             serial));
     287            1 :   account_section = NULL;
     288            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     289              :           TALER_EXCHANGEDB_get_pending_profit_drain (pg,
     290              :                                                      &serial,
     291              :                                                      &got_wtid,
     292              :                                                      &account_section,
     293              :                                                      &payto_uri,
     294              :                                                      &request_timestamp,
     295              :                                                      &amount,
     296              :                                                      &master_sig));
     297              :   /* the executed rows are still on file */
     298            1 :   FAILIF (2 != TDB_count (pg,
     299              :                           "FROM profit_drains WHERE executed"));
     300            1 :   return 0;
     301              : }
     302              : 
     303              : 
     304              : /**
     305              :  * The checks to run, in order.
     306              :  */
     307              : static const struct TDB_Test tests[] = {
     308              :   { "profit-drains-empty",
     309              :     &check_empty },
     310              :   { "profit-drains-insert-and-lookup",
     311              :     &check_insert_and_lookup },
     312              :   { "profit-drains-pending-order",
     313              :     &check_pending_order },
     314              :   { NULL, NULL }
     315              : };
     316              : 
     317              : 
     318              : int
     319            1 : main (int argc,
     320              :       char *const *argv)
     321              : {
     322            1 :   return TDB_main (argc,
     323              :                    argv,
     324              :                    "test-profit-drains",
     325              :                    "Tests for the exchangedb `profit_drains' table",
     326              :                    tests);
     327              : }
     328              : 
     329              : 
     330              : /* end of test_profit_drains.c */
        

Generated by: LCOV version 2.0-1