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 (13796B)


      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                                                       &requirement_row,
     87                                                       &total));
     88   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     89           TALER_EXCHANGEDB_get_aggregation_transient_by_wtid (
     90             pg,
     91             &account.h_full,
     92             &wtid,
     93             &total,
     94             &requirement_row));
     95   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     96           TALER_EXCHANGEDB_get_aggregation_transient_by_normalized_payto (
     97             pg,
     98             &account.h_normalized,
     99             &payto_uri,
    100             &wtid,
    101             &merchant_pub,
    102             &total));
    103   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    104           TALER_EXCHANGEDB_update_aggregation_transient (pg,
    105                                                          &account.h_full,
    106                                                          &wtid,
    107                                                          0,
    108                                                          &one));
    109   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    110           TALER_EXCHANGEDB_delete_aggregation_transient (pg,
    111                                                          &account.h_full,
    112                                                          &wtid));
    113   FAILIF (0 != TDB_count (pg,
    114                           "FROM aggregation_transient"));
    115   return 0;
    116 }
    117 
    118 
    119 /**
    120  * An inserted row is found by all three lookups.
    121  *
    122  * @param pg the database context
    123  * @return 0 on success
    124  */
    125 static int
    126 check_insert_and_lookup (struct TALER_EXCHANGEDB_PostgresContext *pg)
    127 {
    128   struct TALER_MerchantPublicKeyP merchant_pub;
    129   struct TALER_MerchantPublicKeyP got_merchant;
    130   struct TALER_WireTransferIdentifierRawP wtid;
    131   struct TALER_WireTransferIdentifierRawP got_wtid;
    132   struct TALER_Amount total = TDB_amount ("3");
    133   struct TALER_Amount got;
    134   struct TALER_FullPayto payto_uri = { NULL };
    135   uint64_t requirement_row = 42;
    136 
    137   TDB_FILL (merchant_pub,
    138             10);
    139   TDB_FILL (wtid,
    140             10);
    141   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    142           TALER_EXCHANGEDB_insert_aggregation_transient (pg,
    143                                                          &account.h_full,
    144                                                          SECTION,
    145                                                          &merchant_pub,
    146                                                          &wtid,
    147                                                          0,
    148                                                          &total));
    149   FAILIF (1 != TDB_count (pg,
    150                           "FROM aggregation_transient"));
    151 
    152   /* by (account, merchant, exchange account) */
    153   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    154           TALER_EXCHANGEDB_get_aggregation_transient (pg,
    155                                                       &account.h_full,
    156                                                       &merchant_pub,
    157                                                       SECTION,
    158                                                       &got_wtid,
    159                                                       &requirement_row,
    160                                                       &got));
    161   FAILIF (0 != GNUNET_memcmp (&got_wtid,
    162                               &wtid));
    163   FAILIF (0 != TALER_amount_cmp (&got,
    164                                  &total));
    165   /* no legitimization reason was recorded */
    166   FAILIF (0 != requirement_row);
    167 
    168   /* by (account, wtid) */
    169   requirement_row = 42;
    170   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    171           TALER_EXCHANGEDB_get_aggregation_transient_by_wtid (
    172             pg,
    173             &account.h_full,
    174             &wtid,
    175             &got,
    176             &requirement_row));
    177   FAILIF (0 != TALER_amount_cmp (&got,
    178                                  &total));
    179   /* no legitimization reason was recorded */
    180   FAILIF (0 != requirement_row);
    181 
    182   /* by normalized account */
    183   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    184           TALER_EXCHANGEDB_get_aggregation_transient_by_normalized_payto (
    185             pg,
    186             &account.h_normalized,
    187             &payto_uri,
    188             &got_wtid,
    189             &got_merchant,
    190             &got));
    191   FAILIF_C (0 != strcmp (payto_uri.full_payto,
    192                          account.payto.full_payto),
    193             GNUNET_free (payto_uri.full_payto));
    194   GNUNET_free (payto_uri.full_payto);
    195   FAILIF (0 != GNUNET_memcmp (&got_merchant,
    196                               &merchant_pub));
    197   FAILIF (0 != GNUNET_memcmp (&got_wtid,
    198                               &wtid));
    199 
    200   /* a different merchant on the same account has nothing */
    201   {
    202     struct TALER_MerchantPublicKeyP other;
    203 
    204     TDB_FILL (other,
    205               99);
    206     FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    207             TALER_EXCHANGEDB_get_aggregation_transient (pg,
    208                                                         &account.h_full,
    209                                                         &other,
    210                                                         SECTION,
    211                                                         &got_wtid,
    212                                                         &requirement_row,
    213                                                         &got));
    214   }
    215   /* and neither does a different exchange bank account */
    216   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    217           TALER_EXCHANGEDB_get_aggregation_transient (pg,
    218                                                       &account.h_full,
    219                                                       &merchant_pub,
    220                                                       "exchange-account-2",
    221                                                       &got_wtid,
    222                                                       &requirement_row,
    223                                                       &got));
    224   return 0;
    225 }
    226 
    227 
    228 /**
    229  * Updating a row changes the amount and the legitimization reason.
    230  *
    231  * @param pg the database context
    232  * @return 0 on success
    233  */
    234 static int
    235 check_update (struct TALER_EXCHANGEDB_PostgresContext *pg)
    236 {
    237   struct TALER_MerchantPublicKeyP merchant_pub;
    238   struct TALER_WireTransferIdentifierRawP wtid;
    239   struct TALER_WireTransferIdentifierRawP got_wtid;
    240   struct TALER_Amount total = TDB_amount ("7");
    241   struct TALER_Amount got;
    242   uint64_t requirement_row = 0;
    243 
    244   TDB_FILL (merchant_pub,
    245             10);
    246   TDB_FILL (wtid,
    247             10);
    248   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    249           TALER_EXCHANGEDB_update_aggregation_transient (pg,
    250                                                          &account.h_full,
    251                                                          &wtid,
    252                                                          5,
    253                                                          &total));
    254   FAILIF (1 != TDB_count (pg,
    255                           "FROM aggregation_transient"));
    256   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    257           TALER_EXCHANGEDB_get_aggregation_transient (pg,
    258                                                       &account.h_full,
    259                                                       &merchant_pub,
    260                                                       SECTION,
    261                                                       &got_wtid,
    262                                                       &requirement_row,
    263                                                       &got));
    264   FAILIF (0 != TALER_amount_cmp (&got,
    265                                  &total));
    266   /* the legitimization reason is read back, so re-parking keeps it */
    267   FAILIF (5 != requirement_row);
    268   requirement_row = 0;
    269   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    270           TALER_EXCHANGEDB_get_aggregation_transient_by_wtid (
    271             pg,
    272             &account.h_full,
    273             &wtid,
    274             &got,
    275             &requirement_row));
    276   FAILIF (5 != requirement_row);
    277 
    278   /* a wire transfer identifier that is not on file is not created */
    279   {
    280     struct TALER_WireTransferIdentifierRawP other;
    281 
    282     TDB_FILL (other,
    283               98);
    284     FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    285             TALER_EXCHANGEDB_update_aggregation_transient (pg,
    286                                                            &account.h_full,
    287                                                            &other,
    288                                                            0,
    289                                                            &total));
    290     FAILIF (1 != TDB_count (pg,
    291                             "FROM aggregation_transient"));
    292   }
    293   return 0;
    294 }
    295 
    296 
    297 /**
    298  * Deleting a row removes it, and a second delete does nothing.
    299  *
    300  * @param pg the database context
    301  * @return 0 on success
    302  */
    303 static int
    304 check_delete (struct TALER_EXCHANGEDB_PostgresContext *pg)
    305 {
    306   struct TALER_MerchantPublicKeyP merchant_pub;
    307   struct TALER_WireTransferIdentifierRawP wtid;
    308   struct TALER_Amount got;
    309   uint64_t requirement_row;
    310 
    311   TDB_FILL (merchant_pub,
    312             10);
    313   TDB_FILL (wtid,
    314             10);
    315   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    316           TALER_EXCHANGEDB_delete_aggregation_transient (pg,
    317                                                          &account.h_full,
    318                                                          &wtid));
    319   FAILIF (0 != TDB_count (pg,
    320                           "FROM aggregation_transient"));
    321   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    322           TALER_EXCHANGEDB_delete_aggregation_transient (pg,
    323                                                          &account.h_full,
    324                                                          &wtid));
    325   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    326           TALER_EXCHANGEDB_get_aggregation_transient (pg,
    327                                                       &account.h_full,
    328                                                       &merchant_pub,
    329                                                       SECTION,
    330                                                       &wtid,
    331                                                       &requirement_row,
    332                                                       &got));
    333   return 0;
    334 }
    335 
    336 
    337 /**
    338  * The checks to run, in order.
    339  */
    340 static const struct TDB_Test tests[] = {
    341   { "aggregation-transient-empty",
    342     &check_empty },
    343   { "aggregation-transient-insert-and-lookup",
    344     &check_insert_and_lookup },
    345   { "aggregation-transient-update",
    346     &check_update },
    347   { "aggregation-transient-delete",
    348     &check_delete },
    349   { NULL, NULL }
    350 };
    351 
    352 
    353 int
    354 main (int argc,
    355       char *const *argv)
    356 {
    357   int ret;
    358 
    359   ret = TDB_main (argc,
    360                   argv,
    361                   "test-aggregation-transient",
    362                   "Tests for the exchangedb `aggregation_transient' table",
    363                   tests);
    364   TDB_account_free (&account);
    365   return ret;
    366 }
    367 
    368 
    369 /* end of test_aggregation_transient.c */