merchant

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

testing_api_cmd_get_product.c (14860B)


      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       {
    116         TALER_TESTING_interpreter_fail (gis->is);
    117         return;
    118       }
    119       if (0 != strcmp (pgr->details.ok.description,
    120                        expected_description))
    121       {
    122         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    123                     "Product description does not match\n");
    124         TALER_TESTING_interpreter_fail (gis->is);
    125         return;
    126       }
    127     }
    128     {
    129       const json_t *expected_description_i18n;
    130 
    131       if (GNUNET_OK !=
    132           TALER_TESTING_get_trait_i18n_description (product_cmd,
    133                                                     &expected_description_i18n))
    134       {
    135         TALER_TESTING_interpreter_fail (gis->is);
    136         return;
    137       }
    138       if (1 != json_equal (pgr->details.ok.description_i18n,
    139                            expected_description_i18n))
    140       {
    141         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    142                     "Product description i18n does not match\n");
    143         TALER_TESTING_interpreter_fail (gis->is);
    144         return;
    145       }
    146     }
    147     {
    148       const struct TALER_Amount *expected_price;
    149 
    150       if (GNUNET_OK !=
    151           TALER_TESTING_get_trait_amount (product_cmd,
    152                                           &expected_price))
    153       {
    154         TALER_TESTING_interpreter_fail (gis->is);
    155         return;
    156       }
    157       if ((GNUNET_OK !=
    158            TALER_amount_cmp_currency (&pgr->details.ok.price,
    159                                       expected_price)) ||
    160           (0 != TALER_amount_cmp (&pgr->details.ok.price,
    161                                   expected_price)))
    162       {
    163         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    164                     "Product price does not match\n");
    165         TALER_TESTING_interpreter_fail (gis->is);
    166         return;
    167       }
    168     }
    169     {
    170       const bool *expected_allow;
    171       bool have_allow = GNUNET_OK ==
    172                         TALER_TESTING_get_trait_product_unit_allow_fraction (
    173         product_cmd,
    174         &expected_allow);
    175       bool override_allow = (NULL != ue) &&
    176                             ue->have_unit_allow_fraction;
    177 
    178       if (override_allow)
    179       {
    180         if (pgr->details.ok.unit_allow_fraction !=
    181             ue->unit_allow_fraction)
    182         {
    183           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    184                       "Product fractional flag does not match expectation\n");
    185           TALER_TESTING_interpreter_fail (gis->is);
    186           return;
    187         }
    188       }
    189       else if (! have_allow)
    190       {
    191         if (pgr->details.ok.unit_allow_fraction)
    192         {
    193           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    194                       "Product fractional flag unexpected\n");
    195           TALER_TESTING_interpreter_fail (gis->is);
    196           return;
    197         }
    198       }
    199       else
    200       {
    201         if (pgr->details.ok.unit_allow_fraction != *expected_allow)
    202         {
    203           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    204                       "Product fractional flag does not match\n");
    205           TALER_TESTING_interpreter_fail (gis->is);
    206           return;
    207         }
    208         {
    209           const char *expected_unit_total_stock;
    210 
    211           if (GNUNET_OK !=
    212               TALER_TESTING_get_trait_product_unit_total_stock (
    213                 product_cmd,
    214                 &expected_unit_total_stock))
    215             TALER_TESTING_interpreter_fail (gis->is);
    216           else if (0 != strcmp (pgr->details.ok.unit_total_stock,
    217                                 expected_unit_total_stock))
    218           {
    219             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    220                         "Product stock string does not match\n");
    221             TALER_TESTING_interpreter_fail (gis->is);
    222             return;
    223           }
    224         }
    225       }
    226     }
    227     {
    228       const uint32_t *expected_precision;
    229       bool have_precision = GNUNET_OK ==
    230                             TALER_TESTING_get_trait_product_unit_precision_level
    231                             (
    232         product_cmd,
    233         &expected_precision);
    234       bool override_precision = (NULL != ue) &&
    235                                 ue->have_unit_precision_level;
    236 
    237       if (override_precision)
    238       {
    239         if (pgr->details.ok.unit_precision_level !=
    240             ue->unit_precision_level)
    241         {
    242           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    243                       "Product fractional precision does not match expectation\n");
    244           TALER_TESTING_interpreter_fail (gis->is);
    245           return;
    246         }
    247       }
    248       else if (have_precision)
    249       {
    250         if (pgr->details.ok.unit_precision_level != *expected_precision)
    251         {
    252           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    253                       "Product fractional precision does not match\n");
    254           TALER_TESTING_interpreter_fail (gis->is);
    255           return;
    256         }
    257       }
    258       else if (! pgr->details.ok.unit_allow_fraction)
    259       {
    260         if (0 != pgr->details.ok.unit_precision_level)
    261         {
    262           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    263                       "Product fractional precision should be zero when disallowed\n");
    264           TALER_TESTING_interpreter_fail (gis->is);
    265           return;
    266         }
    267       }
    268       else if (pgr->details.ok.unit_precision_level > 8)
    269       {
    270         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    271                     "Product fractional precision exceeds supported range\n");
    272         TALER_TESTING_interpreter_fail (gis->is);
    273         return;
    274       }
    275     }
    276     {
    277       const char *expected_image;
    278 
    279       if (GNUNET_OK !=
    280           TALER_TESTING_get_trait_product_image (product_cmd,
    281                                                  &expected_image))
    282       {
    283         TALER_TESTING_interpreter_fail (gis->is);
    284         return;
    285       }
    286       if (0 != strcmp (pgr->details.ok.image,
    287                        expected_image))
    288       {
    289         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    290                     "Product image does not match\n");
    291         TALER_TESTING_interpreter_fail (gis->is);
    292         return;
    293       }
    294     }
    295     {
    296       const json_t *expected_taxes;
    297 
    298       if (GNUNET_OK !=
    299           TALER_TESTING_get_trait_taxes (product_cmd,
    300                                          &expected_taxes))
    301       {
    302         TALER_TESTING_interpreter_fail (gis->is);
    303         return;
    304       }
    305       if (1 != json_equal (pgr->details.ok.taxes,
    306                            expected_taxes))
    307       {
    308         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    309                     "Product taxes do not match\n");
    310         TALER_TESTING_interpreter_fail (gis->is);
    311         return;
    312       }
    313     }
    314     {
    315       const char *expected_unit;
    316 
    317       if (GNUNET_OK !=
    318           TALER_TESTING_get_trait_product_unit (product_cmd,
    319                                                 &expected_unit))
    320       {
    321         TALER_TESTING_interpreter_fail (gis->is);
    322         return;
    323       }
    324       if (0 != strcmp (pgr->details.ok.unit,
    325                        expected_unit))
    326       {
    327         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    328                     "Product unit does not match\n");
    329         TALER_TESTING_interpreter_fail (gis->is);
    330         return;
    331       }
    332     }
    333     {
    334       const json_t *expected_location;
    335 
    336       if (GNUNET_OK !=
    337           TALER_TESTING_get_trait_address (product_cmd,
    338                                            &expected_location))
    339       {
    340         TALER_TESTING_interpreter_fail (gis->is);
    341         return;
    342       }
    343       if (NULL != expected_location)
    344       {
    345         if (1 != json_equal (pgr->details.ok.location,
    346                              expected_location))
    347         {
    348           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    349                       "Product location does not match\n");
    350           TALER_TESTING_interpreter_fail (gis->is);
    351           return;
    352         }
    353       }
    354     }
    355     {
    356       const int64_t *expected_total_stock;
    357 
    358       if (GNUNET_OK !=
    359           TALER_TESTING_get_trait_product_stock (product_cmd,
    360                                                  &expected_total_stock))
    361       {
    362         TALER_TESTING_interpreter_fail (gis->is);
    363         return;
    364       }
    365       if (pgr->details.ok.total_stock != *expected_total_stock)
    366       {
    367         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    368                     "Product total stock does not match\n");
    369         TALER_TESTING_interpreter_fail (gis->is);
    370         return;
    371       }
    372     }
    373     {
    374       const struct GNUNET_TIME_Timestamp *expected_next_restock;
    375 
    376       if (GNUNET_OK !=
    377           TALER_TESTING_get_trait_timestamp (product_cmd,
    378                                              0,
    379                                              &expected_next_restock))
    380       {
    381         TALER_TESTING_interpreter_fail (gis->is);
    382         return;
    383       }
    384       if (GNUNET_TIME_timestamp_cmp (pgr->details.ok.next_restock,
    385                                      !=,
    386                                      *expected_next_restock))
    387       {
    388         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    389                     "Product next restock does not match\n");
    390         TALER_TESTING_interpreter_fail (gis->is);
    391         return;
    392       }
    393     }
    394     break;
    395   case MHD_HTTP_UNAUTHORIZED:
    396     break;
    397   case MHD_HTTP_NOT_FOUND:
    398     break;
    399   default:
    400     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    401                 "Unhandled HTTP status.\n");
    402   }
    403   TALER_TESTING_interpreter_next (gis->is);
    404 }
    405 
    406 
    407 /**
    408  * Run the "GET product" CMD.
    409  *
    410  *
    411  * @param cls closure.
    412  * @param cmd command being run now.
    413  * @param is interpreter state.
    414  */
    415 static void
    416 get_product_run (void *cls,
    417                  const struct TALER_TESTING_Command *cmd,
    418                  struct TALER_TESTING_Interpreter *is)
    419 {
    420   struct GetProductState *gis = cls;
    421 
    422   gis->is = is;
    423   gis->igh = TALER_MERCHANT_get_private_product_create (
    424     TALER_TESTING_interpreter_get_context (is),
    425     gis->merchant_url,
    426     gis->product_id);
    427   {
    428     enum TALER_ErrorCode ec;
    429 
    430     ec = TALER_MERCHANT_get_private_product_start (
    431       gis->igh,
    432       &get_product_cb,
    433       gis);
    434     GNUNET_assert (TALER_EC_NONE == ec);
    435   }
    436 }
    437 
    438 
    439 /**
    440  * Free the state of a "GET product" CMD, and possibly
    441  * cancel a pending operation thereof.
    442  *
    443  * @param cls closure.
    444  * @param cmd command being run.
    445  */
    446 static void
    447 get_product_cleanup (void *cls,
    448                      const struct TALER_TESTING_Command *cmd)
    449 {
    450   struct GetProductState *gis = cls;
    451 
    452   if (NULL != gis->igh)
    453   {
    454     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    455                 "GET /products/$ID operation did not complete\n");
    456     TALER_MERCHANT_get_private_product_cancel (gis->igh);
    457   }
    458   GNUNET_free (gis);
    459 }
    460 
    461 
    462 struct TALER_TESTING_Command
    463 TALER_TESTING_cmd_merchant_get_product (const char *label,
    464                                         const char *merchant_url,
    465                                         const char *product_id,
    466                                         unsigned int http_status,
    467                                         const char *product_reference)
    468 {
    469   return TALER_TESTING_cmd_merchant_get_product2 (label,
    470                                                   merchant_url,
    471                                                   product_id,
    472                                                   http_status,
    473                                                   product_reference,
    474                                                   NULL);
    475 }
    476 
    477 
    478 struct TALER_TESTING_Command
    479 TALER_TESTING_cmd_merchant_get_product2 (
    480   const char *label,
    481   const char *merchant_url,
    482   const char *product_id,
    483   unsigned int http_status,
    484   const char *product_reference,
    485   const struct TALER_TESTING_ProductUnitExpectations *unit_expectations)
    486 {
    487   struct GetProductState *gis;
    488 
    489   gis = GNUNET_new (struct GetProductState);
    490   gis->merchant_url = merchant_url;
    491   gis->product_id = product_id;
    492   gis->http_status = http_status;
    493   gis->product_reference = product_reference;
    494   gis->unit_expectations = unit_expectations;
    495   {
    496     struct TALER_TESTING_Command cmd = {
    497       .cls = gis,
    498       .label = label,
    499       .run = &get_product_run,
    500       .cleanup = &get_product_cleanup
    501     };
    502 
    503     return cmd;
    504   }
    505 }
    506 
    507 
    508 /* end of testing_api_cmd_get_product.c */