LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_post_products.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 92.0 % 175 161
Test Date: 2026-09-11 17:45:24 Functions: 100.0 % 11 11

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2020 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_post_products.c
      21              :  * @brief command to test POST /products
      22              :  * @author Christian Grothoff
      23              :  */
      24              : #include "platform.h"
      25              : struct PostProductsState;
      26              : #define TALER_MERCHANT_POST_PRIVATE_PRODUCTS_RESULT_CLOSURE struct PostProductsState
      27              : #include <taler/taler_exchange_service.h>
      28              : #include <taler/taler_testing_lib.h>
      29              : #include "taler/taler_merchant_testing_lib.h"
      30              : #include <taler/merchant/post-private-products.h>
      31              : 
      32              : 
      33              : /**
      34              :  * State of a "POST /products" CMD.
      35              :  */
      36              : struct PostProductsState
      37              : {
      38              : 
      39              :   /**
      40              :    * Handle for a "POST /products" request.
      41              :    */
      42              :   struct TALER_MERCHANT_PostPrivateProductsHandle *iph;
      43              : 
      44              :   /**
      45              :    * The interpreter state.
      46              :    */
      47              :   struct TALER_TESTING_Interpreter *is;
      48              : 
      49              :   /**
      50              :    * Base URL of the merchant serving the request.
      51              :    */
      52              :   const char *merchant_url;
      53              : 
      54              :   /**
      55              :    * ID of the product to run POST for.
      56              :    */
      57              :   const char *product_id;
      58              : 
      59              :   /**
      60              :    * description of the product
      61              :    */
      62              :   const char *description;
      63              : 
      64              :   /**
      65              :    * Map from IETF BCP 47 language tags to localized descriptions
      66              :    */
      67              :   json_t *description_i18n;
      68              : 
      69              :   /**
      70              :    * unit in which the product is measured (liters, kilograms, packages, etc.)
      71              :    */
      72              :   const char *unit;
      73              : 
      74              :   /**
      75              :    * the price for one @a unit of the product
      76              :    */
      77              :   struct TALER_Amount price;
      78              : 
      79              :   /**
      80              :    * Array of unit prices (may point to @e price for single-entry usage).
      81              :    */
      82              :   struct TALER_Amount *unit_prices;
      83              : 
      84              :   /**
      85              :    * Number of entries in @e unit_prices.
      86              :    */
      87              :   size_t unit_prices_len;
      88              : 
      89              :   /**
      90              :    * True if @e unit_prices must be freed.
      91              :    */
      92              :   bool owns_unit_prices;
      93              : 
      94              :   /**
      95              :    * True if we should send an explicit unit_price array.
      96              :    */
      97              :   bool use_unit_price_array;
      98              : 
      99              :   /**
     100              :    * base64-encoded product image
     101              :    */
     102              :   char *image;
     103              : 
     104              :   /**
     105              :    * list of taxes paid by the merchant
     106              :    */
     107              :   json_t *taxes;
     108              : 
     109              :   /**
     110              :    * in @e units, -1 to indicate "infinite" (i.e. electronic books)
     111              :    */
     112              :   int64_t total_stock;
     113              : 
     114              :   /**
     115              :    * Fractional stock component when fractional quantities are enabled.
     116              :    */
     117              :   uint32_t total_stock_frac;
     118              : 
     119              :   /**
     120              :    * Fractional precision level associated with fractional quantities.
     121              :    */
     122              :   uint32_t unit_precision_level;
     123              : 
     124              :   /**
     125              :    * whether fractional quantities are allowed for this product.
     126              :    */
     127              :   bool unit_allow_fraction;
     128              : 
     129              :   /**
     130              :    * Cached string representation of the stock level.
     131              :    */
     132              :   char unit_total_stock[64];
     133              : 
     134              :   /**
     135              :    * set to true if we should use the extended fractional API.
     136              :    */
     137              :   bool use_fractional;
     138              : 
     139              :   /**
     140              :    * where the product is in stock
     141              :    */
     142              :   json_t *address;
     143              : 
     144              :   /**
     145              :    * Minimum age requirement to use for the product.
     146              :    */
     147              :   unsigned int minimum_age;
     148              : 
     149              :   /**
     150              :    * when the next restocking is expected to happen, 0 for unknown,
     151              :    */
     152              :   struct GNUNET_TIME_Timestamp next_restock;
     153              : 
     154              :   /**
     155              :    * Categories array length.
     156              :    */
     157              :   unsigned int num_cats;
     158              : 
     159              :   /**
     160              :    * Categories array.
     161              :    */
     162              :   uint64_t *cats;
     163              : 
     164              :   /**
     165              :    * Expected HTTP response code.
     166              :    */
     167              :   unsigned int http_status;
     168              : 
     169              : };
     170              : 
     171              : static void
     172           42 : post_products_update_unit_total_stock (struct PostProductsState *pps)
     173              : {
     174           42 :   if (-1 == pps->total_stock)
     175              :   {
     176            2 :     pps->total_stock = INT64_MAX;
     177            2 :     pps->total_stock_frac = INT32_MAX;
     178              :   }
     179           40 :   else if (! pps->unit_allow_fraction)
     180              :   {
     181           38 :     pps->total_stock_frac = 0;
     182              :   }
     183           42 :   TALER_MERCHANT_format_stock_string ((uint64_t) pps->total_stock,
     184              :                                       pps->total_stock_frac,
     185           42 :                                       pps->unit_total_stock,
     186              :                                       sizeof (pps->unit_total_stock));
     187           42 : }
     188              : 
     189              : 
     190              : static uint32_t
     191           40 : default_precision_from_unit (const char *unit)
     192              : {
     193              :   struct PrecisionRule
     194              :   {
     195              :     const char *unit;
     196              :     uint32_t precision;
     197              :   };
     198              :   static const struct PrecisionRule rules[] = {
     199              :     { "WeightUnitMg", 0 },
     200              :     { "SizeUnitMm", 0 },
     201              :     { "WeightUnitG", 1 },
     202              :     { "SizeUnitCm", 1 },
     203              :     { "SurfaceUnitMm2", 1 },
     204              :     { "VolumeUnitMm3", 1 },
     205              :     { "WeightUnitOunce", 2 },
     206              :     { "SizeUnitInch", 2 },
     207              :     { "SurfaceUnitCm2", 2 },
     208              :     { "VolumeUnitOunce", 2 },
     209              :     { "VolumeUnitInch3", 2 },
     210              :     { "TimeUnitHour", 2 },
     211              :     { "TimeUnitMonth", 2 },
     212              :     { "WeightUnitTon", 3 },
     213              :     { "WeightUnitKg", 3 },
     214              :     { "WeightUnitPound", 3 },
     215              :     { "SizeUnitM", 3 },
     216              :     { "SizeUnitDm", 3 },
     217              :     { "SizeUnitFoot", 3 },
     218              :     { "SurfaceUnitDm2", 3 },
     219              :     { "SurfaceUnitFoot2", 3 },
     220              :     { "VolumeUnitCm3", 3 },
     221              :     { "VolumeUnitLitre", 3 },
     222              :     { "VolumeUnitGallon", 3 },
     223              :     { "TimeUnitSecond", 3 },
     224              :     { "TimeUnitMinute", 3 },
     225              :     { "TimeUnitDay", 3 },
     226              :     { "TimeUnitWeek", 3 },
     227              :     { "SurfaceUnitM2", 4 },
     228              :     { "SurfaceUnitInch2", 4 },
     229              :     { "TimeUnitYear", 4 },
     230              :     { "VolumeUnitDm3", 5 },
     231              :     { "VolumeUnitFoot3", 5 },
     232              :     { "VolumeUnitM3", 6 }
     233              :   };
     234              : 
     235           40 :   const size_t rules_len = sizeof (rules) / sizeof (rules[0]);
     236           40 :   if (NULL == unit)
     237            0 :     return 0;
     238              : 
     239         1400 :   for (size_t i = 0; i<rules_len; i++)
     240         1360 :     if (0 == strcmp (unit,
     241         1360 :                      rules[i].unit))
     242            0 :       return rules[i].precision;
     243           40 :   return 0;
     244              : }
     245              : 
     246              : 
     247              : /**
     248              :  * Callback for a POST /products operation.
     249              :  *
     250              :  * @param cls closure for this function
     251              :  * @param ppr response being processed
     252              :  */
     253              : static void
     254           40 : post_products_cb (struct PostProductsState *pis,
     255              :                   const struct TALER_MERCHANT_PostPrivateProductsResponse *ppr)
     256              : {
     257              : 
     258           40 :   pis->iph = NULL;
     259           40 :   if (pis->http_status != ppr->hr.http_status)
     260              :   {
     261            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     262              :                 "Unexpected response code %u (%d) to command %s\n",
     263              :                 ppr->hr.http_status,
     264              :                 (int) ppr->hr.ec,
     265              :                 TALER_TESTING_interpreter_get_current_label (pis->is));
     266            0 :     TALER_TESTING_interpreter_fail (pis->is);
     267            0 :     return;
     268              :   }
     269           40 :   switch (ppr->hr.http_status)
     270              :   {
     271           32 :   case MHD_HTTP_NO_CONTENT:
     272           32 :     break;
     273            4 :   case MHD_HTTP_BAD_REQUEST:
     274            4 :     break;
     275            0 :   case MHD_HTTP_UNAUTHORIZED:
     276            0 :     break;
     277            0 :   case MHD_HTTP_FORBIDDEN:
     278            0 :     break;
     279            2 :   case MHD_HTTP_NOT_FOUND:
     280            2 :     break;
     281            2 :   case MHD_HTTP_CONFLICT:
     282            2 :     break;
     283            0 :   default:
     284            0 :     GNUNET_break (0);
     285            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     286              :                 "Unhandled HTTP status %u for POST /products.\n",
     287              :                 ppr->hr.http_status);
     288              :   }
     289           40 :   TALER_TESTING_interpreter_next (pis->is);
     290              : }
     291              : 
     292              : 
     293              : /**
     294              :  * Run the "POST /products" CMD.
     295              :  *
     296              :  *
     297              :  * @param cls closure.
     298              :  * @param cmd command being run now.
     299              :  * @param is interpreter state.
     300              :  */
     301              : static void
     302           40 : post_products_run (void *cls,
     303              :                    const struct TALER_TESTING_Command *cmd,
     304              :                    struct TALER_TESTING_Interpreter *is)
     305              : {
     306           40 :   struct PostProductsState *pis = cls;
     307              : 
     308           40 :   pis->is = is;
     309           40 :   pis->iph = TALER_MERCHANT_post_private_products_create (
     310              :     TALER_TESTING_interpreter_get_context (is),
     311              :     pis->merchant_url,
     312              :     pis->product_id,
     313              :     pis->description,
     314              :     pis->unit,
     315           40 :     pis->unit_prices_len,
     316           40 :     pis->unit_prices);
     317           40 :   GNUNET_assert (GNUNET_OK ==
     318              :                  TALER_MERCHANT_post_private_products_set_options (
     319              :                    pis->iph,
     320              :                    TALER_MERCHANT_post_private_products_option_description_i18n (
     321              :                      pis->description_i18n),
     322              :                    TALER_MERCHANT_post_private_products_option_total_stock_val (
     323              :                      pis->total_stock),
     324              :                    TALER_MERCHANT_post_private_products_option_taxes (
     325              :                      pis->taxes),
     326              :                    TALER_MERCHANT_post_private_products_option_image (
     327              :                      pis->image),
     328              :                    TALER_MERCHANT_post_private_products_option_address (
     329              :                      pis->address),
     330              :                    TALER_MERCHANT_post_private_products_option_next_restock (
     331              :                      pis->next_restock),
     332              :                    TALER_MERCHANT_post_private_products_option_minimum_age (
     333              :                      pis->minimum_age),
     334              :                    TALER_MERCHANT_post_private_products_option_categories (
     335              :                      pis->num_cats,
     336              :                      pis->cats)));
     337           40 :   if (pis->use_fractional)
     338              :   {
     339            4 :     GNUNET_assert (
     340              :       GNUNET_OK ==
     341              :       TALER_MERCHANT_post_private_products_set_options (
     342              :         pis->iph,
     343              :         TALER_MERCHANT_post_private_products_option_total_stock_frac (
     344              :           pis->total_stock_frac),
     345              :         TALER_MERCHANT_post_private_products_option_unit_allow_fraction (
     346              :           pis->unit_allow_fraction),
     347              :         TALER_MERCHANT_post_private_products_option_unit_precision_level (
     348              :           pis->unit_precision_level)));
     349              :   }
     350              :   {
     351              :     enum TALER_ErrorCode ec;
     352              : 
     353           40 :     ec = TALER_MERCHANT_post_private_products_start (
     354              :       pis->iph,
     355              :       &post_products_cb,
     356              :       pis);
     357           40 :     GNUNET_assert (TALER_EC_NONE == ec);
     358              :   }
     359           40 : }
     360              : 
     361              : 
     362              : /**
     363              :  * Offers information from the POST /products CMD state to other
     364              :  * commands.
     365              :  *
     366              :  * @param cls closure
     367              :  * @param[out] ret result (could be anything)
     368              :  * @param trait name of the trait
     369              :  * @param index index number of the object to extract.
     370              :  * @return #GNUNET_OK on success
     371              :  */
     372              : static enum GNUNET_GenericReturnValue
     373          144 : post_products_traits (void *cls,
     374              :                       const void **ret,
     375              :                       const char *trait,
     376              :                       unsigned int index)
     377              : {
     378          144 :   struct PostProductsState *pps = cls;
     379              :   struct TALER_TESTING_Trait traits[] = {
     380          144 :     TALER_TESTING_make_trait_product_description (pps->description),
     381          144 :     TALER_TESTING_make_trait_i18n_description (pps->description_i18n),
     382          144 :     TALER_TESTING_make_trait_product_unit (pps->unit),
     383          144 :     TALER_TESTING_make_trait_amount (&pps->price),
     384          144 :     TALER_TESTING_make_trait_product_image (pps->image),
     385          144 :     TALER_TESTING_make_trait_taxes (pps->taxes),
     386          144 :     TALER_TESTING_make_trait_product_stock (&pps->total_stock),
     387          144 :     TALER_TESTING_make_trait_product_unit_total_stock (
     388          144 :       pps->unit_total_stock),
     389          144 :     TALER_TESTING_make_trait_product_unit_precision_level (
     390          144 :       &pps->unit_precision_level),
     391          144 :     TALER_TESTING_make_trait_product_unit_allow_fraction (
     392          144 :       &pps->unit_allow_fraction),
     393          144 :     TALER_TESTING_make_trait_address (pps->address),
     394          144 :     TALER_TESTING_make_trait_timestamp (0,
     395          144 :                                         &pps->next_restock),
     396          144 :     TALER_TESTING_make_trait_product_id (pps->product_id),
     397          144 :     TALER_TESTING_trait_end (),
     398              :   };
     399              : 
     400          144 :   return TALER_TESTING_get_trait (traits,
     401              :                                   ret,
     402              :                                   trait,
     403              :                                   index);
     404              : }
     405              : 
     406              : 
     407              : /**
     408              :  * Free the state of a "POST product" CMD, and possibly
     409              :  * cancel a pending operation thereof.
     410              :  *
     411              :  * @param cls closure.
     412              :  * @param cmd command being run.
     413              :  */
     414              : static void
     415           40 : post_products_cleanup (void *cls,
     416              :                        const struct TALER_TESTING_Command *cmd)
     417              : {
     418           40 :   struct PostProductsState *pis = cls;
     419              : 
     420           40 :   if (NULL != pis->iph)
     421              :   {
     422            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     423              :                 "POST /products operation did not complete\n");
     424            0 :     TALER_MERCHANT_post_private_products_cancel (pis->iph);
     425              :   }
     426           40 :   if (pis->owns_unit_prices)
     427            6 :     GNUNET_free (pis->unit_prices);
     428           40 :   json_decref (pis->description_i18n);
     429           40 :   GNUNET_free (pis->image);
     430           40 :   json_decref (pis->taxes);
     431           40 :   json_decref (pis->address);
     432           40 :   GNUNET_free (pis->cats);
     433           40 :   GNUNET_free (pis);
     434           40 : }
     435              : 
     436              : 
     437              : struct TALER_TESTING_Command
     438           34 : TALER_TESTING_cmd_merchant_post_products2 (
     439              :   const char *label,
     440              :   const char *merchant_url,
     441              :   const char *product_id,
     442              :   const char *description,
     443              :   json_t *description_i18n,
     444              :   const char *unit,
     445              :   const char *price,
     446              :   const char *image,
     447              :   json_t *taxes,
     448              :   int64_t total_stock,
     449              :   uint32_t minimum_age,
     450              :   json_t *address,
     451              :   struct GNUNET_TIME_Timestamp next_restock,
     452              :   unsigned int http_status)
     453              : {
     454              :   struct PostProductsState *pis;
     455              : 
     456           34 :   GNUNET_assert ((NULL == taxes) ||
     457              :                  json_is_array (taxes));
     458           34 :   GNUNET_assert ((NULL == description_i18n) ||
     459              :                  json_is_object (description_i18n));
     460           34 :   pis = GNUNET_new (struct PostProductsState);
     461           34 :   pis->merchant_url = merchant_url;
     462           34 :   pis->product_id = product_id;
     463           34 :   pis->http_status = http_status;
     464           34 :   pis->description = description;
     465           34 :   pis->description_i18n = description_i18n; /* ownership taken */
     466           34 :   pis->unit = unit;
     467           34 :   pis->unit_precision_level = default_precision_from_unit (unit);
     468           34 :   GNUNET_assert (GNUNET_OK ==
     469              :                  TALER_string_to_amount (price,
     470              :                                          &pis->price));
     471           34 :   pis->unit_prices = &pis->price;
     472           34 :   pis->unit_prices_len = 1;
     473           34 :   pis->owns_unit_prices = false;
     474           34 :   pis->use_unit_price_array = false;
     475           34 :   pis->image = GNUNET_strdup (image);
     476           34 :   pis->taxes = taxes; /* ownership taken */
     477           34 :   pis->total_stock = total_stock;
     478           34 :   pis->total_stock_frac = 0;
     479           34 :   pis->unit_allow_fraction = false;
     480           34 :   pis->use_fractional = false;
     481           34 :   pis->minimum_age = minimum_age;
     482           34 :   pis->address = address; /* ownership taken */
     483           34 :   pis->next_restock = next_restock;
     484           34 :   pis->num_cats = 0;
     485           34 :   pis->cats = NULL;
     486           34 :   post_products_update_unit_total_stock (pis);
     487              :   {
     488           34 :     struct TALER_TESTING_Command cmd = {
     489              :       .cls = pis,
     490              :       .label = label,
     491              :       .run = &post_products_run,
     492              :       .cleanup = &post_products_cleanup,
     493              :       .traits = &post_products_traits
     494              :     };
     495              : 
     496           34 :     return cmd;
     497              :   }
     498              : }
     499              : 
     500              : 
     501              : struct TALER_TESTING_Command
     502            2 : TALER_TESTING_cmd_merchant_post_products3 (
     503              :   const char *label,
     504              :   const char *merchant_url,
     505              :   const char *product_id,
     506              :   const char *description,
     507              :   json_t *description_i18n,
     508              :   const char *unit,
     509              :   const char *price,
     510              :   const char *image,
     511              :   json_t *taxes,
     512              :   int64_t total_stock,
     513              :   uint32_t total_stock_frac,
     514              :   bool unit_allow_fraction,
     515              :   uint32_t minimum_age,
     516              :   json_t *address,
     517              :   struct GNUNET_TIME_Timestamp next_restock,
     518              :   unsigned int http_status)
     519              : {
     520              :   struct TALER_TESTING_Command cmd;
     521              : 
     522            2 :   cmd = TALER_TESTING_cmd_merchant_post_products2 (label,
     523              :                                                    merchant_url,
     524              :                                                    product_id,
     525              :                                                    description,
     526              :                                                    description_i18n,
     527              :                                                    unit,
     528              :                                                    price,
     529              :                                                    image,
     530              :                                                    taxes,
     531              :                                                    total_stock,
     532              :                                                    minimum_age,
     533              :                                                    address,
     534              :                                                    next_restock,
     535              :                                                    http_status);
     536              :   {
     537            2 :     struct PostProductsState *pis = cmd.cls;
     538              : 
     539            2 :     pis->total_stock_frac = total_stock_frac;
     540            2 :     pis->unit_allow_fraction = unit_allow_fraction;
     541            2 :     pis->use_fractional = true;
     542            2 :     post_products_update_unit_total_stock (pis);
     543              :   }
     544            2 :   return cmd;
     545              : }
     546              : 
     547              : 
     548              : struct TALER_TESTING_Command
     549            6 : TALER_TESTING_cmd_merchant_post_products_with_unit_prices (
     550              :   const char *label,
     551              :   const char *merchant_url,
     552              :   const char *product_id,
     553              :   const char *description,
     554              :   const char *unit,
     555              :   const char *const *unit_prices,
     556              :   size_t unit_prices_len,
     557              :   unsigned int http_status)
     558              : {
     559              :   struct PostProductsState *pis;
     560              : 
     561            6 :   GNUNET_assert (0 < unit_prices_len);
     562            6 :   GNUNET_assert (NULL != unit_prices);
     563            6 :   pis = GNUNET_new (struct PostProductsState);
     564            6 :   pis->merchant_url = merchant_url;
     565            6 :   pis->product_id = product_id;
     566            6 :   pis->http_status = http_status;
     567            6 :   pis->description = description;
     568            6 :   pis->description_i18n = json_pack ("{s:s}", "en", description);
     569            6 :   pis->unit = unit;
     570            6 :   pis->unit_precision_level = default_precision_from_unit (unit);
     571            6 :   pis->unit_prices = GNUNET_new_array (unit_prices_len,
     572              :                                        struct TALER_Amount);
     573            6 :   pis->unit_prices_len = unit_prices_len;
     574            6 :   pis->owns_unit_prices = true;
     575            6 :   pis->use_unit_price_array = true;
     576           18 :   for (size_t i = 0; i < unit_prices_len; i++)
     577           12 :     GNUNET_assert (GNUNET_OK ==
     578              :                    TALER_string_to_amount (unit_prices[i],
     579              :                                            &pis->unit_prices[i]));
     580            6 :   pis->price = pis->unit_prices[0];
     581            6 :   pis->image = GNUNET_strdup ("");
     582            6 :   pis->taxes = json_array ();
     583            6 :   pis->total_stock = 4;
     584            6 :   pis->total_stock_frac = 0;
     585            6 :   pis->unit_allow_fraction = false;
     586            6 :   pis->use_fractional = false;
     587            6 :   pis->minimum_age = 0;
     588            6 :   pis->address = json_pack ("{s:s}", "street", "my street");
     589            6 :   pis->next_restock = GNUNET_TIME_UNIT_ZERO_TS;
     590            6 :   pis->num_cats = 0;
     591            6 :   pis->cats = NULL;
     592            6 :   post_products_update_unit_total_stock (pis);
     593              :   {
     594            6 :     struct TALER_TESTING_Command cmd = {
     595              :       .cls = pis,
     596              :       .label = label,
     597              :       .run = &post_products_run,
     598              :       .cleanup = &post_products_cleanup,
     599              :       .traits = &post_products_traits
     600              :     };
     601              : 
     602            6 :     return cmd;
     603              :   }
     604              : }
     605              : 
     606              : 
     607              : struct TALER_TESTING_Command
     608           12 : TALER_TESTING_cmd_merchant_post_products (
     609              :   const char *label,
     610              :   const char *merchant_url,
     611              :   const char *product_id,
     612              :   const char *description,
     613              :   const char *price,
     614              :   unsigned int http_status)
     615              : {
     616           12 :   return TALER_TESTING_cmd_merchant_post_products2 (
     617              :     label,
     618              :     merchant_url,
     619              :     product_id,
     620              :     description,
     621              :     json_pack ("{s:s}", "en", description),
     622              :     "test-unit",
     623              :     price,
     624              :     "",
     625              :     json_array (),
     626              :     4, /* total stock */
     627              :     0, /* minimum age */
     628              :     json_pack ("{s:s}", "street", "my street"),
     629           12 :     GNUNET_TIME_UNIT_ZERO_TS,
     630              :     http_status);
     631              : }
     632              : 
     633              : 
     634              : struct TALER_TESTING_Command
     635           10 : TALER_TESTING_cmd_merchant_post_products_with_categories (
     636              :   const char *label,
     637              :   const char *merchant_url,
     638              :   const char *product_id,
     639              :   const char *description,
     640              :   const char *unit,
     641              :   const char *price,
     642              :   unsigned int num_cats,
     643              :   const uint64_t *cats,
     644              :   bool unit_allow_fraction,
     645              :   uint32_t unit_precision_level,
     646              :   unsigned int http_status)
     647              : {
     648              :   struct TALER_TESTING_Command cmd;
     649              : 
     650           10 :   cmd = TALER_TESTING_cmd_merchant_post_products2 (
     651              :     label,
     652              :     merchant_url,
     653              :     product_id,
     654              :     description,
     655              :     json_pack ("{s:s}", "en", description),
     656              :     unit,
     657              :     price,
     658              :     "",
     659              :     json_array (),
     660              :     4, /* total stock */
     661              :     0, /* minimum age */
     662              :     json_pack ("{s:s}", "street", "my street"),
     663           10 :     GNUNET_TIME_UNIT_ZERO_TS,
     664              :     http_status);
     665              :   {
     666           10 :     struct PostProductsState *pis = cmd.cls;
     667              : 
     668           10 :     pis->num_cats = num_cats;
     669           10 :     if (0 < num_cats)
     670              :     {
     671           10 :       pis->cats = GNUNET_new_array (num_cats,
     672              :                                     uint64_t);
     673           20 :       for (unsigned int i = 0; i < num_cats; i++)
     674           10 :         pis->cats[i] = cats[i];
     675              :     }
     676           10 :     pis->unit_allow_fraction = unit_allow_fraction;
     677           10 :     pis->unit_precision_level = unit_precision_level;
     678           10 :     if (unit_allow_fraction)
     679            2 :       pis->use_fractional = true;
     680              :   }
     681           10 :   return cmd;
     682              : }
     683              : 
     684              : 
     685              : /* end of testing_api_cmd_post_products.c */
        

Generated by: LCOV version 2.0-1