exchange

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

test_aggregation_transient.c (13104B)


      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_transient.c
     18  * @brief tests for the exchangedb functions whose primary table is
     19  *        `aggregation_transient`
     20  * @author Christian Grothoff
     21  *
     22  * Covers #TALER_EXCHANGEDB_insert_aggregation_transient(),
     23  * #TALER_EXCHANGEDB_update_aggregation_transient(),
     24  * #TALER_EXCHANGEDB_delete_aggregation_transient(),
     25  * #TALER_EXCHANGEDB_get_aggregation_transient(),
     26  * #TALER_EXCHANGEDB_get_aggregation_transient_by_wtid() and
     27  * #TALER_EXCHANGEDB_get_aggregation_transient_by_normalized_payto().
     28  *
     29  * The table holds what the aggregator decided to hold back for later; it
     30  * is updated in place and deleted on payout, so it has no serial ID.  It
     31  * references `wire_targets`, which TDB_account() creates.  The three
     32  * lookups reach the row by three different keys, which is what the checks
     33  * exercise.
     34  */
     35 #include "test_common.h"
     36 #include "exchange-database/delete_aggregation_transient.h"
     37 #include "exchange-database/get_aggregation_transient.h"
     38 #include "exchange-database/get_aggregation_transient_by_normalized_payto.h"
     39 #include "exchange-database/get_aggregation_transient_by_wtid.h"
     40 #include "exchange-database/insert_aggregation_transient.h"
     41 #include "exchange-database/update_aggregation_transient.h"
     42 
     43 
     44 /**
     45  * Exchange bank account the checks aggregate for.
     46  */
     47 #define SECTION "exchange-account-1"
     48 
     49 
     50 /**
     51  * Account the merchants of the checks are paid at.
     52  */
     53 static struct TDB_Account account;
     54 
     55 
     56 /**
     57  * Nothing is found while the table is empty, and updating or deleting a
     58  * row that is not there does nothing.
     59  *
     60  * @param pg the database context
     61  * @return 0 on success
     62  */
     63 static int
     64 check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
     65 {
     66   struct TALER_MerchantPublicKeyP merchant_pub;
     67   struct TALER_WireTransferIdentifierRawP wtid;
     68   struct TALER_Amount total;
     69   struct TALER_Amount one = TDB_amount ("1");
     70   struct TALER_FullPayto payto_uri = { NULL };
     71   uint64_t requirement_row;
     72 
     73   TDB_account (pg,
     74                10,
     75                &account);
     76   TDB_FILL (merchant_pub,
     77             1);
     78   TDB_FILL (wtid,
     79             1);
     80   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     81           TALER_EXCHANGEDB_get_aggregation_transient (pg,
     82                                                       &account.h_full,
     83                                                       &merchant_pub,
     84                                                       SECTION,
     85                                                       &wtid,
     86                                                       &total));
     87   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     88           TALER_EXCHANGEDB_get_aggregation_transient_by_wtid (
     89             pg,
     90             &account.h_full,
     91             &wtid,
     92             &total,
     93             &requirement_row));
     94   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     95           TALER_EXCHANGEDB_get_aggregation_transient_by_normalized_payto (
     96             pg,
     97             &account.h_normalized,
     98             &payto_uri,
     99             &wtid,
    100             &merchant_pub,
    101             &total));
    102   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    103           TALER_EXCHANGEDB_update_aggregation_transient (pg,
    104                                                          &account.h_full,
    105                                                          &wtid,
    106                                                          0,
    107                                                          &one));
    108   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    109           TALER_EXCHANGEDB_delete_aggregation_transient (pg,
    110                                                          &account.h_full,
    111                                                          &wtid));
    112   FAILIF (0 != TDB_count (pg,
    113                           "FROM aggregation_transient"));
    114   return 0;
    115 }
    116 
    117 
    118 /**
    119  * An inserted row is found by all three lookups.
    120  *
    121  * @param pg the database context
    122  * @return 0 on success
    123  */
    124 static int
    125 check_insert_and_lookup (struct TALER_EXCHANGEDB_PostgresContext *pg)
    126 {
    127   struct TALER_MerchantPublicKeyP merchant_pub;
    128   struct TALER_MerchantPublicKeyP got_merchant;
    129   struct TALER_WireTransferIdentifierRawP wtid;
    130   struct TALER_WireTransferIdentifierRawP got_wtid;
    131   struct TALER_Amount total = TDB_amount ("3");
    132   struct TALER_Amount got;
    133   struct TALER_FullPayto payto_uri = { NULL };
    134   uint64_t requirement_row = 42;
    135 
    136   TDB_FILL (merchant_pub,
    137             10);
    138   TDB_FILL (wtid,
    139             10);
    140   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    141           TALER_EXCHANGEDB_insert_aggregation_transient (pg,
    142                                                          &account.h_full,
    143                                                          SECTION,
    144                                                          &merchant_pub,
    145                                                          &wtid,
    146                                                          0,
    147                                                          &total));
    148   FAILIF (1 != TDB_count (pg,
    149                           "FROM aggregation_transient"));
    150 
    151   /* by (account, merchant, exchange account) */
    152   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    153           TALER_EXCHANGEDB_get_aggregation_transient (pg,
    154                                                       &account.h_full,
    155                                                       &merchant_pub,
    156                                                       SECTION,
    157                                                       &got_wtid,
    158                                                       &got));
    159   FAILIF (0 != GNUNET_memcmp (&got_wtid,
    160                               &wtid));
    161   FAILIF (0 != TALER_amount_cmp (&got,
    162                                  &total));
    163 
    164   /* by (account, wtid) */
    165   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    166           TALER_EXCHANGEDB_get_aggregation_transient_by_wtid (
    167             pg,
    168             &account.h_full,
    169             &wtid,
    170             &got,
    171             &requirement_row));
    172   FAILIF (0 != TALER_amount_cmp (&got,
    173                                  &total));
    174   /* no legitimization reason was recorded */
    175   FAILIF (0 != requirement_row);
    176 
    177   /* by normalized account */
    178   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    179           TALER_EXCHANGEDB_get_aggregation_transient_by_normalized_payto (
    180             pg,
    181             &account.h_normalized,
    182             &payto_uri,
    183             &got_wtid,
    184             &got_merchant,
    185             &got));
    186   FAILIF_C (0 != strcmp (payto_uri.full_payto,
    187                          account.payto.full_payto),
    188             GNUNET_free (payto_uri.full_payto));
    189   GNUNET_free (payto_uri.full_payto);
    190   FAILIF (0 != GNUNET_memcmp (&got_merchant,
    191                               &merchant_pub));
    192   FAILIF (0 != GNUNET_memcmp (&got_wtid,
    193                               &wtid));
    194 
    195   /* a different merchant on the same account has nothing */
    196   {
    197     struct TALER_MerchantPublicKeyP other;
    198 
    199     TDB_FILL (other,
    200               99);
    201     FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    202             TALER_EXCHANGEDB_get_aggregation_transient (pg,
    203                                                         &account.h_full,
    204                                                         &other,
    205                                                         SECTION,
    206                                                         &got_wtid,
    207                                                         &got));
    208   }
    209   /* and neither does a different exchange bank account */
    210   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    211           TALER_EXCHANGEDB_get_aggregation_transient (pg,
    212                                                       &account.h_full,
    213                                                       &merchant_pub,
    214                                                       "exchange-account-2",
    215                                                       &got_wtid,
    216                                                       &got));
    217   return 0;
    218 }
    219 
    220 
    221 /**
    222  * Updating a row changes the amount and the legitimization reason.
    223  *
    224  * @param pg the database context
    225  * @return 0 on success
    226  */
    227 static int
    228 check_update (struct TALER_EXCHANGEDB_PostgresContext *pg)
    229 {
    230   struct TALER_MerchantPublicKeyP merchant_pub;
    231   struct TALER_WireTransferIdentifierRawP wtid;
    232   struct TALER_WireTransferIdentifierRawP got_wtid;
    233   struct TALER_Amount total = TDB_amount ("7");
    234   struct TALER_Amount got;
    235   uint64_t requirement_row = 0;
    236 
    237   TDB_FILL (merchant_pub,
    238             10);
    239   TDB_FILL (wtid,
    240             10);
    241   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    242           TALER_EXCHANGEDB_update_aggregation_transient (pg,
    243                                                          &account.h_full,
    244                                                          &wtid,
    245                                                          5,
    246                                                          &total));
    247   FAILIF (1 != TDB_count (pg,
    248                           "FROM aggregation_transient"));
    249   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    250           TALER_EXCHANGEDB_get_aggregation_transient (pg,
    251                                                       &account.h_full,
    252                                                       &merchant_pub,
    253                                                       SECTION,
    254                                                       &got_wtid,
    255                                                       &got));
    256   FAILIF (0 != TALER_amount_cmp (&got,
    257                                  &total));
    258   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    259           TALER_EXCHANGEDB_get_aggregation_transient_by_wtid (
    260             pg,
    261             &account.h_full,
    262             &wtid,
    263             &got,
    264             &requirement_row));
    265   FAILIF (5 != requirement_row);
    266 
    267   /* a wire transfer identifier that is not on file is not created */
    268   {
    269     struct TALER_WireTransferIdentifierRawP other;
    270 
    271     TDB_FILL (other,
    272               98);
    273     FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    274             TALER_EXCHANGEDB_update_aggregation_transient (pg,
    275                                                            &account.h_full,
    276                                                            &other,
    277                                                            0,
    278                                                            &total));
    279     FAILIF (1 != TDB_count (pg,
    280                             "FROM aggregation_transient"));
    281   }
    282   return 0;
    283 }
    284 
    285 
    286 /**
    287  * Deleting a row removes it, and a second delete does nothing.
    288  *
    289  * @param pg the database context
    290  * @return 0 on success
    291  */
    292 static int
    293 check_delete (struct TALER_EXCHANGEDB_PostgresContext *pg)
    294 {
    295   struct TALER_MerchantPublicKeyP merchant_pub;
    296   struct TALER_WireTransferIdentifierRawP wtid;
    297   struct TALER_Amount got;
    298 
    299   TDB_FILL (merchant_pub,
    300             10);
    301   TDB_FILL (wtid,
    302             10);
    303   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    304           TALER_EXCHANGEDB_delete_aggregation_transient (pg,
    305                                                          &account.h_full,
    306                                                          &wtid));
    307   FAILIF (0 != TDB_count (pg,
    308                           "FROM aggregation_transient"));
    309   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    310           TALER_EXCHANGEDB_delete_aggregation_transient (pg,
    311                                                          &account.h_full,
    312                                                          &wtid));
    313   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    314           TALER_EXCHANGEDB_get_aggregation_transient (pg,
    315                                                       &account.h_full,
    316                                                       &merchant_pub,
    317                                                       SECTION,
    318                                                       &wtid,
    319                                                       &got));
    320   return 0;
    321 }
    322 
    323 
    324 /**
    325  * The checks to run, in order.
    326  */
    327 static const struct TDB_Test tests[] = {
    328   { "aggregation-transient-empty",
    329     &check_empty },
    330   { "aggregation-transient-insert-and-lookup",
    331     &check_insert_and_lookup },
    332   { "aggregation-transient-update",
    333     &check_update },
    334   { "aggregation-transient-delete",
    335     &check_delete },
    336   { NULL, NULL }
    337 };
    338 
    339 
    340 int
    341 main (int argc,
    342       char *const *argv)
    343 {
    344   int ret;
    345 
    346   ret = TDB_main (argc,
    347                   argv,
    348                   "test-aggregation-transient",
    349                   "Tests for the exchangedb `aggregation_transient' table",
    350                   tests);
    351   TDB_account_free (&account);
    352   return ret;
    353 }
    354 
    355 
    356 /* end of test_aggregation_transient.c */