From cde317f8e848c61e399c6c3e93264b1736f33e74 Mon Sep 17 00:00:00 2001 From: Jonathan Buchanan Date: Fri, 26 Jun 2020 23:58:55 -0400 Subject: more docs/thorough checks for backend db & long polling for merchant get order --- src/backenddb/test_merchantdb.c | 541 ++++++++++++++++++----- src/include/taler_merchant_testing_lib.h | 18 + src/testing/test_merchant_api.c | 7 + src/testing/testing_api_cmd_merchant_get_order.c | 340 ++++++++++++++ 4 files changed, 795 insertions(+), 111 deletions(-) diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c index 3765088e..cc7ca659 100644 --- a/src/backenddb/test_merchantdb.c +++ b/src/backenddb/test_merchantdb.c @@ -57,6 +57,9 @@ static struct TALER_MERCHANTDB_Plugin *plugin; ) +/* ********** Instances ********** */ + + /** * Container for instance settings along with keys. */ @@ -79,7 +82,6 @@ struct InstanceData }; -/* Instances */ /** * Creates data for an instance to use with testing. * @@ -97,12 +99,12 @@ make_instance (char *instance_id, instance->instance.name = "Test"; instance->instance.address = json_array (); GNUNET_assert (NULL != instance->instance.address); - GNUNET_assert (0 == json_array_append (instance->instance.address, - json_string ("123 Example St"))); + GNUNET_assert (0 == json_array_append_new (instance->instance.address, + json_string ("123 Example St"))); instance->instance.jurisdiction = json_array (); GNUNET_assert (NULL != instance->instance.jurisdiction); - GNUNET_assert (0 == json_array_append (instance->instance.jurisdiction, - json_string ("Ohio"))); + GNUNET_assert (0 == json_array_append_new (instance->instance.jurisdiction, + json_string ("Ohio"))); GNUNET_assert (GNUNET_OK == TALER_string_to_amount ("EUR:1200.40", &instance->instance. @@ -401,6 +403,13 @@ test_lookup_instances (bool active_only, } +/** + * Tests removing the private key of the given instance from the database. + * + * @param instance the instance whose private key to delete. + * @param expected_result the result we expect the db to return. + * @return 0 on success, 1 otherwise. + */ static int test_delete_instance_private_key (const struct InstanceData *instance, enum GNUNET_DB_QueryStatus expected_result) @@ -414,6 +423,14 @@ test_delete_instance_private_key (const struct InstanceData *instance, } +/** + * Tests inserting an account for a merchant instance. + * + * @param instance the instance to associate the account with. + * @param account the account to insert. + * @param expected_result the result expected from the db. + * @return 0 on success, 1 otherwise. + */ static int test_insert_account (const struct InstanceData *instance, const struct TALER_MERCHANTDB_AccountDetails *account, @@ -428,6 +445,13 @@ test_insert_account (const struct InstanceData *instance, } +/** + * Tests deactivating an account. + * + * @param account the account to inactivate. + * @param expected_result the result expected from the plugin. + * @return 0 on success, 1 otherwise. + */ static int test_inactivate_account (const struct TALER_MERCHANTDB_AccountDetails *account, enum GNUNET_DB_QueryStatus expected_result) @@ -445,8 +469,14 @@ test_inactivate_account (const struct TALER_MERCHANTDB_AccountDetails *account, */ struct TestInstances_Closure { + /** + * The list of instances that we use for testing instances. + */ struct InstanceData instances[2]; + /** + * The list of accounts to use with the instances. + */ struct TALER_MERCHANTDB_AccountDetails accounts[2]; }; @@ -454,6 +484,8 @@ struct TestInstances_Closure /** * Sets up the data structures used in the instance tests + * + * @cls the closure to initialize with test data. */ static void pre_test_instances (struct TestInstances_Closure *cls) @@ -472,6 +504,8 @@ pre_test_instances (struct TestInstances_Closure *cls) /** * Handles all teardown after testing + * + * @cls the closure to free data from */ static void post_test_instances (struct TestInstances_Closure *cls) @@ -485,6 +519,7 @@ post_test_instances (struct TestInstances_Closure *cls) * Function that tests instances. * * @param cls closure with config + * @return 0 on success, 1 if failure. */ static int run_test_instances (struct TestInstances_Closure *cls) @@ -515,19 +550,41 @@ run_test_instances (struct TestInstances_Closure *cls) instances)); /* Test update instance */ cls->instances[0].instance.name = "Test - updated"; + json_array_append_new (cls->instances[0].instance.address, + json_pack ("{s:s, s:I}", + "this", "is", + "more data", 47)); + json_array_append_new (cls->instances[0].instance.jurisdiction, + json_pack ("{s:s}", + "vegetables", "bad")); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:0.04", + &cls->instances[0].instance. + default_max_deposit_fee)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:1.23", + &cls->instances[0].instance. + default_max_wire_fee)); + cls->instances[0].instance.default_wire_fee_amortization = 2; + cls->instances[0].instance.default_wire_transfer_delay = + GNUNET_TIME_UNIT_HOURS; + cls->instances[0].instance.default_pay_delay = GNUNET_TIME_UNIT_MINUTES; + TEST_RET_ON_FAIL (test_update_instance (&cls->instances[0], GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); TEST_RET_ON_FAIL (test_lookup_instances (false, 1, instances)); + TEST_RET_ON_FAIL (test_update_instance (&cls->instances[1], + GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); /* Test account creation */ TEST_RET_ON_FAIL (test_insert_account (&cls->instances[0], &cls->accounts[0], GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); + /* Test double account insertion fails */ TEST_RET_ON_FAIL (test_insert_account (&cls->instances[1], &cls->accounts[1], GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); - /* Test double account insertion fails */ TEST_RET_ON_FAIL (test_insert_account (&cls->instances[0], &cls->accounts[0], GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); @@ -561,7 +618,7 @@ run_test_instances (struct TestInstances_Closure *cls) "Lookup account failed: incorrect serial number found\n"); return 1; } - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != plugin->lookup_account (plugin->cls, cls->instances[0].instance.id, "payto://other-uri", @@ -597,10 +654,10 @@ run_test_instances (struct TestInstances_Closure *cls) /** * Function that tests instances. * - * @param cls closure with config + * @return 0 on success, 1 otherwise. */ static int -test_instances (void *cls) +test_instances () { struct TestInstances_Closure test_cls; pre_test_instances (&test_cls); @@ -610,9 +667,14 @@ test_instances (void *cls) } -/* Products */ +/* *********** Products ********** */ +/** + * Frees memory associated with a product. + * + * @param pd the product containing the memory to free. + */ static void free_product (struct TALER_MERCHANTDB_ProductDetails *pd) { @@ -629,13 +691,29 @@ free_product (struct TALER_MERCHANTDB_ProductDetails *pd) } +/** + * A container for data relevant to a product. + */ struct ProductData { + /** + * The identifier of the product. + */ const char *id; + + /** + * The details of the product. + */ struct TALER_MERCHANTDB_ProductDetails product; }; +/** + * Creates a product for testing with. + * + * @param id the id of the product. + * @param product the product data to fill. + */ static void make_product (const char *id, struct ProductData *product) @@ -661,6 +739,11 @@ make_product (const char *id, } +/** + * Frees memory associated with @e ProductData. + * + * @param product the container to free. + */ static void free_product_data (struct ProductData *product) { @@ -672,49 +755,12 @@ free_product_data (struct ProductData *product) /** - * Closure for testing product lookup + * Compare two products for equality. + * + * @param a the first product. + * @param b the second product. + * @return 0 on equality, 1 otherwise. */ -struct TestLookupProducts_Closure -{ - /** - * Number of product ids to compare to - */ - unsigned int products_to_cmp_length; - - /** - * Pointer to array of product ids - */ - const struct ProductData *products_to_cmp; - - /** - * Pointer to array of number of matches for each product - */ - unsigned int *results_matching; - - /** - * Total number of results returned - */ - unsigned int results_length; -}; - - -static void -lookup_products_cb (void *cls, - const char *product_id) -{ - struct TestLookupProducts_Closure *cmp = cls; - if (NULL == cmp) - return; - cmp->results_length += 1; - for (unsigned int i = 0; cmp->products_to_cmp_length > i; ++i) - { - if (0 == strcmp (cmp->products_to_cmp[i].id, - product_id)) - cmp->results_matching[i] += 1; - } -} - - static int check_products_equal (const struct TALER_MERCHANTDB_ProductDetails *a, const struct TALER_MERCHANTDB_ProductDetails *b) @@ -744,6 +790,14 @@ check_products_equal (const struct TALER_MERCHANTDB_ProductDetails *a, } +/** + * Tests inserting product data into the database. + * + * @param instance the instance to insert the product for. + * @param product the product data to insert. + * @param expected_result the result we expect the db to return. + * @return 0 when successful, 1 otherwise. + */ static int test_insert_product (const struct InstanceData *instance, const struct ProductData *product, @@ -759,6 +813,14 @@ test_insert_product (const struct InstanceData *instance, } +/** + * Tests updating product data in the database. + * + * @param instance the instance to update the product for. + * @param product the product data to update. + * @param expected_result the result we expect the db to return. + * @return 0 when successful, 1 otherwise. + */ static int test_update_product (const struct InstanceData *instance, const struct ProductData *product, @@ -774,6 +836,13 @@ test_update_product (const struct InstanceData *instance, } +/** + * Tests looking up a product from the db. + * + * @param instance the instance to query from. + * @param product the product to query and compare to. + * @return 0 when successful, 1 otherwise. + */ static int test_lookup_product (const struct InstanceData *instance, const struct ProductData *product) @@ -803,6 +872,64 @@ test_lookup_product (const struct InstanceData *instance, } +/** + * Closure for testing product lookup + */ +struct TestLookupProducts_Closure +{ + /** + * Number of product ids to compare to + */ + unsigned int products_to_cmp_length; + + /** + * Pointer to array of product ids + */ + const struct ProductData *products_to_cmp; + + /** + * Pointer to array of number of matches for each product + */ + unsigned int *results_matching; + + /** + * Total number of results returned + */ + unsigned int results_length; +}; + + +/** + * Function called after calling @e test_lookup_products + * + * @param cls a pointer to the lookup closure. + * @param product_id the identifier of the product found. + */ +static void +lookup_products_cb (void *cls, + const char *product_id) +{ + struct TestLookupProducts_Closure *cmp = cls; + if (NULL == cmp) + return; + cmp->results_length += 1; + for (unsigned int i = 0; cmp->products_to_cmp_length > i; ++i) + { + if (0 == strcmp (cmp->products_to_cmp[i].id, + product_id)) + cmp->results_matching[i] += 1; + } +} + + +/** + * Tests looking up all products for an instance. + * + * @param instance the instance to query from. + * @param products_length the number of products we are expecting. + * @param products the list of products that we expect to be found. + * @return 0 when successful, 1 otherwise. + */ static int test_lookup_products (const struct InstanceData *instance, unsigned int products_length, @@ -844,6 +971,14 @@ test_lookup_products (const struct InstanceData *instance, } +/** + * Tests deleting a product. + * + * @param instance the instance to delete the product from. + * @param product the product that should be deleted. + * @param expected_result the result that we expect the plugin to return. + * @return 0 when successful, 1 otherwise. + */ static int test_delete_product (const struct InstanceData *instance, const struct ProductData *product, @@ -863,17 +998,22 @@ test_delete_product (const struct InstanceData *instance, */ struct TestProducts_Closure { + /** + * The instance to use for this test. + */ struct InstanceData instance; /** - * The array of products + * The array of products. */ struct ProductData products[2]; }; /** - * Sets up the data structures used in the product tests + * Sets up the data structures used in the product tests. + * + * @param cls the closure to fill with test data. */ static void pre_test_products (struct TestProducts_Closure *cls) @@ -898,7 +1038,9 @@ pre_test_products (struct TestProducts_Closure *cls) /** - * Handles all teardown after testing + * Handles all teardown after testing. + * + * @param cls the closure containing memory to be freed. */ static void post_test_products (struct TestProducts_Closure *cls) @@ -909,6 +1051,12 @@ post_test_products (struct TestProducts_Closure *cls) } +/** + * Runs the tests for products. + * + * @param cls the container of the test data. + * @return 0 on success, 1 otherwise. + */ static int run_test_products (struct TestProducts_Closure *cls) { @@ -949,11 +1097,31 @@ run_test_products (struct TestProducts_Closure *cls) /* Test product update */ cls->products[0].product.description = "This is a test product that has been updated!"; + GNUNET_assert (0 == + json_array_append_new ( + cls->products[0].product.description_i18n, + json_string ( + "description in another language"))); + cls->products[0].product.unit = "barrels"; + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:7.68", + &cls->products[0].product.price)); + GNUNET_assert (0 == + json_array_append_new (cls->products[0].product.taxes, + json_string ("2% sales tax"))); + // cls->products[0].product.total_stock = 40; + // cls->products[0].product.total_sold = 5; + // cls->products[0].product.total_lost = 1; + // TEST that these counters cannot be decreased. + TEST_RET_ON_FAIL (test_update_product (&cls->instance, &cls->products[0], GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); TEST_RET_ON_FAIL (test_lookup_product (&cls->instance, &cls->products[0])); + TEST_RET_ON_FAIL (test_update_product (&cls->instance, + &cls->products[1], + GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); /* Test collective product lookup */ TEST_RET_ON_FAIL (test_insert_product (&cls->instance, &cls->products[1], @@ -1003,8 +1171,13 @@ run_test_products (struct TestProducts_Closure *cls) } +/** + * Takes care of product testing. + * + * @return 0 on success, 1 otherwise. + */ static int -test_products (void *cls) +test_products () { struct TestProducts_Closure test_cls; pre_test_products (&test_cls); @@ -1014,7 +1187,7 @@ test_products (void *cls) } -/* Orders */ +/* ********** Orders ********** */ /** @@ -1039,6 +1212,12 @@ struct OrderData }; +/** + * Builds an order for testing. + * + * @param order_id the identifier to use for the order. + * @param order the container to fill with data. + */ static void make_order (const char *order_id, struct OrderData *order) @@ -1068,6 +1247,11 @@ make_order (const char *order_id, } +/** + * Frees memory associated with an order. + * + * @param the order to free. + */ static void free_order_data (struct OrderData *order) { @@ -1075,6 +1259,68 @@ free_order_data (struct OrderData *order) } +/** + * Tests inserting an order into the database. + * + * @param instance the instance to insert the order for. + * @param order the order to insert. + * @param expected_result the value we expect the db to return. + * @return 0 on success, 1 otherwise. + */ +static int +test_insert_order (const struct InstanceData *instance, + const struct OrderData *order, + enum GNUNET_DB_QueryStatus expected_result) +{ + TEST_COND_RET_ON_FAIL (expected_result == + plugin->insert_order (plugin->cls, + instance->instance.id, + order->id, + order->pay_deadline, + order->contract), + "Insert order failed\n"); + return 0; +} + + +/** + * Tests looking up an order in the database. + * + * @param instance the instance to lookup from. + * @param order the order that should be looked up. + * @return 0 on success, 1 otherwise. + */ +static int +test_lookup_order (const struct InstanceData *instance, + const struct OrderData *order) +{ + json_t *lookup_terms = NULL; + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->lookup_order (plugin->cls, + instance->instance.id, + order->id, + &lookup_terms)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Lookup order failed\n"); + if (NULL != lookup_terms) + json_decref (lookup_terms); + return 1; + } + if (1 != json_equal (order->contract, + lookup_terms)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Lookup order failed: incorrect order returned\n"); + if (NULL != lookup_terms) + json_decref (lookup_terms); + return 1; + } + json_decref (lookup_terms); + return 0; +} + + /** * Closure for testing order lookup */ @@ -1102,6 +1348,14 @@ struct TestLookupOrders_Closure }; +/** + * Called after @e test_lookup_orders. + * + * @param cls the lookup closure. + * @param order_id the identifier of the order found. + * @param order_serial the row number of the order found. + * @param timestamp when the order was added to the database. + */ static void lookup_orders_cb (void *cls, const char *order_id, @@ -1125,53 +1379,15 @@ lookup_orders_cb (void *cls, } -static int -test_insert_order (const struct InstanceData *instance, - const struct OrderData *order, - enum GNUNET_DB_QueryStatus expected_result) -{ - TEST_COND_RET_ON_FAIL (expected_result == - plugin->insert_order (plugin->cls, - instance->instance.id, - order->id, - order->pay_deadline, - order->contract), - "Insert order failed\n"); - return 0; -} - - -static int -test_lookup_order (const struct InstanceData *instance, - const struct OrderData *order) -{ - json_t *lookup_terms = NULL; - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->lookup_order (plugin->cls, - instance->instance.id, - order->id, - &lookup_terms)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Lookup order failed\n"); - if (NULL != lookup_terms) - json_decref (lookup_terms); - return 1; - } - if (1 != json_equal (order->contract, - lookup_terms)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Lookup order failed: incorrect order returned\n"); - if (NULL != lookup_terms) - json_decref (lookup_terms); - return 1; - } - json_decref (lookup_terms); - return 0; -} - - +/** + * Tests looking up orders for an instance. + * + * @param instance the instance. + * @param filter the filters applied on the lookup. + * @param orders_length the number of orders we expect to find. + * @param orders the orders we expect to find. + * @return 0 on success, 1 otherwise. + */ static int test_lookup_orders (const struct InstanceData *instance, const struct TALER_MERCHANTDB_OrderFilter *filter, @@ -1219,6 +1435,14 @@ test_lookup_orders (const struct InstanceData *instance, } +/** + * Tests deleting an order from the database. + * + * @param instance the instance to delete the order from. + * @param order the order to delete. + * @param expected_result the result we expect to receive. + * @return 0 on success, 1 otherwise. + */ static int test_delete_order (const struct InstanceData *instance, const struct OrderData *order, @@ -1233,6 +1457,14 @@ test_delete_order (const struct InstanceData *instance, } +/** + * Test inserting contract terms for an order. + * + * @param instance the instance. + * @param order the order containing the contract terms. + * @param expected_result the result we expect to receive. + * @return 0 on success, 1 otherwise. + */ static int test_insert_contract_terms (const struct InstanceData *instance, const struct OrderData *order, @@ -1248,6 +1480,13 @@ test_insert_contract_terms (const struct InstanceData *instance, } +/** + * Tests lookup of contract terms + * + * @param instance the instance to lookup from. + * @param order the order to lookup for. + * @return 0 on success, 1 otherwise. + */ static int test_lookup_contract_terms (const struct InstanceData *instance, const struct OrderData *order) @@ -1280,6 +1519,14 @@ test_lookup_contract_terms (const struct InstanceData *instance, } +/** + * Tests deleting contract terms for an order. + * + * @param instance the instance to delete from. + * @param order the order whose contract terms we should delete. + * @param expected_result the result we expect to receive. + * @return 0 on success, 1 otherwise. + */ static int test_delete_contract_terms (const struct InstanceData *instance, const struct OrderData *order, @@ -1295,6 +1542,14 @@ test_delete_contract_terms (const struct InstanceData *instance, } +/** + * Test marking a contract as paid in the database. + * + * @param instance the instance to use. + * @param order the order whose contract to use. + * @param expected_result the result we expect to receive. + * @return 0 on success, 1 otherwise. + */ static int test_mark_contract_paid (const struct InstanceData *instance, const struct OrderData *order, @@ -1314,6 +1569,14 @@ test_mark_contract_paid (const struct InstanceData *instance, } +/** + * Tests looking up the status of an order. + * + * @param instance the instance to lookup from. + * @param order the order to lookup. + * @param expected_paid whether the order was paid or not. + * @return 0 on success, 1 otherwise. + */ static int test_lookup_order_status (const struct InstanceData *instance, const struct OrderData *order, @@ -1348,6 +1611,14 @@ test_lookup_order_status (const struct InstanceData *instance, } +/** + * Test looking up an order by its fulfillment. + * + * @param instance the instance to lookup from. + * @param order the order to lookup. + * @param the session id associated with the payment. + * @return 0 on success, 1 otherwise. + */ static int test_lookup_order_by_fulfillment (const struct InstanceData *instance, const struct OrderData *order, @@ -1383,6 +1654,15 @@ test_lookup_order_by_fulfillment (const struct InstanceData *instance, } +/** + * Test looking up the status of an order. + * + * @param order_id the row of the order in the database. + * @param session_id the session id associated with the payment. + * @param expected_paid whether the order was paid or not. + * @param expected_wired whether the order was wired or not. + * @return 0 on success, 1 otherwise. + */ static int test_lookup_payment_status (uint64_t order_id, const char *session_id, @@ -1409,6 +1689,13 @@ test_lookup_payment_status (uint64_t order_id, } +/** + * Test marking an order as being wired. + * + * @param order_id the row of the order in the database. + * @param expected_result the result we expect the plugin to return. + * @return 0 on success, 1 otherwise. + */ static int test_mark_order_wired (uint64_t order_id, enum GNUNET_DB_QueryStatus expected_result) @@ -1426,7 +1713,14 @@ test_mark_order_wired (uint64_t order_id, */ struct TestOrders_Closure { + /** + * The instance to use for the order tests. + */ struct InstanceData instance; + + /** + * A product to use for the order tests. + */ struct ProductData product; /** @@ -1436,6 +1730,11 @@ struct TestOrders_Closure }; +/** + * Initializes order test data. + * + * @param cls the order test closure. + */ static void pre_test_orders (struct TestOrders_Closure *cls) { @@ -1452,12 +1751,17 @@ pre_test_orders (struct TestOrders_Closure *cls) &cls->orders[0]); make_order ("test_orders_od_1", &cls->orders[1]); - GNUNET_assert (0 == json_object_set (cls->orders[1].contract, - "other_field", - json_string ("Second contract"))); + GNUNET_assert (0 == json_object_set_new (cls->orders[1].contract, + "other_field", + json_string ("Second contract"))); } +/** + * Frees memory after order tests. + * + * @param cls the order test closure. + */ static void post_test_orders (struct TestOrders_Closure *cls) { @@ -1468,6 +1772,12 @@ post_test_orders (struct TestOrders_Closure *cls) } +/** + * Run the tests for orders. + * + * @param cls the order test closure. + * @return 0 on success, 1 on failure. + */ static int run_test_orders (struct TestOrders_Closure *cls) { @@ -1642,8 +1952,13 @@ run_test_orders (struct TestOrders_Closure *cls) } +/** + * Does all tasks for testing orders. + * + * @return 0 when successful, 1 otherwise. + */ static int -test_orders (void *cls) +test_orders () { struct TestOrders_Closure test_cls; pre_test_orders (&test_cls); @@ -2970,6 +3285,10 @@ run_test_transfers (struct TestTransfers_Closure *cls) GNUNET_assert (GNUNET_OK == TALER_string_to_amount ("EUR:0.49", &transfer_data.wire_fee)); + TEST_RET_ON_FAIL (test_lookup_payment_status (5, + NULL, + false, + false)); /* Test insert deposit to transfer */ { const struct TALER_EXCHANGE_DepositData deposit_data = { @@ -4756,9 +5075,9 @@ test_lookup_orders_all_filters (void *cls) static int run_tests (void *cls) { - TEST_RET_ON_FAIL (test_instances (cls)); - TEST_RET_ON_FAIL (test_products (cls)); - TEST_RET_ON_FAIL (test_orders (cls)); + TEST_RET_ON_FAIL (test_instances ()); + TEST_RET_ON_FAIL (test_products ()); + TEST_RET_ON_FAIL (test_orders ()); TEST_RET_ON_FAIL (test_deposits (cls)); TEST_RET_ON_FAIL (test_transfers (cls)); TEST_RET_ON_FAIL (test_tips (cls)); diff --git a/src/include/taler_merchant_testing_lib.h b/src/include/taler_merchant_testing_lib.h index 980eb6ea..4c7039b1 100644 --- a/src/include/taler_merchant_testing_lib.h +++ b/src/include/taler_merchant_testing_lib.h @@ -504,6 +504,24 @@ TALER_TESTING_cmd_merchant_get_order (const char *label, ...); +/** + * Start a long poll for GET /private/orders/$ORDER_ID. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_poll_order_start (const char *label, + const char *merchant_url, + const char *order_id, + struct GNUNET_TIME_Relative timeout); + + +/** + * Complete a long poll for GET /private/orders/$ORDER_ID. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_poll_order_conclude (const char *label, + unsigned int http_status, + const char *poll_start_reference); + /** * Make a "claim order" command. * diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c index 3dd65c60..3bee5bd9 100644 --- a/src/testing/test_merchant_api.c +++ b/src/testing/test_merchant_api.c @@ -325,6 +325,10 @@ run (void *cls, "create-proposal-1", GNUNET_TIME_UNIT_MINUTES), #endif + TALER_TESTING_cmd_poll_order_start ("poll-order-merchant-1-start", + merchant_url, + "1", + GNUNET_TIME_UNIT_MINUTES), TALER_TESTING_cmd_merchant_pay_order ("deposit-simple", merchant_url, MHD_HTTP_OK, @@ -332,6 +336,9 @@ run (void *cls, "withdraw-coin-1", "EUR:5", "EUR:4.99"), + TALER_TESTING_cmd_poll_order_conclude ("poll-order-merchant-1-conclude", + MHD_HTTP_OK, + "poll-order-merchant-1-start"), TALER_TESTING_cmd_wallet_get_order ("get-order-wallet-1-2", merchant_url, "create-proposal-1", diff --git a/src/testing/testing_api_cmd_merchant_get_order.c b/src/testing/testing_api_cmd_merchant_get_order.c index f3070392..76bc75b8 100644 --- a/src/testing/testing_api_cmd_merchant_get_order.c +++ b/src/testing/testing_api_cmd_merchant_get_order.c @@ -366,4 +366,344 @@ TALER_TESTING_cmd_merchant_get_order (const char *label, } +struct MerchantPollOrderConcludeState +{ + /** + * 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; + + /** + * Expected HTTP response status code. + */ + unsigned int expected_http_status; +}; + + +struct MerchantPollOrderStartState +{ + /** + * The merchant base URL. + */ + const char *merchant_url; + + /** + * The handle to the current GET /private/orders/$ORDER_ID request. + */ + struct TALER_MERCHANT_OrderMerchantGetHandle *ogh; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Reference to a command that created an order. + */ + const char *order_id; + + /** + * How long to wait for server to return a response. + */ + struct GNUNET_TIME_Relative timeout; + + /** + * Conclude state waiting for completion (if any). + */ + struct MerchantPollOrderConcludeState *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 /poll-payment + * command expired or we got a response. Checks if the + * result is what we expected. + * + * @param cls a `struct PollPaymentConcludeState` + */ +static void +conclude_task (void *cls) +{ + struct MerchantPollOrderConcludeState *ppc = cls; + const struct TALER_TESTING_Command *poll_cmd; + struct MerchantPollOrderStartState *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 /private/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); + } + 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); +} + + +/** + * Callback to process a GET /orders/$ID request + * + * @param cls closure + * @param hr HTTP response details + * @param osr order status response details (on success) + */ +static void +merchant_poll_order_cb ( + void *cls, + const struct TALER_MERCHANT_HttpResponse *hr, + const struct TALER_MERCHANT_OrderStatusResponse *osr) +{ + /* FIXME, deeper checks should be implemented here. */ + struct MerchantPollOrderStartState *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 gts->tip_reference here to + // 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 order" CMD. + * + * @param cls closure. + * @param cmd command being run now. + * @param is interpreter state. + */ +static void +merchant_poll_order_start_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct MerchantPollOrderStartState *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_merchant_order_get (is->ctx, + pos->merchant_url, + pos->order_id, + NULL, + false, + pos->timeout, + &merchant_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 +merchant_poll_order_start_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct MerchantPollOrderStartState *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_merchant_order_get_cancel (pos->ogh); + } + GNUNET_free (pos); +} + + +/** + * Start a long poll for GET /private/orders/$ORDER_ID. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_poll_order_start (const char *label, + const char *merchant_url, + const char *order_id, + struct GNUNET_TIME_Relative timeout) +{ + struct MerchantPollOrderStartState *pos; + + pos = GNUNET_new (struct MerchantPollOrderStartState); + pos->order_id = order_id; + pos->merchant_url = merchant_url; + pos->timeout = timeout; + { + struct TALER_TESTING_Command cmd = { + .cls = pos, + .label = label, + .run = &merchant_poll_order_start_run, + .cleanup = &merchant_poll_order_start_cleanup + }; + + return cmd; + } +} + + +/** + * Run the "GET order" CMD. + * + * @param cls closure. + * @param cmd command being run now. + * @param is interpreter state. + */ +static void +merchant_poll_order_conclude_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct MerchantPollOrderConcludeState *poc = cls; + const struct TALER_TESTING_Command *poll_cmd; + struct MerchantPollOrderStartState *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_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 +merchant_poll_order_conclude_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct MerchantPollOrderConcludeState *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/$ORDER_ID. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_poll_order_conclude (const char *label, + unsigned int http_status, + const char *poll_start_reference) +{ + struct MerchantPollOrderConcludeState *cps; + + cps = GNUNET_new (struct MerchantPollOrderConcludeState); + cps->start_reference = poll_start_reference; + cps->expected_http_status = http_status; + { + struct TALER_TESTING_Command cmd = { + .cls = cps, + .label = label, + .run = &merchant_poll_order_conclude_run, + .cleanup = &merchant_poll_order_conclude_cleanup + }; + + return cmd; + } +} + + /* end of testing_api_cmd_merchant_get_order.c */ -- cgit v1.2.3