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 0 : struct GetInstanceState *gis = cls;
112 : const struct TALER_TESTING_Command *instance_cmd;
113 :
114 0 : instance_cmd = TALER_TESTING_interpreter_lookup_command (
115 : gis->is,
116 : gis->instance_reference);
117 :
118 0 : gis->igh = NULL;
119 0 : if (gis->http_status != hr->http_status)
120 : {
121 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
122 : "Unexpected response code %u (%d) to command %s\n",
123 : hr->http_status,
124 : (int) hr->ec,
125 : TALER_TESTING_interpreter_get_current_label (gis->is));
126 0 : TALER_TESTING_interpreter_fail (gis->is);
127 0 : return;
128 : }
129 0 : switch (hr->http_status)
130 : {
131 0 : case MHD_HTTP_OK:
132 : {
133 : const char **name;
134 :
135 0 : if (GNUNET_OK !=
136 0 : TALER_TESTING_get_trait_instance_name (instance_cmd,
137 : &name))
138 0 : TALER_TESTING_interpreter_fail (gis->is);
139 0 : if (0 != strcmp (details->name,
140 : *name))
141 : {
142 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
143 : "Instance name does not match: Got `%s', wanted `%s'\n",
144 : details->name,
145 : *name);
146 0 : TALER_TESTING_interpreter_fail (gis->is);
147 0 : return;
148 : }
149 : }
150 : {
151 : const json_t *address;
152 :
153 0 : if (GNUNET_OK !=
154 0 : TALER_TESTING_get_trait_address (instance_cmd,
155 : &address))
156 0 : TALER_TESTING_interpreter_fail (gis->is);
157 0 : if (1 != json_equal (details->address,
158 : address))
159 : {
160 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
161 : "Instance address does not match\n");
162 0 : TALER_TESTING_interpreter_fail (gis->is);
163 0 : return;
164 : }
165 : }
166 : {
167 : const struct json_t *jurisdiction;
168 :
169 0 : if (GNUNET_OK !=
170 0 : TALER_TESTING_get_trait_jurisdiction (instance_cmd,
171 : &jurisdiction))
172 0 : TALER_TESTING_interpreter_fail (gis->is);
173 0 : if (1 != json_equal (details->jurisdiction,
174 : jurisdiction))
175 : {
176 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
177 : "Instance jurisdiction does not match\n");
178 0 : TALER_TESTING_interpreter_fail (gis->is);
179 0 : return;
180 : }
181 : }
182 : {
183 : const struct TALER_Amount *default_max_wire_fee;
184 :
185 0 : if (GNUNET_OK !=
186 0 : TALER_TESTING_get_trait_max_wire_fee (instance_cmd,
187 : &default_max_wire_fee))
188 0 : TALER_TESTING_interpreter_fail (gis->is);
189 0 : if ((GNUNET_OK != TALER_amount_cmp_currency (
190 : details->default_max_wire_fee,
191 0 : default_max_wire_fee)) ||
192 0 : (0 != TALER_amount_cmp (details->default_max_wire_fee,
193 : default_max_wire_fee)))
194 : {
195 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
196 : "Instance default max wire fee does not match\n");
197 0 : TALER_TESTING_interpreter_fail (gis->is);
198 0 : return;
199 : }
200 : }
201 : {
202 : const uint32_t *default_wire_fee_amortization;
203 :
204 0 : if (GNUNET_OK !=
205 0 : TALER_TESTING_get_trait_wire_fee_amortization (instance_cmd,
206 : &
207 : default_wire_fee_amortization))
208 0 : TALER_TESTING_interpreter_fail (gis->is);
209 0 : if (details->default_wire_fee_amortization !=
210 0 : *default_wire_fee_amortization)
211 : {
212 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
213 : "Instance default wire fee amortization does not match\n");
214 0 : TALER_TESTING_interpreter_fail (gis->is);
215 0 : return;
216 : }
217 : }
218 : {
219 : const struct TALER_Amount *default_max_deposit_fee;
220 :
221 0 : if (GNUNET_OK !=
222 0 : TALER_TESTING_get_trait_max_deposit_fee (instance_cmd,
223 : &default_max_deposit_fee))
224 0 : TALER_TESTING_interpreter_fail (gis->is);
225 0 : if ( (GNUNET_OK !=
226 0 : TALER_amount_cmp_currency (
227 : details->default_max_deposit_fee,
228 0 : default_max_deposit_fee)) ||
229 0 : (0 != TALER_amount_cmp (details->default_max_deposit_fee,
230 : default_max_deposit_fee)) )
231 : {
232 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
233 : "Instance default max deposit fee %s does not match\n",
234 : TALER_amount2s (details->default_max_deposit_fee));
235 0 : TALER_TESTING_interpreter_fail (gis->is);
236 0 : return;
237 : }
238 : }
239 : {
240 : const struct GNUNET_TIME_Relative *default_wire_transfer_delay;
241 :
242 0 : if (GNUNET_OK !=
243 0 : TALER_TESTING_get_trait_wire_delay (instance_cmd,
244 : &default_wire_transfer_delay))
245 0 : TALER_TESTING_interpreter_fail (gis->is);
246 0 : if (details->default_wire_transfer_delay.rel_value_us !=
247 0 : default_wire_transfer_delay->rel_value_us)
248 : {
249 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
250 : "Instance default wire transfer delay does not match\n");
251 0 : TALER_TESTING_interpreter_fail (gis->is);
252 0 : return;
253 : }
254 : }
255 : {
256 : const struct GNUNET_TIME_Relative *default_pay_delay;
257 0 : if (GNUNET_OK !=
258 0 : TALER_TESTING_get_trait_pay_delay (instance_cmd,
259 : &default_pay_delay))
260 0 : TALER_TESTING_interpreter_fail (gis->is);
261 0 : if (details->default_pay_delay.rel_value_us !=
262 0 : default_pay_delay->rel_value_us)
263 : {
264 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
265 : "Instance default pay delay does not match\n");
266 0 : TALER_TESTING_interpreter_fail (gis->is);
267 0 : return;
268 : }
269 : }
270 : /* We aren't guaranteed an order for the accounts, so we just have to check
271 : that we can match each account returned with exactly one account
272 : expected. */
273 0 : if (gis->cmp_accounts)
274 0 : {
275 0 : unsigned int expected_accounts_length =
276 0 : gis->active_accounts_length + gis->inactive_accounts_length;
277 0 : unsigned int matches[accounts_length];
278 :
279 0 : if (accounts_length != expected_accounts_length)
280 : {
281 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
282 : "Accounts length does not match\n");
283 0 : TALER_TESTING_interpreter_fail (gis->is);
284 0 : return;
285 : }
286 :
287 0 : memset (matches,
288 : 0,
289 : sizeof (unsigned int) * accounts_length);
290 :
291 : /* Compare the accounts */
292 0 : for (unsigned int i = 0; i < accounts_length; ++i)
293 : {
294 0 : for (unsigned int j = 0; j < gis->active_accounts_length; ++j)
295 : {
296 0 : if ((0 == strcasecmp (accounts[i].payto_uri,
297 0 : gis->active_accounts[j])) &&
298 0 : (true == accounts[i].active))
299 : {
300 0 : matches[i] += 1;
301 : }
302 : }
303 0 : for (unsigned int j = 0; j < gis->inactive_accounts_length; ++j)
304 : {
305 0 : if ((0 == strcasecmp (accounts[i].payto_uri,
306 0 : gis->inactive_accounts[j])) &&
307 0 : (false == accounts[i].active))
308 : {
309 0 : matches[i] += 1;
310 : }
311 : }
312 : }
313 :
314 : // Each account should have exactly one match.
315 0 : for (unsigned int i = 0; i < accounts_length; ++i)
316 : {
317 0 : if (1 != matches[i])
318 : {
319 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
320 : "Instance account does not match\n");
321 0 : TALER_TESTING_interpreter_fail (gis->is);
322 0 : return;
323 : }
324 : }
325 : }
326 0 : break;
327 0 : case MHD_HTTP_UNAUTHORIZED:
328 0 : break;
329 0 : case MHD_HTTP_NOT_FOUND:
330 0 : break;
331 0 : default:
332 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
333 : "Unhandled HTTP status %u for GET instance ID.\n",
334 : hr->http_status);
335 : }
336 0 : TALER_TESTING_interpreter_next (gis->is);
337 : }
338 :
339 :
340 : /**
341 : * Run the "GET instance" CMD.
342 : *
343 : *
344 : * @param cls closure.
345 : * @param cmd command being run now.
346 : * @param is interpreter state.
347 : */
348 : static void
349 0 : get_instance_run (void *cls,
350 : const struct TALER_TESTING_Command *cmd,
351 : struct TALER_TESTING_Interpreter *is)
352 : {
353 0 : struct GetInstanceState *gis = cls;
354 :
355 0 : gis->is = is;
356 0 : gis->igh = TALER_MERCHANT_instance_get (is->ctx,
357 : gis->merchant_url,
358 : gis->instance_id,
359 : &get_instance_cb,
360 : gis);
361 0 : GNUNET_assert (NULL != gis->igh);
362 0 : }
363 :
364 :
365 : /**
366 : * Free the state of a "GET instance" CMD, and possibly
367 : * cancel a pending operation thereof.
368 : *
369 : * @param cls closure.
370 : * @param cmd command being run.
371 : */
372 : static void
373 0 : get_instance_cleanup (void *cls,
374 : const struct TALER_TESTING_Command *cmd)
375 : {
376 0 : struct GetInstanceState *gis = cls;
377 :
378 0 : if (NULL != gis->igh)
379 : {
380 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
381 : "GET /instances/$ID operation did not complete\n");
382 0 : TALER_MERCHANT_instance_get_cancel (gis->igh);
383 : }
384 0 : GNUNET_free (gis);
385 0 : }
386 :
387 :
388 : struct TALER_TESTING_Command
389 0 : TALER_TESTING_cmd_merchant_get_instance (const char *label,
390 : const char *merchant_url,
391 : const char *instance_id,
392 : unsigned int http_status,
393 : const char *instance_reference)
394 : {
395 : struct GetInstanceState *gis;
396 :
397 0 : gis = GNUNET_new (struct GetInstanceState);
398 0 : gis->merchant_url = merchant_url;
399 0 : gis->instance_id = instance_id;
400 0 : gis->http_status = http_status;
401 0 : gis->instance_reference = instance_reference;
402 0 : gis->cmp_accounts = false;
403 : {
404 0 : struct TALER_TESTING_Command cmd = {
405 : .cls = gis,
406 : .label = label,
407 : .run = &get_instance_run,
408 : .cleanup = &get_instance_cleanup
409 : };
410 :
411 0 : return cmd;
412 : }
413 : }
414 :
415 :
416 : struct TALER_TESTING_Command
417 0 : TALER_TESTING_cmd_merchant_get_instance2 (const char *label,
418 : const char *merchant_url,
419 : const char *instance_id,
420 : unsigned int http_status,
421 : const char *instance_reference,
422 : const char *active_accounts[],
423 : unsigned int active_accounts_length,
424 : const char *inactive_accounts[],
425 : unsigned int inactive_accounts_length)
426 : {
427 : struct GetInstanceState *gis;
428 :
429 0 : gis = GNUNET_new (struct GetInstanceState);
430 0 : gis->merchant_url = merchant_url;
431 0 : gis->instance_id = instance_id;
432 0 : gis->http_status = http_status;
433 0 : gis->instance_reference = instance_reference;
434 0 : gis->cmp_accounts = true;
435 0 : gis->active_accounts = active_accounts;
436 0 : gis->active_accounts_length = active_accounts_length;
437 0 : gis->inactive_accounts = inactive_accounts;
438 0 : gis->inactive_accounts_length = inactive_accounts_length;
439 : {
440 0 : struct TALER_TESTING_Command cmd = {
441 : .cls = gis,
442 : .label = label,
443 : .run = &get_instance_run,
444 : .cleanup = &get_instance_cleanup
445 : };
446 :
447 0 : return cmd;
448 : }
449 : }
450 :
451 :
452 : /* end of testing_api_cmd_get_instance.c */
|