LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_get_orders.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 69.6 % 161 112
Test Date: 2025-11-13 17:46:01 Functions: 100.0 % 12 12

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2020-2023 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_get_orders.c
      21              :  * @brief command to test GET /orders
      22              :  * @author Jonathan Buchanan
      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 "GET orders" CMD.
      33              :  */
      34              : struct GetOrdersState
      35              : {
      36              : 
      37              :   /**
      38              :    * Handle for a "GET orders" request.
      39              :    */
      40              :   struct TALER_MERCHANT_OrdersGetHandle *ogh;
      41              : 
      42              :   /**
      43              :    * The interpreter state.
      44              :    */
      45              :   struct TALER_TESTING_Interpreter *is;
      46              : 
      47              :   /**
      48              :    * Base URL of the merchant serving the request.
      49              :    */
      50              :   const char *merchant_url;
      51              : 
      52              :   /**
      53              :    * Expected HTTP response code.
      54              :    */
      55              :   unsigned int http_status;
      56              : 
      57              :   /**
      58              :    * A NULL-terminated array of CMD labels that created orders.
      59              :    */
      60              :   const char **orders;
      61              : 
      62              :   /**
      63              :    * The length of @e orders.
      64              :    */
      65              :   unsigned int orders_length;
      66              : 
      67              : };
      68              : 
      69              : 
      70              : /**
      71              :  * Callback for a GET /orders operation.
      72              :  *
      73              :  * @param cls closure for this function
      74              :  * @param ogr response
      75              :  */
      76              : static void
      77            7 : get_orders_cb (void *cls,
      78              :                const struct TALER_MERCHANT_OrdersGetResponse *ogr)
      79              : {
      80            7 :   struct GetOrdersState *gos = cls;
      81              : 
      82            7 :   gos->ogh = NULL;
      83            7 :   if (gos->http_status != ogr->hr.http_status)
      84              :   {
      85            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      86              :                 "Unexpected response code %u (%d) to command %s\n",
      87              :                 ogr->hr.http_status,
      88              :                 (int) ogr->hr.ec,
      89              :                 TALER_TESTING_interpreter_get_current_label (gos->is));
      90            0 :     TALER_TESTING_interpreter_fail (gos->is);
      91            0 :     return;
      92              :   }
      93            7 :   switch (ogr->hr.http_status)
      94              :   {
      95            7 :   case MHD_HTTP_OK:
      96            7 :     if (ogr->details.ok.orders_length != gos->orders_length)
      97              :     {
      98            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      99              :                   "Number of orders found does not match\n");
     100            0 :       TALER_TESTING_interpreter_fail (gos->is);
     101            0 :       return;
     102              :     }
     103           15 :     for (unsigned int i = 0; i < ogr->details.ok.orders_length; ++i)
     104              :     {
     105            8 :       const struct TALER_MERCHANT_OrderEntry *order =
     106            8 :         &ogr->details.ok.orders[i];
     107              :       const struct TALER_TESTING_Command *order_cmd;
     108              : 
     109            8 :       order_cmd = TALER_TESTING_interpreter_lookup_command (
     110              :         gos->is,
     111            8 :         gos->orders[i]);
     112              : 
     113              :       {
     114              :         const char *order_id;
     115              : 
     116            8 :         if (GNUNET_OK !=
     117            8 :             TALER_TESTING_get_trait_order_id (order_cmd,
     118              :                                               &order_id))
     119              :         {
     120            0 :           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     121              :                       "Could not fetch order id\n");
     122            0 :           TALER_TESTING_interpreter_fail (gos->is);
     123            0 :           return;
     124              :         }
     125            8 :         if (0 != strcmp (order->order_id,
     126              :                          order_id))
     127              :         {
     128            0 :           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     129              :                       "Order id does not match\n");
     130            0 :           TALER_TESTING_interpreter_fail (gos->is);
     131            0 :           return;
     132              :         }
     133              :       }
     134              :       {
     135              :         const json_t *contract_terms;
     136              :         struct TALER_Amount amount;
     137              :         const char *summary;
     138              :         struct GNUNET_JSON_Specification spec[] = {
     139            8 :           GNUNET_JSON_spec_string ("summary",
     140              :                                    &summary),
     141            8 :           TALER_JSON_spec_amount_any ("amount",
     142              :                                       &amount),
     143            8 :           GNUNET_JSON_spec_end ()
     144              :         };
     145              : 
     146            8 :         if (GNUNET_OK !=
     147            8 :             TALER_TESTING_get_trait_contract_terms (order_cmd,
     148              :                                                     &contract_terms))
     149              :         {
     150            0 :           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     151              :                       "Could not fetch order contract terms\n");
     152            0 :           TALER_TESTING_interpreter_fail (gos->is);
     153            0 :           return;
     154              :         }
     155            8 :         if (GNUNET_OK !=
     156            8 :             GNUNET_JSON_parse (contract_terms,
     157              :                                spec,
     158              :                                NULL, NULL))
     159              :         {
     160            0 :           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     161              :                       "Could not parse order contract terms\n");
     162            0 :           TALER_TESTING_interpreter_fail (gos->is);
     163            0 :           return;
     164              :         }
     165            8 :         if ((0 != strcmp (summary,
     166           16 :                           order->summary)) ||
     167            8 :             (GNUNET_OK != TALER_amount_cmp_currency (&amount,
     168            8 :                                                      &order->amount)) ||
     169            8 :             (0 != TALER_amount_cmp (&amount,
     170              :                                     &order->amount)))
     171              :         {
     172            0 :           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     173              :                       "Order summary and/or amount does not match\n");
     174            0 :           TALER_TESTING_interpreter_fail (gos->is);
     175            0 :           return;
     176              :         }
     177              :       }
     178              :     }
     179            7 :     break;
     180            0 :   case MHD_HTTP_ACCEPTED:
     181              :     /* FIXME: do more checks here (new KYC logic!) */
     182            0 :     break;
     183            0 :   default:
     184            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     185              :                 "Unhandled HTTP status.\n");
     186              :   }
     187            7 :   TALER_TESTING_interpreter_next (gos->is);
     188              : }
     189              : 
     190              : 
     191              : /**
     192              :  * Run the "GET /orders" CMD.
     193              :  *
     194              :  * @param cls closure.
     195              :  * @param cmd command being run now.
     196              :  * @param is interpreter state.
     197              :  */
     198              : static void
     199            7 : get_orders_run (void *cls,
     200              :                 const struct TALER_TESTING_Command *cmd,
     201              :                 struct TALER_TESTING_Interpreter *is)
     202              : {
     203            7 :   struct GetOrdersState *gos = cls;
     204              : 
     205            7 :   gos->is = is;
     206            7 :   gos->ogh = TALER_MERCHANT_orders_get (TALER_TESTING_interpreter_get_context (
     207              :                                           is),
     208              :                                         gos->merchant_url,
     209              :                                         &get_orders_cb,
     210              :                                         gos);
     211            7 :   GNUNET_assert (NULL != gos->ogh);
     212            7 : }
     213              : 
     214              : 
     215              : /**
     216              :  * Free the state of a "GET orders" CMD, and possibly
     217              :  * cancel a pending operation thereof.
     218              :  *
     219              :  * @param cls closure.
     220              :  * @param cmd command being run.
     221              :  */
     222              : static void
     223            7 : get_orders_cleanup (void *cls,
     224              :                     const struct TALER_TESTING_Command *cmd)
     225              : {
     226            7 :   struct GetOrdersState *gos = cls;
     227              : 
     228            7 :   if (NULL != gos->ogh)
     229              :   {
     230            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     231              :                 "GET /orders operation did not complete\n");
     232            0 :     TALER_MERCHANT_orders_get_cancel (gos->ogh);
     233              :   }
     234            7 :   GNUNET_array_grow (gos->orders,
     235              :                      gos->orders_length,
     236              :                      0);
     237            7 :   GNUNET_free (gos);
     238            7 : }
     239              : 
     240              : 
     241              : struct TALER_TESTING_Command
     242            7 : TALER_TESTING_cmd_merchant_get_orders (const char *label,
     243              :                                        const char *merchant_url,
     244              :                                        unsigned int http_status,
     245              :                                        ...)
     246              : {
     247              :   struct GetOrdersState *gos;
     248              : 
     249            7 :   gos = GNUNET_new (struct GetOrdersState);
     250            7 :   gos->merchant_url = merchant_url;
     251            7 :   gos->http_status = http_status;
     252              :   {
     253              :     const char *clabel;
     254              :     va_list ap;
     255              : 
     256            7 :     va_start (ap, http_status);
     257           15 :     while (NULL != (clabel = va_arg (ap, const char *)))
     258              :     {
     259            8 :       GNUNET_array_append (gos->orders,
     260              :                            gos->orders_length,
     261              :                            clabel);
     262              :     }
     263            7 :     va_end (ap);
     264              :   }
     265              :   {
     266            7 :     struct TALER_TESTING_Command cmd = {
     267              :       .cls = gos,
     268              :       .label = label,
     269              :       .run = &get_orders_run,
     270              :       .cleanup = &get_orders_cleanup
     271              :     };
     272              : 
     273            7 :     return cmd;
     274              :   }
     275              : }
     276              : 
     277              : 
     278              : struct MerchantPollOrdersConcludeState
     279              : {
     280              :   /**
     281              :    * The interpreter state.
     282              :    */
     283              :   struct TALER_TESTING_Interpreter *is;
     284              : 
     285              :   /**
     286              :    * Reference to a command that can provide a poll orders start command.
     287              :    */
     288              :   const char *start_reference;
     289              : 
     290              :   /**
     291              :    * Task to wait for the deadline.
     292              :    */
     293              :   struct GNUNET_SCHEDULER_Task *task;
     294              : 
     295              :   /**
     296              :    * Expected HTTP response status code.
     297              :    */
     298              :   unsigned int expected_http_status;
     299              : };
     300              : 
     301              : 
     302              : struct MerchantPollOrdersStartState
     303              : {
     304              :   /**
     305              :    * The merchant base URL.
     306              :    */
     307              :   const char *merchant_url;
     308              : 
     309              :   /**
     310              :    * The handle to the current GET /private/orders request.
     311              :    */
     312              :   struct TALER_MERCHANT_OrdersGetHandle *ogh;
     313              : 
     314              :   /**
     315              :    * The interpreter state.
     316              :    */
     317              :   struct TALER_TESTING_Interpreter *is;
     318              : 
     319              :   /**
     320              :    * How long to wait for server to return a response.
     321              :    */
     322              :   struct GNUNET_TIME_Relative timeout;
     323              : 
     324              :   /**
     325              :    * Conclude state waiting for completion (if any).
     326              :    */
     327              :   struct MerchantPollOrdersConcludeState *cs;
     328              : 
     329              :   /**
     330              :    * The HTTP status code returned by the backend.
     331              :    */
     332              :   unsigned int http_status;
     333              : 
     334              :   /**
     335              :    * When the request should be completed by.
     336              :    */
     337              :   struct GNUNET_TIME_Absolute deadline;
     338              : };
     339              : 
     340              : 
     341              : /**
     342              :  * Task called when either the timeout for the get orders
     343              :  * command expired or we got a response.  Checks if the
     344              :  * result is what we expected.
     345              :  *
     346              :  * @param cls a `struct MerchantPollOrdersConcludeState`
     347              :  */
     348              : static void
     349            2 : conclude_task (void *cls)
     350              : {
     351            2 :   struct MerchantPollOrdersConcludeState *poc = cls;
     352              :   const struct TALER_TESTING_Command *poll_cmd;
     353              :   struct MerchantPollOrdersStartState *pos;
     354              :   struct GNUNET_TIME_Absolute now;
     355              : 
     356            2 :   poc->task = NULL;
     357              :   poll_cmd =
     358            2 :     TALER_TESTING_interpreter_lookup_command (poc->is,
     359              :                                               poc->start_reference);
     360            2 :   if (NULL == poll_cmd)
     361            0 :     TALER_TESTING_FAIL (poc->is);
     362            2 :   pos = poll_cmd->cls;
     363            2 :   if (NULL != pos->ogh)
     364              :   {
     365            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     366              :                 "Expected poll GET /private/orders to have completed, but it did not!\n");
     367            0 :     TALER_TESTING_FAIL (poc->is);
     368              :   }
     369            2 :   if (pos->http_status != poc->expected_http_status)
     370              :   {
     371            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     372              :                 "Expected HTTP status %u, got %u\n",
     373              :                 poc->expected_http_status,
     374              :                 pos->http_status);
     375            0 :     TALER_TESTING_FAIL (poc->is);
     376              :   }
     377            2 :   now = GNUNET_TIME_absolute_get ();
     378            2 :   if (GNUNET_TIME_absolute_cmp (GNUNET_TIME_absolute_add (
     379              :                                   pos->deadline,
     380              :                                   GNUNET_TIME_UNIT_SECONDS),
     381              :                                 <,
     382              :                                 now))
     383              :   {
     384            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     385              :                 "Expected answer to be delayed until %llu, but got response at %llu\n",
     386              :                 (unsigned long long) pos->deadline.abs_value_us,
     387              :                 (unsigned long long) now.abs_value_us);
     388            0 :     TALER_TESTING_FAIL (poc->is);
     389              :   }
     390            2 :   TALER_TESTING_interpreter_next (poc->is);
     391              : }
     392              : 
     393              : 
     394              : /**
     395              :  * Callback to process a GET /orders request
     396              :  *
     397              :  * @param cls closure
     398              :  * @param ogr response details
     399              :  */
     400              : static void
     401            2 : merchant_poll_orders_cb (
     402              :   void *cls,
     403              :   const struct TALER_MERCHANT_OrdersGetResponse *ogr)
     404              : {
     405            2 :   struct MerchantPollOrdersStartState *pos = cls;
     406              : 
     407            2 :   pos->ogh = NULL;
     408            2 :   if (MHD_HTTP_OK != ogr->hr.http_status)
     409              :   {
     410            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     411              :                 "Unexpected response code %u (%d) to command %s\n",
     412              :                 ogr->hr.http_status,
     413              :                 (int) ogr->hr.ec,
     414              :                 TALER_TESTING_interpreter_get_current_label (pos->is));
     415            0 :     TALER_TESTING_interpreter_fail (pos->is);
     416            0 :     return;
     417              :   }
     418            2 :   switch (ogr->hr.http_status)
     419              :   {
     420            2 :   case MHD_HTTP_OK:
     421              :     // FIXME: use order references to check if the data returned matches that from the POST / PATCH
     422            2 :     break;
     423            0 :   default:
     424            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     425              :                 "Unhandled HTTP status.\n");
     426              :   }
     427            2 :   pos->http_status = ogr->hr.http_status;
     428            2 :   if (NULL != pos->cs)
     429              :   {
     430            0 :     GNUNET_SCHEDULER_cancel (pos->cs->task);
     431            0 :     pos->cs->task = GNUNET_SCHEDULER_add_now (&conclude_task,
     432            0 :                                               pos->cs);
     433              :   }
     434              : }
     435              : 
     436              : 
     437              : /**
     438              :  * Run the "GET orders" CMD.
     439              :  *
     440              :  * @param cls closure.
     441              :  * @param cmd command being run now.
     442              :  * @param is interpreter state.
     443              :  */
     444              : static void
     445            2 : merchant_poll_orders_start_run (void *cls,
     446              :                                 const struct TALER_TESTING_Command *cmd,
     447              :                                 struct TALER_TESTING_Interpreter *is)
     448              : {
     449            2 :   struct MerchantPollOrdersStartState *pos = cls;
     450              : 
     451              :   /* add 1s grace time to timeout */
     452              :   pos->deadline
     453            2 :     = GNUNET_TIME_relative_to_absolute (
     454              :         GNUNET_TIME_relative_add (pos->timeout,
     455              :                                   GNUNET_TIME_UNIT_SECONDS));
     456            2 :   pos->is = is;
     457            2 :   pos->ogh = TALER_MERCHANT_orders_get2 (TALER_TESTING_interpreter_get_context (
     458              :                                            is),
     459              :                                          pos->merchant_url,
     460              :                                          TALER_EXCHANGE_YNA_ALL,
     461              :                                          TALER_EXCHANGE_YNA_ALL,
     462              :                                          TALER_EXCHANGE_YNA_ALL,
     463            2 :                                          GNUNET_TIME_UNIT_ZERO_TS,
     464              :                                          1,
     465              :                                          2,
     466              :                                          pos->timeout,
     467              :                                          &merchant_poll_orders_cb,
     468              :                                          pos);
     469            2 :   GNUNET_assert (NULL != pos->ogh);
     470              :   /* We CONTINUE to run the interpreter while the long-polled command
     471              :      completes asynchronously! */
     472            2 :   TALER_TESTING_interpreter_next (pos->is);
     473            2 : }
     474              : 
     475              : 
     476              : /**
     477              :  * Free the state of a "GET orders" CMD, and possibly
     478              :  * cancel a pending operation thereof.
     479              :  *
     480              :  * @param cls closure.
     481              :  * @param cmd command being run.
     482              :  */
     483              : static void
     484            2 : merchant_poll_orders_start_cleanup (void *cls,
     485              :                                     const struct TALER_TESTING_Command *cmd)
     486              : {
     487            2 :   struct MerchantPollOrdersStartState *pos = cls;
     488              : 
     489            2 :   if (NULL != pos->ogh)
     490              :   {
     491            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     492              :                 "Command `%s' was not terminated\n",
     493              :                 TALER_TESTING_interpreter_get_current_label (
     494              :                   pos->is));
     495            0 :     TALER_MERCHANT_orders_get_cancel (pos->ogh);
     496              :   }
     497            2 :   GNUNET_free (pos);
     498            2 : }
     499              : 
     500              : 
     501              : struct TALER_TESTING_Command
     502            2 : TALER_TESTING_cmd_poll_orders_start (const char *label,
     503              :                                      const char *merchant_url,
     504              :                                      struct GNUNET_TIME_Relative timeout)
     505              : {
     506              :   struct MerchantPollOrdersStartState *pos;
     507              : 
     508            2 :   pos = GNUNET_new (struct MerchantPollOrdersStartState);
     509            2 :   pos->merchant_url = merchant_url;
     510            2 :   pos->timeout = timeout;
     511              :   {
     512            2 :     struct TALER_TESTING_Command cmd = {
     513              :       .cls = pos,
     514              :       .label = label,
     515              :       .run = &merchant_poll_orders_start_run,
     516              :       .cleanup = &merchant_poll_orders_start_cleanup
     517              :     };
     518              : 
     519            2 :     return cmd;
     520              :   }
     521              : }
     522              : 
     523              : 
     524              : /**
     525              :  * Wait for the "GET orders" CMD to complete.
     526              :  *
     527              :  * @param cls closure.
     528              :  * @param cmd command being run now.
     529              :  * @param is interpreter state.
     530              :  */
     531              : static void
     532            2 : merchant_poll_orders_conclude_run (void *cls,
     533              :                                    const struct TALER_TESTING_Command *cmd,
     534              :                                    struct TALER_TESTING_Interpreter *is)
     535              : {
     536            2 :   struct MerchantPollOrdersConcludeState *poc = cls;
     537              :   const struct TALER_TESTING_Command *poll_cmd;
     538              :   struct MerchantPollOrdersStartState *pos;
     539              : 
     540            2 :   poc->is = is;
     541              :   poll_cmd =
     542            2 :     TALER_TESTING_interpreter_lookup_command (is,
     543              :                                               poc->start_reference);
     544            2 :   if (NULL == poll_cmd)
     545            0 :     TALER_TESTING_FAIL (poc->is);
     546            2 :   GNUNET_assert (poll_cmd->run == &merchant_poll_orders_start_run);
     547            2 :   pos = poll_cmd->cls;
     548            2 :   pos->cs = poc;
     549            2 :   if (NULL == pos->ogh)
     550            2 :     poc->task = GNUNET_SCHEDULER_add_now (&conclude_task,
     551              :                                           poc);
     552              :   else
     553            0 :     poc->task = GNUNET_SCHEDULER_add_at (pos->deadline,
     554              :                                          &conclude_task,
     555              :                                          poc);
     556              : }
     557              : 
     558              : 
     559              : /**
     560              :  * Free the state of a "GET orders" CMD, and possibly
     561              :  * cancel a pending operation thereof.
     562              :  *
     563              :  * @param cls closure.
     564              :  * @param cmd command being run.
     565              :  */
     566              : static void
     567            2 : merchant_poll_orders_conclude_cleanup (void *cls,
     568              :                                        const struct TALER_TESTING_Command *cmd)
     569              : {
     570            2 :   struct MerchantPollOrdersConcludeState *poc = cls;
     571              : 
     572            2 :   if (NULL != poc->task)
     573              :   {
     574            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     575              :                 "Command `%s' was not terminated\n",
     576              :                 TALER_TESTING_interpreter_get_current_label (
     577              :                   poc->is));
     578            0 :     GNUNET_SCHEDULER_cancel (poc->task);
     579            0 :     poc->task = NULL;
     580              :   }
     581            2 :   GNUNET_free (poc);
     582            2 : }
     583              : 
     584              : 
     585              : struct TALER_TESTING_Command
     586            2 : TALER_TESTING_cmd_poll_orders_conclude (const char *label,
     587              :                                         unsigned int http_status,
     588              :                                         const char *poll_start_reference)
     589              : {
     590              :   struct MerchantPollOrdersConcludeState *poc;
     591              : 
     592            2 :   poc = GNUNET_new (struct MerchantPollOrdersConcludeState);
     593            2 :   poc->start_reference = poll_start_reference;
     594            2 :   poc->expected_http_status = http_status;
     595              :   {
     596            2 :     struct TALER_TESTING_Command cmd = {
     597              :       .cls = poc,
     598              :       .label = label,
     599              :       .run = &merchant_poll_orders_conclude_run,
     600              :       .cleanup = &merchant_poll_orders_conclude_cleanup
     601              :     };
     602              : 
     603            2 :     return cmd;
     604              :   }
     605              : }
     606              : 
     607              : 
     608              : /* end of testing_api_cmd_get_orders.c */
        

Generated by: LCOV version 2.0-1