commit c0228ee670fc971658abda363fc2c2090fa94546
parent fd8e8a5f730cdd8b13a14d28a3d8341e989986f6
Author: Bohdan Potuzhnyi <potub1@bfh.ch>
Date: Mon, 14 Oct 2024 10:28:07 +0000
adding test for donau
Diffstat:
8 files changed, 709 insertions(+), 0 deletions(-)
diff --git a/src/include/taler_merchant_donau.h b/src/include/taler_merchant_donau.h
@@ -25,6 +25,8 @@
#include <taler/taler_util.h>
#include <taler/taler_error_codes.h>
#include <taler/taler_exchange_service.h>
+#include "taler_merchant_service.h"
+#include <donau/donau_service.h>
#include <donau/donau_util.h>
#include <donau/donaudb_plugin.h>
@@ -145,4 +147,136 @@ typedef void
const json_t *donau_keys_json
);
+/**
+ * SPECIFICATION FOR REQUESTS /donau
+ */
+
+/**
+ * Handle for a GET /donau operation.
+ */
+struct TALER_MERCHANT_DonauInstanceGetHandle;
+
+/**
+ * Handle for a GET /donau operation.
+ */
+struct TALER_MERCHANT_DonauInstanceGetHandle;
+
+/**
+ * Individual Donau instance details.
+ */
+struct TALER_MERCHANT_DonauInstanceEntry
+{
+ /**
+ * The URL of the Donau service.
+ */
+ const char *donau_url;
+
+ /**
+ * Name of the charity associated with the Donau instance.
+ */
+ const char *charity_name;
+
+ /**
+ * Public key of the charity.
+ */
+ struct DONAU_CharityPublicKeyP charity_pub_key;
+
+ /**
+ * ID of the charity on Donau.
+ */
+ uint64_t charity_id;
+
+ /**
+ * Maximum donation amount per year allowed for the charity.
+ */
+ struct TALER_Amount charity_max_per_year;
+
+ /**
+ * Total donations received by the charity in the current year.
+ */
+ struct TALER_Amount charity_receipts_to_date;
+
+ /**
+ * The current year being tracked for donations.
+ */
+ int64_t current_year;
+
+ /**
+ * Additional key information related to the Donau instance.
+ */
+ struct DONAU_Keys donau_keys;
+};
+
+/**
+ * Response for a GET /donau request.
+ */
+struct TALER_MERCHANT_DonauInstanceGetResponse
+{
+ /**
+ * HTTP response details.
+ */
+ struct TALER_MERCHANT_HttpResponse hr;
+
+ /**
+ * Details depending on the HTTP status.
+ */
+ union
+ {
+ /**
+ * Details for #MHD_HTTP_OK.
+ */
+ struct
+ {
+ /**
+ * Length of the @e donau_instances array.
+ */
+ unsigned int donau_instances_length;
+
+ /**
+ * Details about the Donau instance.
+ */
+ struct TALER_MERCHANT_DonauInstanceEntry *donau_instances;
+ } ok;
+ } details;
+};
+
+/**
+ * Function called with the result of the GET /donau operation.
+ *
+ * @param cls closure
+ * @param dir response details
+ */
+typedef void
+(*TALER_MERCHANT_DonauInstanceGetCallback)(
+ void *cls,
+ const struct TALER_MERCHANT_DonauInstanceGetResponse *dir);
+
+/**
+ * Get the details on the Donau instance. This will connect to the
+ * Donau service backend and retrieve information about the instance.
+ * The respective information will be passed to the @a cb once available.
+ *
+ * @param ctx the context
+ * @param cb function to call with the backend's instance information
+ * @param cb_cls closure for @a cb
+ * @return the instance handle; NULL upon error
+ */
+struct TALER_MERCHANT_DonauInstanceGetHandle *
+TALER_MERCHANT_donau_instances_get(
+ struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ TALER_MERCHANT_DonauInstanceGetCallback cb,
+ void *cb_cls);
+
+
+/**
+ * Cancel the GET /donau instances operation. Must not be called by clients
+ * after the callback was invoked.
+ *
+ * @param dgh request to cancel.
+ */
+void
+TALER_MERCHANT_donau_instances_get_cancel(
+ struct TALER_MERCHANT_DonauInstanceGetHandle *dgh);
+
#endif
\ No newline at end of file
diff --git a/src/include/taler_merchant_testing_lib.h b/src/include/taler_merchant_testing_lib.h
@@ -1790,6 +1790,25 @@ TALER_TESTING_cmd_checkserver2 (const char *label,
const char *expected_header,
const char *expected_body);
+/**
+ * Define a "GET /donau" CMD.
+ *
+ * @param label command label.
+ * @param url base URL of the Donau service serving the
+ * GET /donau_instances request.
+ * @param expected_http_status expected HTTP response code.
+ * @param ... NULL-terminated list of labels (const char *) of
+ * donau instances (commands) we expect to be returned in the list
+ * (assuming @a expected_http_status is #MHD_HTTP_OK)
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_merchant_get_donau_instances(const char *label,
+ const char *url,
+ unsigned int expected_http_status,
+ ...);
+
+
/* ****** Specific traits supported by this component ******* */
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
@@ -82,6 +82,14 @@ libtalermerchant_la_LIBADD = \
-lcurl \
$(XLIB)
+if HAVE_DONAU
+ libtalermerchant_la_SOURCES += \
+ merchant_api_get_donau_instance.c
+
+ libtalermerchant_la_LIBADD += \
+ -ldonau
+endif
+
check_PROGRAMS = \
test_merchant_api_common
diff --git a/src/lib/merchant_api_get_donau_instance.c b/src/lib/merchant_api_get_donau_instance.c
@@ -0,0 +1,281 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Lesser General Public License as published by the Free Software
+ Foundation; either version 2.1, 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License along with
+ TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file merchant_api_get_donau_instance.c
+ * @brief Implementation of the GET /donau request of the merchant's HTTP API
+ * @author Bohdan Potuzhnyi
+ * @author Vlada Svirsh
+ */
+
+#include "platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+/* DONAU RELATED IMPORTS */
+#include "taler_merchant_donau.h"
+#include <donau/donau_service.h>
+
+/**
+ * Handle for a GET /donau operation.
+ */
+struct TALER_MERCHANT_DonauInstanceGetHandle
+{
+ /**
+ * The URL for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_MERCHANT_DonauInstanceGetCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+};
+
+/**
+ * Parse Donau instance information from @a ia.
+ *
+ * @param ia JSON array (or NULL!) with Donau instance data
+ * @param igr response to fill
+ * @param dgh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_donau_instances (const json_t *ia,
+ struct TALER_MERCHANT_DonauInstanceGetResponse *igr,
+ struct TALER_MERCHANT_DonauInstanceGetHandle *dgh)
+{
+ unsigned int instances_len = (unsigned int) json_array_size(ia);
+
+ if ((json_array_size(ia) != (size_t)instances_len))
+ {
+ GNUNET_break(0);
+ return GNUNET_SYSERR;
+ }
+
+ struct TALER_MERCHANT_DonauInstanceEntry instances[GNUNET_NZL (instances_len)];
+ size_t index;
+ json_t *value;
+
+ json_array_foreach(ia, index, value)
+ {
+ struct TALER_MERCHANT_DonauInstanceEntry *instance = &instances[index];
+
+ /* Specification for parsing each instance */
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string("donau_url",
+ &instance->donau_url),
+ GNUNET_JSON_spec_string("charity_name",
+ &instance->charity_name),
+ GNUNET_JSON_spec_fixed_auto("charity_pub_key",
+ &instance->charity_pub_key),
+ GNUNET_JSON_spec_uint64("charity_id",
+ &instance->charity_id),
+ TALER_JSON_spec_amount_any("charity_max_per_year",
+ &instance->charity_max_per_year),
+ TALER_JSON_spec_amount_any("charity_receipts_to_date",
+ &instance->charity_receipts_to_date),
+ GNUNET_JSON_spec_int64("current_year",
+ &instance->current_year),
+ GNUNET_JSON_spec_end()
+ };
+
+ if (GNUNET_OK != GNUNET_JSON_parse(value, spec, NULL, NULL))
+ {
+ GNUNET_break_op(0);
+ return GNUNET_SYSERR;
+ }
+
+ /* Parse the Donau keys */
+ const json_t *donau_keys_json = json_object_get(value, "donau_keys");
+ if (NULL == donau_keys_json)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Failed to parse donau keys JSON\n");
+ return GNUNET_SYSERR;
+ }
+
+ /* Convert donau_keys_json to donau_keys struct */
+ struct DONAU_Keys *donau_keys_ptr = DONAU_keys_from_json(donau_keys_json);
+ if (NULL == donau_keys_ptr)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Failed to convert donau keys from JSON\n");
+ return GNUNET_SYSERR;
+ }
+
+ /* Assign donau_keys to the instance details */
+ instance->donau_keys = *donau_keys_ptr;
+ }
+
+ igr->details.ok.donau_instances_length = instances_len;
+ igr->details.ok.donau_instances = instances;
+ dgh->cb(dgh->cb_cls, igr);
+ dgh->cb = NULL; /* just to be sure */
+
+ return GNUNET_OK;
+}
+
+/**
+ * Function called when we're done processing the
+ * HTTP /donau request.
+ *
+ * @param cls the `struct TALER_MERCHANT_DonauInstanceGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_get_donau_instances_finished(void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_MERCHANT_DonauInstanceGetHandle *dgh = cls;
+ const json_t *json = response;
+ struct TALER_MERCHANT_DonauInstanceGetResponse igr = {
+ .hr.http_status = (unsigned int)response_code,
+ .hr.reply = json
+ };
+
+ dgh->job = NULL;
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "Got /donau response with status code %u\n",
+ (unsigned int)response_code);
+
+ switch (response_code)
+ {
+ case MHD_HTTP_OK:
+ {
+ const json_t *donau_instances;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const("donau_instances", &donau_instances),
+ GNUNET_JSON_spec_end()
+ };
+
+ if (GNUNET_OK != GNUNET_JSON_parse(json, spec, NULL, NULL))
+ {
+ igr.hr.http_status = 0;
+ igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+
+ if (GNUNET_OK == parse_donau_instances(donau_instances, &igr, dgh))
+ {
+ TALER_MERCHANT_donau_instances_get_cancel(dgh);
+ return;
+ }
+
+ igr.hr.http_status = 0;
+ igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+
+ case MHD_HTTP_UNAUTHORIZED:
+ igr.hr.ec = TALER_JSON_get_error_code(json);
+ igr.hr.hint = TALER_JSON_get_error_hint(json);
+ break;
+
+ case MHD_HTTP_NOT_FOUND:
+ igr.hr.ec = TALER_JSON_get_error_code(json);
+ igr.hr.hint = TALER_JSON_get_error_hint(json);
+ break;
+
+ default:
+ igr.hr.ec = TALER_JSON_get_error_code(json);
+ igr.hr.hint = TALER_JSON_get_error_hint(json);
+ GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int)response_code,
+ (int)igr.hr.ec);
+ break;
+ }
+
+ dgh->cb(dgh->cb_cls, &igr);
+ TALER_MERCHANT_donau_instances_get_cancel(dgh);
+}
+
+/**
+ * Initiate the GET /donau request.
+ *
+ * @param ctx CURL context
+ * @param backend_url base URL for the backend
+ * @param cb callback function to handle the response
+ * @param cb_cls closure for the callback function
+ * @return the handle for the operation, or NULL on error
+ */
+struct TALER_MERCHANT_DonauInstanceGetHandle *
+TALER_MERCHANT_donau_instances_get (struct GNUNET_CURL_Context *ctx,
+ const char *backend_url,
+ TALER_MERCHANT_DonauInstanceGetCallback cb,
+ void *cb_cls)
+{
+ struct TALER_MERCHANT_DonauInstanceGetHandle *dgh;
+ CURL *eh;
+
+ dgh = GNUNET_new(struct TALER_MERCHANT_DonauInstanceGetHandle);
+ dgh->ctx = ctx;
+ dgh->cb = cb;
+ dgh->cb_cls = cb_cls;
+
+ dgh->url = TALER_url_join(backend_url, "private/donau", NULL);
+ if (NULL == dgh->url)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct request URL.\n");
+ GNUNET_free(dgh);
+ return NULL;
+ }
+
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting URL '%s'\n", dgh->url);
+
+ eh = TALER_MERCHANT_curl_easy_get_(dgh->url);
+ dgh->job = GNUNET_CURL_job_add(ctx, eh, &handle_get_donau_instances_finished, dgh);
+
+ return dgh;
+}
+
+/**
+ * Cancel the GET /donau operation.
+ *
+ * @param dgh the handle for the operation to be canceled
+ */
+void
+TALER_MERCHANT_donau_instances_get_cancel (struct TALER_MERCHANT_DonauInstanceGetHandle *dgh)
+{
+ if (NULL != dgh->job)
+ GNUNET_CURL_job_cancel(dgh->job);
+
+ GNUNET_free(dgh->url);
+ GNUNET_free(dgh);
+}
diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am
@@ -32,6 +32,7 @@ libtalermerchanttesting_la_SOURCES = \
testing_api_cmd_abort_order.c \
testing_api_cmd_claim_order.c \
testing_api_cmd_depositcheck.c \
+ testing_api_cmd_get_donau_instances.c \
testing_api_cmd_get_instance.c \
testing_api_cmd_get_instances.c \
testing_api_cmd_get_orders.c \
diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c
@@ -1817,6 +1817,13 @@ run (void *cls,
TALER_TESTING_cmd_end ()
};
+ struct TALER_TESTING_Command donau[] = {
+ TALER_TESTING_cmd_merchant_get_donau_instances(
+ "get-donau-instance",
+ "http://localhost:8080/donau",
+ MHD_HTTP_OK),
+ };
+
struct TALER_TESTING_Command commands[] = {
/* general setup */
TALER_TESTING_cmd_run_fakebank (
@@ -2141,6 +2148,18 @@ run (void *cls,
repurchase),
TALER_TESTING_cmd_batch ("tokens",
tokens),
+ #ifdef HAVE_DONAU_DONAU_SERVICE_H
+ TALER_TESTING_cmd_system_start (
+ "start-donau-service",
+ config_file,
+ "-D",
+ "-u", "exchange-account-exchange",
+ NULL),
+
+ TALER_TESTING_cmd_batch ("donau",
+ donau),
+
+ #endif
/**
* End the suite.
*/
diff --git a/src/testing/test_merchant_api.conf b/src/testing/test_merchant_api.conf
@@ -2,6 +2,7 @@
#
[PATHS]
TALER_TEST_HOME = test_merchant_api_home/
+DONAU_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/donau-system-runtime/
[taler]
CURRENCY = EUR
@@ -71,3 +72,39 @@ WIRE_GATEWAY_AUTH_METHOD = NONE
[admin-accountcredentials-exchange]
WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/"
WIRE_GATEWAY_AUTH_METHOD = NONE
+
+[donau]
+TERMS_ETAG = tos
+PRIVACY_ETAG = 0
+PORT = 8079
+DB = postgres
+DOMAIN = "Bern"
+BASE_URL = "http://localhost:8079/"
+SERVE = tcp
+EXPIRE_IDLE_SLEEP_INTERVAL ="1 s"
+EXPIRE_LEGAL = 5
+
+[donaudb-postgres]
+CONFIG = "postgres:///talercheck"
+
+[donau-secmod-cs]
+LOOKAHEAD_SIGN = "24 days"
+KEY_DIR = ${TALER_TEST_HOME}taler/exchange-secmod-cs/keys
+OVERLAP_DURATION = 0
+SM_PRIV_KEY = ${DONAU_RUNTIME_DIR}donau-secmod-cs/secmod-private-key
+UNIXPATH = ${DONAU_RUNTIME_DIR}donau-secmod-cs/server.sock
+
+[donau-secmod-rsa]
+LOOKAHEAD_SIGN = "24 days"
+KEY_DIR = ${TALER_TEST_HOME}taler/exchange-secmod-rsa/keys
+OVERLAP_DURATION = 0
+SM_PRIV_KEY = ${DONAU_RUNTIME_DIR}donau-secmod-rsa/secmod-private-key
+UNIXPATH = ${DONAU_RUNTIME_DIR}donau-secmod-rsa/server.sock
+
+[donau-secmod-eddsa]
+LOOKAHEAD_SIGN = "24 days"
+DURATION = "14 days"
+KEY_DIR = ${TALER_TEST_HOME}taler/exchange-secmod-eddsa/keys
+OVERLAP_DURATION = 0
+SM_PRIV_KEY = ${DONAU_RUNTIME_DIR}donau-secmod-eddsa/secmod-private-key
+UNIXPATH = ${DONAU_RUNTIME_DIR}donau-secmod-eddsa/server.sock
+\ No newline at end of file
diff --git a/src/testing/testing_api_cmd_get_donau_instances.c b/src/testing/testing_api_cmd_get_donau_instances.c
@@ -0,0 +1,209 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2021 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 testing_api_cmd_kyc_get.c
+ * @brief command to test kyc_get request
+ * @author Bohdan Potuzhnyi
+ * @author Vlada Svirsh
+ */
+#include "platform.h"
+#include <taler/taler_exchange_service.h>
+#include <taler/taler_testing_lib.h>
+#include "taler_merchant_service.h"
+#include "taler_merchant_testing_lib.h"
+#include "taler_merchant_donau.h"
+
+/**
+ * State of a "GET donau instances" CMD.
+ */
+struct GetDonauInstancesState
+{
+ /**
+ * Handle for a "GET donau instances" request.
+ */
+ struct TALER_MERCHANT_DonauInstanceGetHandle *dgh;
+
+ /**
+ * The interpreter state.
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * Base URL of the merchant/donau service.
+ */
+ const char *url;
+
+ /**
+ * Expected HTTP response code.
+ */
+ unsigned int expected_http_status;
+
+ /**
+ * A NULL-terminated array of Donau instance names.
+ */
+ const char **instances;
+
+ /**
+ * The number of instances in the `instances` array.
+ */
+ unsigned int instances_length;
+};
+
+/**
+ * Callback for a /get/donau operation.
+ *
+ * @param cls closure for this function
+ * @param response response details from the Donau service
+ */
+static void
+get_donau_instances_cb (void *cls,
+ const struct TALER_MERCHANT_DonauInstanceGetResponse *response)
+{
+ struct GetDonauInstancesState *gis = cls;
+
+ gis->dgh = NULL;
+
+ if (gis->expected_http_status != response->hr.http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u (%d) to command %s\n",
+ response->hr.http_status,
+ (int) response->hr.ec,
+ TALER_TESTING_interpreter_get_current_label (gis->is));
+ TALER_TESTING_interpreter_fail (gis->is);
+ return;
+ }
+
+ // Process successful response if the status is 200 OK
+ if (MHD_HTTP_OK == response->hr.http_status)
+ {
+ unsigned int instance_count = response->details.ok.donau_instances_length;
+ if (instance_count != gis->instances_length)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Expected %u instances, but got %u\n",
+ gis->instances_length, instance_count);
+ TALER_TESTING_interpreter_fail(gis->is);
+ return;
+ }
+
+ for (unsigned int i = 0; i < instance_count; ++i)
+ {
+ const struct TALER_MERCHANT_DonauInstanceEntry *instance = &response->details.ok.donau_instances[i];
+
+ if (0 != strcmp(instance->charity_name, gis->instances[i]))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Instance charity name mismatch: expected %s, got %s\n",
+ gis->instances[i], instance->charity_name);
+ TALER_TESTING_interpreter_fail(gis->is);
+ return;
+ }
+ }
+ }
+
+ // Continue to the next test command
+ TALER_TESTING_interpreter_next (gis->is);
+}
+
+/**
+ * Run the "GET donau instance" CMD.
+ *
+ * @param cls closure.
+ * @param cmd command being run now.
+ * @param is interpreter state.
+ */
+static void
+get_donau_instances_run(void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct GetDonauInstancesState *gis = cls;
+
+ gis->is = is;
+ gis->dgh = TALER_MERCHANT_donau_instances_get(
+ TALER_TESTING_interpreter_get_context(is),
+ gis->url,
+ &get_donau_instances_cb,
+ gis);
+
+ GNUNET_assert(NULL != gis->dgh); // Ensure the request handle is valid
+}
+
+/**
+ * Free the state of a "GET donau instance" CMD, and possibly
+ * cancel a pending operation thereof.
+ *
+ * @param cls closure.
+ * @param cmd command being run.
+ */
+static void
+get_donau_instances_cleanup(void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct GetDonauInstancesState *gis = cls;
+
+ if (NULL != gis->dgh)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "GET /donau instances operation did not complete\n");
+ TALER_MERCHANT_donau_instances_get_cancel(gis->dgh);
+ }
+
+ GNUNET_free(gis); // Free the state
+}
+
+/**
+ * Create a new command to GET Donau instances.
+ *
+ * @param label label for the command
+ * @param url the base URL of the Donau service
+ * @param expected_http_status expected HTTP status code
+ * @return a new testing command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_merchant_get_donau_instances(const char *label,
+ const char *url,
+ unsigned int expected_http_status,
+ ...)
+{
+ struct GetDonauInstancesState *gis;
+
+ gis = GNUNET_new(struct GetDonauInstancesState);
+ gis->url = url;
+ gis->expected_http_status = expected_http_status;
+ gis->instances_length = 0;
+
+ const char *instance_label;
+ va_list ap;
+ va_start(ap, expected_http_status);
+ while (NULL != (instance_label = va_arg(ap, const char *)))
+ {
+ GNUNET_array_append(gis->instances, gis->instances_length, instance_label);
+ }
+ va_end(ap);
+
+ struct TALER_TESTING_Command cmd = {
+ .cls = gis,
+ .label = label,
+ .run = &get_donau_instances_run,
+ .cleanup = &get_donau_instances_cleanup
+ };
+
+ return cmd;
+}
+