exchange

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

test_aggregation_deferrals.c (8756B)


      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_aggregation_deferrals.c
     18  * @brief tests for the exchangedb functions whose primary table is
     19  *        `aggregation_deferrals`
     20  * @author Christian Grothoff
     21  *
     22  * Covers #TALER_EXCHANGEDB_insert_aggregation_deferral() and
     23  * #TALER_EXCHANGEDB_get_aggregation_deferral_by_wtid().
     24  *
     25  * The table is the exchange's append-only account of why it did not make a
     26  * wire transfer it had already decided on.  Each row hangs off the deposit
     27  * of the aggregate that is last to reach its wire deadline, so the insert
     28  * finds nothing to attach to until `aggregation_tracking` has been written
     29  * -- which is the first thing the checks pin down.  The lookup returns the
     30  * most recent claim, so the checks defer twice.
     31  */
     32 #include "test_common.h"
     33 #include "exchange-database/do_aggregate.h"
     34 #include "exchange-database/get_aggregation_deferral_by_wtid.h"
     35 #include "exchange-database/insert_aggregation_deferral.h"
     36 
     37 
     38 /**
     39  * Account the merchants of the checks are paid at.
     40  */
     41 static struct TDB_Account account;
     42 
     43 
     44 /**
     45  * Denomination the checks deposit.
     46  */
     47 static struct TDB_Denom denom;
     48 
     49 
     50 /**
     51  * The wire transfer the checks aggregate into.
     52  */
     53 static struct TALER_WireTransferIdentifierRawP wtid;
     54 
     55 
     56 /**
     57  * Build a timestamp from a number of seconds since the epoch.
     58  *
     59  * @param secs seconds since the epoch
     60  * @return the timestamp
     61  */
     62 static struct GNUNET_TIME_Timestamp
     63 ts (uint64_t secs)
     64 {
     65   struct GNUNET_TIME_Absolute abs = {
     66     .abs_value_us = secs * 1000LLU * 1000LLU
     67   };
     68 
     69   return GNUNET_TIME_absolute_to_timestamp (abs);
     70 }
     71 
     72 
     73 /**
     74  * Without an aggregation to hang off, nothing is recorded and nothing is
     75  * found.
     76  *
     77  * @param pg the database context
     78  * @return 0 on success
     79  */
     80 static int
     81 check_no_aggregation (struct TALER_EXCHANGEDB_PostgresContext *pg)
     82 {
     83   struct TALER_Amount amount = TDB_amount ("1");
     84   struct TALER_Amount got;
     85   struct GNUNET_TIME_Timestamp deferral_time;
     86   enum TALER_EXCHANGEDB_DeferralReason reason;
     87   uint64_t requirement_row;
     88 
     89   TDB_denom (pg,
     90              10,
     91              "5",
     92              "0.1",
     93              &denom);
     94   TDB_account (pg,
     95                10,
     96                &account);
     97   TDB_FILL (wtid,
     98             10);
     99   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    100           TALER_EXCHANGEDB_get_aggregation_deferral_by_wtid (pg,
    101                                                              &wtid,
    102                                                              &got,
    103                                                              &reason,
    104                                                              &requirement_row,
    105                                                              &deferral_time));
    106   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    107           TALER_EXCHANGEDB_insert_aggregation_deferral (
    108             pg,
    109             &wtid,
    110             &account.h_full,
    111             &amount,
    112             TALER_EXCHANGEDB_DR_AMOUNT_TOO_SMALL,
    113             0,
    114             ts (1600000000)));
    115   FAILIF (0 != TDB_count (pg,
    116                           "FROM aggregation_deferrals"));
    117   return 0;
    118 }
    119 
    120 
    121 /**
    122  * With an aggregation on file, the deferral is recorded and read back.
    123  *
    124  * @param pg the database context
    125  * @return 0 on success
    126  */
    127 static int
    128 check_defer (struct TALER_EXCHANGEDB_PostgresContext *pg)
    129 {
    130   struct TALER_CoinPublicInfo coin;
    131   struct TDB_Deposit dep;
    132   struct TALER_Amount total;
    133   struct TALER_Amount amount = TDB_amount ("0.9");
    134   struct TALER_Amount got;
    135   struct GNUNET_TIME_Timestamp deferral_time;
    136   enum TALER_EXCHANGEDB_DeferralReason reason;
    137   uint64_t requirement_row = 42;
    138 
    139   TDB_coin (pg,
    140             &denom,
    141             20,
    142             &coin,
    143             NULL);
    144   TDB_deposit (pg,
    145                &account,
    146                &coin,
    147                20,
    148                "1",
    149                "0.1",
    150                ts (1600000000),
    151                ts (1600000000),
    152                &dep);
    153   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    154             TALER_EXCHANGEDB_do_aggregate (pg,
    155                                            &account.h_full,
    156                                            &dep.merchant_pub,
    157                                            &wtid,
    158                                            &total),
    159             TDB_coin_free (&coin));
    160   TDB_coin_free (&coin);
    161 
    162   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    163           TALER_EXCHANGEDB_insert_aggregation_deferral (
    164             pg,
    165             &wtid,
    166             &account.h_full,
    167             &amount,
    168             TALER_EXCHANGEDB_DR_AMOUNT_TOO_SMALL,
    169             0,
    170             ts (1600000000)));
    171   FAILIF (1 != TDB_count (pg,
    172                           "FROM aggregation_deferrals"));
    173   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    174           TALER_EXCHANGEDB_get_aggregation_deferral_by_wtid (pg,
    175                                                              &wtid,
    176                                                              &got,
    177                                                              &reason,
    178                                                              &requirement_row,
    179                                                              &deferral_time));
    180   FAILIF (0 != TALER_amount_cmp (&got,
    181                                  &amount));
    182   FAILIF (TALER_EXCHANGEDB_DR_AMOUNT_TOO_SMALL != reason);
    183   FAILIF (0 != requirement_row);
    184   FAILIF (GNUNET_TIME_timestamp_cmp (deferral_time,
    185                                      !=,
    186                                      ts (1600000000)));
    187 
    188   /* a wire transfer nobody deferred is still unknown */
    189   {
    190     struct TALER_WireTransferIdentifierRawP other;
    191 
    192     TDB_FILL (other,
    193               99);
    194     FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    195             TALER_EXCHANGEDB_get_aggregation_deferral_by_wtid (
    196               pg,
    197               &other,
    198               &got,
    199               &reason,
    200               &requirement_row,
    201               &deferral_time));
    202   }
    203   return 0;
    204 }
    205 
    206 
    207 /**
    208  * The table is append-only: a second deferral is a second row, and the
    209  * lookup returns the newer one.
    210  *
    211  * @param pg the database context
    212  * @return 0 on success
    213  */
    214 static int
    215 check_append_only (struct TALER_EXCHANGEDB_PostgresContext *pg)
    216 {
    217   struct TALER_Amount amount = TDB_amount ("0.9");
    218   struct TALER_Amount got;
    219   struct GNUNET_TIME_Timestamp deferral_time;
    220   enum TALER_EXCHANGEDB_DeferralReason reason;
    221   uint64_t requirement_row = 0;
    222 
    223   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    224           TALER_EXCHANGEDB_insert_aggregation_deferral (
    225             pg,
    226             &wtid,
    227             &account.h_full,
    228             &amount,
    229             TALER_EXCHANGEDB_DR_KYC,
    230             7,
    231             ts (1600003600)));
    232   FAILIF (2 != TDB_count (pg,
    233                           "FROM aggregation_deferrals"));
    234   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    235           TALER_EXCHANGEDB_get_aggregation_deferral_by_wtid (pg,
    236                                                              &wtid,
    237                                                              &got,
    238                                                              &reason,
    239                                                              &requirement_row,
    240                                                              &deferral_time));
    241   FAILIF (TALER_EXCHANGEDB_DR_KYC != reason);
    242   FAILIF (7 != requirement_row);
    243   FAILIF (GNUNET_TIME_timestamp_cmp (deferral_time,
    244                                      !=,
    245                                      ts (1600003600)));
    246   return 0;
    247 }
    248 
    249 
    250 /**
    251  * The checks to run, in order.
    252  */
    253 static const struct TDB_Test tests[] = {
    254   { "aggregation-deferrals-no-aggregation",
    255     &check_no_aggregation },
    256   { "aggregation-deferrals-defer",
    257     &check_defer },
    258   { "aggregation-deferrals-append-only",
    259     &check_append_only },
    260   { NULL, NULL }
    261 };
    262 
    263 
    264 int
    265 main (int argc,
    266       char *const *argv)
    267 {
    268   int ret;
    269 
    270   ret = TDB_main (argc,
    271                   argv,
    272                   "test-aggregation-deferrals",
    273                   "Tests for the exchangedb `aggregation_deferrals' table",
    274                   tests);
    275   TDB_account_free (&account);
    276   TDB_denom_free (&denom);
    277   return ret;
    278 }
    279 
    280 
    281 /* end of test_aggregation_deferrals.c */