diff options
author | Marcello Stanisci <stanisci.m@gmail.com> | 2018-02-22 14:51:12 +0100 |
---|---|---|
committer | Marcello Stanisci <stanisci.m@gmail.com> | 2018-02-26 14:12:46 +0100 |
commit | b593d416d6e788b2053c2f5ebb634e0bb39fe560 (patch) | |
tree | cf08a83fb1922c32d4a0e4ce161ae3ff20023a69 /src/bank-lib | |
parent | 8d5cc9f550da58610ad220d72f21d930c47ad0a8 (diff) | |
download | exchange-b593d416d6e788b2053c2f5ebb634e0bb39fe560.tar.xz |
Bank-lib tests, using the new (libraries-based) style.
Diffstat (limited to 'src/bank-lib')
-rw-r--r-- | src/bank-lib/Makefile.am | 43 | ||||
-rw-r--r-- | src/bank-lib/bank.conf | 6 | ||||
-rw-r--r-- | src/bank-lib/test_bank_api_new.c | 185 | ||||
-rw-r--r-- | src/bank-lib/test_bank_api_with_fakebank_new.c | 223 | ||||
-rw-r--r-- | src/bank-lib/testing_api_cmd_history.c | 735 | ||||
-rw-r--r-- | src/bank-lib/testing_api_cmd_reject.c | 164 | ||||
-rw-r--r-- | src/bank-lib/testing_api_helpers.c | 205 |
7 files changed, 1558 insertions, 3 deletions
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 <http://www.gnu.org/licenses/> +*/ + +/** + * @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 <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include <microhttpd.h> +#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 <http://www.gnu.org/licenses/> +*/ + +/** + * @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 <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include <microhttpd.h> +#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 + <http://www.gnu.org/licenses/> +*/ + +/** + * @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 <gnunet/gnunet_curl_lib.h> +#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;i<is->ip;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;off<h_len;off++) + { + GNUNET_free (h[off].details.wire_transfer_subject); + json_decref (h[off].details.account_details); + } + GNUNET_free_non_null (h); +} + +/** + * Log which history we expected. + * + * @param h what we expected + * @param h_len number of entries in @a h + * @param off position of the missmatch + */ +static void +print_expected (struct History *h, + uint64_t h_len, + unsigned int off) +{ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Transaction history missmatch at position %u/%llu\n", + off, + (unsigned long long) h_len); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expected history:\n"); + for (uint64_t i=0;i<h_len;i++) + { + char *acc; + + acc = json_dumps (h[i].details.account_details, + JSON_COMPACT); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "H(%llu): %s%s (serial: %llu, subject: %s, to: %s)\n", + (unsigned long long) i, + (TALER_BANK_DIRECTION_CREDIT == h[i].direction) ? "+" : "-", + TALER_amount2s (&h[i].details.amount), + (unsigned long long) h[i].row_id, + h[i].details.wire_transfer_subject, + acc); + if (NULL != acc) + free (acc); + } +} + +/** + * Build history of transactions matching the current + * command in @a is. + * + * @param is interpreter state + * @param[out] rh history array to initialize + * @return number of entries in @a rh + */ +static uint64_t +build_history (struct TALER_TESTING_Interpreter *is, + struct History **rh) +{ + struct HistoryState *hs = is->commands[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 + <http://www.gnu.org/licenses/> +*/ + +/** + * @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 <gnunet/gnunet_curl_lib.h> +#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 + <http://www.gnu.org/licenses/> +*/ + +/** + * @file bank-lib/testing_api_helpers.c + * @brief convenience functions for bank-lib tests. + * @author Marcello Stanisci + */ + +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#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 */ |