From f4b19c002aa3ef8054729f7e61254232d575b7a6 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 24 Apr 2020 17:53:31 +0200 Subject: toward stesting --- src/backend/taler-merchant-httpd.c | 8 +- src/include/taler_merchant_service.h | 30 +-- src/include/taler_merchant_testing_lib.h | 102 ++++++++- src/lib/merchant_api_delete_product.c | 15 +- src/lib/merchant_api_get_product.c | 15 +- src/lib/merchant_api_get_products.c | 21 +- src/lib/merchant_api_lock_product.c | 15 +- src/lib/merchant_api_patch_product.c | 15 +- src/lib/merchant_api_post_products.c | 21 +- src/testing/Makefile.am | 5 + src/testing/testing_api_cmd_delete_product.c | 182 ++++++++++++++++ src/testing/testing_api_cmd_get_product.c | 224 +++++++++++++++++++ src/testing/testing_api_cmd_get_products.c | 181 +++++++++++++++ src/testing/testing_api_cmd_lock_product.c | 200 +++++++++++++++++ src/testing/testing_api_cmd_patch_product.c | 283 ++++++++++++++++++++++++ src/testing/testing_api_cmd_post_products.c | 315 +++++++++++++++++++++++++++ 16 files changed, 1514 insertions(+), 118 deletions(-) create mode 100644 src/testing/testing_api_cmd_delete_product.c create mode 100644 src/testing/testing_api_cmd_get_product.c create mode 100644 src/testing/testing_api_cmd_get_products.c create mode 100644 src/testing/testing_api_cmd_lock_product.c create mode 100644 src/testing/testing_api_cmd_patch_product.c create mode 100644 src/testing/testing_api_cmd_post_products.c diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 4efa6ad2..25c7bab4 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -955,8 +955,8 @@ url_handler (void *cls, handlers = public_handlers; } } - if (strcmp (url, - "")) + if (0 == strcmp (url, + "")) url = "/"; /* code below does not like empty string */ { @@ -1010,8 +1010,8 @@ url_handler (void *cls, if ( (NULL == infix_url) ^ (GNUNET_NO == rh->have_id_segment) ) continue; /* infix existence missmatch */ - if ( (NULL == suffix_url) - ^ (NULL != rh->url_suffix) ) + if ( ( (NULL == suffix_url) + ^ (NULL == rh->url_suffix) ) ) continue; /* suffix existence missmatch */ if ( (NULL != suffix_url) && ( (suffix_strlen != strlen (rh->url_suffix)) || diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h index 42ea1fa7..435f65d2 100644 --- a/src/include/taler_merchant_service.h +++ b/src/include/taler_merchant_service.h @@ -421,7 +421,7 @@ typedef void /** - * Setup an new instance in the backend. + * Modify an existing instance in the backend. * * @param ctx the context * @param backend_url HTTP base URL for the backend @@ -722,7 +722,7 @@ struct TALER_MERCHANT_InventoryEntry typedef void (*TALER_MERCHANT_ProductsGetCallback)( void *cls, - struct TALER_MERCHANT_HttpResponse *hr, + const struct TALER_MERCHANT_HttpResponse *hr, unsigned int products_length, const struct TALER_MERCHANT_InventoryEntry products[]); @@ -732,8 +732,6 @@ typedef void * * @param ctx the context * @param backend_url HTTP base URL for the backend - * @param instance_id instance to query about its products, - * NULL to query the default instance * @param cb function to call with the backend's inventory information * @param cb_cls closure for @a cb * @return the request handle; NULL upon error @@ -742,7 +740,6 @@ struct TALER_MERCHANT_ProductsGetHandle * TALER_MERCHANT_products_get ( struct GNUNET_CURL_Context *ctx, const char *backend_url, - const char *instance_id, TALER_MERCHANT_ProductsGetCallback cb, void *cb_cls); @@ -793,7 +790,7 @@ struct TALER_MERCHANT_ProductGetHandle; typedef void (*TALER_MERCHANT_ProductGetCallback)( void *cls, - struct TALER_MERCHANT_HttpResponse *hr, + const struct TALER_MERCHANT_HttpResponse *hr, const char *description, const json_t *description_i18n, const char *unit, @@ -813,8 +810,6 @@ typedef void * * @param ctx the context * @param backend_url HTTP base URL for the backend - * @param instance_id instance to query about its products, - * NULL to query the default instance * @param product_id identifier of the product to inquire about * @param cb function to call with the backend's product information * @param cb_cls closure for @a cb @@ -824,7 +819,6 @@ struct TALER_MERCHANT_ProductGetHandle * TALER_MERCHANT_product_get ( struct GNUNET_CURL_Context *ctx, const char *backend_url, - const char *instance_id, const char *product_id, TALER_MERCHANT_ProductGetCallback cb, void *cb_cls); @@ -864,8 +858,6 @@ typedef void * * @param ctx the context * @param backend_url HTTP base URL for the backend - * @param instance_id instance to add a product to, - * NULL to query the default instance * @param product_id identifier to use for the product * @param description description of the product * @param description_i18n Map from IETF BCP 47 language tags to localized descriptions @@ -888,7 +880,6 @@ struct TALER_MERCHANT_ProductsPostHandle * TALER_MERCHANT_products_post ( struct GNUNET_CURL_Context *ctx, const char *backend_url, - const char *instance_id, const char *product_id, const char *description, const json_t *description_i18n, @@ -928,7 +919,7 @@ struct TALER_MERCHANT_ProductPatchHandle; typedef void (*TALER_MERCHANT_ProductPatchCallback)( void *cls, - struct TALER_MERCHANT_HttpResponse *hr); + const struct TALER_MERCHANT_HttpResponse *hr); /** @@ -937,8 +928,6 @@ typedef void * * @param ctx the context * @param backend_url HTTP base URL for the backend - * @param instance_id instance to add a product to, - * NULL to query the default instance * @param product_id identifier to use for the product; the product must exist, * or the transaction will fail with a #MHD_HTTP_NOT_FOUND * HTTP status code @@ -966,7 +955,6 @@ struct TALER_MERCHANT_ProductPatchHandle * TALER_MERCHANT_product_patch ( struct GNUNET_CURL_Context *ctx, const char *backend_url, - const char *instance_id, const char *product_id, const char *description, const json_t *description_i18n, @@ -1007,7 +995,7 @@ struct TALER_MERCHANT_ProductLockHandle; typedef void (*TALER_MERCHANT_ProductLockCallback)( void *cls, - struct TALER_MERCHANT_HttpResponse *hr); + const struct TALER_MERCHANT_HttpResponse *hr); /** @@ -1016,8 +1004,6 @@ typedef void * * @param ctx the context * @param backend_url HTTP base URL for the backend - * @param instance_id instance to query about its products, - * NULL to query the default instance * @param product_id identifier of the product * @param uuid UUID that identifies the client holding the lock * @param duration how long should the lock be held @@ -1030,7 +1016,6 @@ struct TALER_MERCHANT_ProductLockHandle * TALER_MERCHANT_product_lock ( struct GNUNET_CURL_Context *ctx, const char *backend_url, - const char *instance_id, const char *product_id, const struct GNUNET_Uuid *uuid, struct GNUNET_TIME_Relative duration, @@ -1065,7 +1050,7 @@ struct TALER_MERCHANT_ProductDeleteHandle; typedef void (*TALER_MERCHANT_ProductDeleteCallback)( void *cls, - struct TALER_MERCHANT_HttpResponse *hr); + const struct TALER_MERCHANT_HttpResponse *hr); /** @@ -1074,8 +1059,6 @@ typedef void * * @param ctx the context * @param backend_url HTTP base URL for the backend - * @param instance_id instance to query about its products, - * NULL to query the default instance * @param product_id identifier of the product * @param cb function to call with the backend's deletion status * @param cb_cls closure for @a cb @@ -1085,7 +1068,6 @@ struct TALER_MERCHANT_ProductDeleteHandle * TALER_MERCHANT_product_delete ( struct GNUNET_CURL_Context *ctx, const char *backend_url, - const char *instance_id, const char *product_id, TALER_MERCHANT_ProductDeleteCallback cb, void *cb_cls); diff --git a/src/include/taler_merchant_testing_lib.h b/src/include/taler_merchant_testing_lib.h index 95f945c9..d9d3c8e1 100644 --- a/src/include/taler_merchant_testing_lib.h +++ b/src/include/taler_merchant_testing_lib.h @@ -256,8 +256,6 @@ TALER_TESTING_cmd_merchant_delete_instance (const char *label, * @param label command label. * @param merchant_url base URL of the merchant serving the * POST /products request. - * @param instance_id instance to add a product to, - * NULL to query the default instance * @param product_id the ID of the product to query * @param description description of the product * @param description_i18n Map from IETF BCP 47 language tags to localized descriptions @@ -280,7 +278,6 @@ TALER_TESTING_cmd_merchant_post_products2 ( const char *label, const char *merchant_url, const char *product_id, - const char *instance_id, const char *description, json_t *description_i18n, const char *unit, @@ -299,8 +296,6 @@ TALER_TESTING_cmd_merchant_post_products2 ( * @param label command label. * @param merchant_url base URL of the merchant serving the * POST /products request. - * @param instance_id instance to add a product to, - * NULL to query the default instance * @param product_id the ID of the product to create * @param description name of the product * @param price price of the product @@ -310,13 +305,108 @@ TALER_TESTING_cmd_merchant_post_products2 ( struct TALER_TESTING_Command TALER_TESTING_cmd_merchant_post_products (const char *label, const char *merchant_url, - const char *instance_id, const char *product_id, const char *description, const char *price, unsigned int http_status); +/** + * Define a "PATCH /products/$ID" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * PATCH /product request. + * @param product_id the ID of the product to query + * @param description description of the product + * @param description_i18n Map from IETF BCP 47 language tags to localized descriptions + * @param unit unit in which the product is measured (liters, kilograms, packages, etc.) + * @param price the price for one @a unit of the product, zero is used to imply that + * this product is not sold separately or that the price is not fixed and + * must be supplied by the front-end. If non-zero, price must include + * applicable taxes. + * @param image base64-encoded product image + * @param taxes list of taxes paid by the merchant + * @param total_stocked in @a units, -1 to indicate "infinite" (i.e. electronic books) + * @param total_lost in @a units, must be larger than previous values, and may + * not exceed total_stocked minus total_sold; if it does, the transaction + * will fail with a #MHD_HTTP_CONFLICT HTTP status code + * @param address where the product is in stock + * @param next_restock when the next restocking is expected to happen, 0 for unknown, + * #GNUNET_TIME_UNIT_FOREVER_ABS for 'never'. + * @param http_status expected HTTP response code. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_patch_product ( + const char *label, + const char *merchant_url, + const char *product_id, + const char *description, + json_t *description_i18n, + const char *unit, + const struct TALER_Amount *price, + json_t *image, + json_t *taxes, + int64_t total_stocked, + uint64_t total_lost, + json_t *address, + struct GNUNET_TIME_Absolute next_restock, + unsigned int http_status); + + +/** + * Define a "GET /products" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * GET /products request. + * @param http_status expected HTTP response code. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_get_products (const char *label, + const char *merchant_url, + unsigned int http_status); + + +/** + * Define a "GET product" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * GET /products/$ID request. + * @param product_id the ID of the product to query + * @param http_status expected HTTP response code. + * @param product_reference reference to a "POST /products" or "PATCH /products/$ID" CMD + * that will provide what we expect the backend to return to us + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_get_product (const char *label, + const char *merchant_url, + const char *product_id, + unsigned int http_status, + const char *product_reference); + + +/** + * Define a "DELETE product" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * DELETE /products/$ID request. + * @param product_id the ID of the product to query + * @param http_status expected HTTP response code. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_delete_product (const char *label, + const char *merchant_url, + const char *product_id, + unsigned int http_status); + + /* ******************** OLD ******************* */ /** diff --git a/src/lib/merchant_api_delete_product.c b/src/lib/merchant_api_delete_product.c index e00f0a34..e406581c 100644 --- a/src/lib/merchant_api_delete_product.c +++ b/src/lib/merchant_api_delete_product.c @@ -121,8 +121,6 @@ handle_delete_product_finished (void *cls, * * @param ctx the context * @param backend_url HTTP base URL for the backend - * @param instance_id instance to query about its products, - * NULL to query the default instance * @param product_id identifier of the product * @param cb function to call with the backend's deletion status * @param cb_cls closure for @a cb @@ -132,7 +130,6 @@ struct TALER_MERCHANT_ProductDeleteHandle * TALER_MERCHANT_product_delete ( struct GNUNET_CURL_Context *ctx, const char *backend_url, - const char *instance_id, const char *product_id, TALER_MERCHANT_ProductDeleteCallback cb, void *cb_cls) @@ -146,15 +143,9 @@ TALER_MERCHANT_product_delete ( { char *path; - if (NULL == instance_id) - GNUNET_asprintf (&path, - "products/%s", - product_id); - else - GNUNET_asprintf (&path, - "instances/%s/products/%s", - instance_id, - product_id); + GNUNET_asprintf (&path, + "products/%s", + product_id); pdh->url = TALER_url_join (backend_url, path, NULL); diff --git a/src/lib/merchant_api_get_product.c b/src/lib/merchant_api_get_product.c index f0f7cca1..fc33dedf 100644 --- a/src/lib/merchant_api_get_product.c +++ b/src/lib/merchant_api_get_product.c @@ -188,8 +188,6 @@ handle_get_product_finished (void *cls, * * @param ctx the context * @param backend_url HTTP base URL for the backend - * @param instance_id instance to query about its products, - * NULL to query the default instance * @param product_id identifier of the product to inquire about * @param cb function to call with the backend's product information * @param cb_cls closure for @a cb @@ -199,7 +197,6 @@ struct TALER_MERCHANT_ProductGetHandle * TALER_MERCHANT_product_get ( struct GNUNET_CURL_Context *ctx, const char *backend_url, - const char *instance_id, const char *product_id, TALER_MERCHANT_ProductGetCallback cb, void *cb_cls) @@ -214,15 +211,9 @@ TALER_MERCHANT_product_get ( { char *path; - if (NULL == instance_id) - GNUNET_asprintf (&path, - "products/%s", - product_id); - else - GNUNET_asprintf (&path, - "instances/%s/products/%s", - instance_id, - product_id); + GNUNET_asprintf (&path, + "products/%s", + product_id); pgh->url = TALER_url_join (backend_url, path, NULL); diff --git a/src/lib/merchant_api_get_products.c b/src/lib/merchant_api_get_products.c index 3c7eae54..86e93cdc 100644 --- a/src/lib/merchant_api_get_products.c +++ b/src/lib/merchant_api_get_products.c @@ -203,8 +203,6 @@ handle_get_products_finished (void *cls, * * @param ctx the context * @param backend_url HTTP base URL for the backend - * @param instance_id instance to query about its products, - * NULL to query the default instance * @param cb function to call with the backend's inventory information * @param cb_cls closure for @a cb * @return the request handle; NULL upon error @@ -213,7 +211,6 @@ struct TALER_MERCHANT_ProductsGetHandle * TALER_MERCHANT_products_get ( struct GNUNET_CURL_Context *ctx, const char *backend_url, - const char *instance_id, TALER_MERCHANT_ProductsGetCallback cb, void *cb_cls) { @@ -224,21 +221,9 @@ TALER_MERCHANT_products_get ( pgh->ctx = ctx; pgh->cb = cb; pgh->cb_cls = cb_cls; - { - char *path; - - if (NULL == instance_id) - GNUNET_asprintf (&path, - "products"); - else - GNUNET_asprintf (&path, - "instances/%s/products", - instance_id); - pgh->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } + pgh->url = TALER_url_join (backend_url, + "products", + NULL); if (NULL == pgh->url) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, diff --git a/src/lib/merchant_api_lock_product.c b/src/lib/merchant_api_lock_product.c index ddd5e8e3..9a831508 100644 --- a/src/lib/merchant_api_lock_product.c +++ b/src/lib/merchant_api_lock_product.c @@ -155,8 +155,6 @@ handle_lock_product_finished (void *cls, * * @param ctx the context * @param backend_url HTTP base URL for the backend - * @param instance_id instance to query about its products, - * NULL to query the default instance * @param product_id identifier of the product * @param uuid UUID that identifies the client holding the lock * @param duration how long should the lock be held @@ -169,7 +167,6 @@ struct TALER_MERCHANT_ProductLockHandle * TALER_MERCHANT_product_lock ( struct GNUNET_CURL_Context *ctx, const char *backend_url, - const char *instance_id, const char *product_id, const struct GNUNET_Uuid *uuid, struct GNUNET_TIME_Relative duration, @@ -199,15 +196,9 @@ TALER_MERCHANT_product_lock ( { char *path; - if (NULL == instance_id) - GNUNET_asprintf (&path, - "products/%s/lock", - product_id); - else - GNUNET_asprintf (&path, - "instances/%s/products/%s/lock", - instance_id, - product_id); + GNUNET_asprintf (&path, + "products/%s/lock", + product_id); plh->url = TALER_url_join (backend_url, path, NULL); diff --git a/src/lib/merchant_api_patch_product.c b/src/lib/merchant_api_patch_product.c index d90ae582..9b0aead2 100644 --- a/src/lib/merchant_api_patch_product.c +++ b/src/lib/merchant_api_patch_product.c @@ -155,8 +155,6 @@ handle_patch_product_finished (void *cls, * * @param ctx the context * @param backend_url HTTP base URL for the backend - * @param instance_id instance to add a product to, - * NULL to query the default instance * @param product_id identifier to use for the product; the product must exist, * or the transaction will fail with a #MHD_HTTP_NOT_FOUND * HTTP status code @@ -184,7 +182,6 @@ struct TALER_MERCHANT_ProductPatchHandle * TALER_MERCHANT_product_patch ( struct GNUNET_CURL_Context *ctx, const char *backend_url, - const char *instance_id, const char *product_id, const char *description, const json_t *description_i18n, @@ -237,15 +234,9 @@ TALER_MERCHANT_product_patch ( { char *path; - if (NULL == instance_id) - GNUNET_asprintf (&path, - "products/%s", - product_id); - else - GNUNET_asprintf (&path, - "instances/%s/products/%s", - instance_id, - product_id); + GNUNET_asprintf (&path, + "products/%s", + product_id); pph->url = TALER_url_join (backend_url, path, NULL); diff --git a/src/lib/merchant_api_post_products.c b/src/lib/merchant_api_post_products.c index b15866dc..77cc8590 100644 --- a/src/lib/merchant_api_post_products.c +++ b/src/lib/merchant_api_post_products.c @@ -157,8 +157,6 @@ handle_post_products_finished (void *cls, * * @param ctx the context * @param backend_url HTTP base URL for the backend - * @param instance_id instance to add a product to, - * NULL to query the default instance * @param product_id identifier to use for the product * @param description description of the product * @param description_i18n Map from IETF BCP 47 language tags to localized descriptions @@ -181,7 +179,6 @@ struct TALER_MERCHANT_ProductsPostHandle * TALER_MERCHANT_products_post ( struct GNUNET_CURL_Context *ctx, const char *backend_url, - const char *instance_id, const char *product_id, const char *description, const json_t *description_i18n, @@ -230,21 +227,9 @@ TALER_MERCHANT_products_post ( pph->ctx = ctx; pph->cb = cb; pph->cb_cls = cb_cls; - { - char *path; - - if (NULL == instance_id) - GNUNET_asprintf (&path, - "products"); - else - GNUNET_asprintf (&path, - "instances/%s/products", - instance_id); - pph->url = TALER_url_join (backend_url, - path, - NULL); - GNUNET_free (path); - } + pph->url = TALER_url_join (backend_url, + "products", + NULL); if (NULL == pph->url) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am index 3aecec65..df922b0d 100644 --- a/src/testing/Makefile.am +++ b/src/testing/Makefile.am @@ -17,10 +17,15 @@ libtalermerchanttesting_la_SOURCES = \ testing_api_cmd_config.c \ testing_api_cmd_get_instance.c \ testing_api_cmd_get_instances.c \ + testing_api_cmd_get_product.c \ + testing_api_cmd_get_products.c \ testing_api_cmd_delete_instance.c \ + testing_api_cmd_delete_product.c \ + testing_api_cmd_lock_product.c \ testing_api_cmd_post_instances.c \ testing_api_cmd_post_products.c \ testing_api_cmd_patch_instance.c \ + testing_api_cmd_patch_product.c \ \ testing_api_cmd_check_payment.c \ testing_api_cmd_history.c \ diff --git a/src/testing/testing_api_cmd_delete_product.c b/src/testing/testing_api_cmd_delete_product.c new file mode 100644 index 00000000..f306c762 --- /dev/null +++ b/src/testing/testing_api_cmd_delete_product.c @@ -0,0 +1,182 @@ +/* + This file is part of TALER + Copyright (C) 2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + +*/ +/** + * @file lib/testing_api_cmd_delete_product.c + * @brief command to test DELETE /product/$ID + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include "taler_merchant_service.h" +#include "taler_merchant_testing_lib.h" + + +/** + * State of a "DELETE /products/$ID" CMD. + */ +struct DeleteProductState +{ + + /** + * Handle for a "DELETE product" request. + */ + struct TALER_MERCHANT_ProductDeleteHandle *pdh; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Base URL of the merchant serving the request. + */ + const char *merchant_url; + + /** + * ID of the product to run DELETE for. + */ + const char *product_id; + + /** + * Expected HTTP response code. + */ + unsigned int http_status; + +}; + + +/** + * Callback for a /delete/products/$ID operation. + * + * @param cls closure for this function + */ +static void +delete_product_cb (void *cls, + const struct TALER_MERCHANT_HttpResponse *hr) +{ + struct DeleteProductState *dis = cls; + + dis->pdh = NULL; + if (dis->http_status != hr->http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u (%d) to command %s\n", + hr->http_status, + (int) hr->ec, + TALER_TESTING_interpreter_get_current_label (dis->is)); + TALER_TESTING_interpreter_fail (dis->is); + return; + } + switch (hr->http_status) + { + case MHD_HTTP_OK: + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unhandled HTTP status.\n"); + } + TALER_TESTING_interpreter_next (dis->is); +} + + +/** + * Run the "DELETE product" CMD. + * + * + * @param cls closure. + * @param cmd command being run now. + * @param is interpreter state. + */ +static void +delete_product_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct DeleteProductState *dis = cls; + + dis->is = is; + dis->pdh = TALER_MERCHANT_product_delete (is->ctx, + dis->merchant_url, + dis->product_id, + &delete_product_cb, + dis); + GNUNET_assert (NULL != dis->pdh); +} + + +/** + * Free the state of a "DELETE product" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd command being run. + */ +static void +delete_product_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct DeleteProductState *dis = cls; + + if (NULL != dis->pdh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "DELETE /products/$ID operation did not complete\n"); + TALER_MERCHANT_product_delete_cancel (dis->pdh); + } + GNUNET_free (dis); +} + + +/** + * Define a "DELETE product" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * DELETE /products/$ID request. + * @param product_id the ID of the product to query + * @param http_status expected HTTP response code. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_delete_product (const char *label, + const char *merchant_url, + const char *product_id, + unsigned int http_status) +{ + struct DeleteProductState *dis; + + dis = GNUNET_new (struct DeleteProductState); + dis->merchant_url = merchant_url; + dis->product_id = product_id; + dis->http_status = http_status; + { + struct TALER_TESTING_Command cmd = { + .cls = dis, + .label = label, + .run = &delete_product_run, + .cleanup = &delete_product_cleanup + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_delete_product.c */ diff --git a/src/testing/testing_api_cmd_get_product.c b/src/testing/testing_api_cmd_get_product.c new file mode 100644 index 00000000..43d39bbc --- /dev/null +++ b/src/testing/testing_api_cmd_get_product.c @@ -0,0 +1,224 @@ +/* + This file is part of TALER + Copyright (C) 2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + +*/ +/** + * @file lib/testing_api_cmd_get_product.c + * @brief command to test GET /product/$ID + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include "taler_merchant_service.h" +#include "taler_merchant_testing_lib.h" + + +/** + * State of a "GET product" CMD. + */ +struct GetProductState +{ + + /** + * Handle for a "GET product" request. + */ + struct TALER_MERCHANT_ProductGetHandle *igh; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Base URL of the merchant serving the request. + */ + const char *merchant_url; + + /** + * ID of the product to run GET for. + */ + const char *product_id; + + /** + * Reference for a POST or PATCH /products CMD (optional). + */ + const char *product_reference; + + /** + * Expected HTTP response code. + */ + unsigned int http_status; + +}; + + +/** + * Callback for a /get/product/$ID operation. + * + * @param cls closure for this function + * @param hr HTTP response details + * @param description description of the product + * @param description_i18n Map from IETF BCP 47 language tags to localized descriptions + * @param unit unit in which the product is measured (liters, kilograms, packages, etc.) + * @param price the price for one @a unit of the product, zero is used to imply that + * this product is not sold separately or that the price is not fixed and + * must be supplied by the front-end. If non-zero, price must include + * applicable taxes. + * @param image base64-encoded product image + * @param taxes list of taxes paid by the merchant + * @param total_stocked in @a units, -1 to indicate "infinite" (i.e. electronic books), + * does NOT indicate remaining stocks, to get remaining stocks, + * subtract @a total_sold and @a total_lost. Note that this still + * does not then say how many of the remaining inventory are locked. + * @param total_sold in @a units, total number of @a unit of product sold + * @param total_lost in @a units, total number of @a unit of product lost from inventory + * @param location where the product is in stock + * @param next_restock when the next restocking is expected to happen, 0 for unknown, + * #GNUNET_TIME_UNIT_FOREVER_ABS for 'never'. + */ +static void +get_product_cb (void *cls, + const struct TALER_MERCHANT_HttpResponse *hr, + const char *description, + const json_t *description_i18n, + const char *unit, + const struct TALER_Amount *price, + const json_t *image, + const json_t *taxes, + int64_t total_stocked, + uint64_t total_sold, + uint64_t total_lost, + const json_t *location, + struct GNUNET_TIME_Absolute next_restock) +{ + /* FIXME, deeper checks should be implemented here. */ + struct GetProductState *gis = cls; + + gis->igh = NULL; + if (gis->http_status != hr->http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u (%d) to command %s\n", + hr->http_status, + (int) hr->ec, + TALER_TESTING_interpreter_get_current_label (gis->is)); + TALER_TESTING_interpreter_fail (gis->is); + return; + } + switch (hr->http_status) + { + case MHD_HTTP_OK: + // FIXME: use gis->product_reference here to + // check if the data returned matches that from the POST / PATCH + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unhandled HTTP status.\n"); + } + TALER_TESTING_interpreter_next (gis->is); +} + + +/** + * Run the "GET product" CMD. + * + * + * @param cls closure. + * @param cmd command being run now. + * @param is interpreter state. + */ +static void +get_product_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct GetProductState *gis = cls; + + gis->is = is; + gis->igh = TALER_MERCHANT_product_get (is->ctx, + gis->merchant_url, + gis->product_id, + &get_product_cb, + gis); + GNUNET_assert (NULL != gis->igh); +} + + +/** + * Free the state of a "GET product" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd command being run. + */ +static void +get_product_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct GetProductState *gis = cls; + + if (NULL != gis->igh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "GET /products/$ID operation did not complete\n"); + TALER_MERCHANT_product_get_cancel (gis->igh); + } + GNUNET_free (gis); +} + + +/** + * Define a "GET product" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * GET /products/$ID request. + * @param product_id the ID of the product to query + * @param http_status expected HTTP response code. + * @param product_reference reference to a "POST /products" or "PATCH /products/$ID" CMD + * that will provide what we expect the backend to return to us + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_get_product (const char *label, + const char *merchant_url, + const char *product_id, + unsigned int http_status, + const char *product_reference) +{ + struct GetProductState *gis; + + gis = GNUNET_new (struct GetProductState); + gis->merchant_url = merchant_url; + gis->product_id = product_id; + gis->http_status = http_status; + gis->product_reference = product_reference; + { + struct TALER_TESTING_Command cmd = { + .cls = gis, + .label = label, + .run = &get_product_run, + .cleanup = &get_product_cleanup + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_get_product.c */ diff --git a/src/testing/testing_api_cmd_get_products.c b/src/testing/testing_api_cmd_get_products.c new file mode 100644 index 00000000..e9e2bd49 --- /dev/null +++ b/src/testing/testing_api_cmd_get_products.c @@ -0,0 +1,181 @@ +/* + This file is part of TALER + Copyright (C) 2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + +*/ +/** + * @file lib/testing_api_cmd_get_products.c + * @brief command to test GET /products + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include "taler_merchant_service.h" +#include "taler_merchant_testing_lib.h" + + +/** + * State of a "GET products" CMD. + */ +struct GetProductsState +{ + + /** + * Handle for a "GET product" request. + */ + struct TALER_MERCHANT_ProductsGetHandle *igh; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Base URL of the merchant serving the request. + */ + const char *merchant_url; + + /** + * Expected HTTP response code. + */ + unsigned int http_status; + +}; + + +/** + * Callback for a GET /products operation. + * + * @param cls closure for this function + * @param hr HTTP response details + * @param products_length length of the @a products array + * @param products array of products the requested instance offers + */ +static void +get_products_cb (void *cls, + const struct TALER_MERCHANT_HttpResponse *hr, + unsigned int products_length, + const struct TALER_MERCHANT_InventoryEntry products[]) +{ + /* FIXME, deeper checks should be implemented here. */ + struct GetProductsState *gis = cls; + + gis->igh = NULL; + if (gis->http_status != hr->http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u (%d) to command %s\n", + hr->http_status, + (int) hr->ec, + TALER_TESTING_interpreter_get_current_label (gis->is)); + TALER_TESTING_interpreter_fail (gis->is); + return; + } + switch (hr->http_status) + { + case MHD_HTTP_OK: + // FIXME: use gis->product_reference here to + // check if the data returned matches that from the POST / PATCH + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unhandled HTTP status.\n"); + } + TALER_TESTING_interpreter_next (gis->is); +} + + +/** + * Run the "GET /products" CMD. + * + * + * @param cls closure. + * @param cmd command being run now. + * @param is interpreter state. + */ +static void +get_products_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct GetProductsState *gis = cls; + + gis->is = is; + gis->igh = TALER_MERCHANT_products_get (is->ctx, + gis->merchant_url, + &get_products_cb, + gis); + GNUNET_assert (NULL != gis->igh); +} + + +/** + * Free the state of a "GET product" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd command being run. + */ +static void +get_products_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct GetProductsState *gis = cls; + + if (NULL != gis->igh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "GET /products/$ID operation did not complete\n"); + TALER_MERCHANT_products_get_cancel (gis->igh); + } + GNUNET_free (gis); +} + + +/** + * Define a "GET /products" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * GET /products request. + * @param http_status expected HTTP response code. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_get_products (const char *label, + const char *merchant_url, + unsigned int http_status) +{ + struct GetProductsState *gis; + + gis = GNUNET_new (struct GetProductsState); + gis->merchant_url = merchant_url; + gis->http_status = http_status; + { + struct TALER_TESTING_Command cmd = { + .cls = gis, + .label = label, + .run = &get_products_run, + .cleanup = &get_products_cleanup + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_get_products.c */ diff --git a/src/testing/testing_api_cmd_lock_product.c b/src/testing/testing_api_cmd_lock_product.c new file mode 100644 index 00000000..009783ee --- /dev/null +++ b/src/testing/testing_api_cmd_lock_product.c @@ -0,0 +1,200 @@ +/* + This file is part of TALER + Copyright (C) 2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + +*/ +/** + * @file lib/testing_api_cmd_lock_product.c + * @brief command to test LOCK /product + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include "taler_merchant_service.h" +#include "taler_merchant_testing_lib.h" + + +/** + * State of a "LOCK /product" CMD. + */ +struct LockProductState +{ + + /** + * Handle for a "GET product" request. + */ + struct TALER_MERCHANT_ProductLockHandle *iph; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Base URL of the merchant serving the request. + */ + const char *merchant_url; + + /** + * ID of the product to run GET for. + */ + const char *product_id; + + + /** + * Expected HTTP response code. + */ + unsigned int http_status; + +}; + + +/** + * Callback for a POST /products/$ID/lock operation. + * + * @param cls closure for this function + */ +static void +lock_product_cb (void *cls, + const struct TALER_MERCHANT_HttpResponse *hr) +{ + struct LockProductState *pis = cls; + + pis->iph = NULL; + if (pis->http_status != hr->http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u (%d) to command %s\n", + hr->http_status, + (int) hr->ec, + TALER_TESTING_interpreter_get_current_label (pis->is)); + TALER_TESTING_interpreter_fail (pis->is); + return; + } + switch (hr->http_status) + { + case MHD_HTTP_OK: + break; + // FIXME: add other legitimate states here... + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unhandled HTTP status.\n"); + } + TALER_TESTING_interpreter_next (pis->is); +} + + +/** + * Run the "LOCK /products/$ID" CMD. + * + * + * @param cls closure. + * @param cmd command being run now. + * @param is interpreter state. + */ +static void +lock_product_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct LockProductState *pis = cls; + + pis->is = is; + pis->iph = TALER_MERCHANT_product_lock (is->ctx, + pis->merchant_url, + pis->product_id, + ... + & lock_product_cb, + pis); + GNUNET_assert (NULL != pis->iph); +} + + +/** + * Free the state of a "GET product" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd command being run. + */ +static void +lock_product_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct LockProductState *pis = cls; + + if (NULL != pis->iph) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "POST /product/$ID/lock operation did not complete\n"); + TALER_MERCHANT_product_lock_cancel (pis->iph); + } + json_decref (pis->address); + json_decref (pis->jurisdiction); + GNUNET_free (pis); +} + + +/** + * Define a "LOCK /products/$ID" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * LOCK /product request. + * @param product_id the ID of the product to query + * @param payto_uris_length length of the @a accounts array + * @param payto_uris URIs of the bank accounts of the merchant product + * @param name name of the merchant product + * @param address physical address of the merchant product + * @param jurisdiction jurisdiction of the merchant product + * @param default_max_wire_fee default maximum wire fee merchant is willing to fully pay + * @param default_wire_fee_amortization default amortization factor for excess wire fees + * @param default_max_deposit_fee default maximum deposit fee merchant is willing to pay + * @param default_wire_transfer_delay default wire transfer delay merchant will ask for + * @param default_pay_delay default validity period for offers merchant makes + * @param http_status expected HTTP response code. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_lock_product ( + const char *label, + const char *merchant_url, + const char *product_id, + ... + unsigned int http_status) +{ + struct LockProductState *pis; + + pis = GNUNET_new (struct LockProductState); + pis->merchant_url = merchant_url; + pis->product_id = product_id; + pis->http_status = http_status; + + { + struct TALER_TESTING_Command cmd = { + .cls = pis, + .label = label, + .run = &lock_product_run, + .cleanup = &lock_product_cleanup + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_lock_product.c */ diff --git a/src/testing/testing_api_cmd_patch_product.c b/src/testing/testing_api_cmd_patch_product.c new file mode 100644 index 00000000..8088932d --- /dev/null +++ b/src/testing/testing_api_cmd_patch_product.c @@ -0,0 +1,283 @@ +/* + This file is part of TALER + Copyright (C) 2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + +*/ +/** + * @file lib/testing_api_cmd_patch_product.c + * @brief command to test PATCH /product + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include "taler_merchant_service.h" +#include "taler_merchant_testing_lib.h" + + +/** + * State of a "PATCH /product" CMD. + */ +struct PatchProductState +{ + + /** + * Handle for a "GET product" request. + */ + struct TALER_MERCHANT_ProductPatchHandle *iph; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Base URL of the merchant serving the request. + */ + const char *merchant_url; + + /** + * ID of the product to run GET for. + */ + const char *product_id; + + /** + * description of the product + */ + const char *description; + + /** + * Map from IETF BCP 47 language tags to localized descriptions + */ + json_t *description_i18n; + + /** + * unit in which the product is measured (liters, kilograms, packages, etc.) + */ + const char *unit; + + /** + * the price for one @a unit of the product + */ + struct TALER_Amount price; + + /** + * base64-encoded product image + */ + json_t *image; + + /** + * list of taxes paid by the merchant + */ + json_t *taxes; + + /** + * in @e units, -1 to indicate "infinite" (i.e. electronic books) + */ + int64_t total_stocked; + + /** + * in @e units. + */ + int64_t total_lost; + + /** + * where the product is in stock + */ + json_t *address; + + /** + * when the next restocking is expected to happen, 0 for unknown, + */ + struct GNUNET_TIME_Absolute next_restock; + + /** + * Expected HTTP response code. + */ + unsigned int http_status; + +}; + + +/** + * Callback for a PATCH /products/$ID operation. + * + * @param cls closure for this function + */ +static void +patch_product_cb (void *cls, + const struct TALER_MERCHANT_HttpResponse *hr) +{ + struct PatchProductState *pis = cls; + + pis->iph = NULL; + if (pis->http_status != hr->http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u (%d) to command %s\n", + hr->http_status, + (int) hr->ec, + TALER_TESTING_interpreter_get_current_label (pis->is)); + TALER_TESTING_interpreter_fail (pis->is); + return; + } + switch (hr->http_status) + { + case MHD_HTTP_OK: + break; + // FIXME: add other legitimate states here... + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unhandled HTTP status.\n"); + } + TALER_TESTING_interpreter_next (pis->is); +} + + +/** + * Run the "PATCH /products/$ID" CMD. + * + * + * @param cls closure. + * @param cmd command being run now. + * @param is interpreter state. + */ +static void +patch_product_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct PatchProductState *pis = cls; + + pis->is = is; + pis->iph = TALER_MERCHANT_product_patch (is->ctx, + pis->merchant_url, + pis->product_id, + pis->description, + pis->description_i18n, + pis->unit, + &pis->price, + pis->image, + pis->taxes, + pis->total_stocked, + pis->total_lost, + pis->address, + pis->next_restock, + &patch_product_cb, + pis); + GNUNET_assert (NULL != pis->iph); +} + + +/** + * Free the state of a "GET product" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd command being run. + */ +static void +patch_product_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct PatchProductState *pis = cls; + + if (NULL != pis->iph) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "PATCH /products/$ID operation did not complete\n"); + TALER_MERCHANT_product_patch_cancel (pis->iph); + } + json_decref (pis->description_i18n); + json_decref (pis->image); + json_decref (pis->taxes); + json_decref (pis->address); + GNUNET_free (pis); +} + + +/** + * Define a "PATCH /products/$ID" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * PATCH /product request. + * @param product_id the ID of the product to query + * @param description description of the product + * @param description_i18n Map from IETF BCP 47 language tags to localized descriptions + * @param unit unit in which the product is measured (liters, kilograms, packages, etc.) + * @param price the price for one @a unit of the product, zero is used to imply that + * this product is not sold separately or that the price is not fixed and + * must be supplied by the front-end. If non-zero, price must include + * applicable taxes. + * @param image base64-encoded product image + * @param taxes list of taxes paid by the merchant + * @param total_stocked in @a units, -1 to indicate "infinite" (i.e. electronic books) + * @param total_lost in @a units, must be larger than previous values, and may + * not exceed total_stocked minus total_sold; if it does, the transaction + * will fail with a #MHD_HTTP_CONFLICT HTTP status code + * @param address where the product is in stock + * @param next_restock when the next restocking is expected to happen, 0 for unknown, + * #GNUNET_TIME_UNIT_FOREVER_ABS for 'never'. + * @param http_status expected HTTP response code. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_patch_product ( + const char *label, + const char *merchant_url, + const char *product_id, + const char *description, + json_t *description_i18n, + const char *unit, + const struct TALER_Amount *price, + json_t *image, + json_t *taxes, + int64_t total_stocked, + uint64_t total_lost, + json_t *address, + struct GNUNET_TIME_Absolute next_restock, + unsigned int http_status) +{ + struct PatchProductState *pis; + + pis = GNUNET_new (struct PatchProductState); + pis->merchant_url = merchant_url; + pis->product_id = product_id; + pis->http_status = http_status; + pis->description = description; + pis->description_i18n = description_i18n; /* ownership taken */ + pis->unit = unit; + pis->price = *price; + pis->image = image; /* ownership taken */ + pis->taxes = taxes; /* ownership taken */ + pis->total_stocked = total_stocked; + pis->total_lost = total_lost; + pis->address = address; /* ownership taken */ + pis->next_restock = next_restock; { + struct TALER_TESTING_Command cmd = { + .cls = pis, + .label = label, + .run = &patch_product_run, + .cleanup = &patch_product_cleanup + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_patch_product.c */ diff --git a/src/testing/testing_api_cmd_post_products.c b/src/testing/testing_api_cmd_post_products.c new file mode 100644 index 00000000..f5fed272 --- /dev/null +++ b/src/testing/testing_api_cmd_post_products.c @@ -0,0 +1,315 @@ +/* + This file is part of TALER + Copyright (C) 2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + +*/ +/** + * @file lib/testing_api_cmd_post_products.c + * @brief command to test POST /products + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include "taler_merchant_service.h" +#include "taler_merchant_testing_lib.h" + + +/** + * State of a "POST /products" CMD. + */ +struct PostProductsState +{ + + /** + * Handle for a "GET product" request. + */ + struct TALER_MERCHANT_ProductsPostHandle *iph; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Base URL of the merchant serving the request. + */ + const char *merchant_url; + + /** + * ID of the product to run POST for. + */ + const char *product_id; + + /** + * description of the product + */ + const char *description; + + /** + * Map from IETF BCP 47 language tags to localized descriptions + */ + json_t *description_i18n; + + /** + * unit in which the product is measured (liters, kilograms, packages, etc.) + */ + const char *unit; + + /** + * the price for one @a unit of the product + */ + struct TALER_Amount price; + + /** + * base64-encoded product image + */ + json_t *image; + + /** + * list of taxes paid by the merchant + */ + json_t *taxes; + + /** + * in @e units, -1 to indicate "infinite" (i.e. electronic books) + */ + int64_t total_stocked; + + /** + * where the product is in stock + */ + json_t *address; + + /** + * when the next restocking is expected to happen, 0 for unknown, + */ + struct GNUNET_TIME_Absolute next_restock; + + /** + * Expected HTTP response code. + */ + unsigned int http_status; + +}; + + +/** + * Callback for a POST /products operation. + * + * @param cls closure for this function + */ +static void +post_products_cb (void *cls, + const struct TALER_MERCHANT_HttpResponse *hr) +{ + struct PostProductsState *pis = cls; + + pis->iph = NULL; + if (pis->http_status != hr->http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u (%d) to command %s\n", + hr->http_status, + (int) hr->ec, + TALER_TESTING_interpreter_get_current_label (pis->is)); + TALER_TESTING_interpreter_fail (pis->is); + return; + } + switch (hr->http_status) + { + case MHD_HTTP_OK: + break; + // FIXME: add other legitimate states here... + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unhandled HTTP status.\n"); + } + TALER_TESTING_interpreter_next (pis->is); +} + + +/** + * Run the "POST /products" CMD. + * + * + * @param cls closure. + * @param cmd command being run now. + * @param is interpreter state. + */ +static void +post_products_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct PostProductsState *pis = cls; + + pis->is = is; + pis->iph = TALER_MERCHANT_products_post (is->ctx, + pis->merchant_url, + pis->product_id, + pis->description, + pis->description_i18n, + pis->unit, + &pis->price, + pis->image, + pis->taxes, + pis->total_stocked, + pis->address, + pis->next_restock, + &post_products_cb, + pis); + GNUNET_assert (NULL != pis->iph); +} + + +/** + * Free the state of a "POST product" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd command being run. + */ +static void +post_products_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct PostProductsState *pis = cls; + + if (NULL != pis->iph) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "POST /products operation did not complete\n"); + TALER_MERCHANT_products_post_cancel (pis->iph); + } + json_decref (pis->description_i18n); + json_decref (pis->image); + json_decref (pis->taxes); + json_decref (pis->address); + GNUNET_free (pis); +} + + +/** + * Define a "POST /products" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * POST /products request. + * @param product_id the ID of the product to query + * @param description description of the product + * @param description_i18n Map from IETF BCP 47 language tags to localized descriptions + * @param unit unit in which the product is measured (liters, kilograms, packages, etc.) + * @param price the price for one @a unit of the product, zero is used to imply that + * this product is not sold separately or that the price is not fixed and + * must be supplied by the front-end. If non-zero, price must include + * applicable taxes. + * @param image base64-encoded product image + * @param taxes list of taxes paid by the merchant + * @param total_stocked in @a units, -1 to indicate "infinite" (i.e. electronic books) + * @param address where the product is in stock + * @param next_restock when the next restocking is expected to happen, 0 for unknown, + * #GNUNET_TIME_UNIT_FOREVER_ABS for 'never'. + * @param http_status expected HTTP response code. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_post_products2 ( + const char *label, + const char *merchant_url, + const char *product_id, + const char *description, + json_t *description_i18n, + const char *unit, + const struct TALER_Amount *price, + json_t *image, + json_t *taxes, + int64_t total_stocked, + json_t *address, + struct GNUNET_TIME_Absolute next_restock, + unsigned int http_status) +{ + struct PostProductsState *pis; + + pis = GNUNET_new (struct PostProductsState); + pis->merchant_url = merchant_url; + pis->product_id = product_id; + pis->http_status = http_status; + pis->description = description; + pis->description_i18n = description_i18n; /* ownership taken */ + pis->unit = unit; + pis->price = *price; + pis->image = image; /* ownership taken */ + pis->taxes = taxes; /* ownership taken */ + pis->total_stocked = total_stocked; + pis->address = address; /* ownership taken */ + pis->next_restock = next_restock; + { + struct TALER_TESTING_Command cmd = { + .cls = pis, + .label = label, + .run = &post_products_run, + .cleanup = &post_products_cleanup + }; + + return cmd; + } +} + + +/** + * Define a "POST /products" CMD, simple version + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * POST /products request. + * @param product_id the ID of the product to create + * @param description name of the product + * @param price price of the product + * @param http_status expected HTTP response code. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_post_products (const char *label, + const char *merchant_url, + const char *product_id, + const char *description, + const char *price, + unsigned int http_status) +{ + struct TALER_Amount amount; + + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (price, + &amount)); + return TALER_TESTING_cmd_merchant_post_products2 ( + label, + merchant_url, + product_id, + description, + json_pack ("{s:s}", "en", description), + "test-unit", + &amount, + json_object (), + json_object (), + 4, + json_pack ("{s:s}", "street", "my street"), + GNUNET_TIME_UNIT_ZERO_ABS, + http_status); +} + + +/* end of testing_api_cmd_post_products.c */ -- cgit v1.2.3