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_merchant_get_order.c
21 : * @brief command to test GET /private/orders/$ORDER_ID.
22 : * @author Jonathan Buchanan
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 for a GET /private/orders/$ORDER_ID CMD.
33 : */
34 : struct MerchantGetOrderState
35 : {
36 : /**
37 : * The merchant base URL.
38 : */
39 : const char *merchant_url;
40 :
41 : /**
42 : * Expected HTTP response code for this CMD.
43 : */
44 : unsigned int http_status;
45 :
46 : /**
47 : * The handle to the current GET /private/orders/$ORDER_ID request.
48 : */
49 : struct TALER_MERCHANT_OrderMerchantGetHandle *ogh;
50 :
51 : /**
52 : * The interpreter state.
53 : */
54 : struct TALER_TESTING_Interpreter *is;
55 :
56 : /**
57 : * Reference to a command that created an order.
58 : */
59 : const char *order_reference;
60 :
61 : /**
62 : * Expected order status.
63 : */
64 : enum TALER_MERCHANT_OrderStatusCode osc;
65 :
66 : /**
67 : * A NULL-terminated list of refunds associated with this order.
68 : */
69 : const char **refunds;
70 :
71 : /**
72 : * The length of @e refunds.
73 : */
74 : unsigned int refunds_length;
75 :
76 : /**
77 : * A NULL-terminated list of transfers associated with this order.
78 : */
79 : const char **transfers;
80 :
81 : /**
82 : * The length of @e transfers.
83 : */
84 : unsigned int transfers_length;
85 :
86 : /**
87 : * A list of forget commands that apply to this order's contract terms.
88 : */
89 : const char **forgets;
90 :
91 : /**
92 : * The length of @e forgets.
93 : */
94 : unsigned int forgets_length;
95 :
96 : /**
97 : * Whether the order was refunded or not.
98 : */
99 : bool refunded;
100 :
101 : /**
102 : * Whether the order was wired or not.
103 : */
104 : bool wired;
105 : };
106 :
107 :
108 : /**
109 : * Forget part of the contract terms.
110 : *
111 : * @param cls pointer to the result of the forget operation.
112 : * @param object_id name of the object to forget.
113 : * @param parent parent of the object at @e object_id.
114 : */
115 : static void
116 0 : apply_forget (void *cls,
117 : const char *object_id,
118 : json_t *parent)
119 : {
120 0 : int *res = cls;
121 :
122 0 : if (GNUNET_SYSERR ==
123 0 : TALER_JSON_contract_part_forget (parent,
124 : object_id))
125 0 : *res = GNUNET_SYSERR;
126 0 : }
127 :
128 :
129 : /**
130 : * Callback to process a GET /orders/$ID request
131 : *
132 : * @param cls closure
133 : * @param osr order status response details
134 : */
135 : static void
136 0 : merchant_get_order_cb (
137 : void *cls,
138 : const struct TALER_MERCHANT_OrderStatusResponse *osr)
139 : {
140 0 : struct MerchantGetOrderState *gos = cls;
141 :
142 0 : gos->ogh = NULL;
143 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
144 : "GET /private/orders/$ID completed with status %u\n",
145 : osr->hr.http_status);
146 0 : if (gos->http_status != osr->hr.http_status)
147 : {
148 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
149 : "Unexpected response code %u (%d) to command %s\n",
150 : osr->hr.http_status,
151 : (int) osr->hr.ec,
152 : TALER_TESTING_interpreter_get_current_label (gos->is));
153 0 : TALER_TESTING_interpreter_fail (gos->is);
154 0 : return;
155 : }
156 0 : switch (osr->hr.http_status)
157 : {
158 0 : case MHD_HTTP_OK:
159 0 : if (gos->osc != osr->details.success.status)
160 : {
161 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
162 : "Order paid does not match\n");
163 0 : TALER_TESTING_interpreter_fail (gos->is);
164 0 : return;
165 : }
166 0 : switch (osr->details.success.status)
167 : {
168 0 : case TALER_MERCHANT_OSC_PAID:
169 : {
170 : const struct TALER_TESTING_Command *order_cmd;
171 : struct TALER_Amount refunded_total;
172 :
173 0 : order_cmd = TALER_TESTING_interpreter_lookup_command (
174 : gos->is,
175 : gos->order_reference);
176 :
177 : {
178 : const json_t *expected_contract_terms;
179 : json_t *ct;
180 :
181 0 : if (GNUNET_OK !=
182 0 : TALER_TESTING_get_trait_contract_terms (order_cmd,
183 : &expected_contract_terms))
184 : {
185 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
186 : "Could not fetch order contract terms\n");
187 0 : TALER_TESTING_interpreter_fail (gos->is);
188 0 : return;
189 : }
190 :
191 0 : ct = json_deep_copy (expected_contract_terms);
192 :
193 : /* Apply all forgets, then compare */
194 0 : for (unsigned int i = 0; i < gos->forgets_length; ++i)
195 : {
196 : const struct TALER_TESTING_Command *forget_cmd;
197 : const uint32_t *paths_length;
198 :
199 0 : forget_cmd = TALER_TESTING_interpreter_lookup_command (
200 : gos->is,
201 0 : gos->forgets[i]);
202 :
203 0 : if (GNUNET_OK !=
204 0 : TALER_TESTING_get_trait_paths_length (forget_cmd,
205 : &paths_length))
206 : {
207 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
208 : "Couldn't fetch forget paths length\n");
209 0 : TALER_TESTING_interpreter_fail (gos->is);
210 0 : return;
211 : }
212 :
213 0 : for (unsigned int j = 0; j < *paths_length; ++j)
214 : {
215 : const char **path;
216 0 : int res = GNUNET_OK;
217 :
218 0 : if (GNUNET_OK !=
219 0 : TALER_TESTING_get_trait_paths (forget_cmd,
220 : j,
221 : &path))
222 : {
223 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
224 : "Couldn't fetch forget path\n");
225 0 : TALER_TESTING_interpreter_fail (gos->is);
226 0 : return;
227 : }
228 :
229 0 : GNUNET_assert (GNUNET_OK ==
230 : TALER_JSON_expand_path (ct,
231 : *path,
232 : &apply_forget,
233 : &res));
234 0 : GNUNET_assert (GNUNET_OK == res);
235 : }
236 : }
237 :
238 0 : if (1 != json_equal (ct,
239 : osr->details.success.details.paid.contract_terms))
240 : {
241 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
242 : "Order contract terms do not match\n");
243 0 : TALER_TESTING_interpreter_fail (gos->is);
244 0 : return;
245 : }
246 :
247 0 : json_decref (ct);
248 : }
249 0 : if (gos->wired != osr->details.success.details.paid.wired)
250 : {
251 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
252 : "Order wired does not match\n");
253 0 : TALER_TESTING_interpreter_fail (gos->is);
254 0 : return;
255 : }
256 0 : if (gos->transfers_length != osr->details.success.details.paid.wts_len)
257 : {
258 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
259 : "Number of transfers found does not match\n");
260 0 : TALER_TESTING_interpreter_fail (gos->is);
261 0 : return;
262 : }
263 0 : for (unsigned int i = 0; i < gos->transfers_length; ++i)
264 : {
265 : const struct TALER_TESTING_Command *transfer_cmd;
266 :
267 0 : transfer_cmd = TALER_TESTING_interpreter_lookup_command (
268 : gos->is,
269 0 : gos->transfers[i]);
270 : {
271 : const struct TALER_WireTransferIdentifierRawP *wtid;
272 :
273 0 : if (GNUNET_OK !=
274 0 : TALER_TESTING_get_trait_wtid (transfer_cmd,
275 : &wtid))
276 : {
277 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
278 : "Could not fetch wire transfer id\n");
279 0 : TALER_TESTING_interpreter_fail (gos->is);
280 0 : return;
281 : }
282 0 : if (0 != GNUNET_memcmp (wtid,
283 : &osr->details.success.details.paid.wts[i].
284 : wtid))
285 : {
286 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
287 : "Wire transfer id does not match\n");
288 0 : TALER_TESTING_interpreter_fail (gos->is);
289 0 : return;
290 : }
291 : }
292 : {
293 : const char **exchange_url;
294 :
295 0 : if (GNUNET_OK !=
296 0 : TALER_TESTING_get_trait_exchange_url (transfer_cmd,
297 : &exchange_url))
298 : {
299 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
300 : "Could not fetch wire transfer exchange url\n");
301 0 : TALER_TESTING_interpreter_fail (gos->is);
302 0 : return;
303 : }
304 0 : if (0 != strcmp (*exchange_url,
305 0 : osr->details.success.details.paid.wts[i].
306 : exchange_url))
307 : {
308 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
309 : "Wire transfer exchange url does not match\n");
310 0 : TALER_TESTING_interpreter_fail (gos->is);
311 0 : return;
312 : }
313 : }
314 : {
315 : struct TALER_Amount transfer_total;
316 : const struct TALER_Amount *transfer_amount;
317 : const struct TALER_Amount *transfer_fee;
318 :
319 0 : if ((GNUNET_OK !=
320 0 : TALER_TESTING_get_trait_amount (transfer_cmd,
321 0 : &transfer_amount)) ||
322 : (GNUNET_OK !=
323 0 : TALER_TESTING_get_trait_fee (transfer_cmd,
324 : &transfer_fee)))
325 : {
326 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
327 : "Could not fetch wire transfer amount/fee\n");
328 0 : TALER_TESTING_interpreter_fail (gos->is);
329 0 : return;
330 : }
331 0 : if (0 > TALER_amount_add (&transfer_total,
332 : transfer_amount,
333 : transfer_fee))
334 : {
335 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
336 : "Could not total wire transfer\n");
337 0 : TALER_TESTING_interpreter_fail (gos->is);
338 0 : return;
339 : }
340 0 : if ((GNUNET_OK !=
341 0 : TALER_amount_cmp_currency (
342 : &transfer_total,
343 0 : &osr->details.success.details.paid.wts[i].total_amount)) ||
344 0 : (0 != TALER_amount_cmp (
345 : &transfer_total,
346 0 : &osr->details.success.details.paid.wts[i].total_amount)))
347 : {
348 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
349 : "Wire transfer total does not match\n");
350 0 : TALER_TESTING_interpreter_fail (gos->is);
351 0 : return;
352 : }
353 : }
354 : }
355 0 : if (gos->refunded != osr->details.success.details.paid.refunded)
356 : {
357 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
358 : "Order refunded does not match\n");
359 0 : TALER_TESTING_interpreter_fail (gos->is);
360 0 : return;
361 : }
362 0 : if (gos->refunds_length !=
363 0 : osr->details.success.details.paid.refunds_len)
364 : {
365 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
366 : "Number of refunds found does not match\n");
367 0 : TALER_TESTING_interpreter_fail (gos->is);
368 0 : return;
369 : }
370 0 : if (0 < gos->refunds_length)
371 0 : GNUNET_assert (
372 : GNUNET_OK ==
373 : TALER_amount_set_zero (
374 : osr->details.success.details.paid.refund_amount.currency,
375 : &refunded_total));
376 0 : for (unsigned int i = 0; i < gos->refunds_length; ++i)
377 : {
378 : const struct TALER_TESTING_Command *refund_cmd;
379 :
380 0 : refund_cmd = TALER_TESTING_interpreter_lookup_command (
381 : gos->is,
382 0 : gos->refunds[i]);
383 : {
384 : const struct TALER_Amount *expected_amount;
385 0 : struct TALER_Amount *amount_found =
386 0 : &osr->details.success.details.paid.refunds[i].refund_amount;
387 :
388 0 : if (GNUNET_OK !=
389 0 : TALER_TESTING_get_trait_amount (refund_cmd,
390 : &expected_amount))
391 : {
392 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
393 : "Could not fetch refund amount\n");
394 0 : TALER_TESTING_interpreter_fail (gos->is);
395 0 : return;
396 : }
397 0 : GNUNET_assert (0 <= TALER_amount_add (&refunded_total,
398 : &refunded_total,
399 : amount_found));
400 0 : if ((GNUNET_OK !=
401 0 : TALER_amount_cmp_currency (expected_amount,
402 0 : &refunded_total)) ||
403 0 : (0 != TALER_amount_cmp (expected_amount,
404 : &refunded_total)))
405 : {
406 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
407 : "Refund amounts do not match\n");
408 0 : TALER_TESTING_interpreter_fail (gos->is);
409 0 : return;
410 : }
411 : }
412 : {
413 : const char **expected_reason;
414 :
415 0 : if (GNUNET_OK !=
416 0 : TALER_TESTING_get_trait_reason (refund_cmd,
417 : &expected_reason))
418 : {
419 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
420 : "Could not fetch reason\n");
421 0 : TALER_TESTING_interpreter_fail (gos->is);
422 0 : return;
423 : }
424 0 : if (0 != strcmp (
425 : *expected_reason,
426 0 : osr->details.success.details.paid.refunds[i].reason))
427 : {
428 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
429 : "Refund reason does not match\n");
430 0 : TALER_TESTING_interpreter_fail (gos->is);
431 0 : return;
432 : }
433 : }
434 : }
435 :
436 0 : if (gos->wired != osr->details.success.details.paid.wired)
437 : {
438 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
439 : "Order wired does not match\n");
440 0 : TALER_TESTING_interpreter_fail (gos->is);
441 0 : return;
442 : }
443 : }
444 0 : break;
445 0 : case TALER_MERCHANT_OSC_CLAIMED:
446 : /* FIXME: Check contract terms... */
447 0 : break;
448 0 : case TALER_MERCHANT_OSC_UNPAID:
449 : {
450 : struct TALER_MERCHANT_PayUriData pud;
451 : const struct TALER_TESTING_Command *order_cmd;
452 : const char **order_id;
453 : const struct TALER_ClaimTokenP *claim_token;
454 :
455 0 : if (GNUNET_OK !=
456 0 : TALER_MERCHANT_parse_pay_uri (
457 : osr->details.success.details.unpaid.taler_pay_uri,
458 : &pud))
459 : {
460 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
461 : "Taler pay uri `%s' is malformed\n",
462 : osr->details.success.details.unpaid.taler_pay_uri);
463 0 : TALER_TESTING_interpreter_fail (gos->is);
464 0 : return;
465 : }
466 :
467 0 : order_cmd = TALER_TESTING_interpreter_lookup_command (
468 : gos->is,
469 : gos->order_reference);
470 :
471 0 : if (GNUNET_OK !=
472 0 : TALER_TESTING_get_trait_order_id (order_cmd,
473 : &order_id))
474 : {
475 0 : TALER_MERCHANT_parse_pay_uri_free (&pud);
476 0 : TALER_TESTING_FAIL (gos->is);
477 : }
478 :
479 0 : if (GNUNET_OK !=
480 0 : TALER_TESTING_get_trait_claim_token (order_cmd,
481 : &claim_token))
482 : {
483 0 : TALER_MERCHANT_parse_pay_uri_free (&pud);
484 0 : TALER_TESTING_FAIL (gos->is);
485 : }
486 : {
487 : char *port;
488 : char *host;
489 :
490 0 : if (GNUNET_OK !=
491 0 : GNUNET_CONFIGURATION_get_value_string (gos->is->cfg,
492 : "merchant",
493 : "PORT",
494 : &port))
495 : {
496 : /* How did we get here without a configured port? */
497 0 : GNUNET_break (0);
498 0 : TALER_TESTING_interpreter_fail (gos->is);
499 0 : TALER_MERCHANT_parse_pay_uri_free (&pud);
500 0 : return;
501 : }
502 0 : GNUNET_asprintf (&host,
503 : "localhost:%s",
504 : port);
505 0 : GNUNET_free (port);
506 0 : if ((0 != strcmp (host,
507 0 : pud.merchant_host)) ||
508 0 : (NULL != pud.merchant_prefix_path) ||
509 0 : (0 != strcmp (*order_id,
510 0 : pud.order_id)) ||
511 0 : (NULL != pud.ssid))
512 : {
513 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
514 : "Order pay uri `%s' does not match, wanted %s/%s\n",
515 : osr->details.success.details.unpaid.taler_pay_uri,
516 : host,
517 : *order_id);
518 0 : TALER_TESTING_interpreter_fail (gos->is);
519 0 : TALER_MERCHANT_parse_pay_uri_free (&pud);
520 0 : GNUNET_free (host);
521 0 : return;
522 : }
523 0 : GNUNET_free (host);
524 : }
525 : /* The claim token is not given in the pay uri if the order
526 : has been claimed already. */
527 0 : if ( (NULL != pud.claim_token) &&
528 0 : ( (NULL == claim_token) ||
529 0 : (0 != GNUNET_memcmp (claim_token,
530 : pud.claim_token)) ) )
531 : {
532 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
533 : "Order pay uri claim token does not match (%d/%d/%d/%d)\n",
534 : NULL == pud.claim_token,
535 : NULL == claim_token,
536 : (NULL != pud.claim_token) &&
537 : GNUNET_is_zero (pud.claim_token),
538 : (NULL != claim_token) &&
539 : GNUNET_is_zero (claim_token));
540 0 : TALER_TESTING_interpreter_fail (gos->is);
541 0 : TALER_MERCHANT_parse_pay_uri_free (&pud);
542 0 : return;
543 : }
544 0 : TALER_MERCHANT_parse_pay_uri_free (&pud);
545 0 : break;
546 : }
547 : }
548 0 : break;
549 0 : default:
550 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
551 : "Unhandled HTTP status.\n");
552 : }
553 0 : TALER_TESTING_interpreter_next (gos->is);
554 : }
555 :
556 :
557 : /**
558 : * Run the "GET order" CMD.
559 : *
560 : * @param cls closure.
561 : * @param cmd command being run now.
562 : * @param is interpreter state.
563 : */
564 : static void
565 0 : merchant_get_order_run (void *cls,
566 : const struct TALER_TESTING_Command *cmd,
567 : struct TALER_TESTING_Interpreter *is)
568 : {
569 0 : struct MerchantGetOrderState *gos = cls;
570 : const struct TALER_TESTING_Command *order_cmd;
571 : const char **order_id;
572 : const struct TALER_PrivateContractHashP *h_contract;
573 :
574 0 : order_cmd = TALER_TESTING_interpreter_lookup_command (
575 : is,
576 : gos->order_reference);
577 :
578 0 : if (GNUNET_OK !=
579 0 : TALER_TESTING_get_trait_order_id (order_cmd,
580 : &order_id))
581 0 : TALER_TESTING_FAIL (is);
582 :
583 0 : if (GNUNET_OK !=
584 0 : TALER_TESTING_get_trait_h_contract_terms (order_cmd,
585 : &h_contract))
586 0 : TALER_TESTING_FAIL (is);
587 :
588 0 : gos->is = is;
589 0 : gos->ogh = TALER_MERCHANT_merchant_order_get (is->ctx,
590 : gos->merchant_url,
591 : *order_id,
592 : NULL,
593 : true,
594 0 : GNUNET_TIME_UNIT_ZERO,
595 : &merchant_get_order_cb,
596 : gos);
597 : }
598 :
599 :
600 : /**
601 : * Free the state of a "GET order" CMD, and possibly
602 : * cancel a pending operation thereof.
603 : *
604 : * @param cls closure.
605 : * @param cmd command being run.
606 : */
607 : static void
608 0 : merchant_get_order_cleanup (void *cls,
609 : const struct TALER_TESTING_Command *cmd)
610 : {
611 0 : struct MerchantGetOrderState *gos = cls;
612 :
613 0 : if (NULL != gos->ogh)
614 : {
615 0 : TALER_LOG_WARNING ("Get tip operation did not complete\n");
616 0 : TALER_MERCHANT_merchant_order_get_cancel (gos->ogh);
617 : }
618 0 : GNUNET_array_grow (gos->transfers,
619 : gos->transfers_length,
620 : 0);
621 0 : GNUNET_array_grow (gos->refunds,
622 : gos->refunds_length,
623 : 0);
624 0 : GNUNET_array_grow (gos->forgets,
625 : gos->forgets_length,
626 : 0);
627 0 : GNUNET_free (gos);
628 0 : }
629 :
630 :
631 : struct TALER_TESTING_Command
632 0 : TALER_TESTING_cmd_merchant_get_order (const char *label,
633 : const char *merchant_url,
634 : const char *order_reference,
635 : enum TALER_MERCHANT_OrderStatusCode osc,
636 : bool refunded,
637 : unsigned int http_status,
638 : ...)
639 : {
640 : struct MerchantGetOrderState *gos;
641 :
642 0 : gos = GNUNET_new (struct MerchantGetOrderState);
643 0 : gos->merchant_url = merchant_url;
644 0 : gos->order_reference = order_reference;
645 0 : gos->osc = osc;
646 0 : gos->refunded = refunded;
647 0 : gos->http_status = http_status;
648 0 : if (refunded)
649 : {
650 : const char *clabel;
651 : va_list ap;
652 :
653 0 : va_start (ap, http_status);
654 0 : while (NULL != (clabel = va_arg (ap, const char *)))
655 : {
656 0 : GNUNET_array_append (gos->refunds,
657 : gos->refunds_length,
658 : clabel);
659 : }
660 0 : va_end (ap);
661 : }
662 : {
663 0 : struct TALER_TESTING_Command cmd = {
664 : .cls = gos,
665 : .label = label,
666 : .run = &merchant_get_order_run,
667 : .cleanup = &merchant_get_order_cleanup
668 : };
669 :
670 0 : return cmd;
671 : }
672 : }
673 :
674 :
675 : struct TALER_TESTING_Command
676 0 : TALER_TESTING_cmd_merchant_get_order2 (const char *label,
677 : const char *merchant_url,
678 : const char *order_reference,
679 : enum TALER_MERCHANT_OrderStatusCode osc,
680 : bool wired,
681 : const char **transfers,
682 : bool refunded,
683 : const char **refunds,
684 : const char **forgets,
685 : unsigned int http_status)
686 : {
687 : struct MerchantGetOrderState *gos;
688 :
689 0 : gos = GNUNET_new (struct MerchantGetOrderState);
690 0 : gos->merchant_url = merchant_url;
691 0 : gos->order_reference = order_reference;
692 0 : gos->osc = osc;
693 0 : gos->wired = wired;
694 0 : gos->refunded = refunded;
695 0 : gos->http_status = http_status;
696 0 : if (wired)
697 : {
698 0 : for (const char **clabel = transfers; *clabel != NULL; ++clabel)
699 : {
700 0 : GNUNET_array_append (gos->transfers,
701 : gos->transfers_length,
702 : *clabel);
703 : }
704 : }
705 0 : if (refunded)
706 : {
707 0 : for (const char **clabel = refunds; *clabel != NULL; ++clabel)
708 : {
709 0 : GNUNET_array_append (gos->refunds,
710 : gos->refunds_length,
711 : *clabel);
712 : }
713 : }
714 0 : if (NULL != forgets)
715 : {
716 0 : for (const char **clabel = forgets; *clabel != NULL; ++clabel)
717 : {
718 0 : GNUNET_array_append (gos->forgets,
719 : gos->forgets_length,
720 : *clabel);
721 : }
722 : }
723 : {
724 0 : struct TALER_TESTING_Command cmd = {
725 : .cls = gos,
726 : .label = label,
727 : .run = &merchant_get_order_run,
728 : .cleanup = &merchant_get_order_cleanup
729 : };
730 :
731 0 : return cmd;
732 : }
733 : }
734 :
735 :
736 : struct MerchantPollOrderConcludeState
737 : {
738 : /**
739 : * The interpreter state.
740 : */
741 : struct TALER_TESTING_Interpreter *is;
742 :
743 : /**
744 : * Reference to a command that can provide a poll order start command.
745 : */
746 : const char *start_reference;
747 :
748 : /**
749 : * Task to wait for the deadline.
750 : */
751 : struct GNUNET_SCHEDULER_Task *task;
752 :
753 : /**
754 : * Expected HTTP response status code.
755 : */
756 : unsigned int expected_http_status;
757 : };
758 :
759 :
760 : struct MerchantPollOrderStartState
761 : {
762 : /**
763 : * The merchant base URL.
764 : */
765 : const char *merchant_url;
766 :
767 : /**
768 : * The handle to the current GET /private/orders/$ORDER_ID request.
769 : */
770 : struct TALER_MERCHANT_OrderMerchantGetHandle *ogh;
771 :
772 : /**
773 : * The interpreter state.
774 : */
775 : struct TALER_TESTING_Interpreter *is;
776 :
777 : /**
778 : * Reference to a command that created an order.
779 : */
780 : const char *order_id;
781 :
782 : /**
783 : * How long to wait for server to return a response.
784 : */
785 : struct GNUNET_TIME_Relative timeout;
786 :
787 : /**
788 : * Conclude state waiting for completion (if any).
789 : */
790 : struct MerchantPollOrderConcludeState *cs;
791 :
792 : /**
793 : * The HTTP status code returned by the backend.
794 : */
795 : unsigned int http_status;
796 :
797 : /**
798 : * When the request should be completed by.
799 : */
800 : struct GNUNET_TIME_Absolute deadline;
801 : };
802 :
803 :
804 : /**
805 : * Task called when either the timeout for the GET /private/order/$ID command
806 : * expired or we got a response. Checks if the result is what we expected.
807 : *
808 : * @param cls a `struct MerchantPollOrderConcludeState`
809 : */
810 : static void
811 0 : conclude_task (void *cls)
812 : {
813 0 : struct MerchantPollOrderConcludeState *ppc = cls;
814 : const struct TALER_TESTING_Command *poll_cmd;
815 : struct MerchantPollOrderStartState *cps;
816 : struct GNUNET_TIME_Absolute now;
817 :
818 0 : ppc->task = NULL;
819 : poll_cmd =
820 0 : TALER_TESTING_interpreter_lookup_command (ppc->is,
821 : ppc->start_reference);
822 0 : if (NULL == poll_cmd)
823 0 : TALER_TESTING_FAIL (ppc->is);
824 0 : cps = poll_cmd->cls;
825 0 : if (NULL != cps->ogh)
826 : {
827 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
828 : "Expected poll GET /private/orders/$ORDER_ID to have completed, but it did not!\n");
829 0 : TALER_TESTING_FAIL (ppc->is);
830 : }
831 0 : if (cps->http_status != ppc->expected_http_status)
832 : {
833 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
834 : "Expected HTTP status %u, got %u\n",
835 : ppc->expected_http_status,
836 : cps->http_status);
837 0 : TALER_TESTING_FAIL (ppc->is);
838 : }
839 0 : now = GNUNET_TIME_absolute_get ();
840 0 : if ((GNUNET_TIME_absolute_add (cps->deadline,
841 0 : GNUNET_TIME_UNIT_SECONDS).abs_value_us <
842 0 : now.abs_value_us) )
843 : {
844 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
845 : "Expected answer to be delayed until %llu, but got response at %llu\n",
846 : (unsigned long long) cps->deadline.abs_value_us,
847 : (unsigned long long) now.abs_value_us);
848 0 : TALER_TESTING_FAIL (ppc->is);
849 : }
850 0 : TALER_TESTING_interpreter_next (ppc->is);
851 : }
852 :
853 :
854 : /**
855 : * Callback to process a GET /private/orders/$ID request
856 : *
857 : * @param cls closure
858 : * @param osr order status response details
859 : */
860 : static void
861 0 : merchant_poll_order_cb (
862 : void *cls,
863 : const struct TALER_MERCHANT_OrderStatusResponse *osr)
864 : {
865 0 : struct MerchantPollOrderStartState *pos = cls;
866 0 : const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr;
867 :
868 0 : pos->ogh = NULL;
869 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
870 : "GET /private/orders/$ID finished with status %u.\n",
871 : hr->http_status);
872 0 : pos->http_status = hr->http_status;
873 0 : switch (hr->http_status)
874 : {
875 0 : case MHD_HTTP_OK:
876 : // FIXME: keep data from 'osr' here for checking?
877 0 : break;
878 0 : default:
879 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
880 : "Unhandled HTTP status.\n");
881 : }
882 0 : if (NULL != pos->cs)
883 : {
884 0 : GNUNET_SCHEDULER_cancel (pos->cs->task);
885 0 : pos->cs->task = GNUNET_SCHEDULER_add_now (&conclude_task,
886 0 : pos->cs);
887 : }
888 0 : }
889 :
890 :
891 : /**
892 : * Run the "GET order" CMD.
893 : *
894 : * @param cls closure.
895 : * @param cmd command being run now.
896 : * @param is interpreter state.
897 : */
898 : static void
899 0 : merchant_poll_order_start_run (void *cls,
900 : const struct TALER_TESTING_Command *cmd,
901 : struct TALER_TESTING_Interpreter *is)
902 : {
903 0 : struct MerchantPollOrderStartState *pos = cls;
904 :
905 : /* add 1s grace time to timeout */
906 : pos->deadline
907 0 : = GNUNET_TIME_absolute_add (GNUNET_TIME_relative_to_absolute (pos->timeout),
908 : GNUNET_TIME_UNIT_SECONDS);
909 0 : pos->is = is;
910 0 : pos->ogh = TALER_MERCHANT_merchant_order_get (is->ctx,
911 : pos->merchant_url,
912 : pos->order_id,
913 : NULL,
914 : false,
915 : pos->timeout,
916 : &merchant_poll_order_cb,
917 : pos);
918 0 : GNUNET_assert (NULL != pos->ogh);
919 : /* We CONTINUE to run the interpreter while the long-polled command
920 : completes asynchronously! */
921 0 : TALER_TESTING_interpreter_next (pos->is);
922 0 : }
923 :
924 :
925 : /**
926 : * Free the state of a "GET order" CMD, and possibly
927 : * cancel a pending operation thereof.
928 : *
929 : * @param cls closure.
930 : * @param cmd command being run.
931 : */
932 : static void
933 0 : merchant_poll_order_start_cleanup (void *cls,
934 : const struct TALER_TESTING_Command *cmd)
935 : {
936 0 : struct MerchantPollOrderStartState *pos = cls;
937 :
938 0 : if (NULL != pos->ogh)
939 : {
940 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
941 : "Command `%s' was not terminated\n",
942 : TALER_TESTING_interpreter_get_current_label (
943 : pos->is));
944 0 : TALER_MERCHANT_merchant_order_get_cancel (pos->ogh);
945 : }
946 0 : GNUNET_free (pos);
947 0 : }
948 :
949 :
950 : /**
951 : * Start a long poll for GET /private/orders/$ORDER_ID.
952 : */
953 : struct TALER_TESTING_Command
954 0 : TALER_TESTING_cmd_poll_order_start (const char *label,
955 : const char *merchant_url,
956 : const char *order_id,
957 : struct GNUNET_TIME_Relative timeout)
958 : {
959 : struct MerchantPollOrderStartState *pos;
960 :
961 0 : pos = GNUNET_new (struct MerchantPollOrderStartState);
962 0 : pos->order_id = order_id;
963 0 : pos->merchant_url = merchant_url;
964 0 : pos->timeout = timeout;
965 : {
966 0 : struct TALER_TESTING_Command cmd = {
967 : .cls = pos,
968 : .label = label,
969 : .run = &merchant_poll_order_start_run,
970 : .cleanup = &merchant_poll_order_start_cleanup
971 : };
972 :
973 0 : return cmd;
974 : }
975 : }
976 :
977 :
978 : /**
979 : * Run the "GET order conclude" CMD.
980 : *
981 : * @param cls closure.
982 : * @param cmd command being run now.
983 : * @param is interpreter state.
984 : */
985 : static void
986 0 : merchant_poll_order_conclude_run (void *cls,
987 : const struct TALER_TESTING_Command *cmd,
988 : struct TALER_TESTING_Interpreter *is)
989 : {
990 0 : struct MerchantPollOrderConcludeState *poc = cls;
991 : const struct TALER_TESTING_Command *poll_cmd;
992 : struct MerchantPollOrderStartState *pos;
993 :
994 0 : poc->is = is;
995 : poll_cmd =
996 0 : TALER_TESTING_interpreter_lookup_command (is,
997 : poc->start_reference);
998 0 : if (NULL == poll_cmd)
999 0 : TALER_TESTING_FAIL (poc->is);
1000 0 : GNUNET_assert (poll_cmd->run == &merchant_poll_order_start_run);
1001 0 : pos = poll_cmd->cls;
1002 0 : pos->cs = poc;
1003 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1004 : "Waiting on GET /private/orders/$ID of %s (%s)\n",
1005 : poc->start_reference,
1006 : (NULL == pos->ogh)
1007 : ? "finished"
1008 : : "active");
1009 0 : if (NULL == pos->ogh)
1010 0 : poc->task = GNUNET_SCHEDULER_add_now (&conclude_task,
1011 : poc);
1012 : else
1013 0 : poc->task = GNUNET_SCHEDULER_add_at (pos->deadline,
1014 : &conclude_task,
1015 : poc);
1016 : }
1017 :
1018 :
1019 : /**
1020 : * Free the state of a "GET order" CMD, and possibly
1021 : * cancel a pending operation thereof.
1022 : *
1023 : * @param cls closure.
1024 : * @param cmd command being run.
1025 : */
1026 : static void
1027 0 : merchant_poll_order_conclude_cleanup (void *cls,
1028 : const struct TALER_TESTING_Command *cmd)
1029 : {
1030 0 : struct MerchantPollOrderConcludeState *poc = cls;
1031 :
1032 0 : if (NULL != poc->task)
1033 : {
1034 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1035 : "Command `%s' was not terminated\n",
1036 : TALER_TESTING_interpreter_get_current_label (
1037 : poc->is));
1038 0 : GNUNET_SCHEDULER_cancel (poc->task);
1039 0 : poc->task = NULL;
1040 : }
1041 0 : GNUNET_free (poc);
1042 0 : }
1043 :
1044 :
1045 : /**
1046 : * Complete a long poll for GET /private/orders/$ORDER_ID.
1047 : */
1048 : struct TALER_TESTING_Command
1049 0 : TALER_TESTING_cmd_poll_order_conclude (const char *label,
1050 : unsigned int http_status,
1051 : const char *poll_start_reference)
1052 : {
1053 : struct MerchantPollOrderConcludeState *cps;
1054 :
1055 0 : cps = GNUNET_new (struct MerchantPollOrderConcludeState);
1056 0 : cps->start_reference = poll_start_reference;
1057 0 : cps->expected_http_status = http_status;
1058 : {
1059 0 : struct TALER_TESTING_Command cmd = {
1060 : .cls = cps,
1061 : .label = label,
1062 : .run = &merchant_poll_order_conclude_run,
1063 : .cleanup = &merchant_poll_order_conclude_cleanup
1064 : };
1065 :
1066 0 : return cmd;
1067 : }
1068 : }
1069 :
1070 :
1071 : /* end of testing_api_cmd_merchant_get_order.c */
|