LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_wallet_get_template.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 71.6 % 197 141
Test Date: 2026-09-11 17:45:24 Functions: 100.0 % 5 5

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2025 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 src/testing/testing_api_cmd_wallet_get_template.c
      18              :  * @brief command to test GET /templates/$ID (wallet)
      19              :  * @author Bohdan Potuzhnyi
      20              :  */
      21              : #include "platform.h"
      22              : struct WalletGetTemplateState;
      23              : #define TALER_MERCHANT_GET_TEMPLATES_RESULT_CLOSURE struct WalletGetTemplateState
      24              : #include <microhttpd.h>
      25              : #include <jansson.h>
      26              : #include <taler/taler_testing_lib.h>
      27              : #include "taler/taler_merchant_service.h"
      28              : #include "taler/taler_merchant_testing_lib.h"
      29              : #include <taler/merchant/get-templates-TEMPLATE_ID.h>
      30              : 
      31              : /**
      32              :  * State of a "GET template" wallet CMD.
      33              :  */
      34              : struct WalletGetTemplateState
      35              : {
      36              :   /**
      37              :    * Handle for a "GET template" request.
      38              :    */
      39              :   struct TALER_MERCHANT_GetTemplatesHandle *igh;
      40              : 
      41              :   /**
      42              :    * The interpreter state.
      43              :    */
      44              :   struct TALER_TESTING_Interpreter *is;
      45              : 
      46              :   /**
      47              :    * Base URL of the merchant serving the request.
      48              :    */
      49              :   const char *merchant_url;
      50              : 
      51              :   /**
      52              :    * ID of the template to run GET for.
      53              :    */
      54              :   const char *template_id;
      55              : 
      56              :   /**
      57              :    * Expected product count (0 to ignore).
      58              :    */
      59              :   size_t expected_products_len;
      60              : 
      61              :   /**
      62              :    * Product id to verify unit info for (optional).
      63              :    */
      64              :   const char *expected_product_id;
      65              : 
      66              :   /**
      67              :    * Expected unit for @e expected_product_id.
      68              :    */
      69              :   const char *expected_unit;
      70              : 
      71              :   /**
      72              :    * Expected allow_fraction for @e expected_product_id.
      73              :    */
      74              :   bool expected_unit_allow_fraction;
      75              : 
      76              :   /**
      77              :    * Expected precision for @e expected_product_id.
      78              :    */
      79              :   uint32_t expected_unit_precision_level;
      80              : 
      81              :   /**
      82              :    * Expected unit name short i18n for @e expected_unit.
      83              :    */
      84              :   json_t *expected_unit_name_short_i18n;
      85              : 
      86              :   /**
      87              :    * Optional second product id expected to be present.
      88              :    */
      89              :   const char *expected_product_id2;
      90              : 
      91              :   /**
      92              :    * Optional third product id expected to be present.
      93              :    */
      94              :   const char *expected_product_id3;
      95              : 
      96              :   /**
      97              :    * Expected category id 1 (0 to ignore).
      98              :    */
      99              :   uint64_t expected_category_id1;
     100              : 
     101              :   /**
     102              :    * Expected category id 2 (0 to ignore).
     103              :    */
     104              :   uint64_t expected_category_id2;
     105              : 
     106              :   /**
     107              :    * Exchange candidate URL expected in the response (optional).
     108              :    */
     109              :   const char *expected_exchange_url;
     110              : 
     111              :   /**
     112              :    * Wire method expected for @e expected_exchange_url.
     113              :    */
     114              :   const char *expected_wire_method;
     115              : 
     116              :   /**
     117              :    * Expected HTTP response code.
     118              :    */
     119              :   unsigned int http_status;
     120              : };
     121              : 
     122              : static bool
     123           30 : product_id_matches (const json_t *product,
     124              :                     const char *expected_id)
     125              : {
     126              :   const json_t *id_val;
     127              : 
     128           30 :   if (NULL == expected_id)
     129           12 :     return false;
     130           18 :   id_val = json_object_get (product,
     131              :                             "product_id");
     132           18 :   if (! json_is_string (id_val))
     133            0 :     return false;
     134           18 :   return (0 == strcmp (json_string_value (id_val),
     135              :                        expected_id));
     136              : }
     137              : 
     138              : 
     139              : /**
     140              :  * Callback for a wallet /get/templates/$ID operation.
     141              :  *
     142              :  * @param cls closure for this function
     143              :  * @param tgr HTTP response details
     144              :  */
     145              : static void
     146            6 : wallet_get_template_cb (struct WalletGetTemplateState *wgs,
     147              :                         const struct
     148              :                         TALER_MERCHANT_GetTemplatesResponse *tgr)
     149              : {
     150              : 
     151            6 :   wgs->igh = NULL;
     152            6 :   if (wgs->http_status != tgr->hr.http_status)
     153              :   {
     154            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     155              :                 "Unexpected response code %u (%d) to command %s\n",
     156              :                 tgr->hr.http_status,
     157              :                 (int) tgr->hr.ec,
     158              :                 TALER_TESTING_interpreter_get_current_label (wgs->is));
     159            0 :     TALER_TESTING_interpreter_fail (wgs->is);
     160            0 :     return;
     161              :   }
     162            6 :   if (MHD_HTTP_OK == tgr->hr.http_status)
     163              :   {
     164            6 :     const json_t *template_contract = tgr->details.ok.template_contract;
     165              :     const json_t *inventory_payload;
     166              :     const json_t *products;
     167              :     bool expect_inventory;
     168              : 
     169            6 :     if ( (NULL == tgr->details.ok.merchant) ||
     170            6 :          (NULL == tgr->details.ok.merchant_pub) )
     171              :     {
     172            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     173              :                   "Missing merchant identity in wallet template\n");
     174            0 :       TALER_TESTING_interpreter_fail (wgs->is);
     175            0 :       return;
     176              :     }
     177            6 :     if (NULL != wgs->expected_exchange_url)
     178              :     {
     179            6 :       bool found_candidate = false;
     180              : 
     181            6 :       if (! tgr->details.ok.exchange_candidates_provided)
     182              :       {
     183            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     184              :                     "Missing exchange_candidates in wallet template\n");
     185            0 :         TALER_TESTING_interpreter_fail (wgs->is);
     186            0 :         return;
     187              :       }
     188            6 :       for (unsigned int i = 0;
     189            6 :            i<tgr->details.ok.num_exchange_candidates;
     190            0 :            i++)
     191              :       {
     192            6 :         const struct TALER_MERCHANT_TemplateExchangeCandidate *candidate
     193            6 :           = &tgr->details.ok.exchange_candidates[i];
     194            6 :         bool found_method = (NULL == wgs->expected_wire_method);
     195              : 
     196            6 :         if (0 != strcmp (candidate->base_url,
     197              :                          wgs->expected_exchange_url))
     198            0 :           continue;
     199            6 :         if (NULL != wgs->expected_wire_method)
     200           12 :           for (unsigned int j = 0; j<candidate->num_wire_methods; j++)
     201            6 :             if (0 == strcasecmp (candidate->wire_methods[j],
     202              :                                  wgs->expected_wire_method))
     203            6 :               found_method = true;
     204            6 :         if (found_method)
     205              :         {
     206            6 :           found_candidate = true;
     207            6 :           break;
     208              :         }
     209              :       }
     210            6 :       if (! found_candidate)
     211              :       {
     212            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     213              :                     "Expected compatible exchange candidate missing\n");
     214            0 :         TALER_TESTING_interpreter_fail (wgs->is);
     215            0 :         return;
     216              :       }
     217              :     }
     218              : 
     219            6 :     inventory_payload = json_object_get (template_contract,
     220              :                                          "inventory_payload");
     221           16 :     expect_inventory = ( (0 < wgs->expected_products_len) ||
     222            4 :                          (NULL != wgs->expected_product_id) ||
     223            4 :                          (NULL != wgs->expected_product_id2) ||
     224            4 :                          (NULL != wgs->expected_product_id3) ||
     225            4 :                          (NULL != wgs->expected_unit_name_short_i18n) ||
     226           14 :                          (0 != wgs->expected_category_id1) ||
     227            4 :                          (0 != wgs->expected_category_id2) );
     228            6 :     if (! json_is_object (inventory_payload))
     229              :     {
     230            2 :       if (! expect_inventory)
     231            2 :         goto done;
     232            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     233              :                   "Missing inventory_payload in wallet template\n");
     234            0 :       TALER_TESTING_interpreter_fail (wgs->is);
     235            0 :       return;
     236              :     }
     237            4 :     products = json_object_get (inventory_payload,
     238              :                                 "products");
     239            4 :     if (! json_is_array (products))
     240              :     {
     241            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     242              :                   "Missing products in wallet template\n");
     243            0 :       TALER_TESTING_interpreter_fail (wgs->is);
     244            0 :       return;
     245              :     }
     246            4 :     if ( (0 < wgs->expected_products_len) &&
     247            2 :          (json_array_size (products) != wgs->expected_products_len) )
     248              :     {
     249            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     250              :                   "Unexpected products length\n");
     251            0 :       TALER_TESTING_interpreter_fail (wgs->is);
     252            0 :       return;
     253              :     }
     254              : 
     255              :     {
     256            4 :       bool found_unit = (NULL == wgs->expected_product_id);
     257            4 :       bool found_extra = (NULL == wgs->expected_product_id2);
     258            4 :       bool found_extra2 = (NULL == wgs->expected_product_id3);
     259              : 
     260           14 :       for (size_t i = 0; i < json_array_size (products); i++)
     261              :       {
     262           10 :         const json_t *product = json_array_get (products,
     263              :                                                 i);
     264              : 
     265           10 :         if (product_id_matches (product,
     266              :                                 wgs->expected_product_id))
     267              :         {
     268              :           const json_t *unit_val;
     269              :           const json_t *allow_val;
     270              :           const json_t *prec_val;
     271              : 
     272            2 :           unit_val = json_object_get (product,
     273              :                                       "unit");
     274            2 :           allow_val = json_object_get (product,
     275              :                                        "unit_allow_fraction");
     276            2 :           prec_val = json_object_get (product,
     277              :                                       "unit_precision_level");
     278            2 :           if ( (NULL == unit_val) ||
     279            2 :                (! json_is_string (unit_val)) ||
     280            2 :                (0 != strcmp (json_string_value (unit_val),
     281              :                              wgs->expected_unit)) )
     282              :           {
     283            0 :             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     284              :                         "Unexpected unit in wallet template\n");
     285            0 :             TALER_TESTING_interpreter_fail (wgs->is);
     286            0 :             return;
     287              :           }
     288            2 :           if ( (! json_is_boolean (allow_val)) ||
     289            2 :                (json_boolean_value (allow_val) !=
     290            2 :                 wgs->expected_unit_allow_fraction) )
     291              :           {
     292            0 :             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     293              :                         "Unexpected unit_allow_fraction in wallet template\n");
     294            0 :             TALER_TESTING_interpreter_fail (wgs->is);
     295            0 :             return;
     296              :           }
     297            2 :           if ( (! json_is_integer (prec_val)) ||
     298            2 :                ((uint32_t) json_integer_value (prec_val) !=
     299            2 :                 wgs->expected_unit_precision_level) )
     300              :           {
     301            0 :             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     302              :                         "Unexpected unit_precision_level in wallet template\n");
     303            0 :             TALER_TESTING_interpreter_fail (wgs->is);
     304            0 :             return;
     305              :           }
     306            2 :           found_unit = true;
     307              :         }
     308           10 :         if (product_id_matches (product,
     309              :                                 wgs->expected_product_id2))
     310            2 :           found_extra = true;
     311           10 :         if (product_id_matches (product,
     312              :                                 wgs->expected_product_id3))
     313            2 :           found_extra2 = true;
     314              :       }
     315            4 :       if (! found_unit || ! found_extra || ! found_extra2)
     316              :       {
     317            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     318              :                     "Expected product ids missing in wallet template\n");
     319            0 :         TALER_TESTING_interpreter_fail (wgs->is);
     320            0 :         return;
     321              :       }
     322              :     }
     323              : 
     324            4 :     if (NULL != wgs->expected_unit_name_short_i18n)
     325              :     {
     326              :       const json_t *units;
     327            2 :       bool found_unit_i18n = false;
     328              : 
     329            2 :       units = json_object_get (inventory_payload,
     330              :                                "units");
     331            2 :       if (! json_is_array (units))
     332              :       {
     333            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     334              :                     "Missing units in wallet template\n");
     335            0 :         TALER_TESTING_interpreter_fail (wgs->is);
     336            0 :         return;
     337              :       }
     338            2 :       for (size_t i = 0; i < json_array_size (units); i++)
     339              :       {
     340            2 :         const json_t *unit = json_array_get (units,
     341              :                                              i);
     342              :         const json_t *unit_id;
     343              :         const json_t *unit_i18n;
     344              : 
     345            2 :         unit_id = json_object_get (unit,
     346              :                                    "unit");
     347            2 :         if (! json_is_string (unit_id))
     348            0 :           continue;
     349            2 :         if (0 != strcmp (json_string_value (unit_id),
     350              :                          wgs->expected_unit))
     351            0 :           continue;
     352            2 :         unit_i18n = json_object_get (unit,
     353              :                                      "unit_name_short_i18n");
     354            2 :         if ( (NULL == unit_i18n) ||
     355            4 :              (! json_is_object (unit_i18n)) ||
     356            2 :              (1 != json_equal (unit_i18n,
     357            2 :                                wgs->expected_unit_name_short_i18n)) )
     358              :         {
     359            0 :           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     360              :                       "Unexpected unit_name_short_i18n in wallet template\n");
     361            0 :           TALER_TESTING_interpreter_fail (wgs->is);
     362            0 :           return;
     363              :         }
     364            2 :         found_unit_i18n = true;
     365            2 :         break;
     366              :       }
     367            2 :       if (! found_unit_i18n)
     368              :       {
     369            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     370              :                     "Expected unit entry missing in wallet template\n");
     371            0 :         TALER_TESTING_interpreter_fail (wgs->is);
     372            0 :         return;
     373              :       }
     374              :     }
     375              : 
     376            4 :     if ( (0 != wgs->expected_category_id1) ||
     377            2 :          (0 != wgs->expected_category_id2) )
     378              :     {
     379              :       const json_t *categories;
     380            2 :       bool found_cat1 = (0 == wgs->expected_category_id1);
     381            2 :       bool found_cat2 = (0 == wgs->expected_category_id2);
     382              : 
     383            2 :       categories = json_object_get (inventory_payload,
     384              :                                     "categories");
     385            2 :       if (! json_is_array (categories))
     386              :       {
     387            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     388              :                     "Missing categories in wallet template\n");
     389            0 :         TALER_TESTING_interpreter_fail (wgs->is);
     390            0 :         return;
     391              :       }
     392            6 :       for (size_t i = 0; i < json_array_size (categories); i++)
     393              :       {
     394            4 :         const json_t *category = json_array_get (categories,
     395              :                                                  i);
     396              :         const json_t *cid;
     397              : 
     398            4 :         cid = json_object_get (category,
     399              :                                "category_id");
     400            4 :         if (! json_is_integer (cid))
     401            0 :           continue;
     402            4 :         if ( (0 != wgs->expected_category_id1) &&
     403            4 :              ((uint64_t) json_integer_value (cid)
     404            4 :               == wgs->expected_category_id1) )
     405            2 :           found_cat1 = true;
     406            4 :         if ( (0 != wgs->expected_category_id2) &&
     407            4 :              ((uint64_t) json_integer_value (cid)
     408            4 :               == wgs->expected_category_id2) )
     409            2 :           found_cat2 = true;
     410              :       }
     411            2 :       if (! found_cat1 || ! found_cat2)
     412              :       {
     413            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     414              :                     "Expected category ids missing in wallet template\n");
     415            0 :         TALER_TESTING_interpreter_fail (wgs->is);
     416            0 :         return;
     417              :       }
     418              :     }
     419              :   }
     420            4 : done:
     421            6 :   TALER_TESTING_interpreter_next (wgs->is);
     422              : }
     423              : 
     424              : 
     425              : /**
     426              :  * Run the "GET /templates/$ID" wallet CMD.
     427              :  *
     428              :  * @param cls closure.
     429              :  * @param cmd command being run now.
     430              :  * @param is interpreter state.
     431              :  */
     432              : static void
     433            6 : wallet_get_template_run (void *cls,
     434              :                          const struct TALER_TESTING_Command *cmd,
     435              :                          struct TALER_TESTING_Interpreter *is)
     436              : {
     437            6 :   struct WalletGetTemplateState *wgs = cls;
     438              : 
     439            6 :   wgs->is = is;
     440            6 :   wgs->igh = TALER_MERCHANT_get_templates_create (
     441              :     TALER_TESTING_interpreter_get_context (is),
     442              :     wgs->merchant_url,
     443              :     wgs->template_id);
     444              :   {
     445              :     enum TALER_ErrorCode ec;
     446              : 
     447            6 :     ec = TALER_MERCHANT_get_templates_start (
     448              :       wgs->igh,
     449              :       &wallet_get_template_cb,
     450              :       wgs);
     451            6 :     GNUNET_assert (TALER_EC_NONE == ec);
     452              :   }
     453            6 : }
     454              : 
     455              : 
     456              : /**
     457              :  * Free the state of a "GET template" CMD, and possibly
     458              :  * cancel a pending operation thereof.
     459              :  *
     460              :  * @param cls closure.
     461              :  * @param cmd command being run.
     462              :  */
     463              : static void
     464            6 : wallet_get_template_cleanup (void *cls,
     465              :                              const struct TALER_TESTING_Command *cmd)
     466              : {
     467            6 :   struct WalletGetTemplateState *wgs = cls;
     468              : 
     469            6 :   if (NULL != wgs->igh)
     470              :   {
     471            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     472              :                 "GET /templates/$ID operation did not complete\n");
     473            0 :     TALER_MERCHANT_get_templates_cancel (wgs->igh);
     474              :   }
     475            6 :   json_decref (wgs->expected_unit_name_short_i18n);
     476            6 :   GNUNET_free (wgs);
     477            6 : }
     478              : 
     479              : 
     480              : struct TALER_TESTING_Command
     481            6 : TALER_TESTING_cmd_merchant_wallet_get_template (
     482              :   const char *label,
     483              :   const char *merchant_url,
     484              :   const char *template_id,
     485              :   size_t expected_products_len,
     486              :   const char *expected_product_id,
     487              :   const char *expected_unit,
     488              :   bool expected_unit_allow_fraction,
     489              :   uint32_t expected_unit_precision_level,
     490              :   json_t *expected_unit_name_short_i18n,
     491              :   const char *expected_product_id2,
     492              :   const char *expected_product_id3,
     493              :   uint64_t expected_category_id1,
     494              :   uint64_t expected_category_id2,
     495              :   const char *expected_exchange_url,
     496              :   const char *expected_wire_method,
     497              :   unsigned int http_status)
     498              : {
     499              :   struct WalletGetTemplateState *wgs;
     500              : 
     501            6 :   wgs = GNUNET_new (struct WalletGetTemplateState);
     502            6 :   wgs->merchant_url = merchant_url;
     503            6 :   wgs->template_id = template_id;
     504            6 :   wgs->expected_products_len = expected_products_len;
     505            6 :   wgs->expected_product_id = expected_product_id;
     506            6 :   wgs->expected_unit = expected_unit;
     507            6 :   wgs->expected_unit_allow_fraction = expected_unit_allow_fraction;
     508            6 :   wgs->expected_unit_precision_level = expected_unit_precision_level;
     509            6 :   if (NULL != expected_unit_name_short_i18n)
     510            2 :     wgs->expected_unit_name_short_i18n =
     511            2 :       json_incref (expected_unit_name_short_i18n);
     512            6 :   wgs->expected_product_id2 = expected_product_id2;
     513            6 :   wgs->expected_product_id3 = expected_product_id3;
     514            6 :   wgs->expected_category_id1 = expected_category_id1;
     515            6 :   wgs->expected_category_id2 = expected_category_id2;
     516            6 :   wgs->expected_exchange_url = expected_exchange_url;
     517            6 :   wgs->expected_wire_method = expected_wire_method;
     518            6 :   wgs->http_status = http_status;
     519              :   {
     520            6 :     struct TALER_TESTING_Command cmd = {
     521              :       .cls = wgs,
     522              :       .label = label,
     523              :       .run = &wallet_get_template_run,
     524              :       .cleanup = &wallet_get_template_cleanup
     525              :     };
     526              : 
     527            6 :     return cmd;
     528              :   }
     529              : }
     530              : 
     531              : 
     532              : /* end of testing_api_cmd_wallet_get_template.c */
        

Generated by: LCOV version 2.0-1