testing_api_cmd_merchant_get_order.c (32757B)
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 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_OrderStatusResponse *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_merchant_order_get ( 606 TALER_TESTING_interpreter_get_context (is), 607 gos->merchant_url, 608 order_id, 609 gos->session_id, 610 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 merchant_get_order_cleanup (void *cls, 625 const struct TALER_TESTING_Command *cmd) 626 { 627 struct MerchantGetOrderState *gos = cls; 628 629 if (NULL != gos->ogh) 630 { 631 TALER_LOG_WARNING ("Get order operation did not complete\n"); 632 TALER_MERCHANT_merchant_order_get_cancel (gos->ogh); 633 } 634 GNUNET_array_grow (gos->transfers, 635 gos->transfers_length, 636 0); 637 GNUNET_array_grow (gos->refunds, 638 gos->refunds_length, 639 0); 640 GNUNET_array_grow (gos->forgets, 641 gos->forgets_length, 642 0); 643 GNUNET_free (gos); 644 } 645 646 647 struct TALER_TESTING_Command 648 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 gos = GNUNET_new (struct MerchantGetOrderState); 660 gos->merchant_url = merchant_url; 661 gos->order_reference = order_reference; 662 gos->osc = osc; 663 gos->refunded = refunded; 664 gos->http_status = http_status; 665 if (refunded) 666 { 667 const char *clabel; 668 va_list ap; 669 670 va_start (ap, http_status); 671 while (NULL != (clabel = va_arg (ap, const char *))) 672 { 673 GNUNET_array_append (gos->refunds, 674 gos->refunds_length, 675 clabel); 676 } 677 va_end (ap); 678 } 679 { 680 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 return cmd; 688 } 689 } 690 691 692 struct TALER_TESTING_Command 693 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 gos = GNUNET_new (struct MerchantGetOrderState); 708 gos->merchant_url = merchant_url; 709 gos->order_reference = order_reference; 710 gos->osc = osc; 711 gos->wired = wired; 712 gos->refunded = refunded; 713 gos->http_status = http_status; 714 if (wired) 715 { 716 for (const char **clabel = transfers; *clabel != NULL; ++clabel) 717 { 718 GNUNET_array_append (gos->transfers, 719 gos->transfers_length, 720 *clabel); 721 } 722 } 723 if (refunded) 724 { 725 for (const char **clabel = refunds; *clabel != NULL; ++clabel) 726 { 727 GNUNET_array_append (gos->refunds, 728 gos->refunds_length, 729 *clabel); 730 } 731 } 732 if (NULL != forgets) 733 { 734 for (const char **clabel = forgets; *clabel != NULL; ++clabel) 735 { 736 GNUNET_array_append (gos->forgets, 737 gos->forgets_length, 738 *clabel); 739 } 740 } 741 { 742 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 return cmd; 750 } 751 } 752 753 754 struct TALER_TESTING_Command 755 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 gos = GNUNET_new (struct MerchantGetOrderState); 767 gos->merchant_url = merchant_url; 768 gos->order_reference = order_reference; 769 gos->osc = osc; 770 gos->session_id = session_id; 771 gos->repurchase_order_ref = repurchase_order_ref; 772 gos->http_status = expected_http_status; 773 { 774 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 return cmd; 782 } 783 } 784 785 786 struct TALER_TESTING_Command 787 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 gos = GNUNET_new (struct MerchantGetOrderState); 798 gos->merchant_url = merchant_url; 799 gos->order_reference = order_reference; 800 gos->osc = osc; 801 gos->expected_min_age = expected_min_age; 802 gos->http_status = expected_http_status; 803 { 804 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 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 conclude_task (void *cls) 892 { 893 struct MerchantPollOrderConcludeState *ppc = cls; 894 const struct TALER_TESTING_Command *poll_cmd; 895 struct MerchantPollOrderStartState *cps; 896 struct GNUNET_TIME_Absolute now; 897 898 ppc->task = NULL; 899 poll_cmd = 900 TALER_TESTING_interpreter_lookup_command (ppc->is, 901 ppc->start_reference); 902 if (NULL == poll_cmd) 903 TALER_TESTING_FAIL (ppc->is); 904 cps = poll_cmd->cls; 905 if (NULL != cps->ogh) 906 { 907 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 908 "Expected poll GET /private/orders/$ORDER_ID to have completed, but it did not!\n"); 909 TALER_TESTING_FAIL (ppc->is); 910 } 911 if (cps->http_status != ppc->expected_http_status) 912 { 913 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 914 "Expected HTTP status %u, got %u\n", 915 ppc->expected_http_status, 916 cps->http_status); 917 TALER_TESTING_FAIL (ppc->is); 918 } 919 now = GNUNET_TIME_absolute_get (); 920 if ((GNUNET_TIME_absolute_add (cps->deadline, 921 GNUNET_TIME_UNIT_SECONDS).abs_value_us < 922 now.abs_value_us) ) 923 { 924 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 TALER_TESTING_FAIL (ppc->is); 929 } 930 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 merchant_poll_order_cb ( 942 void *cls, 943 const struct TALER_MERCHANT_OrderStatusResponse *osr) 944 { 945 struct MerchantPollOrderStartState *pos = cls; 946 const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr; 947 948 pos->ogh = NULL; 949 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 950 "GET /private/orders/$ID finished with status %u.\n", 951 hr->http_status); 952 pos->http_status = hr->http_status; 953 switch (hr->http_status) 954 { 955 case MHD_HTTP_OK: 956 // FIXME: keep data from 'osr' here for checking? 957 break; 958 default: 959 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 960 "Unhandled HTTP status.\n"); 961 } 962 if (NULL != pos->cs) 963 { 964 GNUNET_SCHEDULER_cancel (pos->cs->task); 965 pos->cs->task = GNUNET_SCHEDULER_add_now (&conclude_task, 966 pos->cs); 967 } 968 } 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 merchant_poll_order_start_run (void *cls, 980 const struct TALER_TESTING_Command *cmd, 981 struct TALER_TESTING_Interpreter *is) 982 { 983 struct MerchantPollOrderStartState *pos = cls; 984 985 /* add 1s grace time to timeout */ 986 pos->deadline 987 = GNUNET_TIME_absolute_add (GNUNET_TIME_relative_to_absolute (pos->timeout), 988 GNUNET_TIME_UNIT_SECONDS); 989 pos->is = is; 990 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 GNUNET_assert (NULL != pos->ogh); 999 /* We CONTINUE to run the interpreter while the long-polled command 1000 completes asynchronously! */ 1001 TALER_TESTING_interpreter_next (pos->is); 1002 } 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 merchant_poll_order_start_cleanup (void *cls, 1014 const struct TALER_TESTING_Command *cmd) 1015 { 1016 struct MerchantPollOrderStartState *pos = cls; 1017 1018 if (NULL != pos->ogh) 1019 { 1020 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1021 "Command `%s' was not terminated\n", 1022 TALER_TESTING_interpreter_get_current_label ( 1023 pos->is)); 1024 TALER_MERCHANT_merchant_order_get_cancel (pos->ogh); 1025 } 1026 GNUNET_free (pos); 1027 } 1028 1029 1030 struct TALER_TESTING_Command 1031 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 pos = GNUNET_new (struct MerchantPollOrderStartState); 1040 pos->order_id = order_id; 1041 pos->merchant_url = merchant_url; 1042 pos->timeout = timeout; 1043 { 1044 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 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 merchant_poll_order_conclude_run (void *cls, 1065 const struct TALER_TESTING_Command *cmd, 1066 struct TALER_TESTING_Interpreter *is) 1067 { 1068 struct MerchantPollOrderConcludeState *poc = cls; 1069 const struct TALER_TESTING_Command *poll_cmd; 1070 struct MerchantPollOrderStartState *pos; 1071 1072 poc->is = is; 1073 poll_cmd = 1074 TALER_TESTING_interpreter_lookup_command (is, 1075 poc->start_reference); 1076 if (NULL == poll_cmd) 1077 TALER_TESTING_FAIL (poc->is); 1078 GNUNET_assert (poll_cmd->run == &merchant_poll_order_start_run); 1079 pos = poll_cmd->cls; 1080 pos->cs = poc; 1081 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 if (NULL == pos->ogh) 1088 poc->task = GNUNET_SCHEDULER_add_now (&conclude_task, 1089 poc); 1090 else 1091 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 merchant_poll_order_conclude_cleanup (void *cls, 1106 const struct TALER_TESTING_Command *cmd) 1107 { 1108 struct MerchantPollOrderConcludeState *poc = cls; 1109 1110 if (NULL != poc->task) 1111 { 1112 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1113 "Command `%s' was not terminated\n", 1114 TALER_TESTING_interpreter_get_current_label ( 1115 poc->is)); 1116 GNUNET_SCHEDULER_cancel (poc->task); 1117 poc->task = NULL; 1118 } 1119 GNUNET_free (poc); 1120 } 1121 1122 1123 struct TALER_TESTING_Command 1124 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 cps = GNUNET_new (struct MerchantPollOrderConcludeState); 1131 cps->start_reference = poll_start_reference; 1132 cps->expected_http_status = http_status; 1133 { 1134 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 return cmd; 1142 } 1143 } 1144 1145 1146 /* end of testing_api_cmd_merchant_get_order.c */