merchant

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

testing_api_cmd_get_product.c (14486B)


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