merchant

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

testing_api_cmd_get_product.c (14572B)


      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 src/testing/testing_api_cmd_get_product.c
     21  * @brief command to test GET /product/$ID
     22  * @author Christian Grothoff
     23  */
     24 #include "platform.h"
     25 struct GetProductState;
     26 #define TALER_MERCHANT_GET_PRIVATE_PRODUCT_RESULT_CLOSURE struct GetProductState
     27 #include <taler/taler_exchange_service.h>
     28 #include <taler/taler_testing_lib.h>
     29 #include "taler/taler_merchant_service.h"
     30 #include "taler/taler_merchant_testing_lib.h"
     31 #include <taler/merchant/get-private-products-PRODUCT_ID.h>
     32 
     33 
     34 /**
     35  * State of a "GET product" CMD.
     36  */
     37 struct GetProductState
     38 {
     39 
     40   /**
     41    * Handle for a "GET product" request.
     42    */
     43   struct TALER_MERCHANT_GetPrivateProductHandle *igh;
     44 
     45   /**
     46    * The interpreter state.
     47    */
     48   struct TALER_TESTING_Interpreter *is;
     49 
     50   /**
     51    * Base URL of the merchant serving the request.
     52    */
     53   const char *merchant_url;
     54 
     55   /**
     56    * ID of the product to run GET for.
     57    */
     58   const char *product_id;
     59 
     60   /**
     61    * Reference for a POST or PATCH /products CMD (optional).
     62    */
     63   const char *product_reference;
     64 
     65   /**
     66    * Expected HTTP response code.
     67    */
     68   unsigned int http_status;
     69 
     70   /**
     71    * Optional overrides for fractional fields.
     72    */
     73   const struct TALER_TESTING_ProductUnitExpectations *unit_expectations;
     74 
     75 };
     76 
     77 
     78 /**
     79  * Callback for a /get/product/$ID operation.
     80  *
     81  * @param cls closure for this function
     82  * @param pgr response details
     83  */
     84 static void
     85 get_product_cb (struct GetProductState *gis,
     86                 const struct TALER_MERCHANT_GetPrivateProductResponse *pgr)
     87 {
     88   const struct TALER_TESTING_Command *product_cmd;
     89   const struct TALER_TESTING_ProductUnitExpectations *ue =
     90     gis->unit_expectations;
     91 
     92   gis->igh = NULL;
     93   if (gis->http_status != pgr->hr.http_status)
     94   {
     95     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     96                 "Unexpected response code %u (%d) to command %s\n",
     97                 pgr->hr.http_status,
     98                 (int) pgr->hr.ec,
     99                 TALER_TESTING_interpreter_get_current_label (gis->is));
    100     TALER_TESTING_interpreter_fail (gis->is);
    101     return;
    102   }
    103   switch (pgr->hr.http_status)
    104   {
    105   case MHD_HTTP_OK:
    106     {
    107       const char *expected_description;
    108 
    109       product_cmd = TALER_TESTING_interpreter_lookup_command (
    110         gis->is,
    111         gis->product_reference);
    112       if (GNUNET_OK !=
    113           TALER_TESTING_get_trait_product_description (product_cmd,
    114                                                        &expected_description))
    115         TALER_TESTING_interpreter_fail (gis->is);
    116       if (0 != strcmp (pgr->details.ok.description,
    117                        expected_description))
    118       {
    119         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    120                     "Product description does not match\n");
    121         TALER_TESTING_interpreter_fail (gis->is);
    122         return;
    123       }
    124     }
    125     {
    126       const json_t *expected_description_i18n;
    127 
    128       if (GNUNET_OK !=
    129           TALER_TESTING_get_trait_i18n_description (product_cmd,
    130                                                     &expected_description_i18n))
    131         TALER_TESTING_interpreter_fail (gis->is);
    132       if (1 != json_equal (pgr->details.ok.description_i18n,
    133                            expected_description_i18n))
    134       {
    135         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    136                     "Product description i18n does not match\n");
    137         TALER_TESTING_interpreter_fail (gis->is);
    138         return;
    139       }
    140     }
    141     {
    142       const struct TALER_Amount *expected_price;
    143 
    144       if (GNUNET_OK !=
    145           TALER_TESTING_get_trait_amount (product_cmd,
    146                                           &expected_price))
    147         TALER_TESTING_interpreter_fail (gis->is);
    148       if ((GNUNET_OK !=
    149            TALER_amount_cmp_currency (&pgr->details.ok.price,
    150                                       expected_price)) ||
    151           (0 != TALER_amount_cmp (&pgr->details.ok.price,
    152                                   expected_price)))
    153       {
    154         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    155                     "Product price does not match\n");
    156         TALER_TESTING_interpreter_fail (gis->is);
    157         return;
    158       }
    159     }
    160     {
    161       const bool *expected_allow;
    162       bool have_allow = GNUNET_OK ==
    163                         TALER_TESTING_get_trait_product_unit_allow_fraction (
    164         product_cmd,
    165         &expected_allow);
    166       bool override_allow = (NULL != ue) &&
    167                             ue->have_unit_allow_fraction;
    168 
    169       if (override_allow)
    170       {
    171         if (pgr->details.ok.unit_allow_fraction !=
    172             ue->unit_allow_fraction)
    173         {
    174           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    175                       "Product fractional flag does not match expectation\n");
    176           TALER_TESTING_interpreter_fail (gis->is);
    177           return;
    178         }
    179       }
    180       else if (! have_allow)
    181       {
    182         if (pgr->details.ok.unit_allow_fraction)
    183         {
    184           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    185                       "Product fractional flag unexpected\n");
    186           TALER_TESTING_interpreter_fail (gis->is);
    187           return;
    188         }
    189       }
    190       else
    191       {
    192         if (pgr->details.ok.unit_allow_fraction != *expected_allow)
    193         {
    194           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    195                       "Product fractional flag does not match\n");
    196           TALER_TESTING_interpreter_fail (gis->is);
    197           return;
    198         }
    199         {
    200           const char *expected_unit_total_stock;
    201 
    202           if (GNUNET_OK !=
    203               TALER_TESTING_get_trait_product_unit_total_stock (
    204                 product_cmd,
    205                 &expected_unit_total_stock))
    206             TALER_TESTING_interpreter_fail (gis->is);
    207           else if (0 != strcmp (pgr->details.ok.unit_total_stock,
    208                                 expected_unit_total_stock))
    209           {
    210             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    211                         "Product stock string does not match\n");
    212             TALER_TESTING_interpreter_fail (gis->is);
    213             return;
    214           }
    215         }
    216       }
    217     }
    218     {
    219       const uint32_t *expected_precision;
    220       bool have_precision = GNUNET_OK ==
    221                             TALER_TESTING_get_trait_product_unit_precision_level
    222                             (
    223         product_cmd,
    224         &expected_precision);
    225       bool override_precision = (NULL != ue) &&
    226                                 ue->have_unit_precision_level;
    227 
    228       if (override_precision)
    229       {
    230         if (pgr->details.ok.unit_precision_level !=
    231             ue->unit_precision_level)
    232         {
    233           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    234                       "Product fractional precision does not match expectation\n");
    235           TALER_TESTING_interpreter_fail (gis->is);
    236           return;
    237         }
    238       }
    239       else if (have_precision)
    240       {
    241         if (pgr->details.ok.unit_precision_level != *expected_precision)
    242         {
    243           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    244                       "Product fractional precision does not match\n");
    245           TALER_TESTING_interpreter_fail (gis->is);
    246           return;
    247         }
    248       }
    249       else if (! pgr->details.ok.unit_allow_fraction)
    250       {
    251         if (0 != pgr->details.ok.unit_precision_level)
    252         {
    253           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    254                       "Product fractional precision should be zero when disallowed\n");
    255           TALER_TESTING_interpreter_fail (gis->is);
    256           return;
    257         }
    258       }
    259       else if (pgr->details.ok.unit_precision_level > 8)
    260       {
    261         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    262                     "Product fractional precision exceeds supported range\n");
    263         TALER_TESTING_interpreter_fail (gis->is);
    264         return;
    265       }
    266     }
    267     {
    268       const char *expected_image;
    269 
    270       if (GNUNET_OK !=
    271           TALER_TESTING_get_trait_product_image (product_cmd,
    272                                                  &expected_image))
    273         TALER_TESTING_interpreter_fail (gis->is);
    274       if (0 != strcmp (pgr->details.ok.image,
    275                        expected_image))
    276       {
    277         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    278                     "Product image does not match\n");
    279         TALER_TESTING_interpreter_fail (gis->is);
    280         return;
    281       }
    282     }
    283     {
    284       const json_t *expected_taxes;
    285 
    286       if (GNUNET_OK !=
    287           TALER_TESTING_get_trait_taxes (product_cmd,
    288                                          &expected_taxes))
    289         TALER_TESTING_interpreter_fail (gis->is);
    290       if (1 != json_equal (pgr->details.ok.taxes,
    291                            expected_taxes))
    292       {
    293         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    294                     "Product taxes do not match\n");
    295         TALER_TESTING_interpreter_fail (gis->is);
    296         return;
    297       }
    298     }
    299     {
    300       const char *expected_unit;
    301 
    302       if (GNUNET_OK !=
    303           TALER_TESTING_get_trait_product_unit (product_cmd,
    304                                                 &expected_unit))
    305         TALER_TESTING_interpreter_fail (gis->is);
    306       if (0 != strcmp (pgr->details.ok.unit,
    307                        expected_unit))
    308       {
    309         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    310                     "Product unit does not match\n");
    311         TALER_TESTING_interpreter_fail (gis->is);
    312         return;
    313       }
    314     }
    315     {
    316       const json_t *expected_location;
    317 
    318       if (GNUNET_OK !=
    319           TALER_TESTING_get_trait_address (product_cmd,
    320                                            &expected_location))
    321         TALER_TESTING_interpreter_fail (gis->is);
    322       if (NULL != expected_location)
    323       {
    324         if (1 != json_equal (pgr->details.ok.location,
    325                              expected_location))
    326         {
    327           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    328                       "Product location does not match\n");
    329           TALER_TESTING_interpreter_fail (gis->is);
    330           return;
    331         }
    332       }
    333     }
    334     {
    335       const int64_t *expected_total_stock;
    336 
    337       if (GNUNET_OK !=
    338           TALER_TESTING_get_trait_product_stock (product_cmd,
    339                                                  &expected_total_stock))
    340         TALER_TESTING_interpreter_fail (gis->is);
    341       if (pgr->details.ok.total_stock != *expected_total_stock)
    342       {
    343         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    344                     "Product total stock does not match\n");
    345         TALER_TESTING_interpreter_fail (gis->is);
    346         return;
    347       }
    348     }
    349     {
    350       const struct GNUNET_TIME_Timestamp *expected_next_restock;
    351 
    352       if (GNUNET_OK !=
    353           TALER_TESTING_get_trait_timestamp (product_cmd,
    354                                              0,
    355                                              &expected_next_restock))
    356         TALER_TESTING_interpreter_fail (gis->is);
    357       if (GNUNET_TIME_timestamp_cmp (pgr->details.ok.next_restock,
    358                                      !=,
    359                                      *expected_next_restock))
    360       {
    361         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    362                     "Product next restock does not match\n");
    363         TALER_TESTING_interpreter_fail (gis->is);
    364         return;
    365       }
    366     }
    367     break;
    368   case MHD_HTTP_UNAUTHORIZED:
    369     break;
    370   case MHD_HTTP_NOT_FOUND:
    371     break;
    372   default:
    373     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    374                 "Unhandled HTTP status.\n");
    375   }
    376   TALER_TESTING_interpreter_next (gis->is);
    377 }
    378 
    379 
    380 /**
    381  * Run the "GET product" CMD.
    382  *
    383  *
    384  * @param cls closure.
    385  * @param cmd command being run now.
    386  * @param is interpreter state.
    387  */
    388 static void
    389 get_product_run (void *cls,
    390                  const struct TALER_TESTING_Command *cmd,
    391                  struct TALER_TESTING_Interpreter *is)
    392 {
    393   struct GetProductState *gis = cls;
    394 
    395   gis->is = is;
    396   gis->igh = TALER_MERCHANT_get_private_product_create (
    397     TALER_TESTING_interpreter_get_context (is),
    398     gis->merchant_url,
    399     gis->product_id);
    400   {
    401     enum TALER_ErrorCode ec;
    402 
    403     ec = TALER_MERCHANT_get_private_product_start (
    404       gis->igh,
    405       &get_product_cb,
    406       gis);
    407     GNUNET_assert (TALER_EC_NONE == ec);
    408   }
    409 }
    410 
    411 
    412 /**
    413  * Free the state of a "GET product" CMD, and possibly
    414  * cancel a pending operation thereof.
    415  *
    416  * @param cls closure.
    417  * @param cmd command being run.
    418  */
    419 static void
    420 get_product_cleanup (void *cls,
    421                      const struct TALER_TESTING_Command *cmd)
    422 {
    423   struct GetProductState *gis = cls;
    424 
    425   if (NULL != gis->igh)
    426   {
    427     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    428                 "GET /products/$ID operation did not complete\n");
    429     TALER_MERCHANT_get_private_product_cancel (gis->igh);
    430   }
    431   GNUNET_free (gis);
    432 }
    433 
    434 
    435 struct TALER_TESTING_Command
    436 TALER_TESTING_cmd_merchant_get_product (const char *label,
    437                                         const char *merchant_url,
    438                                         const char *product_id,
    439                                         unsigned int http_status,
    440                                         const char *product_reference)
    441 {
    442   return TALER_TESTING_cmd_merchant_get_product2 (label,
    443                                                   merchant_url,
    444                                                   product_id,
    445                                                   http_status,
    446                                                   product_reference,
    447                                                   NULL);
    448 }
    449 
    450 
    451 struct TALER_TESTING_Command
    452 TALER_TESTING_cmd_merchant_get_product2 (
    453   const char *label,
    454   const char *merchant_url,
    455   const char *product_id,
    456   unsigned int http_status,
    457   const char *product_reference,
    458   const struct TALER_TESTING_ProductUnitExpectations *unit_expectations)
    459 {
    460   struct GetProductState *gis;
    461 
    462   gis = GNUNET_new (struct GetProductState);
    463   gis->merchant_url = merchant_url;
    464   gis->product_id = product_id;
    465   gis->http_status = http_status;
    466   gis->product_reference = product_reference;
    467   gis->unit_expectations = unit_expectations;
    468   {
    469     struct TALER_TESTING_Command cmd = {
    470       .cls = gis,
    471       .label = label,
    472       .run = &get_product_run,
    473       .cleanup = &get_product_cleanup
    474     };
    475 
    476     return cmd;
    477   }
    478 }
    479 
    480 
    481 /* end of testing_api_cmd_get_product.c */