new test interpreter architecture, first draft

This commit is contained in:
Christian Grothoff 2018-01-21 15:39:48 +01:00
parent cd0f9e8f8d
commit 82b0eda3ef
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
12 changed files with 1671 additions and 7 deletions

1
.gitignore vendored
View File

@ -92,3 +92,4 @@ contrib/auditor-report.log
contrib/auditor-report.tex contrib/auditor-report.tex
contrib/auditor-report.pdf contrib/auditor-report.pdf
src/bank-lib/taler-bank-transfer src/bank-lib/taler-bank-transfer
src/exchange-lib/test_exchange_api_new

View File

@ -7,12 +7,12 @@ if USE_COVERAGE
endif endif
lib_LTLIBRARIES = \ lib_LTLIBRARIES = \
libtalerexchange.la libtalerexchange.la \
libtalertesting.la
libtalerexchange_la_LDFLAGS = \ libtalerexchange_la_LDFLAGS = \
-version-info 4:0:0 \ -version-info 4:0:0 \
-no-undefined -no-undefined
libtalerexchange_la_SOURCES = \ libtalerexchange_la_SOURCES = \
exchange_api_common.c \ exchange_api_common.c \
exchange_api_handle.c exchange_api_handle.h \ exchange_api_handle.c exchange_api_handle.h \
@ -25,7 +25,6 @@ libtalerexchange_la_SOURCES = \
exchange_api_track_transaction.c \ exchange_api_track_transaction.c \
exchange_api_track_transfer.c \ exchange_api_track_transfer.c \
exchange_api_wire.c exchange_api_wire.c
libtalerexchange_la_LIBADD = \ libtalerexchange_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/util/libtalerutil.la \
@ -35,6 +34,26 @@ libtalerexchange_la_LIBADD = \
-ljansson \ -ljansson \
$(XLIB) $(XLIB)
libtalertesting_la_LDFLAGS = \
-version-info 0:0:0 \
-no-undefined
libtalertesting_la_SOURCES = \
testing_api_loop.c \
testing_api_traits.c \
testing_api_cmd_fakebank_transfer.c \
testing_api_trait_reserve_priv.c \
testing_api_trait_coin_priv.c
libtalertesting_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetcurl \
-lgnunetjson \
-lgnunetutil \
-ljansson \
$(XLIB)
if HAVE_LIBCURL if HAVE_LIBCURL
libtalerexchange_la_LIBADD += -lcurl libtalerexchange_la_LIBADD += -lcurl
else else
@ -45,12 +64,14 @@ endif
check_PROGRAMS = \ check_PROGRAMS = \
test_exchange_api \ test_exchange_api \
test_exchange_api_keys_cherry_picking test_exchange_api_keys_cherry_picking \
test_exchange_api_new
AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH; AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
TESTS = \ TESTS = \
$(check_PROGRAMS) test_exchange_api \
test_exchange_api_keys_cherry_picking
test_exchange_api_SOURCES = \ test_exchange_api_SOURCES = \
test_exchange_api.c test_exchange_api.c
@ -65,6 +86,20 @@ test_exchange_api_LDADD = \
-lgnunetutil \ -lgnunetutil \
-ljansson -ljansson
test_exchange_api_new_SOURCES = \
test_exchange_api_new.c
test_exchange_api_new_LDADD = \
libtalertesting.la \
libtalerexchange.la \
$(LIBGCRYPT_LIBS) \
$(top_builddir)/src/bank-lib/libtalerfakebank.la \
$(top_builddir)/src/bank-lib/libtalerbank.la \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetcurl \
-lgnunetutil \
-ljansson
test_exchange_api_keys_cherry_picking_SOURCES = \ test_exchange_api_keys_cherry_picking_SOURCES = \
test_exchange_api_keys_cherry_picking.c test_exchange_api_keys_cherry_picking.c
test_exchange_api_keys_cherry_picking_LDADD = \ test_exchange_api_keys_cherry_picking_LDADD = \

View File

@ -3612,7 +3612,7 @@ run (void *cls)
.details.refund.fee = "EUR:0.01", .details.refund.fee = "EUR:0.01",
.details.refund.deposit_ref = "deposit-refund-1b", .details.refund.deposit_ref = "deposit-refund-1b",
}, },
/* Run transfers. This will do the transfer as refund deadline /* Run transfers. This will do the transfer as refund deadline
was 0, except of course because the refund succeeded, the was 0, except of course because the refund succeeded, the
transfer should no longer be done. */ transfer should no longer be done. */
{ .oc = OC_RUN_AGGREGATOR, { .oc = OC_RUN_AGGREGATOR,
@ -3621,7 +3621,7 @@ run (void *cls)
{ .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
.label = "check-refund-fast-not-run" }, .label = "check-refund-fast-not-run" },
/* ************** End of refund API testing************* */ /* ************** End of refund API testing************* */
/* ************** Test /payback API ************* */ /* ************** Test /payback API ************* */

View File

@ -0,0 +1,332 @@
/*
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 exchange/test_exchange_api_new.c
* @brief testcase to test exchange's HTTP API interface
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
* @author Christian Grothoff
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_signatures.h"
#include "taler_exchange_service.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_util_lib.h>
#include <microhttpd.h>
#include "taler_bank_service.h"
#include "taler_fakebank_lib.h"
#include "taler_testing_lib.h"
/**
* Is the configuration file is set to include wire format 'test'?
*/
#define WIRE_TEST 1
/**
* Is the configuration file is set to include wire format 'sepa'?
*/
#define WIRE_SEPA 1
/**
* Account number of the exchange at the bank.
*/
#define EXCHANGE_ACCOUNT_NO 2
/**
* Handle to access the exchange.
*/
// static struct TALER_EXCHANGE_Handle *exchange;
/**
* Pipe used to communicate child death via signal.
*/
static struct GNUNET_DISK_PipeHandle *sigpipe;
/**
* Handle to the exchange process.
*/
static struct GNUNET_OS_Process *exchanged;
/**
* Handle to our fakebank.
*/
static struct TALER_FAKEBANK_Handle *fakebank;
/**
* Function run when the test terminates (good or bad).
* Cleans up our state.
*
* @param cls NULL
*/
static void
do_shutdown (void *cls)
{
if (NULL != fakebank)
{
TALER_FAKEBANK_stop (fakebank);
fakebank = NULL;
}
}
/**
* Main function that will tell the interpreter what to do.
*
* @param cls closure
*/
static void
run (void *cls,
struct TALER_TESTING_Interpreter *is)
{
struct TALER_TESTING_Command commands[] = {
TALER_TESTING_cmd_fakebank_transfer ("create-reserve-1",
"EUR:5.01",
42,
EXCHANGE_ACCOUNT_NO,
"user42",
"pass42"),
TALER_TESTING_cmd_end ()
};
fakebank = TALER_FAKEBANK_start (8082);
TALER_TESTING_run (is,
commands);
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
NULL);
}
/**
* Signal handler called for SIGCHLD. Triggers the
* respective handler by writing to the trigger pipe.
*/
static void
sighandler_child_death ()
{
static char c;
int old_errno = errno; /* back-up errno */
GNUNET_break (1 ==
GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
(sigpipe, GNUNET_DISK_PIPE_END_WRITE),
&c, sizeof (c)));
errno = old_errno; /* restore errno */
}
/**
* Remove files from previous runs
*/
static void
cleanup_files ()
{
struct GNUNET_CONFIGURATION_Handle *cfg;
char *dir;
cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK !=
GNUNET_CONFIGURATION_load (cfg,
"test_exchange_api.conf"))
{
GNUNET_break (0);
GNUNET_CONFIGURATION_destroy (cfg);
exit (77);
}
GNUNET_assert (GNUNET_OK ==
GNUNET_CONFIGURATION_get_value_filename (cfg,
"exchange",
"keydir",
&dir));
if (GNUNET_YES ==
GNUNET_DISK_directory_test (dir,
GNUNET_NO))
GNUNET_break (GNUNET_OK ==
GNUNET_DISK_directory_remove (dir));
GNUNET_free (dir);
GNUNET_CONFIGURATION_destroy (cfg);
}
int
main (int argc,
char * const *argv)
{
struct GNUNET_OS_Process *proc;
struct GNUNET_SIGNAL_Context *shc_chld;
enum GNUNET_OS_ProcessStatusType type;
unsigned long code;
unsigned int iter;
int result;
/* These might get in the way... */
unsetenv ("XDG_DATA_HOME");
unsetenv ("XDG_CONFIG_HOME");
GNUNET_log_setup ("test-exchange-api",
"INFO",
NULL);
if (GNUNET_OK !=
GNUNET_NETWORK_test_port_free (IPPROTO_TCP,
8081))
{
fprintf (stderr,
"Required port %u not available, skipping.\n",
8081);
return 77;
}
if (GNUNET_OK !=
GNUNET_NETWORK_test_port_free (IPPROTO_TCP,
8082))
{
fprintf (stderr,
"Required port %u not available, skipping.\n",
8082);
return 77;
}
cleanup_files ();
proc = GNUNET_OS_start_process (GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-keyup",
"taler-exchange-keyup",
"-c", "test_exchange_api.conf",
"-o", "auditor.in",
NULL);
if (NULL == proc)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to run `taler-exchange-keyup`, is your PATH correct?\n");
return 77;
}
GNUNET_OS_process_wait (proc);
GNUNET_OS_process_destroy (proc);
proc = GNUNET_OS_start_process (GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-auditor-sign",
"taler-auditor-sign",
"-c", "test_exchange_api.conf",
"-u", "http://auditor/",
"-m", "98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG",
"-r", "auditor.in",
"-o", "test_exchange_api_home/.local/share/taler/auditors/auditor.out",
NULL);
if (NULL == proc)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to run `taler-exchange-keyup`, is your PATH correct?\n");
return 77;
}
GNUNET_OS_process_wait (proc);
GNUNET_OS_process_destroy (proc);
proc = GNUNET_OS_start_process (GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-dbinit",
"taler-exchange-dbinit",
"-c", "test_exchange_api.conf",
"-r",
NULL);
if (NULL == proc)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to run `taler-exchange-dbinit`, is your PATH correct?\n");
return 77;
}
if (GNUNET_SYSERR ==
GNUNET_OS_process_wait_status (proc,
&type,
&code))
{
GNUNET_break (0);
GNUNET_OS_process_destroy (proc);
return 1;
}
GNUNET_OS_process_destroy (proc);
if ( (type == GNUNET_OS_PROCESS_EXITED) &&
(0 != code) )
{
fprintf (stderr,
"Failed to setup database\n");
return 77;
}
if ( (type != GNUNET_OS_PROCESS_EXITED) ||
(0 != code) )
{
fprintf (stderr,
"Unexpected error running `taler-exchange-dbinit'!\n");
return 1;
}
exchanged = GNUNET_OS_start_process (GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-httpd",
"taler-exchange-httpd",
"-c", "test_exchange_api.conf",
"-i",
NULL);
/* give child time to start and bind against the socket */
fprintf (stderr,
"Waiting for `taler-exchange-httpd' to be ready");
iter = 0;
do
{
if (10 == iter)
{
fprintf (stderr,
"Failed to launch `taler-exchange-httpd' (or `wget')\n");
GNUNET_OS_process_kill (exchanged,
SIGTERM);
GNUNET_OS_process_wait (exchanged);
GNUNET_OS_process_destroy (exchanged);
return 77;
}
fprintf (stderr, ".");
sleep (1);
iter++;
}
while (0 != system ("wget -q -t 1 -T 1 http://127.0.0.1:8081/keys -o /dev/null -O /dev/null"));
fprintf (stderr, "\n");
sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO,
GNUNET_NO, GNUNET_NO);
GNUNET_assert (NULL != sigpipe);
shc_chld = GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD,
&sighandler_child_death);
result = TALER_TESTING_setup (&run, NULL);
GNUNET_SIGNAL_handler_uninstall (shc_chld);
shc_chld = NULL;
GNUNET_DISK_pipe_close (sigpipe);
GNUNET_break (0 ==
GNUNET_OS_process_kill (exchanged,
SIGTERM));
GNUNET_break (GNUNET_OK ==
GNUNET_OS_process_wait (exchanged));
GNUNET_OS_process_destroy (exchanged);
return (GNUNET_OK == result) ? 0 : 1;
}

View File

@ -0,0 +1,311 @@
/*
This file is part of TALER
Copyright (C) 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 exchange-lib/testing_api_cmd_fakebank_transfer.c
* @brief implementation of a fakebank wire transfer command
* @author Christian Grothoff
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_bank_service.h"
#include "taler_fakebank_lib.h"
#include "taler_signatures.h"
#include "taler_testing_lib.h"
/**
*
*/
struct FakebankTransferState
{
/**
* Label to another admin_add_incoming command if we
* should deposit into an existing reserve, NULL if
* a fresh reserve should be created.
*/
const char *reserve_reference;
/**
* String describing the amount to add to the reserve.
*/
struct TALER_Amount amount;
/**
* Wire transfer subject. NULL to use public key corresponding
* to @e reserve_priv or @e reserve_reference. Should only be
* set manually to test invalid wire transfer subjects.
*/
const char *subject;
/**
* Sender (debit) account number.
*/
uint64_t debit_account_no;
/**
* Receiver (credit) account number.
*/
uint64_t credit_account_no;
/**
* Username to use for authentication.
*/
const char *auth_username;
/**
* Password to use for authentication.
*/
const char *auth_password;
/**
* Set (by the interpreter) to the reserve's private key
* we used to fill the reserve.
*/
struct TALER_ReservePrivateKeyP reserve_priv;
/**
* Set to the API's handle during the operation.
*/
struct TALER_BANK_AdminAddIncomingHandle *aih;
/**
* Interpreter state while command is running.
*/
struct TALER_TESTING_Interpreter *is;
/**
* Set to the wire transfer's unique ID.
*/
uint64_t serial_id;
};
/**
* Function called upon completion of our /admin/add/incoming request.
*
* @param cls closure with the interpreter state
* @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 serial_id unique ID of the wire transfer
* @param full_response full response from the exchange (for logging, in case of errors)
*/
static void
add_incoming_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
uint64_t serial_id,
const json_t *full_response)
{
struct FakebankTransferState *fts = cls;
struct TALER_TESTING_Interpreter *is = fts->is;
fts->aih = NULL;
fts->serial_id = serial_id;
if (MHD_HTTP_OK != http_status)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
TALER_TESTING_interpreter_next (is);
}
/**
* Runs the command. Note that upon return, the interpreter
* will not automatically run the next command, as the command
* may continue asynchronously in other scheduler tasks. Thus,
* the command must ensure to eventually call
* #TALER_TESTING_interpreter_next() or
* #TALER_TESTING_interpreter_fail().
*
* @param is interpreter state
*/
static void
fakebank_transfer_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct FakebankTransferState *fts = cls;
char *subject;
struct TALER_BANK_AuthenticationData auth;
struct TALER_ReservePublicKeyP reserve_pub;
if (NULL != fts->subject)
{
subject = GNUNET_strdup (fts->subject);
}
else
{
/* Use reserve public key as subject */
#if FIXME_NEEDS_TRAIT
if (NULL != fts->reserve_reference)
{
ref = find_command (is,
fts->reserve_reference);
GNUNET_assert (NULL != ref);
GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
// FIXME: needs trait...
fts->reserve_priv = ref->details.admin_add_incoming.reserve_priv;
}
else
{
#endif
struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
priv = GNUNET_CRYPTO_eddsa_key_create ();
fts->reserve_priv.eddsa_priv = *priv;
GNUNET_free (priv);
#if FIXME_NEEDS_TRAIT
}
#endif
GNUNET_CRYPTO_eddsa_key_get_public (&fts->reserve_priv.eddsa_priv,
&reserve_pub.eddsa_pub);
subject = GNUNET_STRINGS_data_to_string_alloc (&reserve_pub,
sizeof (reserve_pub));
}
auth.method = TALER_BANK_AUTH_BASIC;
auth.details.basic.username = (char *) fts->auth_username;
auth.details.basic.password = (char *) fts->auth_password;
fts->is = is;
fts->aih
= TALER_BANK_admin_add_incoming (TALER_TESTING_interpreter_get_context (is),
"http://localhost:8082/", /* bank URL: FIXME */
&auth,
"https://exchange.com/", /* exchange URL: FIXME */
subject,
&fts->amount,
fts->debit_account_no,
fts->credit_account_no,
&add_incoming_cb,
fts);
GNUNET_free (subject);
if (NULL == fts->aih)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
}
/**
* Clean up after the command. Run during forced termination
* (CTRL-C) or test failure or test success.
*
* @param cls closure
*/
static void
fakebank_transfer_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct FakebankTransferState *fts = cls;
if (NULL != fts->aih)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %s did not complete\n",
cmd->label);
TALER_BANK_admin_add_incoming_cancel (fts->aih);
}
GNUNET_free (fts);
}
/**
* Extract information from a command that is useful for other
* commands.
*
* @param cls closure
* @param ret[out] result (could be anything)
* @param trait name of the trait
* @param selector more detailed information about which object
* to return in case there were multiple generated
* by the command
* @return #GNUNET_OK on success
*/
static int
fakebank_transfer_traits (void *cls,
void **ret,
const char *trait,
const char *selector)
{
struct FakebankTransferState *fts = cls;
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_reserve_priv (NULL,
&fts->reserve_priv),
TALER_TESTING_trait_end ()
};
if (NULL != fts->subject)
{
GNUNET_break (0);
return GNUNET_SYSERR; /* we do NOT create a reserve private key */
}
return TALER_TESTING_get_trait (traits,
ret,
trait,
selector);
}
/**
* Create fakebank_transfer command.
*
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_fakebank_transfer (const char *label,
const char *amount,
uint64_t debit_account_no,
uint64_t credit_account_no,
const char *auth_username,
const char *auth_password)
{
struct TALER_TESTING_Command cmd;
struct FakebankTransferState *fts;
fts = GNUNET_new (struct FakebankTransferState);
fts->credit_account_no = credit_account_no;
fts->debit_account_no = debit_account_no;
fts->auth_username = auth_username;
fts->auth_password = auth_password;
if (GNUNET_OK !=
TALER_string_to_amount (amount,
&fts->amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse amount `%s' at %s\n",
amount,
label);
GNUNET_assert (0);
}
cmd.cls = fts;
cmd.label = label;
cmd.run = &fakebank_transfer_run;
cmd.cleanup = &fakebank_transfer_cleanup;
cmd.traits = &fakebank_transfer_traits;
return cmd;
}
/* end of testing_api_cmd_fakebank_transfer.c */

View File

@ -0,0 +1,193 @@
/*
This file is part of TALER
Copyright (C) 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 exchange-lib/testing_api_cmd_withdraw.c
* @brief main interpreter loop for testcases
* @author Christian Grothoff
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_signatures.h"
struct WithdrawState
{
/**
* Which reserve should we withdraw from?
*/
const char *reserve_reference;
/**
* String describing the denomination value we should withdraw.
* A corresponding denomination key must exist in the exchange's
* offerings. Can be NULL if @e pk is set instead.
*/
struct TALER_Amount amount;
/**
* If @e amount is NULL, this specifies the denomination key to
* use. Otherwise, this will be set (by the interpreter) to the
* denomination PK matching @e amount.
*/
const struct TALER_EXCHANGE_DenomPublicKey *pk;
/**
* Set (by the interpreter) to the exchange's signature over the
* coin's public key.
*/
struct TALER_DenominationSignature sig;
/**
* Private key material of the coin, set by the interpreter.
*/
struct TALER_PlanchetSecretsP ps;
/**
* Withdraw handle (while operation is running).
*/
struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh;
};
/**
* Runs the command. Note that upon return, the interpreter
* will not automatically run the next command, as the command
* may continue asynchronously in other scheduler tasks. Thus,
* the command must ensure to eventually call
* #TALER_TESTING_interpreter_next() or
* #TALER_TESTING_interpreter_fail().
*
* @param i interpreter state
*/
static void
withdraw_run (void *cls,
struct TALER_TESTING_Interpreter *i)
{
struct WithdrawState *ws = cls;
struct TALER_ReservePrivateKeyP rp;
struct TALER_TESTING_Command *create_reserve;
create_reserve
= TALER_TESTING_interpreter_lookup_command (i,
ws->reserve_reference);
if (NULL == create_reserve)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (i);
return;
}
if (GNUNET_OK !=
TALER_TESTING_get_trait_reserve_priv (create_reserve,
NULL,
&rp))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (i);
return;
}
// ... trigger withdraw
// ... eventually: TALER_TESTING_interpreter_next (i);
}
/**
* Clean up after the command. Run during forced termination
* (CTRL-C) or test failure or test success.
*
* @param cls closure
*/
static void
withdraw_cleanup (void *cls)
{
struct WithdrawState *ws = cls;
}
/**
* Extract information from a command that is useful for other
* commands.
*
* @param cls closure
* @param ret[out] result (could be anything)
* @param trait name of the trait
* @param selector more detailed information about which object
* to return in case there were multiple generated
* by the command
* @return #GNUNET_OK on success
*/
static int
withdraw_traits (void *cls,
void **ret,
const char *trait,
const char *selector)
{
struct WithdrawState *ws = cls;
struct TALER_INTERPRETER_Trait traits[] = {
TALER_TESTING_make_trait_coin_priv (NULL /* only one coin */,
&ws->ps.coin_priv),
#if 0
TALER_TESTING_make_trait_coin_blinding_key (NULL /* only one coin */,
&ws->ps.blinding_key),
TALER_TESTING_make_trait_coin_denomination_key (NULL /* only one coin */,
ws->pk),
TALER_TESTING_make_trait_coin_denomination_sig (NULL /* only one coin */,
&ws->sig),
#endif
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait (traits,
ret,
trait,
selector);
}
/**
* Create withdraw command.
*
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_withdraw_amount (const char *label,
const char *reserve_reference,
const char *amount)
{
struct TALER_TESTING_Command cmd;
struct WithdrawState *ws;
ws = GNUNET_new (struct WithdrawState);
ws->reserve_reference = reserve_reference;
// convert amount -> ws->amount;
cmd.cls = ws;
cmd.label = label;
cmd.run = &withdraw_run;
cmd.cleanup = &withdraw_cleanup;
cmd.traits = &withdraw_traits;
return cmd;
}
/* end of testing_api_cmd_withdraw.c */

View File

@ -0,0 +1,327 @@
/*
This file is part of TALER
Copyright (C) 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 exchange-lib/testing_api_loop.c
* @brief main interpreter loop for testcases
* @author Christian Grothoff
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "taler_testing_lib.h"
/**
* Global state of the interpreter, used by a command
* to access information about other commands.
*/
struct TALER_TESTING_Interpreter
{
/**
* Commands the interpreter will run.
*/
struct TALER_TESTING_Command *commands;
/**
* Interpreter task (if one is scheduled).
*/
struct GNUNET_SCHEDULER_Task *task;
/**
* Main execution context for the main loop.
*/
struct GNUNET_CURL_Context *ctx;
/**
* Context for running the CURL event loop.
*/
struct GNUNET_CURL_RescheduleContext *rc;
/**
* Task run on timeout.
*/
struct GNUNET_SCHEDULER_Task *timeout_task;
/**
* Instruction pointer. Tells #interpreter_run() which
* instruction to run next.
*/
unsigned int ip;
/**
* Result of the testcases, #GNUNET_OK on success
*/
int result;
/**
* Pipe used to communicate child death via signal.
*/
struct GNUNET_DISK_PipeHandle *sigpipe;
};
/**
* Lookup command by label.
*
* @param is interpreter state to search
* @param label label to look for
* @return NULL if command was not found
*/
const struct TALER_TESTING_Command *
TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is,
const char *label)
{
const struct TALER_TESTING_Command *cmd;
if (NULL == label)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Attempt to lookup command for empty label\n");
return NULL;
}
for (unsigned int i=0;NULL != (cmd = &is->commands[i])->label;i++)
if ( (NULL != cmd->label) &&
(0 == strcmp (cmd->label,
label)) )
return cmd;
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command not found: %s\n",
label);
return NULL;
}
/**
* Obtain main execution context for the main loop.
*/
struct GNUNET_CURL_Context *
TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *i)
{
return i->ctx;
}
/**
* Run the main interpreter loop that performs exchange operations.
*
* @param cls contains the `struct InterpreterState`
*/
static void
interpreter_run (void *cls);
/**
* Current command is done, run the next one.
*/
void
TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *i)
{
if (GNUNET_SYSERR == i->result)
return; /* ignore, we already failed! */
i->ip++;
i->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
i);
}
/**
* Current command failed, clean up and fail the test case.
*/
void
TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *i)
{
i->result = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
}
/**
* Create command array terminator.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_end (void)
{
static struct TALER_TESTING_Command cmd;
return cmd;
}
/**
* Run the main interpreter loop that performs exchange operations.
*
* @param cls contains the `struct TALER_TESTING_Interpreter`
*/
static void
interpreter_run (void *cls)
{
struct TALER_TESTING_Interpreter *is = cls;
struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
is->task = NULL;
if (NULL == cmd->label)
{
is->result = GNUNET_OK;
GNUNET_SCHEDULER_shutdown ();
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Running command `%s'\n",
cmd->label);
cmd->run (cmd->cls,
cmd,
is);
}
/**
* Function run when the test terminates (good or bad).
* Cleans up our state.
*
* @param cls the interpreter state.
*/
static void
do_shutdown (void *cls)
{
struct TALER_TESTING_Interpreter *is = cls;
struct TALER_TESTING_Command *cmd;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Executing shutdown at `%s'\n",
is->commands[is->ip].label);
for (unsigned int j=0;NULL != (cmd = &is->commands[j])->label;j++)
cmd->cleanup (cmd->cls,
cmd);
if (NULL != is->task)
{
GNUNET_SCHEDULER_cancel (is->task);
is->task = NULL;
}
if (NULL != is->ctx)
{
GNUNET_CURL_fini (is->ctx);
is->ctx = NULL;
}
if (NULL != is->rc)
{
GNUNET_CURL_gnunet_rc_destroy (is->rc);
is->rc = NULL;
}
if (NULL != is->timeout_task)
{
GNUNET_SCHEDULER_cancel (is->timeout_task);
is->timeout_task = NULL;
}
GNUNET_free_non_null (is->commands);
}
/**
* Function run when the test terminates (good or bad) with timeout.
*
* @param cls NULL
*/
static void
do_timeout (void *cls)
{
struct TALER_TESTING_Interpreter *is = cls;
is->timeout_task = NULL;
GNUNET_SCHEDULER_shutdown ();
}
void
TALER_TESTING_run (struct TALER_TESTING_Interpreter *is,
struct TALER_TESTING_Command *commands)
{
unsigned int i;
for (i=0;NULL != commands[i].label;i++) ;
is->commands = GNUNET_new_array (i + 1,
struct TALER_TESTING_Command);
memcpy (is->commands,
commands,
sizeof (struct TALER_TESTING_Command) * i);
is->timeout_task
= GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
(GNUNET_TIME_UNIT_SECONDS, 300),
&do_timeout,
is);
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
is);
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
}
struct MainContext
{
TALER_TESTING_Main main_cb;
void *main_cb_cls;
struct TALER_TESTING_Interpreter *is;
};
static void
main_wrapper (void *cls)
{
struct MainContext *main_ctx = cls;
struct TALER_TESTING_Interpreter *is = main_ctx->is;
is->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
&is->rc);
GNUNET_assert (NULL != is->ctx);
is->rc = GNUNET_CURL_gnunet_rc_create (is->ctx);
main_ctx->main_cb (main_ctx->main_cb_cls,
main_ctx->is);
}
/**
* Initialize scheduler loop and curl context for the testcase.
*/
int
TALER_TESTING_setup (TALER_TESTING_Main main_cb,
void *main_cb_cls)
{
struct TALER_TESTING_Interpreter is;
struct MainContext main_ctx = {
.main_cb = main_cb,
.main_cb_cls = main_cb_cls,
.is = &is
};
memset (&is,
0,
sizeof (is));
GNUNET_SCHEDULER_run (&main_wrapper,
&main_ctx);
return is.result;
}
/* end of testing_api_loop.c */

View File

@ -0,0 +1,67 @@
/*
This file is part of TALER
Copyright (C) 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 exchange-lib/testing_api_traits.c
* @brief main interpreter loop for testcases
* @author Christian Grothoff
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "taler_testing_lib.h"
#define TALER_TESTING_TRAIT_COIN_PRIVATE_KEY "coin-private-key"
/**
* Obtain a coin private key from a @a cmd.
*
* @param cmd command to extract trait from
* @param selector which coin to pick if @a cmd has multiple on offer
* @param coin_priv[out] set to the private key of the coin
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_coin_priv (const struct TALER_TESTING_Command *cmd,
const char *selector,
struct TALER_CoinSpendPrivateKeyP **coin_priv)
{
return cmd->traits (cmd->cls,
(void **) coin_priv,
TALER_TESTING_TRAIT_COIN_PRIVATE_KEY,
selector);
}
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_coin_priv (const char *selector,
const struct TALER_CoinSpendPrivateKeyP *coin_priv)
{
struct TALER_TESTING_Trait ret = {
.selector = selector,
.trait_name = TALER_TESTING_TRAIT_COIN_PRIVATE_KEY,
.ptr = (const void *) coin_priv
};
return ret;
}
/* end of testing_api_trait_coin_priv.c */

View File

@ -0,0 +1,65 @@
/*
This file is part of TALER
Copyright (C) 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 exchange-lib/testing_api_trait_reserve_priv.c
* @brief implements reserve private key trait
* @author Christian Grothoff
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "taler_testing_lib.h"
#define TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY "reserve-private-key"
/**
* Obtain a reserve private key from a @a cmd.
*
* @param cmd command to extract trait from
* @param selector which coin to pick if @a cmd has multiple on offer
* @param reserve_priv[out] set to the private key of the reserve
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_reserve_priv (const struct TALER_TESTING_Command *cmd,
const char *selector,
struct TALER_ReservePrivateKeyP **reserve_priv)
{
return cmd->traits (cmd->cls,
(void **) reserve_priv,
TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY,
selector);
}
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_reserve_priv (const char *selector,
const struct TALER_ReservePrivateKeyP *reserve_priv)
{
struct TALER_TESTING_Trait ret = {
.selector = selector,
.trait_name = TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY,
.ptr = (const void *) reserve_priv
};
return ret;
}
/* end of testing_api_trait_reserve_priv.c */

View File

@ -0,0 +1,70 @@
/*
This file is part of TALER
Copyright (C) 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 exchange-lib/testing_api_traits.c
* @brief loop for trait resolution
* @author Christian Grothoff
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "taler_testing_lib.h"
struct TALER_TESTING_Trait
TALER_TESTING_trait_end ()
{
struct TALER_TESTING_Trait end = {
.selector = NULL,
.trait_name = NULL,
.ptr = NULL
};
return end;
}
int
TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
void **ret,
const char *trait,
const char *selector)
{
for (unsigned int i=0;
NULL != traits[i].trait_name;
i++)
{
if ( (0 == strcmp (trait,
traits[i].trait_name)) &&
( (NULL == selector) ||
(0 == strcasecmp (selector,
traits[i].selector) ) ) )
{
*ret = (void *) traits[i].ptr;
return GNUNET_OK;
}
}
/* FIXME: log */
return GNUNET_SYSERR;
}
/* end of testing_api_traits.c */

View File

@ -23,6 +23,7 @@ talerinclude_HEADERS = \
taler_exchangedb_plugin.h \ taler_exchangedb_plugin.h \
taler_fakebank_lib.h \ taler_fakebank_lib.h \
taler_json_lib.h \ taler_json_lib.h \
taler_testing_lib.h \
taler_util.h \ taler_util.h \
taler_pq_lib.h \ taler_pq_lib.h \
taler_signatures.h \ taler_signatures.h \

View File

@ -0,0 +1,262 @@
/*
This file is part of TALER
(C) 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 include/taler_testing_lib.h
* @brief API for writing an interpreter to test Taler components
* @author Christian Grothoff <christian@grothoff.org>
* @author Marcello Stanisci
*/
#ifndef TALER_TESTING_LIB_H
#define TALER_TESTING_LIB_H
#include "taler_util.h"
#include <gnunet/gnunet_json_lib.h>
#include "taler_json_lib.h"
#include <microhttpd.h>
/* ******************* Generic interpreter logic ****************** */
/**
* Global state of the interpreter, used by a command
* to access information about other commands.
*/
struct TALER_TESTING_Interpreter;
/**
* A command to be run by the interpreter.
*/
struct TALER_TESTING_Command
{
/**
* Closure for all commands with command-specific context
* information.
*/
void *cls;
/**
* Label for the command.
*/
const char *label;
/**
* Runs the command. Note that upon return, the interpreter
* will not automatically run the next command, as the command
* may continue asynchronously in other scheduler tasks. Thus,
* the command must ensure to eventually call
* #TALER_TESTING_interpreter_next() or
* #TALER_TESTING_interpreter_fail().
*
* @param i interpreter state
*/
void
(*run)(void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *i);
/**
* Clean up after the command. Run during forced termination
* (CTRL-C) or test failure or test success.
*
* @param cls closure
*/
void
(*cleanup)(void *cls,
const struct TALER_TESTING_Command *cmd);
/**
* Extract information from a command that is useful for other
* commands.
*
* @param cls closure
* @param ret[out] result (could be anything)
* @param trait name of the trait
* @param selector more detailed information about which object
* to return in case there were multiple generated
* by the command
* @return #GNUNET_OK on success
*/
int
(*traits)(void *cls,
void **ret,
const char *trait,
const char *selector);
};
/**
* Lookup command by label.
*/
const struct TALER_TESTING_Command *
TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *i,
const char *label);
/**
* Obtain main execution context for the main loop.
*/
struct GNUNET_CURL_Context *
TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is);
/**
* Current command is done, run the next one.
*/
void
TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *is);
/**
* Current command failed, clean up and fail the test case.
*/
void
TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is);
/**
* Create command array terminator.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_end (void);
void
TALER_TESTING_run (struct TALER_TESTING_Interpreter *is,
struct TALER_TESTING_Command *commands);
typedef void
(*TALER_TESTING_Main)(void *cls,
struct TALER_TESTING_Interpreter *is);
/**
* Initialize scheduler loop and curl context for the testcase.
*/
int
TALER_TESTING_setup (TALER_TESTING_Main main_cb,
void *main_cb_cls);
/* ****************** Specific interpreter commands **************** */
/**
* Perform a wire transfer (formerly Admin-add-incoming)
*
* @return NULL on failure
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_fakebank_transfer (const char *label,
const char *amount,
uint64_t debit_account_no,
uint64_t credit_account_no,
const char *auth_username,
const char *auth_password);
/**
* Create withdraw command.
*
* @return NULL on failure
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_withdraw_amount (const char *label,
struct TALER_EXCHANGE_Handle *exchange,
const char *reserve_reference,
const char *amount,
unsigned int expected_response_code);
/* ********************** Generic trait logic for implementing traits ******************* */
/**
* A trait.
*/
struct TALER_TESTING_Trait
{
const char *selector;
const char *trait_name;
const void *ptr;
};
struct TALER_TESTING_Trait
TALER_TESTING_trait_end (void);
int
TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
void **ret,
const char *trait,
const char *selector);
/* ****************** Specific traits supported by this component *************** */
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_reserve_priv (const char *selector,
const struct TALER_ReservePrivateKeyP *reserve_priv);
/**
* Obtain a reserve private key from a @a cmd.
*
* @param cmd command to extract trait from
* @param selector which coin to pick if @a cmd has multiple on offer
* @param reserve_priv[out] set to the private key of the reserve
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_reserve_priv (const struct TALER_TESTING_Command *cmd,
const char *selector,
struct TALER_ReservePrivateKeyP **reserve_priv);
/**
* @param selector
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_coin_priv (const char *selector,
const struct TALER_CoinSpendPrivateKeyP *coin_priv);
/**
* Obtain a coin private key from a @a cmd.
*
* @param cmd command to extract trait from
* @param selector which coin to pick if @a cmd has multiple on offer
* @param coin_priv[out] set to the private key of the coin
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_coin_priv (const struct TALER_TESTING_Command *cmd,
const char *selector,
struct TALER_CoinSpendPrivateKeyP **coin_priv);
#endif