summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/taler-merchant-httpd_private-post-orders.c29
-rw-r--r--src/include/taler_merchant_testing_lib.h18
-rw-r--r--src/testing/test_merchant_api.c6
-rw-r--r--src/testing/testing_api_cmd_get_orders.c337
4 files changed, 390 insertions, 0 deletions
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c
index e7b04e3d..2772aef5 100644
--- a/src/backend/taler-merchant-httpd_private-post-orders.c
+++ b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -30,6 +30,7 @@
#include "taler-merchant-httpd_private-post-orders.h"
#include "taler-merchant-httpd_auditors.h"
#include "taler-merchant-httpd_exchanges.h"
+#include "taler-merchant-httpd_private-get-orders.h"
/**
@@ -198,6 +199,8 @@ execute_transaction (struct TMH_HandlerContext *hc,
const struct GNUNET_Uuid uuids[])
{
enum GNUNET_DB_QueryStatus qs;
+ struct GNUNET_TIME_Absolute timestamp;
+ uint64_t order_serial;
if (GNUNET_OK !=
TMH_db->start (TMH_db->cls,
@@ -249,11 +252,37 @@ execute_transaction (struct TMH_HandlerContext *hc,
return qs;
}
}
+ /* Get the order serial and timestamp for the order we just created to
+ update long-poll clients. */
+ qs = TMH_db->lookup_order_summary (TMH_db->cls,
+ hc->instance->settings.id,
+ order_id,
+ &timestamp,
+ &order_serial);
+ if (1 != qs)
+ {
+ TMH_db->rollback (TMH_db->cls);
+ return qs;
+ }
/* finally, commit transaction (note: if it fails, we ALSO re-acquire
the UUID locks, which is exactly what we want) */
qs = TMH_db->commit (TMH_db->cls);
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ /* Notify clients that have been waiting for the payment to succeed */
+ TMH_long_poll_resume (order_id,
+ hc->instance,
+ NULL);
+ TMH_notify_order_change (hc->instance,
+ order_id,
+ false, /* paid */
+ false, /* refunded */
+ false, /* wired */
+ timestamp,
+ order_serial);
+
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* 1 == success! */
+ }
return qs;
}
diff --git a/src/include/taler_merchant_testing_lib.h b/src/include/taler_merchant_testing_lib.h
index 4c7039b1..219997de 100644
--- a/src/include/taler_merchant_testing_lib.h
+++ b/src/include/taler_merchant_testing_lib.h
@@ -455,6 +455,24 @@ TALER_TESTING_cmd_merchant_get_orders (const char *label,
/**
+ * Start a long poll for GET /private/orders.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_poll_orders_start (const char *label,
+ const char *merchant_url,
+ struct GNUNET_TIME_Relative timeout);
+
+
+/**
+ * Complete a long poll for GET /private/orders.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_poll_orders_conclude (const char *label,
+ unsigned int http_status,
+ const char *poll_start_reference);
+
+
+/**
* Define a GET /orders/$ORDER_ID CMD.
*
* @param label the command label
diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c
index 3bee5bd9..501cc7e6 100644
--- a/src/testing/test_merchant_api.c
+++ b/src/testing/test_merchant_api.c
@@ -271,6 +271,9 @@ run (void *cls,
"create-reserve-1",
"EUR:0",
MHD_HTTP_OK),
+ TALER_TESTING_cmd_poll_orders_start ("poll-orders-1-start",
+ merchant_url,
+ GNUNET_TIME_UNIT_MINUTES),
TALER_TESTING_cmd_merchant_post_orders ("create-proposal-1",
merchant_url,
MHD_HTTP_OK,
@@ -283,6 +286,9 @@ run (void *cls,
\"fulfillment_url\": \"https://example.com/\",\
\"products\": [ {\"description\":\"ice cream\",\
\"value\":\"{EUR:5}\"} ] }"),
+ TALER_TESTING_cmd_poll_orders_conclude ("poll-orders-1-conclude",
+ MHD_HTTP_OK,
+ "poll-orders-1-start"),
TALER_TESTING_cmd_merchant_get_orders ("get-orders-1",
merchant_url,
MHD_HTTP_OK,
diff --git a/src/testing/testing_api_cmd_get_orders.c b/src/testing/testing_api_cmd_get_orders.c
index d4073b04..db3e20a2 100644
--- a/src/testing/testing_api_cmd_get_orders.c
+++ b/src/testing/testing_api_cmd_get_orders.c
@@ -236,4 +236,341 @@ TALER_TESTING_cmd_merchant_get_orders (const char *label,
}
+struct MerchantPollOrdersConcludeState
+{
+ /**
+ * The interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * Reference to a command that can provide a poll orders start command.
+ */
+ const char *start_reference;
+
+ /**
+ * Task to wait for the deadline.
+ */
+ struct GNUNET_SCHEDULER_Task *task;
+
+ /**
+ * Expected HTTP response status code.
+ */
+ unsigned int expected_http_status;
+};
+
+
+struct MerchantPollOrdersStartState
+{
+ /**
+ * The merchant base URL.
+ */
+ const char *merchant_url;
+
+ /**
+ * The handle to the current GET /private/orders request.
+ */
+ struct TALER_MERCHANT_OrdersGetHandle *ogh;
+
+ /**
+ * The interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * How long to wait for server to return a response.
+ */
+ struct GNUNET_TIME_Relative timeout;
+
+ /**
+ * Conclude state waiting for completion (if any).
+ */
+ struct MerchantPollOrdersConcludeState *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;
+};
+
+
+/**
+ * Task called when either the timeout for the get orders
+ * command expired or we got a response. Checks if the
+ * result is what we expected.
+ *
+ * @param cls a `struct MerchantPollOrdersConcludeState`
+ */
+static void
+conclude_task (void *cls)
+{
+ struct MerchantPollOrdersConcludeState *poc = cls;
+ const struct TALER_TESTING_Command *poll_cmd;
+ struct MerchantPollOrdersStartState *pos;
+ struct GNUNET_TIME_Absolute now;
+
+ poc->task = NULL;
+ poll_cmd =
+ TALER_TESTING_interpreter_lookup_command (poc->is,
+ poc->start_reference);
+ if (NULL == poll_cmd)
+ TALER_TESTING_FAIL (poc->is);
+ pos = poll_cmd->cls;
+ if (NULL != pos->ogh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Expected poll GET /private/orders to have completed, but it did not!\n");
+ TALER_TESTING_FAIL (poc->is);
+ }
+ if (pos->http_status != poc->expected_http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Expected HTTP status %u, got %u\n",
+ poc->expected_http_status,
+ pos->http_status);
+ TALER_TESTING_FAIL (poc->is);
+ }
+ now = GNUNET_TIME_absolute_get ();
+ if ((GNUNET_TIME_absolute_add (pos->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) pos->deadline.abs_value_us,
+ (unsigned long long) now.abs_value_us);
+ TALER_TESTING_FAIL (poc->is);
+ }
+ TALER_TESTING_interpreter_next (poc->is);
+}
+
+
+/**
+ * Callback to process a GET /orders request
+ *
+ * @param cls closure
+ * @param hr HTTP response details
+ * @param osr order status response details (on success)
+ */
+static void
+merchant_poll_orders_cb (
+ void *cls,
+ const struct TALER_MERCHANT_HttpResponse *hr,
+ unsigned int orders_length,
+ const struct TALER_MERCHANT_OrderEntry orders[])
+{
+ /* FIXME, deeper checks should be implemented here. */
+ struct MerchantPollOrdersStartState *pos = cls;
+
+ pos->ogh = NULL;
+ if (MHD_HTTP_OK != 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 (pos->is));
+ TALER_TESTING_interpreter_fail (pos->is);
+ return;
+ }
+ switch (hr->http_status)
+ {
+ case MHD_HTTP_OK:
+ // FIXME: use order references
+ // check if the data returned matches that from the POST / PATCH
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unhandled HTTP status.\n");
+ }
+ pos->http_status = hr->http_status;
+ if (NULL != pos->cs)
+ {
+ GNUNET_SCHEDULER_cancel (pos->cs->task);
+ pos->cs->task = GNUNET_SCHEDULER_add_now (&conclude_task,
+ pos->cs);
+ }
+}
+
+
+/**
+ * Run the "GET orders" CMD.
+ *
+ * @param cls closure.
+ * @param cmd command being run now.
+ * @param is interpreter state.
+ */
+static void
+merchant_poll_orders_start_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct MerchantPollOrdersStartState *pos = cls;
+
+ /* 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_orders_get2 (is->ctx,
+ pos->merchant_url,
+ TALER_EXCHANGE_YNA_ALL,
+ TALER_EXCHANGE_YNA_ALL,
+ TALER_EXCHANGE_YNA_ALL,
+ GNUNET_TIME_UNIT_ZERO_ABS,
+ 1,
+ 2,
+ pos->timeout,
+ &merchant_poll_orders_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 orders" CMD, and possibly
+ * cancel a pending operation thereof.
+ *
+ * @param cls closure.
+ * @param cmd command being run.
+ */
+static void
+merchant_poll_orders_start_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct MerchantPollOrdersStartState *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_orders_get_cancel (pos->ogh);
+ }
+ GNUNET_free (pos);
+}
+
+
+/**
+ * Start a long poll for GET /private/orders.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_poll_orders_start (const char *label,
+ const char *merchant_url,
+ struct GNUNET_TIME_Relative timeout)
+{
+ struct MerchantPollOrdersStartState *pos;
+
+ pos = GNUNET_new (struct MerchantPollOrdersStartState);
+ pos->merchant_url = merchant_url;
+ pos->timeout = timeout;
+ {
+ struct TALER_TESTING_Command cmd = {
+ .cls = pos,
+ .label = label,
+ .run = &merchant_poll_orders_start_run,
+ .cleanup = &merchant_poll_orders_start_cleanup
+ };
+
+ return cmd;
+ }
+}
+
+
+/**
+ * Run the "GET orders" CMD.
+ *
+ * @param cls closure.
+ * @param cmd command being run now.
+ * @param is interpreter state.
+ */
+static void
+merchant_poll_orders_conclude_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct MerchantPollOrdersConcludeState *poc = cls;
+ const struct TALER_TESTING_Command *poll_cmd;
+ struct MerchantPollOrdersStartState *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 == &merchant_poll_orders_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 orders" CMD, and possibly
+ * cancel a pending operation thereof.
+ *
+ * @param cls closure.
+ * @param cmd command being run.
+ */
+static void
+merchant_poll_orders_conclude_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct MerchantPollOrdersConcludeState *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);
+}
+
+
+/**
+ * Complete a long poll for GET /private/orders.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_poll_orders_conclude (const char *label,
+ unsigned int http_status,
+ const char *poll_start_reference)
+{
+ struct MerchantPollOrdersConcludeState *poc;
+
+ poc = GNUNET_new (struct MerchantPollOrdersConcludeState);
+ poc->start_reference = poll_start_reference;
+ poc->expected_http_status = http_status;
+ {
+ struct TALER_TESTING_Command cmd = {
+ .cls = poc,
+ .label = label,
+ .run = &merchant_poll_orders_conclude_run,
+ .cleanup = &merchant_poll_orders_conclude_cleanup
+ };
+
+ return cmd;
+ }
+}
+
+
/* end of testing_api_cmd_get_orders.c */