LCOV - code coverage report
Current view: top level - exchangedb - test_wire_accounts.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 98.9 % 87 86
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_wire_accounts.c
      18              :  * @brief tests for the exchangedb functions whose primary table is
      19              :  *        `wire_accounts`
      20              :  * @author Christian Grothoff
      21              :  *
      22              :  * Covers #TALER_EXCHANGEDB_insert_wire(), #TALER_EXCHANGEDB_update_wire(),
      23              :  * #TALER_EXCHANGEDB_get_wire_timestamp() and
      24              :  * #TALER_EXCHANGEDB_iterate_wire_accounts().
      25              :  *
      26              :  * `wire_accounts` has no foreign keys -- it is the exchange's own list of
      27              :  * bank accounts, not the `wire_targets` list of everyone else's.  The
      28              :  * optional columns (conversion URL, gateways, bank label) are exercised
      29              :  * both set and NULL, since the iterator has to hand both back.
      30              :  */
      31              : #include "test_common.h"
      32              : #include "exchange-database/insert_wire.h"
      33              : #include "exchange-database/update_wire.h"
      34              : #include "exchange-database/get_wire_timestamp.h"
      35              : #include "exchange-database/iterate_wire_accounts.h"
      36              : 
      37              : 
      38              : /**
      39              :  * Closure for #account_cb().
      40              :  */
      41              : struct AccountContext
      42              : {
      43              :   /**
      44              :    * Account we are looking for.
      45              :    */
      46              :   const char *payto_uri;
      47              : 
      48              :   /**
      49              :    * How many accounts did the callback see in total?
      50              :    */
      51              :   unsigned int total;
      52              : 
      53              :   /**
      54              :    * How many times did we see @e payto_uri?
      55              :    */
      56              :   unsigned int matched;
      57              : 
      58              :   /**
      59              :    * Set if the account was reported with data we did not store.
      60              :    */
      61              :   bool mismatch;
      62              : 
      63              :   /**
      64              :    * Set if the account was reported with a conversion URL.
      65              :    */
      66              :   bool have_conversion_url;
      67              : 
      68              :   /**
      69              :    * Set if the account was reported with a bank label.
      70              :    */
      71              :   bool have_bank_label;
      72              : 
      73              :   /**
      74              :    * Priority reported for the account.
      75              :    */
      76              :   int64_t priority;
      77              : };
      78              : 
      79              : 
      80              : /**
      81              :  * Callback for #TALER_EXCHANGEDB_iterate_wire_accounts().
      82              :  *
      83              :  * @param cls a `struct AccountContext *`
      84              :  * @param payto_uri the account
      85              :  * @param conversion_url conversion service, NULL if none
      86              :  * @param open_banking_gateway open banking gateway, NULL if none
      87              :  * @param prepared_transfer_url transfer gateway, NULL if none
      88              :  * @param debit_restrictions debit restrictions on the account
      89              :  * @param credit_restrictions credit restrictions on the account
      90              :  * @param master_sig master signature over the account
      91              :  * @param bank_label label for the UI, NULL if none
      92              :  * @param priority ordering hint for the UI
      93              :  */
      94              : static void
      95            6 : account_cb (void *cls,
      96              :             const struct TALER_FullPayto payto_uri,
      97              :             const char *conversion_url,
      98              :             const char *open_banking_gateway,
      99              :             const char *prepared_transfer_url,
     100              :             const json_t *debit_restrictions,
     101              :             const json_t *credit_restrictions,
     102              :             const struct TALER_MasterSignatureP *master_sig,
     103              :             const char *bank_label,
     104              :             int64_t priority)
     105              : {
     106            6 :   struct AccountContext *ctx = cls;
     107              : 
     108              :   (void) open_banking_gateway;
     109              :   (void) prepared_transfer_url;
     110              :   (void) master_sig;
     111            6 :   ctx->total++;
     112            6 :   if (0 != strcmp (payto_uri.full_payto,
     113              :                    ctx->payto_uri))
     114            3 :     return;
     115            3 :   ctx->matched++;
     116            3 :   ctx->have_conversion_url = (NULL != conversion_url);
     117            3 :   ctx->have_bank_label = (NULL != bank_label);
     118            3 :   ctx->priority = priority;
     119            3 :   if ( (! json_is_array (debit_restrictions)) ||
     120            3 :        (! json_is_array (credit_restrictions)) )
     121            0 :     ctx->mismatch = true;
     122              : }
     123              : 
     124              : 
     125              : /**
     126              :  * Nothing is known about an account that was never inserted, and updating
     127              :  * it does not create it.
     128              :  *
     129              :  * @param pg the database context
     130              :  * @return 0 on success
     131              :  */
     132              : static int
     133            1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
     134              : {
     135            1 :   struct TALER_FullPayto payto = {
     136              :     .full_payto = (char *) "payto://x-taler-bank/localhost/wa-none"
     137              :   };
     138              :   struct GNUNET_TIME_Timestamp last_date;
     139              :   struct TALER_MasterSignatureP master_sig;
     140            1 :   json_t *empty = json_array ();
     141              : 
     142            1 :   TDB_FILL (master_sig,
     143              :             1);
     144            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     145              :             TALER_EXCHANGEDB_get_wire_timestamp (pg,
     146              :                                                  payto,
     147              :                                                  &last_date),
     148              :             json_decref (empty));
     149            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     150              :             TALER_EXCHANGEDB_update_wire (pg,
     151              :                                           payto,
     152              :                                           NULL,
     153              :                                           NULL,
     154              :                                           NULL,
     155              :                                           empty,
     156              :                                           empty,
     157              :                                           GNUNET_TIME_timestamp_get (),
     158              :                                           &master_sig,
     159              :                                           NULL,
     160              :                                           0,
     161              :                                           true),
     162              :             json_decref (empty));
     163            1 :   FAILIF_C (0 != TDB_count (pg,
     164              :                             "FROM wire_accounts"),
     165              :             json_decref (empty));
     166            1 :   json_decref (empty);
     167            1 :   return 0;
     168              : }
     169              : 
     170              : 
     171              : /**
     172              :  * Insert an account with all optional columns set and read it back.
     173              :  *
     174              :  * @param pg the database context
     175              :  * @return 0 on success
     176              :  */
     177              : static int
     178            1 : check_insert_full (struct TALER_EXCHANGEDB_PostgresContext *pg)
     179              : {
     180            1 :   const char *uri = "payto://x-taler-bank/localhost/wa-full";
     181            1 :   struct TALER_FullPayto payto = {
     182              :     .full_payto = (char *) uri
     183              :   };
     184            1 :   struct GNUNET_TIME_Timestamp start = GNUNET_TIME_timestamp_get ();
     185              :   struct GNUNET_TIME_Timestamp last_date;
     186              :   struct TALER_MasterSignatureP master_sig;
     187              :   struct AccountContext ctx;
     188              :   json_t *restrictions;
     189              : 
     190            1 :   TDB_FILL (master_sig,
     191              :             2);
     192            1 :   restrictions = json_pack ("[{s:s}]",
     193              :                             "type",
     194              :                             "deny");
     195            1 :   GNUNET_assert (NULL != restrictions);
     196            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     197              :             TALER_EXCHANGEDB_insert_wire (pg,
     198              :                                           payto,
     199              :                                           "https://conversion.example/",
     200              :                                           "https://obg.example/",
     201              :                                           "https://ptu.example/",
     202              :                                           restrictions,
     203              :                                           restrictions,
     204              :                                           start,
     205              :                                           &master_sig,
     206              :                                           "My Bank",
     207              :                                           7),
     208              :             json_decref (restrictions));
     209            1 :   json_decref (restrictions);
     210            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     211              :           TALER_EXCHANGEDB_get_wire_timestamp (pg,
     212              :                                                payto,
     213              :                                                &last_date));
     214            1 :   FAILIF (GNUNET_TIME_timestamp_cmp (last_date,
     215              :                                      !=,
     216              :                                      start));
     217            1 :   memset (&ctx,
     218              :           0,
     219              :           sizeof (ctx));
     220            1 :   ctx.payto_uri = uri;
     221            1 :   FAILIF (0 >=
     222              :           TALER_EXCHANGEDB_iterate_wire_accounts (pg,
     223              :                                                   &account_cb,
     224              :                                                   &ctx));
     225            1 :   FAILIF (1 != ctx.matched);
     226            1 :   FAILIF (ctx.mismatch);
     227            1 :   FAILIF (! ctx.have_conversion_url);
     228            1 :   FAILIF (! ctx.have_bank_label);
     229            1 :   FAILIF (7 != ctx.priority);
     230            1 :   return 0;
     231              : }
     232              : 
     233              : 
     234              : /**
     235              :  * Insert an account with every optional column left out; the iterator has
     236              :  * to hand the NULLs back as NULL.
     237              :  *
     238              :  * @param pg the database context
     239              :  * @return 0 on success
     240              :  */
     241              : static int
     242            1 : check_insert_minimal (struct TALER_EXCHANGEDB_PostgresContext *pg)
     243              : {
     244            1 :   const char *uri = "payto://x-taler-bank/localhost/wa-min";
     245            1 :   struct TALER_FullPayto payto = {
     246              :     .full_payto = (char *) uri
     247              :   };
     248              :   struct TALER_MasterSignatureP master_sig;
     249              :   struct AccountContext ctx;
     250            1 :   json_t *empty = json_array ();
     251              : 
     252            1 :   TDB_FILL (master_sig,
     253              :             3);
     254            1 :   GNUNET_assert (NULL != empty);
     255            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     256              :             TALER_EXCHANGEDB_insert_wire (pg,
     257              :                                           payto,
     258              :                                           NULL,
     259              :                                           NULL,
     260              :                                           NULL,
     261              :                                           empty,
     262              :                                           empty,
     263              :                                           GNUNET_TIME_timestamp_get (),
     264              :                                           &master_sig,
     265              :                                           NULL,
     266              :                                           -3),
     267              :             json_decref (empty));
     268            1 :   json_decref (empty);
     269            1 :   memset (&ctx,
     270              :           0,
     271              :           sizeof (ctx));
     272            1 :   ctx.payto_uri = uri;
     273            1 :   FAILIF (0 >=
     274              :           TALER_EXCHANGEDB_iterate_wire_accounts (pg,
     275              :                                                   &account_cb,
     276              :                                                   &ctx));
     277            1 :   FAILIF (1 != ctx.matched);
     278            1 :   FAILIF (ctx.mismatch);
     279            1 :   FAILIF (ctx.have_conversion_url);
     280            1 :   FAILIF (ctx.have_bank_label);
     281            1 :   FAILIF (-3 != ctx.priority);
     282            1 :   FAILIF (2 != ctx.total);
     283            1 :   return 0;
     284              : }
     285              : 
     286              : 
     287              : /**
     288              :  * Disabling an account takes it out of the iterator but leaves the row.
     289              :  *
     290              :  * @param pg the database context
     291              :  * @return 0 on success
     292              :  */
     293              : static int
     294            1 : check_disable (struct TALER_EXCHANGEDB_PostgresContext *pg)
     295              : {
     296            1 :   const char *uri = "payto://x-taler-bank/localhost/wa-min";
     297            1 :   struct TALER_FullPayto payto = {
     298              :     .full_payto = (char *) uri
     299              :   };
     300            1 :   struct GNUNET_TIME_Timestamp change = GNUNET_TIME_timestamp_get ();
     301              :   struct GNUNET_TIME_Timestamp last_date;
     302              :   struct AccountContext ctx;
     303              : 
     304              :   /* a disabled account may be stored without restrictions and signature */
     305            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     306              :           TALER_EXCHANGEDB_update_wire (pg,
     307              :                                         payto,
     308              :                                         NULL,
     309              :                                         NULL,
     310              :                                         NULL,
     311              :                                         NULL,
     312              :                                         NULL,
     313              :                                         change,
     314              :                                         NULL,
     315              :                                         NULL,
     316              :                                         0,
     317              :                                         false));
     318            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     319              :           TALER_EXCHANGEDB_get_wire_timestamp (pg,
     320              :                                                payto,
     321              :                                                &last_date));
     322            1 :   FAILIF (GNUNET_TIME_timestamp_cmp (last_date,
     323              :                                      !=,
     324              :                                      change));
     325            1 :   memset (&ctx,
     326              :           0,
     327              :           sizeof (ctx));
     328            1 :   ctx.payto_uri = uri;
     329            1 :   FAILIF (0 >=
     330              :           TALER_EXCHANGEDB_iterate_wire_accounts (pg,
     331              :                                                   &account_cb,
     332              :                                                   &ctx));
     333            1 :   FAILIF (0 != ctx.matched);
     334            1 :   FAILIF (1 != ctx.total);
     335            1 :   FAILIF (2 != TDB_count (pg,
     336              :                           "FROM wire_accounts"));
     337              : 
     338              :   /* re-enabling brings it back, with the new details */
     339              :   {
     340              :     struct TALER_MasterSignatureP master_sig;
     341            1 :     json_t *empty = json_array ();
     342              : 
     343            1 :     TDB_FILL (master_sig,
     344              :               4);
     345            1 :     GNUNET_assert (NULL != empty);
     346            1 :     FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     347              :               TALER_EXCHANGEDB_update_wire (pg,
     348              :                                             payto,
     349              :                                             "https://conv2.example/",
     350              :                                             NULL,
     351              :                                             NULL,
     352              :                                             empty,
     353              :                                             empty,
     354              :                                             change,
     355              :                                             &master_sig,
     356              :                                             "Relabelled",
     357              :                                             11,
     358              :                                             true),
     359              :               json_decref (empty));
     360            1 :     json_decref (empty);
     361              :   }
     362            1 :   memset (&ctx,
     363              :           0,
     364              :           sizeof (ctx));
     365            1 :   ctx.payto_uri = uri;
     366            1 :   FAILIF (0 >=
     367              :           TALER_EXCHANGEDB_iterate_wire_accounts (pg,
     368              :                                                   &account_cb,
     369              :                                                   &ctx));
     370            1 :   FAILIF (1 != ctx.matched);
     371            1 :   FAILIF (! ctx.have_conversion_url);
     372            1 :   FAILIF (! ctx.have_bank_label);
     373            1 :   FAILIF (11 != ctx.priority);
     374            1 :   return 0;
     375              : }
     376              : 
     377              : 
     378              : /**
     379              :  * The checks to run, in order.
     380              :  */
     381              : static const struct TDB_Test tests[] = {
     382              :   { "wire-accounts-empty",
     383              :     &check_empty },
     384              :   { "wire-accounts-insert-full",
     385              :     &check_insert_full },
     386              :   { "wire-accounts-insert-minimal",
     387              :     &check_insert_minimal },
     388              :   { "wire-accounts-disable",
     389              :     &check_disable },
     390              :   { NULL, NULL }
     391              : };
     392              : 
     393              : 
     394              : int
     395            1 : main (int argc,
     396              :       char *const *argv)
     397              : {
     398            1 :   return TDB_main (argc,
     399              :                    argv,
     400              :                    "test-wire-accounts",
     401              :                    "Tests for the exchangedb `wire_accounts' table",
     402              :                    tests);
     403              : }
     404              : 
     405              : 
     406              : /* end of test_wire_accounts.c */
        

Generated by: LCOV version 2.0-1