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