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