Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2020 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU General Public License as
7 : published by the Free Software Foundation; either version 3, or
8 : (at your option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not, see
17 : <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file testing_api_cmd_get_instance.c
21 : * @brief command to test GET /instance/$ID
22 : * @author Christian Grothoff
23 : */
24 : #include "platform.h"
25 : #include <taler/taler_exchange_service.h>
26 : #include <taler/taler_testing_lib.h>
27 : #include "taler_merchant_service.h"
28 : #include "taler_merchant_testing_lib.h"
29 :
30 :
31 : /**
32 : * State of a "GET instance" CMD.
33 : */
34 : struct GetInstanceState
35 : {
36 :
37 : /**
38 : * Handle for a "GET instance" request.
39 : */
40 : struct TALER_MERCHANT_InstanceGetHandle *igh;
41 :
42 : /**
43 : * The interpreter state.
44 : */
45 : struct TALER_TESTING_Interpreter *is;
46 :
47 : /**
48 : * Base URL of the merchant serving the request.
49 : */
50 : const char *merchant_url;
51 :
52 : /**
53 : * ID of the instance to run GET for.
54 : */
55 : const char *instance_id;
56 :
57 : /**
58 : * Reference for a POST or PATCH /instances CMD (optional).
59 : */
60 : const char *instance_reference;
61 :
62 : /**
63 : * Whether we should check the instance's accounts or not.
64 : */
65 : bool cmp_accounts;
66 :
67 : /**
68 : * The accounts of the merchant we expect to be active.
69 : */
70 : const char **active_accounts;
71 :
72 : /**
73 : * The length of @e active_accounts.
74 : */
75 : unsigned int active_accounts_length;
76 :
77 : /**
78 : * The accounts of the merchant we expect to be inactive.
79 : */
80 : const char **inactive_accounts;
81 :
82 : /**
83 : * The length of @e inactive_accounts.
84 : */
85 : unsigned int inactive_accounts_length;
86 :
87 : /**
88 : * Expected HTTP response code.
89 : */
90 : unsigned int http_status;
91 :
92 : };
93 :
94 :
95 : /**
96 : * Callback for a /get/instance/$ID operation.
97 : *
98 : * @param cls closure for this function
99 : * @param hr HTTP response
100 : * @param accounts_length how many bank accounts the instance has
101 : * @param accounts the list of the instance's bank accounts
102 : * @param details all the details related to this particular instance
103 : */
104 : static void
105 0 : get_instance_cb (void *cls,
106 : const struct TALER_MERCHANT_HttpResponse *hr,
107 : unsigned int accounts_length,
108 : const struct TALER_MERCHANT_Account accounts[],
109 : const struct TALER_MERCHANT_InstanceDetails *details)
110 : {
111 : /* FIXME, deeper checks should be implemented here (for accounts). */
112 0 : struct GetInstanceState *gis = cls;
113 : const struct TALER_TESTING_Command *instance_cmd;
114 :
115 0 : instance_cmd = TALER_TESTING_interpreter_lookup_command (
116 : gis->is,
117 : gis->instance_reference);
118 :
119 0 : gis->igh = NULL;
120 0 : if (gis->http_status != hr->http_status)
121 : {
122 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
123 : "Unexpected response code %u (%d) to command %s\n",
124 : hr->http_status,
125 : (int) hr->ec,
126 : TALER_TESTING_interpreter_get_current_label (gis->is));
127 0 : TALER_TESTING_interpreter_fail (gis->is);
128 0 : return;
129 : }
130 0 : switch (hr->http_status)
131 : {
132 0 : case MHD_HTTP_OK:
133 : {
134 : const char **name;
135 :
136 0 : if (GNUNET_OK !=
137 0 : TALER_TESTING_get_trait_instance_name (instance_cmd,
138 : &name))
139 0 : TALER_TESTING_interpreter_fail (gis->is);
140 0 : if (0 != strcmp (details->name,
141 : *name))
142 : {
143 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
144 : "Instance name does not match: Got `%s', wanted `%s'\n",
145 : details->name,
146 : *name);
147 0 : TALER_TESTING_interpreter_fail (gis->is);
148 0 : return;
149 : }
150 : }
151 : {
152 : const json_t *address;
153 :
154 0 : if (GNUNET_OK !=
155 0 : TALER_TESTING_get_trait_address (instance_cmd,
156 : &address))
157 0 : TALER_TESTING_interpreter_fail (gis->is);
158 0 : if (1 != json_equal (details->address,
159 : address))
160 : {
161 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
162 : "Instance address does not match\n");
163 0 : TALER_TESTING_interpreter_fail (gis->is);
164 0 : return;
165 : }
166 : }
167 : {
168 : const struct json_t *jurisdiction;
169 :
170 0 : if (GNUNET_OK !=
171 0 : TALER_TESTING_get_trait_jurisdiction (instance_cmd,
172 : &jurisdiction))
173 0 : TALER_TESTING_interpreter_fail (gis->is);
174 0 : if (1 != json_equal (details->jurisdiction,
175 : jurisdiction))
176 : {
177 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
178 : "Instance jurisdiction does not match\n");
179 0 : TALER_TESTING_interpreter_fail (gis->is);
180 0 : return;
181 : }
182 : }
183 : {
184 : const struct TALER_Amount *default_max_wire_fee;
185 :
186 0 : if (GNUNET_OK !=
187 0 : TALER_TESTING_get_trait_max_wire_fee (instance_cmd,
188 : &default_max_wire_fee))
189 0 : TALER_TESTING_interpreter_fail (gis->is);
190 0 : if ((GNUNET_OK != TALER_amount_cmp_currency (
191 : details->default_max_wire_fee,
192 0 : default_max_wire_fee)) ||
193 0 : (0 != TALER_amount_cmp (details->default_max_wire_fee,
194 : default_max_wire_fee)))
195 : {
196 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
197 : "Instance default max wire fee does not match\n");
198 0 : TALER_TESTING_interpreter_fail (gis->is);
199 0 : return;
200 : }
201 : }
202 : {
203 : const uint32_t *default_wire_fee_amortization;
204 :
205 0 : if (GNUNET_OK !=
206 0 : TALER_TESTING_get_trait_wire_fee_amortization (instance_cmd,
207 : &
208 : default_wire_fee_amortization))
209 0 : TALER_TESTING_interpreter_fail (gis->is);
210 0 : if (details->default_wire_fee_amortization !=
211 0 : *default_wire_fee_amortization)
212 : {
213 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
214 : "Instance default wire fee amortization does not match\n");
215 0 : TALER_TESTING_interpreter_fail (gis->is);
216 0 : return;
217 : }
218 : }
219 : {
220 : const struct TALER_Amount *default_max_deposit_fee;
221 :
222 0 : if (GNUNET_OK !=
223 0 : TALER_TESTING_get_trait_max_deposit_fee (instance_cmd,
224 : &default_max_deposit_fee))
225 0 : TALER_TESTING_interpreter_fail (gis->is);
226 0 : if ( (GNUNET_OK !=
227 0 : TALER_amount_cmp_currency (
228 : details->default_max_deposit_fee,
229 0 : default_max_deposit_fee)) ||
230 0 : (0 != TALER_amount_cmp (details->default_max_deposit_fee,
231 : default_max_deposit_fee)) )
232 : {
233 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
234 : "Instance default max deposit fee %s does not match\n",
235 : TALER_amount2s (details->default_max_deposit_fee));
236 0 : TALER_TESTING_interpreter_fail (gis->is);
237 0 : return;
238 : }
239 : }
240 : {
241 : const struct GNUNET_TIME_Relative *default_wire_transfer_delay;
242 :
243 0 : if (GNUNET_OK !=
244 0 : TALER_TESTING_get_trait_wire_delay (instance_cmd,
245 : &default_wire_transfer_delay))
246 0 : TALER_TESTING_interpreter_fail (gis->is);
247 0 : if (details->default_wire_transfer_delay.rel_value_us !=
248 0 : default_wire_transfer_delay->rel_value_us)
249 : {
250 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
251 : "Instance default wire transfer delay does not match\n");
252 0 : TALER_TESTING_interpreter_fail (gis->is);
253 0 : return;
254 : }
255 : }
256 : {
257 : const struct GNUNET_TIME_Relative *default_pay_delay;
258 0 : if (GNUNET_OK !=
259 0 : TALER_TESTING_get_trait_pay_delay (instance_cmd,
260 : &default_pay_delay))
261 0 : TALER_TESTING_interpreter_fail (gis->is);
262 0 : if (details->default_pay_delay.rel_value_us !=
263 0 : default_pay_delay->rel_value_us)
264 : {
265 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
266 : "Instance default pay delay does not match\n");
267 0 : TALER_TESTING_interpreter_fail (gis->is);
268 0 : return;
269 : }
270 : }
271 : /* We aren't guaranteed an order for the accounts, so we just have to check
272 : that we can match each account returned with exactly one account
273 : expected. */
274 0 : if (gis->cmp_accounts)
275 0 : {
276 0 : unsigned int expected_accounts_length =
277 0 : gis->active_accounts_length + gis->inactive_accounts_length;
278 0 : unsigned int matches[accounts_length];
279 :
280 0 : if (accounts_length != expected_accounts_length)
281 : {
282 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
283 : "Accounts length does not match\n");
284 0 : TALER_TESTING_interpreter_fail (gis->is);
285 0 : return;
286 : }
287 :
288 0 : memset (matches,
289 : 0,
290 : sizeof (unsigned int) * accounts_length);
291 :
292 : // Compare the accounts
293 0 : for (unsigned int i = 0; i < accounts_length; ++i)
294 : {
295 0 : for (unsigned int j = 0; j < gis->active_accounts_length; ++j)
296 : {
297 0 : if ((0 == strcasecmp (accounts[i].payto_uri,
298 0 : gis->active_accounts[j])) &&
299 0 : (true == accounts[i].active))
300 : {
301 0 : matches[i] += 1;
302 : }
303 : }
304 0 : for (unsigned int j = 0; j < gis->inactive_accounts_length; ++j)
305 : {
306 0 : if ((0 == strcasecmp (accounts[i].payto_uri,
307 0 : gis->inactive_accounts[j])) &&
308 0 : (false == accounts[i].active))
309 : {
310 0 : matches[i] += 1;
311 : }
312 : }
313 : }
314 :
315 : // Each account should have exactly one match.
316 0 : for (unsigned int i = 0; i < accounts_length; ++i)
317 : {
318 0 : if (1 != matches[i])
319 : {
320 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
321 : "Instance account does not match\n");
322 0 : TALER_TESTING_interpreter_fail (gis->is);
323 0 : return;
324 : }
325 : }
326 : }
327 0 : break;
328 0 : case MHD_HTTP_UNAUTHORIZED:
329 0 : break;
330 0 : case MHD_HTTP_NOT_FOUND:
331 0 : break;
332 0 : default:
333 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
334 : "Unhandled HTTP status %u for GET instance ID.\n",
335 : hr->http_status);
336 : }
337 0 : TALER_TESTING_interpreter_next (gis->is);
338 : }
339 :
340 :
341 : /**
342 : * Run the "GET instance" CMD.
343 : *
344 : *
345 : * @param cls closure.
346 : * @param cmd command being run now.
347 : * @param is interpreter state.
348 : */
349 : static void
350 0 : get_instance_run (void *cls,
351 : const struct TALER_TESTING_Command *cmd,
352 : struct TALER_TESTING_Interpreter *is)
353 : {
354 0 : struct GetInstanceState *gis = cls;
355 :
356 0 : gis->is = is;
357 0 : gis->igh = TALER_MERCHANT_instance_get (is->ctx,
358 : gis->merchant_url,
359 : gis->instance_id,
360 : &get_instance_cb,
361 : gis);
362 0 : GNUNET_assert (NULL != gis->igh);
363 0 : }
364 :
365 :
366 : /**
367 : * Free the state of a "GET instance" CMD, and possibly
368 : * cancel a pending operation thereof.
369 : *
370 : * @param cls closure.
371 : * @param cmd command being run.
372 : */
373 : static void
374 0 : get_instance_cleanup (void *cls,
375 : const struct TALER_TESTING_Command *cmd)
376 : {
377 0 : struct GetInstanceState *gis = cls;
378 :
379 0 : if (NULL != gis->igh)
380 : {
381 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
382 : "GET /instances/$ID operation did not complete\n");
383 0 : TALER_MERCHANT_instance_get_cancel (gis->igh);
384 : }
385 0 : GNUNET_free (gis);
386 0 : }
387 :
388 :
389 : struct TALER_TESTING_Command
390 0 : TALER_TESTING_cmd_merchant_get_instance (const char *label,
391 : const char *merchant_url,
392 : const char *instance_id,
393 : unsigned int http_status,
394 : const char *instance_reference)
395 : {
396 : struct GetInstanceState *gis;
397 :
398 0 : gis = GNUNET_new (struct GetInstanceState);
399 0 : gis->merchant_url = merchant_url;
400 0 : gis->instance_id = instance_id;
401 0 : gis->http_status = http_status;
402 0 : gis->instance_reference = instance_reference;
403 0 : gis->cmp_accounts = false;
404 : {
405 0 : struct TALER_TESTING_Command cmd = {
406 : .cls = gis,
407 : .label = label,
408 : .run = &get_instance_run,
409 : .cleanup = &get_instance_cleanup
410 : };
411 :
412 0 : return cmd;
413 : }
414 : }
415 :
416 :
417 : struct TALER_TESTING_Command
418 0 : TALER_TESTING_cmd_merchant_get_instance2 (const char *label,
419 : const char *merchant_url,
420 : const char *instance_id,
421 : unsigned int http_status,
422 : const char *instance_reference,
423 : const char *active_accounts[],
424 : unsigned int active_accounts_length,
425 : const char *inactive_accounts[],
426 : unsigned int inactive_accounts_length)
427 : {
428 : struct GetInstanceState *gis;
429 :
430 0 : gis = GNUNET_new (struct GetInstanceState);
431 0 : gis->merchant_url = merchant_url;
432 0 : gis->instance_id = instance_id;
433 0 : gis->http_status = http_status;
434 0 : gis->instance_reference = instance_reference;
435 0 : gis->cmp_accounts = true;
436 0 : gis->active_accounts = active_accounts;
437 0 : gis->active_accounts_length = active_accounts_length;
438 0 : gis->inactive_accounts = inactive_accounts;
439 0 : gis->inactive_accounts_length = inactive_accounts_length;
440 : {
441 0 : struct TALER_TESTING_Command cmd = {
442 : .cls = gis,
443 : .label = label,
444 : .run = &get_instance_run,
445 : .cleanup = &get_instance_cleanup
446 : };
447 :
448 0 : return cmd;
449 : }
450 : }
451 :
452 :
453 : /* end of testing_api_cmd_get_instance.c */
|