/*
This file is part of TALER
Copyright (C) 2014-2020 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
*/
/**
* @file testing_api_cmd_tip_authorize.c
* @brief command to test the tipping.
* @author Marcello Stanisci
*/
#include "platform.h"
#include
#include
#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;
/**
* Reference to the reserv to authorize the tip
* from (if NULL, the merchant decides).
*/
const char *reserve_reference;
/**
* Human-readable justification for the
* tip authorization carried on by this CMD.
*/
const char *justification;
/**
* Amount that should be authorized for tipping.
*/
struct TALER_Amount amount;
/**
* Expected Taler error code for this CMD.
*/
enum TALER_ErrorCode expected_ec;
/**
* Tip taler:// URI.
*/
char *tip_uri;
/**
* The tip id; set when the CMD succeeds.
*/
struct TALER_TipIdentifierP tip_id;
/**
* Expiration date for this tip.
*/
struct GNUNET_TIME_Timestamp tip_expiration;
/**
* Handle to the on-going /tip-authorize request.
*/
struct TALER_MERCHANT_TipAuthorizeHandle *tao;
/**
* The interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
/**
* Task used for retries.
*/
struct GNUNET_SCHEDULER_Task *retry_task;
/**
* How long do we wait between retries?
*/
struct GNUNET_TIME_Relative backoff;
/**
* How many retries are left?
*/
unsigned int retries_left;
};
/**
* Run the main logic of talking to the merchant.
*
* @param cls a `struct TipAuthorizeState`.
*/
static void
do_retry (void *cls);
/**
* 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 hr HTTP response we got
* @param tip_id unique identifier for the tip
* @param taler_tip_uri URI to let the wallet know about the tip
* @param expiration when the tip expires
*/
static void
tip_authorize_cb (void *cls,
const struct TALER_MERCHANT_HttpResponse *hr,
struct TALER_TipIdentifierP *tip_id,
const char *taler_tip_uri,
struct GNUNET_TIME_Timestamp expiration)
{
struct TipAuthorizeState *tas = cls;
tas->tao = NULL;
if (tas->http_status != hr->http_status)
{
if ( (MHD_HTTP_NOT_FOUND == hr->http_status) &&
(0 < tas->retries_left) )
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Reserve authorization failed. Reserve may not yet be ready, retrying %u more times.\n",
tas->retries_left);
tas->retries_left--;
tas->backoff = GNUNET_TIME_randomized_backoff (tas->backoff,
GNUNET_TIME_UNIT_SECONDS);
tas->retry_task = GNUNET_SCHEDULER_add_delayed (tas->backoff,
&do_retry,
tas);
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u (%d) to command %s\n",
hr->http_status,
hr->ec,
TALER_TESTING_interpreter_get_current_label (tas->is));
TALER_TESTING_interpreter_fail (tas->is);
return;
}
if (tas->expected_ec != hr->ec)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected error code %d (%u) to command %s\n",
(int) hr->ec,
hr->http_status,
TALER_TESTING_interpreter_get_current_label (tas->is));
TALER_TESTING_interpreter_fail (tas->is);
return;
}
if ( (MHD_HTTP_OK == hr->http_status) &&
(TALER_EC_NONE == hr->ec) )
{
tas->tip_uri = strdup (taler_tip_uri);
tas->tip_id = *tip_id;
tas->tip_expiration = expiration;
}
TALER_TESTING_interpreter_next (tas->is);
}
/**
* Offers information from the /tip-authorize CMD state to other
* commands.
*
* @param cls closure
* @param[out] ret 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 enum GNUNET_GenericReturnValue
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 (&tas->tip_id),
TALER_TESTING_make_trait_amount (&tas->amount),
TALER_TESTING_make_trait_reason (&tas->justification),
TALER_TESTING_make_trait_timestamp (0,
&tas->tip_expiration),
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;
tas->retries_left = 16;
tas->is = is;
tas->retry_task = GNUNET_SCHEDULER_add_now (&do_retry,
tas);
}
static void
do_retry (void *cls)
{
struct TipAuthorizeState *tas = cls;
tas->retry_task = NULL;
if (NULL == tas->reserve_reference)
{
tas->tao = TALER_MERCHANT_tip_authorize (tas->is->ctx,
tas->merchant_url,
"http://merchant.com/pickup",
&tas->amount,
tas->justification,
&tip_authorize_cb,
tas);
}
else
{
const struct TALER_TESTING_Command *reserve_cmd;
const struct TALER_ReservePublicKeyP *reserve_pub;
reserve_cmd = TALER_TESTING_interpreter_lookup_command (
tas->is,
tas->reserve_reference);
GNUNET_assert (GNUNET_OK ==
TALER_TESTING_get_trait_reserve_pub (reserve_cmd,
&reserve_pub));
tas->tao = TALER_MERCHANT_tip_authorize2 (tas->is->ctx,
tas->merchant_url,
reserve_pub,
"http://merchant.com/pickup",
&tas->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 TALER_TipIdentifierP));
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);
}
if (NULL != tas->retry_task)
{
GNUNET_SCHEDULER_cancel (tas->retry_task);
tas->retry_task = NULL;
}
GNUNET_free (tas->tip_uri);
GNUNET_free (tas);
}
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->http_status = http_status;
tas->expected_ec = ec;
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (amount,
&tas->amount));
{
struct TALER_TESTING_Command cmd = {
.label = label,
.cls = tas,
.run = &tip_authorize_run,
.cleanup = &tip_authorize_cleanup,
.traits = &tip_authorize_traits
};
return cmd;
}
}
struct TALER_TESTING_Command
TALER_TESTING_cmd_tip_authorize_from_reserve_with_ec (
const char *label,
const char *merchant_url,
const char *exchange_url,
const char *reserve_reference,
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->http_status = http_status;
tas->expected_ec = ec;
tas->reserve_reference = reserve_reference;
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (amount,
&tas->amount));
{
struct TALER_TESTING_Command cmd = {
.label = label,
.cls = tas,
.run = &tip_authorize_run,
.cleanup = &tip_authorize_cleanup,
.traits = &tip_authorize_traits
};
return cmd;
}
}
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->http_status = http_status;
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (amount,
&tas->amount));
{
struct TALER_TESTING_Command cmd = {
.label = label,
.cls = tas,
.run = &tip_authorize_run,
.cleanup = &tip_authorize_cleanup,
.traits = &tip_authorize_traits
};
return cmd;
}
}
struct TALER_TESTING_Command
TALER_TESTING_cmd_tip_authorize_from_reserve (const char *label,
const char *merchant_url,
const char *exchange_url,
const char *reserve_reference,
unsigned int http_status,
const char *justification,
const char *amount)
{
struct TipAuthorizeState *tas;
tas = GNUNET_new (struct TipAuthorizeState);
tas->merchant_url = merchant_url;
tas->reserve_reference = reserve_reference;
tas->justification = justification;
tas->http_status = http_status;
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (amount,
&tas->amount));
{
struct TALER_TESTING_Command cmd = {
.label = label,
.cls = tas,
.run = &tip_authorize_run,
.cleanup = &tip_authorize_cleanup,
.traits = &tip_authorize_traits
};
return cmd;
}
}
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 */