aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-10-14 23:02:02 +0200
committerChristian Grothoff <christian@grothoff.org>2020-10-14 23:02:02 +0200
commite98ca0d39480b9cb23f7d0f620d9ca10d9afa7aa (patch)
tree3c44c2f95afab72cb3ff64f7311a74fb26b56135
parentbf9641e1512338edd48624f23e6be356cf251ef4 (diff)
implementing long-polling test commands for wallet get order operation (#6466)
-rw-r--r--src/backend/taler-merchant-httpd_get-orders-ID.c8
-rw-r--r--src/include/taler_merchant_service.h6
-rw-r--r--src/include/taler_merchant_testing_lib.h37
-rw-r--r--src/testing/testing_api_cmd_merchant_get_order.c20
-rw-r--r--src/testing/testing_api_cmd_wallet_get_order.c444
5 files changed, 492 insertions, 23 deletions
diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.c b/src/backend/taler-merchant-httpd_get-orders-ID.c
index 42e56200..82404f6a 100644
--- a/src/backend/taler-merchant-httpd_get-orders-ID.c
+++ b/src/backend/taler-merchant-httpd_get-orders-ID.c
@@ -114,14 +114,14 @@ struct GetOrderData
struct TALER_Amount refund_amount;
/**
- * Did we suspend @a connection?
+ * Return code: #TALER_EC_NONE if successful.
*/
- bool suspended;
+ enum TALER_ErrorCode ec;
/**
- * Return code: #TALER_EC_NONE if successful.
+ * Did we suspend @a connection?
*/
- enum TALER_ErrorCode ec;
+ bool suspended;
/**
* Set to true if we are dealing with an unclaimed order
diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h
index e34226a0..cd16caca 100644
--- a/src/include/taler_merchant_service.h
+++ b/src/include/taler_merchant_service.h
@@ -1466,7 +1466,7 @@ struct TALER_MERCHANT_OrderWalletGetHandle;
/**
- * Callback to process a GET /orders/$ID request
+ * Callback to process a GET /orders/$ID response
*
* @param cls closure
* @param hr HTTP response details
@@ -1509,7 +1509,7 @@ typedef void
* @param timeout timeout to use in long polling (how long may the server wait to reply
* before generating an unpaid response). Note that this is just provided to
* the server, we as client will block until the response comes back or until
- * #TALER_MERCHANT_order_get_cancel() is called.
+ * #TALER_MERCHANT_wallet_order_get_cancel() is called.
* @param session_id for which session should the payment status be checked. Use
* NULL to disregard sessions.
* @param min_refund long poll for the service to approve a refund exceeding this value;
@@ -1825,7 +1825,7 @@ typedef void
* @param timeout timeout to use in long polling (how long may the server wait to reply
* before generating an unpaid response). Note that this is just provided to
* the server, we as client will block until the response comes back or until
- * #TALER_MERCHANT_order_get_cancel() is called.
+ * #TALER_MERCHANT_merchant_order_get_cancel() is called.
* @param cb callback which will work the response gotten from the backend
* @param cb_cls closure to pass to @a cb
* @return handle for this operation, NULL upon errors
diff --git a/src/include/taler_merchant_testing_lib.h b/src/include/taler_merchant_testing_lib.h
index ac500766..8b53c324 100644
--- a/src/include/taler_merchant_testing_lib.h
+++ b/src/include/taler_merchant_testing_lib.h
@@ -575,6 +575,13 @@ TALER_TESTING_cmd_merchant_get_orders (const char *label,
/**
* Start a long poll for GET /private/orders.
+ *
+ * FIXME: needs additional arguments to specify range to long poll for!
+ *
+ * @param label the command label
+ * @param merchant_url base URL of the merchant which will
+ * serve the request.
+ * @param timeout how long to wait for the request to complete
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_poll_orders_start (const char *label,
@@ -584,6 +591,10 @@ TALER_TESTING_cmd_poll_orders_start (const char *label,
/**
* Complete a long poll for GET /private/orders.
+ *
+ * @param label the command label
+ * @param http_status expected HTTP response code
+ * @param poll_start_reference reference to the #TALER_TESTING_cmd_poll_orders_start command
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_poll_orders_conclude (const char *label,
@@ -614,23 +625,39 @@ TALER_TESTING_cmd_wallet_get_order (const char *label,
/**
- * Start a long poll for GET /private/orders/$ORDER_ID.
+ * Start a long poll for GET /orders/$ORDER_ID.
+ *
+ * @param label the command label
+ * @param merchant_url base URL of the merchant which will
+ * serve the request.
+ * @param order_ref reference to a command that created an order.
+ * @param timeout how long to wait for the request to complete
+ * @param await_refund NULL to not wait for refund, amount of value
+ * zero to wait for any refund amount, non-zero to poll
+ * for refund exceeding the given amount
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_wallet_poll_order_start (
const char *label,
const char *merchant_url,
- const char *order_id,
- struct GNUNET_TIME_Relative timeout);
+ const char *order_ref,
+ struct GNUNET_TIME_Relative timeout,
+ const struct TALER_Amount *await_refund);
/**
- * Complete a long poll for GET /private/orders/$ORDER_ID.
+ * Complete a long poll for GET /orders/$ORDER_ID.
+ *
+ * @param label the command label
+ * @param expected_http_status expected HTTP response code
+ * @param expected_refund_amount refund expected, NULL for no refund expected
+ * @param poll_start_reference reference to the #TALER_TESTING_cmd_wallet_poll_order_start command
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_wallet_poll_order_conclude (
const char *label,
- unsigned int http_status,
+ unsigned int expected_http_status,
+ const char *expected_refund_amount,
const char *poll_start_reference);
diff --git a/src/testing/testing_api_cmd_merchant_get_order.c b/src/testing/testing_api_cmd_merchant_get_order.c
index 241562e0..002442ac 100644
--- a/src/testing/testing_api_cmd_merchant_get_order.c
+++ b/src/testing/testing_api_cmd_merchant_get_order.c
@@ -64,11 +64,6 @@ struct MerchantGetOrderState
enum TALER_MERCHANT_OrderStatusCode osc;
/**
- * Whether the order was refunded or not.
- */
- bool refunded;
-
- /**
* A NULL-terminated list of refunds associated with this order.
*/
const char **refunds;
@@ -79,11 +74,6 @@ struct MerchantGetOrderState
unsigned int refunds_length;
/**
- * Whether the order was wired or not.
- */
- bool wired;
-
- /**
* A NULL-terminated list of transfers associated with this order.
*/
const char **transfers;
@@ -102,6 +92,16 @@ struct MerchantGetOrderState
* The length of @e forgets.
*/
unsigned int forgets_length;
+
+ /**
+ * Whether the order was refunded or not.
+ */
+ bool refunded;
+
+ /**
+ * Whether the order was wired or not.
+ */
+ bool wired;
};
diff --git a/src/testing/testing_api_cmd_wallet_get_order.c b/src/testing/testing_api_cmd_wallet_get_order.c
index d667f101..56ff4746 100644
--- a/src/testing/testing_api_cmd_wallet_get_order.c
+++ b/src/testing/testing_api_cmd_wallet_get_order.c
@@ -149,7 +149,7 @@ wallet_get_order_cb (
TALER_TESTING_interpreter_fail (gos->is);
return;
}
- if (!paid_b)
+ if (! paid_b)
{
/* FIXME: Check all of the members of `pud` */
struct TALER_MERCHANT_PayUriData pud;
@@ -331,4 +331,446 @@ TALER_TESTING_cmd_wallet_get_order (const char *label,
}
+struct WalletPollOrderConcludeState
+{
+ /**
+ * The interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * Reference to a command that can provide a poll order start command.
+ */
+ const char *start_reference;
+
+ /**
+ * Task to wait for the deadline.
+ */
+ struct GNUNET_SCHEDULER_Task *task;
+
+ /**
+ * Amount of a refund expected.
+ */
+ struct TALER_Amount expected_refund_amount;
+
+ /**
+ * Expected HTTP response status code.
+ */
+ unsigned int expected_http_status;
+
+ /**
+ * Are we expecting a refund?
+ */
+ bool expected_refund;
+};
+
+
+struct WalletPollOrderStartState
+{
+ /**
+ * The merchant base URL.
+ */
+ const char *merchant_url;
+
+ /**
+ * The handle to the current GET /orders/$ORDER_ID request.
+ */
+ struct TALER_MERCHANT_OrderWalletGetHandle *ogh;
+
+ /**
+ * The interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * Reference to a command that created an order.
+ */
+ const char *order_ref;
+
+ /**
+ * How long to wait for server to return a response.
+ */
+ struct GNUNET_TIME_Relative timeout;
+
+ /**
+ * Conclude state waiting for completion (if any).
+ */
+ struct WalletPollOrderConcludeState *cs;
+
+ /**
+ * The HTTP status code returned by the backend.
+ */
+ unsigned int http_status;
+
+ /**
+ * When the request should be completed by.
+ */
+ struct GNUNET_TIME_Absolute deadline;
+
+ /**
+ * Minimum refund to wait for.
+ */
+ struct TALER_Amount refund_threshold;
+
+ /**
+ * Available refund as returned by the merchant.
+ */
+ struct TALER_Amount refund_available;
+
+ /**
+ * Should we poll for a refund?
+ */
+ bool wait_for_refund;
+
+ /**
+ * Did we receive a refund according to response from the merchant?
+ */
+ bool refunded;
+
+ /**
+ * Was the order paid according to response from the merchant?
+ */
+ bool paid;
+
+ /**
+ * Has the order a pending refund according to response from the merchant?
+ */
+ bool refund_pending;
+};
+
+
+/**
+ * Task called when either the timeout for the GET /private/order/<ID> command
+ * expired or we got a response. Checks if the result is what we expected.
+ *
+ * @param cls a `struct WalletPollOrderConcludeState`
+ */
+static void
+conclude_task (void *cls)
+{
+ struct WalletPollOrderConcludeState *ppc = cls;
+ const struct TALER_TESTING_Command *poll_cmd;
+ struct WalletPollOrderStartState *cps;
+ struct GNUNET_TIME_Absolute now;
+
+ ppc->task = NULL;
+ poll_cmd =
+ TALER_TESTING_interpreter_lookup_command (ppc->is,
+ ppc->start_reference);
+ if (NULL == poll_cmd)
+ TALER_TESTING_FAIL (ppc->is);
+ cps = poll_cmd->cls;
+ if (NULL != cps->ogh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Expected poll GET /orders/$ORDER_ID to have completed, but it did not!\n");
+ TALER_TESTING_FAIL (ppc->is);
+ }
+ if (cps->http_status != ppc->expected_http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Expected HTTP status %u, got %u\n",
+ ppc->expected_http_status,
+ cps->http_status);
+ TALER_TESTING_FAIL (ppc->is);
+ }
+ if (ppc->expected_refund != cps->refunded)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Order was %srefunded, contrary to our expectations\n",
+ cps->refunded ? "" : "NOT ");
+ TALER_TESTING_FAIL (ppc->is);
+ }
+ if (cps->refunded)
+ {
+ if (0 != TALER_amount_cmp (&ppc->expected_refund_amount,
+ &cps->refund_available))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Refund amount %s does not match our expectation!\n",
+ TALER_amount2s (&cps->refund_available));
+ TALER_TESTING_FAIL (ppc->is);
+ }
+ }
+ // FIXME: add checks for cps->paid/refund_available status flags?
+ now = GNUNET_TIME_absolute_get ();
+ if ((GNUNET_TIME_absolute_add (cps->deadline,
+ GNUNET_TIME_UNIT_SECONDS).abs_value_us <
+ now.abs_value_us) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Expected answer to be delayed until %llu, but got response at %llu\n",
+ (unsigned long long) cps->deadline.abs_value_us,
+ (unsigned long long) now.abs_value_us);
+ TALER_TESTING_FAIL (ppc->is);
+ }
+ TALER_TESTING_interpreter_next (ppc->is);
+}
+
+
+/**
+ * Process response from a GET /orders/$ID request
+ *
+ * @param cls a `struct WalletPollOrderStartState *`
+ * @param hr HTTP response details
+ * @param paid #GNUNET_YES if the payment is settled, #GNUNET_NO if not
+ * settled, #GNUNET_SYSERR on error
+ * (note that refunded payments are returned as paid!)
+ * @param refunded #GNUNET_YES if there is at least on refund on this payment,
+ * #GNUNET_NO if refunded, #GNUNET_SYSERR or error
+ * @param refund_pending #GNUNET_YES if there are refunds waiting to be
+ * obtained, #GNUNET_NO if all refunds have been obtained, #GNUNET_SYSERR
+ * on error.
+ * @param refunded_amount amount that was refunded, NULL if there
+ * was no refund
+ * @param taler_pay_uri the URI that instructs the wallets to process
+ * the payment
+ * @param already_paid_order_id equivalent order that this customer
+ * paid already, or NULL for none
+ */
+static void
+wallet_poll_order_cb (
+ void *cls,
+ const struct TALER_MERCHANT_HttpResponse *hr,
+ enum GNUNET_GenericReturnValue paid,
+ enum GNUNET_GenericReturnValue refunded,
+ enum GNUNET_GenericReturnValue refund_pending,
+ struct TALER_Amount *refund_amount,
+ const char *taler_pay_uri,
+ const char *already_paid_order_id)
+{
+ struct WalletPollOrderStartState *pos = cls;
+
+ pos->ogh = NULL;
+ pos->http_status = hr->http_status;
+ switch (hr->http_status)
+ {
+ case MHD_HTTP_OK:
+ pos->paid = (GNUNET_YES == paid);
+ pos->refunded = (GNUNET_YES == refunded);
+ pos->refund_pending = (GNUNET_YES == refund_pending);
+ if (NULL != refund_amount)
+ pos->refund_available = *refund_amount;
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unhandled HTTP status.\n");
+ break;
+ }
+ if (NULL != pos->cs)
+ {
+ GNUNET_SCHEDULER_cancel (pos->cs->task);
+ pos->cs->task = GNUNET_SCHEDULER_add_now (&conclude_task,
+ pos->cs);
+ }
+}
+
+
+/**
+ * Run the "GET order" CMD.
+ *
+ * @param cls closure.
+ * @param cmd command being run now.
+ * @param is interpreter state.
+ */
+static void
+wallet_poll_order_start_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct WalletPollOrderStartState *pos = cls;
+ const struct TALER_TESTING_Command *order_cmd;
+ const char *order_id;
+ const struct GNUNET_HashCode *h_contract;
+
+ order_cmd = TALER_TESTING_interpreter_lookup_command (
+ is,
+ pos->order_ref);
+
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_order_id (order_cmd,
+ 0,
+ &order_id))
+ TALER_TESTING_FAIL (is);
+
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_h_contract_terms (order_cmd,
+ 0,
+ &h_contract))
+ TALER_TESTING_FAIL (is);
+
+ /* add 1s grace time to timeout */
+ pos->deadline
+ = GNUNET_TIME_absolute_add (GNUNET_TIME_relative_to_absolute (pos->timeout),
+ GNUNET_TIME_UNIT_SECONDS);
+ pos->is = is;
+ pos->ogh = TALER_MERCHANT_wallet_order_get (is->ctx,
+ pos->merchant_url,
+ order_id,
+ h_contract,
+ pos->timeout,
+ NULL,
+ pos->wait_for_refund
+ ? &pos->refund_threshold
+ : NULL,
+ pos->wait_for_refund,
+ &wallet_poll_order_cb,
+ pos);
+ GNUNET_assert (NULL != pos->ogh);
+ /* We CONTINUE to run the interpreter while the long-polled command
+ completes asynchronously! */
+ TALER_TESTING_interpreter_next (pos->is);
+}
+
+
+/**
+ * Free the state of a "GET order" CMD, and possibly
+ * cancel a pending operation thereof.
+ *
+ * @param cls closure.
+ * @param cmd command being run.
+ */
+static void
+wallet_poll_order_start_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct WalletPollOrderStartState *pos = cls;
+
+ if (NULL != pos->ogh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Command `%s' was not terminated\n",
+ TALER_TESTING_interpreter_get_current_label (
+ pos->is));
+ TALER_MERCHANT_wallet_order_get_cancel (pos->ogh);
+ }
+ GNUNET_free (pos);
+}
+
+
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_wallet_poll_order_start (
+ const char *label,
+ const char *merchant_url,
+ const char *order_ref,
+ struct GNUNET_TIME_Relative timeout,
+ const struct TALER_Amount *await_refund)
+{
+ struct WalletPollOrderStartState *pos;
+
+ pos = GNUNET_new (struct WalletPollOrderStartState);
+ pos->order_ref = order_ref;
+ pos->merchant_url = merchant_url;
+ pos->timeout = timeout;
+ if (NULL != await_refund)
+ {
+ pos->wait_for_refund = true;
+ pos->refund_threshold = *await_refund;
+ }
+ {
+ struct TALER_TESTING_Command cmd = {
+ .cls = pos,
+ .label = label,
+ .run = &wallet_poll_order_start_run,
+ .cleanup = &wallet_poll_order_start_cleanup
+ };
+
+ return cmd;
+ }
+}
+
+
+/**
+ * Run the "GET order conclude" CMD.
+ *
+ * @param cls closure.
+ * @param cmd command being run now.
+ * @param is interpreter state.
+ */
+static void
+wallet_poll_order_conclude_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct WalletPollOrderConcludeState *poc = cls;
+ const struct TALER_TESTING_Command *poll_cmd;
+ struct WalletPollOrderStartState *pos;
+
+ poc->is = is;
+ poll_cmd =
+ TALER_TESTING_interpreter_lookup_command (is,
+ poc->start_reference);
+ if (NULL == poll_cmd)
+ TALER_TESTING_FAIL (poc->is);
+ GNUNET_assert (poll_cmd->run == &wallet_poll_order_start_run);
+ pos = poll_cmd->cls;
+ pos->cs = poc;
+ if (NULL == pos->ogh)
+ poc->task = GNUNET_SCHEDULER_add_now (&conclude_task,
+ poc);
+ else
+ poc->task = GNUNET_SCHEDULER_add_at (pos->deadline,
+ &conclude_task,
+ poc);
+}
+
+
+/**
+ * Free the state of a "GET order" CMD, and possibly
+ * cancel a pending operation thereof.
+ *
+ * @param cls closure.
+ * @param cmd command being run.
+ */
+static void
+wallet_poll_order_conclude_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct WalletPollOrderConcludeState *poc = cls;
+
+ if (NULL != poc->task)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Command `%s' was not terminated\n",
+ TALER_TESTING_interpreter_get_current_label (
+ poc->is));
+ GNUNET_SCHEDULER_cancel (poc->task);
+ poc->task = NULL;
+ }
+ GNUNET_free (poc);
+}
+
+
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_wallet_poll_order_conclude (
+ const char *label,
+ unsigned int expected_http_status,
+ const char *expected_refund_amount,
+ const char *poll_start_reference)
+{
+ struct WalletPollOrderConcludeState *cps;
+
+ cps = GNUNET_new (struct WalletPollOrderConcludeState);
+ cps->start_reference = poll_start_reference;
+ cps->expected_http_status = expected_http_status;
+ if (NULL != expected_refund_amount)
+ {
+ cps->expected_refund = true;
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (expected_refund_amount,
+ &cps->expected_refund_amount));
+ }
+ {
+ struct TALER_TESTING_Command cmd = {
+ .cls = cps,
+ .label = label,
+ .run = &wallet_poll_order_conclude_run,
+ .cleanup = &wallet_poll_order_conclude_cleanup
+ };
+
+ return cmd;
+ }
+}
+
+
/* end of testing_api_cmd_wallet_get_order.c */