summaryrefslogtreecommitdiff
path: root/src/testing/testing_api_cmd_post_orders.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-04-26 12:04:24 +0200
committerChristian Grothoff <christian@grothoff.org>2020-04-26 12:04:24 +0200
commitdcbac9e63f37f9a5dd8b47a2cfc068d73068d4fc (patch)
tree12a4a549b33538f3b6e07a9f12a55ab68198903e /src/testing/testing_api_cmd_post_orders.c
parent6ca84e4121cf4bd1b8c66d2ef4bd58eeb7003ea3 (diff)
downloadmerchant-dcbac9e63f37f9a5dd8b47a2cfc068d73068d4fc.tar.gz
merchant-dcbac9e63f37f9a5dd8b47a2cfc068d73068d4fc.tar.bz2
merchant-dcbac9e63f37f9a5dd8b47a2cfc068d73068d4fc.zip
fix fTBFS
Diffstat (limited to 'src/testing/testing_api_cmd_post_orders.c')
-rw-r--r--src/testing/testing_api_cmd_post_orders.c397
1 files changed, 397 insertions, 0 deletions
diff --git a/src/testing/testing_api_cmd_post_orders.c b/src/testing/testing_api_cmd_post_orders.c
new file mode 100644
index 00000000..c7635e31
--- /dev/null
+++ b/src/testing/testing_api_cmd_post_orders.c
@@ -0,0 +1,397 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2018 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
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file testing_api_cmd_post_orders.c
+ * @brief command to run POST /orders
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include <taler/taler_exchange_service.h>
+#include <taler/taler_testing_lib.h>
+#include "taler_merchant_service.h"
+#include "taler_merchant_testing_lib.h"
+
+/**
+ * State for a "POST /orders" CMD.
+ */
+struct OrdersState
+{
+
+ /**
+ * The order.
+ */
+ const char *order;
+
+ /**
+ * Expected status code.
+ */
+ unsigned int http_status;
+
+ /**
+ * Order id.
+ */
+ const char *order_id;
+
+ /**
+ * Contract terms obtained from the backend.
+ */
+ json_t *contract_terms;
+
+ /**
+ * Contract terms hash code.
+ */
+ struct GNUNET_HashCode h_contract_terms;
+
+ /**
+ * The /orders operation handle.
+ */
+ struct TALER_MERCHANT_PostOrdersOperation *po;
+
+ /**
+ * The (initial) GET /orders/$ID operation handle.
+ * The logic is such that after a orders creation,
+ * it soon makes a orders lookup in order to check
+ * if the merchant backend is actually aware.
+ */
+ struct TALER_MERCHANT_ProposalLookupOperation *plo;
+
+ /**
+ * The nonce.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey nonce;
+
+ /**
+ * URL of the merchant backend.
+ */
+ const char *merchant_url;
+
+ /**
+ * The interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * Merchant signature over the orders.
+ */
+ struct TALER_MerchantSignatureP merchant_sig;
+
+ /**
+ * Merchant public key.
+ */
+ struct TALER_MerchantPublicKeyP merchant_pub;
+};
+
+
+/**
+ * Offer internal data to other commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param index index number of the object to extract.
+ * @return #GNUNET_OK on success
+ */
+static int
+orders_traits (void *cls,
+ const void **ret,
+ const char *trait,
+ unsigned int index)
+{
+ struct OrdersState *ps = cls;
+ // FIXME: wtf is this?
+#define MAKE_TRAIT_NONCE(ptr) \
+ TALER_TESTING_make_trait_merchant_pub ( \
+ 1, (struct TALER_MerchantPublicKeyP *) (ptr))
+ struct TALER_TESTING_Trait traits[] = {
+ TALER_TESTING_make_trait_order_id (0, ps->order_id),
+ TALER_TESTING_make_trait_contract_terms (0, ps->contract_terms),
+ TALER_TESTING_make_trait_h_contract_terms (0, &ps->h_contract_terms),
+ TALER_TESTING_make_trait_merchant_sig (0, &ps->merchant_sig),
+ TALER_TESTING_make_trait_merchant_pub (0, &ps->merchant_pub),
+ MAKE_TRAIT_NONCE (&ps->nonce),
+ TALER_TESTING_trait_end ()
+ };
+
+ return TALER_TESTING_get_trait (traits,
+ ret,
+ trait,
+ index);
+}
+
+
+/**
+ * Used to fill the "orders" CMD state with backend-provided
+ * values. Also double-checks that the orders was correctly
+ * created.
+ *
+ * @param cls closure
+ * @param hr HTTP response we got
+ * @param sig merchant's signature
+ * @param hash hash over the contract
+ */
+static void
+orders_lookup_initial_cb (void *cls,
+ const struct TALER_MERCHANT_HttpResponse *hr,
+ const json_t *contract_terms,
+ const struct TALER_MerchantSignatureP *sig,
+ const struct GNUNET_HashCode *hash)
+{
+ struct OrdersState *ps = cls;
+ struct TALER_MerchantPublicKeyP merchant_pub;
+ const char *error_name;
+ unsigned int error_line;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("merchant_pub",
+ &merchant_pub),
+ GNUNET_JSON_spec_end ()
+ };
+
+ ps->plo = NULL;
+ if (ps->http_status != hr->http_status)
+ TALER_TESTING_FAIL (ps->is);
+
+ ps->contract_terms = json_deep_copy (contract_terms);
+ ps->h_contract_terms = *hash;
+ ps->merchant_sig = *sig;
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (contract_terms,
+ spec,
+ &error_name,
+ &error_line))
+ {
+ char *log;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Parser failed on %s:%u\n",
+ error_name,
+ error_line);
+ log = json_dumps (ps->contract_terms,
+ JSON_INDENT (1));
+ fprintf (stderr,
+ "%s\n",
+ log);
+ free (log);
+ TALER_TESTING_FAIL (ps->is);
+ }
+ ps->merchant_pub = merchant_pub;
+ TALER_TESTING_interpreter_next (ps->is);
+}
+
+
+/**
+ * Callback that processes the response following a
+ * POST /orders. NOTE: no contract terms are included
+ * here; they need to be taken via the "orders lookup"
+ * method.
+ *
+ * @param cls closure.
+ * @param hr HTTP response
+ * @param order_id order id of the orders.
+ */
+static void
+order_cb (void *cls,
+ const struct TALER_MERCHANT_HttpResponse *hr,
+ const char *order_id)
+{
+ struct OrdersState *ps = cls;
+
+ ps->po = NULL;
+ if (ps->http_status != hr->http_status)
+ {
+ TALER_LOG_ERROR ("Given vs expected: %u(%d) vs %u\n",
+ hr->http_status,
+ (int) hr->ec,
+ ps->http_status);
+ TALER_TESTING_FAIL (ps->is);
+ }
+ if (0 == ps->http_status)
+ {
+ TALER_LOG_DEBUG ("/orders, expected 0 status code\n");
+ TALER_TESTING_interpreter_next (ps->is);
+ return;
+ }
+ switch (hr->http_status)
+ {
+ case MHD_HTTP_OK:
+ ps->order_id = GNUNET_strdup (order_id);
+ break;
+ default:
+ {
+ char *s = json_dumps (hr->reply,
+ JSON_COMPACT);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected status code from /orders: %u (%d) at %s; JSON: %s\n",
+ hr->http_status,
+ hr->ec,
+ TALER_TESTING_interpreter_get_current_label (ps->is),
+ s);
+ GNUNET_free_non_null (s);
+ /**
+ * Not failing, as test cases are _supposed_
+ * to create non 200 OK situations.
+ */
+ TALER_TESTING_interpreter_next (ps->is);
+ }
+ return;
+ }
+
+ if (NULL ==
+ (ps->plo = TALER_MERCHANT_proposal_lookup (ps->is->ctx,
+ ps->merchant_url,
+ ps->order_id,
+ &ps->nonce,
+ &orders_lookup_initial_cb,
+ ps)))
+ TALER_TESTING_FAIL (ps->is);
+}
+
+
+/**
+ * Run a "orders" CMD.
+ *
+ * @param cls closure.
+ * @param cmd command currently being run.
+ * @param is interpreter state.
+ */
+static void
+orders_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct OrdersState *ps = cls;
+ json_t *order;
+ json_error_t error;
+
+ ps->is = is;
+ order = json_loads (ps->order,
+ JSON_REJECT_DUPLICATES,
+ &error);
+ if (NULL == order)
+ {
+ // human error here.
+ GNUNET_break (0);
+ fprintf (stderr, "%s\n", error.text);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ if (NULL == json_object_get (order,
+ "order_id"))
+ {
+ struct GNUNET_TIME_Absolute now;
+ char *order_id;
+
+ // FIXME: should probably use get_monotone() to ensure uniqueness!
+ now = GNUNET_TIME_absolute_get ();
+ order_id = GNUNET_STRINGS_data_to_string_alloc
+ (&now.abs_value_us,
+ sizeof (now.abs_value_us));
+ json_object_set_new (order,
+ "order_id",
+ json_string (order_id));
+ GNUNET_free (order_id);
+ }
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+ &ps->nonce,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+ ps->po = TALER_MERCHANT_orders_post (is->ctx,
+ ps->merchant_url,
+ order,
+ &order_cb,
+ ps);
+ json_decref (order);
+ GNUNET_assert (NULL != ps->po);
+}
+
+
+/**
+ * Free the state of a "orders" CMD, and possibly
+ * cancel it if it did not complete.
+ *
+ * @param cls closure.
+ * @param cmd command being freed.
+ */
+static void
+orders_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct OrdersState *ps = cls;
+
+ if (NULL != ps->po)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command '%s' did not complete (orders put)\n",
+ cmd->label);
+ TALER_MERCHANT_orders_post_cancel (ps->po);
+ ps->po = NULL;
+ }
+
+ if (NULL != ps->plo)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command '%s' did not complete"
+ " (orders lookup)\n",
+ cmd->label);
+ TALER_MERCHANT_proposal_lookup_cancel (ps->plo);
+ ps->plo = NULL;
+ }
+
+ json_decref (ps->contract_terms);
+ GNUNET_free_non_null ((void *) ps->order_id);
+ GNUNET_free (ps);
+}
+
+
+/**
+ * Make the "orders" command.
+ *
+ * @param label command label
+ * @param merchant_url base URL of the merchant serving
+ * the orders request.
+ * @param http_status expected HTTP status.
+ * @param order the order to PUT to the merchant.
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_merchant_post_orders (const char *label,
+ const char *merchant_url,
+ unsigned int http_status,
+ const char *order)
+{
+ struct OrdersState *ps;
+
+ ps = GNUNET_new (struct OrdersState);
+ ps->order = order;
+ ps->http_status = http_status;
+ ps->merchant_url = merchant_url;
+ {
+ struct TALER_TESTING_Command cmd = {
+ .cls = ps,
+ .label = label,
+ .run = &orders_run,
+ .cleanup = &orders_cleanup,
+ .traits = &orders_traits
+ };
+
+ return cmd;
+ }
+}