diff options
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-orders.c | 29 | ||||
-rw-r--r-- | src/include/taler_merchant_testing_lib.h | 18 | ||||
-rw-r--r-- | src/testing/test_merchant_api.c | 6 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_get_orders.c | 337 |
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, + ×tamp, + &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 */ |