merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit f6633b5cb3567046f73d6cf4de0ab5b2eb4d217b
parent 5acd586004bd7281e6dcf14217e3fee316716804
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat,  2 Nov 2019 23:09:52 +0100

simplify structure

Diffstat:
Msrc/lib/Makefile.am | 13++++++++-----
Dsrc/lib/testing_api_cmd_refund.c | 636-------------------------------------------------------------------------------
Asrc/lib/testing_api_cmd_refund_increase.c | 234+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/lib/testing_api_cmd_tip.c | 1205-------------------------------------------------------------------------------
Asrc/lib/testing_api_cmd_tip_authorize.c | 375+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/testing_api_cmd_tip_pickup.c | 598+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/testing_api_cmd_tip_query.c | 297+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 1512 insertions(+), 1846 deletions(-)

diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am @@ -42,14 +42,17 @@ libtalermerchant_la_LIBADD = \ $(XLIB) libtalermerchanttesting_la_SOURCES = \ - testing_api_cmd_proposal.c \ + testing_api_cmd_history.c \ testing_api_cmd_pay.c \ - testing_api_cmd_refund.c \ - testing_api_cmd_tip.c \ + testing_api_cmd_proposal.c \ + testing_api_cmd_refund_increase.c \ + testing_api_cmd_refund_lookup.c \ + testing_api_cmd_rewind.c \ + testing_api_cmd_tip_authorize.c \ + testing_api_cmd_tip_pickup.c \ + testing_api_cmd_tip_query.c \ testing_api_cmd_track_transaction.c \ testing_api_cmd_track_transfer.c \ - testing_api_cmd_history.c \ - testing_api_cmd_rewind.c \ testing_api_helpers.c \ testing_api_trait_merchant_sig.c \ testing_api_trait_string.c \ diff --git a/src/lib/testing_api_cmd_refund.c b/src/lib/testing_api_cmd_refund.c @@ -1,636 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2018 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 - published by the Free Software Foundation; either version 3, or - (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public - License along with TALER; see the file COPYING. If not, see - <http://www.gnu.org/licenses/> -*/ - -/** - * @file lib/testing_api_cmd_refund.c - * @brief command to test refunds. - * @author Marcello Stanisci - */ - -#include "platform.h" -#include <taler/taler_exchange_service.h> -#include <taler/taler_testing_lib.h> -#include "taler_merchant_service.h" -#include "taler_merchant_testing_lib.h" - - -/** - * State for a "refund increase" CMD. - */ -struct RefundIncreaseState -{ - /** - * Operation handle for a POST /refund request. - */ - struct TALER_MERCHANT_RefundIncreaseOperation *rio; - - /** - * Base URL of the merchant serving the request. - */ - const char *merchant_url; - - /** - * Order id of the contract to refund. - */ - const char *order_id; - - /** - * The amount to refund. - */ - const char *refund_amount; - - /** - * Refund fee. - */ - const char *refund_fee; - - /** - * Human-readable justification for the refund. - */ - const char *reason; - - /** - * Interpreter state. - */ - struct TALER_TESTING_Interpreter *is; - - /** - * Expected HTTP response code. - */ - unsigned int http_code; -}; - - -/** - * State for a "refund lookup" CMD. - */ -struct RefundLookupState -{ - /** - * Operation handle for a GET /public/refund request. - */ - struct TALER_MERCHANT_RefundLookupOperation *rlo; - - /** - * Base URL of the merchant serving the request. - */ - const char *merchant_url; - - /** - * Order id to look up. - */ - const char *order_id; - - /** - * Reference to a "pay" CMD, used to double-check if - * refunded coins were actually spent: - */ - const char *pay_reference; - - /** - * Reference to a "refund increase" CMD that offer - * the expected amount to be refunded; can be NULL. - */ - const char *increase_reference; - - /** - * Expected HTTP response code. - */ - unsigned int http_code; - - /** - * Interpreter state. - */ - struct TALER_TESTING_Interpreter *is; - - /** - * Explicit amount to be refunded, must be defined if @a - * increase_reference is NULL. - */ - const char *refund_amount; -}; - - -/** - * Free the state of a "refund increase" CMD, and - * possibly cancel a pending "refund increase" operation. - * - * @param cls closure - * @param cmd command currently being freed. - */ -static void -refund_increase_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct RefundIncreaseState *ris = cls; - - if (NULL != ris->rio) - { - TALER_LOG_WARNING ("Refund-increase operation" - " did not complete\n"); - TALER_MERCHANT_refund_increase_cancel (ris->rio); - } - GNUNET_free (ris); -} - - -/** - * Free the state of a "refund lookup" CMD, and - * possibly cancel a pending "refund lookup" operation. - * - * @param cls closure - * @param cmd command currently being freed. - */ -static void -refund_lookup_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct RefundLookupState *rls = cls; - - if (NULL != rls->rlo) - { - TALER_LOG_WARNING ("Refund-lookup operation" - " did not complete\n"); - TALER_MERCHANT_refund_lookup_cancel (rls->rlo); - } - GNUNET_free (rls); -} - - -/** - * Process POST /refund (increase) response; just checking - * if the HTTP response code is the one expected. - * - * @param cls closure - * @param http_status HTTP status code - * @param ec taler-specific error object - * @param obj response body; is NULL on success. - */ -static void -refund_increase_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *obj) -{ - struct RefundIncreaseState *ris = cls; - - ris->rio = NULL; - if (ris->http_code != http_status) - TALER_TESTING_FAIL (ris->is); - - TALER_TESTING_interpreter_next (ris->is); -} - - -/** - * Run the "refund increase" CMD. - * - * @param cls closure. - * @param cmd command currently being run. - * @param is the interpreter state. - */ -static void -refund_increase_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct RefundIncreaseState *ris = cls; - struct TALER_Amount refund_amount; - - ris->is = is; - if (GNUNET_OK != TALER_string_to_amount (ris->refund_amount, - &refund_amount)) - TALER_TESTING_FAIL (is); - ris->rio = TALER_MERCHANT_refund_increase (is->ctx, - ris->merchant_url, - ris->order_id, - &refund_amount, - ris->reason, - &refund_increase_cb, - ris); - GNUNET_assert (NULL != ris->rio); -} - - -/** - * Callback that frees all the elements in the hashmap - * - * @param cls closure, NULL - * @param key current key - * @param value a `struct TALER_Amount` - * - * @return always #GNUNET_YES (continue to iterate) - */ -static int -hashmap_free (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct TALER_Amount *refund_amount = value; - - GNUNET_free (refund_amount); - return GNUNET_YES; -} - - -/** - * Process "GET /public/refund" (lookup) response; - * mainly checking if the refunded amount matches the - * expectation. - * - * @param cls closure - * @param http_status HTTP status code - * @param ec taler-specific error object - * @param obj response body; is NULL on error. - */ -static void -refund_lookup_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *obj) -{ - struct RefundLookupState *rls = cls; - struct GNUNET_CONTAINER_MultiHashMap *map; - size_t index; - json_t *elem; - const char *error_name; - unsigned int error_line; - struct GNUNET_HashCode h_coin_pub; - const char *coin_reference; - char *coin_reference_dup; - const char *icoin_reference; - const struct TALER_TESTING_Command *pay_cmd; - const struct TALER_TESTING_Command *increase_cmd; - const char *refund_amount; - struct TALER_Amount acc; - struct TALER_Amount ra; - const json_t *arr; - - rls->rlo = NULL; - if (rls->http_code != http_status) - TALER_TESTING_FAIL (rls->is); - - arr = json_object_get (obj, "refund_permissions"); - if (NULL == arr) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Tolerating a refund permission not found\n"); - TALER_TESTING_interpreter_next (rls->is); - return; - } - map = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO); - - /* Put in array every refunded coin. */ - json_array_foreach (arr, index, elem) - { - struct TALER_CoinSpendPublicKeyP coin_pub; - struct TALER_Amount *irefund_amount = GNUNET_new - (struct TALER_Amount); - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("coin_pub", &coin_pub), - TALER_JSON_spec_amount ("refund_amount", irefund_amount), - GNUNET_JSON_spec_end () - }; - - GNUNET_assert (GNUNET_OK == GNUNET_JSON_parse (elem, - spec, - &error_name, - &error_line)); - GNUNET_CRYPTO_hash (&coin_pub, - sizeof (struct TALER_CoinSpendPublicKeyP), - &h_coin_pub); - GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put - (map, - &h_coin_pub, // which - irefund_amount, // how much - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - }; - - /* Compare spent coins with refunded, and if they match, - * increase an accumulator. */ - if (NULL == - (pay_cmd = TALER_TESTING_interpreter_lookup_command - (rls->is, rls->pay_reference))) - TALER_TESTING_FAIL (rls->is); - - if (GNUNET_OK != TALER_TESTING_get_trait_coin_reference - (pay_cmd, 0, &coin_reference)) - TALER_TESTING_FAIL (rls->is); - - GNUNET_assert (GNUNET_OK == TALER_amount_get_zero ("EUR", - &acc)); - coin_reference_dup = GNUNET_strdup (coin_reference); - for (icoin_reference = strtok (coin_reference_dup, ";"); - NULL != icoin_reference; - icoin_reference = strtok (NULL, ";")) - { - const struct TALER_CoinSpendPrivateKeyP *icoin_priv; - struct TALER_CoinSpendPublicKeyP icoin_pub; - struct GNUNET_HashCode h_icoin_pub; - struct TALER_Amount *iamount; - const struct TALER_TESTING_Command *icoin_cmd; - - if (NULL == - (icoin_cmd = TALER_TESTING_interpreter_lookup_command - (rls->is, icoin_reference)) ) - { - GNUNET_break (0); - TALER_LOG_ERROR ("Bad reference `%s'\n", - icoin_reference); - TALER_TESTING_interpreter_fail (rls->is); - GNUNET_CONTAINER_multihashmap_destroy (map); - return; - } - - if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv - (icoin_cmd, 0, &icoin_priv)) - { - GNUNET_break (0); - TALER_LOG_ERROR ("Command `%s' failed to give coin" - " priv trait\n", - icoin_reference); - TALER_TESTING_interpreter_fail (rls->is); - GNUNET_CONTAINER_multihashmap_destroy (map); - return; - } - - GNUNET_CRYPTO_eddsa_key_get_public (&icoin_priv->eddsa_priv, - &icoin_pub.eddsa_pub); - GNUNET_CRYPTO_hash (&icoin_pub, - sizeof (struct TALER_CoinSpendPublicKeyP), - &h_icoin_pub); - - iamount = GNUNET_CONTAINER_multihashmap_get - (map, &h_icoin_pub); - - /* Can be NULL: not all coins are involved in refund */ - if (NULL == iamount) - continue; - - GNUNET_assert (GNUNET_OK == TALER_amount_add (&acc, - &acc, - iamount)); - } - - GNUNET_free (coin_reference_dup); - - if (NULL != - (increase_cmd = TALER_TESTING_interpreter_lookup_command - (rls->is, rls->increase_reference))) - { - if (GNUNET_OK != TALER_TESTING_get_trait_amount - (increase_cmd, 0, &refund_amount)) - TALER_TESTING_FAIL (rls->is); - - if (GNUNET_OK != TALER_string_to_amount - (refund_amount, &ra)) - TALER_TESTING_FAIL (rls->is); - } - else - { - GNUNET_assert (NULL != rls->refund_amount); - - if (GNUNET_OK != TALER_string_to_amount - (rls->refund_amount, &ra)) - TALER_TESTING_FAIL (rls->is); - } - - GNUNET_CONTAINER_multihashmap_iterate (map, - &hashmap_free, - NULL); - GNUNET_CONTAINER_multihashmap_destroy (map); - - /* Check that what the backend claims to have been refunded - * actually matches _our_ refund expectation. */ - if (0 != TALER_amount_cmp (&acc, - &ra)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Incomplete refund: expected '%s', got '%s'\n", - TALER_amount_to_string (&ra), - TALER_amount_to_string (&acc)); - TALER_TESTING_interpreter_fail (rls->is); - return; - } - - TALER_TESTING_interpreter_next (rls->is); -} - - -/** - * Run the "refund lookup" CMD. - * - * @param cls closure. - * @param cmd command being currently run. - * @param is interpreter state. - */ -static void -refund_lookup_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct RefundLookupState *rls = cls; - - rls->is = is; - rls->rlo = TALER_MERCHANT_refund_lookup (is->ctx, - rls->merchant_url, - rls->order_id, - &refund_lookup_cb, - rls); - GNUNET_assert (NULL != rls->rlo); -} - - -/** - * Offer internal data from the "refund increase" CMD - * state to other commands. - * - * @param cls closure - * @param ret[out] result (could be anything) - * @param trait name of the trait - * @param index index number of the object to extract. - * @return #GNUNET_OK on success - */ -static int -refund_increase_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - struct RefundIncreaseState *ris = cls; - - struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_amount (0, ris->refund_amount), - TALER_TESTING_trait_end () - }; - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); - - return GNUNET_SYSERR; -} - - -/** - * Define a "refund increase" CMD. - * - * @param label command label. - * @param merchant_url base URL of the backend serving the - * "refund increase" request. - * @param reason refund justification, human-readable. - * @param order_id order id of the contract to refund. - * @param refund_amount amount to be refund-increased. - * @param refund_fee refund fee. - * @param http_code expected HTTP response code. - * - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_refund_increase - (const char *label, - const char *merchant_url, - const char *reason, - const char *order_id, - const char *refund_amount, - const char *refund_fee, - unsigned int http_code) -{ - struct RefundIncreaseState *ris; - - ris = GNUNET_new (struct RefundIncreaseState); - ris->merchant_url = merchant_url; - ris->order_id = order_id; - ris->refund_amount = refund_amount; - ris->refund_fee = refund_fee; - ris->reason = reason; - ris->http_code = http_code; - - struct TALER_TESTING_Command cmd = { - .cls = ris, - .label = label, - .run = &refund_increase_run, - .cleanup = &refund_increase_cleanup, - .traits = &refund_increase_traits - }; - - return cmd; -} - - -/** - * Define a "refund lookup" CMD. - * - * @param label command label. - * @param merchant_url base URL of the merchant serving the - * "refund lookup" request. - * @param increase_reference reference to a "refund increase" CMD - * that will offer the amount to check the looked up refund - * against. Must NOT be NULL. - * @param pay_reference reference to the "pay" CMD whose coins got - * refunded. It is used to double-check if the refunded - * coins were actually spent in the first place. - * @param order_id order id whose refund status is to be looked up. - * @param http_code expected HTTP response code. - * - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_refund_lookup - (const char *label, - const char *merchant_url, - const char *increase_reference, - const char *pay_reference, - const char *order_id, - unsigned int http_code) -{ - struct RefundLookupState *rls; - - rls = GNUNET_new (struct RefundLookupState); - rls->merchant_url = merchant_url; - rls->order_id = order_id; - rls->pay_reference = pay_reference; - rls->increase_reference = increase_reference; - rls->http_code = http_code; - - struct TALER_TESTING_Command cmd = { - .cls = rls, - .label = label, - .run = &refund_lookup_run, - .cleanup = &refund_lookup_cleanup - }; - - return cmd; -} - - -/** - * Define a "refund lookup" CMD, equipped with a expected refund - * amount. - * - * @param label command label. - * @param merchant_url base URL of the merchant serving the - * "refund lookup" request. - * @param increase_reference reference to a "refund increase" CMD - * that will offer the amount to check the looked up refund - * against. Can be NULL, takes precedence over @a - * refund_amount. - * @param pay_reference reference to the "pay" CMD whose coins got - * refunded. It is used to double-check if the refunded - * coins were actually spent in the first place. - * @param order_id order id whose refund status is to be looked up. - * @param http_code expected HTTP response code. - * @param refund_amount expected refund amount. Must be defined - * if @a increase_reference is NULL. - * - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_refund_lookup_with_amount - (const char *label, - const char *merchant_url, - const char *increase_reference, - const char *pay_reference, - const char *order_id, - unsigned int http_code, - const char *refund_amount) -{ - struct RefundLookupState *rls; - - rls = GNUNET_new (struct RefundLookupState); - rls->merchant_url = merchant_url; - rls->order_id = order_id; - rls->pay_reference = pay_reference; - rls->increase_reference = increase_reference; - rls->http_code = http_code; - rls->refund_amount = refund_amount; - - struct TALER_TESTING_Command cmd = { - .cls = rls, - .label = label, - .run = &refund_lookup_run, - .cleanup = &refund_lookup_cleanup - }; - - return cmd; -} - - -/* end of testing_api_cmd_refund.c */ diff --git a/src/lib/testing_api_cmd_refund_increase.c b/src/lib/testing_api_cmd_refund_increase.c @@ -0,0 +1,234 @@ +/* + This file is part of TALER + Copyright (C) 2014-2018 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 + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ + +/** + * @file lib/testing_api_cmd_refund_increase.c + * @brief command to test refunds. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include <taler/taler_exchange_service.h> +#include <taler/taler_testing_lib.h> +#include "taler_merchant_service.h" +#include "taler_merchant_testing_lib.h" + + +/** + * State for a "refund increase" CMD. + */ +struct RefundIncreaseState +{ + /** + * Operation handle for a POST /refund request. + */ + struct TALER_MERCHANT_RefundIncreaseOperation *rio; + + /** + * Base URL of the merchant serving the request. + */ + const char *merchant_url; + + /** + * Order id of the contract to refund. + */ + const char *order_id; + + /** + * The amount to refund. + */ + const char *refund_amount; + + /** + * Refund fee. + */ + const char *refund_fee; + + /** + * Human-readable justification for the refund. + */ + const char *reason; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Expected HTTP response code. + */ + unsigned int http_code; +}; + + +/** + * Free the state of a "refund increase" CMD, and + * possibly cancel a pending "refund increase" operation. + * + * @param cls closure + * @param cmd command currently being freed. + */ +static void +refund_increase_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct RefundIncreaseState *ris = cls; + + if (NULL != ris->rio) + { + TALER_LOG_WARNING ("Refund-increase operation" + " did not complete\n"); + TALER_MERCHANT_refund_increase_cancel (ris->rio); + } + GNUNET_free (ris); +} + + +/** + * Process POST /refund (increase) response; just checking + * if the HTTP response code is the one expected. + * + * @param cls closure + * @param http_status HTTP status code + * @param ec taler-specific error object + * @param obj response body; is NULL on success. + */ +static void +refund_increase_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + const json_t *obj) +{ + struct RefundIncreaseState *ris = cls; + + ris->rio = NULL; + if (ris->http_code != http_status) + TALER_TESTING_FAIL (ris->is); + + TALER_TESTING_interpreter_next (ris->is); +} + + +/** + * Run the "refund increase" CMD. + * + * @param cls closure. + * @param cmd command currently being run. + * @param is the interpreter state. + */ +static void +refund_increase_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct RefundIncreaseState *ris = cls; + struct TALER_Amount refund_amount; + + ris->is = is; + if (GNUNET_OK != TALER_string_to_amount (ris->refund_amount, + &refund_amount)) + TALER_TESTING_FAIL (is); + ris->rio = TALER_MERCHANT_refund_increase (is->ctx, + ris->merchant_url, + ris->order_id, + &refund_amount, + ris->reason, + &refund_increase_cb, + ris); + GNUNET_assert (NULL != ris->rio); +} + + +/** + * Offer internal data from the "refund increase" CMD + * state to other commands. + * + * @param cls closure + * @param ret[out] result (could be anything) + * @param trait name of the trait + * @param index index number of the object to extract. + * @return #GNUNET_OK on success + */ +static int +refund_increase_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct RefundIncreaseState *ris = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_amount (0, ris->refund_amount), + TALER_TESTING_trait_end () + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +/** + * Define a "refund increase" CMD. + * + * @param label command label. + * @param merchant_url base URL of the backend serving the + * "refund increase" request. + * @param reason refund justification, human-readable. + * @param order_id order id of the contract to refund. + * @param refund_amount amount to be refund-increased. + * @param refund_fee refund fee. + * @param http_code expected HTTP response code. + * + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_refund_increase + (const char *label, + const char *merchant_url, + const char *reason, + const char *order_id, + const char *refund_amount, + const char *refund_fee, + unsigned int http_code) +{ + struct RefundIncreaseState *ris; + + ris = GNUNET_new (struct RefundIncreaseState); + ris->merchant_url = merchant_url; + ris->order_id = order_id; + ris->refund_amount = refund_amount; + ris->refund_fee = refund_fee; + ris->reason = reason; + ris->http_code = http_code; + { + struct TALER_TESTING_Command cmd = { + .cls = ris, + .label = label, + .run = &refund_increase_run, + .cleanup = &refund_increase_cleanup, + .traits = &refund_increase_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_refund_increase.c */ diff --git a/src/lib/testing_api_cmd_tip.c b/src/lib/testing_api_cmd_tip.c @@ -1,1205 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2018 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 - published by the Free Software Foundation; either version 3, or - (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public - License along with TALER; see the file COPYING. If not, see - <http://www.gnu.org/licenses/> -*/ - -/** - * @file lib/testing_api_cmd_tip.c - * @brief command to test the tipping. - * @author Marcello Stanisci - */ - -#include "platform.h" -#include <taler/taler_exchange_service.h> -#include <taler/taler_testing_lib.h> -#include "taler_merchant_service.h" -#include "taler_merchant_testing_lib.h" - -/** - * State for a /tip-pickup CMD. - */ -struct TipPickupState -{ - /** - * Merchant base URL. - */ - const char *merchant_url; - - /** - * Exchange base URL. - */ - const char *exchange_url; - - /** - * Expected HTTP response code. - */ - unsigned int http_status; - - /** - * Reference to a /tip/authorize CMD. This will be used to - * get the tip id to make the request with. - */ - const char *authorize_reference; - - /** - * If set to non NULL, it references another pickup CMD - * that will provide all the data which is needed to issue - * the request (like planchet secrets, denomination keys..). - */ - const char *replay_reference; - - /** - * Handle to a on-going /tip/pickup request. - */ - struct TALER_MERCHANT_TipPickupOperation *tpo; - - /** - * The interpreter state. - */ - struct TALER_TESTING_Interpreter *is; - - /** - * An array of string-defined amounts that indicates - * which denominations are going to be used to receive - * tips. - */ - const char **amounts; - - /** - * The object version of the above @a amounts. - */ - struct TALER_Amount *amounts_obj; - - /** - * How many coins are involved in the tipping operation. - */ - unsigned int num_coins; - - /** - * The array of denomination keys, in the same order of @a - * amounts. - */ - const struct TALER_EXCHANGE_DenomPublicKey **dks; - - - /** - * The array of planchet secrets, in the same order of @a - * amounts. - */ - struct TALER_PlanchetSecretsP *psa; - - /** - * Temporary data structure of @e num_coins entries for the - * withdraw operations. - */ - struct WithdrawHandle *withdraws; - - /** - * Set (by the interpreter) to an array of @a num_coins - * signatures created from the (successful) tip operation. - */ - struct TALER_DenominationSignature *sigs; - - /** - * Expected Taler error code (NOTE: this is NOT the HTTP - * response code). - */ - enum TALER_ErrorCode expected_ec; -}; - - -/** - * State for a /tip-query CMD. - */ -struct TipQueryState -{ - - /** - * The merchant base URL. - */ - const char *merchant_url; - - /** - * Expected HTTP response code for this CMD. - */ - unsigned int http_status; - - /** - * The handle to the current /tip-query request. - */ - struct TALER_MERCHANT_TipQueryOperation *tqo; - - /** - * The interpreter state. - */ - struct TALER_TESTING_Interpreter *is; - - /** - * Expected amount to be picked up. - */ - const char *expected_amount_picked_up; - - /** - * Expected amount to be tip-authorized. - */ - const char *expected_amount_authorized; - - /** - * Amount that is expected to be still available - * from the tip reserve. - */ - const char *expected_amount_available; -}; - - -/** - * State for a /tip-authorize CMD. - */ -struct TipAuthorizeState -{ - - /** - * Merchant base URL. - */ - const char *merchant_url; - - /** - * Expected HTTP response code. - */ - unsigned int http_status; - - /** - * Human-readable justification for the - * tip authorization carried on by this CMD. - */ - const char *justification; - - /** - * Amount that should be authorized for tipping. - */ - const char *amount; - - /** - * Expected Taler error code for this CMD. - */ - enum TALER_ErrorCode expected_ec; - - /** - * Tip taler:// URI. - */ - const char *tip_uri; - - /** - * The tip id; set when the CMD succeeds. - */ - struct GNUNET_HashCode tip_id; - - /** - * Expiration date for this tip. - */ - struct GNUNET_TIME_Absolute tip_expiration; - - /** - * Handle to the on-going /tip-authorize request. - */ - struct TALER_MERCHANT_TipAuthorizeOperation *tao; - - /** - * The interpreter state. - */ - struct TALER_TESTING_Interpreter *is; -}; - -/** - * Callback for a /tip-authorize request. Set into the state - * what was returned from the backend (@a tip_id and @a - * tip_expiration). - * - * @param cls closure - * @param http_status HTTP status returned by the merchant backend - * @param ec taler-specific error code - * @param taler_tip_uri URI to let the wallet know about the tip - * @param tip_id unique identifier for the tip - */ -static void -tip_authorize_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const char *taler_tip_uri, - struct GNUNET_HashCode *tip_id) -{ - struct TipAuthorizeState *tas = cls; - - tas->tao = NULL; - if (tas->http_status != http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u (%d)" - " to command %s\n", - http_status, - ec, - TALER_TESTING_interpreter_get_current_label - (tas->is)); - - TALER_TESTING_interpreter_fail (tas->is); - return; - } - - if (tas->expected_ec != ec) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected error code %d (%u) to command %s\n", - ec, - http_status, - TALER_TESTING_interpreter_get_current_label - (tas->is)); - TALER_TESTING_interpreter_fail (tas->is); - return; - } - if ( (MHD_HTTP_OK == http_status) && - (TALER_EC_NONE == ec) ) - { - tas->tip_uri = strdup (taler_tip_uri); - tas->tip_id = *tip_id; - } - - TALER_TESTING_interpreter_next (tas->is); -} - - -/** - * Offers information from the /tip-authorize CMD state to other - * commands. - * - * @param cls closure - * @param ret[out] result (could be anything) - * @param trait name of the trait - * @param index index number of the object to extract. - * @return #GNUNET_OK on success - */ -static int -tip_authorize_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - struct TipAuthorizeState *tas = cls; - struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_tip_id (0, &tas->tip_id), - TALER_TESTING_trait_end (), - }; - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); -} - - -/** - * Runs the /tip-authorize CMD - * - * @param cls closure - * @param cmd the CMD representing _this_ command - * @param is interpreter state - */ -static void -tip_authorize_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct TipAuthorizeState *tas = cls; - struct TALER_Amount amount; - - tas->is = is; - if (GNUNET_OK != TALER_string_to_amount (tas->amount, - &amount)) - TALER_TESTING_FAIL (is); - - tas->tao = TALER_MERCHANT_tip_authorize - (is->ctx, - tas->merchant_url, - "http://merchant.com/pickup", - "http://merchant.com/continue", - &amount, - tas->justification, - tip_authorize_cb, - tas); - - GNUNET_assert (NULL != tas->tao); -} - - -/** - * Run the /tip-authorize CMD, the "fake" version of it. - * - * @param cls closure - * @param cmd the CMD representing _this_ command - * @param is interpreter state * - */ -static void -tip_authorize_fake_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct TipAuthorizeState *tas = cls; - - /* Make up a tip id. */ - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, - &tas->tip_id, - sizeof (struct GNUNET_HashCode)); - - TALER_TESTING_interpreter_next (is); -} - - -/** - * Free the state from a /tip-authorize CMD, and possibly - * cancel any pending operation. - * - * @param cls closure - * @param cmd the /tip-authorize CMD that is about to be freed. - */ -static void -tip_authorize_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct TipAuthorizeState *tas = cls; - - if (NULL != tas->tao) - { - TALER_LOG_WARNING ("Tip-autorize operation" - " did not complete\n"); - TALER_MERCHANT_tip_authorize_cancel (tas->tao); - } - GNUNET_free (tas); -} - - -/** - * Create a /tip-authorize CMD, specifying the Taler error code - * that is expected to be returned by the backend. - * - * @param label this command label - * @param merchant_url the base URL of the merchant that will - * serve the /tip-authorize request. - * @param exchange_url the base URL of the exchange that owns - * the reserve from which the tip is going to be gotten. - * @param http_status the HTTP response code which is expected - * for this operation. - * @param justification human-readable justification for this - * tip authorization. - * @param amount the amount to authorize for tipping. - * @param ec expected Taler-defined error code. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_tip_authorize_with_ec - (const char *label, - const char *merchant_url, - const char *exchange_url, - unsigned int http_status, - const char *justification, - const char *amount, - enum TALER_ErrorCode ec) -{ - struct TipAuthorizeState *tas; - - tas = GNUNET_new (struct TipAuthorizeState); - tas->merchant_url = merchant_url; - tas->justification = justification; - tas->amount = amount; - tas->http_status = http_status; - tas->expected_ec = ec; - - struct TALER_TESTING_Command cmd = { - .label = label, - .cls = tas, - .run = &tip_authorize_run, - .cleanup = &tip_authorize_cleanup, - .traits = &tip_authorize_traits - }; - - return cmd; -} - - -/** - * Create a /tip-authorize CMD. - * - * @param label this command label - * @param merchant_url the base URL of the merchant that will - * serve the /tip-authorize request. - * @param exchange_url the base URL of the exchange that owns - * the reserve from which the tip is going to be gotten. - * @param http_status the HTTP response code which is expected - * for this operation. - * @param justification human-readable justification for this - * tip authorization. - * @param amount the amount to authorize for tipping. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_tip_authorize (const char *label, - const char *merchant_url, - const char *exchange_url, - unsigned int http_status, - const char *justification, - const char *amount) -{ - struct TipAuthorizeState *tas; - - tas = GNUNET_new (struct TipAuthorizeState); - tas->merchant_url = merchant_url; - tas->justification = justification; - tas->amount = amount; - tas->http_status = http_status; - - struct TALER_TESTING_Command cmd = { - .label = label, - .cls = tas, - .run = &tip_authorize_run, - .cleanup = &tip_authorize_cleanup, - .traits = &tip_authorize_traits - }; - - return cmd; -} - - -/** - * Callback to process a GET /tip-query request, it mainly - * checks that what the backend returned matches the command's - * expectations. - * - * @param cls closure - * @param http_status HTTP status code for this request - * @param ec Taler-specific error code - * @param raw raw response body - * @param reserve_expiration when the tip reserve will expire - * @param reserve_pub tip reserve public key - * @param amount_authorized total amount authorized on tip reserve - * @param amount_available total amount still available on - * tip reserve - * @param amount_picked_up total amount picked up from tip reserve - */ -static void -tip_query_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *raw, - struct GNUNET_TIME_Absolute reserve_expiration, - struct TALER_ReservePublicKeyP *reserve_pub, - struct TALER_Amount *amount_authorized, - struct TALER_Amount *amount_available, - struct TALER_Amount *amount_picked_up) -{ - struct TipQueryState *tqs = cls; - struct TALER_Amount a; - - tqs->tqo = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Tip query callback at command `%s'\n", - TALER_TESTING_interpreter_get_current_label - (tqs->is)); - - GNUNET_assert (NULL != reserve_pub); - GNUNET_assert (NULL != amount_authorized); - GNUNET_assert (NULL != amount_available); - GNUNET_assert (NULL != amount_picked_up); - - if (tqs->expected_amount_available) - { - GNUNET_assert (GNUNET_OK == TALER_string_to_amount - (tqs->expected_amount_available, &a)); - { - char *str; - - str = TALER_amount_to_string (amount_available); - TALER_LOG_INFO ("expected available %s, actual %s\n", - TALER_amount2s (&a), - str); - GNUNET_free (str); - } - if (0 != TALER_amount_cmp (amount_available, &a)) - TALER_TESTING_FAIL (tqs->is); - } - - if (tqs->expected_amount_authorized) - { - GNUNET_assert (GNUNET_OK == TALER_string_to_amount - (tqs->expected_amount_authorized, &a)); - { - char *str; - - str = TALER_amount_to_string (amount_authorized); - TALER_LOG_INFO ("expected authorized %s, actual %s\n", - TALER_amount2s (&a), - str); - GNUNET_free (str); - } - if (0 != TALER_amount_cmp (amount_authorized, &a)) - TALER_TESTING_FAIL (tqs->is); - } - - if (tqs->expected_amount_picked_up) - { - GNUNET_assert (GNUNET_OK == TALER_string_to_amount - (tqs->expected_amount_picked_up, &a)); - { - char *str; - str = TALER_amount_to_string (amount_picked_up); - TALER_LOG_INFO ("expected picked_up %s, actual %s\n", - TALER_amount2s (&a), - str); - GNUNET_free (str); - } - if (0 != TALER_amount_cmp (amount_picked_up, &a)) - TALER_TESTING_FAIL (tqs->is); - } - - if (tqs->http_status != http_status) - TALER_TESTING_FAIL (tqs->is); - - TALER_TESTING_interpreter_next (tqs->is); -} - - -/** - * Free the state from a /tip-query CMD, and possibly cancel - * a pending /tip-query request. - * - * @param cls closure. - * @param cmd the /tip-query CMD to free. - */ -static void -tip_query_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct TipQueryState *tqs = cls; - - if (NULL != tqs->tqo) - { - TALER_LOG_WARNING ("Tip-query operation" - " did not complete\n"); - TALER_MERCHANT_tip_query_cancel (tqs->tqo); - } - GNUNET_free (tqs); -} - - -/** - * Run a /tip-query CMD. - * - * @param cls closure. - * @param cmd the current /tip-query CMD. - * @param is the interpreter state. - */ -static void -tip_query_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct TipQueryState *tqs = cls; - - tqs->is = is; - tqs->tqo = TALER_MERCHANT_tip_query (is->ctx, - tqs->merchant_url, - &tip_query_cb, - tqs); - GNUNET_assert (NULL != tqs->tqo); -} - - -/** - * Define a /tip-query CMD equipped with a expected amount. - * - * @param label the command label - * @param merchant_url base URL of the merchant which will - * server the /tip-query request. - * @param http_status expected HTTP response code for the - * /tip-query request. - * @param expected_amount_picked_up expected amount already - * picked up. - * @param expected_amount_authorized expected amount that was - * authorized in the first place. - * @param expected_amount_available expected amount which is - * still available from the tip reserve - * @return the command - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_tip_query_with_amounts - (const char *label, - const char *merchant_url, - unsigned int http_status, - const char *expected_amount_picked_up, - const char *expected_amount_authorized, - const char *expected_amount_available) -{ - struct TipQueryState *tqs; - - tqs = GNUNET_new (struct TipQueryState); - tqs->merchant_url = merchant_url; - tqs->http_status = http_status; - tqs->expected_amount_picked_up = expected_amount_picked_up; - tqs->expected_amount_authorized = expected_amount_authorized; - tqs->expected_amount_available = expected_amount_available; - - struct TALER_TESTING_Command cmd = { - .cls = tqs, - .label = label, - .run = &tip_query_run, - .cleanup = &tip_query_cleanup - }; - - return cmd; -} - - -/** - * Define a /tip-query CMD. - * - * @param label the command label - * @param merchant_url base URL of the merchant which will - * server the /tip-query request. - * @param http_status expected HTTP response code for the - * /tip-query request. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_tip_query (const char *label, - const char *merchant_url, - unsigned int http_status) -{ - struct TipQueryState *tqs; - - tqs = GNUNET_new (struct TipQueryState); - tqs->merchant_url = merchant_url; - tqs->http_status = http_status; - - struct TALER_TESTING_Command cmd = { - .cls = tqs, - .label = label, - .run = &tip_query_run, - .cleanup = &tip_query_cleanup - }; - - return cmd; -} - - -/** - * Internal withdraw handle used when withdrawing tips. - */ -struct WithdrawHandle -{ - /** - * Withdraw operation this handle represents. - */ - struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh; - - /** - * Interpreter state. - */ - struct TALER_TESTING_Interpreter *is; - - /** - * Offset of this withdraw operation in the current - * @e is command. - */ - unsigned int off; - - - /** - * Internal state of the "pickup" CMD. - */ - struct TipPickupState *tps; -}; - -/** - * This callback handles the response of a withdraw operation - * from the exchange, that is the final step in getting the tip. - * - * @param cls closure, a `struct WithdrawHandle *` - * @param http_status HTTP response code, #MHD_HTTP_OK (200) - * for successful status request, 0 if the exchange's - * reply is bogus (fails to follow the protocol) - * @param ec taler-specific error code, #TALER_EC_NONE on success - * @param sig signature over the coin, NULL on error - * @param full_response full response from the exchange - * (for logging, in case of errors) - */ -static void -pickup_withdraw_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const struct TALER_DenominationSignature *sig, - const json_t *full_response) -{ - struct WithdrawHandle *wh = cls; - struct TALER_TESTING_Interpreter *is = wh->is; - - struct TipPickupState *tps = wh->tps; - - wh->wsh = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Withdraw operation %u completed with %u (%d)\n", - wh->off, - http_status, - ec); - GNUNET_assert (wh->off < tps->num_coins); - if ( (MHD_HTTP_OK != http_status) || - (TALER_EC_NONE != ec) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u (%d)" - " to command %s when withdrawing\n", - http_status, - ec, - TALER_TESTING_interpreter_get_current_label (is)); - TALER_TESTING_interpreter_fail (is); - return; - } - if (NULL == tps->sigs) - tps->sigs = GNUNET_new_array - (tps->num_coins, struct TALER_DenominationSignature); - - GNUNET_assert (NULL == tps->sigs[wh->off].rsa_signature); - tps->sigs[wh->off].rsa_signature - = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature); - - for (unsigned int i = 0; i<tps->num_coins; i++) - if (NULL != tps->withdraws[wh->off].wsh) - return; - /* still some ops ongoing */ - - GNUNET_free (tps->withdraws); - tps->withdraws = NULL; - TALER_TESTING_interpreter_next (is); -} - - -/** - * Callback for a /tip-pickup request, it mainly checks if - * values returned from the backend are as expected, and if so - * (and if the status was 200 OK) proceede with the withdrawal. - * - * @param cls closure - * @param http_status HTTP status returned by the merchant - * backend, "200 OK" on success - * @param ec taler-specific error code - * @param reserve_pub public key of the reserve that made the - * @a reserve_sigs, NULL on error - * @param num_reserve_sigs length of the @a reserve_sigs array, - * 0 on error - * @param reserve_sigs array of signatures authorizing withdrawals, - * NULL on error - * @param json original json response - */ -static void -pickup_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const struct TALER_ReservePublicKeyP *reserve_pub, - unsigned int num_reserve_sigs, - const struct TALER_ReserveSignatureP *reserve_sigs, - const json_t *json) -{ - struct TipPickupState *tps = cls; - - tps->tpo = NULL; - if (http_status != tps->http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u (%d) to command %s\n", - http_status, - ec, - TALER_TESTING_interpreter_get_current_label - (tps->is)); - TALER_TESTING_FAIL (tps->is); - } - - if (ec != tps->expected_ec) - TALER_TESTING_FAIL (tps->is); - - /* Safe to go ahead: http status was expected. */ - if ( (MHD_HTTP_OK != http_status) || - (TALER_EC_NONE != ec) ) - { - TALER_TESTING_interpreter_next (tps->is); - return; - } - if (num_reserve_sigs != tps->num_coins) - TALER_TESTING_FAIL (tps->is); - - /* pickup successful, now withdraw! */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Obtained %u signatures for withdrawal" - " from picking up a tip\n", - num_reserve_sigs); - - GNUNET_assert (NULL == tps->withdraws); - tps->withdraws = GNUNET_new_array - (num_reserve_sigs, struct WithdrawHandle); - - for (unsigned int i = 0; i<num_reserve_sigs; i++) - { - struct WithdrawHandle *wh = &tps->withdraws[i]; - - wh->off = i; - wh->is = tps->is; - wh->tps = tps; - GNUNET_assert - ( (NULL == wh->wsh) && - ( (NULL == tps->sigs) || - (NULL == tps->sigs[wh->off].rsa_signature) ) ); - wh->wsh = TALER_EXCHANGE_reserve_withdraw2 - (tps->is->exchange, - tps->dks[i], - &reserve_sigs[i], - reserve_pub, - &tps->psa[i], - &pickup_withdraw_cb, - wh); - if (NULL == wh->wsh) - TALER_TESTING_FAIL (tps->is); - } - if (0 == num_reserve_sigs) - TALER_TESTING_interpreter_next (tps->is); -} - - -/** - * Run a /tip-pickup CMD. - * - * @param cls closure - * @param cmd the current /tip-pickup CMD. - * @param is interpreter state. - */ -static void -tip_pickup_run (void *cls, - const struct TALER_TESTING_Command *cmd, - struct TALER_TESTING_Interpreter *is) -{ - struct TipPickupState *tps = cls; - unsigned int num_planchets; - const struct TALER_TESTING_Command *replay_cmd; - const struct TALER_TESTING_Command *authorize_cmd; - const struct GNUNET_HashCode *tip_id; - - tps->is = is; - tps->exchange_url = TALER_EXCHANGE_get_base_url (is->exchange); - if (NULL == tps->replay_reference) - { - replay_cmd = NULL; - - /* Count planchets. */ - for (num_planchets = 0; - NULL != tps->amounts[num_planchets]; - num_planchets++) - ; - } - else - { - const unsigned int *np; - if (NULL == /* looking for "parent" tip-pickup command */ - (replay_cmd = TALER_TESTING_interpreter_lookup_command - (is, tps->replay_reference)) ) - TALER_TESTING_FAIL (is); - - if (GNUNET_OK != TALER_TESTING_get_trait_uint - (replay_cmd, 0, &np)) - TALER_TESTING_FAIL (is); - num_planchets = *np; - } - - if (NULL == - (authorize_cmd = TALER_TESTING_interpreter_lookup_command - (is, tps->authorize_reference)) ) - TALER_TESTING_FAIL (is); - - tps->num_coins = num_planchets; - { - struct TALER_PlanchetDetail planchets[num_planchets]; - - tps->psa = GNUNET_new_array (num_planchets, - struct TALER_PlanchetSecretsP); - tps->dks = GNUNET_new_array - (num_planchets, - const struct TALER_EXCHANGE_DenomPublicKey *); - - tps->amounts_obj = GNUNET_new_array - (num_planchets, struct TALER_Amount); - - for (unsigned int i = 0; i<num_planchets; i++) - { - if (NULL == replay_cmd) - { - GNUNET_assert (GNUNET_OK == TALER_string_to_amount - (tps->amounts[i], &tps->amounts_obj[i])); - - tps->dks[i] = TALER_TESTING_find_pk - (is->keys, - &tps->amounts_obj[i]); - - if (NULL == tps->dks[i]) - TALER_TESTING_FAIL (is); - - TALER_planchet_setup_random (&tps->psa[i]); - } - else - { - if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub - (replay_cmd, i, &tps->dks[i])) - TALER_TESTING_FAIL (is); - - struct TALER_PlanchetSecretsP *ps; - - if (GNUNET_OK != TALER_TESTING_get_trait_planchet_secrets - (replay_cmd, i, &ps)) - TALER_TESTING_FAIL (is); - tps->psa[i] = *ps; - } - - if (GNUNET_OK != TALER_planchet_prepare (&tps->dks[i]->key, - &tps->psa[i], - &planchets[i])) - TALER_TESTING_FAIL (is); - } - - if (GNUNET_OK != TALER_TESTING_get_trait_tip_id - (authorize_cmd, 0, &tip_id)) - TALER_TESTING_FAIL (is); - - tps->tpo = TALER_MERCHANT_tip_pickup (is->ctx, - tps->merchant_url, - tip_id, - num_planchets, - planchets, - &pickup_cb, - tps); - for (unsigned int i = 0; i<num_planchets; i++) - { - GNUNET_free (planchets[i].coin_ev); - planchets[i].coin_ev = NULL; - planchets[i].coin_ev_size = 0; - } - GNUNET_assert (NULL != tps->tpo); - } -} - - -/** - * Free a /tip-pickup CMD state, and possibly cancel a - * pending /tip-pickup request. - * - * @param cls closure. - * @param cmd current CMD to be freed. - */ -static void -tip_pickup_cleanup (void *cls, - const struct TALER_TESTING_Command *cmd) -{ - struct TipPickupState *tps = cls; - - GNUNET_free_non_null (tps->amounts_obj); - GNUNET_free_non_null (tps->dks); - GNUNET_free_non_null (tps->psa); - GNUNET_free_non_null (tps->withdraws); - if (NULL != tps->sigs) - { - for (unsigned int i = 0; i<tps->num_coins; i++) - if (NULL != tps->sigs[i].rsa_signature) - GNUNET_CRYPTO_rsa_signature_free (tps->sigs[i].rsa_signature); - GNUNET_free (tps->sigs); - } - - if (NULL != tps->tpo) - { - TALER_LOG_WARNING ("Tip-pickup operation" - " did not complete\n"); - TALER_MERCHANT_tip_pickup_cancel (tps->tpo); - } - - GNUNET_free (tps); -} - - -/** - * Offers information from the /tip-pickup CMD state to other - * commands. - * - * @param cls closure - * @param ret[out] result (could be anything) - * @param trait name of the trait - * @param index index number of the object to extract. - * @return #GNUNET_OK on success - */ -static int -tip_pickup_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - struct TipPickupState *tps = cls; - #define NUM_TRAITS (tps->num_coins * 5) + 2 - struct TALER_TESTING_Trait traits[NUM_TRAITS]; - - for (unsigned int i = 0; i<tps->num_coins; i++) - { - traits[i] = TALER_TESTING_make_trait_planchet_secrets - (i, &tps->psa[i]); - - traits[i + tps->num_coins] = - TALER_TESTING_make_trait_coin_priv - (i, &tps->psa[i].coin_priv); - - traits[i + (tps->num_coins * 2)] = - TALER_TESTING_make_trait_denom_pub (i, tps->dks[i]); - - traits[i + (tps->num_coins * 3)] = - TALER_TESTING_make_trait_denom_sig (i, &tps->sigs[i]); - - traits[i + (tps->num_coins * 4)] = - TALER_TESTING_make_trait_amount_obj - (i, &tps->amounts_obj[i]); - - } - traits[NUM_TRAITS - 2] = TALER_TESTING_make_trait_url - (0, tps->exchange_url); - traits[NUM_TRAITS - 1] = TALER_TESTING_trait_end (); - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); -} - - -/** - * Define a /tip-pickup CMD, equipped with the expected error - * code. - * - * @param label the command label - * @param merchant_url base URL of the backend which will serve - * the /tip-pickup request. - * @param http_status expected HTTP response code. - * @param authorize_reference reference to a /tip-autorize CMD - * that offers a tip id to pick up. - * @param amounts array of string-defined amounts that specifies - * which denominations will be accepted for tipping. - * @param exchange connection handle to the exchange that will - * eventually serve the withdraw operation. - * @param ec expected Taler error code. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_tip_pickup_with_ec - (const char *label, - const char *merchant_url, - unsigned int http_status, - const char *authorize_reference, - const char **amounts, - enum TALER_ErrorCode ec) -{ - struct TipPickupState *tps; - - tps = GNUNET_new (struct TipPickupState); - tps->merchant_url = merchant_url; - tps->authorize_reference = authorize_reference; - tps->amounts = amounts; - tps->http_status = http_status; - tps->expected_ec = ec; - - struct TALER_TESTING_Command cmd = { - .cls = tps, - .label = label, - .run = &tip_pickup_run, - .cleanup = &tip_pickup_cleanup, - .traits = &tip_pickup_traits - }; - - return cmd; -} - - -/** - * Define a /tip-pickup CMD. - * - * @param label the command label - * @param merchant_url base URL of the backend which will serve - * the /tip-pickup request. - * @param http_status expected HTTP response code. - * @param authorize_reference reference to a /tip-autorize CMD - * that offers a tip id to pick up. - * @param amounts array of string-defined amounts that specifies - * which denominations will be accepted for tipping. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_tip_pickup - (const char *label, - const char *merchant_url, - unsigned int http_status, - const char *authorize_reference, - const char **amounts) -{ - struct TipPickupState *tps; - - tps = GNUNET_new (struct TipPickupState); - tps->merchant_url = merchant_url; - tps->authorize_reference = authorize_reference; - tps->amounts = amounts; - tps->http_status = http_status; - - struct TALER_TESTING_Command cmd = { - .cls = tps, - .label = label, - .run = &tip_pickup_run, - .cleanup = &tip_pickup_cleanup, - .traits = &tip_pickup_traits - }; - - return cmd; -} - - -/** - * This commands does not query the backend at all, - * but just makes up a fake authorization id that will - * be subsequently used by the "pick up" CMD in order - * to test against such a case. - * - * @param label command label. - * - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_tip_authorize_fake (const char *label) -{ - struct TipAuthorizeState *tas; - - tas = GNUNET_new (struct TipAuthorizeState); - - struct TALER_TESTING_Command cmd = { - .label = label, - .cls = tas, - .run = &tip_authorize_fake_run, - .cleanup = &tip_authorize_cleanup, - .traits = &tip_authorize_traits - }; - - return cmd; -} - - -/* end of testing_api_cmd_tip.c */ diff --git a/src/lib/testing_api_cmd_tip_authorize.c b/src/lib/testing_api_cmd_tip_authorize.c @@ -0,0 +1,375 @@ +/* + This file is part of TALER + Copyright (C) 2014-2018 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 + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ + +/** + * @file lib/testing_api_cmd_tip_authorize.c + * @brief command to test the tipping. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include <taler/taler_exchange_service.h> +#include <taler/taler_testing_lib.h> +#include "taler_merchant_service.h" +#include "taler_merchant_testing_lib.h" + + +/** + * State for a /tip-authorize CMD. + */ +struct TipAuthorizeState +{ + + /** + * Merchant base URL. + */ + const char *merchant_url; + + /** + * Expected HTTP response code. + */ + unsigned int http_status; + + /** + * Human-readable justification for the + * tip authorization carried on by this CMD. + */ + const char *justification; + + /** + * Amount that should be authorized for tipping. + */ + const char *amount; + + /** + * Expected Taler error code for this CMD. + */ + enum TALER_ErrorCode expected_ec; + + /** + * Tip taler:// URI. + */ + const char *tip_uri; + + /** + * The tip id; set when the CMD succeeds. + */ + struct GNUNET_HashCode tip_id; + + /** + * Expiration date for this tip. + */ + struct GNUNET_TIME_Absolute tip_expiration; + + /** + * Handle to the on-going /tip-authorize request. + */ + struct TALER_MERCHANT_TipAuthorizeOperation *tao; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; +}; + + +/** + * Callback for a /tip-authorize request. Set into the state + * what was returned from the backend (@a tip_id and @a + * tip_expiration). + * + * @param cls closure + * @param http_status HTTP status returned by the merchant backend + * @param ec taler-specific error code + * @param taler_tip_uri URI to let the wallet know about the tip + * @param tip_id unique identifier for the tip + */ +static void +tip_authorize_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + const char *taler_tip_uri, + struct GNUNET_HashCode *tip_id) +{ + struct TipAuthorizeState *tas = cls; + + tas->tao = NULL; + if (tas->http_status != http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u (%d)" + " to command %s\n", + http_status, + ec, + TALER_TESTING_interpreter_get_current_label + (tas->is)); + + TALER_TESTING_interpreter_fail (tas->is); + return; + } + + if (tas->expected_ec != ec) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected error code %d (%u) to command %s\n", + ec, + http_status, + TALER_TESTING_interpreter_get_current_label + (tas->is)); + TALER_TESTING_interpreter_fail (tas->is); + return; + } + if ( (MHD_HTTP_OK == http_status) && + (TALER_EC_NONE == ec) ) + { + tas->tip_uri = strdup (taler_tip_uri); + tas->tip_id = *tip_id; + } + + TALER_TESTING_interpreter_next (tas->is); +} + + +/** + * Offers information from the /tip-authorize CMD state to other + * commands. + * + * @param cls closure + * @param ret[out] result (could be anything) + * @param trait name of the trait + * @param index index number of the object to extract. + * @return #GNUNET_OK on success + */ +static int +tip_authorize_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct TipAuthorizeState *tas = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_tip_id (0, &tas->tip_id), + TALER_TESTING_trait_end (), + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +/** + * Runs the /tip-authorize CMD + * + * @param cls closure + * @param cmd the CMD representing _this_ command + * @param is interpreter state + */ +static void +tip_authorize_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct TipAuthorizeState *tas = cls; + struct TALER_Amount amount; + + tas->is = is; + if (GNUNET_OK != TALER_string_to_amount (tas->amount, + &amount)) + TALER_TESTING_FAIL (is); + + tas->tao = TALER_MERCHANT_tip_authorize + (is->ctx, + tas->merchant_url, + "http://merchant.com/pickup", + "http://merchant.com/continue", + &amount, + tas->justification, + tip_authorize_cb, + tas); + + GNUNET_assert (NULL != tas->tao); +} + + +/** + * Run the /tip-authorize CMD, the "fake" version of it. + * + * @param cls closure + * @param cmd the CMD representing _this_ command + * @param is interpreter state * + */ +static void +tip_authorize_fake_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct TipAuthorizeState *tas = cls; + + /* Make up a tip id. */ + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &tas->tip_id, + sizeof (struct GNUNET_HashCode)); + + TALER_TESTING_interpreter_next (is); +} + + +/** + * Free the state from a /tip-authorize CMD, and possibly + * cancel any pending operation. + * + * @param cls closure + * @param cmd the /tip-authorize CMD that is about to be freed. + */ +static void +tip_authorize_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct TipAuthorizeState *tas = cls; + + if (NULL != tas->tao) + { + TALER_LOG_WARNING ("Tip-autorize operation" + " did not complete\n"); + TALER_MERCHANT_tip_authorize_cancel (tas->tao); + } + GNUNET_free (tas); +} + + +/** + * Create a /tip-authorize CMD, specifying the Taler error code + * that is expected to be returned by the backend. + * + * @param label this command label + * @param merchant_url the base URL of the merchant that will + * serve the /tip-authorize request. + * @param exchange_url the base URL of the exchange that owns + * the reserve from which the tip is going to be gotten. + * @param http_status the HTTP response code which is expected + * for this operation. + * @param justification human-readable justification for this + * tip authorization. + * @param amount the amount to authorize for tipping. + * @param ec expected Taler-defined error code. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_tip_authorize_with_ec + (const char *label, + const char *merchant_url, + const char *exchange_url, + unsigned int http_status, + const char *justification, + const char *amount, + enum TALER_ErrorCode ec) +{ + struct TipAuthorizeState *tas; + + tas = GNUNET_new (struct TipAuthorizeState); + tas->merchant_url = merchant_url; + tas->justification = justification; + tas->amount = amount; + tas->http_status = http_status; + tas->expected_ec = ec; + + struct TALER_TESTING_Command cmd = { + .label = label, + .cls = tas, + .run = &tip_authorize_run, + .cleanup = &tip_authorize_cleanup, + .traits = &tip_authorize_traits + }; + + return cmd; +} + + +/** + * Create a /tip-authorize CMD. + * + * @param label this command label + * @param merchant_url the base URL of the merchant that will + * serve the /tip-authorize request. + * @param exchange_url the base URL of the exchange that owns + * the reserve from which the tip is going to be gotten. + * @param http_status the HTTP response code which is expected + * for this operation. + * @param justification human-readable justification for this + * tip authorization. + * @param amount the amount to authorize for tipping. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_tip_authorize (const char *label, + const char *merchant_url, + const char *exchange_url, + unsigned int http_status, + const char *justification, + const char *amount) +{ + struct TipAuthorizeState *tas; + + tas = GNUNET_new (struct TipAuthorizeState); + tas->merchant_url = merchant_url; + tas->justification = justification; + tas->amount = amount; + tas->http_status = http_status; + + struct TALER_TESTING_Command cmd = { + .label = label, + .cls = tas, + .run = &tip_authorize_run, + .cleanup = &tip_authorize_cleanup, + .traits = &tip_authorize_traits + }; + + return cmd; +} + + +/** + * This commands does not query the backend at all, + * but just makes up a fake authorization id that will + * be subsequently used by the "pick up" CMD in order + * to test against such a case. + * + * @param label command label. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_tip_authorize_fake (const char *label) +{ + struct TipAuthorizeState *tas; + + tas = GNUNET_new (struct TipAuthorizeState); + + struct TALER_TESTING_Command cmd = { + .label = label, + .cls = tas, + .run = &tip_authorize_fake_run, + .cleanup = &tip_authorize_cleanup, + .traits = &tip_authorize_traits + }; + + return cmd; +} + + +/* end of testing_api_cmd_tip_authorize.c */ diff --git a/src/lib/testing_api_cmd_tip_pickup.c b/src/lib/testing_api_cmd_tip_pickup.c @@ -0,0 +1,598 @@ +/* + This file is part of TALER + Copyright (C) 2014-2018 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 + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ + +/** + * @file lib/testing_api_cmd_tip.c + * @brief command to test the tipping. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include <taler/taler_exchange_service.h> +#include <taler/taler_testing_lib.h> +#include "taler_merchant_service.h" +#include "taler_merchant_testing_lib.h" + +/** + * State for a /tip-pickup CMD. + */ +struct TipPickupState +{ + /** + * Merchant base URL. + */ + const char *merchant_url; + + /** + * Exchange base URL. + */ + const char *exchange_url; + + /** + * Expected HTTP response code. + */ + unsigned int http_status; + + /** + * Reference to a /tip/authorize CMD. This will be used to + * get the tip id to make the request with. + */ + const char *authorize_reference; + + /** + * If set to non NULL, it references another pickup CMD + * that will provide all the data which is needed to issue + * the request (like planchet secrets, denomination keys..). + */ + const char *replay_reference; + + /** + * Handle to a on-going /tip/pickup request. + */ + struct TALER_MERCHANT_TipPickupOperation *tpo; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * An array of string-defined amounts that indicates + * which denominations are going to be used to receive + * tips. + */ + const char **amounts; + + /** + * The object version of the above @a amounts. + */ + struct TALER_Amount *amounts_obj; + + /** + * How many coins are involved in the tipping operation. + */ + unsigned int num_coins; + + /** + * The array of denomination keys, in the same order of @a + * amounts. + */ + const struct TALER_EXCHANGE_DenomPublicKey **dks; + + /** + * The array of planchet secrets, in the same order of @a + * amounts. + */ + struct TALER_PlanchetSecretsP *psa; + + /** + * Temporary data structure of @e num_coins entries for the + * withdraw operations. + */ + struct WithdrawHandle *withdraws; + + /** + * Set (by the interpreter) to an array of @a num_coins + * signatures created from the (successful) tip operation. + */ + struct TALER_DenominationSignature *sigs; + + /** + * Expected Taler error code (NOTE: this is NOT the HTTP + * response code). + */ + enum TALER_ErrorCode expected_ec; +}; + + +/** + * Internal withdraw handle used when withdrawing tips. + */ +struct WithdrawHandle +{ + /** + * Withdraw operation this handle represents. + */ + struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Offset of this withdraw operation in the current + * @e is command. + */ + unsigned int off; + + /** + * Internal state of the "pickup" CMD. + */ + struct TipPickupState *tps; +}; + + +/** + * This callback handles the response of a withdraw operation + * from the exchange, that is the final step in getting the tip. + * + * @param cls closure, a `struct WithdrawHandle *` + * @param http_status HTTP response code, #MHD_HTTP_OK (200) + * for successful status request, 0 if the exchange's + * reply is bogus (fails to follow the protocol) + * @param ec taler-specific error code, #TALER_EC_NONE on success + * @param sig signature over the coin, NULL on error + * @param full_response full response from the exchange + * (for logging, in case of errors) + */ +static void +pickup_withdraw_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + const struct TALER_DenominationSignature *sig, + const json_t *full_response) +{ + struct WithdrawHandle *wh = cls; + struct TALER_TESTING_Interpreter *is = wh->is; + + struct TipPickupState *tps = wh->tps; + + wh->wsh = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Withdraw operation %u completed with %u (%d)\n", + wh->off, + http_status, + ec); + GNUNET_assert (wh->off < tps->num_coins); + if ( (MHD_HTTP_OK != http_status) || + (TALER_EC_NONE != ec) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u (%d)" + " to command %s when withdrawing\n", + http_status, + ec, + TALER_TESTING_interpreter_get_current_label (is)); + TALER_TESTING_interpreter_fail (is); + return; + } + if (NULL == tps->sigs) + tps->sigs = GNUNET_new_array + (tps->num_coins, struct TALER_DenominationSignature); + + GNUNET_assert (NULL == tps->sigs[wh->off].rsa_signature); + tps->sigs[wh->off].rsa_signature + = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature); + + for (unsigned int i = 0; i<tps->num_coins; i++) + if (NULL != tps->withdraws[wh->off].wsh) + return; + /* still some ops ongoing */ + + GNUNET_free (tps->withdraws); + tps->withdraws = NULL; + TALER_TESTING_interpreter_next (is); +} + + +/** + * Callback for a /tip-pickup request, it mainly checks if + * values returned from the backend are as expected, and if so + * (and if the status was 200 OK) proceede with the withdrawal. + * + * @param cls closure + * @param http_status HTTP status returned by the merchant + * backend, "200 OK" on success + * @param ec taler-specific error code + * @param reserve_pub public key of the reserve that made the + * @a reserve_sigs, NULL on error + * @param num_reserve_sigs length of the @a reserve_sigs array, + * 0 on error + * @param reserve_sigs array of signatures authorizing withdrawals, + * NULL on error + * @param json original json response + */ +static void +pickup_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + const struct TALER_ReservePublicKeyP *reserve_pub, + unsigned int num_reserve_sigs, + const struct TALER_ReserveSignatureP *reserve_sigs, + const json_t *json) +{ + struct TipPickupState *tps = cls; + + tps->tpo = NULL; + if (http_status != tps->http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u (%d) to command %s\n", + http_status, + ec, + TALER_TESTING_interpreter_get_current_label + (tps->is)); + TALER_TESTING_FAIL (tps->is); + } + + if (ec != tps->expected_ec) + TALER_TESTING_FAIL (tps->is); + + /* Safe to go ahead: http status was expected. */ + if ( (MHD_HTTP_OK != http_status) || + (TALER_EC_NONE != ec) ) + { + TALER_TESTING_interpreter_next (tps->is); + return; + } + if (num_reserve_sigs != tps->num_coins) + TALER_TESTING_FAIL (tps->is); + + /* pickup successful, now withdraw! */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Obtained %u signatures for withdrawal" + " from picking up a tip\n", + num_reserve_sigs); + + GNUNET_assert (NULL == tps->withdraws); + tps->withdraws = GNUNET_new_array + (num_reserve_sigs, struct WithdrawHandle); + + for (unsigned int i = 0; i<num_reserve_sigs; i++) + { + struct WithdrawHandle *wh = &tps->withdraws[i]; + + wh->off = i; + wh->is = tps->is; + wh->tps = tps; + GNUNET_assert + ( (NULL == wh->wsh) && + ( (NULL == tps->sigs) || + (NULL == tps->sigs[wh->off].rsa_signature) ) ); + wh->wsh = TALER_EXCHANGE_reserve_withdraw2 + (tps->is->exchange, + tps->dks[i], + &reserve_sigs[i], + reserve_pub, + &tps->psa[i], + &pickup_withdraw_cb, + wh); + if (NULL == wh->wsh) + TALER_TESTING_FAIL (tps->is); + } + if (0 == num_reserve_sigs) + TALER_TESTING_interpreter_next (tps->is); +} + + +/** + * Run a /tip-pickup CMD. + * + * @param cls closure + * @param cmd the current /tip-pickup CMD. + * @param is interpreter state. + */ +static void +tip_pickup_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct TipPickupState *tps = cls; + unsigned int num_planchets; + const struct TALER_TESTING_Command *replay_cmd; + const struct TALER_TESTING_Command *authorize_cmd; + const struct GNUNET_HashCode *tip_id; + + tps->is = is; + tps->exchange_url = TALER_EXCHANGE_get_base_url (is->exchange); + if (NULL == tps->replay_reference) + { + replay_cmd = NULL; + + /* Count planchets. */ + for (num_planchets = 0; + NULL != tps->amounts[num_planchets]; + num_planchets++) + ; + } + else + { + const unsigned int *np; + if (NULL == /* looking for "parent" tip-pickup command */ + (replay_cmd = TALER_TESTING_interpreter_lookup_command + (is, tps->replay_reference)) ) + TALER_TESTING_FAIL (is); + + if (GNUNET_OK != TALER_TESTING_get_trait_uint + (replay_cmd, 0, &np)) + TALER_TESTING_FAIL (is); + num_planchets = *np; + } + + if (NULL == + (authorize_cmd = TALER_TESTING_interpreter_lookup_command + (is, tps->authorize_reference)) ) + TALER_TESTING_FAIL (is); + + tps->num_coins = num_planchets; + { + struct TALER_PlanchetDetail planchets[num_planchets]; + + tps->psa = GNUNET_new_array (num_planchets, + struct TALER_PlanchetSecretsP); + tps->dks = GNUNET_new_array + (num_planchets, + const struct TALER_EXCHANGE_DenomPublicKey *); + + tps->amounts_obj = GNUNET_new_array + (num_planchets, struct TALER_Amount); + + for (unsigned int i = 0; i<num_planchets; i++) + { + if (NULL == replay_cmd) + { + GNUNET_assert (GNUNET_OK == TALER_string_to_amount + (tps->amounts[i], &tps->amounts_obj[i])); + + tps->dks[i] = TALER_TESTING_find_pk + (is->keys, + &tps->amounts_obj[i]); + + if (NULL == tps->dks[i]) + TALER_TESTING_FAIL (is); + + TALER_planchet_setup_random (&tps->psa[i]); + } + else + { + if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub + (replay_cmd, i, &tps->dks[i])) + TALER_TESTING_FAIL (is); + + struct TALER_PlanchetSecretsP *ps; + + if (GNUNET_OK != TALER_TESTING_get_trait_planchet_secrets + (replay_cmd, i, &ps)) + TALER_TESTING_FAIL (is); + tps->psa[i] = *ps; + } + + if (GNUNET_OK != TALER_planchet_prepare (&tps->dks[i]->key, + &tps->psa[i], + &planchets[i])) + TALER_TESTING_FAIL (is); + } + + if (GNUNET_OK != TALER_TESTING_get_trait_tip_id + (authorize_cmd, 0, &tip_id)) + TALER_TESTING_FAIL (is); + + tps->tpo = TALER_MERCHANT_tip_pickup (is->ctx, + tps->merchant_url, + tip_id, + num_planchets, + planchets, + &pickup_cb, + tps); + for (unsigned int i = 0; i<num_planchets; i++) + { + GNUNET_free (planchets[i].coin_ev); + planchets[i].coin_ev = NULL; + planchets[i].coin_ev_size = 0; + } + GNUNET_assert (NULL != tps->tpo); + } +} + + +/** + * Free a /tip-pickup CMD state, and possibly cancel a + * pending /tip-pickup request. + * + * @param cls closure. + * @param cmd current CMD to be freed. + */ +static void +tip_pickup_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct TipPickupState *tps = cls; + + GNUNET_free_non_null (tps->amounts_obj); + GNUNET_free_non_null (tps->dks); + GNUNET_free_non_null (tps->psa); + GNUNET_free_non_null (tps->withdraws); + if (NULL != tps->sigs) + { + for (unsigned int i = 0; i<tps->num_coins; i++) + if (NULL != tps->sigs[i].rsa_signature) + GNUNET_CRYPTO_rsa_signature_free (tps->sigs[i].rsa_signature); + GNUNET_free (tps->sigs); + } + + if (NULL != tps->tpo) + { + TALER_LOG_WARNING ("Tip-pickup operation" + " did not complete\n"); + TALER_MERCHANT_tip_pickup_cancel (tps->tpo); + } + + GNUNET_free (tps); +} + + +/** + * Offers information from the /tip-pickup CMD state to other + * commands. + * + * @param cls closure + * @param ret[out] result (could be anything) + * @param trait name of the trait + * @param index index number of the object to extract. + * @return #GNUNET_OK on success + */ +static int +tip_pickup_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct TipPickupState *tps = cls; + #define NUM_TRAITS (tps->num_coins * 5) + 2 + struct TALER_TESTING_Trait traits[NUM_TRAITS]; + + for (unsigned int i = 0; i<tps->num_coins; i++) + { + traits[i] = TALER_TESTING_make_trait_planchet_secrets + (i, &tps->psa[i]); + + traits[i + tps->num_coins] = + TALER_TESTING_make_trait_coin_priv + (i, &tps->psa[i].coin_priv); + + traits[i + (tps->num_coins * 2)] = + TALER_TESTING_make_trait_denom_pub (i, tps->dks[i]); + + traits[i + (tps->num_coins * 3)] = + TALER_TESTING_make_trait_denom_sig (i, &tps->sigs[i]); + + traits[i + (tps->num_coins * 4)] = + TALER_TESTING_make_trait_amount_obj + (i, &tps->amounts_obj[i]); + + } + traits[NUM_TRAITS - 2] = TALER_TESTING_make_trait_url + (0, tps->exchange_url); + traits[NUM_TRAITS - 1] = TALER_TESTING_trait_end (); + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +/** + * Define a /tip-pickup CMD, equipped with the expected error + * code. + * + * @param label the command label + * @param merchant_url base URL of the backend which will serve + * the /tip-pickup request. + * @param http_status expected HTTP response code. + * @param authorize_reference reference to a /tip-autorize CMD + * that offers a tip id to pick up. + * @param amounts array of string-defined amounts that specifies + * which denominations will be accepted for tipping. + * @param exchange connection handle to the exchange that will + * eventually serve the withdraw operation. + * @param ec expected Taler error code. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_tip_pickup_with_ec + (const char *label, + const char *merchant_url, + unsigned int http_status, + const char *authorize_reference, + const char **amounts, + enum TALER_ErrorCode ec) +{ + struct TipPickupState *tps; + + tps = GNUNET_new (struct TipPickupState); + tps->merchant_url = merchant_url; + tps->authorize_reference = authorize_reference; + tps->amounts = amounts; + tps->http_status = http_status; + tps->expected_ec = ec; + + struct TALER_TESTING_Command cmd = { + .cls = tps, + .label = label, + .run = &tip_pickup_run, + .cleanup = &tip_pickup_cleanup, + .traits = &tip_pickup_traits + }; + + return cmd; +} + + +/** + * Define a /tip-pickup CMD. + * + * @param label the command label + * @param merchant_url base URL of the backend which will serve + * the /tip-pickup request. + * @param http_status expected HTTP response code. + * @param authorize_reference reference to a /tip-autorize CMD + * that offers a tip id to pick up. + * @param amounts array of string-defined amounts that specifies + * which denominations will be accepted for tipping. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_tip_pickup + (const char *label, + const char *merchant_url, + unsigned int http_status, + const char *authorize_reference, + const char **amounts) +{ + struct TipPickupState *tps; + + tps = GNUNET_new (struct TipPickupState); + tps->merchant_url = merchant_url; + tps->authorize_reference = authorize_reference; + tps->amounts = amounts; + tps->http_status = http_status; + + struct TALER_TESTING_Command cmd = { + .cls = tps, + .label = label, + .run = &tip_pickup_run, + .cleanup = &tip_pickup_cleanup, + .traits = &tip_pickup_traits + }; + + return cmd; +} + + +/* end of testing_api_cmd_tip_pickup.c */ diff --git a/src/lib/testing_api_cmd_tip_query.c b/src/lib/testing_api_cmd_tip_query.c @@ -0,0 +1,297 @@ +/* + This file is part of TALER + Copyright (C) 2014-2018 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 + published by the Free Software Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ + +/** + * @file lib/testing_api_cmd_tip_query.c + * @brief command to test the tipping. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include <taler/taler_exchange_service.h> +#include <taler/taler_testing_lib.h> +#include "taler_merchant_service.h" +#include "taler_merchant_testing_lib.h" + + +/** + * State for a /tip-query CMD. + */ +struct TipQueryState +{ + + /** + * The merchant base URL. + */ + const char *merchant_url; + + /** + * Expected HTTP response code for this CMD. + */ + unsigned int http_status; + + /** + * The handle to the current /tip-query request. + */ + struct TALER_MERCHANT_TipQueryOperation *tqo; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Expected amount to be picked up. + */ + const char *expected_amount_picked_up; + + /** + * Expected amount to be tip-authorized. + */ + const char *expected_amount_authorized; + + /** + * Amount that is expected to be still available + * from the tip reserve. + */ + const char *expected_amount_available; +}; + + +/** + * Callback to process a GET /tip-query request, it mainly + * checks that what the backend returned matches the command's + * expectations. + * + * @param cls closure + * @param http_status HTTP status code for this request + * @param ec Taler-specific error code + * @param raw raw response body + * @param reserve_expiration when the tip reserve will expire + * @param reserve_pub tip reserve public key + * @param amount_authorized total amount authorized on tip reserve + * @param amount_available total amount still available on + * tip reserve + * @param amount_picked_up total amount picked up from tip reserve + */ +static void +tip_query_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + const json_t *raw, + struct GNUNET_TIME_Absolute reserve_expiration, + struct TALER_ReservePublicKeyP *reserve_pub, + struct TALER_Amount *amount_authorized, + struct TALER_Amount *amount_available, + struct TALER_Amount *amount_picked_up) +{ + struct TipQueryState *tqs = cls; + struct TALER_Amount a; + + tqs->tqo = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Tip query callback at command `%s'\n", + TALER_TESTING_interpreter_get_current_label + (tqs->is)); + + GNUNET_assert (NULL != reserve_pub); + GNUNET_assert (NULL != amount_authorized); + GNUNET_assert (NULL != amount_available); + GNUNET_assert (NULL != amount_picked_up); + + if (tqs->expected_amount_available) + { + GNUNET_assert (GNUNET_OK == TALER_string_to_amount + (tqs->expected_amount_available, &a)); + { + char *str; + + str = TALER_amount_to_string (amount_available); + TALER_LOG_INFO ("expected available %s, actual %s\n", + TALER_amount2s (&a), + str); + GNUNET_free (str); + } + if (0 != TALER_amount_cmp (amount_available, &a)) + TALER_TESTING_FAIL (tqs->is); + } + + if (tqs->expected_amount_authorized) + { + GNUNET_assert (GNUNET_OK == TALER_string_to_amount + (tqs->expected_amount_authorized, &a)); + { + char *str; + + str = TALER_amount_to_string (amount_authorized); + TALER_LOG_INFO ("expected authorized %s, actual %s\n", + TALER_amount2s (&a), + str); + GNUNET_free (str); + } + if (0 != TALER_amount_cmp (amount_authorized, &a)) + TALER_TESTING_FAIL (tqs->is); + } + + if (tqs->expected_amount_picked_up) + { + GNUNET_assert (GNUNET_OK == TALER_string_to_amount + (tqs->expected_amount_picked_up, &a)); + { + char *str; + str = TALER_amount_to_string (amount_picked_up); + TALER_LOG_INFO ("expected picked_up %s, actual %s\n", + TALER_amount2s (&a), + str); + GNUNET_free (str); + } + if (0 != TALER_amount_cmp (amount_picked_up, &a)) + TALER_TESTING_FAIL (tqs->is); + } + + if (tqs->http_status != http_status) + TALER_TESTING_FAIL (tqs->is); + + TALER_TESTING_interpreter_next (tqs->is); +} + + +/** + * Free the state from a /tip-query CMD, and possibly cancel + * a pending /tip-query request. + * + * @param cls closure. + * @param cmd the /tip-query CMD to free. + */ +static void +tip_query_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct TipQueryState *tqs = cls; + + if (NULL != tqs->tqo) + { + TALER_LOG_WARNING ("Tip-query operation" + " did not complete\n"); + TALER_MERCHANT_tip_query_cancel (tqs->tqo); + } + GNUNET_free (tqs); +} + + +/** + * Run a /tip-query CMD. + * + * @param cls closure. + * @param cmd the current /tip-query CMD. + * @param is the interpreter state. + */ +static void +tip_query_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct TipQueryState *tqs = cls; + + tqs->is = is; + tqs->tqo = TALER_MERCHANT_tip_query (is->ctx, + tqs->merchant_url, + &tip_query_cb, + tqs); + GNUNET_assert (NULL != tqs->tqo); +} + + +/** + * Define a /tip-query CMD equipped with a expected amount. + * + * @param label the command label + * @param merchant_url base URL of the merchant which will + * server the /tip-query request. + * @param http_status expected HTTP response code for the + * /tip-query request. + * @param expected_amount_picked_up expected amount already + * picked up. + * @param expected_amount_authorized expected amount that was + * authorized in the first place. + * @param expected_amount_available expected amount which is + * still available from the tip reserve + * @return the command + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_tip_query_with_amounts + (const char *label, + const char *merchant_url, + unsigned int http_status, + const char *expected_amount_picked_up, + const char *expected_amount_authorized, + const char *expected_amount_available) +{ + struct TipQueryState *tqs; + + tqs = GNUNET_new (struct TipQueryState); + tqs->merchant_url = merchant_url; + tqs->http_status = http_status; + tqs->expected_amount_picked_up = expected_amount_picked_up; + tqs->expected_amount_authorized = expected_amount_authorized; + tqs->expected_amount_available = expected_amount_available; + + struct TALER_TESTING_Command cmd = { + .cls = tqs, + .label = label, + .run = &tip_query_run, + .cleanup = &tip_query_cleanup + }; + + return cmd; +} + + +/** + * Define a /tip-query CMD. + * + * @param label the command label + * @param merchant_url base URL of the merchant which will + * server the /tip-query request. + * @param http_status expected HTTP response code for the + * /tip-query request. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_tip_query (const char *label, + const char *merchant_url, + unsigned int http_status) +{ + struct TipQueryState *tqs; + + tqs = GNUNET_new (struct TipQueryState); + tqs->merchant_url = merchant_url; + tqs->http_status = http_status; + + struct TALER_TESTING_Command cmd = { + .cls = tqs, + .label = label, + .run = &tip_query_run, + .cleanup = &tip_query_cleanup + }; + + return cmd; +} + + +/* end of testing_api_cmd_tip_query.c */