summaryrefslogtreecommitdiff
path: root/src/testing/testing_api_cmd_post_using_templates.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/testing/testing_api_cmd_post_using_templates.c')
-rw-r--r--src/testing/testing_api_cmd_post_using_templates.c396
1 files changed, 382 insertions, 14 deletions
diff --git a/src/testing/testing_api_cmd_post_using_templates.c b/src/testing/testing_api_cmd_post_using_templates.c
index 956a421a..7aeec33d 100644
--- a/src/testing/testing_api_cmd_post_using_templates.c
+++ b/src/testing/testing_api_cmd_post_using_templates.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022-2023 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
@@ -27,6 +27,7 @@
#include "taler_merchant_service.h"
#include "taler_merchant_testing_lib.h"
+
/**
* State of a "POST /templates" CMD.
*/
@@ -44,11 +45,23 @@ struct PostUsingTemplatesState
struct TALER_TESTING_Interpreter *is;
/**
+ * The (initial) POST /orders/$ID/claim operation handle.
+ * The logic is such that after an order creation,
+ * we immediately claim the order.
+ */
+ struct TALER_MERCHANT_OrderClaimHandle *och;
+
+ /**
* Base URL of the merchant serving the request.
*/
const char *merchant_url;
/**
+ * ID of the using template to run.
+ */
+ const char *using_template_id;
+
+ /**
* Summary given by the customer.
*/
const char *summary;
@@ -64,12 +77,145 @@ struct PostUsingTemplatesState
const char *template_ref;
/**
+ * Order id.
+ */
+ char *order_id;
+
+ /**
+ * The order id we expect the merchant to assign (if not NULL).
+ */
+ const char *expected_order_id;
+
+ /**
+ * Contract terms obtained from the backend.
+ */
+ json_t *contract_terms;
+
+ /**
+ * Order submitted to the backend.
+ */
+ json_t *order_terms;
+
+ /**
+ * Contract terms hash code.
+ */
+ struct TALER_PrivateContractHashP h_contract_terms;
+
+ /**
+ * Merchant signature over the orders.
+ */
+ struct TALER_MerchantSignatureP merchant_sig;
+
+ /**
+ * Merchant public key.
+ */
+ struct TALER_MerchantPublicKeyP merchant_pub;
+
+ /**
+ * The nonce.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey nonce;
+
+ /**
+ * The claim token
+ */
+ struct TALER_ClaimTokenP claim_token;
+
+ /**
+ * Should the command also CLAIM the order?
+ */
+ bool with_claim;
+
+ /**
+ * If not NULL, the command should duplicate the request and verify the
+ * response is the same as in this command.
+ */
+ const char *duplicate_of;
+
+ /**
+ * Label of command creating/updating OTP device, or NULL.
+ */
+ const char *otp_ref;
+
+ /**
+ * Encoded key for the payment verification.
+ */
+ const char *otp_key;
+
+ /**
+ * Option that add amount of the order
+ */
+ const enum TALER_MerchantConfirmationAlgorithm *otp_alg;
+
+ /**
* Expected HTTP response code.
*/
unsigned int http_status;
};
+/**
+ * Used to fill the "using_template" CMD state with backend-provided
+ * values. Also double-checks that the using_template was correctly
+ * created.
+ *
+ * @param cls closure
+ * @param ocr response we got
+ */
+static void
+using_claim_cb (void *cls,
+ const struct TALER_MERCHANT_OrderClaimResponse *ocr)
+{
+ struct PostUsingTemplatesState *tis = cls;
+ const char *error_name;
+ unsigned int error_line;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("merchant_pub",
+ &tis->merchant_pub),
+ GNUNET_JSON_spec_end ()
+ };
+
+ tis->och = NULL;
+ if (tis->http_status != ocr->hr.http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Expected status %u, got %u\n",
+ tis->http_status,
+ ocr->hr.http_status);
+ TALER_TESTING_FAIL (tis->is);
+ }
+ if (MHD_HTTP_OK != ocr->hr.http_status)
+ {
+ TALER_TESTING_interpreter_next (tis->is);
+ return;
+ }
+ tis->contract_terms = json_deep_copy (
+ (json_t *) ocr->details.ok.contract_terms);
+ tis->h_contract_terms = ocr->details.ok.h_contract_terms;
+ tis->merchant_sig = ocr->details.ok.sig;
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (tis->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 (tis->contract_terms,
+ JSON_INDENT (1));
+ fprintf (stderr,
+ "%s\n",
+ log);
+ free (log);
+ TALER_TESTING_FAIL (tis->is);
+ }
+ TALER_TESTING_interpreter_next (tis->is);
+}
+
/**
* Callback for a POST /using-templates operation.
@@ -94,22 +240,102 @@ post_using_templates_cb (void *cls,
TALER_TESTING_interpreter_fail (tis->is);
return;
}
+ if (0 == tis->http_status)
+ {
+ TALER_LOG_DEBUG ("/using_templates, expected 0 status code\n");
+ TALER_TESTING_interpreter_next (tis->is);
+ return;
+ }
+ // check for order
switch (por->hr.http_status)
{
case MHD_HTTP_OK:
- break;
- case MHD_HTTP_CONFLICT:
+ if (NULL != por->details.ok.token)
+ tis->claim_token = *por->details.ok.token;
+ tis->order_id = GNUNET_strdup (por->details.ok.order_id);
+ if ((NULL != tis->expected_order_id) &&
+ (0 != strcmp (por->details.ok.order_id,
+ tis->expected_order_id)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Order id assigned does not match\n");
+ TALER_TESTING_interpreter_fail (tis->is);
+ return;
+ }
+ if (NULL != tis->duplicate_of)
+ {
+ const struct TALER_TESTING_Command *order_cmd;
+ const struct TALER_ClaimTokenP *prev_token;
+ struct TALER_ClaimTokenP zero_token = {0};
+
+ order_cmd = TALER_TESTING_interpreter_lookup_command (
+ tis->is,
+ tis->duplicate_of);
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_claim_token (order_cmd,
+ &prev_token))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not fetch previous order claim token\n");
+ TALER_TESTING_interpreter_fail (tis->is);
+ return;
+ }
+ if (NULL == por->details.ok.token)
+ prev_token = &zero_token;
+ if (0 != GNUNET_memcmp (prev_token,
+ por->details.ok.token))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Claim tokens for identical requests do not match\n");
+ TALER_TESTING_interpreter_fail (tis->is);
+ return;
+ }
+ }
break;
case MHD_HTTP_NOT_FOUND:
- break;
+ TALER_TESTING_interpreter_next (tis->is);
+ return;
+ case MHD_HTTP_GONE:
+ TALER_TESTING_interpreter_next (tis->is);
+ return;
+ case MHD_HTTP_CONFLICT:
+ TALER_TESTING_interpreter_next (tis->is);
+ return;
default:
- GNUNET_break (0);
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Unhandled HTTP status %u for POST /templates/$ID.\n",
- por->hr.http_status);
- break;
+ {
+ char *s = json_dumps (por->hr.reply,
+ JSON_COMPACT);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected status code from /orders: %u (%d) at %s; JSON: %s\n",
+ por->hr.http_status,
+ (int) por->hr.ec,
+ TALER_TESTING_interpreter_get_current_label (tis->is),
+ s);
+ GNUNET_free (s);
+ /**
+ * Not failing, as test cases are _supposed_
+ * to create non 200 OK situations.
+ */
+ TALER_TESTING_interpreter_next (tis->is);
+ }
+ return;
}
- TALER_TESTING_interpreter_next (tis->is);
+
+ if (! tis->with_claim)
+ {
+ TALER_TESTING_interpreter_next (tis->is);
+ return;
+ }
+ if (NULL ==
+ (tis->och = TALER_MERCHANT_order_claim (
+ TALER_TESTING_interpreter_get_context (tis->is),
+ tis->merchant_url,
+ tis->order_id,
+ &tis->nonce,
+ &tis->claim_token,
+ &using_claim_cb,
+ tis)))
+ TALER_TESTING_FAIL (tis->is);
}
@@ -128,7 +354,7 @@ post_using_templates_run (void *cls,
{
struct PostUsingTemplatesState *tis = cls;
const struct TALER_TESTING_Command *ref;
- const char **template_id;
+ const char *template_id;
tis->is = is;
ref = TALER_TESTING_interpreter_lookup_command (is,
@@ -137,10 +363,23 @@ post_using_templates_run (void *cls,
TALER_TESTING_get_trait_template_id (ref,
&template_id))
TALER_TESTING_FAIL (is);
+ if (NULL != tis->otp_ref)
+ {
+ ref = TALER_TESTING_interpreter_lookup_command (is,
+ tis->otp_ref);
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_otp_key (ref,
+ &tis->otp_key))
+ TALER_TESTING_FAIL (is);
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_otp_alg (ref,
+ &tis->otp_alg))
+ TALER_TESTING_FAIL (is);
+ }
tis->iph = TALER_MERCHANT_using_templates_post (
- is->ctx,
+ TALER_TESTING_interpreter_get_context (is),
tis->merchant_url,
- *template_id,
+ template_id,
tis->summary,
TALER_amount_is_valid (&tis->amount)
? &tis->amount
@@ -169,6 +408,16 @@ post_using_templates_traits (void *cls,
{
struct PostUsingTemplatesState *pts = cls;
struct TALER_TESTING_Trait traits[] = {
+ TALER_TESTING_make_trait_order_id (pts->order_id),
+ TALER_TESTING_make_trait_contract_terms (pts->contract_terms),
+ TALER_TESTING_make_trait_order_terms (pts->order_terms),
+ TALER_TESTING_make_trait_h_contract_terms (&pts->h_contract_terms),
+ TALER_TESTING_make_trait_merchant_sig (&pts->merchant_sig),
+ TALER_TESTING_make_trait_merchant_pub (&pts->merchant_pub),
+ TALER_TESTING_make_trait_claim_nonce (&pts->nonce),
+ TALER_TESTING_make_trait_claim_token (&pts->claim_token),
+ TALER_TESTING_make_trait_otp_key (pts->otp_key),
+ TALER_TESTING_make_trait_otp_alg (pts->otp_alg),
TALER_TESTING_trait_end (),
};
@@ -199,25 +448,144 @@ post_using_templates_cleanup (void *cls,
"POST /using-templates operation did not complete\n");
TALER_MERCHANT_using_templates_post_cancel (tis->iph);
}
+ json_decref (tis->order_terms);
+ json_decref (tis->contract_terms);
+ GNUNET_free (tis->order_id);
GNUNET_free (tis);
}
+/**
+ * Mark part of the contract terms as possible to forget.
+ *
+ * @param cls pointer to the result of the forget operation.
+ * @param object_id name of the object to forget.
+ * @param parent parent of the object at @e object_id.
+ */
+static void
+mark_forgettable (void *cls,
+ const char *object_id,
+ json_t *parent)
+{
+ GNUNET_assert (GNUNET_OK ==
+ TALER_JSON_contract_mark_forgettable (parent,
+ object_id));
+}
+
+
+/**
+ * Constructs the json for a POST using template request.
+ *
+ * @param using_template_id the name of the using_template to add, can be NULL.
+ * @param refund_deadline the deadline for refunds on this using template.
+ * @param pay_deadline the deadline for payment on this using template.
+ * @param amount the amount this using template is for.
+ * @param[out] using_template where to write the json string.
+ */
+static void
+make_order_json (const char *using_template_id,
+ struct GNUNET_TIME_Timestamp refund_deadline,
+ struct GNUNET_TIME_Timestamp pay_deadline,
+ const char *amount,
+ json_t **using_template)
+{
+ struct GNUNET_TIME_Timestamp refund = refund_deadline;
+ struct GNUNET_TIME_Timestamp pay = pay_deadline;
+ json_t *contract_terms;
+ struct TALER_Amount tamount;
+ json_t *arr;
+
+ if (NULL != amount)
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (amount,
+ &tamount));
+ /* Include required fields and some dummy objects to test forgetting. */
+ arr = json_array ();
+ GNUNET_assert (NULL != arr);
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ arr,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string (
+ "item", "speakers"))));
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ arr,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string (
+ "item", "headphones"))));
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ arr,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string (
+ "item", "earbuds"))));
+ contract_terms = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("summary",
+ "merchant-lib testcase"),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string (
+ "using_template_id", using_template_id)),
+ NULL == amount
+ ? GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("amount",
+ NULL))
+ : TALER_JSON_pack_amount ("amount",
+ &tamount),
+ GNUNET_JSON_pack_string ("fulfillment_url",
+ "https://example.com"),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_timestamp ("refund_deadline",
+ refund)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_timestamp ("pay_deadline",
+ pay)),
+ GNUNET_JSON_pack_string ("dummy_obj",
+ "EUR:1.0"),
+ GNUNET_JSON_pack_array_steal ("dummy_array",
+ arr));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_JSON_expand_path (contract_terms,
+ "$.dummy_obj",
+ &mark_forgettable,
+ NULL));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_JSON_expand_path (contract_terms,
+ "$.dummy_array[*].item",
+ &mark_forgettable,
+ NULL));
+ *using_template = contract_terms;
+}
+
+
struct TALER_TESTING_Command
TALER_TESTING_cmd_merchant_post_using_templates (
const char *label,
const char *template_ref,
+ const char *otp_ref,
const char *merchant_url,
+ const char *using_template_id,
const char *summary,
- const char *amount, unsigned int http_status)
+ const char *amount,
+ struct GNUNET_TIME_Timestamp refund_deadline,
+ struct GNUNET_TIME_Timestamp pay_deadline,
+ unsigned int http_status)
{
struct PostUsingTemplatesState *tis;
tis = GNUNET_new (struct PostUsingTemplatesState);
tis->template_ref = template_ref;
+ tis->otp_ref = otp_ref;
tis->merchant_url = merchant_url;
+ tis->using_template_id = using_template_id;
tis->http_status = http_status;
tis->summary = summary;
+ tis->with_claim = true;
+ make_order_json (using_template_id,
+ refund_deadline,
+ pay_deadline,
+ amount,
+ &tis->order_terms);
if (NULL != amount)
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (amount,