merchant

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

testing_api_cmd_wallet_get_template.c (13877B)


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