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