merchant

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

testing_api_cmd_wallet_get_template.c (13791B)


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