LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_bank_history_debit.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 106 161 65.8 %
Date: 2021-08-30 06:43:37 Functions: 7 8 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2018-2020 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/testing_api_cmd_bank_history_debit.c
      21             :  * @brief command to check the /history/outgoing API from the bank.
      22             :  * @author Marcello Stanisci
      23             :  */
      24             : #include "platform.h"
      25             : #include "taler_json_lib.h"
      26             : #include <gnunet/gnunet_curl_lib.h>
      27             : #include "taler_exchange_service.h"
      28             : #include "taler_testing_lib.h"
      29             : #include "taler_fakebank_lib.h"
      30             : #include "taler_bank_service.h"
      31             : #include "taler_fakebank_lib.h"
      32             : 
      33             : /**
      34             :  * Item in the transaction history, as reconstructed from the
      35             :  * command history.
      36             :  */
      37             : struct History
      38             : {
      39             : 
      40             :   /**
      41             :    * Wire details.
      42             :    */
      43             :   struct TALER_BANK_DebitDetails details;
      44             : 
      45             :   /**
      46             :    * Serial ID of the wire transfer.
      47             :    */
      48             :   uint64_t row_id;
      49             : 
      50             :   /**
      51             :    * URL to free.
      52             :    */
      53             :   char *c_url;
      54             : 
      55             :   /**
      56             :    * URL to free.
      57             :    */
      58             :   char *d_url;
      59             : };
      60             : 
      61             : 
      62             : /**
      63             :  * State for a "history" CMD.
      64             :  */
      65             : struct HistoryState
      66             : {
      67             :   /**
      68             :    * Base URL of the account offering the "history" operation.
      69             :    */
      70             :   const char *account_url;
      71             : 
      72             :   /**
      73             :    * Reference to command defining the
      74             :    * first row number we want in the result.
      75             :    */
      76             :   const char *start_row_reference;
      77             : 
      78             :   /**
      79             :    * How many rows we want in the result, _at most_,
      80             :    * and ascending/descending.
      81             :    */
      82             :   long long num_results;
      83             : 
      84             :   /**
      85             :    * Login data to use to authenticate.
      86             :    */
      87             :   struct TALER_BANK_AuthenticationData auth;
      88             : 
      89             :   /**
      90             :    * Handle to a pending "history" operation.
      91             :    */
      92             :   struct TALER_BANK_DebitHistoryHandle *hh;
      93             : 
      94             :   /**
      95             :    * Expected number of results (= rows).
      96             :    */
      97             :   uint64_t results_obtained;
      98             : 
      99             :   /**
     100             :    * Set to #GNUNET_YES if the callback detects something
     101             :    * unexpected.
     102             :    */
     103             :   int failed;
     104             : 
     105             :   /**
     106             :    * Expected history.
     107             :    */
     108             :   struct History *h;
     109             : 
     110             :   /**
     111             :    * Length of @e h
     112             :    */
     113             :   unsigned int total;
     114             : 
     115             : };
     116             : 
     117             : 
     118             : /**
     119             :  * Offer internal data to other commands.
     120             :  *
     121             :  * @param cls closure.
     122             :  * @param[out] ret set to the wanted data.
     123             :  * @param trait name of the trait.
     124             :  * @param index index number of the traits to be returned.
     125             :  *
     126             :  * @return #GNUNET_OK on success
     127             :  */
     128             : static int
     129           2 : history_traits (void *cls,
     130             :                 const void **ret,
     131             :                 const char *trait,
     132             :                 unsigned int index)
     133             : {
     134             :   (void) cls;
     135             :   (void) ret;
     136             :   (void) trait;
     137             :   (void) index;
     138             :   /* Must define this function because some callbacks
     139             :    * look for certain traits on _all_ the commands. */
     140           2 :   return GNUNET_SYSERR;
     141             : }
     142             : 
     143             : 
     144             : /**
     145             :  * Log which history we expected.  Called when an error occurs.
     146             :  *
     147             :  * @param h what we expected.
     148             :  * @param h_len number of entries in @a h.
     149             :  * @param off position of the mismatch.
     150             :  */
     151             : static void
     152           0 : print_expected (struct History *h,
     153             :                 unsigned int h_len,
     154             :                 unsigned int off)
     155             : {
     156           0 :   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     157             :               "Transaction history (debit) mismatch at position %u/%u\n",
     158             :               off,
     159             :               h_len);
     160           0 :   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     161             :               "Expected history:\n");
     162           0 :   for (unsigned int i = 0; i<h_len; i++)
     163             :   {
     164           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     165             :                 "H(%u): %s (serial: %llu, subject: %s, counterpart: %s)\n",
     166             :                 i,
     167             :                 TALER_amount2s (&h[i].details.amount),
     168             :                 (unsigned long long) h[i].row_id,
     169             :                 TALER_B2S (&h[i].details.wtid),
     170             :                 h[i].details.credit_account_uri);
     171             :   }
     172           0 : }
     173             : 
     174             : 
     175             : /**
     176             :  * This function constructs the list of history elements that
     177             :  * interest the account number of the caller.  It has two main
     178             :  * loops: the first to figure out how many history elements have
     179             :  * to be allocated, and the second to actually populate every
     180             :  * element.
     181             :  *
     182             :  * @param is interpreter state (supposedly having the
     183             :  *        current CMD pointing at a "history" CMD).
     184             :  * @param[out] rh history array to initialize.
     185             :  * @return number of entries in @a rh.
     186             :  */
     187             : static unsigned int
     188           4 : build_history (struct TALER_TESTING_Interpreter *is,
     189             :                struct History **rh)
     190             : {
     191           4 :   struct HistoryState *hs = is->commands[is->ip].cls;
     192             :   unsigned int total;
     193             :   unsigned int pos;
     194             :   struct History *h;
     195             :   const struct TALER_TESTING_Command *add_incoming_cmd;
     196             :   int inc;
     197             :   int start;
     198             :   int end;
     199             :   /* #GNUNET_YES whenever either no 'start' value was given for the history
     200             :    * query, or the given value is found in the list of all the CMDs. */
     201             :   int ok;
     202           4 :   const uint64_t *row_id_start = NULL;
     203             : 
     204           4 :   if (NULL != hs->start_row_reference)
     205             :   {
     206           0 :     TALER_LOG_INFO
     207             :       ("`%s': start row given via reference `%s'\n",
     208             :       TALER_TESTING_interpreter_get_current_label  (is),
     209             :       hs->start_row_reference);
     210           0 :     add_incoming_cmd = TALER_TESTING_interpreter_lookup_command
     211             :                          (is, hs->start_row_reference);
     212           0 :     GNUNET_assert (NULL != add_incoming_cmd);
     213           0 :     GNUNET_assert (GNUNET_OK ==
     214             :                    TALER_TESTING_get_trait_uint64 (add_incoming_cmd,
     215             :                                                    0,
     216             :                                                    &row_id_start));
     217             :   }
     218             : 
     219           4 :   GNUNET_assert (0 != hs->num_results);
     220           4 :   if (0 == is->ip)
     221             :   {
     222           0 :     TALER_LOG_DEBUG ("Checking history at first CMD..\n");
     223           0 :     *rh = NULL;
     224           0 :     return 0;
     225             :   }
     226             : 
     227             :   /* AKA 'delta' */
     228           4 :   if (hs->num_results > 0)
     229             :   {
     230           4 :     inc = 1;  /* _inc_rement: go forwards */
     231           4 :     start = 0;
     232           4 :     end = is->ip;
     233             :   }
     234             :   else
     235             :   {
     236           0 :     inc = -1; /* decrement: we go backwards */
     237           0 :     start = is->ip - 1;
     238           0 :     end = -1; /* range is exclusive, do look at 0! */
     239             :   }
     240             : 
     241           4 :   ok = GNUNET_NO;
     242           4 :   if (NULL == row_id_start)
     243           4 :     ok = GNUNET_YES;
     244           4 :   h = NULL;
     245           4 :   total = 0;
     246           4 :   GNUNET_array_grow (h,
     247             :                      total,
     248             :                      4);
     249           4 :   pos = 0;
     250           4 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     251             :               "Checking commands %u to %u for debit history\n",
     252             :               start,
     253             :               end);
     254          32 :   for (int off = start; off != end; off += inc)
     255             :   {
     256          28 :     const struct TALER_TESTING_Command *cmd = &is->commands[off];
     257             :     const uint64_t *row_id;
     258             :     const char *debit_account;
     259             :     const char *credit_account;
     260             :     const struct TALER_Amount *amount;
     261             :     const struct TALER_WireTransferIdentifierRawP *wtid;
     262             :     const char *exchange_base_url;
     263             : 
     264          28 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     265             :                 "Checking if command %s is relevant for debit history\n",
     266             :                 cmd->label);
     267          28 :     if ( (GNUNET_OK !=
     268          28 :           TALER_TESTING_get_trait_bank_row (cmd,
     269           8 :                                             &row_id)) ||
     270             :          (GNUNET_OK !=
     271           8 :           TALER_TESTING_get_trait_payto (cmd,
     272             :                                          TALER_TESTING_PT_DEBIT,
     273           8 :                                          &debit_account)) ||
     274             :          (GNUNET_OK !=
     275           8 :           TALER_TESTING_get_trait_payto (cmd,
     276             :                                          TALER_TESTING_PT_CREDIT,
     277           8 :                                          &credit_account)) ||
     278             :          (GNUNET_OK !=
     279           8 :           TALER_TESTING_get_trait_amount_obj (cmd,
     280             :                                               0,
     281           8 :                                               &amount)) ||
     282             :          (GNUNET_OK !=
     283           8 :           TALER_TESTING_get_trait_wtid (cmd,
     284             :                                         0,
     285           2 :                                         &wtid)) ||
     286             :          (GNUNET_OK !=
     287           2 :           TALER_TESTING_get_trait_url (cmd,
     288             :                                        TALER_TESTING_UT_EXCHANGE_BASE_URL,
     289             :                                        &exchange_base_url)) )
     290          26 :       continue; /* not an event we care about */
     291             :     /* Seek "/history/outgoing" starting row.  */
     292           2 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     293             :                 "Command %s is relevant for debit history!\n",
     294             :                 cmd->label);
     295           2 :     if ( (NULL != row_id_start) &&
     296           0 :          (*row_id_start == *row_id) &&
     297             :          (GNUNET_NO == ok) )
     298             :     {
     299             :       /* Until here, nothing counted. */
     300           0 :       ok = GNUNET_YES;
     301           0 :       continue;
     302             :     }
     303             :     /* when 'start' was _not_ given, then ok == GNUNET_YES */
     304           2 :     if (GNUNET_NO == ok)
     305           0 :       continue; /* skip until we find the marker */
     306           2 :     if (total >= GNUNET_MAX (hs->num_results,
     307             :                              -hs->num_results) )
     308             :     {
     309           0 :       TALER_LOG_DEBUG ("Hit history limit\n");
     310           0 :       break;
     311             :     }
     312           2 :     TALER_LOG_INFO ("Found history: %s->%s for account %s\n",
     313             :                     debit_account,
     314             :                     credit_account,
     315             :                     hs->account_url);
     316             :     /* found matching record, make sure we have room */
     317           2 :     if (pos == total)
     318           0 :       GNUNET_array_grow (h,
     319             :                          total,
     320             :                          pos * 2);
     321           2 :     h[pos].c_url = GNUNET_strdup (credit_account);
     322           2 :     h[pos].d_url = GNUNET_strdup (debit_account);
     323           2 :     h[pos].details.credit_account_uri = h[pos].c_url;
     324           2 :     h[pos].details.debit_account_uri = h[pos].d_url;
     325           2 :     h[pos].details.amount = *amount;
     326           2 :     h[pos].row_id = *row_id;
     327           2 :     h[pos].details.wtid = *wtid;
     328           2 :     h[pos].details.exchange_base_url = exchange_base_url;
     329           2 :     pos++;
     330             :   }
     331           4 :   GNUNET_assert (GNUNET_YES == ok);
     332           4 :   GNUNET_array_grow (h,
     333             :                      total,
     334             :                      pos);
     335           4 :   if (0 == pos)
     336           2 :     TALER_LOG_DEBUG ("Empty debit history computed\n");
     337           4 :   *rh = h;
     338           4 :   return total;
     339             : }
     340             : 
     341             : 
     342             : /**
     343             :  * Check that the "/history/outgoing" response matches the
     344             :  * CMD whose offset in the list of CMDs is @a off.
     345             :  *
     346             :  * @param h expected history
     347             :  * @param total number of entries in @a h
     348             :  * @param off the offset (of the CMD list) where the command
     349             :  *        to check is.
     350             :  * @param details the expected transaction details.
     351             :  * @return #GNUNET_OK if the transaction is what we expect.
     352             :  */
     353             : static int
     354           2 : check_result (struct History *h,
     355             :               uint64_t total,
     356             :               unsigned int off,
     357             :               const struct TALER_BANK_DebitDetails *details)
     358             : {
     359           2 :   if (off >= total)
     360             :   {
     361           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     362             :                 "Test says history has at most %u"
     363             :                 " results, but got result #%u to check\n",
     364             :                 (unsigned int) total,
     365             :                 off);
     366           0 :     print_expected (h,
     367             :                     total,
     368             :                     off);
     369           0 :     return GNUNET_SYSERR;
     370             :   }
     371           2 :   if ( (0 != GNUNET_memcmp (&h[off].details.wtid,
     372           2 :                             &details->wtid)) ||
     373           2 :        (0 != TALER_amount_cmp (&h[off].details.amount,
     374           2 :                                &details->amount)) ||
     375           2 :        (0 != strcasecmp (h[off].details.credit_account_uri,
     376             :                          details->credit_account_uri)) )
     377             :   {
     378           0 :     GNUNET_break (0);
     379           0 :     print_expected (h,
     380             :                     total,
     381             :                     off);
     382           0 :     return GNUNET_SYSERR;
     383             :   }
     384           2 :   return GNUNET_OK;
     385             : }
     386             : 
     387             : 
     388             : /**
     389             :  * This callback will (1) check that the HTTP response code
     390             :  * is acceptable and (2) that the history is consistent.  The
     391             :  * consistency is checked by going through all the past CMDs,
     392             :  * reconstructing then the expected history as of those, and
     393             :  * finally check it against what the bank returned.
     394             :  *
     395             :  * @param cls closure.
     396             :  * @param http_status HTTP response code, #MHD_HTTP_OK (200)
     397             :  *        for successful status request 0 if the bank's reply is
     398             :  *        bogus (fails to follow the protocol),
     399             :  *        #MHD_HTTP_NO_CONTENT if there are no more results; on
     400             :  *        success the last callback is always of this status
     401             :  *        (even if `abs(num_results)` were already returned).
     402             :  * @param ec taler status code.
     403             :  * @param row_id monotonically increasing counter corresponding to
     404             :  *        the transaction.
     405             :  * @param details details about the wire transfer.
     406             :  * @param json detailed response from the HTTPD, or NULL if
     407             :  *        reply was not in JSON.
     408             :  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
     409             :  */
     410             : static int
     411           6 : history_cb (void *cls,
     412             :             unsigned int http_status,
     413             :             enum TALER_ErrorCode ec,
     414             :             uint64_t row_id,
     415             :             const struct TALER_BANK_DebitDetails *details,
     416             :             const json_t *json)
     417             : {
     418           6 :   struct TALER_TESTING_Interpreter *is = cls;
     419           6 :   struct HistoryState *hs = is->commands[is->ip].cls;
     420             : 
     421             :   (void) row_id;
     422           6 :   if (NULL == details)
     423             :   {
     424           4 :     hs->hh = NULL;
     425           4 :     if ( (hs->results_obtained != hs->total) ||
     426           4 :          (GNUNET_YES == hs->failed) ||
     427             :          (MHD_HTTP_NO_CONTENT != http_status) )
     428             :     {
     429           0 :       GNUNET_break (0);
     430           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     431             :                   "Expected history of length %u, got %llu;"
     432             :                   " HTTP status code: %u/%d, failed: %d\n",
     433             :                   hs->total,
     434             :                   (unsigned long long) hs->results_obtained,
     435             :                   http_status,
     436             :                   (int) ec,
     437             :                   hs->failed);
     438           0 :       print_expected (hs->h,
     439             :                       hs->total,
     440             :                       UINT_MAX);
     441           0 :       TALER_TESTING_interpreter_fail (is);
     442           0 :       return GNUNET_SYSERR;
     443             :     }
     444           4 :     TALER_TESTING_interpreter_next (is);
     445           4 :     return GNUNET_OK;
     446             :   }
     447           2 :   if (MHD_HTTP_OK != http_status)
     448             :   {
     449           0 :     hs->hh = NULL;
     450           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     451             :                 "Unwanted response code from /history/outgoing: %u\n",
     452             :                 http_status);
     453           0 :     TALER_TESTING_interpreter_fail (is);
     454           0 :     return GNUNET_SYSERR;
     455             :   }
     456             : 
     457             :   /* check current element */
     458           2 :   if (GNUNET_OK != check_result (hs->h,
     459           2 :                                  hs->total,
     460           2 :                                  hs->results_obtained,
     461             :                                  details))
     462             :   {
     463             :     char *acc;
     464             : 
     465           0 :     GNUNET_break (0);
     466           0 :     acc = json_dumps (json,
     467             :                       JSON_COMPACT);
     468           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     469             :                 "Result %u was `%s'\n",
     470             :                 (unsigned int) hs->results_obtained++,
     471             :                 acc);
     472           0 :     if (NULL != acc)
     473           0 :       free (acc);
     474           0 :     hs->failed = GNUNET_YES;
     475           0 :     return GNUNET_SYSERR;
     476             :   }
     477           2 :   hs->results_obtained++;
     478           2 :   return GNUNET_OK;
     479             : }
     480             : 
     481             : 
     482             : /**
     483             :  * Run the command.
     484             :  *
     485             :  * @param cls closure.
     486             :  * @param cmd the command to execute.
     487             :  * @param is the interpreter state.
     488             :  */
     489             : static void
     490           4 : history_run (void *cls,
     491             :              const struct TALER_TESTING_Command *cmd,
     492             :              struct TALER_TESTING_Interpreter *is)
     493             : {
     494           4 :   struct HistoryState *hs = cls;
     495           4 :   uint64_t row_id = (hs->num_results > 0) ? 0 : UINT64_MAX;
     496             :   const uint64_t *row_ptr;
     497             : 
     498             :   (void) cmd;
     499             :   /* Get row_id from trait. */
     500           4 :   if (NULL != hs->start_row_reference)
     501             :   {
     502             :     const struct TALER_TESTING_Command *history_cmd;
     503             : 
     504             :     history_cmd
     505           0 :       = TALER_TESTING_interpreter_lookup_command (is,
     506             :                                                   hs->start_row_reference);
     507             : 
     508           0 :     if (NULL == history_cmd)
     509           0 :       TALER_TESTING_FAIL (is);
     510           0 :     if (GNUNET_OK !=
     511           0 :         TALER_TESTING_get_trait_uint64 (history_cmd,
     512             :                                         0,
     513             :                                         &row_ptr))
     514           0 :       TALER_TESTING_FAIL (is);
     515             :     else
     516           0 :       row_id = *row_ptr;
     517           0 :     TALER_LOG_DEBUG ("row id (from trait) is %llu\n",
     518             :                      (unsigned long long) row_id);
     519             :   }
     520           4 :   hs->total = build_history (is, &hs->h);
     521           4 :   hs->hh = TALER_BANK_debit_history (is->ctx,
     522           4 :                                      &hs->auth,
     523             :                                      row_id,
     524           4 :                                      hs->num_results,
     525             :                                      GNUNET_TIME_UNIT_ZERO,
     526             :                                      &history_cb,
     527             :                                      is);
     528           4 :   GNUNET_assert (NULL != hs->hh);
     529             : }
     530             : 
     531             : 
     532             : /**
     533             :  * Free the state from a "history" CMD, and possibly cancel
     534             :  * a pending operation thereof.
     535             :  *
     536             :  * @param cls closure.
     537             :  * @param cmd the command which is being cleaned up.
     538             :  */
     539             : static void
     540           4 : history_cleanup (void *cls,
     541             :                  const struct TALER_TESTING_Command *cmd)
     542             : {
     543           4 :   struct HistoryState *hs = cls;
     544             : 
     545             :   (void) cmd;
     546           4 :   if (NULL != hs->hh)
     547             :   {
     548           0 :     TALER_LOG_WARNING ("/history/outgoing did not complete\n");
     549           0 :     TALER_BANK_debit_history_cancel (hs->hh);
     550             :   }
     551           6 :   for (unsigned int off = 0; off<hs->total; off++)
     552             :   {
     553           2 :     GNUNET_free (hs->h[off].c_url);
     554           2 :     GNUNET_free (hs->h[off].d_url);
     555             :   }
     556           4 :   GNUNET_free (hs->h);
     557           4 :   GNUNET_free (hs);
     558           4 : }
     559             : 
     560             : 
     561             : /**
     562             :  * Make a "history" CMD.
     563             :  *
     564             :  * @param label command label.
     565             :  * @param auth login data to use
     566             :  * @param start_row_reference reference to a command that can
     567             :  *        offer a row identifier, to be used as the starting row
     568             :  *        to accept in the result.
     569             :  * @param num_results how many rows we want in the result.
     570             :  * @return the command.
     571             :  */
     572             : struct TALER_TESTING_Command
     573           4 : TALER_TESTING_cmd_bank_debits (const char *label,
     574             :                                const struct TALER_BANK_AuthenticationData *auth,
     575             :                                const char *start_row_reference,
     576             :                                long long num_results)
     577             : {
     578             :   struct HistoryState *hs;
     579             : 
     580           4 :   hs = GNUNET_new (struct HistoryState);
     581           4 :   hs->account_url = auth->wire_gateway_url;
     582           4 :   hs->start_row_reference = start_row_reference;
     583           4 :   hs->num_results = num_results;
     584           4 :   hs->auth = *auth;
     585             : 
     586             :   {
     587           4 :     struct TALER_TESTING_Command cmd = {
     588             :       .label = label,
     589             :       .cls = hs,
     590             :       .run = &history_run,
     591             :       .cleanup = &history_cleanup,
     592             :       .traits = &history_traits
     593             :     };
     594             : 
     595           4 :     return cmd;
     596             :   }
     597             : }
     598             : 
     599             : 
     600             : /* end of testing_api_cmd_bank_history_debit.c */

Generated by: LCOV version 1.14