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