aboutsummaryrefslogtreecommitdiff
path: root/src/exchange-lib
diff options
context:
space:
mode:
authorMarcello Stanisci <stanisci.m@gmail.com>2018-01-23 10:28:24 +0100
committerMarcello Stanisci <stanisci.m@gmail.com>2018-02-12 16:12:07 +0100
commitfe6960cce854cd4c665a27c4368e4397c8e7bcfb (patch)
tree162a95572079b3e7a014cb845ee028329a06f5f8 /src/exchange-lib
parentb198bb3867b6a15c65a8860af12d7a634de906a0 (diff)
Implement new traits-based tests.
Diffstat (limited to 'src/exchange-lib')
-rw-r--r--src/exchange-lib/Makefile.am42
-rw-r--r--src/exchange-lib/test_exchange_api_keys_cherry_picking.conf2
-rw-r--r--src/exchange-lib/test_exchange_api_keys_cherry_picking_new.c135
-rw-r--r--src/exchange-lib/test_exchange_api_new.c671
-rw-r--r--src/exchange-lib/testing_api_cmd_bank_check.c268
-rw-r--r--src/exchange-lib/testing_api_cmd_check_keys.c153
-rw-r--r--src/exchange-lib/testing_api_cmd_deposit.c448
-rw-r--r--src/exchange-lib/testing_api_cmd_exec_aggregator.c162
-rw-r--r--src/exchange-lib/testing_api_cmd_exec_auditor-sign.c221
-rw-r--r--src/exchange-lib/testing_api_cmd_exec_keyup.c168
-rw-r--r--src/exchange-lib/testing_api_cmd_exec_wirewatch.c6
-rw-r--r--src/exchange-lib/testing_api_cmd_fakebank_transfer.c113
-rw-r--r--src/exchange-lib/testing_api_cmd_payback.c491
-rw-r--r--src/exchange-lib/testing_api_cmd_refresh.c993
-rw-r--r--src/exchange-lib/testing_api_cmd_refund.c295
-rw-r--r--src/exchange-lib/testing_api_cmd_signal.c112
-rw-r--r--src/exchange-lib/testing_api_cmd_status.c240
-rw-r--r--src/exchange-lib/testing_api_cmd_track.c819
-rw-r--r--src/exchange-lib/testing_api_cmd_wire.c267
-rw-r--r--src/exchange-lib/testing_api_cmd_withdraw.c117
-rw-r--r--src/exchange-lib/testing_api_helpers.c156
-rw-r--r--src/exchange-lib/testing_api_loop.c334
-rw-r--r--src/exchange-lib/testing_api_trait_blinding_key.c8
-rw-r--r--src/exchange-lib/testing_api_trait_coin_priv.c38
-rw-r--r--src/exchange-lib/testing_api_trait_denom_pub.c46
-rw-r--r--src/exchange-lib/testing_api_trait_denom_sig.c8
-rw-r--r--src/exchange-lib/testing_api_trait_fresh_coin.c74
-rw-r--r--src/exchange-lib/testing_api_trait_key_peer.c82
-rw-r--r--src/exchange-lib/testing_api_trait_number.c74
-rw-r--r--src/exchange-lib/testing_api_trait_process.c8
-rw-r--r--src/exchange-lib/testing_api_trait_reserve_priv.c8
-rw-r--r--src/exchange-lib/testing_api_trait_string.c209
-rw-r--r--src/exchange-lib/testing_api_trait_wtid.c74
-rw-r--r--src/exchange-lib/testing_api_traits.c54
34 files changed, 6558 insertions, 338 deletions
diff --git a/src/exchange-lib/Makefile.am b/src/exchange-lib/Makefile.am
index 64a5f9649..cf80a1d42 100644
--- a/src/exchange-lib/Makefile.am
+++ b/src/exchange-lib/Makefile.am
@@ -34,14 +34,26 @@ libtalerexchange_la_LIBADD = \
-ljansson \
$(XLIB)
-
libtalertesting_la_LDFLAGS = \
-version-info 0:0:0 \
-no-undefined
libtalertesting_la_SOURCES = \
+ testing_api_cmd_exec_aggregator.c \
testing_api_cmd_exec_wirewatch.c \
+ testing_api_cmd_exec_keyup.c \
+ testing_api_cmd_exec_auditor-sign.c \
testing_api_cmd_fakebank_transfer.c \
testing_api_cmd_withdraw.c \
+ testing_api_cmd_wire.c \
+ testing_api_cmd_refund.c \
+ testing_api_cmd_status.c \
+ testing_api_cmd_deposit.c \
+ testing_api_cmd_refresh.c \
+ testing_api_cmd_track.c \
+ testing_api_cmd_bank_check.c \
+ testing_api_cmd_payback.c \
+ testing_api_cmd_signal.c \
+ testing_api_cmd_check_keys.c \
testing_api_helpers.c \
testing_api_loop.c \
testing_api_traits.c \
@@ -50,7 +62,12 @@ libtalertesting_la_SOURCES = \
testing_api_trait_denom_pub.c \
testing_api_trait_denom_sig.c \
testing_api_trait_process.c \
- testing_api_trait_reserve_priv.c
+ testing_api_trait_reserve_priv.c \
+ testing_api_trait_number.c \
+ testing_api_trait_fresh_coin.c \
+ testing_api_trait_string.c \
+ testing_api_trait_key_peer.c \
+ testing_api_trait_wtid.c
libtalertesting_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
@@ -71,13 +88,17 @@ endif
endif
check_PROGRAMS = \
+ test_exchange_api_keys_cherry_picking_new \
+ test_exchange_api_new \
test_exchange_api \
- test_exchange_api_keys_cherry_picking \
- test_exchange_api_new
+ test_exchange_api_keys_cherry_picking
AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
+# FIXME: uncomment those.
TESTS = \
+ test_exchange_api_keys_cherry_picking_new \
+ test_exchange_api_new \
test_exchange_api \
test_exchange_api_keys_cherry_picking
@@ -108,6 +129,19 @@ test_exchange_api_new_LDADD = \
-lgnunetutil \
-ljansson
+test_exchange_api_keys_cherry_picking_new_SOURCES = \
+ test_exchange_api_keys_cherry_picking_new.c
+test_exchange_api_keys_cherry_picking_new_LDADD = \
+ libtalertesting.la \
+ libtalerexchange.la \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/json/libtalerjson.la \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/bank-lib/libtalerbank.la \
+ -lgnunetcurl \
+ -lgnunetutil \
+ -ljansson
+
test_exchange_api_keys_cherry_picking_SOURCES = \
test_exchange_api_keys_cherry_picking.c
test_exchange_api_keys_cherry_picking_LDADD = \
diff --git a/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf b/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf
index 2b94dba65..b2d2fbc5f 100644
--- a/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf
+++ b/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf
@@ -1,7 +1,7 @@
# This file is in the public domain.
#
[PATHS]
-# Persistant data storage for the testcase
+# Persistent data storage for the testcase
TALER_TEST_HOME = test_exchange_api_home/
[taler]
diff --git a/src/exchange-lib/test_exchange_api_keys_cherry_picking_new.c b/src/exchange-lib/test_exchange_api_keys_cherry_picking_new.c
new file mode 100644
index 000000000..c32d6424f
--- /dev/null
+++ b/src/exchange-lib/test_exchange_api_keys_cherry_picking_new.c
@@ -0,0 +1,135 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/test_exchange_api_keys_cherry_picking_new.c
+ * @brief testcase to test exchange's /keys cherry picking ability
+ * @author Marcello Stanisci
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_signatures.h"
+#include "taler_exchange_service.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler_bank_service.h"
+#include "taler_fakebank_lib.h"
+#include "taler_testing_lib.h"
+
+/**
+ * Configuration file we use. One (big) configuration is used
+ * for the various components for this test.
+ */
+#define CONFIG_FILE "test_exchange_api_keys_cherry_picking.conf"
+
+/**
+ * Used to increase the number of denomination keys.
+ */
+#define CONFIG_FILE_EXTENDED \
+ "test_exchange_api_keys_cherry_picking_extended.conf"
+
+
+/**
+ * Main function that will tell the interpreter what commands to
+ * run.
+ *
+ * @param cls closure
+ */
+static void
+run (void *cls,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct TALER_TESTING_Command commands[] = {
+
+ /* Send signal to the exchange to see if it reacts */
+ TALER_TESTING_cmd_signal ("signal-reaction-1",
+ is->exchanged,
+ SIGUSR1),
+
+ TALER_TESTING_cmd_check_keys ("check-keys-1",
+ 1, 4,
+ is->exchange),
+
+ TALER_TESTING_cmd_exec_keyup ("keyup-2", /* 1st keyup happens at start-up */
+ CONFIG_FILE_EXTENDED),
+
+ TALER_TESTING_cmd_exec_auditor_sign ("sign-keys-1",
+ CONFIG_FILE),
+
+ TALER_TESTING_cmd_signal ("trigger-keys-reload-1",
+ is->exchanged,
+ SIGUSR1),
+
+ TALER_TESTING_cmd_check_keys ("check-keys-2",
+ 2, 8,
+ is->exchange),
+
+
+ /**
+ * End the suite. Fixme: better to have a label for this
+ * too, as it shows "(null)" in logs.
+ */
+ TALER_TESTING_cmd_end ()
+ };
+
+ TALER_TESTING_run (is, commands);
+}
+
+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-exchange-api-cherry-picking-new",
+ "DEBUG", NULL);
+ TALER_TESTING_cleanup_files (CONFIG_FILE);
+ /* @helpers. Run keyup, create tables, ... Note: it
+ * fetches the port number from config in order to see
+ * if it's available. */
+ switch (TALER_TESTING_prepare_exchange (CONFIG_FILE))
+ {
+ case GNUNET_SYSERR:
+ GNUNET_break (0);
+ return 1;
+ case GNUNET_NO:
+ return 77;
+ case GNUNET_OK:
+ if (GNUNET_OK !=
+ /* Set up event loop and reschedule context, plus
+ * start/stop the exchange. It calls TALER_TESTING_setup
+ * which creates the 'is' object.
+ */
+ TALER_TESTING_setup_with_exchange (&run,
+ NULL,
+ CONFIG_FILE))
+ return 1;
+ break;
+ default:
+ GNUNET_break (0);
+ return 1;
+ }
+ return 0;
+}
+
+/* end of test_exchange_api_keys_cherry_picking_new.c */
diff --git a/src/exchange-lib/test_exchange_api_new.c b/src/exchange-lib/test_exchange_api_new.c
index f9d60249d..580c4e972 100644
--- a/src/exchange-lib/test_exchange_api_new.c
+++ b/src/exchange-lib/test_exchange_api_new.c
@@ -2,23 +2,29 @@
This file is part of TALER
Copyright (C) 2014-2018 Taler Systems SA
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
+ TALER is 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.
+ 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/>
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
*/
+
/**
* @file exchange/test_exchange_api_new.c
* @brief testcase to test exchange's HTTP API interface
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
* @author Christian Grothoff
+ * @author Marcello Stanisci
*/
+
#include "platform.h"
#include "taler_util.h"
#include "taler_signatures.h"
@@ -37,10 +43,17 @@
#define CONFIG_FILE "test_exchange_api.conf"
/**
- * URL of the fakebank. Obtained from CONFIG_FILE's "exchange-wire-test:BANK_URL" option.
+ * URL of the fakebank. Obtained from CONFIG_FILE's
+ * "exchange-wire-test:BANK_URI" option.
*/
static char *fakebank_url;
+/**
+ * FIXME: what about putting exchange_url also global
+ * here? Right now, the exchange port is being "bounced"
+ * between functions before exchange_url is constructed
+ * and TALER_EXCHANGE_connect() is called with that.
+ */
/**
* Account number of the exchange at the bank.
@@ -72,6 +85,15 @@ static char *fakebank_url;
TALER_TESTING_cmd_exec_wirewatch (label, CONFIG_FILE)
/**
+ * Execute the taler-exchange-aggregator command with
+ * our configuration file.
+ *
+ * @param label label to use for the command.
+ */
+#define CMD_EXEC_AGGREGATOR(label) \
+ TALER_TESTING_cmd_exec_aggregator (label, CONFIG_FILE)
+
+/**
* Run wire transfer of funds from some user's account to the
* exchange.
*
@@ -83,9 +105,22 @@ static char *fakebank_url;
fakebank_url, USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO, \
USER_LOGIN_NAME, USER_LOGIN_PASS)
+/**
+ * Run wire transfer of funds from some user's account to the
+ * exchange.
+ *
+ * @param label label to use for the command.
+ * @param amount amount to transfer, i.e. "EUR:1"
+ */
+#define CMD_TRANSFER_TO_EXCHANGE_SUBJECT(label,amount,subject) \
+ TALER_TESTING_cmd_fakebank_transfer_with_subject \
+ (label, amount, fakebank_url, USER_ACCOUNT_NO, \
+ EXCHANGE_ACCOUNT_NO, USER_LOGIN_NAME, USER_LOGIN_PASS, \
+ subject)
/**
- * Main function that will tell the interpreter what commands to run.
+ * Main function that will tell the interpreter what commands to
+ * run.
*
* @param cls closure
*/
@@ -94,9 +129,609 @@ run (void *cls,
struct TALER_TESTING_Interpreter *is)
{
struct TALER_TESTING_Command commands[] = {
+
+ /****** Start of "wire" testing ******/
+
+ /**
+ * Move money to the exchange's bank account.
+ */
CMD_TRANSFER_TO_EXCHANGE ("create-reserve-1",
"EUR:5.01"),
- CMD_EXEC_WIREWATCH ("exec-wirewatch-1"),
+
+ /**
+ * Make a reserve exist, according to the previous
+ * transfer.
+ */
+ CMD_EXEC_WIREWATCH ("wirewatch-1"),
+
+ /**
+ * Check if 'test' wire method is offered by the exchange.
+ */
+ TALER_TESTING_cmd_wire ("wire-test-1",
+ is->exchange,
+ "test",
+ NULL,
+ MHD_HTTP_OK),
+
+ /**
+ * Check if 'sepa' wire method is offered by the exchange.
+ */
+ TALER_TESTING_cmd_wire ("wire-sepa-1",
+ is->exchange,
+ "sepa",
+ NULL,
+ MHD_HTTP_OK),
+
+ /****** End of "wire" testing ******/
+
+ /****** Start of withdraw and spend testing ******/
+
+ /**
+ * Withdraw EUR:5.
+ */
+ TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1",
+ is->exchange,
+ "create-reserve-1",
+ "EUR:5",
+ MHD_HTTP_OK),
+
+ /**
+ * Check the reserve is depleted.
+ */
+ TALER_TESTING_cmd_status ("status-1",
+ is->exchange,
+ "create-reserve-1",
+ "EUR:0",
+ MHD_HTTP_OK),
+ /**
+ * Spend the coin.
+ */
+ TALER_TESTING_cmd_deposit
+ ("deposit-simple", is->exchange, "withdraw-coin-1", 0,
+ TALER_TESTING_make_wire_details
+ ("{ \"type\":\"test\",\"account_number\":42}",
+ fakebank_url),
+ "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}",
+ GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_OK),
+
+ /**
+ * Try to overdraw.
+ */
+ TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2",
+ is->exchange,
+ "create-reserve-1",
+ "EUR:5",
+ MHD_HTTP_FORBIDDEN),
+
+ /**
+ * Try to double spend using different wire details.
+ */
+ TALER_TESTING_cmd_deposit
+ ("deposit-double-1", is->exchange, "withdraw-coin-1", 0,
+ TALER_TESTING_make_wire_details
+ ("{\"type\":\"test\",\"account_number\":43}",
+ fakebank_url),
+ "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}",
+ GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN),
+
+ /**
+ * Try to double spend using a different transaction id.
+ * (copied verbatim from old exchange-lib tests.)
+ * FIXME: how can it get a different transaction id? There
+ * isn't such a thing actually, the exchange only knows about
+ * contract terms' hashes. So since the contract terms are
+ * exactly the same as the previous command, how can a different
+ * id be generated?
+ */
+ TALER_TESTING_cmd_deposit
+ ("deposit-double-1", is->exchange, "withdraw-coin-1", 0,
+ TALER_TESTING_make_wire_details
+ ("{ \"type\":\"test\", \"account_number\":43}",
+ fakebank_url),
+ "{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}",
+ GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN),
+
+ /**
+ * Try to double spend with different proposal.
+ */
+ TALER_TESTING_cmd_deposit
+ ("deposit-double-2", is->exchange, "withdraw-coin-1", 0,
+ TALER_TESTING_make_wire_details
+ ("{ \"type\":\"test\", \"account_number\":43}",
+ fakebank_url),
+ "{\"items\":[{\"name\":\"ice cream\",\"value\":2}]}",
+ GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_FORBIDDEN),
+
+ /****** End of withdraw and spend testing ******/
+
+ /****** Start of refresh testing ******/
+
+ /**
+ * Fill reserve with EUR:5, 1ct is for fees. NOTE: the old
+ * test-suite gave a account number of _424_ to the user at
+ * this step; to type less, here the _42_ number is reused.
+ * Does this change the tests semantics?
+ */
+ CMD_TRANSFER_TO_EXCHANGE ("refresh-create-reserve-1",
+ "EUR:5.01"),
+
+ /**
+ * Make previous command effective.
+ */
+ CMD_EXEC_WIREWATCH ("wirewatch-2"),
+
+ /**
+ * Withdraw EUR:5.
+ */
+ TALER_TESTING_cmd_withdraw_amount
+ ("refresh-withdraw-coin-1",
+ is->exchange,
+ "refresh-create-reserve-1",
+ "EUR:5",
+ MHD_HTTP_OK),
+ /**
+ * Try to partially spend (deposit) 1 EUR of the 5 EUR coin
+ * (in full) (merchant would receive EUR:0.99 due to 1 ct
+ * deposit fee)
+ */
+ TALER_TESTING_cmd_deposit
+ ("refresh-deposit-partial", is->exchange,
+ "refresh-withdraw-coin-1", 0,
+ TALER_TESTING_make_wire_details
+ ("{ \"type\":\"test\",\"account_number\":42}",
+ fakebank_url),
+ "{\"items\":[{\"name\":\"ice cream\",\
+ \"value\":\"EUR:1\"}]}",
+ GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_OK),
+
+ /**
+ * Melt the rest of the coin's value
+ * (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
+ TALER_TESTING_cmd_refresh_melt
+ ("refresh-melt-1", is->exchange, "EUR:4",
+ "refresh-withdraw-coin-1", MHD_HTTP_OK),
+ /**
+ * Complete (successful) melt operation, and
+ * withdraw the coins
+ */
+ TALER_TESTING_cmd_refresh_reveal
+ ("refresh-reveal-1", is->exchange,
+ "refresh-melt-1", MHD_HTTP_OK),
+
+ /**
+ * Do it again to check idempotency
+ */
+ TALER_TESTING_cmd_refresh_reveal
+ ("refresh-reveal-1-idempotency",
+ is->exchange, "refresh-melt-1", MHD_HTTP_OK),
+
+ /**
+ * Test that /refresh/link works
+ */
+ TALER_TESTING_cmd_refresh_link
+ ("refresh-link-1", is->exchange,
+ "refresh-reveal-1", MHD_HTTP_OK),
+
+ /**
+ * Try to spend a refreshed EUR:1 coin
+ */
+ TALER_TESTING_cmd_deposit
+ ("refresh-deposit-refreshed-1a", is->exchange,
+ "refresh-reveal-1-idempotency", 0,
+ TALER_TESTING_make_wire_details
+ ("{ \"type\":\"test\",\"account_number\":42}",
+ fakebank_url),
+ "{\"items\":[{\"name\":\"ice cream\",\
+ \"value\":3}]}",
+ GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_OK),
+
+ /**
+ * Try to spend a refreshed EUR:0.1 coin
+ */
+ TALER_TESTING_cmd_deposit
+ ("refresh-deposit-refreshed-1b", is->exchange,
+ "refresh-reveal-1", 4,
+ TALER_TESTING_make_wire_details
+ ("{ \"type\":\"test\",\"account_number\":43}",
+ fakebank_url),
+ "{\"items\":[{\"name\":\"ice cream\",\
+ \"value\":3}]}",
+ GNUNET_TIME_UNIT_ZERO, "EUR:0.1", MHD_HTTP_OK),
+
+ /* Test running a failing melt operation (same operation
+ * again must fail) */
+ TALER_TESTING_cmd_refresh_melt
+ ("refresh-melt-failing", is->exchange, "EUR:4",
+ "refresh-withdraw-coin-1", MHD_HTTP_FORBIDDEN),
+
+ /* FIXME: also test with coin that was already melted
+ * (signature differs from coin that was deposited...) */
+
+ /****** End of refresh testing ******/
+
+ /* **** Test tracking API ***** */
+
+ /**
+ * Try resolving a deposit's WTID, as we never triggered
+ * execution of transactions, the answer should be that
+ * the exchange knows about the deposit, but has no WTID yet.
+ */
+ TALER_TESTING_cmd_track_transaction
+ ("deposit-wtid-found", is->exchange,
+ "deposit-simple", 0, MHD_HTTP_ACCEPTED, NULL),
+
+ /**
+ * Try resolving a deposit's WTID for a failed deposit.
+ * As the deposit failed, the answer should be that the
+ * exchange does NOT know about the deposit.
+ */
+ TALER_TESTING_cmd_track_transaction
+ ("deposit-wtid-failing", is->exchange,
+ "deposit-double-2", 0, MHD_HTTP_NOT_FOUND, NULL),
+
+ /**
+ * Try resolving an undefined (all zeros) WTID; this
+ * should fail as obviously the exchange didn't use that
+ * WTID value for any transaction.
+ */
+ TALER_TESTING_cmd_track_transfer_empty
+ ("wire-deposit-failing", is->exchange,
+ NULL, 0, MHD_HTTP_NOT_FOUND),
+
+ /**
+ * Run transfers. Note that _actual_ aggregation will NOT
+ * happen here, as each deposit operation is run with a
+ * fresh merchant public key! NOTE: this comment comes
+ * "verbatim" from the old test-suite, and IMO does not explain
+ * a lot!
+ */
+ CMD_EXEC_AGGREGATOR ("run-aggregator"),
+
+ /**
+ * Check all the transfers took place.
+ */
+ TALER_TESTING_cmd_check_bank_transfer
+ ("check_bank_transfer-499c", "https://exchange.com/",
+ "EUR:4.98", 2, 42),
+
+ TALER_TESTING_cmd_check_bank_transfer
+ ("check_bank_transfer-99c1", "https://exchange.com/",
+ "EUR:0.98", 2, 42),
+
+ TALER_TESTING_cmd_check_bank_transfer
+ ("check_bank_transfer-99c2", "https://exchange.com/",
+ "EUR:0.98", 2, 42),
+
+ TALER_TESTING_cmd_check_bank_transfer
+ ("check_bank_transfer-99c", "https://exchange.com/",
+ "EUR:0.08", 2, 43),
+
+ TALER_TESTING_cmd_check_bank_transfer
+ ("check_bank_transfer-aai-1", "https://exchange.com/",
+ "EUR:5.01", 42, 2),
+
+ /**
+ * NOTE: the old test-suite had this "check bank transfer"
+ * command with debit account == 424.
+ */
+ TALER_TESTING_cmd_check_bank_transfer
+ ("check_bank_transfer-aai-2", "https://exchange.com/",
+ "EUR:5.01", 42, 2),
+
+ TALER_TESTING_cmd_check_bank_empty ("check_bank_empty"),
+
+ TALER_TESTING_cmd_track_transaction
+ ("deposit-wtid-ok", is->exchange,
+ "deposit-simple", 0, MHD_HTTP_OK, "check_bank_transfer-499c"),
+
+ TALER_TESTING_cmd_track_transfer
+ ("wire-deposit-success-bank", is->exchange,
+ "check_bank_transfer-99c1", 0, MHD_HTTP_OK, "EUR:0.98",
+ "EUR:0.01"),
+
+ TALER_TESTING_cmd_track_transfer
+ ("wire-deposits-success-wtid", is->exchange,
+ "deposit-wtid-ok", 0, MHD_HTTP_OK, "EUR:4.98",
+ "EUR:0.01"),
+
+ /* **** End of test tracking API ***** */
+
+ /* **** test /refund API ***** */
+
+ /**
+ * Fill reserve with EUR:5.01, as withdraw fee is 1 ct per
+ * config.
+ */
+ CMD_TRANSFER_TO_EXCHANGE ("create-reserve-r1",
+ "EUR:5.01"),
+
+
+ /**
+ * Run wire-watch to trigger the reserve creation.
+ */
+ CMD_EXEC_WIREWATCH ("wirewatch-3"),
+
+ /* Withdraw a 5 EUR coin, at fee of 1 ct */
+ TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-r1",
+ is->exchange,
+ "create-reserve-r1",
+ "EUR:5",
+ MHD_HTTP_OK),
+ /**
+ * Spend 5 EUR of the 5 EUR coin (in full) (merchant would
+ * receive EUR:4.99 due to 1 ct deposit fee)
+ */
+ TALER_TESTING_cmd_deposit
+ ("deposit-refund-1", is->exchange, "withdraw-coin-r1", 0,
+ TALER_TESTING_make_wire_details
+ ("{ \"type\":\"test\", \"account_number\":42}",
+ fakebank_url),
+ "{\"items\":[{\"name\":\"ice cream\","
+ "\"value\":\"EUR:5\"}]}",
+ GNUNET_TIME_UNIT_MINUTES, "EUR:5", MHD_HTTP_OK),
+
+
+ /**
+ * Run transfers. Should do nothing as refund deadline blocks
+ * it
+ */
+ CMD_EXEC_AGGREGATOR ("run-aggregator-refund"),
+
+ /**
+ * Check that aggregator didn't do anything, as expected.
+ * Note, this operation takes two commands: one to "flush"
+ * the preliminary transfer (used to withdraw) from the
+ * fakebank and the second to actually check there are not
+ * other transfers around.
+ */
+
+ TALER_TESTING_cmd_check_bank_transfer
+ ("check_bank_transfer-pre-refund", "https://exchange.com/",
+ "EUR:5.01", 42, 2),
+
+ TALER_TESTING_cmd_check_bank_empty
+ ("check_bank_transfer-pre-refund"),
+
+ TALER_TESTING_cmd_refund
+ ("refund-ok", MHD_HTTP_OK,
+ "EUR:5", "EUR:0.01", "deposit-refund-1"),
+
+ /**
+ * Spend 4.99 EUR of the refunded 4.99 EUR coin (1ct gone
+ * due to refund) (merchant would receive EUR:4.98 due to
+ * 1 ct deposit fee) */
+ TALER_TESTING_cmd_deposit
+ ("deposit-refund-2", is->exchange, "withdraw-coin-r1", 0,
+ TALER_TESTING_make_wire_details
+ ("{ \"type\":\"test\", \"account_number\":42}",
+ fakebank_url),
+ "{\"items\":[{\"name\":\"more ice cream\","
+ "\"value\":\"EUR:5\"}]}",
+ GNUNET_TIME_UNIT_ZERO, "EUR:4.99", MHD_HTTP_OK),
+
+
+ /**
+ * Run transfers. This will do the transfer as refund deadline
+ * was 0
+ */
+ CMD_EXEC_AGGREGATOR ("run-aggregator-3"),
+
+ /**
+ * Check that deposit did run.
+ */
+ TALER_TESTING_cmd_check_bank_transfer
+ ("check_bank_transfer-pre-refund", "https://exchange.com/",
+ "EUR:4.97", 2, 42),
+
+ /**
+ * Run failing refund, as past deadline & aggregation.
+ */
+ TALER_TESTING_cmd_refund
+ ("refund-fail", MHD_HTTP_GONE,
+ "EUR:4.99", "EUR:0.01", "deposit-refund-2"),
+
+ TALER_TESTING_cmd_check_bank_empty
+ ("check-empty-after-refund"),
+
+ /**
+ * Test refunded coins are never executed, even past
+ * refund deadline
+ */
+ CMD_TRANSFER_TO_EXCHANGE ("create-reserve-rb",
+ "EUR:5.01"),
+
+ CMD_EXEC_WIREWATCH ("wirewatch-rb"),
+
+ TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-rb",
+ is->exchange,
+ "create-reserve-rb",
+ "EUR:5",
+ MHD_HTTP_OK),
+
+ TALER_TESTING_cmd_check_bank_transfer
+ ("check_bank_transfer-aai-3b", "https://exchange.com/",
+ "EUR:5.01", 42, 2),
+
+
+ TALER_TESTING_cmd_deposit
+ ("deposit-refund-1b", is->exchange, "withdraw-coin-rb", 0,
+ TALER_TESTING_make_wire_details
+ ("{ \"type\":\"test\", \"account_number\":42}",
+ fakebank_url),
+ "{\"items\":[{\"name\":\"ice cream\","
+ "\"value\":\"EUR:5\"}]}",
+ GNUNET_TIME_UNIT_ZERO, "EUR:5", MHD_HTTP_OK),
+
+ /**
+ * Trigger refund (before aggregator had a chance to execute
+ * deposit, even though refund deadline was zero).
+ */
+ TALER_TESTING_cmd_refund
+ ("refund-ok-fast", MHD_HTTP_OK,
+ "EUR:5", "EUR:0.01", "deposit-refund-1b"),
+
+ /**
+ * Run transfers. This will do the transfer as refund deadline
+ * was 0, except of course because the refund succeeded, the
+ * transfer should no longer be done.
+ */
+ CMD_EXEC_AGGREGATOR ("run-aggregator-3b"),
+
+ /* check that aggregator didn't do anything, as expected */
+ TALER_TESTING_cmd_check_bank_empty
+ ("check-refund-fast-not-run"),
+
+ /* ************** End of refund API testing ************* */
+
+ /* ************** Test /payback API ************* */
+
+ /**
+ * Fill reserve with EUR:5.01, as withdraw fee is 1 ct per
+ * config.
+ */
+ CMD_TRANSFER_TO_EXCHANGE ("payback-create-reserve-1",
+ "EUR:5.01"),
+
+ /**
+ * Run wire-watch to trigger the reserve creation.
+ */
+ CMD_EXEC_WIREWATCH ("wirewatch-4"),
+
+ /* Withdraw a 5 EUR coin, at fee of 1 ct */
+ TALER_TESTING_cmd_withdraw_amount ("payback-withdraw-coin-1",
+ is->exchange,
+ "payback-create-reserve-1",
+ "EUR:5",
+ MHD_HTTP_OK),
+
+ TALER_TESTING_cmd_revoke ("revoke-1", MHD_HTTP_OK,
+ "payback-withdraw-coin-1",
+ CONFIG_FILE),
+
+ TALER_TESTING_cmd_payback ("payback-1", MHD_HTTP_OK,
+ "payback-withdraw-coin-1", "EUR:5"),
+
+ /* Check the money is back with the reserve */
+ TALER_TESTING_cmd_status ("payback-reserve-status-1",
+ is->exchange,
+ "payback-create-reserve-1",
+ "EUR:5.0",
+ MHD_HTTP_OK),
+
+ /**
+ * Fill reserve with EUR:2.02, as withdraw fee is 1 ct per
+ * config, then withdraw two coin, partially spend one, and
+ * then have the rest paid back. Check deposit of other coin
+ * fails. (Do not use EUR:5 here as the EUR:5 coin was
+ * revoked and we did not bother to create a new one...)
+ */
+ CMD_TRANSFER_TO_EXCHANGE ("payback-create-reserve-2",
+ "EUR:2.02"),
+
+ /* Make previous command effective. */
+ CMD_EXEC_WIREWATCH ("wirewatch-5"),
+
+ /* Withdraw a 1 EUR coin, at fee of 1 ct */
+ TALER_TESTING_cmd_withdraw_amount ("payback-withdraw-coin-2a",
+ is->exchange,
+ "payback-create-reserve-2",
+ "EUR:1",
+ MHD_HTTP_OK),
+
+ /* Withdraw a 1 EUR coin, at fee of 1 ct */
+ TALER_TESTING_cmd_withdraw_amount ("payback-withdraw-coin-2b",
+ is->exchange,
+ "payback-create-reserve-2",
+ "EUR:1",
+ MHD_HTTP_OK),
+
+ TALER_TESTING_cmd_deposit
+ ("payback-deposit-partial", is->exchange,
+ "payback-withdraw-coin-2a", 0,
+ TALER_TESTING_make_wire_details
+ ("{ \"type\":\"test\",\"account_number\":42}",
+ fakebank_url),
+ "{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}",
+ GNUNET_TIME_UNIT_ZERO, "EUR:0.5", MHD_HTTP_OK),
+
+
+ TALER_TESTING_cmd_revoke ("revoke-2", MHD_HTTP_OK,
+ "payback-withdraw-coin-2a",
+ CONFIG_FILE),
+
+ TALER_TESTING_cmd_payback ("payback-2", MHD_HTTP_OK,
+ "payback-withdraw-coin-2a",
+ "EUR:0.5"),
+
+ TALER_TESTING_cmd_payback ("payback-2b", MHD_HTTP_FORBIDDEN,
+ "payback-withdraw-coin-2a",
+ "EUR:0.5"),
+
+ TALER_TESTING_cmd_deposit
+ ("payback-deposit-revoked", is->exchange,
+ "payback-withdraw-coin-2b", 0,
+ TALER_TESTING_make_wire_details
+ ("{ \"type\":\"test\",\"account_number\":42}",
+ fakebank_url),
+ "{\"items\":[{\"name\":\"more ice cream\",\"value\":1}]}",
+ GNUNET_TIME_UNIT_ZERO, "EUR:1", MHD_HTTP_NOT_FOUND),
+
+
+ /* Test deposit fails after payback, with proof in payback */
+
+ /* FIXME: #3887: right now, the exchange will never return the
+ * coin's transaction history with payback data, as we get a
+ * 404 on the DK! */
+ TALER_TESTING_cmd_deposit
+ ("payback-deposit-partial-after-payback", is->exchange,
+ "payback-withdraw-coin-2a", 0,
+ TALER_TESTING_make_wire_details
+ ("{ \"type\":\"test\",\"account_number\":42}",
+ fakebank_url),
+ "{\"items\":[{\"name\":\"extra ice cream\",\"value\":1}]}",
+ GNUNET_TIME_UNIT_ZERO, "EUR:0.5", MHD_HTTP_NOT_FOUND),
+
+ /* Test that revoked coins cannot be withdrawn */
+ CMD_TRANSFER_TO_EXCHANGE ("payback-create-reserve-3",
+ "EUR:1.01"),
+
+ CMD_EXEC_WIREWATCH ("wirewatch-6"),
+
+ TALER_TESTING_cmd_withdraw_amount
+ ("payback-withdraw-coin-3-revoked",
+ is->exchange,
+ "payback-create-reserve-3",
+ "EUR:1",
+ MHD_HTTP_NOT_FOUND),
+
+ /* check that we are empty before the rejection test */
+ TALER_TESTING_cmd_check_bank_transfer
+ ("check_bank_transfer-pr1", "https://exchange.com/",
+ "EUR:5.01", 42, 2),
+ TALER_TESTING_cmd_check_bank_transfer
+ ("check_bank_transfer-pr2", "https://exchange.com/",
+ "EUR:2.02", 42, 2),
+ TALER_TESTING_cmd_check_bank_transfer
+ ("check_bank_transfer-pr3", "https://exchange.com/",
+ "EUR:1.01", 42, 2),
+
+ TALER_TESTING_cmd_check_bank_empty ("check-empty-again"),
+
+ /* Test rejection of bogus wire transfers */
+ CMD_TRANSFER_TO_EXCHANGE_SUBJECT ("bogus-subject",
+ "EUR:1.01",
+ "not a reserve public key"),
+
+ CMD_EXEC_WIREWATCH ("wirewatch-7"),
+
+ TALER_TESTING_cmd_check_bank_empty ("check-empty-from-reject"),
+
+ /* ************** End of /payback API ************* */
+
+ /**
+ * End the suite. Fixme: better to have a label for this
+ * too, as it shows a "(null)" token on logs.
+ */
TALER_TESTING_cmd_end ()
};
@@ -105,7 +740,6 @@ run (void *cls,
fakebank_url);
}
-
int
main (int argc,
char * const *argv)
@@ -116,9 +750,15 @@ main (int argc,
GNUNET_log_setup ("test-exchange-api-new",
"INFO",
NULL);
- if (NULL == (fakebank_url = TALER_TESTING_prepare_fakebank (CONFIG_FILE)))
+ if (NULL == (fakebank_url
+ /* Check fakebank port is available and config cares
+ * about bank url. */
+ = TALER_TESTING_prepare_fakebank (CONFIG_FILE)))
return 77;
TALER_TESTING_cleanup_files (CONFIG_FILE);
+ /* @helpers. Run keyup, create tables, ... Note: it
+ * fetches the port number from config in order to see
+ * if it's available. */
switch (TALER_TESTING_prepare_exchange (CONFIG_FILE))
{
case GNUNET_SYSERR:
@@ -128,10 +768,15 @@ main (int argc,
return 77;
case GNUNET_OK:
if (GNUNET_OK !=
+ /* Set up event loop and reschedule context, plus
+ * start/stop the exchange. It calls TALER_TESTING_setup
+ * which creates the 'is' object.
+ */
TALER_TESTING_setup_with_exchange (&run,
NULL,
CONFIG_FILE))
return 1;
+ break;
default:
GNUNET_break (0);
return 1;
diff --git a/src/exchange-lib/testing_api_cmd_bank_check.c b/src/exchange-lib/testing_api_cmd_bank_check.c
new file mode 100644
index 000000000..ab5522441
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_bank_check.c
@@ -0,0 +1,268 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3, or
+ (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_bank_check.c
+ * @brief command to check if a particular wire transfer took
+ * place.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+#include "taler_fakebank_lib.h"
+
+struct BankCheckState
+{
+
+ /**
+ * Exchange base URL (Fixme: why?)
+ */
+ const char *exchange_base_url;
+
+ /**
+ * Expected transferred amount.
+ */
+ const char *amount;
+
+ /**
+ * Expected account number that gave money
+ */
+ unsigned int debit_account;
+
+ /**
+ * Expected account number that received money
+ */
+ unsigned int credit_account;
+
+ /**
+ * Wire transfer subject (set by fakebank-lib).
+ */
+ char *subject;
+
+ /**
+ * Binary form of the transfer subject. Some commands expect
+ * it - via appropriate traits - to be in binary form.
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ /**
+ * Interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+};
+
+/**
+ * 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
+check_bank_transfer_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct BankCheckState *bcs = cls;
+ struct TALER_Amount amount;
+
+
+ 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 (GNUNET_OK !=
+ TALER_FAKEBANK_check (is->fakebank,
+ &amount,
+ bcs->debit_account,
+ bcs->credit_account,
+ bcs->exchange_base_url,
+ &bcs->subject))
+ {
+ GNUNET_break (0);
+ 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
+check_bank_transfer_cleanup
+ (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct BankCheckState *bcs = cls;
+
+ GNUNET_free_non_null (bcs->subject);
+ GNUNET_free (bcs);
+}
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ * to return in case there were multiple generated
+ * by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+check_bank_transfer_traits (void *cls,
+ void **ret,
+ const char *trait,
+ unsigned int index)
+{
+
+
+ struct BankCheckState *bcs = cls;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_STRINGS_string_to_data
+ (bcs->subject,
+ strlen (bcs->subject),
+ &bcs->wtid,
+ sizeof (struct TALER_WireTransferIdentifierRawP)));
+
+ struct TALER_TESTING_Trait traits[] = {
+ TALER_TESTING_make_trait_transfer_subject (0, bcs->subject),
+ TALER_TESTING_make_trait_wtid (0, &bcs->wtid),
+ TALER_TESTING_trait_end ()
+ };
+
+ return TALER_TESTING_get_trait (traits,
+ ret,
+ trait,
+ index);
+}
+
+
+
+/**
+ * Command to check whether a particular wire transfer has been
+ * made or not.
+ *
+ * @param label the command label
+ * @param exchange_base_url base url of the exchange (Fixme: why?)
+ * @param amount the amount expected to be transferred
+ * @param debit_account the account that gave money
+ * @param credit_account the account that received money
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+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)
+{
+ struct BankCheckState *bcs;
+ struct TALER_TESTING_Command cmd;
+
+ bcs = GNUNET_new (struct BankCheckState);
+ bcs->exchange_base_url = exchange_base_url;
+ bcs->amount = amount;
+ bcs->debit_account = debit_account;
+ bcs->credit_account = credit_account;
+
+ cmd.label = label;
+ cmd.cls = bcs;
+ cmd.run = &check_bank_transfer_run;
+ cmd.cleanup = &check_bank_transfer_cleanup;
+ // traits?
+ cmd.traits = &check_bank_transfer_traits;
+
+ return cmd;
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+check_bank_empty_cleanup
+ (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ return;
+}
+
+/**
+ * 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
+check_bank_empty_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+
+ if (GNUNET_OK != TALER_FAKEBANK_check_empty (is->fakebank))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ TALER_TESTING_interpreter_next (is);
+}
+
+/**
+ * Check bank's balance is zero.
+ *
+ * @param credit_account the account that received money
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_check_bank_empty (const char *label)
+{
+ struct TALER_TESTING_Command cmd;
+
+ cmd.label = label;
+ cmd.run = &check_bank_empty_run;
+ cmd.cleanup = &check_bank_empty_cleanup;
+
+ return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_check_keys.c b/src/exchange-lib/testing_api_cmd_check_keys.c
new file mode 100644
index 000000000..8f77a83b4
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_check_keys.c
@@ -0,0 +1,153 @@
+/*
+ This file is part of TALER
+ (C) 2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3, or
+ (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_check_keys.c
+ * @brief Implementation of "check keys" test command.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+
+struct CheckKeysState
+{
+ /**
+ * FIXME
+ */
+ unsigned int generation;
+
+ /**
+ * FIXME
+ */
+ unsigned int num_denom_keys;
+
+ /**
+ * Connection to the exchange.
+ */
+ struct TALER_EXCHANGE_Handle *exchange;
+
+};
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct SignalState.
+ * @param cmd the command to execute, a /wire one.
+ * @param is the interpreter state.
+ */
+void
+check_keys_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct CheckKeysState *cks = cls;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "cmd `%s', key generation: %d\n",
+ cmd->label, is->key_generation);
+
+ if (is->key_generation < cks->generation)
+ {
+ /* Go back to waiting for /keys signal! */
+ is->working = GNUNET_NO;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Triggering /keys dl, cmd `%s'\n",
+ cmd->label);
+ GNUNET_break (0 == TALER_EXCHANGE_check_keys_current
+ (cks->exchange, GNUNET_YES).abs_value_us);
+ return;
+ }
+ if (is->key_generation > cks->generation)
+ {
+ /* We got /keys too often, strange. Fatal. May theoretically
+ happen if somehow we were really unlucky and /keys expired
+ "naturally", but obviously with a sane configuration this
+ should also not be. */
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ /* /keys was updated, let's check they were OK! */
+ if (cks->num_denom_keys != is->keys->num_denom_keys)
+ {
+ /* Did not get the expected number of denomination keys! */
+ GNUNET_break (0);
+ fprintf (stderr, "Got %u keys in step %s\n",
+ is->keys->num_denom_keys, cmd->label);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ TALER_TESTING_interpreter_next (is);
+}
+
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct SignalState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+check_keys_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct CheckKeysState *cks = cls;
+
+ GNUNET_free (cks);
+}
+
+/**
+ * Make a "check keys" command.
+ *
+ * @param label command label
+ * @param generation FIXME
+ * @param num_denom_keys FIXME
+ *
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_check_keys
+ (const char *label,
+ unsigned int generation,
+ unsigned int num_denom_keys,
+ struct TALER_EXCHANGE_Handle *exchange)
+{
+
+ struct CheckKeysState *cks;
+ struct TALER_TESTING_Command cmd;
+
+ cks = GNUNET_new (struct CheckKeysState);
+
+ cks->generation = generation;
+ cks->num_denom_keys = num_denom_keys;
+ cks->exchange = exchange;
+
+ cmd.cls = cks;
+ cmd.label = label;
+ cmd.run = &check_keys_run;
+ cmd.cleanup = &check_keys_cleanup;
+
+ return cmd;
+
+
+}
diff --git a/src/exchange-lib/testing_api_cmd_deposit.c b/src/exchange-lib/testing_api_cmd_deposit.c
new file mode 100644
index 000000000..f79515959
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_deposit.c
@@ -0,0 +1,448 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_deposit.c
+ * @brief command for testing /deposit.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+#include "taler_signatures.h"
+
+struct DepositState
+{
+
+ /**
+ * Amount to deposit.
+ */
+ const char *amount;
+
+ /**
+ * Reference to any command that is able to provide a coin.
+ */
+ const char *coin_reference;
+
+ /**
+ * If this @e coin_ref refers to an operation that generated
+ * an array of coins, this value determines which coin to pick.
+ */
+ unsigned int coin_index;
+
+ /**
+ * JSON string describing the merchant's "wire details".
+ */
+ char *wire_details;
+
+ /**
+ * JSON string describing what a proposal is about.
+ */
+ const char *contract_terms;
+
+ /**
+ * Relative time (to add to 'now') to compute the refund
+ * deadline. Zero for no refunds.
+ */
+ struct GNUNET_TIME_Relative refund_deadline;
+
+ /**
+ * Set (by the interpreter) to a fresh private key.
+ */
+ struct TALER_MerchantPrivateKeyP merchant_priv;
+
+ /**
+ * Deposit handle while operation is running.
+ */
+ struct TALER_EXCHANGE_DepositHandle *dh;
+
+ /**
+ * Expected HTTP response code.
+ */
+ unsigned int expected_response_code;
+
+ /**
+ * Interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * Exchange connection.
+ */
+ struct TALER_EXCHANGE_Handle *exchange;
+};
+
+/**
+ * Function called with the result of a /deposit operation.
+ *
+ * @param cls closure with the interpreter state
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for
+ * successful deposit; 0 if the exchange's reply is bogus
+ * (fails to follow the protocol)
+ * @param ec taler-specific error code, #TALER_EC_NONE on success
+ * @param exchange_pub public key the exchange used for signing
+ * @param obj the received JSON reply, should be kept as proof
+ * (and, in case of errors, be forwarded to the customer)
+ */
+static void
+deposit_cb (void *cls,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ const struct TALER_ExchangePublicKeyP *exchange_pub,
+ const json_t *obj)
+{
+ struct DepositState *ds = cls;
+
+ ds->dh = NULL;
+ if (ds->expected_response_code != http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u to command %s\n",
+ http_status,
+ ds->is->commands[ds->is->ip].label);
+ json_dumpf (obj, stderr, 0);
+ TALER_TESTING_interpreter_fail (ds->is);
+ return;
+ }
+ TALER_TESTING_interpreter_next (ds->is);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /wire one.
+ * @param i the interpreter state.
+ */
+void
+deposit_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct DepositState *ds = cls;
+ const struct TALER_TESTING_Command *coin_cmd;
+ struct TALER_TESTING_Command *this_cmd;
+ struct TALER_CoinSpendPrivateKeyP *coin_priv;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+ struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
+ struct TALER_DenominationSignature *denom_pub_sig;
+ struct TALER_CoinSpendSignatureP coin_sig;
+ struct GNUNET_TIME_Absolute refund_deadline;
+ struct GNUNET_TIME_Absolute wire_deadline;
+ struct GNUNET_TIME_Absolute timestamp;
+ struct GNUNET_CRYPTO_EddsaPrivateKey *merchant_priv;
+ struct TALER_MerchantPublicKeyP merchant_pub;
+ struct GNUNET_HashCode h_contract_terms;
+ json_t *contract_terms;
+ json_t *wire;
+ struct TALER_Amount amount;
+
+ ds->is = is;
+ this_cmd = &is->commands[is->ip];
+
+ GNUNET_assert (ds->coin_reference);
+ coin_cmd = TALER_TESTING_interpreter_lookup_command
+ (is,
+ ds->coin_reference);
+ if (NULL == coin_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ /* Fixme: do prefer "interpreter fail" over assertions,
+ * as the former takes care of shutting down processes too */
+ GNUNET_assert (NULL != coin_cmd);
+
+ GNUNET_assert (GNUNET_OK
+ == TALER_TESTING_get_trait_coin_priv (coin_cmd,
+ ds->coin_index,
+ &coin_priv));
+
+ GNUNET_assert (GNUNET_OK
+ == TALER_TESTING_get_trait_denom_pub (coin_cmd,
+ ds->coin_index,
+ &denom_pub));
+
+ GNUNET_assert (GNUNET_OK
+ == TALER_TESTING_get_trait_denom_sig (coin_cmd,
+ ds->coin_index,
+ &denom_pub_sig));
+ if (GNUNET_OK !=
+ TALER_string_to_amount (ds->amount,
+ &amount))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse amount `%s' at '%u/%s'\n",
+ ds->amount, is->ip, this_cmd->label);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ contract_terms = json_loads (ds->contract_terms,
+ JSON_REJECT_DUPLICATES,
+ NULL);
+ if (NULL == contract_terms)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse proposal data `%s' at %u/%s\n",
+ ds->contract_terms, is->ip, this_cmd->label);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ GNUNET_assert (GNUNET_OK ==
+ TALER_JSON_hash (contract_terms,
+ &h_contract_terms));
+ json_decref (contract_terms);
+
+ wire = json_loads (ds->wire_details,
+ JSON_REJECT_DUPLICATES,
+ NULL);
+ if (NULL == wire)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse wire details `%s' at %u/%s\n",
+ ds->wire_details,
+ is->ip,
+ this_cmd->label);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
+ &coin_pub.eddsa_pub);
+
+ merchant_priv = GNUNET_CRYPTO_eddsa_key_create ();
+ ds->merchant_priv.eddsa_priv = *merchant_priv;
+ GNUNET_free (merchant_priv);
+
+ if (0 != ds->refund_deadline.rel_value_us)
+ {
+ refund_deadline = GNUNET_TIME_relative_to_absolute
+ (ds->refund_deadline);
+ wire_deadline = GNUNET_TIME_relative_to_absolute
+ (GNUNET_TIME_relative_multiply
+ (ds->refund_deadline, 2));
+ }
+ else
+ {
+ refund_deadline = GNUNET_TIME_UNIT_ZERO_ABS;
+ wire_deadline = GNUNET_TIME_relative_to_absolute
+ (GNUNET_TIME_UNIT_ZERO);
+ }
+ GNUNET_CRYPTO_eddsa_key_get_public
+ (&ds->merchant_priv.eddsa_priv,
+ &merchant_pub.eddsa_pub);
+
+ timestamp = GNUNET_TIME_absolute_get ();
+ GNUNET_TIME_round_abs (&timestamp);
+ GNUNET_TIME_round_abs (&refund_deadline);
+ GNUNET_TIME_round_abs (&wire_deadline);
+
+ {
+ struct TALER_DepositRequestPS dr;
+
+ memset (&dr, 0, sizeof (dr));
+ dr.purpose.size = htonl
+ (sizeof (struct TALER_DepositRequestPS));
+ dr.purpose.purpose = htonl
+ (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
+ dr.h_contract_terms = h_contract_terms;
+ GNUNET_assert (GNUNET_OK == TALER_JSON_hash
+ (wire, &dr.h_wire));
+ dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
+ dr.refund_deadline = GNUNET_TIME_absolute_hton
+ (refund_deadline);
+ TALER_amount_hton (&dr.amount_with_fee, &amount);
+ TALER_amount_hton
+ (&dr.deposit_fee, &denom_pub->fee_deposit);
+ dr.merchant = merchant_pub;
+ dr.coin_pub = coin_pub;
+ GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign
+ (&coin_priv->eddsa_priv,
+ &dr.purpose,
+ &coin_sig.eddsa_signature));
+ }
+ ds->dh = TALER_EXCHANGE_deposit
+ (ds->exchange,
+ &amount,
+ wire_deadline,
+ wire,
+ &h_contract_terms,
+ &coin_pub,
+ denom_pub_sig,
+ &denom_pub->key,
+ timestamp,
+ &merchant_pub,
+ refund_deadline,
+ &coin_sig,
+ &deposit_cb,
+ ds);
+
+ if (NULL == ds->dh)
+ {
+ GNUNET_break (0);
+ json_decref (wire);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ json_decref (wire);
+ return;
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+deposit_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct DepositState *ds = cls;
+
+ if (NULL != ds->dh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ ds->is->ip,
+ cmd->label);
+ TALER_EXCHANGE_deposit_cancel (ds->dh);
+ ds->dh = NULL;
+ }
+
+ GNUNET_free (ds->wire_details);
+ GNUNET_free (ds);
+}
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ * to return in case there were multiple generated
+ * by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+deposit_traits (void *cls,
+ void **ret,
+ const char *trait,
+ unsigned int index)
+{
+ struct DepositState *ds = cls;
+ const struct TALER_TESTING_Command *coin_cmd;
+ /* Will point to coin cmd internals. */
+ struct TALER_CoinSpendPrivateKeyP *coin_spent_priv;
+
+ coin_cmd = TALER_TESTING_interpreter_lookup_command
+ (ds->is, ds->coin_reference);
+
+ if (NULL == coin_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (ds->is);
+ return GNUNET_NO;
+ }
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
+ (coin_cmd, ds->coin_index, &coin_spent_priv))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (ds->is);
+ return GNUNET_NO;
+ }
+
+ struct TALER_TESTING_Trait traits[] = {
+ TALER_TESTING_make_trait_coin_priv (0, coin_spent_priv),
+ TALER_TESTING_make_trait_wire_details (0, ds->wire_details),
+ TALER_TESTING_make_trait_contract_terms (0, ds->contract_terms),
+ TALER_TESTING_make_trait_peer_key
+ (0, &ds->merchant_priv.eddsa_priv),
+ TALER_TESTING_trait_end ()
+ };
+
+ return TALER_TESTING_get_trait (traits,
+ ret,
+ trait,
+ index);
+}
+
+/**
+ * Create a deposit command.
+ *
+ * @param label command label
+ * @param exchange exchange connection
+ * @param coin_reference reference to any operation that can
+ * provide a coin
+ * @param coin_index if @a withdraw_reference offers an array of
+ * coins, this parameter selects which one in that array.
+ * This value is currently ignored, as only one-coin
+ * withdrawals are implemented.
+ * @param wire_details bank details of the merchant performing the
+ * deposit
+ * @param contract_terms contract terms to be signed over by the
+ * coin
+ * @param refund_deadline refund deadline, zero means 'no refunds'
+ * @param amount how much is going to be deposited
+ * @param expected_response_code which HTTP status code we expect
+ * in the response
+ *
+ * @return the deposit command to be run by the interpreter
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_deposit
+ (const char *label,
+ struct TALER_EXCHANGE_Handle *exchange,
+ const char *coin_reference,
+ unsigned int coin_index,
+ char *wire_details,
+ const char *contract_terms,
+ struct GNUNET_TIME_Relative refund_deadline,
+ const char *amount,
+ unsigned int expected_response_code)
+{
+ struct TALER_TESTING_Command cmd;
+ struct DepositState *ds;
+
+ ds = GNUNET_new (struct DepositState);
+ ds->exchange = exchange;
+ ds->coin_reference = coin_reference;
+ ds->coin_index = coin_index;
+ ds->wire_details = wire_details;
+ ds->contract_terms = contract_terms;
+ ds->refund_deadline = refund_deadline;
+ ds->amount = amount;
+ ds->expected_response_code = expected_response_code;
+
+ cmd.cls = ds;
+ cmd.label = label;
+ cmd.run = &deposit_run;
+ cmd.cleanup = &deposit_cleanup;
+ cmd.traits = &deposit_traits;
+
+ return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_exec_aggregator.c b/src/exchange-lib/testing_api_cmd_exec_aggregator.c
new file mode 100644
index 000000000..8d2131b11
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_exec_aggregator.c
@@ -0,0 +1,162 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_exec_aggregator.c
+ * @brief run the taler-exchange-aggregator command
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+
+struct AggregatorState
+{
+
+ /**
+ * Process for the aggregator.
+ */
+ struct GNUNET_OS_Process *aggregator_proc;
+
+ /**
+ * Which configuration file should we pass to the process?
+ */
+ const char *config_filename;
+
+};
+
+
+/**
+ * Runs the command. Note that upon return, the interpreter
+ * will not automatically run the next command, as the command
+ * may continue asynchronously in other scheduler tasks. Thus,
+ * the command must ensure to eventually call
+ * #TALER_TESTING_interpreter_next() or
+ * #TALER_TESTING_interpreter_fail().
+ *
+ * @param is interpreter state
+ */
+static void
+aggregator_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct AggregatorState *as = cls;
+
+ as->aggregator_proc
+ = GNUNET_OS_start_process (GNUNET_NO,
+ GNUNET_OS_INHERIT_STD_ALL,
+ NULL, NULL, NULL,
+ "taler-exchange-aggregator",
+ "taler-exchange-aggregator",
+ "-c", as->config_filename,
+ "-t", /* exit when done */
+ NULL);
+ if (NULL == as->aggregator_proc)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ TALER_TESTING_wait_for_sigchld (is);
+}
+
+
+/**
+ * Clean up after the command. Run during forced termination
+ * (CTRL-C) or test failure or test success.
+ *
+ * @param cls closure
+ */
+static void
+aggregator_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct AggregatorState *as = cls;
+
+ if (NULL != as->aggregator_proc)
+ {
+ GNUNET_break (0 ==
+ GNUNET_OS_process_kill (as->aggregator_proc,
+ SIGKILL));
+ GNUNET_OS_process_wait (as->aggregator_proc);
+ GNUNET_OS_process_destroy (as->aggregator_proc);
+ as->aggregator_proc = NULL;
+ }
+ GNUNET_free (as);
+}
+
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ * to return in case there were multiple generated
+ * by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+aggregator_traits (void *cls,
+ void **ret,
+ const char *trait,
+ unsigned int index)
+{
+ struct AggregatorState *as = cls;
+ struct TALER_TESTING_Trait traits[] = {
+ TALER_TESTING_make_trait_process (0, &as->aggregator_proc),
+ TALER_TESTING_trait_end ()
+ };
+
+ return TALER_TESTING_get_trait (traits,
+ ret,
+ trait,
+ index);
+}
+
+
+/**
+ * Execute taler-exchange-wirewatch process.
+ *
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_exec_aggregator (const char *label,
+ const char *config_filename)
+{
+ struct TALER_TESTING_Command cmd;
+ struct AggregatorState *as;
+
+ as = GNUNET_new (struct AggregatorState);
+ as->config_filename = config_filename;
+ cmd.cls = as;
+ cmd.label = label;
+ cmd.run = &aggregator_run;
+ cmd.cleanup = &aggregator_cleanup;
+ cmd.traits = &aggregator_traits;
+ return cmd;
+}
+
+/* end of testing_api_cmd_exec_aggregator.c */
diff --git a/src/exchange-lib/testing_api_cmd_exec_auditor-sign.c b/src/exchange-lib/testing_api_cmd_exec_auditor-sign.c
new file mode 100644
index 000000000..bae0c076a
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_exec_auditor-sign.c
@@ -0,0 +1,221 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_exec_auditor-sign.c
+ * @brief run the taler-exchange-aggregator command
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+
+struct AuditorSignState
+{
+
+ /**
+ * Process for the "auditor sign" command.
+ */
+ struct GNUNET_OS_Process *auditor_sign_proc;
+
+ /**
+ * Which configuration file should we pass to the process?
+ */
+ const char *config_filename;
+
+};
+
+
+/**
+ * Runs the command. Note that upon return, the interpreter
+ * will not automatically run the next command, as the command
+ * may continue asynchronously in other scheduler tasks. Thus,
+ * the command must ensure to eventually call
+ * #TALER_TESTING_interpreter_next() or
+ * #TALER_TESTING_interpreter_fail().
+ *
+ * @param is interpreter state
+ */
+static void
+auditor_sign_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct AuditorSignState *ass = cls;
+
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ char *test_home_dir;
+ char *signed_keys_out;
+ char *exchange_master_pub;
+
+ cfg = GNUNET_CONFIGURATION_create ();
+ if (GNUNET_OK != GNUNET_CONFIGURATION_load
+ (cfg, ass->config_filename))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "paths",
+ "TALER_TEST_HOME",
+ &test_home_dir))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "paths",
+ "TALER_TEST_HOME");
+ GNUNET_CONFIGURATION_destroy (cfg);
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ GNUNET_asprintf (&signed_keys_out,
+ "%s/.local/share/taler/auditors/auditor.out",
+ test_home_dir);
+
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "exchange",
+ "MASTER_PUBLIC_KEY",
+ &exchange_master_pub))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "MASTER_PUBLIC_KEY");
+ GNUNET_CONFIGURATION_destroy (cfg);
+
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ GNUNET_CONFIGURATION_destroy (cfg);
+
+ ass->auditor_sign_proc = GNUNET_OS_start_process
+ (GNUNET_NO,
+ GNUNET_OS_INHERIT_STD_ALL,
+ NULL, NULL, NULL,
+ "taler-auditor-sign",
+ "taler-auditor-sign",
+ "-c", ass->config_filename,
+ "-u", "http://auditor/",
+ "-m", exchange_master_pub,
+ "-r", "auditor.in",
+ "-o", signed_keys_out,
+ NULL);
+
+ if (NULL == ass->auditor_sign_proc)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ TALER_TESTING_wait_for_sigchld (is);
+}
+
+
+/**
+ * Clean up after the command. Run during forced termination
+ * (CTRL-C) or test failure or test success.
+ *
+ * @param cls closure
+ */
+static void
+auditor_sign_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct AuditorSignState *ass = cls;
+
+ if (NULL != ass->auditor_sign_proc)
+ {
+ GNUNET_break (0 == GNUNET_OS_process_kill
+ (ass->auditor_sign_proc, SIGKILL));
+ GNUNET_OS_process_wait (ass->auditor_sign_proc);
+ GNUNET_OS_process_destroy (ass->auditor_sign_proc);
+ ass->auditor_sign_proc = NULL;
+ }
+ GNUNET_free (ass);
+}
+
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ * to return in case there were multiple generated
+ * by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+auditor_sign_traits (void *cls,
+ void **ret,
+ const char *trait,
+ unsigned int index)
+{
+ struct AuditorSignState *ass = cls;
+ struct TALER_TESTING_Trait traits[] = {
+ TALER_TESTING_make_trait_process (0, &ass->auditor_sign_proc),
+ TALER_TESTING_trait_end ()
+ };
+
+ return TALER_TESTING_get_trait (traits,
+ ret,
+ trait,
+ index);
+}
+
+
+/**
+ * Execute taler-auditor-sign process.
+ *
+ * @param label command label
+ * @param config_filename configuration filename
+ *
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_exec_auditor_sign (const char *label,
+ const char *config_filename)
+{
+ struct TALER_TESTING_Command cmd;
+ struct AuditorSignState *ass;
+
+ ass = GNUNET_new (struct AuditorSignState);
+ ass->config_filename = config_filename;
+ cmd.cls = ass;
+ cmd.label = label;
+ cmd.run = &auditor_sign_run;
+ cmd.cleanup = &auditor_sign_cleanup;
+ cmd.traits = &auditor_sign_traits;
+ return cmd;
+}
+
+/* end of testing_api_cmd_exec_auditor-sign.c */
diff --git a/src/exchange-lib/testing_api_cmd_exec_keyup.c b/src/exchange-lib/testing_api_cmd_exec_keyup.c
new file mode 100644
index 000000000..9398dd4fe
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_exec_keyup.c
@@ -0,0 +1,168 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_exec_keyup.c
+ * @brief run the taler-exchange-keyup command
+ * @author Marcello Stanisci
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+
+struct KeyupState
+{
+
+ /**
+ * Process for the "keyup" command.
+ */
+ struct GNUNET_OS_Process *keyup_proc;
+
+ /**
+ * Which configuration file should we pass to the process?
+ */
+ const char *config_filename;
+
+};
+
+
+/**
+ * Runs the command. Note that upon return, the interpreter
+ * will not automatically run the next command, as the command
+ * may continue asynchronously in other scheduler tasks. Thus,
+ * the command must ensure to eventually call
+ * #TALER_TESTING_interpreter_next() or
+ * #TALER_TESTING_interpreter_fail().
+ *
+ * @param is interpreter state
+ */
+static void
+keyup_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct KeyupState *ks = cls;
+
+ ks->keyup_proc = GNUNET_OS_start_process
+ (GNUNET_NO,
+ GNUNET_OS_INHERIT_STD_ALL,
+ NULL, NULL, NULL,
+ "taler-exchange-keyup",
+ "taler-exchange-keyup",
+ "-c", ks->config_filename,
+ "-o", "auditor.in",
+ NULL);
+
+ if (NULL == ks->keyup_proc)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ TALER_TESTING_wait_for_sigchld (is);
+}
+
+
+/**
+ * Clean up after the command. Run during forced termination
+ * (CTRL-C) or test failure or test success.
+ *
+ * @param cls closure
+ */
+static void
+keyup_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct KeyupState *ks = cls;
+
+ if (NULL != ks->keyup_proc)
+ {
+ GNUNET_break (0 ==
+ GNUNET_OS_process_kill (ks->keyup_proc,
+ SIGKILL));
+ GNUNET_OS_process_wait (ks->keyup_proc);
+ GNUNET_OS_process_destroy (ks->keyup_proc);
+ ks->keyup_proc = NULL;
+ }
+ GNUNET_free (ks);
+}
+
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ * to return in case there were multiple generated
+ * by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+keyup_traits (void *cls,
+ void **ret,
+ const char *trait,
+ unsigned int index)
+{
+ struct KeyupState *ks = cls;
+ struct TALER_TESTING_Trait traits[] = {
+ TALER_TESTING_make_trait_process (0, &ks->keyup_proc),
+ TALER_TESTING_trait_end ()
+ };
+
+ return TALER_TESTING_get_trait (traits,
+ ret,
+ trait,
+ index);
+}
+
+
+/**
+ * Execute taler-exchange-keyup process.
+ *
+ * @param label command label
+ * @param config_filename configuration filename
+ *
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_exec_keyup (const char *label,
+ const char *config_filename)
+{
+ struct TALER_TESTING_Command cmd;
+ struct KeyupState *ks;
+
+ ks = GNUNET_new (struct KeyupState);
+ ks->config_filename = config_filename;
+ cmd.cls = ks;
+ cmd.label = label;
+ cmd.run = &keyup_run;
+ cmd.cleanup = &keyup_cleanup;
+ cmd.traits = &keyup_traits;
+ return cmd;
+}
+
+/* end of testing_api_cmd_exec_keyup.c */
diff --git a/src/exchange-lib/testing_api_cmd_exec_wirewatch.c b/src/exchange-lib/testing_api_cmd_exec_wirewatch.c
index 7b95afbc4..107ac55a7 100644
--- a/src/exchange-lib/testing_api_cmd_exec_wirewatch.c
+++ b/src/exchange-lib/testing_api_cmd_exec_wirewatch.c
@@ -122,11 +122,11 @@ static int
wirewatch_traits (void *cls,
void **ret,
const char *trait,
- const char *selector)
+ unsigned int index)
{
struct WirewatchState *ws = cls;
struct TALER_TESTING_Trait traits[] = {
- TALER_TESTING_make_trait_process (NULL,
+ TALER_TESTING_make_trait_process (0,
&ws->wirewatch_proc),
TALER_TESTING_trait_end ()
};
@@ -134,7 +134,7 @@ wirewatch_traits (void *cls,
return TALER_TESTING_get_trait (traits,
ret,
trait,
- selector);
+ index);
}
diff --git a/src/exchange-lib/testing_api_cmd_fakebank_transfer.c b/src/exchange-lib/testing_api_cmd_fakebank_transfer.c
index 6b1a99dc3..dfbaaddea 100644
--- a/src/exchange-lib/testing_api_cmd_fakebank_transfer.c
+++ b/src/exchange-lib/testing_api_cmd_fakebank_transfer.c
@@ -2,18 +2,21 @@
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
+ TALER is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
+
/**
* @file exchange-lib/testing_api_cmd_fakebank_transfer.c
* @brief implementation of a fakebank wire transfer command
@@ -104,14 +107,17 @@ struct FakebankTransferState
/**
- * Function called upon completion of our /admin/add/incoming request.
+ * Function called upon completion of our /admin/add/incoming
+ * request.
*
* @param cls closure with the interpreter state
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
- * 0 if the exchange's reply is bogus (fails to follow the protocol)
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for
+ * successful status request; 0 if the exchange's reply is
+ * bogus (fails to follow the protocol)
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param serial_id unique ID of the wire transfer
- * @param full_response full response from the exchange (for logging, in case of errors)
+ * @param full_response full response from the exchange (for
+ * logging, in case of errors)
*/
static void
add_incoming_cb (void *cls,
@@ -167,8 +173,8 @@ fakebank_transfer_run (void *cls,
const struct TALER_TESTING_Command *ref;
struct TALER_ReservePrivateKeyP *reserve_priv;
- ref = TALER_TESTING_interpreter_lookup_command (is,
- fts->reserve_reference);
+ ref = TALER_TESTING_interpreter_lookup_command
+ (is, fts->reserve_reference);
if (NULL == ref)
{
GNUNET_break (0);
@@ -177,7 +183,7 @@ fakebank_transfer_run (void *cls,
}
if (GNUNET_OK !=
TALER_TESTING_get_trait_reserve_priv (ref,
- NULL,
+ 0,
&reserve_priv))
{
GNUNET_break (0);
@@ -193,27 +199,27 @@ fakebank_transfer_run (void *cls,
fts->reserve_priv.eddsa_priv = *priv;
GNUNET_free (priv);
}
- GNUNET_CRYPTO_eddsa_key_get_public (&fts->reserve_priv.eddsa_priv,
- &reserve_pub.eddsa_pub);
- subject = GNUNET_STRINGS_data_to_string_alloc (&reserve_pub,
- sizeof (reserve_pub));
+ GNUNET_CRYPTO_eddsa_key_get_public
+ (&fts->reserve_priv.eddsa_priv, &reserve_pub.eddsa_pub);
+ subject = GNUNET_STRINGS_data_to_string_alloc
+ (&reserve_pub, sizeof (reserve_pub));
}
auth.method = TALER_BANK_AUTH_BASIC;
auth.details.basic.username = (char *) fts->auth_username;
auth.details.basic.password = (char *) fts->auth_password;
fts->is = is;
- fts->aih
- = TALER_BANK_admin_add_incoming (TALER_TESTING_interpreter_get_context (is),
- fts->bank_url,
- &auth,
- "https://exchange.com/", /* exchange URL: FIXME */
- subject,
- &fts->amount,
- fts->debit_account_no,
- fts->credit_account_no,
- &add_incoming_cb,
- fts);
+ fts->aih = TALER_BANK_admin_add_incoming
+ (TALER_TESTING_interpreter_get_context (is),
+ fts->bank_url,
+ &auth,
+ "https://exchange.com/", /* exchange URL: FIXME */
+ subject,
+ &fts->amount,
+ fts->debit_account_no,
+ fts->credit_account_no,
+ &add_incoming_cb,
+ fts);
GNUNET_free (subject);
if (NULL == fts->aih)
{
@@ -263,11 +269,11 @@ static int
fakebank_transfer_traits (void *cls,
void **ret,
const char *trait,
- const char *selector)
+ unsigned int index)
{
struct FakebankTransferState *fts = cls;
struct TALER_TESTING_Trait traits[] = {
- TALER_TESTING_make_trait_reserve_priv (NULL,
+ TALER_TESTING_make_trait_reserve_priv (0,
&fts->reserve_priv),
TALER_TESTING_trait_end ()
};
@@ -275,12 +281,13 @@ fakebank_transfer_traits (void *cls,
if (NULL != fts->subject)
{
GNUNET_break (0);
- return GNUNET_SYSERR; /* we do NOT create a reserve private key */
+ /* we do NOT create a reserve private key */
+ return GNUNET_SYSERR;
}
return TALER_TESTING_get_trait (traits,
ret,
trait,
- selector);
+ index);
}
@@ -330,14 +337,15 @@ TALER_TESTING_cmd_fakebank_transfer (const char *label,
*
*/
struct TALER_TESTING_Command
-TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label,
- const char *amount,
- const char *bank_url,
- uint64_t debit_account_no,
- uint64_t credit_account_no,
- const char *auth_username,
- const char *auth_password,
- const char *subject)
+TALER_TESTING_cmd_fakebank_transfer_with_subject
+ (const char *label,
+ const char *amount,
+ const char *bank_url,
+ uint64_t debit_account_no,
+ uint64_t credit_account_no,
+ const char *auth_username,
+ const char *auth_password,
+ const char *subject)
{
struct TALER_TESTING_Command cmd;
struct FakebankTransferState *fts;
@@ -373,14 +381,15 @@ TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label,
*
*/
struct TALER_TESTING_Command
-TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label,
- const char *amount,
- const char *bank_url,
- uint64_t debit_account_no,
- uint64_t credit_account_no,
- const char *auth_username,
- const char *auth_password,
- const char *ref)
+TALER_TESTING_cmd_fakebank_transfer_with_ref
+ (const char *label,
+ const char *amount,
+ const char *bank_url,
+ uint64_t debit_account_no,
+ uint64_t credit_account_no,
+ const char *auth_username,
+ const char *auth_password,
+ const char *ref)
{
struct TALER_TESTING_Command cmd;
struct FakebankTransferState *fts;
diff --git a/src/exchange-lib/testing_api_cmd_payback.c b/src/exchange-lib/testing_api_cmd_payback.c
new file mode 100644
index 000000000..6c791e561
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_payback.c
@@ -0,0 +1,491 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3, or
+ (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange/testing_api_cmd_payback.c
+ * @brief Implement the /revoke and /payback test commands.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+
+struct RevokeState
+{
+ /**
+ * Expected HTTP status code.
+ */
+ unsigned int expected_response_code;
+
+ /**
+ * Command that offers a denomination to revoke.
+ */
+ const char *coin_reference;
+
+ /**
+ * The interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * The revoke process handle.
+ */
+ struct GNUNET_OS_Process *revoke_proc;
+
+ /**
+ * Configuration filename.
+ */
+ const char *config_filename;
+
+ /**
+ * Encoding of the denomination (to revoke) public key hash.
+ */
+ char *dhks;
+
+};
+
+struct PaybackState
+{
+ /**
+ * Expected HTTP status code.
+ */
+ unsigned int expected_response_code;
+
+ /**
+ * Command that offers a reserve private key plus a
+ * coin to be paid back.
+ */
+ const char *coin_reference;
+
+ /**
+ * The interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * Amount expected to be paid back.
+ */
+ const char *amount;
+
+ /**
+ * Connection to the exchange.
+ */
+ struct TALER_EXCHANGE_Handle *exchange;
+
+ /**
+ * Handle to the ongoing operation.
+ */
+ struct TALER_EXCHANGE_PaybackHandle *ph;
+
+};
+
+/**
+ * Check the result of the payback request.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for
+ * successful status request; 0 if the exchange's reply is
+ * bogus (fails to follow the protocol)
+ * @param ec taler-specific error code, #TALER_EC_NONE on success
+ * @param amount amount the exchange will wire back for this coin
+ * @param timestamp what time did the exchange receive the
+ * /payback request
+ * @param reserve_pub public key of the reserve receiving the
+ * payback
+ * @param full_response full response from the exchange (for
+ * logging, in case of errors)
+ */
+static void
+payback_cb (void *cls,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ const struct TALER_Amount *amount,
+ struct GNUNET_TIME_Absolute timestamp,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const json_t *full_response)
+{
+
+ struct PaybackState *ps = cls;
+ struct TALER_TESTING_Interpreter *is = ps->is;
+ struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
+ const struct TALER_TESTING_Command *reserve_cmd;
+ struct TALER_ReservePrivateKeyP *reserve_priv;
+ struct TALER_ReservePublicKeyP rp;
+ struct TALER_Amount expected_amount;
+
+ ps->ph = NULL;
+ if (ps->expected_response_code != http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u to command %s\n",
+ http_status,
+ cmd->label);
+ json_dumpf (full_response, stderr, 0);
+ fprintf (stderr, "\n");
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ reserve_cmd = TALER_TESTING_interpreter_lookup_command
+ (is, ps->coin_reference);
+
+ if (NULL == reserve_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_reserve_priv
+ (reserve_cmd, 0, &reserve_priv))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
+ &rp.eddsa_pub);
+
+ switch (http_status)
+ {
+ case MHD_HTTP_OK:
+ if (GNUNET_OK != TALER_string_to_amount
+ (ps->amount, &expected_amount))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ if (0 != TALER_amount_cmp (amount, &expected_amount))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Total amount missmatch to command %s\n",
+ cmd->label);
+ json_dumpf (full_response, stderr, 0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ if (0 != memcmp (reserve_pub, &rp,
+ sizeof (struct TALER_ReservePublicKeyP)))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unmanaged HTTP status code.\n");
+ break;
+ }
+ TALER_TESTING_interpreter_next (is);
+}
+
+/**
+ * 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
+payback_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct PaybackState *ps = cls;
+ const struct TALER_TESTING_Command *coin_cmd;
+ struct TALER_CoinSpendPrivateKeyP *coin_priv;
+ struct TALER_DenominationBlindingKeyP *blinding_key;
+ struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
+ struct TALER_DenominationSignature *coin_sig;
+ struct TALER_PlanchetSecretsP planchet;
+
+ ps->is = is;
+ ps->exchange = is->exchange;
+ coin_cmd = TALER_TESTING_interpreter_lookup_command
+ (is, ps->coin_reference);
+
+ if (NULL == coin_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
+ (coin_cmd, 0, &coin_priv))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_blinding_key
+ (coin_cmd, 0, &blinding_key))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ planchet.coin_priv = *coin_priv;
+ planchet.blinding_key = *blinding_key;
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub
+ (coin_cmd, 0, &denom_pub))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_denom_sig
+ (coin_cmd, 0, &coin_sig))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Trying to get '%s..' paid back\n",
+ TALER_B2S (&denom_pub->h_key));
+
+ ps->ph = TALER_EXCHANGE_payback (ps->exchange,
+ denom_pub,
+ coin_sig,
+ &planchet,
+ payback_cb,
+ ps);
+ GNUNET_assert (NULL != ps->ph);
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+revoke_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+
+ struct RevokeState *rs = cls;
+
+ if (NULL != rs->revoke_proc)
+ {
+ GNUNET_break (0 == GNUNET_OS_process_kill
+ (rs->revoke_proc, SIGKILL));
+ GNUNET_OS_process_wait (rs->revoke_proc);
+ GNUNET_OS_process_destroy (rs->revoke_proc);
+ rs->revoke_proc = NULL;
+ }
+
+ GNUNET_free (rs->dhks);
+ GNUNET_free (rs);
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+payback_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct PaybackState *ps = cls;
+ if (NULL != ps->ph)
+ {
+ TALER_EXCHANGE_payback_cancel (ps->ph);
+ ps->ph = NULL;
+ }
+ GNUNET_free (ps);
+}
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ * to return in case there were multiple generated
+ * by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+revoke_traits (void *cls,
+ void **ret,
+ const char *trait,
+ unsigned int index)
+{
+
+ struct RevokeState *rs = cls;
+
+ struct TALER_TESTING_Trait traits[] = {
+ /* Needed by the handler which waits the proc'
+ * death and calls the next command */
+ TALER_TESTING_make_trait_process (0, &rs->revoke_proc),
+ TALER_TESTING_trait_end ()
+ };
+
+ return TALER_TESTING_get_trait (traits,
+ ret,
+ trait,
+ index);
+}
+
+/**
+ * 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
+revoke_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct RevokeState *rs = cls;
+ const struct TALER_TESTING_Command *coin_cmd;
+ struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
+
+ rs->is = is;
+ /* Get denom pub from trait */
+ coin_cmd = TALER_TESTING_interpreter_lookup_command
+ (is, rs->coin_reference);
+
+ if (NULL == coin_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_denom_pub
+ (coin_cmd, 0, &denom_pub));
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Trying to revoke denom '%s..'\n",
+ TALER_B2S (&denom_pub->h_key));
+
+ rs->dhks = GNUNET_STRINGS_data_to_string_alloc
+ (&denom_pub->h_key, sizeof (struct GNUNET_HashCode));
+
+ rs->revoke_proc = GNUNET_OS_start_process
+ (GNUNET_NO,
+ GNUNET_OS_INHERIT_STD_ALL,
+ NULL, NULL, NULL,
+ "taler-exchange-keyup",
+ "taler-exchange-keyup",
+ "-c", rs->config_filename,
+ "-r", rs->dhks,
+ NULL);
+
+
+ if (NULL == rs->revoke_proc)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Revoke is ongoing..\n");
+
+ is->reload_keys = GNUNET_OK;
+ TALER_TESTING_wait_for_sigchld (is);
+}
+
+
+/**
+ * Make a /payback command.
+ *
+ * @param label the command label
+ * @param expected_response_code expected HTTP status code
+ * @param coin_reference reference to any command which
+ * offers a reserve private key
+ * @param amount denomination to pay back.
+ *
+ * @return a /revoke command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_payback (const char *label,
+ unsigned int expected_response_code,
+ const char *coin_reference,
+ const char *amount)
+{
+ struct PaybackState *ps;
+ struct TALER_TESTING_Command cmd;
+
+ ps = GNUNET_new (struct PaybackState);
+ ps->expected_response_code = expected_response_code;
+ ps->coin_reference = coin_reference;
+ ps->amount = amount;
+
+ cmd.cls = ps;
+ cmd.label = label;
+ cmd.run = &payback_run;
+ cmd.cleanup = &payback_cleanup;
+
+ return cmd;
+}
+
+/**
+ * Make a /revoke command.
+ *
+ * @param label the command label
+ * @param expected_response_code expected HTTP status code
+ * @param coin_reference reference to any command which offers
+ * a coin trait
+ * @param config_filename configuration file name.
+ *
+ * @return a /revoke command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_revoke (const char *label,
+ unsigned int expected_response_code,
+ const char *coin_reference,
+ const char *config_filename)
+{
+
+ struct RevokeState *rs;
+ struct TALER_TESTING_Command cmd;
+
+ rs = GNUNET_new (struct RevokeState);
+ rs->expected_response_code = expected_response_code;
+ rs->coin_reference = coin_reference;
+ rs->config_filename = config_filename;
+
+ cmd.cls = rs;
+ cmd.label = label;
+ cmd.run = &revoke_run;
+ cmd.cleanup = &revoke_cleanup;
+ cmd.traits = &revoke_traits;
+
+ return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_refresh.c b/src/exchange-lib/testing_api_cmd_refresh.c
new file mode 100644
index 000000000..3e58552ba
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_refresh.c
@@ -0,0 +1,993 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_refresh.c
+ * @brief commands for testing all "refresh" features.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+#include "taler_signatures.h"
+
+/**
+ * Structure specifying details about a coin to be melted.
+ * Used in a NULL-terminated array as part of command
+ * specification.
+ */
+struct MeltDetails
+{
+
+ /**
+ * Amount to melt (including fee).
+ */
+ const char *amount;
+
+ /**
+ * Reference to reserve_withdraw operations for coin to
+ * be used for the /refresh/melt operation.
+ */
+ const char *coin_reference;
+};
+
+
+/**
+ * State for a "refresh melt" operation.
+ */
+struct RefreshMeltState
+{
+ /**
+ * Fixme: figure out this data purpose.
+ */
+ const char *amount;
+
+ /**
+ * Information about coins to be melted.
+ */
+ struct MeltDetails melted_coin;
+
+ /**
+ * Data used in the refresh operation.
+ */
+ char *refresh_data;
+
+ /**
+ * Number of bytes in @e refresh_data.
+ */
+ size_t refresh_data_length;
+
+ /**
+ * Reference to a previous melt command.
+ */
+ const char *melt_reference;
+
+ /**
+ * Melt handle while operation is running.
+ */
+ struct TALER_EXCHANGE_RefreshMeltHandle *rmh;
+
+ /**
+ * Connection to the exchange.
+ */
+ struct TALER_EXCHANGE_Handle *exchange;
+
+ /**
+ * Interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * Expected HTTP response code.
+ */
+ unsigned int expected_response_code;
+
+ /**
+ * Array of the public keys corresponding to
+ * the @e fresh_amounts, set by the interpreter.
+ */
+ struct TALER_EXCHANGE_DenomPublicKey *fresh_pks;
+
+ /**
+ * Set by the interpreter (upon completion) to the
+ * noreveal index selected by the exchange.
+ */
+ uint16_t noreveal_index;
+};
+
+/**
+ * State for a "refresh reveal" operation.
+ */
+struct RefreshRevealState
+{
+ /**
+ * Link to a "refresh melt" command.
+ */
+ const char *melt_reference;
+
+ /**
+ * Reveal handle while operation is running.
+ */
+ struct TALER_EXCHANGE_RefreshRevealHandle *rrh;
+
+ /**
+ * Number of fresh coins withdrawn, set by the interpreter.
+ * Length of the @e fresh_coins array.
+ */
+ unsigned int num_fresh_coins;
+
+ /**
+ * Information about coins withdrawn, set by the interpreter.
+ */
+ struct FreshCoin *fresh_coins;
+
+ /**
+ * Connection to the exchange.
+ */
+ struct TALER_EXCHANGE_Handle *exchange;
+
+ /**
+ * Interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * Expected HTTP response code.
+ */
+ unsigned int expected_response_code;
+};
+
+/**
+ * State for a "refresh link" operation.
+ */
+struct RefreshLinkState
+{
+ /**
+ * Link to a "refresh reveal" command.
+ */
+ const char *reveal_reference;
+
+ /**
+ * Link handle while operation is running.
+ */
+ struct TALER_EXCHANGE_RefreshLinkHandle *rlh;
+
+ /**
+ * Connection to the exchange.
+ */
+ struct TALER_EXCHANGE_Handle *exchange;
+
+ /**
+ * Interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * Expected HTTP response code.
+ */
+ unsigned int expected_response_code;
+};
+
+
+/**
+ * Function called with the result of the /refresh/reveal operation.
+ *
+ * @param cls closure with the interpreter state
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for
+ * successful status request. 0 if the exchange's reply is
+ * bogus (fails to follow the protocol)
+ * @param ec taler-specific error code, #TALER_EC_NONE on success
+ * @param num_coins number of fresh coins created, length of the
+ * @a sigs and @a coin_privs arrays, 0 if the operation
+ * failed
+ * @param coin_privs array of @a num_coins private keys for the
+ * coins that were created, NULL on error
+ * @param sigs array of signature over @a num_coins coins, NULL on
+ * error
+ * @param full_response full response from the exchange (for
+ * logging, in case of errors)
+ */
+static void
+reveal_cb (void *cls,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ unsigned int num_coins,
+ const struct TALER_CoinSpendPrivateKeyP *coin_privs,
+ const struct TALER_DenominationSignature *sigs,
+ const json_t *full_response)
+{
+
+ struct RefreshRevealState *rrs = cls;
+ const struct TALER_TESTING_Command *melt_cmd;
+
+ rrs->rrh = NULL;
+ if (rrs->expected_response_code != http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u to command %s\n",
+ http_status,
+ rrs->is->commands[rrs->is->ip].label);
+ json_dumpf (full_response, stderr, 0);
+ TALER_TESTING_interpreter_fail (rrs->is);
+ return;
+ }
+ melt_cmd = TALER_TESTING_interpreter_lookup_command
+ (rrs->is, rrs->melt_reference);
+ if (NULL == melt_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rrs->is);
+ return;
+ }
+ rrs->num_fresh_coins = num_coins;
+ switch (http_status)
+ {
+ case MHD_HTTP_OK:
+ rrs->fresh_coins = GNUNET_new_array
+ (num_coins, struct FreshCoin);
+
+ struct TALER_EXCHANGE_DenomPublicKey *fresh_pks;
+ unsigned int i;
+ if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub
+ (melt_cmd, 0, &fresh_pks))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rrs->is);
+ return;
+ }
+
+ for (i=0; i<num_coins; i++)
+ {
+ struct FreshCoin *fc = &rrs->fresh_coins[i];
+
+ fc->pk = &fresh_pks[i];
+ fc->coin_priv = coin_privs[i];
+ fc->sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup
+ (sigs[i].rsa_signature);
+ }
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unknown HTTP status %d\n",
+ http_status);
+ }
+ TALER_TESTING_interpreter_next (rrs->is);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure.
+ * @param cmd the command to execute, a /wire one.
+ * @param is the interpreter state.
+ */
+void
+refresh_reveal_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct RefreshRevealState *rrs = cls;
+ struct RefreshMeltState *rms;
+ const struct TALER_TESTING_Command *melt_cmd;
+
+ rrs->is = is;
+ melt_cmd = TALER_TESTING_interpreter_lookup_command
+ (is, rrs->melt_reference);
+
+ if (NULL == melt_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rrs->is);
+ return;
+ }
+ rms = melt_cmd->cls;
+ rrs->rrh = TALER_EXCHANGE_refresh_reveal
+ (rrs->exchange,
+ rms->refresh_data_length,
+ rms->refresh_data,
+ rms->noreveal_index,
+ &reveal_cb, rrs);
+
+ if (NULL == rrs->rrh)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+}
+
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+refresh_reveal_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct RefreshRevealState *rrs = cls;
+
+ if (NULL != rrs->rrh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ rrs->is->ip,
+ cmd->label);
+
+ TALER_EXCHANGE_refresh_reveal_cancel (rrs->rrh);
+ rrs->rrh = NULL;
+ }
+
+ { /* Not sure why block-ing this */
+ unsigned int j;
+
+ for (j=0; j < rrs->num_fresh_coins; j++)
+ GNUNET_CRYPTO_rsa_signature_free
+ (rrs->fresh_coins[j].sig.rsa_signature);
+ }
+
+ GNUNET_free_non_null (rrs->fresh_coins);
+ rrs->fresh_coins = NULL;
+ rrs->num_fresh_coins = 0;
+}
+
+
+/**
+ * Function called with the result of a /refresh/link operation.
+ *
+ * @param cls closure with the interpreter state
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for
+ * successful status request 0 if the exchange's reply is
+ * bogus (fails to follow the protocol)
+ * @param ec taler-specific error code, #TALER_EC_NONE on success
+ * @param num_coins number of fresh coins created, length of the
+ * @a sigs and @a coin_privs arrays, 0 if the operation
+ * failed
+ * @param coin_privs array of @a num_coins private keys for the
+ * coins that were created, NULL on error
+ * @param sigs array of signature over @a num_coins coins, NULL on
+ * error
+ * @param pubs array of public keys for the @a sigs, NULL on error
+ * @param full_response full response from the exchange (for
+ * logging, in case of errors)
+ */
+static void
+link_cb (void *cls,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ unsigned int num_coins,
+ const struct TALER_CoinSpendPrivateKeyP *coin_privs,
+ const struct TALER_DenominationSignature *sigs,
+ const struct TALER_DenominationPublicKey *pubs,
+ const json_t *full_response)
+{
+
+ struct RefreshLinkState *rls = cls;
+ const struct TALER_TESTING_Command *reveal_cmd;
+ struct TALER_TESTING_Command *link_cmd
+ = &rls->is->commands[rls->is->ip];
+ unsigned int found;
+ unsigned int *num_fresh_coins;
+
+ rls->rlh = NULL;
+ if (rls->expected_response_code != http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u to command %s\n",
+ http_status,
+ link_cmd->label);
+ json_dumpf (full_response, stderr, 0);
+ TALER_TESTING_interpreter_fail (rls->is);
+ return;
+ }
+ reveal_cmd = TALER_TESTING_interpreter_lookup_command
+ (rls->is, rls->reveal_reference);
+
+ if (NULL == reveal_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rls->is);
+ return;
+ }
+
+ switch (http_status)
+ {
+ case MHD_HTTP_OK:
+ /* check that number of coins returned matches */
+ if (GNUNET_OK != TALER_TESTING_get_trait_uint
+ (reveal_cmd, 0, &num_fresh_coins))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rls->is);
+ return;
+ }
+ if (num_coins != *num_fresh_coins)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected number of fresh coins: %d vs %d\n",
+ num_coins, *num_fresh_coins);
+ TALER_TESTING_interpreter_fail (rls->is);
+ return;
+ }
+ /* check that the coins match */
+ for (unsigned int i=0;i<num_coins;i++)
+ for (unsigned int j=i+1;j<num_coins;j++)
+ if (0 == memcmp
+ (&coin_privs[i], &coin_privs[j],
+ sizeof (struct TALER_CoinSpendPrivateKeyP)))
+ GNUNET_break (0);
+ /* Note: coins might be legitimately permutated in here... */
+ found = 0;
+
+ /* Will point to the pointer inside the cmd state. */
+ struct FreshCoin *fc = NULL;
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_fresh_coins
+ (reveal_cmd, 0, &fc))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rls->is);
+ return;
+ }
+
+ for (unsigned int i=0;i<num_coins;i++)
+ for (unsigned int j=0;j<num_coins;j++)
+ {
+ if ( (0 == memcmp
+ (&coin_privs[i], &fc[i].coin_priv,
+ sizeof (struct TALER_CoinSpendPrivateKeyP))) &&
+ (0 == GNUNET_CRYPTO_rsa_signature_cmp
+ (fc[i].sig.rsa_signature,
+ sigs[i].rsa_signature)) &&
+ (0 == GNUNET_CRYPTO_rsa_public_key_cmp
+ (fc[i].pk->key.rsa_public_key,
+ pubs[i].rsa_public_key)) )
+ {
+ found++;
+ break;
+ }
+ }
+ if (found != num_coins)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Only %u/%u coins match expectations\n",
+ found, num_coins);
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rls->is);
+ return;
+ }
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unknown HTTP response code %u.\n",
+ http_status);
+ }
+ TALER_TESTING_interpreter_next (rls->is);
+}
+
+
+/**
+ * Run the command.
+ *
+ * @param cls closure
+ * @param cmd the command to execute, a /wire one.
+ * @param i the interpreter state.
+ */
+void
+refresh_link_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+
+ struct RefreshLinkState *rls = cls;
+ struct RefreshRevealState *rrs;
+ struct RefreshMeltState *rms;
+
+ const struct TALER_TESTING_Command *reveal_cmd;
+ const struct TALER_TESTING_Command *melt_cmd;
+ const struct TALER_TESTING_Command *coin_cmd;
+ rls->is = is;
+
+ reveal_cmd = TALER_TESTING_interpreter_lookup_command
+ (rls->is, rls->reveal_reference);
+
+ if (NULL == reveal_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rls->is);
+ return;
+ }
+ rrs = reveal_cmd->cls;
+ melt_cmd = TALER_TESTING_interpreter_lookup_command
+ (rls->is, rrs->melt_reference);
+
+ if (NULL == melt_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rls->is);
+ return;
+ }
+
+ /* find reserve_withdraw command */
+ {
+ const struct MeltDetails *md;
+
+ rms = melt_cmd->cls;
+ md = &rms->melted_coin;
+ coin_cmd = TALER_TESTING_interpreter_lookup_command
+ (rls->is, md->coin_reference);
+ if (NULL == coin_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rls->is);
+ return;
+ }
+ }
+
+ struct TALER_CoinSpendPrivateKeyP *coin_priv;
+ if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
+ (coin_cmd, 0, &coin_priv))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rls->is);
+ return;
+ }
+
+ /* finally, use private key from withdraw sign command */
+ rls->rlh = TALER_EXCHANGE_refresh_link
+ (rls->exchange, coin_priv, &link_cb, rls);
+
+ if (NULL == rls->rlh)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rls->is);
+ return;
+ }
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure
+ * @param cmd the command which is being cleaned up.
+ */
+void
+refresh_link_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct RefreshLinkState *rls = cls;
+
+ if (NULL != rls->rlh)
+ {
+
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ rls->is->ip,
+ cmd->label);
+ TALER_EXCHANGE_refresh_link_cancel (rls->rlh);
+ rls->rlh = NULL;
+ }
+}
+
+
+/**
+ * Function called with the result of the /refresh/melt operation.
+ *
+ * @param cls closure with the interpreter state
+ * @param http_status HTTP response code, never #MHD_HTTP_OK (200)
+ * as for successful intermediate response this callback is
+ * skipped. 0 if the exchange's reply is bogus (fails to
+ * follow the protocol)
+ * @param ec taler-specific error code, #TALER_EC_NONE on success
+ * @param noreveal_index choice by the exchange in the
+ * cut-and-choose protocol, UINT16_MAX on error
+ * @param exchange_pub public key the exchange used for signing
+ * @param full_response full response from the exchange (for
+ * logging, in case of errors)
+ */
+static void
+melt_cb (void *cls,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ uint32_t noreveal_index,
+ const struct TALER_ExchangePublicKeyP *exchange_pub,
+ const json_t *full_response)
+{
+ struct RefreshMeltState *rms = cls;
+
+ rms->rmh = NULL;
+ if (rms->expected_response_code != http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u to command %s\n",
+ http_status,
+ rms->is->commands[rms->is->ip].label);
+ json_dumpf (full_response, stderr, 0);
+ TALER_TESTING_interpreter_fail (rms->is);
+ return;
+ }
+ rms->noreveal_index = noreveal_index;
+ TALER_TESTING_interpreter_next (rms->is);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /wire one.
+ * @param i the interpreter state.
+ */
+void
+refresh_melt_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct RefreshMeltState *rms = cls;
+ unsigned int num_fresh_coins;
+ const struct TALER_TESTING_Command *coin_command;
+ const char *melt_fresh_amounts[] = {
+ /* with 0.01 withdraw fees (except for 1ct coins),
+ this totals up to exactly EUR:3.97, and with
+ the 0.03 refresh fee, to EUR:4.0 */
+ "EUR:1", "EUR:1", "EUR:1", "EUR:0.1", "EUR:0.1", "EUR:0.1",
+ "EUR:0.1", "EUR:0.1", "EUR:0.1", "EUR:0.1", "EUR:0.1",
+ "EUR:0.01", "EUR:0.01", "EUR:0.01", "EUR:0.01", "EUR:0.01",
+ "EUR:0.01", NULL};
+ const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk;
+
+ rms->is = is;
+ rms->noreveal_index = UINT16_MAX;
+ for (num_fresh_coins=0;
+ NULL != melt_fresh_amounts[num_fresh_coins];
+ num_fresh_coins++) ;
+
+ rms->fresh_pks = GNUNET_new_array
+ (num_fresh_coins,
+ struct TALER_EXCHANGE_DenomPublicKey);
+ {
+ struct TALER_CoinSpendPrivateKeyP *melt_priv;
+ struct TALER_Amount melt_amount;
+ struct TALER_Amount fresh_amount;
+ struct TALER_DenominationSignature *melt_sig;
+ struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub;
+ unsigned int i;
+
+ const struct MeltDetails *md = &rms->melted_coin;
+ if (NULL == (coin_command
+ = TALER_TESTING_interpreter_lookup_command
+ (is, md->coin_reference)))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rms->is);
+ return;
+ }
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
+ (coin_command, 0, &melt_priv))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rms->is);
+ return;
+ }
+
+ if (GNUNET_OK !=
+ TALER_string_to_amount (md->amount,
+ &melt_amount))
+ {
+ GNUNET_break (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse amount `%s' at %u\n",
+ md->amount,
+ is->ip);
+ TALER_TESTING_interpreter_fail (rms->is);
+ return;
+ }
+ if (GNUNET_OK != TALER_TESTING_get_trait_denom_sig
+ (coin_command, 0, &melt_sig))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rms->is);
+ return;
+ }
+ if (GNUNET_OK != TALER_TESTING_get_trait_denom_pub
+ (coin_command, 0, &melt_denom_pub))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rms->is);
+ return;
+ }
+
+ for (i=0;i<num_fresh_coins;i++)
+ {
+ if (GNUNET_OK != TALER_string_to_amount
+ (melt_fresh_amounts[i], &fresh_amount))
+ {
+ GNUNET_break (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse amount `%s' at index %u\n",
+ melt_fresh_amounts[i], i);
+ TALER_TESTING_interpreter_fail (rms->is);
+ return;
+ }
+ fresh_pk = TALER_TESTING_find_pk
+ (TALER_EXCHANGE_get_keys (rms->exchange), &fresh_amount);
+ if (NULL == fresh_pk)
+ {
+ GNUNET_break (0);
+ /* Subroutine logs specific error */
+ TALER_TESTING_interpreter_fail (rms->is);
+ return;
+ }
+
+ rms->fresh_pks[i] = *fresh_pk;
+ }
+ rms->refresh_data = TALER_EXCHANGE_refresh_prepare
+ (melt_priv, &melt_amount, melt_sig, melt_denom_pub,
+ GNUNET_YES, num_fresh_coins, rms->fresh_pks,
+ &rms->refresh_data_length);
+
+ if (NULL == rms->refresh_data)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rms->is);
+ return;
+ }
+ rms->rmh = TALER_EXCHANGE_refresh_melt
+ (rms->exchange, rms->refresh_data_length,
+ rms->refresh_data, &melt_cb, rms);
+
+ if (NULL == rms->rmh)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (rms->is);
+ return;
+ }
+ }
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct RefreshMeltState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+refresh_melt_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct RefreshMeltState *rms = cls;
+
+ if (NULL != rms->rmh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ rms->is->ip, rms->is->commands[rms->is->ip].label);
+ TALER_EXCHANGE_refresh_melt_cancel (rms->rmh);
+ rms->rmh = NULL;
+ }
+ GNUNET_free_non_null (rms->fresh_pks);
+ rms->fresh_pks = NULL;
+ GNUNET_free_non_null (rms->refresh_data);
+ rms->refresh_data = NULL;
+ rms->refresh_data_length = 0;
+}
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ * to return in case there were multiple generated
+ * by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+refresh_melt_traits (void *cls,
+ void **ret,
+ const char *trait,
+ unsigned int index)
+{
+ struct RefreshMeltState *rms = cls;
+
+ struct TALER_TESTING_Trait traits[] = {
+ TALER_TESTING_make_trait_denom_pub (0, rms->fresh_pks),
+ TALER_TESTING_trait_end ()
+ };
+
+ return TALER_TESTING_get_trait (traits,
+ ret,
+ trait,
+ index);
+}
+
+
+/**
+ * Create a "refresh melt" command.
+ *
+ * @param label command label
+ * @param exchange connection to the exchange
+ * @param amount Fixme
+ * @param coin_reference reference to a command that will provide
+ * a coin to refresh
+ * @param expected_response_code expected HTTP code
+ */
+
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_refresh_melt
+ (const char *label,
+ struct TALER_EXCHANGE_Handle *exchange,
+ const char *amount,
+ const char *coin_reference,
+ unsigned int expected_response_code)
+{
+ struct RefreshMeltState *rms;
+ struct MeltDetails md;
+ struct TALER_TESTING_Command cmd;
+
+ md.coin_reference = coin_reference;
+ md.amount = amount;
+
+ rms = GNUNET_new (struct RefreshMeltState);
+ rms->amount = amount;
+ rms->melted_coin = md;
+ rms->expected_response_code = expected_response_code;
+ rms->exchange = exchange;
+
+ cmd.label = label;
+ cmd.cls = rms;
+ cmd.run = &refresh_melt_run;
+ cmd.cleanup = &refresh_melt_cleanup;
+ cmd.traits = &refresh_melt_traits;
+
+ return cmd;
+}
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ * to return in case there were multiple generated
+ * by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+refresh_reveal_traits (void *cls,
+ void **ret,
+ const char *trait,
+ unsigned int index)
+{
+ struct RefreshRevealState *rrs = cls;
+ unsigned int num_coins = rrs->num_fresh_coins;
+ #define NUM_TRAITS (num_coins * 3) + 3
+ struct TALER_TESTING_Trait traits[NUM_TRAITS];
+ unsigned int i;
+
+ /* Making coin privs traits */
+ for (i=0; i<num_coins; i++)
+ traits[i] = TALER_TESTING_make_trait_coin_priv
+ (i, &rrs->fresh_coins[i].coin_priv);
+
+ /* Making denom pubs traits */
+ for (i=0; i<num_coins; i++)
+ traits[num_coins + i]
+ = TALER_TESTING_make_trait_denom_pub
+ (i, rrs->fresh_coins[i].pk);
+
+ /* Making denom sigs traits */
+ for (i=0; i<num_coins; i++)
+ traits[(num_coins * 2) + i]
+ = TALER_TESTING_make_trait_denom_sig
+ (i, &rrs->fresh_coins[i].sig);
+
+ /* number of fresh coins */
+ traits[(num_coins * 3)] = TALER_TESTING_make_trait_uint
+ (0, &rrs->num_fresh_coins);
+
+ /* whole array of fresh coins */
+ traits[(num_coins * 3) + 1]
+ = TALER_TESTING_make_trait_fresh_coins (0, rrs->fresh_coins),
+
+ /* end of traits */
+ traits[(num_coins * 3) + 2] = TALER_TESTING_trait_end ();
+
+ return TALER_TESTING_get_trait (traits,
+ ret,
+ trait,
+ index);
+}
+
+/**
+ * Create a "refresh reveal" command.
+ *
+ * @param label command label
+ * @param exchange connection to the exchange
+ * @param melt_reference reference to a "refresh melt" command
+ * @param expected_response_code expected HTTP response code
+ *
+ * @return the "refresh reveal" command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_refresh_reveal
+ (const char *label,
+ struct TALER_EXCHANGE_Handle *exchange,
+ const char *melt_reference,
+ unsigned int expected_response_code)
+{
+ struct RefreshRevealState *rrs;
+ struct TALER_TESTING_Command cmd;
+
+ rrs = GNUNET_new (struct RefreshRevealState);
+ rrs->melt_reference = melt_reference;
+ rrs->exchange = exchange;
+ rrs->expected_response_code = expected_response_code;
+
+ cmd.cls = rrs;
+ cmd.label = label;
+ cmd.run = &refresh_reveal_run;
+ cmd.cleanup = &refresh_reveal_cleanup;
+ cmd.traits = &refresh_reveal_traits;
+
+ return cmd;
+}
+
+
+/**
+ * Create a "refresh link" command.
+ *
+ * @param label command label
+ * @param exchange connection to the exchange
+ * @param melt_reference reference to a "refresh melt" command
+ * @param expected_response_code expected HTTP response code
+ *
+ * @return the "refresh link" command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_refresh_link
+ (const char *label,
+ struct TALER_EXCHANGE_Handle *exchange,
+ const char *reveal_reference,
+ unsigned int expected_response_code)
+{
+ struct RefreshLinkState *rrs;
+ struct TALER_TESTING_Command cmd;
+
+ rrs = GNUNET_new (struct RefreshLinkState);
+ rrs->reveal_reference = reveal_reference;
+ rrs->exchange = exchange;
+ rrs->expected_response_code = expected_response_code;
+
+ cmd.cls = rrs;
+ cmd.label = label;
+ cmd.run = &refresh_link_run;
+ cmd.cleanup = &refresh_link_cleanup;
+
+ return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_refund.c b/src/exchange-lib/testing_api_cmd_refund.c
new file mode 100644
index 000000000..a092ee2d2
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_refund.c
@@ -0,0 +1,295 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3, or
+ (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange/testing_api_cmd_refund.c
+ * @brief Implement the /refund test command, plus other
+ * corollary commands (?).
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+
+struct RefundState
+{
+ /**
+ * Expected HTTP response code.
+ */
+ unsigned int expected_response_code;
+
+ /**
+ * Amount to be refunded.
+ */
+ const char *refund_amount;
+
+ /**
+ * Expected refund fee.
+ */
+ const char *refund_fee;
+
+ /**
+ * Reference to any command that can provide a coin to refund.
+ */
+ const char *coin_reference;
+
+ /**
+ * Refund transaction identifier. Left un-initialized in the
+ * old test-suite. What's the best way to init it?
+ */
+ uint64_t refund_transaction_id;
+
+ /**
+ * Connection to the exchange.
+ */
+ struct TALER_EXCHANGE_Handle *exchange;
+
+ /**
+ * Handle to the refund operation.
+ */
+ struct TALER_EXCHANGE_RefundHandle *rh;
+
+ /**
+ * Interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+};
+
+
+/**
+ * Check the result for the refund request.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for
+ * successful deposit; 0 if the exchange's reply is bogus
+ * (fails to follow the protocol).
+ * @param ec taler-specific error code, #TALER_EC_NONE on success
+ * @param exchange_pub public key the exchange used for signing @a
+ * obj
+ * @param obj the received JSON reply, should be kept as proof
+ * (and, in particular, be forwarded to the customer)
+ */
+static void
+refund_cb (void *cls,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ const struct TALER_ExchangePublicKeyP *exchange_pub,
+ const json_t *obj)
+{
+
+ struct RefundState *rs = cls;
+ struct TALER_TESTING_Command *refund_cmd;
+
+ refund_cmd = &rs->is->commands[rs->is->ip];
+ rs->rh = NULL;
+ if (rs->expected_response_code != http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u to command %s\n",
+ http_status,
+ refund_cmd->label);
+ json_dumpf (obj, stderr, 0);
+ TALER_TESTING_interpreter_fail (rs->is);
+ return;
+ }
+
+ switch (http_status)
+ {
+ case MHD_HTTP_OK:
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Good /refund status code\n");
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unmanaged HTTP status code: %u, command: %s\n",
+ http_status, refund_cmd->label);
+ }
+
+ TALER_TESTING_interpreter_next (rs->is);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /wire one.
+ * @param i the interpreter state.
+ */
+void
+refund_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct RefundState *rs = cls;
+ struct TALER_CoinSpendPrivateKeyP *coin_priv;
+ struct TALER_CoinSpendPublicKeyP coin;
+ const char *contract_terms;
+ struct GNUNET_HashCode h_contract_terms;
+ json_t *j_contract_terms;
+ struct TALER_Amount refund_fee;
+ struct TALER_Amount refund_amount;
+ const struct GNUNET_CRYPTO_EddsaPrivateKey *merchant_priv;
+ const struct TALER_TESTING_Command *coin_cmd;
+
+ rs->exchange = is->exchange;
+ rs->is = is;
+
+ if (GNUNET_OK !=
+ TALER_string_to_amount (rs->refund_amount,
+ &refund_amount))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse amount `%s' at %u/%s\n",
+ rs->refund_amount,
+ is->ip,
+ cmd->label);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_string_to_amount (rs->refund_fee,
+ &refund_fee))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse amount `%s' at %u/%s\n",
+ rs->refund_fee,
+ is->ip,
+ cmd->label);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ coin_cmd = TALER_TESTING_interpreter_lookup_command
+ (is, rs->coin_reference);
+
+ if (NULL == coin_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_contract_terms
+ (coin_cmd, 0, &contract_terms))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ j_contract_terms = json_loads
+ (contract_terms, JSON_REJECT_DUPLICATES, NULL);
+
+ /* Very unlikely to fail */
+ GNUNET_assert (NULL != j_contract_terms);
+ GNUNET_assert (GNUNET_OK == TALER_JSON_hash
+ (j_contract_terms, &h_contract_terms));
+
+ json_decref (j_contract_terms);
+
+ /* Hunting for a coin .. */
+ if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
+ (coin_cmd, 0, &coin_priv))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
+ &coin.eddsa_pub);
+ if (GNUNET_OK != TALER_TESTING_get_trait_peer_key
+ (coin_cmd, 0, &merchant_priv))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ rs->rh = TALER_EXCHANGE_refund
+ (rs->exchange, &refund_amount, &refund_fee, &h_contract_terms,
+ &coin, rs->refund_transaction_id,
+ (const struct TALER_MerchantPrivateKeyP *) merchant_priv,
+ &refund_cb, rs);
+
+ GNUNET_assert (NULL != rs->rh);
+}
+
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+refund_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct RefundState *rs = cls;
+
+ if (NULL != rs->rh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ rs->is->ip,
+ cmd->label);
+ TALER_EXCHANGE_refund_cancel (rs->rh);
+ rs->rh = NULL;
+ }
+ GNUNET_free (rs);
+}
+
+/**
+ * Create a /refund test command.
+ *
+ * @param label command label
+ * @param expected_response_code expected HTTP status code
+ * @param refund_amount the amount to ask a refund for
+ * @param refund_fee expected refund fee
+ * @param coin_reference reference to a command that can
+ * provide a coin to be refunded.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_refund (const char *label,
+ unsigned int expected_response_code,
+ const char *refund_amount,
+ const char *refund_fee,
+ const char *coin_reference)
+{
+ struct RefundState *rs;
+ struct TALER_TESTING_Command cmd;
+
+ rs = GNUNET_new (struct RefundState);
+
+ rs->expected_response_code = expected_response_code;
+ rs->refund_amount = refund_amount;
+ rs->refund_fee = refund_fee;
+ rs->coin_reference = coin_reference;
+
+ cmd.cls = rs;
+ cmd.label = label;
+ cmd.run = &refund_run;
+ cmd.cleanup = &refund_cleanup;
+
+ return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_signal.c b/src/exchange-lib/testing_api_cmd_signal.c
new file mode 100644
index 000000000..5b0fa1b9d
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_signal.c
@@ -0,0 +1,112 @@
+/*
+ This file is part of TALER
+ (C) 2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3, or
+ (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_signal.c
+ * @brief command(s) to send signals to processes.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+
+struct SignalState
+{
+ /**
+ * The process to send the signal to.
+ */
+ struct GNUNET_OS_Process *process;
+
+ /**
+ * The signal to send to the process.
+ */
+ int signal;
+
+};
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct SignalState.
+ * @param cmd the command to execute, a /wire one.
+ * @param is the interpreter state.
+ */
+void
+signal_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct SignalState *ss = cls;
+
+ GNUNET_break (0 == GNUNET_OS_process_kill
+ (ss->process, ss->signal));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Signaling '%d'..\n",
+ ss->signal);
+ sleep (6);
+ TALER_TESTING_interpreter_next (is);
+}
+
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct SignalState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+signal_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct SignalState *ss = cls;
+
+ GNUNET_free (ss);
+}
+
+/**
+ * Send a signal to a process.
+ *
+ * @param process handle to the process
+ * @param signal signal to send
+ *
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_signal (const char *label,
+ struct GNUNET_OS_Process *process,
+ int signal)
+{
+ struct SignalState *ss;
+ struct TALER_TESTING_Command cmd;
+
+ ss = GNUNET_new (struct SignalState);
+
+ ss->process = process;
+ ss->signal = signal;
+
+ cmd.cls = ss;
+ cmd.label = label;
+ cmd.run = &signal_run;
+ cmd.cleanup = &signal_cleanup;
+
+ return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_status.c b/src/exchange-lib/testing_api_cmd_status.c
new file mode 100644
index 000000000..c948277b5
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_status.c
@@ -0,0 +1,240 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3, or
+ (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange/testing_api_cmd_status.c
+ * @brief Implement the /reserve/status test command.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+
+struct StatusState
+{
+ /**
+ * Label to the command which created the reserve to check,
+ * needed to resort the reserve key.
+ */
+ const char *reserve_reference;
+
+ /**
+ * Handle to a /reserve/status operation.
+ */
+ struct TALER_EXCHANGE_ReserveStatusHandle *rsh;
+
+ /**
+ * Expected reserve balance.
+ */
+ const char *expected_balance;
+
+ /**
+ * Expected HTTP response code.
+ */
+ unsigned int expected_response_code;
+
+ /**
+ * Handle to the exchange.
+ */
+ struct TALER_EXCHANGE_Handle *exchange;
+
+ /**
+ * Interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+};
+
+/**
+ * Check exchange returned expected values.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for
+ * successful status request 0 if the exchange's reply is
+ * bogus (fails to follow the protocol)
+ * @param ec taler-specific error code, #TALER_EC_NONE on success
+ * @param[in] json original response in JSON format (useful only
+ * for diagnostics)
+ * @param balance current balance in the reserve, NULL on error
+ * @param history_length number of entries in the transaction
+ * history, 0 on error
+ * @param history detailed transaction history, NULL on error
+ */
+void
+reserve_status_cb
+ (void *cls,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ const json_t *json,
+ const struct TALER_Amount *balance,
+ unsigned int history_length,
+ const struct TALER_EXCHANGE_ReserveHistory *history)
+{
+ struct StatusState *ss = cls;
+ struct TALER_Amount eb;
+
+ ss->rsh = NULL;
+ if (ss->expected_response_code != http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected HTTP response code: %d\n",
+ http_status);
+ TALER_TESTING_interpreter_fail (ss->is);
+ return;
+ }
+
+ GNUNET_assert (GNUNET_OK == TALER_string_to_amount
+ (ss->expected_balance, &eb));
+
+ if (0 != TALER_amount_cmp (&eb, balance))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected amount in reserve\n");
+ TALER_TESTING_interpreter_fail (ss->is);
+ return;
+ }
+
+/**
+ * Fixme: need a way to check if reserve history is consistent.
+ * Every command which relates to reserve 'x' should be added in
+ * a linked list of all commands that relate to the same reserve
+ * 'x'.
+ *
+ * API-wise, any command that relates to a reserve should offer a
+ * method called e.g. "compare_with_history" that takes an element
+ * of the array returned by "/reserve/status" and checks if that
+ * element correspond to itself (= the command exposing the check-
+ * method).
+ */
+
+ TALER_TESTING_interpreter_next (ss->is);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /wire one.
+ * @param i the interpreter state.
+ */
+void
+status_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+
+ struct StatusState *ss = cls;
+ const struct TALER_TESTING_Command *create_reserve;
+ struct TALER_ReservePrivateKeyP *reserve_priv;
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ ss->is = is;
+ GNUNET_assert (NULL != ss->reserve_reference);
+
+ create_reserve
+ = TALER_TESTING_interpreter_lookup_command
+ (is, ss->reserve_reference);
+
+ if (NULL == create_reserve)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_reserve_priv (create_reserve,
+ 0,
+ &reserve_priv))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
+ &reserve_pub.eddsa_pub);
+ ss->rsh
+ = TALER_EXCHANGE_reserve_status (ss->exchange,
+ &reserve_pub,
+ &reserve_status_cb,
+ ss);
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+status_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct StatusState *ss = cls;
+
+ if (NULL != ss->rsh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ ss->is->ip,
+ cmd->label);
+ TALER_EXCHANGE_reserve_status_cancel (ss->rsh);
+ ss->rsh = NULL;
+ }
+ GNUNET_free (ss);
+}
+
+
+/**
+ * Create a /reserve/status command.
+ *
+ * @param label the command label.
+ * @param exchange the exchange to connect to.
+ * @param reserve_reference reference to the reserve to check.
+ * @param expected_balance balance expected to be at the referenced reserve.
+ * @param expected_response_code expected HTTP response code.
+ *
+ * @return the command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_status (const char *label,
+ struct TALER_EXCHANGE_Handle *exchange,
+ const char *reserve_reference,
+ const char *expected_balance,
+ unsigned int expected_response_code)
+{
+ struct TALER_TESTING_Command cmd;
+ struct StatusState *ss;
+
+ ss = GNUNET_new (struct StatusState);
+ ss->exchange = exchange;
+ ss->reserve_reference = reserve_reference;
+ ss->expected_balance = expected_balance;
+ ss->expected_response_code = expected_response_code;
+
+ cmd.cls = ss;
+ cmd.label = label;
+ cmd.run = &status_run;
+ cmd.cleanup = &status_cleanup;
+
+ return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_track.c b/src/exchange-lib/testing_api_cmd_track.c
new file mode 100644
index 000000000..40894872b
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_track.c
@@ -0,0 +1,819 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3, or
+ (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange/testing_api_cmd_status.c
+ * @brief Implement the /reserve/status test command.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+
+struct TrackTransactionState
+{
+
+ /**
+ * Which #OC_CHECK_BANK_TRANSFER wtid should this match? NULL
+ * for none.
+ */
+ const char *bank_transfer_reference;
+
+ /**
+ * Wire transfer identifier, set if #MHD_HTTP_OK was the
+ * response code.
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ /**
+ * Expected HTTP response code.
+ */
+ unsigned int expected_response_code;
+
+ /**
+ * Reference to any operation that can provide a transaction.
+ * Tipically a /deposit operation.
+ */
+ const char *transaction_reference;
+
+ /**
+ * Index of the coin involved in the transaction.
+ */
+ unsigned int coin_index;
+
+ /**
+ * Handle to the deposit wtid request.
+ */
+ struct TALER_EXCHANGE_TrackTransactionHandle *tth;
+
+ /**
+ * Handle to the exchange.
+ */
+ struct TALER_EXCHANGE_Handle *exchange;
+
+ /**
+ * Interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+};
+
+struct TrackTransferState
+{
+
+ /**
+ * Expected amount for this WTID.
+ */
+ const char *expected_total_amount;
+
+ /**
+ * Expected fee for this WTID.
+ */
+ const char *expected_wire_fee;
+
+ /**
+ * Expected HTTP response code.
+ */
+ unsigned int expected_response_code;
+
+ /**
+ * Reference to any operation that can provide a WTID.
+ */
+ const char *wtid_reference;
+
+ /**
+ * Reference to any operation that can provide wire details.
+ */
+ const char *wire_details_reference;
+
+ /**
+ * Reference to any operation that can provide an amount.
+ */
+ const char *total_amount_reference;
+
+ /**
+ * Index to the WTID to pick.
+ */
+ unsigned int index;
+
+ /**
+ * Handle to the deposit wtid request.
+ */
+ struct TALER_EXCHANGE_TrackTransferHandle *tth;
+
+ /**
+ * Handle to the exchange.
+ */
+ struct TALER_EXCHANGE_Handle *exchange;
+
+ /**
+ * Interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+};
+
+
+/**
+ * Function called with detailed wire transfer data.
+ *
+ * @param cls closure
+ * @param http_status HTTP status code we got, 0 on exchange
+ * protocol violation
+ * @param ec taler-specific error code, #TALER_EC_NONE on success
+ * @param exchange_pub public key the exchange used for signing
+ * @param json original json reply (may include signatures, those
+ * have then been validated already)
+ * @param wtid wire transfer identifier used by the exchange, NULL
+ * if exchange did not yet execute the transaction
+ * @param execution_time actual or planned execution time for the
+ * wire transfer
+ * @param coin_contribution contribution to the @a total_amount of
+ * the deposited coin (may be NULL)
+ * @param total_amount total amount of the wire transfer, or NULL
+ * if the exchange could not provide any @a wtid (set only
+ * if @a http_status is #MHD_HTTP_OK)
+ */
+static void
+deposit_wtid_cb
+ (void *cls,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ const struct TALER_ExchangePublicKeyP *exchange_pub,
+ const json_t *json,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ struct GNUNET_TIME_Absolute execution_time,
+ const struct TALER_Amount *coin_contribution)
+{
+ struct TrackTransactionState *tts = cls;
+ struct TALER_TESTING_Interpreter *is = tts->is;
+ struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
+
+ tts->tth = NULL;
+ if (tts->expected_response_code != http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u to command %s\n",
+ http_status,
+ cmd->label);
+ json_dumpf (json, stderr, 0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ switch (http_status)
+ {
+ case MHD_HTTP_OK:
+ tts->wtid = *wtid;
+ if (NULL != tts->bank_transfer_reference)
+ {
+ const struct TALER_TESTING_Command *bank_transfer_cmd;
+ char *ws;
+
+ ws = GNUNET_STRINGS_data_to_string_alloc (wtid,
+ sizeof (*wtid));
+ bank_transfer_cmd = TALER_TESTING_interpreter_lookup_command
+ (is, tts->bank_transfer_reference);
+
+ if (NULL == bank_transfer_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ char *transfer_subject;
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_transfer_subject
+ (bank_transfer_cmd, 0, &transfer_subject))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ if (0 != strcmp (ws, transfer_subject))
+ {
+ GNUNET_break (0);
+ GNUNET_free (ws);
+ TALER_TESTING_interpreter_fail (tts->is);
+ return;
+ }
+
+ GNUNET_free (ws);
+ }
+ break;
+ case MHD_HTTP_ACCEPTED:
+ /* allowed, nothing to check here */
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ /* allowed, nothing to check here */
+ break;
+ default:
+ GNUNET_break (0);
+ break;
+ }
+ TALER_TESTING_interpreter_next (tts->is);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /track/transaction one.
+ * @param is the interpreter state.
+ */
+void
+track_transaction_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct TrackTransactionState *tts = cls;
+ const struct TALER_TESTING_Command *transaction_cmd;
+ struct TALER_CoinSpendPrivateKeyP *coin_priv;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+ const char *wire_details;
+ const char *contract_terms;
+ json_t *j_wire_details;
+ json_t *j_contract_terms;
+ struct GNUNET_HashCode h_wire_details;
+ struct GNUNET_HashCode h_contract_terms;
+ const struct GNUNET_CRYPTO_EddsaPrivateKey *merchant_priv;
+
+ tts->is = is;
+ transaction_cmd = TALER_TESTING_interpreter_lookup_command
+ (tts->is, tts->transaction_reference);
+
+ if (NULL == transaction_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (tts->is);
+ return;
+ }
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_coin_priv
+ (transaction_cmd, tts->coin_index, &coin_priv))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (tts->is);
+ return;
+ }
+
+ GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
+ &coin_pub.eddsa_pub);
+
+ /* Get the strings.. */
+ if (GNUNET_OK != TALER_TESTING_get_trait_wire_details
+ (transaction_cmd, 0, &wire_details))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (tts->is);
+ return;
+ }
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_contract_terms
+ (transaction_cmd, 0, &contract_terms))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (tts->is);
+ return;
+ }
+
+ /* Parse them.. */
+ j_wire_details = json_loads
+ (wire_details, JSON_REJECT_DUPLICATES, NULL);
+ j_contract_terms = json_loads
+ (contract_terms, JSON_REJECT_DUPLICATES, NULL);
+
+ if ((NULL == j_wire_details) || (NULL == j_contract_terms))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (tts->is);
+ return;
+ }
+
+ /* Should not fail here, json has been parsed already */
+ GNUNET_assert
+ ( (GNUNET_OK == TALER_JSON_hash (j_wire_details,
+ &h_wire_details)) &&
+ (GNUNET_OK == TALER_JSON_hash (j_contract_terms,
+ &h_contract_terms)) );
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_peer_key
+ (transaction_cmd, 0, &merchant_priv))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (tts->is);
+ return;
+ }
+
+ tts->tth = TALER_EXCHANGE_track_transaction
+ (tts->exchange,
+ (struct TALER_MerchantPrivateKeyP *) merchant_priv,
+ &h_wire_details,
+ &h_contract_terms,
+ &coin_pub,
+ &deposit_wtid_cb,
+ tts);
+
+ GNUNET_assert (NULL != tts->tth);
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+track_transaction_cleanup
+ (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct TrackTransactionState *tts = cls;
+
+ if (NULL != tts->tth)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ tts->is->ip,
+ cmd->label);
+ TALER_EXCHANGE_track_transaction_cancel (tts->tth);
+ tts->tth = NULL;
+ }
+ GNUNET_free (tts);
+}
+
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ * to return in case there were multiple generated
+ * by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+track_transaction_traits (void *cls,
+ void **ret,
+ const char *trait,
+ unsigned int index)
+{
+ struct TrackTransactionState *tts = cls;
+
+ struct TALER_TESTING_Trait traits[] = {
+ TALER_TESTING_make_trait_wtid (0, &tts->wtid),
+ TALER_TESTING_trait_end ()
+ };
+
+ return TALER_TESTING_get_trait (traits,
+ ret,
+ trait,
+ index);
+}
+
+/**
+ * Create a /track/transaction command.
+ *
+ * @param label the command label.
+ * @param exchange the exchange to connect to.
+ * @param transaction_reference reference to a deposit operation.
+ * @param coin_index index of the coin involved in the transaction
+ * @param expected_response_code expected HTTP response code.
+ * @param bank_transfer_reference which #OC_CHECK_BANK_TRANSFER
+ * wtid should this match? NULL
+ * for none
+ *
+ * @return the command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_track_transaction
+ (const char *label,
+ struct TALER_EXCHANGE_Handle *exchange,
+ const char *transaction_reference,
+ unsigned int coin_index,
+ unsigned int expected_response_code,
+ const char *bank_transfer_reference)
+{
+ struct TALER_TESTING_Command cmd;
+ struct TrackTransactionState *tts;
+
+ tts = GNUNET_new (struct TrackTransactionState);
+ tts->exchange = exchange;
+ tts->transaction_reference = transaction_reference;
+ tts->expected_response_code = expected_response_code;
+ tts->bank_transfer_reference = bank_transfer_reference;
+ tts->coin_index = coin_index;
+
+ cmd.cls = tts;
+ cmd.label = label;
+ cmd.run = &track_transaction_run;
+ cmd.cleanup = &track_transaction_cleanup;
+ cmd.traits = &track_transaction_traits;
+
+ return cmd;
+
+}
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+track_transfer_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+
+ struct TrackTransferState *tts = cls;
+
+ if (NULL != tts->tth)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ tts->is->ip,
+ cmd->label);
+ TALER_EXCHANGE_track_transfer_cancel (tts->tth);
+ tts->tth = NULL;
+ }
+ GNUNET_free (tts);
+
+}
+
+/**
+ * Function called with detailed wire transfer data, including all
+ * of the coin transactions that were combined into the wire
+ * transfer.
+ *
+ * @param cls closure
+ * @param http_status HTTP status code we got, 0 on exchange
+ * protocol violation
+ * @param ec taler-specific error code, #TALER_EC_NONE on success
+ * @param exchange_pub public key the exchange used for signing
+ * @param json original json reply (may include signatures, those
+ * have then been validated already)
+ * @param h_wire hash of the wire transfer address the transfer
+ * went to, or NULL on error
+ * @param execution_time time when the exchange claims to have
+ * performed the wire transfer
+ * @param total_amount total amount of the wire transfer, or NULL
+ * if the exchange could not provide any @a wtid (set only
+ * if @a http_status is #MHD_HTTP_OK)
+ * @param wire_fee wire fee that was charged by the exchange
+ * @param details_length length of the @a details array
+ * @param details array with details about the combined
+ * transactions
+ */
+static void
+track_transfer_cb
+ (void *cls,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ const struct TALER_ExchangePublicKeyP *exchange_pub,
+ const json_t *json,
+ const struct GNUNET_HashCode *h_wire,
+ struct GNUNET_TIME_Absolute execution_time,
+ const struct TALER_Amount *total_amount,
+ const struct TALER_Amount *wire_fee,
+ unsigned int details_length,
+ const struct TALER_TrackTransferDetails *details)
+{
+ struct TrackTransferState *tts = cls;
+ struct TALER_TESTING_Interpreter *is = tts->is;
+ struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
+
+ const struct TALER_TESTING_Command *wtid_cmd;
+ struct TALER_Amount expected_amount;
+
+ tts->tth = NULL;
+
+ if (tts->expected_response_code != http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u to command %s\n",
+ http_status,
+ cmd->label);
+ json_dumpf (json, stderr, 0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ switch (http_status)
+ {
+
+ if (
+ (NULL == tts->expected_total_amount) ||
+ (NULL == tts->expected_wire_fee))
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Expected amount and fee not specified, "
+ "likely to segfault...\n");
+
+ case MHD_HTTP_OK:
+ if (GNUNET_OK !=
+ TALER_string_to_amount (tts->expected_total_amount,
+ &expected_amount))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ if (0 != TALER_amount_cmp (total_amount,
+ &expected_amount))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Total amount missmatch to command %s - "
+ "%s vs %s\n",
+ cmd->label,
+ TALER_amount_to_string (total_amount),
+ TALER_amount_to_string (&expected_amount));
+ json_dumpf (json, stderr, 0);
+ fprintf (stderr, "\n");
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ if (GNUNET_OK !=
+ TALER_string_to_amount (tts->expected_wire_fee,
+ &expected_amount))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ if (0 != TALER_amount_cmp (wire_fee,
+ &expected_amount))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Wire fee missmatch to command %s\n",
+ cmd->label);
+ json_dumpf (json, stderr, 0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ wtid_cmd = TALER_TESTING_interpreter_lookup_command
+ (is, tts->wtid_reference);
+
+ if (NULL == wtid_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ /**
+ * Optionally checking: (1) wire-details for this transfer
+ * match the ones from a referenced "deposit" operation -
+ * or any operation that could provide wire-details. (2)
+ * Total amount for this transfer matches the one from any
+ * referenced command that could provide one.
+ */
+
+ if (NULL != tts->wire_details_reference)
+ {
+ const struct TALER_TESTING_Command *wire_details_cmd;
+ const char *wire_details;
+ json_t *j_wire_details;
+ struct GNUNET_HashCode h_wire_details;
+
+ if (NULL == (wire_details_cmd
+ = TALER_TESTING_interpreter_lookup_command
+ (is, tts->wire_details_reference)))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_wire_details
+ (wire_details_cmd, 0, &wire_details))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ j_wire_details = json_loads
+ (wire_details, JSON_REJECT_DUPLICATES, NULL);
+
+ GNUNET_assert (NULL != j_wire_details);
+
+ GNUNET_assert (GNUNET_OK == TALER_JSON_hash
+ (j_wire_details, &h_wire_details));
+
+ if (0 != memcmp (&h_wire_details,
+ h_wire,
+ sizeof (struct GNUNET_HashCode)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Wire hash missmath to command %s\n",
+ cmd->label);
+ json_dumpf (json, stderr, 0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ }
+ if (NULL != tts->total_amount_reference)
+ {
+ const struct TALER_TESTING_Command *total_amount_cmd;
+ const char *total_amount_from_reference_str;
+ struct TALER_Amount total_amount_from_reference;
+
+ if (NULL == (total_amount_cmd
+ = TALER_TESTING_interpreter_lookup_command
+ (is, tts->total_amount_reference)))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_amount
+ (total_amount_cmd, 0, &total_amount_from_reference_str))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+
+ GNUNET_assert (GNUNET_OK == TALER_string_to_amount
+ (total_amount_from_reference_str,
+ &total_amount_from_reference));
+
+ if (0 != TALER_amount_cmp (total_amount,
+ &total_amount_from_reference))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Amount missmath to command %s\n",
+ cmd->label);
+ json_dumpf (json, stderr, 0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ }
+ }
+ TALER_TESTING_interpreter_next (is);
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure.
+ * @param cmd the command to execute, a /track/transfer one.
+ * @param is the interpreter state.
+ */
+void
+track_transfer_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ /* looking for a wtid to track .. */
+
+ struct TrackTransferState *tts = cls;
+ struct TALER_WireTransferIdentifierRawP wtid;
+ struct TALER_WireTransferIdentifierRawP *wtid_ptr;
+
+ /* If no reference is given, we'll use a all-zeros
+ * WTID */
+ memset (&wtid, 0, sizeof (wtid));
+ wtid_ptr = &wtid;
+
+ tts->is = is;
+ if (NULL != tts->wtid_reference)
+ {
+ const struct TALER_TESTING_Command *wtid_cmd;
+
+ wtid_cmd = TALER_TESTING_interpreter_lookup_command
+ (tts->is, tts->wtid_reference);
+
+ if (NULL == wtid_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (tts->is);
+ return;
+ }
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_wtid
+ (wtid_cmd, tts->index, &wtid_ptr))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (tts->is);
+ return;
+ }
+ }
+ tts->tth = TALER_EXCHANGE_track_transfer (tts->exchange,
+ wtid_ptr,
+ &track_transfer_cb,
+ tts);
+
+ GNUNET_assert (NULL != tts->tth);
+}
+
+/**
+ * Make a /track/transfer command, expecting the transfer
+ * not being done (yet).
+ *
+ * @param label the command label
+ * @param exchange connection to the exchange
+ * @param wtid_reference reference to any command which can provide
+ * a wtid
+ * @param index in case there are multiple wtid offered, this
+ * parameter selects a particular one
+ * @param expected_response_code expected HTTP response code
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_track_transfer_empty
+ (const char *label,
+ struct TALER_EXCHANGE_Handle *exchange,
+ const char *wtid_reference,
+ unsigned int index,
+ unsigned int expected_response_code)
+{
+ struct TrackTransferState *tts;
+ struct TALER_TESTING_Command cmd;
+
+ tts = GNUNET_new (struct TrackTransferState);
+
+ tts->wtid_reference = wtid_reference;
+ tts->index = index;
+ tts->expected_response_code = expected_response_code;
+ tts->exchange = exchange;
+
+ cmd.cls = tts;
+ cmd.label = label;
+ cmd.run = &track_transfer_run;
+ cmd.cleanup = &track_transfer_cleanup;
+
+ return cmd;
+}
+
+/**
+ * Make a /track/transfer command, specifying which amount and
+ * wire fee are expected.
+ *
+ * @param label the command label
+ * @param exchange connection to the exchange
+ * @param wtid_reference reference to any command which can provide
+ * a wtid
+ * @param index in case there are multiple wtid offered, this
+ * parameter selects a particular one
+ * @param expected_response_code expected HTTP response code
+ * @param expected_amount how much money we expect being
+ * moved with this wire-transfer.
+ * @param expected_wire_fee expected wire fee.
+ *
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_track_transfer
+ (const char *label,
+ struct TALER_EXCHANGE_Handle *exchange,
+ const char *wtid_reference,
+ unsigned int index,
+ unsigned int expected_response_code,
+ const char *expected_total_amount,
+ const char *expected_wire_fee)
+{
+ struct TrackTransferState *tts;
+ struct TALER_TESTING_Command cmd;
+
+ tts = GNUNET_new (struct TrackTransferState);
+
+ tts->wtid_reference = wtid_reference;
+ tts->index = index;
+ tts->expected_response_code = expected_response_code;
+ tts->exchange = exchange;
+ tts->expected_total_amount = expected_total_amount;
+ tts->expected_wire_fee = expected_wire_fee;
+
+ cmd.cls = tts;
+ cmd.label = label;
+ cmd.run = &track_transfer_run;
+ cmd.cleanup = &track_transfer_cleanup;
+
+ return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_wire.c b/src/exchange-lib/testing_api_cmd_wire.c
new file mode 100644
index 000000000..f65daec00
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_wire.c
@@ -0,0 +1,267 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_cmd_wire.c
+ * @brief command for testing /wire.
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_testing_lib.h"
+
+struct WireState
+{
+
+ /**
+ * Handle to the /wire operation.
+ */
+ struct TALER_EXCHANGE_WireHandle *wh;
+
+ /**
+ * Which wire-method we expect are offered by the exchange.
+ */
+ const char *expected_method;
+
+ /**
+ * Flag indicating if the expected method is actually
+ * offered.
+ */
+ unsigned int method_found;
+
+ /**
+ * Fee we expect is charged for this wire-transfer method.
+ */
+ const char *expected_fee;
+
+ /**
+ * Expected HTTP response code.
+ */
+ unsigned int expected_response_code;
+
+ /**
+ * Interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * Connection to the exchange.
+ */
+ struct TALER_EXCHANGE_Handle *exchange;
+};
+
+
+/**
+ * Check all the expected values have been returned by /wire.
+ *
+ * @param cls closure
+ * @param wire_method name of the wire method (i.e. "sepa")
+ * @param fees fee structure for this method
+ */
+static void
+check_method_and_fee_cb
+ (void *cls,
+ const char *wire_method,
+ const struct TALER_EXCHANGE_WireAggregateFees *fees);
+
+/**
+ * Callbacks called with the result(s) of a wire format inquiry
+ * request to the exchange.
+ *
+ * @param cls closure with the interpreter state
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200)
+ * for successful request; 0 if the exchange's
+ * reply is bogus (fails to follow the protocol)
+ * @param ec taler-specific error code, #TALER_EC_NONE on success
+ * @param obj the received JSON reply, if successful this should
+ * be the wire format details as provided by /wire.
+ */
+static void
+wire_cb (void *cls,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ const json_t *obj)
+{
+ struct WireState *ws = cls;
+ struct TALER_TESTING_Command *cmd
+ = &ws->is->commands[ws->is->ip];
+
+ /**
+ * The handle has been free'd by GNUnet curl-lib. FIXME:
+ * shouldn't GNUnet nullify it once it frees it?
+ */
+ ws->wh = NULL;
+ if (ws->expected_response_code != http_status)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (ws->is);
+ return;
+ }
+
+ if (GNUNET_OK != TALER_EXCHANGE_wire_get_fees
+ (&TALER_EXCHANGE_get_keys (ws->exchange)->master_pub,
+ obj,
+ // will check synchronously.
+ &check_method_and_fee_cb,
+ ws))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Wire fee extraction in command %s failed\n",
+ cmd->label);
+ json_dumpf (obj, stderr, 0);
+ TALER_TESTING_interpreter_fail (ws->is);
+ return;
+ }
+
+ if (ws->method_found != GNUNET_OK)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "/wire does not offer method '%s'\n",
+ ws->expected_method);
+ TALER_TESTING_interpreter_fail (ws->is);
+ return;
+ }
+ TALER_TESTING_interpreter_next (ws->is);
+}
+
+/**
+ * Check all the expected values have been returned by /wire.
+ *
+ * @param cls closure
+ * @param wire_method name of the wire method (i.e. "sepa")
+ * @param fees fee structure for this method
+ */
+static void
+check_method_and_fee_cb
+ (void *cls,
+ const char *wire_method,
+ const struct TALER_EXCHANGE_WireAggregateFees *fees)
+{
+ struct WireState *ws = cls;
+ struct TALER_TESTING_Command *cmd
+ = &ws->is->commands[ws->is->ip]; // ugly?
+ struct TALER_Amount expected_fee;
+
+ if (0 == strcmp (ws->expected_method, wire_method))
+ ws->method_found = GNUNET_OK;
+
+ if ( ws->expected_fee && (ws->method_found == GNUNET_OK) )
+ {
+ GNUNET_assert (GNUNET_OK == TALER_string_to_amount
+ (ws->expected_fee,
+ &expected_fee));
+ while (NULL != fees)
+ {
+ if (0 != TALER_amount_cmp (&fees->wire_fee,
+ &expected_fee))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Wire fee missmatch to command %s\n",
+ cmd->label);
+ TALER_TESTING_interpreter_fail (ws->is);
+ return;
+ }
+ fees = fees->next;
+ }
+ }
+}
+
+/**
+ * Run the command.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command to execute, a /wire one.
+ * @param i the interpreter state.
+ */
+void
+wire_run (void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *i)
+{
+ struct WireState *ws = cls;
+ ws->is = i;
+ ws->wh = TALER_EXCHANGE_wire (ws->exchange,
+ &wire_cb,
+ ws);
+}
+
+
+/**
+ * Cleanup the state.
+ *
+ * @param cls closure, typically a #struct WireState.
+ * @param cmd the command which is being cleaned up.
+ */
+void
+wire_cleanup (void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct WireState *ws = cls;
+
+ if (NULL != ws->wh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Command %u (%s) did not complete\n",
+ ws->is->ip,
+ cmd->label);
+ TALER_EXCHANGE_wire_cancel (ws->wh);
+ ws->wh = NULL;
+ }
+ GNUNET_free (ws);
+}
+
+/**
+ * Create a /wire command.
+ *
+ * @param label the command label.
+ * @param exchange the exchange to connect to.
+ * @param expected_method which wire-transfer method is expected
+ * to be offered by the exchange.
+ * @param expected_fee the fee the exchange should charge.
+ * @param expected_response_code the HTTP response the exchange
+ * should return.
+ *
+ * @return the command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_wire (const char *label,
+ struct TALER_EXCHANGE_Handle *exchange,
+ const char *expected_method,
+ const char *expected_fee,
+ unsigned int expected_response_code)
+{
+ struct TALER_TESTING_Command cmd;
+ struct WireState *ws;
+
+ ws = GNUNET_new (struct WireState);
+ ws->exchange = exchange;
+ ws->expected_method = expected_method;
+ ws->expected_fee = expected_fee;
+ ws->expected_response_code = expected_response_code;
+
+ cmd.cls = ws;
+ cmd.label = label;
+ cmd.run = &wire_run;
+ cmd.cleanup = &wire_cleanup;
+
+ return cmd;
+}
diff --git a/src/exchange-lib/testing_api_cmd_withdraw.c b/src/exchange-lib/testing_api_cmd_withdraw.c
index 642722b45..eb3bc8a6b 100644
--- a/src/exchange-lib/testing_api_cmd_withdraw.c
+++ b/src/exchange-lib/testing_api_cmd_withdraw.c
@@ -2,16 +2,18 @@
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
+ 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/>
*/
/**
@@ -85,14 +87,17 @@ struct WithdrawState
/**
- * Function called upon completion of our /reserve/withdraw request.
+ * Function called upon completion of our /reserve/withdraw
+ * request.
*
* @param cls closure with the withdraw state
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
- * 0 if the exchange's reply is bogus (fails to follow the protocol)
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for
+ * successful status request; 0 if the exchange's reply is
+ * bogus (fails to follow the protocol)
* @param ec taler-specific error code, #TALER_EC_NONE on success
* @param sig signature over the coin, NULL on error
- * @param full_response full response from the exchange (for logging, in case of errors)
+ * @param full_response full response from the exchange (for
+ * logging, in case of errors)
*/
static void
reserve_withdraw_cb (void *cls,
@@ -164,9 +169,8 @@ withdraw_run (void *cls,
struct TALER_ReservePrivateKeyP *rp;
const struct TALER_TESTING_Command *create_reserve;
- create_reserve
- = TALER_TESTING_interpreter_lookup_command (is,
- ws->reserve_reference);
+ create_reserve = TALER_TESTING_interpreter_lookup_command
+ (is, ws->reserve_reference);
if (NULL == create_reserve)
{
GNUNET_break (0);
@@ -175,7 +179,7 @@ withdraw_run (void *cls,
}
if (GNUNET_OK !=
TALER_TESTING_get_trait_reserve_priv (create_reserve,
- NULL,
+ 0,
&rp))
{
GNUNET_break (0);
@@ -245,38 +249,72 @@ static int
withdraw_traits (void *cls,
void **ret,
const char *trait,
- const char *selector)
+ unsigned int index)
{
struct WithdrawState *ws = cls;
+ const struct TALER_TESTING_Command *reserve_cmd;
+ struct TALER_ReservePrivateKeyP *reserve_priv;
+
+ /* We offer the reserve key where these coins were withdrawn
+ * from. */
+ reserve_cmd = TALER_TESTING_interpreter_lookup_command
+ (ws->is, ws->reserve_reference);
+
+ if (NULL == reserve_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (ws->is);
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_OK != TALER_TESTING_get_trait_reserve_priv
+ (reserve_cmd, 0, &reserve_priv))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (ws->is);
+ return GNUNET_SYSERR;
+ }
+
struct TALER_TESTING_Trait traits[] = {
- TALER_TESTING_make_trait_coin_priv (NULL /* only one coin */,
+ TALER_TESTING_make_trait_coin_priv (0 /* only one coin */,
&ws->ps.coin_priv),
- TALER_TESTING_make_trait_blinding_key (NULL /* only one coin */,
+ TALER_TESTING_make_trait_blinding_key (0 /* only one coin */,
&ws->ps.blinding_key),
- TALER_TESTING_make_trait_denom_pub (NULL /* only one coin */,
+ TALER_TESTING_make_trait_denom_pub (0 /* only one coin */,
ws->pk),
- TALER_TESTING_make_trait_denom_sig (NULL /* only one coin */,
+ TALER_TESTING_make_trait_denom_sig (0 /* only one coin */,
&ws->sig),
+ TALER_TESTING_make_trait_reserve_priv (0,
+ reserve_priv),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait (traits,
ret,
trait,
- selector);
+ index);
}
/**
- * Create withdraw command.
+ * Create a withdraw command.
*
+ * @param label command label, used by other commands to
+ * reference this.
+ * @param exchange handle to the exchange.
+ * @param amount how much we withdraw.
+ * @param expected_response_code which HTTP response code
+ * we expect from the exchange.
+ *
+ * @return the withdraw command to be executed by the interpreter.
*/
struct TALER_TESTING_Command
-TALER_TESTING_cmd_withdraw_amount (const char *label,
- struct TALER_EXCHANGE_Handle *exchange,
- const char *reserve_reference,
- const char *amount,
- unsigned int expected_response_code)
+TALER_TESTING_cmd_withdraw_amount
+ (const char *label,
+ struct TALER_EXCHANGE_Handle *exchange,
+ const char *reserve_reference,
+ const char *amount,
+ unsigned int expected_response_code)
{
struct TALER_TESTING_Command cmd;
struct WithdrawState *ws;
@@ -294,8 +332,9 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
label);
GNUNET_assert (0);
}
- ws->pk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (exchange),
- &ws->amount);
+ ws->pk = TALER_TESTING_find_pk
+ (TALER_EXCHANGE_get_keys (exchange),
+ &ws->amount);
if (NULL == ws->pk)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -319,11 +358,12 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
*
*/
struct TALER_TESTING_Command
-TALER_TESTING_cmd_withdraw_denomination (const char *label,
- struct TALER_EXCHANGE_Handle *exchange,
- const char *reserve_reference,
- const struct TALER_EXCHANGE_DenomPublicKey *dk,
- unsigned int expected_response_code)
+TALER_TESTING_cmd_withdraw_denomination
+ (const char *label,
+ struct TALER_EXCHANGE_Handle *exchange,
+ const char *reserve_reference,
+ const struct TALER_EXCHANGE_DenomPublicKey *dk,
+ unsigned int expected_response_code)
{
struct TALER_TESTING_Command cmd;
struct WithdrawState *ws;
@@ -349,7 +389,4 @@ TALER_TESTING_cmd_withdraw_denomination (const char *label,
}
-
-
-
/* end of testing_api_cmd_withdraw.c */
diff --git a/src/exchange-lib/testing_api_helpers.c b/src/exchange-lib/testing_api_helpers.c
index 1721b3e96..a6e421e16 100644
--- a/src/exchange-lib/testing_api_helpers.c
+++ b/src/exchange-lib/testing_api_helpers.c
@@ -1,4 +1,3 @@
-
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
@@ -31,6 +30,8 @@
/**
* Remove files from previous runs
+ *
+ * @param config_name configuration filename.
*/
void
TALER_TESTING_cleanup_files (const char *config_name)
@@ -65,7 +66,7 @@ TALER_TESTING_cleanup_files (const char *config_name)
/**
* Prepare launching an exchange. Checks that the configured
* port is available, runs taler-exchange-keyup,
- * taler-auditor-sign and taler-exchange-dbinit. Does not
+ * taler-auditor-sign and taler-exchange-dbinit. Does NOT
* launch the exchange process itself.
*
* @param config_filename configuration file to use
@@ -79,42 +80,16 @@ TALER_TESTING_prepare_exchange (const char *config_filename)
enum GNUNET_OS_ProcessStatusType type;
unsigned long code;
struct GNUNET_CONFIGURATION_Handle *cfg;
- unsigned long long port;
-
- cfg = GNUNET_CONFIGURATION_create ();
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_load (cfg,
- config_filename))
- return GNUNET_NO;
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_number (cfg,
- "exchange",
- "PORT",
- &port))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "exchange",
- "PORT");
- GNUNET_CONFIGURATION_destroy (cfg);
- return GNUNET_NO;
- }
- 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);
- return GNUNET_NO;
- }
+ char *test_home_dir;
+ char *signed_keys_out;
+ char *exchange_master_pub;
proc = GNUNET_OS_start_process (GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-keyup",
"taler-exchange-keyup",
- "-c", "test_exchange_api.conf",
+ "-c", config_filename,
"-o", "auditor.in",
NULL);
if (NULL == proc)
@@ -126,21 +101,58 @@ TALER_TESTING_prepare_exchange (const char *config_filename)
GNUNET_OS_process_wait (proc);
GNUNET_OS_process_destroy (proc);
+ cfg = GNUNET_CONFIGURATION_create ();
+ if (GNUNET_OK != GNUNET_CONFIGURATION_load
+ (cfg, config_filename))
+ return GNUNET_SYSERR;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "paths",
+ "TALER_TEST_HOME",
+ &test_home_dir))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "paths",
+ "TALER_TEST_HOME");
+ GNUNET_CONFIGURATION_destroy (cfg);
+ return GNUNET_SYSERR;
+ }
+
+ GNUNET_asprintf (&signed_keys_out,
+ "%s/.local/share/taler/auditors/auditor.out",
+ test_home_dir);
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "exchange",
+ "MASTER_PUBLIC_KEY",
+ &exchange_master_pub))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "MASTER_PUBLIC_KEY");
+ GNUNET_CONFIGURATION_destroy (cfg);
+ return GNUNET_SYSERR;
+ }
+
+ GNUNET_CONFIGURATION_destroy (cfg);
+
proc = GNUNET_OS_start_process (GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-auditor-sign",
"taler-auditor-sign",
- "-c", "test_exchange_api.conf",
+ "-c", config_filename,
"-u", "http://auditor/",
- "-m", "98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG",
+ "-m", exchange_master_pub,
"-r", "auditor.in",
- "-o", "test_exchange_api_home/.local/share/taler/auditors/auditor.out",
+ "-o", signed_keys_out,
NULL);
if (NULL == proc)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to run `taler-exchange-keyup`, is your PATH correct?\n");
+ "Failed to run `taler-auditor-sign`, is your PATH correct?\n");
return GNUNET_NO;
}
GNUNET_OS_process_wait (proc);
@@ -151,7 +163,7 @@ TALER_TESTING_prepare_exchange (const char *config_filename)
NULL, NULL, NULL,
"taler-exchange-dbinit",
"taler-exchange-dbinit",
- "-c", "test_exchange_api.conf",
+ "-c", config_filename,
"-r",
NULL);
if (NULL == proc)
@@ -245,22 +257,60 @@ TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys,
* Initialize scheduler loop and curl context for the testcase
* including starting and stopping the exchange using the given
* configuration file.
+ *
+ * @param main_cb routine containing all the commands to run.
+ * @param main_cb_cls closure for @a main_cb, typically NULL.
+ * @param config_file configuration file for the test-suite.
+ *
+ * @return FIXME: depends on what TALER_TESTING_setup returns.
*/
int
TALER_TESTING_setup_with_exchange (TALER_TESTING_Main main_cb,
void *main_cb_cls,
- const char *config_file)
+ const char *config_filename)
{
int result;
unsigned int iter;
struct GNUNET_OS_Process *exchanged;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ unsigned long long port;
+
+ cfg = GNUNET_CONFIGURATION_create ();
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_load (cfg,
+ config_filename))
+ return GNUNET_NO;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg,
+ "exchange",
+ "PORT",
+ &port))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "PORT");
+ GNUNET_CONFIGURATION_destroy (cfg);
+ return GNUNET_NO;
+ }
+
+ 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);
+ return GNUNET_NO;
+ }
+
exchanged = GNUNET_OS_start_process (GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-httpd",
"taler-exchange-httpd",
- "-c", config_file,
+ "-c", config_filename,
"-i",
NULL);
/* give child time to start and bind against the socket */
@@ -287,7 +337,9 @@ TALER_TESTING_setup_with_exchange (TALER_TESTING_Main main_cb,
fprintf (stderr, "\n");
result = TALER_TESTING_setup (main_cb,
- main_cb_cls);
+ main_cb_cls,
+ config_filename,
+ exchanged);
GNUNET_break (0 ==
GNUNET_OS_process_kill (exchanged,
SIGTERM));
@@ -325,10 +377,32 @@ TALER_TESTING_url_port_free (const char *url)
return GNUNET_OK;
}
+/**
+ * Allocate and return a piece of wire-details. Mostly, it adds
+ * the bank_url to the JSON.
+ *
+ * @param template the wire-details template.
+ * @param bank_url the bank_url
+ *
+ * @return the filled out and stringified wire-details. To
+ * be manually free'd.
+ */
+char *
+TALER_TESTING_make_wire_details (const char *template,
+ const char *bank_url)
+{
+ json_t *jtemplate;
+
+ GNUNET_assert (NULL != (jtemplate = json_loads
+ (template, JSON_REJECT_DUPLICATES, NULL)));
+ GNUNET_assert (0 == json_object_set
+ (jtemplate, "bank_url", json_string (bank_url)));
+ return json_dumps (jtemplate, JSON_COMPACT);
+}
/**
* Prepare launching a fakebank. Check that the configuration
- * file has the right option, and that the port is avaiable.
+ * file has the right option, and that the port is available.
* If everything is OK, return the configured URL of the fakebank.
*
* @param config_filename configuration file to use
diff --git a/src/exchange-lib/testing_api_loop.c b/src/exchange-lib/testing_api_loop.c
index 8c2364984..f22c99d86 100644
--- a/src/exchange-lib/testing_api_loop.c
+++ b/src/exchange-lib/testing_api_loop.c
@@ -2,18 +2,21 @@
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
+ TALER is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
+
/**
* @file exchange-lib/testing_api_loop.c
* @brief main interpreter loop for testcases
@@ -28,73 +31,12 @@
#include "taler_testing_lib.h"
#include "taler_fakebank_lib.h"
-
-/**
- * Global state of the interpreter, used by a command
- * to access information about other commands.
- */
-struct TALER_TESTING_Interpreter
-{
-
- /**
- * Commands the interpreter will run.
- */
- struct TALER_TESTING_Command *commands;
-
- /**
- * Interpreter task (if one is scheduled).
- */
- struct GNUNET_SCHEDULER_Task *task;
-
- /**
- * ID of task called whenever we get a SIGCHILD.
- * Used for #TALER_TESTING_wait_for_sigchld().
- */
- struct GNUNET_SCHEDULER_Task *child_death_task;
-
- /**
- * Main execution context for the main loop.
- */
- struct GNUNET_CURL_Context *ctx;
-
- /**
- * Context for running the CURL event loop.
- */
- struct GNUNET_CURL_RescheduleContext *rc;
-
- /**
- * Handle to our fakebank, if #TALER_TESTING_run_with_fakebank() was used.
- * Otherwise NULL.
- */
- struct TALER_FAKEBANK_Handle *fakebank;
-
- /**
- * Task run on timeout.
- */
- struct GNUNET_SCHEDULER_Task *timeout_task;
-
- /**
- * Instruction pointer. Tells #interpreter_run() which
- * instruction to run next.
- */
- unsigned int ip;
-
- /**
- * Result of the testcases, #GNUNET_OK on success
- */
- int result;
-
-};
-
-
/**
* Pipe used to communicate child death via signal.
* Must be global, as used in signal handler!
*/
static struct GNUNET_DISK_PipeHandle *sigpipe;
-
-
/**
* Lookup command by label.
*
@@ -103,8 +45,9 @@ static struct GNUNET_DISK_PipeHandle *sigpipe;
* @return NULL if command was not found
*/
const struct TALER_TESTING_Command *
-TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is,
- const char *label)
+TALER_TESTING_interpreter_lookup_command
+ (struct TALER_TESTING_Interpreter *is,
+ const char *label)
{
const struct TALER_TESTING_Command *cmd;
@@ -131,23 +74,35 @@ TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is,
* Obtain main execution context for the main loop.
*/
struct GNUNET_CURL_Context *
-TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is)
+TALER_TESTING_interpreter_get_context
+ (struct TALER_TESTING_Interpreter *is)
{
return is->ctx;
}
struct TALER_FAKEBANK_Handle *
-TALER_TESTING_interpreter_get_fakebank (struct TALER_TESTING_Interpreter *is)
+TALER_TESTING_interpreter_get_fakebank
+ (struct TALER_TESTING_Interpreter *is)
{
return is->fakebank;
}
+/**
+ * Run tests starting the "fakebank" first. The "fakebank"
+ * is a C minimalist version of the human-oriented Python bank,
+ * which is also part of the Taler project.
+ *
+ * @param is pointer to the interpreter state
+ * @param commands the list of commands to execute
+ * @param bank_url the url the fakebank is supposed to run on
+ */
void
-TALER_TESTING_run_with_fakebank (struct TALER_TESTING_Interpreter *is,
- struct TALER_TESTING_Command *commands,
- const char *bank_url)
+TALER_TESTING_run_with_fakebank
+ (struct TALER_TESTING_Interpreter *is,
+ struct TALER_TESTING_Command *commands,
+ const char *bank_url)
{
const char *port;
long pnum;
@@ -159,7 +114,7 @@ TALER_TESTING_run_with_fakebank (struct TALER_TESTING_Interpreter *is,
else
pnum = strtol (port + 1, NULL, 10);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Staring Fakebank on port %u (%s)\n",
+ "Starting Fakebank on port %u (%s)\n",
(unsigned int) pnum,
bank_url);
is->fakebank = TALER_FAKEBANK_start ((uint16_t) pnum);
@@ -192,8 +147,7 @@ TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *i)
if (GNUNET_SYSERR == i->result)
return; /* ignore, we already failed! */
i->ip++;
- i->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
- i);
+ i->task = GNUNET_SCHEDULER_add_now (&interpreter_run, i);
}
@@ -201,21 +155,25 @@ TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *i)
* Current command failed, clean up and fail the test case.
*/
void
-TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *i)
+TALER_TESTING_interpreter_fail
+ (struct TALER_TESTING_Interpreter *is)
{
- i->result = GNUNET_SYSERR;
+ // FIXME: disconnect from the exchange.
+ is->result = GNUNET_SYSERR;
+ // this cleans up too.
GNUNET_SCHEDULER_shutdown ();
}
/**
* Create command array terminator.
+ *
+ * @return a end-command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_end (void)
{
static struct TALER_TESTING_Command cmd;
-
return cmd;
}
@@ -224,7 +182,8 @@ TALER_TESTING_cmd_end (void)
* Obtain current label.
*/
const char *
-TALER_TESTING_interpreter_get_current_label (struct TALER_TESTING_Interpreter *is)
+TALER_TESTING_interpreter_get_current_label
+ (struct TALER_TESTING_Interpreter *is)
{
struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
@@ -278,6 +237,10 @@ do_shutdown (void *cls)
for (unsigned int j=0;NULL != (cmd = &is->commands[j])->label;j++)
cmd->cleanup (cmd->cls,
cmd);
+ if (NULL != is->exchange)
+ {
+ TALER_EXCHANGE_disconnect (is->exchange);
+ }
if (NULL != is->task)
{
GNUNET_SCHEDULER_cancel (is->task);
@@ -351,7 +314,7 @@ maint_child_death (void *cls)
sizeof (c)));
if (GNUNET_OK !=
TALER_TESTING_get_trait_process (cmd,
- NULL,
+ 0,
&processp))
{
GNUNET_break (0);
@@ -361,6 +324,14 @@ maint_child_death (void *cls)
GNUNET_OS_process_wait (*processp);
GNUNET_OS_process_destroy (*processp);
*processp = NULL;
+
+ if (GNUNET_OK == is->reload_keys)
+ {
+ GNUNET_break (0 == GNUNET_OS_process_kill
+ (is->exchanged, SIGUSR1));
+ sleep (5); /* make sure signal was received and processed */
+ }
+
TALER_TESTING_interpreter_next (is);
}
@@ -372,7 +343,8 @@ maint_child_death (void *cls)
* with the next command.
*/
void
-TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is)
+TALER_TESTING_wait_for_sigchld
+ (struct TALER_TESTING_Interpreter *is)
{
const struct GNUNET_DISK_FileHandle *pr;
@@ -384,42 +356,66 @@ TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is)
pr,
&maint_child_death,
is);
-
}
+/**
+ * Run the testsuite. FIXME: explain why the commands are
+ * copied into the state.
+ *
+ * @param is the interpreter state
+ * @param commands the list of command to execute
+ */
void
TALER_TESTING_run (struct TALER_TESTING_Interpreter *is,
struct TALER_TESTING_Command *commands)
{
unsigned int i;
-
+ /* get the number of commands */
for (i=0;NULL != commands[i].label;i++) ;
+
is->commands = GNUNET_new_array (i + 1,
struct TALER_TESTING_Command);
memcpy (is->commands,
commands,
sizeof (struct TALER_TESTING_Command) * i);
- is->timeout_task
- = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
- (GNUNET_TIME_UNIT_SECONDS, 300),
- &do_timeout,
- is);
- GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
- is);
- is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
- is);
+ is->timeout_task = GNUNET_SCHEDULER_add_delayed
+ (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300),
+ &do_timeout, is);
+ GNUNET_SCHEDULER_add_shutdown (&do_shutdown, is);
+ is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, is);
}
+/**
+ * Information used by the wrapper around the main
+ * "run" method.
+ */
struct MainContext
{
+ /**
+ * Main "run" method.
+ */
TALER_TESTING_Main main_cb;
+ /**
+ * Closure for "run".
+ */
void *main_cb_cls;
+ /**
+ * Interpreter state.
+ */
struct TALER_TESTING_Interpreter *is;
+ /**
+ * Configuration filename. The wrapper uses it to fetch
+ * the exchange port number; We could have passed the port
+ * number here, but having the config filename seems more
+ * generic.
+ */
+ const char *config_filename;
+
};
@@ -433,58 +429,170 @@ sighandler_child_death ()
static char c;
int old_errno = errno; /* back-up errno */
- GNUNET_break (1 ==
- GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
- (sigpipe,
- GNUNET_DISK_PIPE_END_WRITE),
- &c, sizeof (c)));
+ GNUNET_break (1 == GNUNET_DISK_file_write
+ (GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_WRITE),
+ &c, sizeof (c)));
errno = old_errno; /* restore errno */
}
+/**
+ * Called once a connection to the exchange has been
+ * established.
+ *
+ * @param cls closure, typically, the "run" method containing
+ * all the commands to be run, and a closure for it.
+ * @param keys the exchange's keys.
+ * @param compat protocol compatibility information.
+ */
+void
+cert_cb (void *cls,
+ const struct TALER_EXCHANGE_Keys *keys,
+ enum TALER_EXCHANGE_VersionCompatibility compat)
+{
+ struct MainContext *main_ctx = cls;
+
+ if (NULL == keys)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Got NULL response for /keys\n");
+
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Got %d DK from /keys\n",
+ keys->num_denom_keys);
+
+ main_ctx->is->key_generation++;
+ main_ctx->is->keys = keys;
+
+ /* /keys has been called for some reason and
+ * the interpreter is already running. */
+ if (GNUNET_YES == main_ctx->is->working)
+ return;
+
+ main_ctx->is->working = GNUNET_YES;
+
+ /* Very first start of tests, call "run()" */
+ if (1 == main_ctx->is->key_generation)
+ {
+ main_ctx->main_cb (main_ctx->main_cb_cls,
+ main_ctx->is);
+ return;
+ }
+
+ /* Tests already started, just trigger the
+ * next command. */
+ GNUNET_SCHEDULER_add_now (&interpreter_run,
+ 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 (void *cls)
{
struct MainContext *main_ctx = cls;
struct TALER_TESTING_Interpreter *is = main_ctx->is;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ char *exchange_url;
+ long long unsigned int exchange_port;
- is->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
- &is->rc);
+ cfg = GNUNET_CONFIGURATION_create ();
+ if (GNUNET_OK != GNUNET_CONFIGURATION_load
+ (cfg, main_ctx->config_filename))
+ return;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg,
+ "exchange",
+ "PORT",
+ &exchange_port))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "PORT");
+ GNUNET_CONFIGURATION_destroy (cfg);
+ return;
+ }
+ GNUNET_asprintf (&exchange_url,
+ "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);
- main_ctx->main_cb (main_ctx->main_cb_cls,
- main_ctx->is);
+
+ GNUNET_assert ( NULL !=
+ (is->exchange = TALER_EXCHANGE_connect (is->ctx,
+ exchange_url,
+ cert_cb,
+ main_ctx)) );
+ GNUNET_free (exchange_url);
+ GNUNET_CONFIGURATION_destroy (cfg);
+
}
/**
- * Initialize scheduler loop and curl context for the testcase.
+ * Install signal handlers plus schedules the main wrapper
+ * around the "run" method.
+ *
+ * @param main_cb the "run" method which coontains all the
+ * commands.
+ * @param main_cb_cls a closure for "run", typically NULL.
+ * @param config_filename configuration filename.
+ * @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..
+ *
+ * @return FIXME: not sure what 'is.result' is at this stage.
*/
int
TALER_TESTING_setup (TALER_TESTING_Main main_cb,
- void *main_cb_cls)
+ void *main_cb_cls,
+ const char *config_filename,
+ struct GNUNET_OS_Process *exchanged)
{
struct TALER_TESTING_Interpreter is;
struct MainContext main_ctx = {
.main_cb = main_cb,
.main_cb_cls = main_cb_cls,
- .is = &is
+ /* 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. */
+ .config_filename = config_filename
};
struct GNUNET_SIGNAL_Context *shc_chld;
-
+ /* zero-ing the state */
memset (&is,
0,
sizeof (is));
+ is.exchanged = exchanged;
sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO,
GNUNET_NO, GNUNET_NO);
GNUNET_assert (NULL != sigpipe);
- shc_chld = GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD,
- &sighandler_child_death);
+ shc_chld = GNUNET_SIGNAL_handler_install
+ (GNUNET_SIGCHLD, &sighandler_child_death);
GNUNET_SCHEDULER_run (&main_wrapper,
&main_ctx);
GNUNET_SIGNAL_handler_uninstall (shc_chld);
GNUNET_DISK_pipe_close (sigpipe);
sigpipe = NULL;
+
+ /*FIXME: ?? */
return is.result;
}
diff --git a/src/exchange-lib/testing_api_trait_blinding_key.c b/src/exchange-lib/testing_api_trait_blinding_key.c
index d23fd93b2..c9415cae9 100644
--- a/src/exchange-lib/testing_api_trait_blinding_key.c
+++ b/src/exchange-lib/testing_api_trait_blinding_key.c
@@ -40,22 +40,22 @@
*/
int
TALER_TESTING_get_trait_blinding_key (const struct TALER_TESTING_Command *cmd,
- const char *selector,
+ unsigned int index,
struct TALER_DenominationBlindingKeyP **blinding_key)
{
return cmd->traits (cmd->cls,
(void **) blinding_key,
TALER_TESTING_TRAIT_BLINDING_KEY,
- selector);
+ index);
}
struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_blinding_key (const char *selector,
+TALER_TESTING_make_trait_blinding_key (unsigned int index,
const struct TALER_DenominationBlindingKeyP *blinding_key)
{
struct TALER_TESTING_Trait ret = {
- .selector = selector,
+ .index = index,
.trait_name = TALER_TESTING_TRAIT_BLINDING_KEY,
.ptr = (const void *) blinding_key
};
diff --git a/src/exchange-lib/testing_api_trait_coin_priv.c b/src/exchange-lib/testing_api_trait_coin_priv.c
index 6ec4170ea..522f72859 100644
--- a/src/exchange-lib/testing_api_trait_coin_priv.c
+++ b/src/exchange-lib/testing_api_trait_coin_priv.c
@@ -2,18 +2,21 @@
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 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.
+ 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
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
+
/**
* @file exchange-lib/testing_api_trait_coin_priv.c
* @brief main interpreter loop for testcases
@@ -34,28 +37,31 @@
* Obtain a coin private key from a @a cmd.
*
* @param cmd command to extract trait from
- * @param selector which coin to pick if @a cmd has multiple on offer
+ * @param selector which coin to pick if @a cmd has multiple on
+ * offer
* @param coin_priv[out] set to the private key of the coin
* @return #GNUNET_OK on success
*/
int
-TALER_TESTING_get_trait_coin_priv (const struct TALER_TESTING_Command *cmd,
- const char *selector,
- struct TALER_CoinSpendPrivateKeyP **coin_priv)
+TALER_TESTING_get_trait_coin_priv
+ (const struct TALER_TESTING_Command *cmd,
+ unsigned int index,
+ struct TALER_CoinSpendPrivateKeyP **coin_priv)
{
return cmd->traits (cmd->cls,
(void **) coin_priv,
TALER_TESTING_TRAIT_COIN_PRIVATE_KEY,
- selector);
+ index);
}
struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_coin_priv (const char *selector,
- const struct TALER_CoinSpendPrivateKeyP *coin_priv)
+TALER_TESTING_make_trait_coin_priv
+ (unsigned int index,
+ const struct TALER_CoinSpendPrivateKeyP *coin_priv)
{
struct TALER_TESTING_Trait ret = {
- .selector = selector,
+ .index = index,
.trait_name = TALER_TESTING_TRAIT_COIN_PRIVATE_KEY,
.ptr = (const void *) coin_priv
};
diff --git a/src/exchange-lib/testing_api_trait_denom_pub.c b/src/exchange-lib/testing_api_trait_denom_pub.c
index 3113818bb..22d9a346b 100644
--- a/src/exchange-lib/testing_api_trait_denom_pub.c
+++ b/src/exchange-lib/testing_api_trait_denom_pub.c
@@ -2,16 +2,18 @@
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 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.
+ 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
+ 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/>
*/
/**
@@ -34,28 +36,39 @@
* Obtain a denomination public key from a @a cmd.
*
* @param cmd command to extract trait from
- * @param selector which coin to pick if @a cmd has multiple on offer
+ * @param selector which coin to pick if @a cmd has multiple on
+ * offer
* @param denom_pub[out] set to the blinding key of the coin
* @return #GNUNET_OK on success
*/
int
-TALER_TESTING_get_trait_denom_pub (const struct TALER_TESTING_Command *cmd,
- const char *selector,
- struct TALER_EXCHANGE_DenomPublicKey **denom_pub)
+TALER_TESTING_get_trait_denom_pub
+ (const struct TALER_TESTING_Command *cmd,
+ unsigned int index,
+ struct TALER_EXCHANGE_DenomPublicKey **denom_pub)
{
return cmd->traits (cmd->cls,
(void **) denom_pub,
TALER_TESTING_TRAIT_DENOM_PUB,
- selector);
+ index);
}
+/**
+ * Make a trait for a denomination public key.
+ *
+ * @param selector in case the trait provides multiple
+ * objects, this parameter extracts a particular one.
+ * @param denom_pub pointer to the data to be returned from
+ * this trait
+ */
struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_denom_pub (const char *selector,
- const struct TALER_EXCHANGE_DenomPublicKey *denom_pub)
+TALER_TESTING_make_trait_denom_pub
+ (unsigned int index,
+ const struct TALER_EXCHANGE_DenomPublicKey *denom_pub)
{
struct TALER_TESTING_Trait ret = {
- .selector = selector,
+ .index = index,
.trait_name = TALER_TESTING_TRAIT_DENOM_PUB,
.ptr = (const void *) denom_pub
};
@@ -63,5 +76,4 @@ TALER_TESTING_make_trait_denom_pub (const char *selector,
return ret;
}
-
/* end of testing_api_trait_denom_pub.c */
diff --git a/src/exchange-lib/testing_api_trait_denom_sig.c b/src/exchange-lib/testing_api_trait_denom_sig.c
index 66785c7f4..9578277ce 100644
--- a/src/exchange-lib/testing_api_trait_denom_sig.c
+++ b/src/exchange-lib/testing_api_trait_denom_sig.c
@@ -40,22 +40,22 @@
*/
int
TALER_TESTING_get_trait_denom_sig (const struct TALER_TESTING_Command *cmd,
- const char *selector,
+ unsigned int index,
struct TALER_DenominationSignature **denom_sig)
{
return cmd->traits (cmd->cls,
(void **) denom_sig,
TALER_TESTING_TRAIT_DENOM_SIG,
- selector);
+ index);
}
struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_denom_sig (const char *selector,
+TALER_TESTING_make_trait_denom_sig (unsigned int index,
const struct TALER_DenominationSignature *denom_sig)
{
struct TALER_TESTING_Trait ret = {
- .selector = selector,
+ .index = index,
.trait_name = TALER_TESTING_TRAIT_DENOM_SIG,
.ptr = (const void *) denom_sig
};
diff --git a/src/exchange-lib/testing_api_trait_fresh_coin.c b/src/exchange-lib/testing_api_trait_fresh_coin.c
new file mode 100644
index 000000000..985591ba3
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_fresh_coin.c
@@ -0,0 +1,74 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_fresh_coin.c
+ * @brief traits to offer fresh conins (after "melt" operations)
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+#define TALER_TESTING_TRAIT_FRESH_COINS "fresh-coins"
+
+/**
+ * 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 fresh_coins[out] will point to array of fresh coins
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_fresh_coins
+ (const struct TALER_TESTING_Command *cmd,
+ unsigned int index,
+ struct FreshCoin **fresh_coins)
+{
+ return cmd->traits (cmd->cls,
+ (void **) fresh_coins,
+ TALER_TESTING_TRAIT_FRESH_COINS,
+ index);
+}
+
+/**
+ * @param selector associate the object with this "tag"
+ * @param fresh_coins the array of fresh coins to offer
+ *
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_fresh_coins
+ (unsigned int index,
+ struct FreshCoin *fresh_coins)
+{
+ struct TALER_TESTING_Trait ret = {
+ .index = index,
+ .trait_name = TALER_TESTING_TRAIT_FRESH_COINS,
+ .ptr = (const void *) fresh_coins
+ };
+ return ret;
+}
+
+/* end of testing_api_trait_fresh_coin.c */
diff --git a/src/exchange-lib/testing_api_trait_key_peer.c b/src/exchange-lib/testing_api_trait_key_peer.c
new file mode 100644
index 000000000..d4e207c54
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_key_peer.c
@@ -0,0 +1,82 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_trait_key_peer.c
+ * @brief traits to offer peer's (private) keys
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+/**
+ * NOTE: calling it "peer" key to make clear it is _not a coin_
+ * key.
+ */
+
+#define TALER_TESTING_TRAIT_KEY_PEER "key-peer"
+
+/**
+ * Obtain a private key from a "peer". Used e.g. to obtain
+ * a merchant's priv to sign a /track request.
+ *
+ * @param index (tipically zero) which key to return if they
+ * exist in an array.
+ * @param selector which coin to pick if @a cmd has multiple on
+ * offer
+ * @param priv[out] set to the key coming from @a cmd.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_peer_key
+ (const struct TALER_TESTING_Command *cmd,
+ unsigned int index,
+ const struct GNUNET_CRYPTO_EddsaPrivateKey **priv)
+{
+ return cmd->traits (cmd->cls,
+ (void **) priv,
+ TALER_TESTING_TRAIT_KEY_PEER,
+ index);
+}
+
+/**
+ * @param index (tipically zero) which key to return if they
+ * exist in an array.
+ * @param priv 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_peer_key
+ (unsigned int index,
+ struct GNUNET_CRYPTO_EddsaPrivateKey *priv)
+{
+ struct TALER_TESTING_Trait ret = {
+ .index = index,
+ .trait_name = TALER_TESTING_TRAIT_KEY_PEER,
+ .ptr = (const void *) priv
+ };
+ return ret;
+}
+
+/* end of testing_api_trait_key_peer.c */
diff --git a/src/exchange-lib/testing_api_trait_number.c b/src/exchange-lib/testing_api_trait_number.c
new file mode 100644
index 000000000..8f011dcae
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_number.c
@@ -0,0 +1,74 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_trait_number.c
+ * @brief traits to offer numbers
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+#define TALER_TESTING_TRAIT_NUMBER "number"
+
+/**
+ * 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_uint
+ (const struct TALER_TESTING_Command *cmd,
+ unsigned int index,
+ unsigned int **n)
+{
+ return cmd->traits (cmd->cls,
+ (void **) n,
+ TALER_TESTING_TRAIT_NUMBER,
+ 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_uint
+ (unsigned int index,
+ const unsigned int *n)
+{
+ struct TALER_TESTING_Trait ret = {
+ .index = index,
+ .trait_name = TALER_TESTING_TRAIT_NUMBER,
+ .ptr = (const void *) n
+ };
+ return ret;
+}
+
+/* end of testing_api_trait_number.c */
diff --git a/src/exchange-lib/testing_api_trait_process.c b/src/exchange-lib/testing_api_trait_process.c
index 877eca973..e3c1bdf4e 100644
--- a/src/exchange-lib/testing_api_trait_process.c
+++ b/src/exchange-lib/testing_api_trait_process.c
@@ -40,22 +40,22 @@
*/
int
TALER_TESTING_get_trait_process (const struct TALER_TESTING_Command *cmd,
- const char *selector,
+ unsigned int index,
struct GNUNET_OS_Process ***processp)
{
return cmd->traits (cmd->cls,
(void **) processp,
TALER_TESTING_TRAIT_PROCESS,
- selector);
+ index);
}
struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_process (const char *selector,
+TALER_TESTING_make_trait_process (unsigned int index,
struct GNUNET_OS_Process **processp)
{
struct TALER_TESTING_Trait ret = {
- .selector = selector,
+ .index = index,
.trait_name = TALER_TESTING_TRAIT_PROCESS,
.ptr = (const void *) processp
};
diff --git a/src/exchange-lib/testing_api_trait_reserve_priv.c b/src/exchange-lib/testing_api_trait_reserve_priv.c
index a849f7653..fb80a064c 100644
--- a/src/exchange-lib/testing_api_trait_reserve_priv.c
+++ b/src/exchange-lib/testing_api_trait_reserve_priv.c
@@ -39,22 +39,22 @@
*/
int
TALER_TESTING_get_trait_reserve_priv (const struct TALER_TESTING_Command *cmd,
- const char *selector,
+ unsigned int index,
struct TALER_ReservePrivateKeyP **reserve_priv)
{
return cmd->traits (cmd->cls,
(void **) reserve_priv,
TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY,
- selector);
+ index);
}
struct TALER_TESTING_Trait
-TALER_TESTING_make_trait_reserve_priv (const char *selector,
+TALER_TESTING_make_trait_reserve_priv (unsigned int index,
const struct TALER_ReservePrivateKeyP *reserve_priv)
{
struct TALER_TESTING_Trait ret = {
- .selector = selector,
+ .index = index,
.trait_name = TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY,
.ptr = (const void *) reserve_priv
};
diff --git a/src/exchange-lib/testing_api_trait_string.c b/src/exchange-lib/testing_api_trait_string.c
new file mode 100644
index 000000000..308c4ea24
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_string.c
@@ -0,0 +1,209 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_trait_string.c
+ * @brief offers strings traits. Mostly used to offer
+ * stringified JSONs.
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+#define TALER_TESTING_TRAIT_WIRE_DETAILS "wire-details"
+#define TALER_TESTING_TRAIT_CONTRACT_TERMS "contract-terms"
+#define TALER_TESTING_TRAIT_TRANSFER_SUBJECT "transfer-subject"
+#define TALER_TESTING_TRAIT_AMOUNT "amount"
+
+/**
+ * Obtain contract terms from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param index always (?) zero, as one command sticks
+ * to one bank account
+ * @param contract_terms[out] where to write the contract
+ * terms.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_contract_terms
+ (const struct TALER_TESTING_Command *cmd,
+ unsigned int index,
+ const char **contract_terms)
+{
+ return cmd->traits (cmd->cls,
+ (void **) contract_terms,
+ TALER_TESTING_TRAIT_CONTRACT_TERMS,
+ index);
+}
+
+/**
+ * @param index always (?) zero, as one command sticks
+ * to one bank account
+ * @param contract_terms contract terms to offer
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_contract_terms
+ (unsigned int index,
+ const char *contract_terms)
+{
+ struct TALER_TESTING_Trait ret = {
+ .index = index,
+ .trait_name = TALER_TESTING_TRAIT_CONTRACT_TERMS,
+ .ptr = (const void *) contract_terms
+ };
+ return ret;
+}
+
+
+/**
+ * Obtain wire details from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param index always (?) zero, as one command sticks
+ * to one bank account
+ * @param wire_details[out] where to write the wire details.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_wire_details
+ (const struct TALER_TESTING_Command *cmd,
+ unsigned int index,
+ const char **wire_details)
+{
+ return cmd->traits (cmd->cls,
+ (void **) wire_details,
+ TALER_TESTING_TRAIT_WIRE_DETAILS,
+ index);
+}
+
+/**
+ * Offer wire details in a trait.
+ *
+ * @param index always (?) zero, as one command sticks
+ * to one bank account
+ * @param wire_details wire details to offer
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_wire_details
+ (unsigned int index,
+ const char *wire_details)
+{
+ struct TALER_TESTING_Trait ret = {
+ .index = index,
+ .trait_name = TALER_TESTING_TRAIT_WIRE_DETAILS,
+ .ptr = (const void *) wire_details
+ };
+ return ret;
+}
+
+
+/**
+ * Obtain a transfer subject from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param index always (?) zero, as one command sticks
+ * to one bank transfer
+ * @param transfer_subject[out] where to write the wire details.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_transfer_subject
+ (const struct TALER_TESTING_Command *cmd,
+ unsigned int index,
+ char **transfer_subject)
+{
+ return cmd->traits (cmd->cls,
+ (void **) transfer_subject,
+ TALER_TESTING_TRAIT_TRANSFER_SUBJECT,
+ index);
+}
+
+/**
+ * Offer wire details in a trait.
+ *
+ * @param index always (?) zero, as one command sticks
+ * to one bank account
+ * @param wire_details wire details to offer
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_transfer_subject
+ (unsigned int index,
+ char *transfer_subject)
+{
+ struct TALER_TESTING_Trait ret = {
+ .index = index,
+ .trait_name = TALER_TESTING_TRAIT_TRANSFER_SUBJECT,
+ .ptr = (const void *) transfer_subject
+ };
+ return ret;
+}
+
+
+/**
+ * Obtain an amount from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param index which amount is to be picked, in case
+ * multiple are offered.
+ * @param amount[out] where to write the wire details.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_amount
+ (const struct TALER_TESTING_Command *cmd,
+ unsigned int index,
+ const char **amount)
+{
+ return cmd->traits (cmd->cls,
+ (void **) amount,
+ TALER_TESTING_TRAIT_AMOUNT,
+ index);
+}
+
+/**
+ * Offer amount in a trait.
+ *
+ * @param index which amount is to be picked, in case
+ * multiple are offered.
+ * @param amount the amount to offer
+ * @return the trait, to be put in the traits array of the command
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_amount
+ (unsigned int index,
+ const char *amount)
+{
+ struct TALER_TESTING_Trait ret = {
+ .index = index,
+ .trait_name = TALER_TESTING_TRAIT_AMOUNT,
+ .ptr = (const void *) amount
+ };
+ return ret;
+}
+
+
+/* end of testing_api_trait_string.c */
diff --git a/src/exchange-lib/testing_api_trait_wtid.c b/src/exchange-lib/testing_api_trait_wtid.c
new file mode 100644
index 000000000..140a2ac3e
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_wtid.c
@@ -0,0 +1,74 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2018 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange-lib/testing_api_trait_number.c
+ * @brief traits to offer numbers
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+#define TALER_TESTING_TRAIT_WTID "wtid"
+
+/**
+ * Obtain a WTID value from @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param index which WTID to pick if @a cmd has multiple on
+ * offer
+ * @param wtid[out] set to the wanted WTID.
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_wtid
+ (const struct TALER_TESTING_Command *cmd,
+ unsigned int index,
+ struct TALER_WireTransferIdentifierRawP **wtid)
+{
+ return cmd->traits (cmd->cls,
+ (void **) wtid,
+ TALER_TESTING_TRAIT_WTID,
+ index);
+}
+
+/**
+ * @param index associate the object with this index
+ * @param wtid 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_wtid
+ (unsigned int index,
+ struct TALER_WireTransferIdentifierRawP *wtid)
+{
+ struct TALER_TESTING_Trait ret = {
+ .index = index,
+ .trait_name = TALER_TESTING_TRAIT_WTID,
+ .ptr = (const void *) wtid
+ };
+ return ret;
+}
+
+/* end of testing_api_trait_number.c */
diff --git a/src/exchange-lib/testing_api_traits.c b/src/exchange-lib/testing_api_traits.c
index 3983d3209..c83c7e778 100644
--- a/src/exchange-lib/testing_api_traits.c
+++ b/src/exchange-lib/testing_api_traits.c
@@ -2,16 +2,18 @@
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 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.
+ 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
+ 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/>
*/
/**
@@ -28,11 +30,15 @@
#include "taler_testing_lib.h"
+/**
+ * End a trait array. Usually, commands offer several traits,
+ * and put them in arrays.
+ */
struct TALER_TESTING_Trait
TALER_TESTING_trait_end ()
{
struct TALER_TESTING_Trait end = {
- .selector = NULL,
+ .index = 0,
.trait_name = NULL,
.ptr = NULL
};
@@ -40,31 +46,35 @@ TALER_TESTING_trait_end ()
return end;
}
-
+/**
+ * Pick the chosen trait from the traits array.
+ *
+ * @param traits the traits array
+ * @param ret where to store the result
+ * @param selector which particular object in the trait should be
+ * returned
+ *
+ * @return GNUNET_OK if no error occurred, GNUNET_SYSERR otherwise
+ */
int
TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
void **ret,
const char *trait,
- const char *selector)
+ unsigned int index)
{
- for (unsigned int i=0;
- NULL != traits[i].trait_name;
- i++)
+ for (unsigned int i=0; NULL != traits[i].trait_name; i++)
{
- if ( (0 == strcmp (trait,
- traits[i].trait_name)) &&
- ( (NULL == selector) ||
- (0 == strcasecmp (selector,
- traits[i].selector) ) ) )
+ if ( (0 == strcmp (trait, traits[i].trait_name)) &&
+ (index == traits[i].index) )
{
*ret = (void *) traits[i].ptr;
return GNUNET_OK;
}
}
- /* FIXME: log */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Trait %s/%u not found.\n",
+ trait, index);
return GNUNET_SYSERR;
}
-
-
/* end of testing_api_traits.c */