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