LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_post_transfers.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 100 130 76.9 %
Date: 2025-06-23 16:22:09 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2020, 2023, 2024 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify
       6             :   it under the terms of the GNU General Public License as
       7             :   published by the Free Software Foundation; either version 3, or
       8             :   (at your option) any later version.
       9             : 
      10             :   TALER is distributed in the hope that it will be useful, but
      11             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :   GNU General Public License for more details.
      14             : 
      15             :   You should have received a copy of the GNU General Public
      16             :   License along with TALER; see the file COPYING.  If not, see
      17             :   <http://www.gnu.org/licenses/>
      18             : */
      19             : /**
      20             :  * @file testing_api_cmd_post_transfers.c
      21             :  * @brief command to test POST /transfers
      22             :  * @author Christian Grothoff
      23             :  */
      24             : #include "platform.h"
      25             : #include <taler/taler_exchange_service.h>
      26             : #include <taler/taler_testing_lib.h>
      27             : #include "taler_merchant_service.h"
      28             : #include "taler_merchant_testing_lib.h"
      29             : 
      30             : 
      31             : /**
      32             :  * State of a "POST /transfers" CMD.
      33             :  */
      34             : struct PostTransfersState
      35             : {
      36             : 
      37             :   /**
      38             :    * Handle for a "POST /transfers" request.
      39             :    */
      40             :   struct TALER_MERCHANT_PostTransfersHandle *pth;
      41             : 
      42             :   /**
      43             :    * Handle for a "GET" bank account history request.
      44             :    */
      45             :   struct TALER_BANK_DebitHistoryHandle *dhh;
      46             : 
      47             :   /**
      48             :    * The interpreter state.
      49             :    */
      50             :   struct TALER_TESTING_Interpreter *is;
      51             : 
      52             :   /**
      53             :    * Base URL of the merchant serving the request.
      54             :    */
      55             :   const char *merchant_url;
      56             : 
      57             :   /**
      58             :    * URL of the bank to run history on.
      59             :    */
      60             :   char *exchange_url;
      61             : 
      62             :   /**
      63             :    * Credit account of the merchant.
      64             :    */
      65             :   struct TALER_FullPayto credit_account;
      66             : 
      67             :   /**
      68             :    * Payto URI to filter on.
      69             :    */
      70             :   struct TALER_FullPayto payto_uri;
      71             : 
      72             :   /**
      73             :    * Set to the hash of the @e payto_uri.
      74             :    */
      75             :   struct TALER_FullPaytoHashP h_payto;
      76             : 
      77             :   /**
      78             :    * Set to the hash of the normalized @e payto_uri.
      79             :    */
      80             :   struct TALER_NormalizedPaytoHashP h_normalized_payto;
      81             : 
      82             :   /**
      83             :    * Authentication details to authenticate to the bank.
      84             :    */
      85             :   struct TALER_BANK_AuthenticationData auth;
      86             : 
      87             :   /**
      88             :    * Set once we discovered the WTID.
      89             :    */
      90             :   struct TALER_WireTransferIdentifierRawP wtid;
      91             : 
      92             :   /**
      93             :    * the credit amount to look for at @e bank_url.
      94             :    */
      95             :   struct TALER_Amount credit_amount;
      96             : 
      97             :   /**
      98             :    * Expected HTTP response code.
      99             :    */
     100             :   unsigned int http_status;
     101             : 
     102             :   /**
     103             :    * Array of deposit command labels we expect to see aggregated.
     104             :    */
     105             :   const char **deposits;
     106             : 
     107             :   /**
     108             :    * Serial number of the wire transfer in the merchant backend,
     109             :    * set by #TALER_TESTING_cmd_merchant_get_transfers(). 0 if unknown.
     110             :    */
     111             :   uint64_t serial;
     112             : 
     113             :   /**
     114             :    * Length of @e deposits.
     115             :    */
     116             :   unsigned int deposits_length;
     117             : 
     118             : };
     119             : 
     120             : 
     121             : /**
     122             :  * Callback for a POST /transfers operation.
     123             :  *
     124             :  * @param cls closure for this function
     125             :  * @param ptr response details
     126             :  */
     127             : static void
     128           6 : transfers_cb (void *cls,
     129             :               const struct TALER_MERCHANT_PostTransfersResponse *ptr)
     130             : {
     131           6 :   struct PostTransfersState *pts = cls;
     132             : 
     133           6 :   pts->pth = NULL;
     134           6 :   if (pts->http_status != ptr->hr.http_status)
     135             :   {
     136           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     137             :                 "Unexpected response code %u (%d) to command %s\n",
     138             :                 ptr->hr.http_status,
     139             :                 (int) ptr->hr.ec,
     140             :                 TALER_TESTING_interpreter_get_current_label (pts->is));
     141           0 :     GNUNET_break (0);
     142           0 :     TALER_TESTING_interpreter_fail (pts->is);
     143           0 :     return;
     144             :   }
     145           6 :   switch (ptr->hr.http_status)
     146             :   {
     147           6 :   case MHD_HTTP_NO_CONTENT:
     148           6 :     break;
     149           0 :   case MHD_HTTP_UNAUTHORIZED:
     150           0 :     break;
     151           0 :   case MHD_HTTP_NOT_FOUND:
     152           0 :     break;
     153           0 :   case MHD_HTTP_GATEWAY_TIMEOUT:
     154           0 :     break;
     155           0 :   default:
     156           0 :     GNUNET_break (0);
     157           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     158             :                 "Unhandled HTTP status %u for POST /transfers.\n",
     159             :                 ptr->hr.http_status);
     160             :   }
     161           6 :   TALER_TESTING_interpreter_next (pts->is);
     162             : }
     163             : 
     164             : 
     165             : /**
     166             :  * Offers information from the POST /transfers CMD state to other
     167             :  * commands.
     168             :  *
     169             :  * @param cls closure
     170             :  * @param[out] ret result (could be anything)
     171             :  * @param trait name of the trait
     172             :  * @param index index number of the object to extract.
     173             :  * @return #GNUNET_OK on success
     174             :  */
     175             : static enum GNUNET_GenericReturnValue
     176          48 : post_transfers_traits (void *cls,
     177             :                        const void **ret,
     178             :                        const char *trait,
     179             :                        unsigned int index)
     180             : {
     181          48 :   struct PostTransfersState *pts = cls;
     182             :   struct TALER_TESTING_Trait traits[] = {
     183          48 :     TALER_TESTING_make_trait_wtid (&pts->wtid),
     184          48 :     TALER_TESTING_make_trait_credit_payto_uri (&pts->credit_account),
     185          48 :     TALER_TESTING_make_trait_h_full_payto (&pts->h_payto),
     186          48 :     TALER_TESTING_make_trait_h_normalized_payto (&pts->h_normalized_payto),
     187          48 :     TALER_TESTING_make_trait_amount (&pts->credit_amount),
     188          48 :     TALER_TESTING_make_trait_exchange_url (pts->exchange_url),
     189          48 :     TALER_TESTING_make_trait_bank_row (&pts->serial),
     190          48 :     TALER_TESTING_trait_end (),
     191             :   };
     192             : 
     193          48 :   return TALER_TESTING_get_trait (traits,
     194             :                                   ret,
     195             :                                   trait,
     196             :                                   index);
     197             : }
     198             : 
     199             : 
     200             : /**
     201             :  * Run the "POST /transfers" CMD. First, get the bank history to find
     202             :  * the wtid.
     203             :  *
     204             :  * @param cls closure.
     205             :  * @param cmd command being run now.
     206             :  * @param is interpreter state.
     207             :  */
     208             : static void
     209           2 : post_transfers_run2 (void *cls,
     210             :                      const struct TALER_TESTING_Command *cmd,
     211             :                      struct TALER_TESTING_Interpreter *is)
     212             : {
     213           2 :   struct PostTransfersState *pts = cls;
     214             : 
     215           2 :   pts->is = is;
     216           2 :   pts->pth = TALER_MERCHANT_transfers_post (
     217             :     TALER_TESTING_interpreter_get_context (pts->is),
     218             :     pts->merchant_url,
     219           2 :     &pts->credit_amount,
     220           2 :     &pts->wtid,
     221             :     pts->credit_account,
     222           2 :     pts->exchange_url,
     223             :     &transfers_cb,
     224             :     pts);
     225           2 :   GNUNET_assert (NULL != pts->pth);
     226           2 : }
     227             : 
     228             : 
     229             : /**
     230             :  * Callbacks of this type are used to serve the result of asking
     231             :  * the bank for the debit transaction history.
     232             :  *
     233             :  * @param cls closure with a `struct PostTransfersState *`
     234             :  * @param reply details from the HTTP response code
     235             :  */
     236             : static void
     237           4 : debit_cb (
     238             :   void *cls,
     239             :   const struct TALER_BANK_DebitHistoryResponse *reply)
     240             : {
     241           4 :   struct PostTransfersState *pts = cls;
     242             : 
     243           4 :   pts->dhh = NULL;
     244           4 :   switch (reply->http_status)
     245             :   {
     246           4 :   case MHD_HTTP_OK:
     247             :     /* handled below */
     248           4 :     break;
     249           0 :   case MHD_HTTP_NO_CONTENT:
     250           0 :     GNUNET_break (0);
     251           0 :     TALER_TESTING_interpreter_fail (pts->is);
     252           0 :     return;
     253           0 :   default:
     254           0 :     GNUNET_break (0);
     255           0 :     TALER_TESTING_interpreter_fail (pts->is);
     256           0 :     return;
     257             :   }
     258           4 :   for (unsigned int i = 0; i<reply->details.ok.details_length; i++)
     259             :   {
     260           4 :     const struct TALER_BANK_DebitDetails *details
     261           4 :       = &reply->details.ok.details[i];
     262             : 
     263           4 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     264             :                 "Bank reports transfer of %s to %s\n",
     265             :                 TALER_amount2s (&details->amount),
     266             :                 details->credit_account_uri.full_payto);
     267           4 :     if (0 != TALER_amount_cmp (&pts->credit_amount,
     268             :                                &details->amount))
     269           0 :       continue;
     270           4 :     pts->wtid = details->wtid;
     271             :     pts->credit_account.full_payto
     272           4 :       = GNUNET_strdup (details->credit_account_uri.full_payto);
     273           4 :     pts->exchange_url = GNUNET_strdup (details->exchange_base_url);
     274           4 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     275             :                 "Bank transfer found, checking with merchant backend at %s about %s from %s to %s with %s\n",
     276             :                 pts->merchant_url,
     277             :                 TALER_amount2s (&pts->credit_amount),
     278             :                 pts->payto_uri.full_payto,
     279             :                 pts->exchange_url,
     280             :                 TALER_B2S (&pts->wtid));
     281           4 :     pts->pth = TALER_MERCHANT_transfers_post (
     282             :       TALER_TESTING_interpreter_get_context (pts->is),
     283             :       pts->merchant_url,
     284           4 :       &pts->credit_amount,
     285           4 :       &pts->wtid,
     286             :       pts->credit_account,
     287           4 :       pts->exchange_url,
     288             :       &transfers_cb,
     289             :       pts);
     290           4 :     GNUNET_assert (NULL != pts->pth);
     291           4 :     break;
     292             :   }
     293           4 :   if (NULL == pts->pth)
     294             :   {
     295           0 :     GNUNET_break (0);
     296           0 :     TALER_TESTING_interpreter_fail (pts->is);
     297           0 :     return;
     298             :   }
     299             : }
     300             : 
     301             : 
     302             : /**
     303             :  * Run the "POST /transfers" CMD. First, get the bank history to find
     304             :  * the wtid.
     305             :  *
     306             :  * @param cls closure.
     307             :  * @param cmd command being run now.
     308             :  * @param is interpreter state.
     309             :  */
     310             : static void
     311           4 : post_transfers_run (void *cls,
     312             :                     const struct TALER_TESTING_Command *cmd,
     313             :                     struct TALER_TESTING_Interpreter *is)
     314             : {
     315           4 :   struct PostTransfersState *pts = cls;
     316             : 
     317           4 :   pts->is = is;
     318           4 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     319             :               "Looking for transfer of %s from %s at bank\n",
     320             :               TALER_amount2s (&pts->credit_amount),
     321             :               pts->payto_uri.full_payto);
     322           4 :   pts->dhh = TALER_BANK_debit_history (TALER_TESTING_interpreter_get_context (
     323             :                                          is),
     324           4 :                                        &pts->auth,
     325             :                                        UINT64_MAX,
     326             :                                        -INT64_MAX,
     327           4 :                                        GNUNET_TIME_UNIT_ZERO,
     328             :                                        &debit_cb,
     329             :                                        pts);
     330           4 :   GNUNET_assert (NULL != pts->dhh);
     331           4 : }
     332             : 
     333             : 
     334             : /**
     335             :  * Free the state of a "POST product" CMD, and possibly
     336             :  * cancel a pending operation thereof.
     337             :  *
     338             :  * @param cls closure.
     339             :  * @param cmd command being run.
     340             :  */
     341             : static void
     342           6 : post_transfers_cleanup (void *cls,
     343             :                         const struct TALER_TESTING_Command *cmd)
     344             : {
     345           6 :   struct PostTransfersState *pts = cls;
     346             : 
     347           6 :   if (NULL != pts->pth)
     348             :   {
     349           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     350             :                 "POST /transfers operation did not complete\n");
     351           0 :     TALER_MERCHANT_transfers_post_cancel (pts->pth);
     352             :   }
     353           6 :   if (NULL != pts->dhh)
     354             :   {
     355           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     356             :                 "GET debit history operation did not complete\n");
     357           0 :     TALER_BANK_debit_history_cancel (pts->dhh);
     358             :   }
     359           6 :   GNUNET_array_grow (pts->deposits,
     360             :                      pts->deposits_length,
     361             :                      0);
     362           6 :   GNUNET_free (pts->exchange_url);
     363           6 :   GNUNET_free (pts->credit_account.full_payto);
     364           6 :   GNUNET_free (pts);
     365           6 : }
     366             : 
     367             : 
     368             : struct TALER_TESTING_Command
     369           4 : TALER_TESTING_cmd_merchant_post_transfer (
     370             :   const char *label,
     371             :   const struct TALER_BANK_AuthenticationData *auth,
     372             :   struct TALER_FullPayto payto_uri,
     373             :   const char *merchant_url,
     374             :   const char *credit_amount,
     375             :   unsigned int http_code,
     376             :   ...)
     377             : {
     378             :   struct PostTransfersState *pts;
     379             : 
     380           4 :   pts = GNUNET_new (struct PostTransfersState);
     381           4 :   pts->merchant_url = merchant_url;
     382           4 :   pts->auth = *auth;
     383           4 :   pts->payto_uri = payto_uri;
     384           4 :   TALER_full_payto_hash (payto_uri,
     385             :                          &pts->h_payto);
     386           4 :   TALER_full_payto_normalize_and_hash (payto_uri,
     387             :                                        &pts->h_normalized_payto);
     388           4 :   GNUNET_assert (GNUNET_OK ==
     389             :                  TALER_string_to_amount (credit_amount,
     390             :                                          &pts->credit_amount));
     391           4 :   pts->http_status = http_code;
     392             :   {
     393             :     const char *clabel;
     394             :     va_list ap;
     395             : 
     396           4 :     va_start (ap, http_code);
     397           8 :     while (NULL != (clabel = va_arg (ap, const char *)))
     398             :     {
     399           4 :       GNUNET_array_append (pts->deposits,
     400             :                            pts->deposits_length,
     401             :                            clabel);
     402             :     }
     403           4 :     va_end (ap);
     404             :   }
     405             :   {
     406           4 :     struct TALER_TESTING_Command cmd = {
     407             :       .cls = pts,
     408             :       .label = label,
     409             :       .run = &post_transfers_run,
     410             :       .cleanup = &post_transfers_cleanup,
     411             :       .traits = &post_transfers_traits
     412             :     };
     413             : 
     414           4 :     return cmd;
     415             :   }
     416             : }
     417             : 
     418             : 
     419             : struct TALER_TESTING_Command
     420           2 : TALER_TESTING_cmd_merchant_post_transfer2 (
     421             :   const char *label,
     422             :   const char *merchant_url,
     423             :   struct TALER_FullPayto payto_uri,
     424             :   const char *credit_amount,
     425             :   const char *wtid,
     426             :   const char *exchange_url,
     427             :   unsigned int http_code)
     428             : {
     429             :   struct PostTransfersState *pts;
     430             : 
     431           2 :   pts = GNUNET_new (struct PostTransfersState);
     432           2 :   pts->merchant_url = merchant_url;
     433             :   pts->credit_account.full_payto
     434           2 :     = GNUNET_strdup (payto_uri.full_payto);
     435           2 :   pts->exchange_url = GNUNET_strdup (exchange_url);
     436           2 :   GNUNET_assert (GNUNET_OK ==
     437             :                  TALER_string_to_amount (credit_amount,
     438             :                                          &pts->credit_amount));
     439           2 :   if (NULL == wtid)
     440             :   {
     441           2 :     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
     442           2 :                                 &pts->wtid,
     443             :                                 sizeof (pts->wtid));
     444             :   }
     445             :   else
     446             :   {
     447           0 :     GNUNET_assert (GNUNET_OK ==
     448             :                    GNUNET_STRINGS_string_to_data (wtid,
     449             :                                                   strlen (wtid),
     450             :                                                   &pts->wtid,
     451             :                                                   sizeof (pts->wtid)));
     452             :   }
     453           2 :   pts->http_status = http_code;
     454             :   {
     455           2 :     struct TALER_TESTING_Command cmd = {
     456             :       .cls = pts,
     457             :       .label = label,
     458             :       .run = &post_transfers_run2,
     459             :       .cleanup = &post_transfers_cleanup,
     460             :       .traits = &post_transfers_traits
     461             :     };
     462             : 
     463           2 :     return cmd;
     464             :   }
     465             : }
     466             : 
     467             : 
     468             : void
     469           7 : TALER_TESTING_cmd_merchant_post_transfer_set_serial (
     470             :   struct TALER_TESTING_Command *cmd,
     471             :   uint64_t serial)
     472             : {
     473           7 :   struct PostTransfersState *pts = cmd->cls;
     474             : 
     475           7 :   GNUNET_assert (cmd->run = &post_transfers_run);
     476           7 :   pts->serial = serial;
     477           7 : }
     478             : 
     479             : 
     480             : /* end of testing_api_cmd_post_transfers.c */

Generated by: LCOV version 1.16