From b593d416d6e788b2053c2f5ebb634e0bb39fe560 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Thu, 22 Feb 2018 14:51:12 +0100 Subject: [PATCH] Bank-lib tests, using the new (libraries-based) style. --- .gitignore | 3 + src/bank-lib/Makefile.am | 43 +- src/bank-lib/bank.conf | 6 + src/bank-lib/test_bank_api_new.c | 185 +++++ .../test_bank_api_with_fakebank_new.c | 223 ++++++ src/bank-lib/testing_api_cmd_history.c | 735 ++++++++++++++++++ src/bank-lib/testing_api_cmd_reject.c | 164 ++++ src/bank-lib/testing_api_helpers.c | 205 +++++ src/exchange-lib/testing_api_cmd_bank_check.c | 108 ++- .../testing_api_cmd_fakebank_transfer.c | 19 +- src/exchange-lib/testing_api_cmd_track.c | 2 +- src/exchange-lib/testing_api_helpers.c | 5 +- src/exchange-lib/testing_api_loop.c | 49 +- src/exchange-lib/testing_api_trait_amount.c | 2 +- src/exchange-lib/testing_api_trait_number.c | 49 +- src/exchange-lib/testing_api_trait_string.c | 50 +- src/include/Makefile.am | 3 +- src/include/taler_testing_bank_lib.h | 108 +++ src/include/taler_testing_lib.h | 108 ++- 19 files changed, 2008 insertions(+), 59 deletions(-) create mode 100644 src/bank-lib/test_bank_api_new.c create mode 100644 src/bank-lib/test_bank_api_with_fakebank_new.c create mode 100644 src/bank-lib/testing_api_cmd_history.c create mode 100644 src/bank-lib/testing_api_cmd_reject.c create mode 100644 src/bank-lib/testing_api_helpers.c create mode 100644 src/include/taler_testing_bank_lib.h diff --git a/.gitignore b/.gitignore index 0ec39faa4..2a1ce723a 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,9 @@ doc/doxygen/doxygen_sqlite3.db src/auditor/taler-auditor src/auditor/taler-auditor-sign src/bank-lib/test_bank_api +src/bank-lib/test_bank_api_new src/bank-lib/test_bank_api_with_fakebank +src/bank-lib/test_bank_api_with_fakebank_new src/exchange-lib/test_exchange_api_new src/exchange-lib/test_exchange_api src/exchange-lib/test_exchange_api_home/.local/share/taler/exchange/live-keys/ @@ -65,6 +67,7 @@ src/util/test_crypto src/util/test_json src/util/test_wireformats src/util/taler-arm +src/util/test_url doc/paper/llncs.cls doc/paper/taler.bbl doc/paper/taler.blg diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am index 967d95ed3..bd1c7e8b1 100644 --- a/src/bank-lib/Makefile.am +++ b/src/bank-lib/Makefile.am @@ -21,7 +21,8 @@ taler_bank_transfer_LDADD = \ lib_LTLIBRARIES = \ libtalerbank.la \ - libtalerfakebank.la + libtalerfakebank.la \ + libtalerbanktesting.la libtalerbank_la_LDFLAGS = \ -version-info 1:0:0 \ @@ -41,7 +42,6 @@ libtalerbank_la_LIBADD = \ -ljansson \ $(XLIB) - libtalerfakebank_la_LDFLAGS = \ -version-info 0:0:0 \ -no-undefined @@ -57,6 +57,22 @@ libtalerfakebank_la_LIBADD = \ -lmicrohttpd \ $(XLIB) +libtalerbanktesting_la_LDFLAGS = \ + -version-info 0:0:0 \ + -no-undefined + +libtalerbanktesting_la_SOURCES = \ + testing_api_cmd_history.c \ + testing_api_cmd_reject.c \ + testing_api_helpers.c + +libtalerbanktesting_la_LIBADD = \ + $(top_builddir)/src/json/libtalerjson.la \ + -lgnunetjson \ + -lgnunetutil \ + -ljansson \ + -lmicrohttpd \ + $(XLIB) if HAVE_LIBCURL libtalerbank_la_LIBADD += -lcurl @@ -68,7 +84,9 @@ endif check_PROGRAMS = \ test_bank_api \ - test_bank_api_with_fakebank + test_bank_api_new \ + test_bank_api_with_fakebank \ + test_bank_api_with_fakebank_new TESTS = \ $(check_PROGRAMS) @@ -84,6 +102,15 @@ test_bank_api_LDADD = \ -lgnunetutil \ -ljansson +test_bank_api_new_SOURCES = \ + test_bank_api_new.c + +test_bank_api_new_LDADD = \ + $(top_builddir)/src/exchange-lib/libtalertesting.la \ + libtalerbanktesting.la \ + -ltalerexchange \ + -lgnunetutil \ + libtalerbank.la test_bank_api_with_fakebank_SOURCES = \ test_bank_interpreter.c test_bank_interpreter.h \ @@ -96,5 +123,15 @@ test_bank_api_with_fakebank_LDADD = \ -lgnunetutil \ -ljansson +test_bank_api_with_fakebank_new_SOURCES = \ + test_bank_api_with_fakebank_new.c + +test_bank_api_with_fakebank_new_LDADD = \ + $(top_builddir)/src/exchange-lib/libtalertesting.la \ + libtalerbanktesting.la \ + -ltalerexchange \ + -lgnunetutil \ + libtalerbank.la + EXTRA_DIST = \ bank.conf diff --git a/src/bank-lib/bank.conf b/src/bank-lib/bank.conf index 0610606cc..f6e4e7fe0 100644 --- a/src/bank-lib/bank.conf +++ b/src/bank-lib/bank.conf @@ -1,2 +1,8 @@ [taler] currency = KUDOS + +[bank] +http_port = 8081 + +[exchange-wire-test] +bank_url = http://localhost:8081/ diff --git a/src/bank-lib/test_bank_api_new.c b/src/bank-lib/test_bank_api_new.c new file mode 100644 index 000000000..a43aa876f --- /dev/null +++ b/src/bank-lib/test_bank_api_new.c @@ -0,0 +1,185 @@ +/* + This file is part of TALER + Copyright (C) 2016, 2017 GNUnet e.V. + + 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 bank/test_bank_api_new.c + * @brief testcase to test bank's HTTP API + * interface against the "real" bank + * @author Christian Grothoff + * @author Marcello Stanisci + */ + +#include "platform.h" +#include "taler_util.h" +#include "taler_signatures.h" +#include "taler_bank_service.h" +#include +#include +#include +#include "taler_exchange_service.h" +#include "test_bank_interpreter.h" +#include "taler_testing_lib.h" +#include "taler_testing_bank_lib.h" + +#define CONFIG_FILE "bank.conf" + +/** + * Bank process. + */ +struct GNUNET_OS_Process *bankd; + +/** + * Bank URL. + */ +char *bank_url; + +/** + * Main function that will tell the interpreter what commands to + * run. + * + * @param cls closure + */ +static void +run (void *cls, + struct TALER_TESTING_Interpreter *is) +{ + + extern struct TALER_BANK_AuthenticationData AUTHS[]; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Bank serves at `%s'\n", + bank_url); + + struct TALER_TESTING_Command commands[] = { + + /** + * NOTE: this command uses internally the _fakebank_ version + * of the add-incoming command. However, this does seem to + * work fine against the Python bank too! Some renaming is + * required.. + */ + TALER_TESTING_cmd_bank_history ("history-0", + bank_url, + EXCHANGE_ACCOUNT_NUMBER, + TALER_BANK_DIRECTION_BOTH, + NULL, + 5), + + /* WARNING: old API has expected http response code among + * the parameters, although it was always set as '200 OK' */ + TALER_TESTING_cmd_fakebank_transfer_with_subject + ("deposit-1", + "KUDOS:5.01", + bank_url, + BANK_ACCOUNT_NUMBER, + EXCHANGE_ACCOUNT_NUMBER, + AUTHS[BANK_ACCOUNT_NUMBER -1].details.basic.username, + AUTHS[BANK_ACCOUNT_NUMBER -1].details.basic.password, + "subject 1", + "http://exchange.com/"), + + TALER_TESTING_cmd_fakebank_transfer_with_subject + ("deposit-2", + "KUDOS:5.01", + bank_url, + BANK_ACCOUNT_NUMBER, + EXCHANGE_ACCOUNT_NUMBER, + AUTHS[BANK_ACCOUNT_NUMBER -1].details.basic.username, + AUTHS[BANK_ACCOUNT_NUMBER -1].details.basic.password, + "subject 2", + "http://exchange.com/"), + + TALER_TESTING_cmd_bank_history ("history-1c", + bank_url, + EXCHANGE_ACCOUNT_NUMBER, + TALER_BANK_DIRECTION_CREDIT, + NULL, + 5), + + TALER_TESTING_cmd_bank_history ("history-1d", + bank_url, + EXCHANGE_ACCOUNT_NUMBER, + TALER_BANK_DIRECTION_DEBIT, + NULL, + 5), + + TALER_TESTING_cmd_bank_history ("history-1dr", + bank_url, + EXCHANGE_ACCOUNT_NUMBER, + TALER_BANK_DIRECTION_DEBIT, + NULL, + -5), + + TALER_TESTING_cmd_bank_history ("history-2fwd", + bank_url, + EXCHANGE_ACCOUNT_NUMBER, + TALER_BANK_DIRECTION_DEBIT, + "deposit-1", + 5), + + TALER_TESTING_cmd_bank_reject ("reject-1", + bank_url, + "deposit-1"), + /** + * End the suite. Fixme: better to have a label for this + * too, as it shows a "(null)" token on logs. + */ + TALER_TESTING_cmd_end () + }; + + TALER_TESTING_run (is, commands); +} + + +/* Pacifies "make check" */ +int +main(int argc, + char * const *argv) +{ + unsigned int ret; + /* These environment variables get in the way... */ + unsetenv ("XDG_DATA_HOME"); + unsetenv ("XDG_CONFIG_HOME"); + GNUNET_log_setup ("test-bank-api-new", "DEBUG", NULL); + + if (NULL == + (bank_url = TALER_TESTING_prepare_bank (CONFIG_FILE))) + return 77; + + if (NULL == (bankd = + TALER_TESTING_run_bank (CONFIG_FILE))) + return 1; + + ret = TALER_TESTING_setup (&run, + NULL, + CONFIG_FILE, + NULL); // means no exchange. + + GNUNET_OS_process_kill (bankd, SIGKILL); + GNUNET_OS_process_wait (bankd); + GNUNET_OS_process_destroy (bankd); + GNUNET_free (bank_url); + + if (GNUNET_OK == ret) + return 0; + + return 1; +} + +/* end of test_bank_api_new.c */ diff --git a/src/bank-lib/test_bank_api_with_fakebank_new.c b/src/bank-lib/test_bank_api_with_fakebank_new.c new file mode 100644 index 000000000..a36251c5e --- /dev/null +++ b/src/bank-lib/test_bank_api_with_fakebank_new.c @@ -0,0 +1,223 @@ +/* + This file is part of TALER + Copyright (C) 2016, 2017 GNUnet e.V. + + 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 bank/test_bank_api_with_fakebank.c + * @brief testcase to test bank's HTTP API + * interface against the fakebank + * @author Marcello Stanisci + * @author Christian Grothoff + */ + +#include "platform.h" +#include "taler_util.h" +#include "taler_signatures.h" +#include "taler_bank_service.h" +#include "taler_exchange_service.h" +#include +#include +#include +#include "test_bank_interpreter.h" +#include "taler_testing_lib.h" +#include "taler_testing_bank_lib.h" + + +#define CONFIG_FILE "bank.conf" + + +/** + * Fakebank URL. + */ +char *fakebank_url; + +/** + * Main function that will tell the interpreter what commands to + * run. + * + * @param cls closure + */ +static void +run (void *cls, + struct TALER_TESTING_Interpreter *is) +{ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Fakebank serves at `%s'\n", + fakebank_url); + extern struct TALER_BANK_AuthenticationData AUTHS[]; + + struct TALER_TESTING_Command commands[] = { + + /** + * NOTE: this command uses internally the _fakebank_ version + * of the add-incoming command. However, this does seem to + * work fine against the Python bank too! Some renaming is + * required.. + */ + TALER_TESTING_cmd_bank_history ("history-0", + fakebank_url, + BANK_ACCOUNT_NUMBER, + TALER_BANK_DIRECTION_BOTH, + NULL, + 1), + + + /* WARNING: old API has expected http response code among + * the parameters, although it was always set as '200 OK' */ + TALER_TESTING_cmd_fakebank_transfer_with_subject + ("debit-1", + "KUDOS:5.01", + fakebank_url, + EXCHANGE_ACCOUNT_NUMBER, + BANK_ACCOUNT_NUMBER, + AUTHS[EXCHANGE_ACCOUNT_NUMBER -1].details.basic.username, + AUTHS[EXCHANGE_ACCOUNT_NUMBER -1].details.basic.password, + "subject 1", + "http://exchange.com/"), + + TALER_TESTING_cmd_bank_history ("history-1c", + fakebank_url, + BANK_ACCOUNT_NUMBER, + TALER_BANK_DIRECTION_CREDIT, + NULL, + 5), + + TALER_TESTING_cmd_bank_history ("history-1d", + fakebank_url, + BANK_ACCOUNT_NUMBER, + TALER_BANK_DIRECTION_DEBIT, + NULL, + 5), + + TALER_TESTING_cmd_fakebank_transfer_with_subject + ("debit-2", + "KUDOS:3.21", + fakebank_url, + EXCHANGE_ACCOUNT_NUMBER, // debit account. + USER_ACCOUNT_NUMBER, + AUTHS[EXCHANGE_ACCOUNT_NUMBER -1].details.basic.username, + AUTHS[EXCHANGE_ACCOUNT_NUMBER -1].details.basic.password, + "subject 2", + "http://exchange.org/"), + + TALER_TESTING_cmd_fakebank_transfer_with_subject + ("credit-2", + "KUDOS:3.22", + fakebank_url, + USER_ACCOUNT_NUMBER, // debit account. + EXCHANGE_ACCOUNT_NUMBER, + AUTHS[USER_ACCOUNT_NUMBER -1].details.basic.username, + AUTHS[USER_ACCOUNT_NUMBER -1].details.basic.password, + "credit 2", + "http://exchange.org/"), + + TALER_TESTING_cmd_bank_history ("history-2b", + fakebank_url, + EXCHANGE_ACCOUNT_NUMBER, + TALER_BANK_DIRECTION_BOTH, + NULL, + 5), + + TALER_TESTING_cmd_bank_history ("history-2bi", + fakebank_url, + EXCHANGE_ACCOUNT_NUMBER, + TALER_BANK_DIRECTION_BOTH, + "debit-1", + 5), + + TALER_TESTING_cmd_check_bank_transfer_with_ref ("expect-2d", + "credit-2"), + + TALER_TESTING_cmd_check_bank_transfer_with_ref ("expect-2c", + "debit-2"), + + TALER_TESTING_cmd_check_bank_transfer_with_ref ("expect-1", + "debit-1"), + + TALER_TESTING_cmd_check_bank_empty ("expect-empty"), + + TALER_TESTING_cmd_fakebank_transfer_with_subject + ("credit-for-reject-1", + "KUDOS:5.01", + fakebank_url, + BANK_ACCOUNT_NUMBER, + EXCHANGE_ACCOUNT_NUMBER, + AUTHS[BANK_ACCOUNT_NUMBER -1].details.basic.username, + AUTHS[BANK_ACCOUNT_NUMBER -1].details.basic.password, + "subject 3", + "http://exchange.net/"), + + TALER_TESTING_cmd_bank_reject ("reject-1", + fakebank_url, + "credit-for-reject-1"), + + TALER_TESTING_cmd_bank_history ("history-r1", + fakebank_url, + BANK_ACCOUNT_NUMBER, + TALER_BANK_DIRECTION_BOTH, + NULL, + 5), + + TALER_TESTING_cmd_bank_history ("history-r1c", + fakebank_url, + BANK_ACCOUNT_NUMBER, + TALER_BANK_DIRECTION_BOTH | + TALER_BANK_DIRECTION_CANCEL, + NULL, + 5), + + TALER_TESTING_cmd_check_bank_transfer_with_ref + ("expect-credit-reject-1", + "credit-for-reject-1"), + + TALER_TESTING_cmd_check_bank_empty ("expect-empty-2"), + + /** + * End the suite. Fixme: better to have a label for this + * too, as it shows a "(null)" token on logs. + */ + TALER_TESTING_cmd_end () + }; + + TALER_TESTING_run_with_fakebank (is, + commands, + fakebank_url); +} + +int +main (int argc, + char * const *argv) +{ + /* These environment variables get in the way... */ + unsetenv ("XDG_DATA_HOME"); + unsetenv ("XDG_CONFIG_HOME"); + GNUNET_log_setup ("test-merchant-api-with-fakebank-new", + "DEBUG", + NULL); + if (NULL == + (fakebank_url = TALER_TESTING_prepare_fakebank (CONFIG_FILE))) + return 77; + + return (GNUNET_OK == TALER_TESTING_setup (&run, + NULL, + CONFIG_FILE, + NULL)) ? 0 : 1; +} + + +/* end of test_bank_api_with_fakebank_new.c */ diff --git a/src/bank-lib/testing_api_cmd_history.c b/src/bank-lib/testing_api_cmd_history.c new file mode 100644 index 000000000..311c910d0 --- /dev/null +++ b/src/bank-lib/testing_api_cmd_history.c @@ -0,0 +1,735 @@ +/* + 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 + +*/ + +/** + * @file bank-lib/testing_api_cmd_history.c + * @brief command to check the /history API from the bank. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_exchange_service.h" +#include "taler_testing_lib.h" +#include "taler_testing_bank_lib.h" +#include "taler_fakebank_lib.h" +#include "taler_bank_service.h" +#include "taler_fakebank_lib.h" + + +struct HistoryState +{ + const char *bank_url; + + uint64_t account_no; + + enum TALER_BANK_Direction direction; + + const char *start_row_reference; + + unsigned int num_results; + + struct TALER_BANK_HistoryHandle *hh; + + uint64_t results_obtained; + + int failed; +}; + +/** + * Item in the transaction history, as reconstructed from the + * command history. + */ +struct History +{ + + /** + * Wire details. + */ + struct TALER_BANK_TransferDetails details; + + /** + * Serial ID of the wire transfer. + */ + uint64_t row_id; + + /** + * Direction of the transfer. + */ + enum TALER_BANK_Direction direction; + +}; + +extern struct TALER_BANK_AuthenticationData AUTHS[]; + +/** + * @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 +history_traits (void *cls, + void **ret, + const char *trait, + unsigned int index) +{ + /* Must define this function because some callbacks + * look for certain traits on _all_ the commands. */ + return GNUNET_SYSERR; +} + +/** + * Test if the /admin/add/incoming transaction at offset @a off + * has been /rejected. + * + * @param is interpreter state (where we are right now) + * @param off offset of the command to test for rejection + * @return #GNUNET_YES if the command at @a off was cancelled + */ +static int +test_cancelled (struct TALER_TESTING_Interpreter *is, + unsigned int off) +{ + const char *rejected_reference; + + for (unsigned int i=0;iip;i++) + { + const struct TALER_TESTING_Command *c = &is->commands[i]; + + + #warning "Errors reported here are NOT fatal" + /* We use the exposure of a reference to a reject + * command as a signal to understand if the current + * command was cancelled; so errors about "reject traits" + * not found are NOT fatal here */ + + if (GNUNET_OK != TALER_TESTING_get_trait_rejected + (c, 0, &rejected_reference)) + continue; + if (0 == strcmp (rejected_reference, + TALER_TESTING_interpreter_get_current_label + (is))) + return GNUNET_YES; + } + return GNUNET_NO; +} + +/** + * Free history @a h of length @a h_len. + * + * @param h history array to free + * @param h_len number of entries in @a h + */ +static void +free_history (struct History *h, + uint64_t h_len) +{ + for (uint64_t off = 0;offcommands[is->ip].cls; + uint64_t total; + struct History *h; + const struct TALER_TESTING_Command *add_incoming_cmd; + int inc; + unsigned int start; + unsigned int end; + int ok; + const uint64_t *row_id_start = NULL; + + if (NULL != hs->start_row_reference) + { + TALER_LOG_INFO ("`%s': start row given via reference `%s'\n", + TALER_TESTING_interpreter_get_current_label + (is), + hs->start_row_reference); + add_incoming_cmd = TALER_TESTING_interpreter_lookup_command + (is, hs->start_row_reference); + GNUNET_assert (NULL != add_incoming_cmd); + GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_uint64 + (add_incoming_cmd, 0, &row_id_start)); + } + + GNUNET_assert (0 != hs->num_results); + if (0 == is->ip) + { + *rh = NULL; + return 0; + } + if (hs->num_results > 0) + { + inc = 1; + start = 0; + end = is->ip - 1; + } + else + { + inc = -1; + start = is->ip - 1; + end = 0; + } + + total = 0; + ok = GNUNET_NO; + + if (NULL == row_id_start) + ok = GNUNET_YES; + + for (unsigned int off = start;off != end + inc; off += inc) + { + const struct TALER_TESTING_Command *pos = &is->commands[off]; + int cancelled; + const uint64_t *row_id; + + /** + * Skip non-add_incoming commands, choose upon "do they + * offer row_id trait?". + */ + + if (GNUNET_OK != TALER_TESTING_get_trait_uint64 + (pos, 0, &row_id)) + continue; + + if (NULL != row_id_start) + { + if (*row_id_start == *row_id) + { + /* Doesn't count, start is excluded from output. */ + total = 0; + ok = GNUNET_YES; + continue; + } + } + if (GNUNET_NO == ok) + continue; /* skip until we find the marker */ + if (total >= hs->num_results * inc) + break; /* hit limit specified by command */ + + cancelled = test_cancelled (is, off); + + if ( (GNUNET_YES == cancelled) && + (0 == (hs->direction & TALER_BANK_DIRECTION_CANCEL)) ) + { + TALER_LOG_INFO ("Ignoring canceled wire" + " transfer from history\n"); + continue; + } + + const uint64_t *credit_account_no; + const uint64_t *debit_account_no; + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT + (pos, &credit_account_no)); + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT + (pos, &debit_account_no)); + + TALER_LOG_INFO ("Potential history element:" + " %llu->%llu; my account: %llu\n", + *debit_account_no, + *credit_account_no, + hs->account_no); + + if ( ( (0 != (hs->direction & TALER_BANK_DIRECTION_CREDIT)) && + (hs->account_no == *credit_account_no)) || + ( (0 != (hs->direction & TALER_BANK_DIRECTION_DEBIT)) && + (hs->account_no == *debit_account_no)) ) + { + TALER_LOG_INFO ("+1 my history\n"); + total++; /* found matching record */ + } + } + GNUNET_assert (GNUNET_YES == ok); + if (0 == total) + { + *rh = NULL; + return 0; + } + GNUNET_assert (total < UINT_MAX); + h = GNUNET_new_array ((unsigned int) total, + struct History); + total = 0; + ok = GNUNET_NO; + if (NULL == row_id_start) + ok = GNUNET_YES; + for (unsigned int off = start;off != end + inc; off += inc) + { + const struct TALER_TESTING_Command *pos = &is->commands[off]; + int cancelled; + const uint64_t *row_id; + + if (GNUNET_OK != TALER_TESTING_GET_TRAIT_ROW_ID + (pos, &row_id)) + continue; + + if (NULL != row_id_start) + { + + if (*row_id_start == *row_id) + { + /* Doesn't count, start is excluded from output. */ + total = 0; + ok = GNUNET_YES; + continue; + } + } + if (GNUNET_NO == ok) + { + TALER_LOG_INFO ("Skip on `%s'\n", + pos->label); + continue; /* skip until we find the marker */ + } + + if (total >= hs->num_results * inc) + { + TALER_LOG_INFO ("hit limit specified by command\n"); + break; + } + const uint64_t *credit_account_no; + const uint64_t *debit_account_no; + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT + (pos, &credit_account_no)); + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT + (pos, &debit_account_no)); + + TALER_LOG_INFO ("Potential history bit:" + " %llu->%llu; my account: %llu\n", + *debit_account_no, + *credit_account_no, + hs->account_no); + + if ( ( (0 != (hs->direction & TALER_BANK_DIRECTION_CREDIT)) && + (hs->account_no == *credit_account_no)) && + ( (0 != (hs->direction & TALER_BANK_DIRECTION_DEBIT)) && + (hs->account_no == *debit_account_no)) ) + { + GNUNET_break (0); + continue; + } + + cancelled = test_cancelled (is, off); + if ( (GNUNET_YES == cancelled) && + (0 == (hs->direction & TALER_BANK_DIRECTION_CANCEL)) ) + { + TALER_LOG_WARNING ("`%s' was cancelled\n", + TALER_TESTING_interpreter_get_current_label + (is)); + continue; + } + + if ( (0 != (hs->direction & TALER_BANK_DIRECTION_CREDIT)) && + (hs->account_no == *credit_account_no)) + { + h[total].direction = TALER_BANK_DIRECTION_CREDIT; + if (GNUNET_YES == cancelled) + h[total].direction |= TALER_BANK_DIRECTION_CANCEL; + h[total].details.account_details + = json_pack ("{s:s, s:s, s:I}", + "type", + "test", + "bank_url", + hs->bank_url, + "account_number", + (json_int_t) *debit_account_no); + GNUNET_assert (NULL != h[total].details.account_details); + } + if ( (0 != (hs->direction & TALER_BANK_DIRECTION_DEBIT)) && + (hs->account_no == *debit_account_no)) + { + h[total].direction = TALER_BANK_DIRECTION_DEBIT; + if (GNUNET_YES == cancelled) + h[total].direction |= TALER_BANK_DIRECTION_CANCEL; + h[total].details.account_details + = json_pack ("{s:s, s:s, s:I}", + "type", + "test", + "bank_url", + hs->bank_url, + "account_number", + (json_int_t) *credit_account_no); + GNUNET_assert (NULL != h[total].details.account_details); + } + if ( ( (0 != (hs->direction & TALER_BANK_DIRECTION_CREDIT)) && + (hs->account_no == *credit_account_no)) || + ( (0 != (hs->direction & TALER_BANK_DIRECTION_DEBIT)) && + (hs->account_no == *debit_account_no)) ) + { + const struct TALER_Amount *amount; + const char *subject; + const char *exchange_url; + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_get_trait_amount_obj + (pos, 0, &amount)); + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_get_trait_transfer_subject + (pos, 0, &subject)); + + GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_url + (pos, 0, &exchange_url)); + + h[total].details.amount = *amount; + + h[total].row_id = *row_id; + GNUNET_asprintf (&h[total].details.wire_transfer_subject, + "%s %s", + subject, + exchange_url); + TALER_LOG_INFO ("+1-bit of my history\n"); + total++; + } + } + *rh = h; + return total; +} + + +/** + * Compute how many results we expect to be returned for + * the history command at @a is. + * + * @param is the interpreter state to inspect + * @return number of results expected + */ +static uint64_t +compute_result_count (struct TALER_TESTING_Interpreter *is) +{ + uint64_t total; + struct History *h; + + total = build_history (is, &h); + free_history (h, total); + return total; +} + +/** + * Check that @a dir and @a details are the transaction + * results we expect at offset @a off in the history of + * the current command executed by @a is + * + * @param is the interpreter state we are in + * @param off the offset of the result + * @param dir the direction of the transaction + * @param details the transaction details to check + * @return #GNUNET_OK if the transaction is what we expect + */ +static int +check_result (struct TALER_TESTING_Interpreter *is, + unsigned int off, + enum TALER_BANK_Direction dir, + const struct TALER_BANK_TransferDetails *details) +{ + uint64_t total; + struct History *h; + + total = build_history (is, &h); + if (off >= total) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Test says history has at most %u" + " results, but got result #%u to check\n", + (unsigned int) total, + off); + print_expected (h, total, off); + return GNUNET_SYSERR; + } + if (h[off].direction != dir) + { + GNUNET_break (0); + print_expected (h, total, off); + free_history (h, + total); + return GNUNET_SYSERR; + } + + if ( (0 != strcmp (h[off].details.wire_transfer_subject, + details->wire_transfer_subject)) || + (0 != TALER_amount_cmp (&h[off].details.amount, + &details->amount)) || + (1 != json_equal (h[off].details.account_details, + details->account_details)) ) + { + GNUNET_break (0); + print_expected (h, total, off); + free_history (h, + total); + return GNUNET_SYSERR; + } + free_history (h, + total); + return GNUNET_OK; +} + +/** + * Callbacks of this type are used to serve the result of asking + * the bank for the transaction history. + * + * @param cls closure + * @param http_status HTTP response code, #MHD_HTTP_OK (200) + * for successful status request 0 if the bank's reply is + * bogus (fails to follow the protocol), + * #MHD_HTTP_NO_CONTENT if there are no more results; on + * success the last callback is always of this status + * (even if `abs(num_results)` were already returned). + * @param ec taler status code + * @param dir direction of the transfer + * @param row_id monotonically increasing counter corresponding to + * the transaction + * @param details details about the wire transfer + * @param json detailed response from the HTTPD, or NULL if + * reply was not in JSON + */ +static void +history_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + enum TALER_BANK_Direction dir, + uint64_t row_id, + const struct TALER_BANK_TransferDetails *details, + const json_t *json) +{ + struct TALER_TESTING_Interpreter *is = cls; + struct HistoryState *hs = is->commands[is->ip].cls; + + if (MHD_HTTP_OK != http_status) + { + hs->hh = NULL; + if ( (hs->results_obtained != compute_result_count (is)) || + (GNUNET_YES == hs->failed) ) + { + uint64_t total; + struct History *h; + + GNUNET_break (0); + total = build_history (is, &h); + GNUNET_log + (GNUNET_ERROR_TYPE_ERROR, + "Expected history of length %llu, got %llu;" + " HTTP status code: %u, failed: %d\n", + (unsigned long long) total, + (unsigned long long) hs->results_obtained, + http_status, + hs->failed); + print_expected (h, total, UINT_MAX); + free_history (h, total); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_interpreter_next (is); + return; + } + if (GNUNET_OK != check_result (is, + hs->results_obtained, + dir, + details)) + { + GNUNET_break (0); + + { + char *acc; + + acc = json_dumps (json, + JSON_COMPACT); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Result %u was `%s'\n", + (unsigned int) hs->results_obtained, + acc); + if (NULL != acc) + free (acc); + } + + hs->failed = GNUNET_YES; + return; + } + hs->results_obtained++; +} + + +/** + * Run the command. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command to execute, a /wire one. + * @param is the interpreter state. + */ +void +history_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct HistoryState *hs = cls; + uint64_t row_id = UINT64_MAX; + const uint64_t *row_id_ptr = &row_id; + struct TALER_BANK_AuthenticationData *auth; + + /* Get row_id from trait. */ + if (NULL != hs->start_row_reference) + { + const struct TALER_TESTING_Command *history_cmd; + + history_cmd = TALER_TESTING_interpreter_lookup_command + (is, hs->start_row_reference); + + if (NULL == history_cmd) + TALER_TESTING_FAIL (is); + + if (GNUNET_OK != TALER_TESTING_get_trait_uint64 + (history_cmd, 0, &row_id_ptr)) + TALER_TESTING_FAIL (is); + row_id = *row_id_ptr; + + TALER_LOG_DEBUG ("row id (from trait) is %llu\n", row_id); + + } + + auth = &AUTHS[hs->account_no - 1]; + hs->hh = TALER_BANK_history (is->ctx, + hs->bank_url, + auth, + hs->account_no, + hs->direction, + row_id, + hs->num_results, + &history_cb, + is); + GNUNET_assert (NULL != hs->hh); +} + + +/** + * Cleanup the state. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command which is being cleaned up. + */ +void +history_cleanup + (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct HistoryState *hs = cls; + + if (NULL != hs->hh) + { + TALER_LOG_WARNING ("/history did not complete\n"); + TALER_BANK_history_cancel (hs->hh); + } + + GNUNET_free (hs); +} + + +/** + * Test /history API from the bank. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_bank_history + (const char *label, + const char *bank_url, + uint64_t account_no, + enum TALER_BANK_Direction direction, + const char *start_row_reference, + unsigned int num_results) +{ + struct HistoryState *hs; + struct TALER_TESTING_Command cmd; + + hs = GNUNET_new (struct HistoryState); + hs->bank_url = bank_url; + hs->account_no = account_no; + hs->direction = direction; + hs->start_row_reference = start_row_reference; + hs->num_results = num_results; + + cmd.label = label; + cmd.cls = hs; + cmd.run = &history_run; + cmd.cleanup = &history_cleanup; + cmd.traits = &history_traits; + + return cmd; +} + +/* end of testing_api_cmd_history.c */ diff --git a/src/bank-lib/testing_api_cmd_reject.c b/src/bank-lib/testing_api_cmd_reject.c new file mode 100644 index 000000000..638699544 --- /dev/null +++ b/src/bank-lib/testing_api_cmd_reject.c @@ -0,0 +1,164 @@ +/* + 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 + +*/ + +/** + * @file bank-lib/testing_api_cmd_reject.c + * @brief command to check the /reject API from the bank. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include "taler_json_lib.h" +#include +#include "taler_exchange_service.h" +#include "taler_testing_lib.h" +#include "taler_fakebank_lib.h" +#include "taler_bank_service.h" +#include "taler_fakebank_lib.h" + +struct RejectState +{ + struct TALER_BANK_RejectHandle *rh; + + const char *deposit_reference; + + const char *bank_url; +}; + +/** + * Callbacks of this type are used to serve the result + * of asking the bank to reject an incoming wire transfer. + * + * @param cls closure + * @param http_status HTTP response code, #MHD_HTTP_NO_CONTENT + * (204) for successful status request; #MHD_HTTP_NOT_FOUND + * if the rowid is unknown; 0 if the bank's reply is bogus + * (fails to follow the protocol), + * @param ec detailed error code + */ +static void +reject_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec) +{ + struct TALER_TESTING_Interpreter *is = cls; + struct RejectState *rs = is->commands[is->ip].cls; + + rs->rh = NULL; + if (MHD_HTTP_NO_CONTENT != http_status) + { + GNUNET_break (0); + fprintf (stderr, + "Unexpected response code %u:\n", + http_status); + TALER_TESTING_interpreter_fail (is); + return; + } + TALER_TESTING_interpreter_next (is); +} + +/** + * Cleanup the state. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command which is being cleaned up. + */ +void +reject_cleanup + (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct RejectState *rs = cls; + + if (NULL != rs->rh) + { + TALER_LOG_WARNING ("/reject did not complete\n"); + TALER_BANK_reject_cancel (rs->rh); + } + + GNUNET_free (rs); +} + +/** + * Run the command. + * + * @param cls closure, typically a #struct WireState. + * @param cmd the command to execute, a /wire one. + * @param is the interpreter state. + */ +void +reject_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct RejectState *rs = cls; + const struct TALER_TESTING_Command *deposit_cmd; + const uint64_t *credit_account; + const uint64_t *row_id; + extern struct TALER_BANK_AuthenticationData *AUTHS; + + deposit_cmd = TALER_TESTING_interpreter_lookup_command + (is, rs->deposit_reference); + + if (NULL != deposit_cmd) + TALER_TESTING_FAIL (is); + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT + (deposit_cmd, &credit_account)); + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_ROW_ID + (deposit_cmd, &row_id)); + + rs->rh = TALER_BANK_reject (is->ctx, + rs->bank_url, + &AUTHS[*credit_account -1], + *credit_account, + *row_id, + &reject_cb, + is); + GNUNET_assert (NULL != rs->rh); +} + + +/** + * FIXME. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_bank_reject (const char *label, + const char *bank_url, + const char *deposit_reference) +{ + struct RejectState *rs; + struct TALER_TESTING_Command cmd; + + rs = GNUNET_new (struct RejectState); + rs->bank_url = bank_url; + rs->deposit_reference = deposit_reference; + + cmd.cls = rs; + cmd.run = &reject_run; + cmd.cleanup = &reject_cleanup; + + return cmd; + +} + +/* end of testing_api_cmd_reject.c */ diff --git a/src/bank-lib/testing_api_helpers.c b/src/bank-lib/testing_api_helpers.c new file mode 100644 index 000000000..8446f9581 --- /dev/null +++ b/src/bank-lib/testing_api_helpers.c @@ -0,0 +1,205 @@ +/* + 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 + +*/ + +/** + * @file bank-lib/testing_api_helpers.c + * @brief convenience functions for bank-lib tests. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include +#include "taler_testing_bank_lib.h" + +/* Keeps each bank account credential at bank account number - 1 */ +struct TALER_BANK_AuthenticationData AUTHS[] = { + + /* Bank credentials */ + {.method = TALER_BANK_AUTH_BASIC, + .details.basic.username = BANK_USERNAME, + .details.basic.password = BANK_PASSWORD}, + + /* Exchange credentials */ + {.method = TALER_BANK_AUTH_BASIC, + .details.basic.username = EXCHANGE_USERNAME, + .details.basic.password = EXCHANGE_PASSWORD }, + + /* User credentials */ + {.method = TALER_BANK_AUTH_BASIC, + .details.basic.username = USER_USERNAME, + .details.basic.password = USER_PASSWORD } +}; + +/** + * Start the (Python) bank process. Assume the port + * is available and the database is clean. Use the "prepare + * bank" function to do such tasks. + * + * @param config_filename configuration filename. + * + * @return the process, or NULL if the process could not + * be started. + */ +struct GNUNET_OS_Process * +TALER_TESTING_run_bank (const char *config_filename) +{ + + struct GNUNET_OS_Process *bank_proc; + unsigned int iter; + + bank_proc = GNUNET_OS_start_process + (GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-bank-manage", + "taler-bank-manage", + "-c", config_filename, + "--with-db=postgres:///talercheck", + "serve-http", NULL); + if (NULL == bank_proc) + BANK_FAIL (); + + /* give child time to start and bind against the socket */ + fprintf (stderr, + "Waiting for `taler-bank-manage' to be ready"); + iter = 0; + do + { + if (10 == iter) + { + fprintf ( + stderr, + "Failed to launch `taler-bank-manage' (or `wget')\n"); + GNUNET_OS_process_kill (bank_proc, + SIGTERM); + GNUNET_OS_process_wait (bank_proc); + GNUNET_OS_process_destroy (bank_proc); + BANK_FAIL (); + } + fprintf (stderr, "."); + sleep (1); + iter++; + } + /*FIXME: no hardcode port*/ + while (0 != system ("wget -q -t 1 -T 1 http://127.0.0.1:8081/" \ + " -o /dev/null -O /dev/null")); + fprintf (stderr, "\n"); + + return bank_proc; + +} + +/** + * Prepare the bank execution. Check if the port is available + * (and reset database?). + * + * @param config_filename configuration filename. + * + * @return the base url, or NULL upon errors. Must be freed + * by the caller. + */ +char * +TALER_TESTING_prepare_bank (const char *config_filename) +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + unsigned long long port; + struct GNUNET_OS_Process *dbreset_proc; + enum GNUNET_OS_ProcessStatusType type; + unsigned long code; + char *base_url; + + cfg = GNUNET_CONFIGURATION_create (); + + if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, config_filename)) + BANK_FAIL (); + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number + (cfg, "bank", + "HTTP_PORT", &port)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "bank", + "HTTP_PORT"); + GNUNET_CONFIGURATION_destroy (cfg); + BANK_FAIL (); + } + GNUNET_CONFIGURATION_destroy (cfg); + + if (GNUNET_OK != GNUNET_NETWORK_test_port_free + (IPPROTO_TCP, (uint16_t) port)) + { + fprintf (stderr, + "Required port %llu not available, skipping.\n", + port); + BANK_FAIL (); + } + + /* DB preparation */ + if (NULL == + (dbreset_proc = GNUNET_OS_start_process ( + GNUNET_NO, + GNUNET_OS_INHERIT_STD_ALL, + NULL, NULL, NULL, + "taler-bank-manage", + "taler-bank-manage", + "-c", "bank.conf", + "--with-db=postgres:///talercheck", /*FIXME: no hardcoded*/ + "django", + "flush", + "--no-input", NULL))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to flush the bank db.\n"); + BANK_FAIL (); + } + + if (GNUNET_SYSERR == + GNUNET_OS_process_wait_status (dbreset_proc, + &type, + &code)) + { + GNUNET_OS_process_destroy (dbreset_proc); + BANK_FAIL (); + } + if ( (type == GNUNET_OS_PROCESS_EXITED) && + (0 != code) ) + { + fprintf (stderr, + "Failed to setup database\n"); + BANK_FAIL (); + } + if ( (type != GNUNET_OS_PROCESS_EXITED) || + (0 != code) ) + { + fprintf (stderr, + "Unexpected error running" + " `taler-bank-manage django flush..'!\n"); + BANK_FAIL (); + } + + GNUNET_OS_process_destroy (dbreset_proc); + + GNUNET_asprintf (&base_url, + "http://localhost:%llu/", + port); + return base_url; +} + + +/* end of testing_api_helpers.c */ diff --git a/src/exchange-lib/testing_api_cmd_bank_check.c b/src/exchange-lib/testing_api_cmd_bank_check.c index 8c4ee2c44..9af156f92 100644 --- a/src/exchange-lib/testing_api_cmd_bank_check.c +++ b/src/exchange-lib/testing_api_cmd_bank_check.c @@ -47,12 +47,12 @@ struct BankCheckState /** * Expected account number that gave money */ - unsigned int debit_account; + uint64_t debit_account; /** * Expected account number that received money */ - unsigned int credit_account; + uint64_t credit_account; /** * Wire transfer subject (set by fakebank-lib). @@ -69,6 +69,11 @@ struct BankCheckState * Interpreter state. */ struct TALER_TESTING_Interpreter *is; + + /** + * FIXME. + */ + const char *deposit_reference; }; /** @@ -84,26 +89,72 @@ check_bank_transfer_run (void *cls, struct TALER_TESTING_Interpreter *is) { struct BankCheckState *bcs = cls; + struct TALER_Amount amount; + const uint64_t *debit_account; + const uint64_t *credit_account; + const char *exchange_base_url; - if (GNUNET_OK != - TALER_string_to_amount (bcs->amount, - &amount)) + if (NULL == bcs->deposit_reference) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse amount `%s' at %u\n", - bcs->amount, - is->ip); - TALER_TESTING_interpreter_fail (is); - return; + debit_account = &bcs->debit_account; + credit_account = &bcs->credit_account; + exchange_base_url = bcs->exchange_base_url; + + if (GNUNET_OK != + TALER_string_to_amount (bcs->amount, + &amount)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse amount `%s' at %u\n", + bcs->amount, + is->ip); + TALER_TESTING_interpreter_fail (is); + return; + } } + + if (NULL != bcs->deposit_reference) + { + const struct TALER_TESTING_Command *deposit_cmd; + const struct TALER_Amount *amount_ptr; + + TALER_LOG_INFO ("`%s' uses reference (%s)\n", + TALER_TESTING_interpreter_get_current_label + (is), + bcs->deposit_reference); + deposit_cmd = TALER_TESTING_interpreter_lookup_command + (is, bcs->deposit_reference); + + if (NULL == deposit_cmd) + TALER_TESTING_FAIL (is); + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_get_trait_amount_obj + (deposit_cmd, 0, &amount_ptr)); + amount = *amount_ptr; + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT + (deposit_cmd, &debit_account)); + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT + (deposit_cmd, &credit_account)); + + GNUNET_assert + (GNUNET_OK == TALER_TESTING_get_trait_url + (deposit_cmd, 0, &exchange_base_url)); // check 0 works! + + } + if (GNUNET_OK != TALER_FAKEBANK_check (is->fakebank, &amount, - bcs->debit_account, - bcs->credit_account, - bcs->exchange_base_url, + *debit_account, + *credit_account, + exchange_base_url, &bcs->subject)) { GNUNET_break (0); @@ -189,8 +240,8 @@ TALER_TESTING_cmd_check_bank_transfer (const char *label, const char *exchange_base_url, const char *amount, - unsigned int debit_account, - unsigned int credit_account) + uint64_t debit_account, + uint64_t credit_account) { struct BankCheckState *bcs; struct TALER_TESTING_Command cmd; @@ -205,7 +256,6 @@ TALER_TESTING_cmd_check_bank_transfer cmd.cls = bcs; cmd.run = &check_bank_transfer_run; cmd.cleanup = &check_bank_transfer_cleanup; - // traits? cmd.traits = &check_bank_transfer_traits; return cmd; @@ -265,3 +315,27 @@ TALER_TESTING_cmd_check_bank_empty (const char *label) return cmd; } + + +/** + * FIXME. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_check_bank_transfer_with_ref + (const char *label, + const char *deposit_reference) +{ + + struct BankCheckState *bcs; + struct TALER_TESTING_Command cmd; + + bcs = GNUNET_new (struct BankCheckState); + bcs->deposit_reference = deposit_reference; + cmd.label = label; + cmd.cls = bcs; + cmd.run = &check_bank_transfer_run; + cmd.cleanup = &check_bank_transfer_cleanup; + cmd.traits = &check_bank_transfer_traits; + + return cmd; +} diff --git a/src/exchange-lib/testing_api_cmd_fakebank_transfer.c b/src/exchange-lib/testing_api_cmd_fakebank_transfer.c index 7da4bf09c..a8bad860a 100644 --- a/src/exchange-lib/testing_api_cmd_fakebank_transfer.c +++ b/src/exchange-lib/testing_api_cmd_fakebank_transfer.c @@ -31,6 +31,7 @@ #include "taler_fakebank_lib.h" #include "taler_signatures.h" #include "taler_testing_lib.h" +#include "taler_testing_bank_lib.h" /** * @@ -348,17 +349,19 @@ fakebank_transfer_traits (void *cls, { struct FakebankTransferState *fts = cls; struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_reserve_priv (0, - &fts->reserve_priv), + TALER_TESTING_make_trait_reserve_priv + (0, &fts->reserve_priv), + TALER_TESTING_MAKE_TRAIT_DEBIT_ACCOUNT + (&fts->debit_account_no), + TALER_TESTING_MAKE_TRAIT_CREDIT_ACCOUNT + (&fts->credit_account_no), + TALER_TESTING_make_trait_url (0, fts->exchange_url), + TALER_TESTING_make_trait_transfer_subject (0, fts->subject), + TALER_TESTING_MAKE_TRAIT_ROW_ID (&fts->serial_id), + TALER_TESTING_make_trait_amount_obj (0, &fts->amount), TALER_TESTING_trait_end () }; - if (NULL != fts->subject) - { - GNUNET_break (0); - /* we do NOT create a reserve private key */ - return GNUNET_SYSERR; - } return TALER_TESTING_get_trait (traits, ret, trait, diff --git a/src/exchange-lib/testing_api_cmd_track.c b/src/exchange-lib/testing_api_cmd_track.c index 40894872b..8c289ed5d 100644 --- a/src/exchange-lib/testing_api_cmd_track.c +++ b/src/exchange-lib/testing_api_cmd_track.c @@ -198,7 +198,7 @@ deposit_wtid_cb return; } - char *transfer_subject; + const char *transfer_subject; if (GNUNET_OK != TALER_TESTING_get_trait_transfer_subject (bank_transfer_cmd, 0, &transfer_subject)) diff --git a/src/exchange-lib/testing_api_helpers.c b/src/exchange-lib/testing_api_helpers.c index b6e1e9897..690c222c9 100644 --- a/src/exchange-lib/testing_api_helpers.c +++ b/src/exchange-lib/testing_api_helpers.c @@ -431,9 +431,8 @@ TALER_TESTING_prepare_fakebank (const char *config_filename) char *fakebank_url; cfg = GNUNET_CONFIGURATION_create (); - if (GNUNET_OK != - GNUNET_CONFIGURATION_load (cfg, - config_filename)) + if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, + config_filename)) return NULL; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, diff --git a/src/exchange-lib/testing_api_loop.c b/src/exchange-lib/testing_api_loop.c index 2bbf46ac7..78ae3611d 100644 --- a/src/exchange-lib/testing_api_loop.c +++ b/src/exchange-lib/testing_api_loop.c @@ -504,6 +504,22 @@ cert_cb (void *cls, main_ctx->is); } +/** + * Initialize scheduler loop and curl context for the testcase, + * and responsible to run the "run" method. + * + * @param cls closure, typically the "run" method, the + * interpreter state and a closure for "run". + */ +static void +main_wrapper_exchange_agnostic (void *cls) +{ + struct MainContext *main_ctx = cls; + + main_ctx->main_cb (main_ctx->main_cb_cls, + main_ctx->is); +} + /** * Initialize scheduler loop and curl context for the testcase, @@ -513,7 +529,7 @@ cert_cb (void *cls, * interpreter state and a closure for "run". */ static void -main_wrapper (void *cls) +main_wrapper_exchange_connect (void *cls) { struct MainContext *main_ctx = cls; struct TALER_TESTING_Interpreter *is = main_ctx->is; @@ -542,11 +558,6 @@ main_wrapper (void *cls) "http://localhost:%llu/", exchange_port); - 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); - GNUNET_assert ( NULL != (is->exchange = TALER_EXCHANGE_connect (is->ctx, exchange_url, @@ -568,7 +579,8 @@ main_wrapper (void *cls) * @param exchanged exchange process handle: will be put in the * state as some commands - e.g. revoke - need to send * signal to it, for example to let it know to reload the - * key state.. + * key state.. if NULL, the interpreter will run without + * trying to connect to the exchange first. * * @return FIXME: not sure what 'is.result' is at this stage. */ @@ -585,10 +597,7 @@ TALER_TESTING_setup (TALER_TESTING_Main main_cb, /* needed to init the curl ctx */ .is = &is, /* needed to read values like exchange port - * number and construct the exchange url. The - * port number _could_ have been passed here, but - * we prefer to stay "general" as other values might - * need to be passed around in the future. */ + * number to construct the exchange url.*/ .config_filename = config_filename }; struct GNUNET_SIGNAL_Context *shc_chld; @@ -602,16 +611,26 @@ TALER_TESTING_setup (TALER_TESTING_Main main_cb, GNUNET_assert (NULL != sigpipe); shc_chld = GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death); + + 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); + /* Blocking */ - GNUNET_SCHEDULER_run (&main_wrapper, - &main_ctx); + + if (NULL != exchanged) + GNUNET_SCHEDULER_run (&main_wrapper_exchange_connect, + &main_ctx); + else + GNUNET_SCHEDULER_run (&main_wrapper_exchange_agnostic, + &main_ctx); + GNUNET_SIGNAL_handler_uninstall (shc_chld); GNUNET_DISK_pipe_close (sigpipe); sigpipe = NULL; - /*FIXME: ?? */ return is.result; } - /* end of testing_api_loop.c */ diff --git a/src/exchange-lib/testing_api_trait_amount.c b/src/exchange-lib/testing_api_trait_amount.c index a9c5b3bc2..f21ec8704 100644 --- a/src/exchange-lib/testing_api_trait_amount.c +++ b/src/exchange-lib/testing_api_trait_amount.c @@ -45,7 +45,7 @@ int TALER_TESTING_get_trait_amount_obj ( const struct TALER_TESTING_Command *cmd, unsigned int index, - struct TALER_Amount **amount) + const struct TALER_Amount **amount) { return cmd->traits (cmd->cls, (void **) amount, diff --git a/src/exchange-lib/testing_api_trait_number.c b/src/exchange-lib/testing_api_trait_number.c index 8f011dcae..4db887927 100644 --- a/src/exchange-lib/testing_api_trait_number.c +++ b/src/exchange-lib/testing_api_trait_number.c @@ -29,7 +29,8 @@ #include "taler_signatures.h" #include "taler_testing_lib.h" -#define TALER_TESTING_TRAIT_NUMBER "number" +#define TALER_TESTING_TRAIT_UINT "uint" +#define TALER_TESTING_TRAIT_UINT64 "uint-64" /** * Obtain a "number" value from @a cmd. @@ -48,7 +49,7 @@ TALER_TESTING_get_trait_uint { return cmd->traits (cmd->cls, (void **) n, - TALER_TESTING_TRAIT_NUMBER, + TALER_TESTING_TRAIT_UINT, index); } @@ -65,10 +66,52 @@ TALER_TESTING_make_trait_uint { struct TALER_TESTING_Trait ret = { .index = index, - .trait_name = TALER_TESTING_TRAIT_NUMBER, + .trait_name = TALER_TESTING_TRAIT_UINT, .ptr = (const void *) n }; return ret; } +/** + * Obtain a "number" value from @a cmd. + * + * @param cmd command to extract trait from + * @param selector which coin to pick if @a cmd has multiple on + * offer + * @param n[out] set to the number coming from @a cmd. + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_get_trait_uint64 + (const struct TALER_TESTING_Command *cmd, + unsigned int index, + const uint64_t **n) +{ + return cmd->traits (cmd->cls, + (void **) n, + TALER_TESTING_TRAIT_UINT64, + index); +} + +/** + * @param selector associate the object with this "tag" + * @param n which object should be returned + * + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_uint64 + (unsigned int index, + const uint64_t *n) +{ + struct TALER_TESTING_Trait ret = { + .index = index, + .trait_name = TALER_TESTING_TRAIT_UINT64, + .ptr = (const void *) n + }; + return ret; +} + + + /* end of testing_api_trait_number.c */ diff --git a/src/exchange-lib/testing_api_trait_string.c b/src/exchange-lib/testing_api_trait_string.c index 0675d5a88..1b1b42219 100644 --- a/src/exchange-lib/testing_api_trait_string.c +++ b/src/exchange-lib/testing_api_trait_string.c @@ -36,6 +36,7 @@ #define TALER_TESTING_TRAIT_AMOUNT "amount" #define TALER_TESTING_TRAIT_URL "url" #define TALER_TESTING_TRAIT_ORDER_ID "order-id" +#define TALER_TESTING_TRAIT_REJECTED "rejected" /** * Obtain contract terms from @a cmd. @@ -135,7 +136,7 @@ int TALER_TESTING_get_trait_transfer_subject (const struct TALER_TESTING_Command *cmd, unsigned int index, - char **transfer_subject) + const char **transfer_subject) { return cmd->traits (cmd->cls, (void **) transfer_subject, @@ -154,7 +155,7 @@ TALER_TESTING_get_trait_transfer_subject struct TALER_TESTING_Trait TALER_TESTING_make_trait_transfer_subject (unsigned int index, - char *transfer_subject) + const char *transfer_subject) { struct TALER_TESTING_Trait ret = { .index = index, @@ -293,5 +294,50 @@ TALER_TESTING_make_trait_order_id return ret; } +/** + * Obtain the reference from a bank transfer which has + * been rejected. + * + * @param cmd command to extract trait from + * @param index which reference is to be picked, in case + * multiple are offered. + * @param rejected_reference[out] where to write the order id. + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_get_trait_rejected + (const struct TALER_TESTING_Command *cmd, + unsigned int index, + const char **rejected_reference) +{ + return cmd->traits (cmd->cls, + (void **) rejected_reference, + TALER_TESTING_TRAIT_REJECTED, + index); +} + +/** + * Offer reference to a bank transfer which has been + * rejected. + * + * @param index which reference is to be picked, in case + * multiple are offered. + * @param rejected_reference the url to offer + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_rejected + (unsigned int index, + const char *rejected) +{ + struct TALER_TESTING_Trait ret = { + .index = index, + .trait_name = TALER_TESTING_TRAIT_REJECTED, + .ptr = (const void *) rejected + }; + return ret; +} + + /* end of testing_api_trait_string.c */ diff --git a/src/include/Makefile.am b/src/include/Makefile.am index 6583ad524..2916a812c 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -28,7 +28,8 @@ talerinclude_HEADERS = \ taler_pq_lib.h \ taler_signatures.h \ taler_wire_lib.h \ - taler_wire_plugin.h + taler_wire_plugin.h \ + taler_testing_bank_lib.h endif diff --git a/src/include/taler_testing_bank_lib.h b/src/include/taler_testing_bank_lib.h new file mode 100644 index 000000000..87187a733 --- /dev/null +++ b/src/include/taler_testing_bank_lib.h @@ -0,0 +1,108 @@ +/* + 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 + +*/ + +/** + * @file include/taler_testing_bank_lib.h + * @brief API for writing an interpreter to test Taler components + * @author Marcello Stanisci + */ +#ifndef TALER_TESTING_BANK_LIB_H +#define TALER_TESTING_BANK_LIB_H + +#include "taler_util.h" +#include +#include "taler_json_lib.h" +#include +#include "taler_bank_service.h" +#include "taler_testing_lib.h" + + +/* ******** Credentials to log in at the bank ******* */ + +#define BANK_ACCOUNT_NUMBER 1 +#define BANK_USERNAME "Bank" +#define BANK_PASSWORD "x" +#define EXCHANGE_ACCOUNT_NUMBER 2 +#define EXCHANGE_USERNAME "Exchange" +#define EXCHANGE_PASSWORD "x" +#define USER_ACCOUNT_NUMBER 3 +#define USER_USERNAME "user3" +#define USER_PASSWORD "pass3" + + +/* ********************* Helper functions ********************* */ + +#define BANK_FAIL() \ + do {GNUNET_break (0); return NULL; } while (0) + +/** + * Start the (Python) bank process. Assume the port + * is available and the database is clean. Use the "prepare + * bank" function to do such tasks. + * + * @param config_filename configuration filename. + * + * @return the process, or NULL if the process could not + * be started. + */ +struct GNUNET_OS_Process * +TALER_TESTING_run_bank (const char *config_filename); + +/** + * Prepare the bank execution. Check if the port is available + * (and reset database?). + * + * @param config_filename configuration filename. + * + * @return the base url, or NULL upon errors. Must be freed + * by the caller. + */ +char * +TALER_TESTING_prepare_bank (const char *config_filename); + + +/* ******************* Generic interpreter logic ************ */ + +/* ************** Specific interpreter commands ************ */ + +/** + * Test /history API from the bank. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_bank_history + (const char *label, + const char *bank_url, + uint64_t account_no, + enum TALER_BANK_Direction direction, + const char *start_row_reference, + unsigned int num_results); + +/** + * FIXME. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_bank_reject (const char *label, + const char *bank_url, + const char *deposit_reference); + +/* *** Generic trait logic for implementing traits ********* */ + +/* ****** Specific traits supported by this component ******* */ + +#endif diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h index b4350e0c3..d06105dd1 100644 --- a/src/include/taler_testing_lib.h +++ b/src/include/taler_testing_lib.h @@ -27,6 +27,7 @@ #define TALER_TESTING_LIB_H #include "taler_util.h" +#include "taler_exchange_service.h" #include #include "taler_json_lib.h" #include @@ -34,6 +35,37 @@ /* ********************* Helper functions ********************* */ +/** + * Print failing line number and trigger shutdown. Useful + * quite any time after the command "run" method has been called. + */ +#define TALER_TESTING_FAIL(is) \ + do \ + {\ + GNUNET_break (0); \ + TALER_TESTING_interpreter_fail (is); \ + return; \ + } while (0) + + +#define TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT(cmd,out) \ + TALER_TESTING_get_trait_uint64 (cmd, 0, out) + +#define TALER_TESTING_MAKE_TRAIT_CREDIT_ACCOUNT(data) \ + TALER_TESTING_make_trait_uint64 (0, data) + +#define TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT(cmd,out) \ + TALER_TESTING_get_trait_uint64 (cmd, 1, out) + +#define TALER_TESTING_MAKE_TRAIT_DEBIT_ACCOUNT(data) \ + TALER_TESTING_make_trait_uint64 (1, data) + +#define TALER_TESTING_GET_TRAIT_ROW_ID(cmd,out) \ + TALER_TESTING_get_trait_uint64 (cmd, 3, out) + +#define TALER_TESTING_MAKE_TRAIT_ROW_ID(data) \ + TALER_TESTING_make_trait_uint64 (3, data) + /** * Allocate and return a piece of wire-details. Mostly, it adds * the bank_url to the JSON. @@ -747,8 +779,16 @@ TALER_TESTING_cmd_check_bank_transfer (const char *label, const char *exchange_base_url, const char *amount, - unsigned int debit_account, - unsigned int credit_account); + uint64_t debit_account, + uint64_t credit_account); + +/** + * FIXME. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_check_bank_transfer_with_ref + (const char *label, + const char *deposit_reference); /** * Check bank's balance is zero. @@ -1017,6 +1057,33 @@ TALER_TESTING_make_trait_denom_sig (unsigned int index, const struct TALER_DenominationSignature *sig); + +/** + * @param selector associate the object with this "tag" + * @param i which object should be returned + * + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_uint64 + (unsigned int index, + const uint64_t *i); + +/** + * Obtain a "number" value from @a cmd. + * + * @param cmd command to extract trait from + * @param selector which coin to pick if @a cmd has multiple on + * offer + * @param n[out] set to the number coming from @a cmd. + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_get_trait_uint64 + (const struct TALER_TESTING_Command *cmd, + unsigned int index, + const uint64_t **n); + /** * @param selector associate the object with this "tag" * @param i which object should be returned @@ -1225,7 +1292,7 @@ int TALER_TESTING_get_trait_transfer_subject (const struct TALER_TESTING_Command *cmd, unsigned int index, - char **transfer_subject); + const char **transfer_subject); /** @@ -1239,7 +1306,7 @@ TALER_TESTING_get_trait_transfer_subject struct TALER_TESTING_Trait TALER_TESTING_make_trait_transfer_subject (unsigned int index, - char *transfer_subject); + const char *transfer_subject); /** @@ -1369,11 +1436,42 @@ int TALER_TESTING_get_trait_amount_obj ( const struct TALER_TESTING_Command *cmd, unsigned int index, - struct TALER_Amount **amount); + const struct TALER_Amount **amount); struct TALER_TESTING_Trait TALER_TESTING_make_trait_amount_obj ( unsigned int index, const struct TALER_Amount *amount); + +/** + * Offer reference to a bank transfer which has been + * rejected. + * + * @param index which reference is to be picked, in case + * multiple are offered. + * @param rejected_reference the url to offer + * @return the trait, to be put in the traits array of the command + */ +struct TALER_TESTING_Trait +TALER_TESTING_make_trait_rejected + (unsigned int index, + const char *rejected); + +/** + * Obtain the reference from a bank transfer which has + * been rejected. + * + * @param cmd command to extract trait from + * @param index which reference is to be picked, in case + * multiple are offered. + * @param rejected_reference[out] where to write the order id. + * @return #GNUNET_OK on success + */ +int +TALER_TESTING_get_trait_rejected + (const struct TALER_TESTING_Command *cmd, + unsigned int index, + const char **rejected_reference); + #endif