merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

testing_api_cmd_wallet_get_template.c (13636B)


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