From e9f8d268447dd2f1168a9189ec16e96c0fa3deec Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 10 Nov 2019 17:21:30 +0100 Subject: add test for long polling on check-payment --- src/include/taler_merchant_testing_lib.h | 36 ++++ src/lib/test_merchant_api.c | 14 +- src/lib/testing_api_cmd_check_payment.c | 306 +++++++++++++++++++++++++++++-- src/lib/testing_api_cmd_poll_payment.c | 3 +- 4 files changed, 337 insertions(+), 22 deletions(-) diff --git a/src/include/taler_merchant_testing_lib.h b/src/include/taler_merchant_testing_lib.h index ba0c927d..30b1bdf5 100644 --- a/src/include/taler_merchant_testing_lib.h +++ b/src/include/taler_merchant_testing_lib.h @@ -120,6 +120,42 @@ TALER_TESTING_cmd_check_payment (const char *label, const char *proposal_reference, unsigned int expect_paid); + +/** + * Make a "check payment" test command with long polling support. + * + * @param label command label. + * @param merchant_url merchant base url + * @param proposal_reference the proposal whose payment status + * is going to be checked. + * @param timeout how long to wait during long polling for the reply + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_check_payment_start (const char *label, + const char *merchant_url, + const char *proposal_reference, + struct GNUNET_TIME_Relative timeout); + + +/** + * Expect completion of a long-polled "check payment" test command. + * + * @param label command label. + * @param check_start_reference payment start operation that should have + * completed + * @param http_status expected HTTP response code. + * @param expect_paid #GNUNET_YES if we expect the proposal to be + * paid, #GNUNET_NO otherwise. + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_check_payment_conclude (const char *label, + unsigned int http_status, + const char *poll_start_reference, + unsigned int expect_paid); + + /** * Start a long-polled "poll-payment" test command. * diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c index 56cbb207..359f20d0 100644 --- a/src/lib/test_merchant_api.c +++ b/src/lib/test_merchant_api.c @@ -295,6 +295,10 @@ run (void *cls, merchant_url, "create-proposal-1", GNUNET_TIME_UNIT_MINUTES), + TALER_TESTING_cmd_check_payment_start ("check-payment-2", + merchant_url, + "create-proposal-1", + GNUNET_TIME_UNIT_MINUTES), TALER_TESTING_cmd_pay ("deposit-simple", merchant_url, MHD_HTTP_OK, @@ -307,12 +311,10 @@ run (void *cls, MHD_HTTP_OK, "poll-payment-2", GNUNET_YES), - TALER_TESTING_cmd_check_payment ("check-payment-2", - merchant_url, - MHD_HTTP_OK, - "create-proposal-1", - GNUNET_YES), - + TALER_TESTING_cmd_check_payment_conclude ("check-payment-conclude-2", + MHD_HTTP_OK, + "check-payment-2", + GNUNET_YES), TALER_TESTING_cmd_pay_abort ("pay-abort-2", merchant_url, "deposit-simple", diff --git a/src/lib/testing_api_cmd_check_payment.c b/src/lib/testing_api_cmd_check_payment.c index 2e988d75..c879b784 100644 --- a/src/lib/testing_api_cmd_check_payment.c +++ b/src/lib/testing_api_cmd_check_payment.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2018 Taler Systems SA + Copyright (C) 2014-2019 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 @@ -16,7 +16,6 @@ License along with TALER; see the file COPYING. If not, see */ - /** * @file lib/testing_api_cmd_check_payment.c * @brief command to test the /check-payment feature. @@ -30,6 +29,11 @@ #include "taler_merchant_testing_lib.h" +/** + * State for a /check-payment conclude CMD. + */ +struct CheckPaymentConcludeState; + /** * State for a /check-payment CMD. */ @@ -47,9 +51,9 @@ struct CheckPaymentState struct TALER_TESTING_Interpreter *is; /** - * Expected HTTP response status code. + * The merchant base URL. */ - unsigned int http_status; + const char *merchant_url; /** * Reference to a command that can provide a order id, @@ -58,14 +62,79 @@ struct CheckPaymentState const char *proposal_reference; /** - * GNUNET_YES if we expect the proposal was paid. + * State for a /check-payment conclude CMD. + */ + struct CheckPaymentConcludeState *cs; + + /** + * 0 if long-polling is not desired. */ - unsigned int expect_paid; + struct GNUNET_TIME_Relative timeout; /** - * The merchant base URL. + * Set to the start time of the @e cpo plus the @e timeout. */ - const char *merchant_url; + struct GNUNET_TIME_Absolute deadline; + + /** + * #GNUNET_YES if we expect the proposal was paid, synchronous variant. + */ + int expect_paid; + + /** + * #GNUNET_YES if the proposal was paid. + */ + int paid; + + /** + * #GNUNET_YES if the proposal was paid and then refunded + */ + int refunded; + + /** + * Observed HTTP response status code. + */ + unsigned int http_status; + + /** + * Expected HTTP response status code, synchronous variant. + */ + unsigned int expected_http_status; + +}; + + +/** + * State for a /check-payment conclude CMD. + */ +struct CheckPaymentConcludeState +{ + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Reference to a command that can provide a check payment 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; + + /** + * #GNUNET_YES if the proposal was expected to be paid. + */ + int expected_paid; + }; @@ -94,6 +163,64 @@ check_payment_cleanup (void *cls, } +/** + * Task called when either the timeout for the /check-payment + * command expired or we got a response. Checks if the + * result is what we expected. + * + * @param cls a `struct CheckPaymentConcludeState` + */ +static void +conclude_task (void *cls) +{ + struct CheckPaymentConcludeState *cpc = cls; + const struct TALER_TESTING_Command *check_cmd; + struct CheckPaymentState *cps; + struct GNUNET_TIME_Absolute now; + + cpc->task = NULL; + check_cmd = + TALER_TESTING_interpreter_lookup_command (cpc->is, + cpc->start_reference); + cps = check_cmd->cls; + if (NULL != cps->cpo) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expected /poll/payment to have completed, but it did not!\n"); + TALER_TESTING_FAIL (cpc->is); + } + if (cps->http_status != cpc->expected_http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expected HTTP status %u, got %u\n", + cpc->expected_http_status, + cps->http_status); + TALER_TESTING_FAIL (cps->is); + } + now = GNUNET_TIME_absolute_get (); + if ( (GNUNET_NO == cps->paid) && + (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 (cps->is); + } + if (cps->paid != cpc->expected_paid) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expected paid status %u, got %u\n", + cpc->expected_paid, + cps->paid); + TALER_TESTING_FAIL (cps->is); + } + TALER_TESTING_interpreter_next (cps->is); +} + + /** * Callback for a /check-payment request. * @@ -127,11 +254,28 @@ check_payment_cb (void *cls, cps->expect_paid, paid, taler_pay_uri); - if (paid != cps->expect_paid) - TALER_TESTING_FAIL (cps->is); - if (cps->http_status != http_status) - TALER_TESTING_FAIL (cps->is); - TALER_TESTING_interpreter_next (cps->is); + cps->paid = paid; + cps->http_status = http_status; + cps->refunded = refunded; + if (0 == cps->timeout.rel_value_us) + { + /* synchronous variant */ + if (paid != cps->expect_paid) + TALER_TESTING_FAIL (cps->is); + if (cps->http_status != http_status) + TALER_TESTING_FAIL (cps->is); + TALER_TESTING_interpreter_next (cps->is); + } + else + { + /* asynchronous variant */ + if (NULL != cps->cs) + { + GNUNET_SCHEDULER_cancel (cps->cs->task); + cps->cs->task = GNUNET_SCHEDULER_add_now (&conclude_task, + cps->cs); + } + } } @@ -166,10 +310,12 @@ check_payment_run (void *cls, cps->merchant_url, order_id, NULL, - GNUNET_TIME_UNIT_ZERO, + cps->timeout, &check_payment_cb, cps); GNUNET_assert (NULL != cps->cpo); + if (0 != cps->timeout.rel_value_us) + TALER_TESTING_interpreter_next (cps->is); } @@ -195,7 +341,7 @@ TALER_TESTING_cmd_check_payment (const char *label, struct CheckPaymentState *cps; cps = GNUNET_new (struct CheckPaymentState); - cps->http_status = http_status; + cps->expected_http_status = http_status; cps->proposal_reference = proposal_reference; cps->expect_paid = expect_paid; cps->merchant_url = merchant_url; @@ -212,4 +358,134 @@ TALER_TESTING_cmd_check_payment (const char *label, } +/** + * Make a "check payment" test command with long polling support. + * + * @param label command label. + * @param merchant_url merchant base url + * @param proposal_reference the proposal whose payment status + * is going to be checked. + * @param timeout how long to wait during long polling for the reply + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_check_payment_start (const char *label, + const char *merchant_url, + const char *proposal_reference, + struct GNUNET_TIME_Relative timeout) +{ + struct CheckPaymentState *cps; + + if (0 == timeout.rel_value_us) + timeout.rel_value_us = 1; /* 0 reserved for blocking version */ + cps = GNUNET_new (struct CheckPaymentState); + cps->timeout = timeout; + cps->proposal_reference = proposal_reference; + cps->merchant_url = merchant_url; + { + struct TALER_TESTING_Command cmd = { + .cls = cps, + .label = label, + .run = &check_payment_run, + .cleanup = &check_payment_cleanup + }; + + return cmd; + } +} + + +/** + * Free a /check-payment conclusion CMD, and possibly cancel a pending + * operation thereof. + * + * @param cls closure + * @param cmd the command currently getting freed. + */ +static void +check_payment_conclude_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct CheckPaymentConcludeState *cps = cls; + + if (NULL != cps->task) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Command `%s' was not terminated\n", + TALER_TESTING_interpreter_get_current_label ( + cps->is)); + GNUNET_SCHEDULER_cancel (cps->task); + cps->task = NULL; + } +} + + +/** + * Run a /check-payment conclusion CMD. + * + * @param cmd the command currenly being run. + * @param cls closure. + * @param is interpreter state. + */ +static void +check_payment_conclude_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct CheckPaymentConcludeState *cpc = cls; + const struct TALER_TESTING_Command *check_cmd; + struct CheckPaymentState *cps; + + cpc->is = is; + check_cmd = + TALER_TESTING_interpreter_lookup_command (is, + cpc->start_reference); + GNUNET_assert (check_cmd->run == &check_payment_run); + cps = check_cmd->cls; + if (NULL == cps->cpo) + cpc->task = GNUNET_SCHEDULER_add_now (&conclude_task, + cpc); + else + cpc->task = GNUNET_SCHEDULER_add_at (cps->deadline, + &conclude_task, + cpc); +} + + +/** + * Expect completion of a long-polled "check payment" test command. + * + * @param label command label. + * @param check_start_reference payment start operation that should have + * completed + * @param http_status expected HTTP response code. + * @param expect_paid #GNUNET_YES if we expect the proposal to be + * paid, #GNUNET_NO otherwise. + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_check_payment_conclude (const char *label, + unsigned int http_status, + const char *poll_start_reference, + unsigned int expect_paid) +{ + struct CheckPaymentConcludeState *cps; + + cps = GNUNET_new (struct CheckPaymentConcludeState); + cps->start_reference = poll_start_reference; + cps->expected_paid = expect_paid; + cps->expected_http_status = http_status; + { + struct TALER_TESTING_Command cmd = { + .cls = cps, + .label = label, + .run = &check_payment_conclude_run, + .cleanup = &check_payment_conclude_cleanup + }; + + return cmd; + } +} + + /* end of testing_api_cmd_check_payment.c */ diff --git a/src/lib/testing_api_cmd_poll_payment.c b/src/lib/testing_api_cmd_poll_payment.c index c6200fb8..40bc79ec 100644 --- a/src/lib/testing_api_cmd_poll_payment.c +++ b/src/lib/testing_api_cmd_poll_payment.c @@ -268,7 +268,7 @@ poll_payment_cb (void *cls, cps->paid = paid; cps->http_status = http_status; cps->refunded = refunded; - if (refunded) + if (GNUNET_YES == refunded) cps->refund = *refund_amount; if (NULL != cps->cs) { @@ -415,6 +415,7 @@ poll_payment_conclude_run (void *cls, poll_cmd = TALER_TESTING_interpreter_lookup_command (is, ppc->start_reference); + GNUNET_assert (poll_cmd->run == &poll_payment_start_run); cps = poll_cmd->cls; if (NULL == cps->cpo) ppc->task = GNUNET_SCHEDULER_add_now (&conclude_task, -- cgit v1.2.3