testing_api_cmd_merchant_get_order.c (34244B)
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 json_decref (ct); 250 TALER_TESTING_interpreter_fail (gos->is); 251 return; 252 } 253 254 for (unsigned int j = 0; j < *paths_length; ++j) 255 { 256 const char *path; 257 int res = GNUNET_OK; 258 259 if (GNUNET_OK != 260 TALER_TESTING_get_trait_paths (forget_cmd, 261 j, 262 &path)) 263 { 264 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 265 "Couldn't fetch forget path\n"); 266 json_decref (ct); 267 TALER_TESTING_interpreter_fail (gos->is); 268 return; 269 } 270 271 GNUNET_assert (GNUNET_OK == 272 TALER_JSON_expand_path (ct, 273 path, 274 &apply_forget, 275 &res)); 276 GNUNET_assert (GNUNET_OK == res); 277 } 278 } 279 280 if (1 != json_equal (ct, 281 osr->details.ok.details.paid.contract_terms)) 282 { 283 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 284 "Order contract terms do not match\n"); 285 json_decref (ct); 286 TALER_TESTING_interpreter_fail (gos->is); 287 return; 288 } 289 290 json_decref (ct); 291 } 292 if (gos->wired != osr->details.ok.details.paid.wired) 293 { 294 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 295 "Order wired does not match\n"); 296 TALER_TESTING_interpreter_fail (gos->is); 297 return; 298 } 299 if (gos->transfers_length != osr->details.ok.details.paid.wts_len) 300 { 301 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 302 "Number of transfers found does not match\n"); 303 TALER_TESTING_interpreter_fail (gos->is); 304 return; 305 } 306 for (unsigned int i = 0; i < gos->transfers_length; ++i) 307 { 308 const struct TALER_TESTING_Command *transfer_cmd; 309 310 transfer_cmd = TALER_TESTING_interpreter_lookup_command ( 311 gos->is, 312 gos->transfers[i]); 313 { 314 const struct TALER_WireTransferIdentifierRawP *wtid; 315 316 if (GNUNET_OK != 317 TALER_TESTING_get_trait_wtid (transfer_cmd, 318 &wtid)) 319 { 320 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 321 "Could not fetch wire transfer id\n"); 322 TALER_TESTING_interpreter_fail (gos->is); 323 return; 324 } 325 if (0 != GNUNET_memcmp (wtid, 326 &osr->details.ok.details.paid.wts[i]. 327 wtid)) 328 { 329 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 330 "Wire transfer id does not match\n"); 331 TALER_TESTING_interpreter_fail (gos->is); 332 return; 333 } 334 } 335 { 336 const char *exchange_url; 337 338 if (GNUNET_OK != 339 TALER_TESTING_get_trait_exchange_url (transfer_cmd, 340 &exchange_url)) 341 { 342 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 343 "Could not fetch wire transfer exchange url\n"); 344 TALER_TESTING_interpreter_fail (gos->is); 345 return; 346 } 347 if (0 != strcmp ( 348 exchange_url, 349 osr->details.ok.details.paid.wts[i].exchange_url)) 350 { 351 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 352 "Wire transfer exchange url does not match\n"); 353 TALER_TESTING_interpreter_fail (gos->is); 354 return; 355 } 356 } 357 } 358 if (gos->refunded != osr->details.ok.details.paid.refunded) 359 { 360 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 361 "Order refunded does not match\n"); 362 TALER_TESTING_interpreter_fail (gos->is); 363 return; 364 } 365 if (gos->refunds_length != 366 osr->details.ok.details.paid.refunds_len) 367 { 368 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 369 "Number of refunds found does not match\n"); 370 TALER_TESTING_interpreter_fail (gos->is); 371 return; 372 } 373 if (0 < gos->refunds_length) 374 GNUNET_assert ( 375 GNUNET_OK == 376 TALER_amount_set_zero ( 377 osr->details.ok.details.paid.refund_amount.currency, 378 &refunded_total)); 379 for (unsigned int i = 0; i < gos->refunds_length; ++i) 380 { 381 const struct TALER_TESTING_Command *refund_cmd; 382 383 refund_cmd = TALER_TESTING_interpreter_lookup_command ( 384 gos->is, 385 gos->refunds[i]); 386 { 387 const struct TALER_Amount *expected_amount; 388 struct TALER_Amount *amount_found = 389 &osr->details.ok.details.paid.refunds[i].refund_amount; 390 391 if (GNUNET_OK != 392 TALER_TESTING_get_trait_amount (refund_cmd, 393 &expected_amount)) 394 { 395 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 396 "Could not fetch refund amount\n"); 397 TALER_TESTING_interpreter_fail (gos->is); 398 return; 399 } 400 GNUNET_assert (0 <= TALER_amount_add (&refunded_total, 401 &refunded_total, 402 amount_found)); 403 if ((GNUNET_OK != 404 TALER_amount_cmp_currency (expected_amount, 405 &refunded_total)) || 406 (0 != TALER_amount_cmp (expected_amount, 407 &refunded_total))) 408 { 409 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 410 "Refund amounts do not match\n"); 411 TALER_TESTING_interpreter_fail (gos->is); 412 return; 413 } 414 } 415 { 416 const char *expected_reason; 417 418 if (GNUNET_OK != 419 TALER_TESTING_get_trait_reason (refund_cmd, 420 &expected_reason)) 421 { 422 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 423 "Could not fetch reason\n"); 424 TALER_TESTING_interpreter_fail (gos->is); 425 return; 426 } 427 if (0 != 428 strcmp ( 429 expected_reason, 430 osr->details.ok.details.paid.refunds[i].reason)) 431 { 432 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 433 "Refund reason does not match\n"); 434 TALER_TESTING_interpreter_fail (gos->is); 435 return; 436 } 437 } 438 } 439 440 if (gos->wired != osr->details.ok.details.paid.wired) 441 { 442 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 443 "Order wired does not match\n"); 444 TALER_TESTING_interpreter_fail (gos->is); 445 return; 446 } 447 } 448 break; 449 case TALER_MERCHANT_OSC_CLAIMED: 450 /* FIXME: Check contract terms... */ 451 if ( (0 != gos->expected_min_age) && 452 (gos->expected_min_age != 453 json_integer_value ( 454 json_object_get ( 455 osr->details.ok.details.claimed.contract_terms, 456 "minimum_age"))) ) 457 { 458 GNUNET_break (0); 459 TALER_TESTING_interpreter_fail (gos->is); 460 return; 461 } 462 break; 463 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 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 rep_cmd = TALER_TESTING_interpreter_lookup_command ( 477 gos->is, 478 gos->repurchase_order_ref); 479 if (GNUNET_OK != 480 TALER_TESTING_get_trait_order_id (rep_cmd, 481 &rep_id)) 482 { 483 TALER_TESTING_FAIL (gos->is); 484 } 485 ri = osr->details.ok.details.unpaid.already_paid_order_id; 486 if ( (NULL == ri) || 487 (0 != 488 strcmp (ri, 489 rep_id)) ) 490 { 491 TALER_TESTING_FAIL (gos->is); 492 } 493 } 494 495 if (GNUNET_OK != 496 TALER_MERCHANT_parse_pay_uri ( 497 osr->details.ok.details.unpaid.taler_pay_uri, 498 &pud)) 499 { 500 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 501 "Taler pay uri `%s' is malformed\n", 502 osr->details.ok.details.unpaid.taler_pay_uri); 503 TALER_TESTING_interpreter_fail (gos->is); 504 return; 505 } 506 507 order_cmd = TALER_TESTING_interpreter_lookup_command ( 508 gos->is, 509 gos->order_reference); 510 511 if (GNUNET_OK != 512 TALER_TESTING_get_trait_order_id (order_cmd, 513 &order_id)) 514 { 515 TALER_MERCHANT_parse_pay_uri_free (&pud); 516 TALER_TESTING_FAIL (gos->is); 517 } 518 519 if (GNUNET_OK != 520 TALER_TESTING_get_trait_claim_token (order_cmd, 521 &claim_token)) 522 { 523 TALER_MERCHANT_parse_pay_uri_free (&pud); 524 TALER_TESTING_FAIL (gos->is); 525 } 526 { 527 char *host; 528 529 host = TALER_MERCHANT_TESTING_extract_host (gos->merchant_url); 530 if ((0 != strcmp (host, 531 pud.merchant_host)) || 532 (NULL != pud.merchant_prefix_path) || 533 (0 != strcmp (order_id, 534 pud.order_id)) || 535 (NULL != pud.ssid)) 536 { 537 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 TALER_TESTING_interpreter_fail (gos->is); 543 TALER_MERCHANT_parse_pay_uri_free (&pud); 544 GNUNET_free (host); 545 return; 546 } 547 GNUNET_free (host); 548 } 549 /* The claim token is not given in the pay uri if the order 550 has been claimed already. */ 551 if ( (NULL != pud.claim_token) && 552 ( (NULL == claim_token) || 553 (0 != GNUNET_memcmp (claim_token, 554 pud.claim_token)) ) ) 555 { 556 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 TALER_TESTING_interpreter_fail (gos->is); 565 TALER_MERCHANT_parse_pay_uri_free (&pud); 566 return; 567 } 568 TALER_MERCHANT_parse_pay_uri_free (&pud); 569 break; 570 } 571 } 572 break; 573 default: 574 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 575 "Unhandled HTTP status.\n"); 576 } 577 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 merchant_get_order_run (void *cls, 590 const struct TALER_TESTING_Command *cmd, 591 struct TALER_TESTING_Interpreter *is) 592 { 593 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 order_cmd = TALER_TESTING_interpreter_lookup_command ( 599 is, 600 gos->order_reference); 601 602 if (GNUNET_OK != 603 TALER_TESTING_get_trait_order_id (order_cmd, 604 &order_id)) 605 TALER_TESTING_FAIL (is); 606 607 if (GNUNET_OK != 608 TALER_TESTING_get_trait_h_contract_terms (order_cmd, 609 &h_contract)) 610 TALER_TESTING_FAIL (is); 611 612 gos->is = is; 613 gos->ogh = TALER_MERCHANT_get_private_order_create ( 614 TALER_TESTING_interpreter_get_context (is), 615 gos->merchant_url, 616 order_id); 617 GNUNET_assert (NULL != gos->ogh); 618 if (NULL != gos->session_id) 619 { 620 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 ec = TALER_MERCHANT_get_private_order_start ( 629 gos->ogh, 630 &merchant_get_order_cb, 631 gos); 632 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 merchant_get_order_cleanup (void *cls, 646 const struct TALER_TESTING_Command *cmd) 647 { 648 struct MerchantGetOrderState *gos = cls; 649 650 if (NULL != gos->ogh) 651 { 652 TALER_LOG_WARNING ("Get order operation did not complete\n"); 653 TALER_MERCHANT_get_private_order_cancel (gos->ogh); 654 } 655 GNUNET_array_grow (gos->transfers, 656 gos->transfers_length, 657 0); 658 GNUNET_array_grow (gos->refunds, 659 gos->refunds_length, 660 0); 661 GNUNET_array_grow (gos->forgets, 662 gos->forgets_length, 663 0); 664 GNUNET_free (gos); 665 } 666 667 668 struct TALER_TESTING_Command 669 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 gos = GNUNET_new (struct MerchantGetOrderState); 681 gos->merchant_url = merchant_url; 682 gos->order_reference = order_reference; 683 gos->osc = osc; 684 gos->refunded = refunded; 685 gos->http_status = http_status; 686 if (refunded) 687 { 688 const char *clabel; 689 va_list ap; 690 691 va_start (ap, http_status); 692 while (NULL != (clabel = va_arg (ap, const char *))) 693 { 694 GNUNET_array_append (gos->refunds, 695 gos->refunds_length, 696 clabel); 697 } 698 va_end (ap); 699 } 700 { 701 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 return cmd; 709 } 710 } 711 712 713 struct TALER_TESTING_Command 714 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 gos = GNUNET_new (struct MerchantGetOrderState); 729 gos->merchant_url = merchant_url; 730 gos->order_reference = order_reference; 731 gos->osc = osc; 732 gos->wired = wired; 733 gos->refunded = refunded; 734 gos->http_status = http_status; 735 if (wired) 736 { 737 for (const char **clabel = transfers; *clabel != NULL; ++clabel) 738 { 739 GNUNET_array_append (gos->transfers, 740 gos->transfers_length, 741 *clabel); 742 } 743 } 744 if (refunded) 745 { 746 for (const char **clabel = refunds; *clabel != NULL; ++clabel) 747 { 748 GNUNET_array_append (gos->refunds, 749 gos->refunds_length, 750 *clabel); 751 } 752 } 753 if (NULL != forgets) 754 { 755 for (const char **clabel = forgets; *clabel != NULL; ++clabel) 756 { 757 GNUNET_array_append (gos->forgets, 758 gos->forgets_length, 759 *clabel); 760 } 761 } 762 { 763 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 return cmd; 771 } 772 } 773 774 775 struct TALER_TESTING_Command 776 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 gos = GNUNET_new (struct MerchantGetOrderState); 788 gos->merchant_url = merchant_url; 789 gos->order_reference = order_reference; 790 gos->osc = osc; 791 gos->session_id = session_id; 792 gos->repurchase_order_ref = repurchase_order_ref; 793 gos->http_status = expected_http_status; 794 { 795 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 return cmd; 803 } 804 } 805 806 807 struct TALER_TESTING_Command 808 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 gos = GNUNET_new (struct MerchantGetOrderState); 819 gos->merchant_url = merchant_url; 820 gos->order_reference = order_reference; 821 gos->osc = osc; 822 gos->expected_min_age = expected_min_age; 823 gos->http_status = expected_http_status; 824 { 825 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 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 conclude_task (void *cls) 913 { 914 struct MerchantPollOrderConcludeState *ppc = cls; 915 const struct TALER_TESTING_Command *poll_cmd; 916 struct MerchantPollOrderStartState *cps; 917 struct GNUNET_TIME_Absolute now; 918 919 ppc->task = NULL; 920 poll_cmd = 921 TALER_TESTING_interpreter_lookup_command (ppc->is, 922 ppc->start_reference); 923 if (NULL == poll_cmd) 924 TALER_TESTING_FAIL (ppc->is); 925 cps = poll_cmd->cls; 926 if (NULL != cps->ogh) 927 { 928 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 929 "Expected poll GET /private/orders/$ORDER_ID to have completed, but it did not!\n"); 930 TALER_TESTING_FAIL (ppc->is); 931 } 932 if (cps->http_status != ppc->expected_http_status) 933 { 934 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 935 "Expected HTTP status %u, got %u\n", 936 ppc->expected_http_status, 937 cps->http_status); 938 TALER_TESTING_FAIL (ppc->is); 939 } 940 now = GNUNET_TIME_absolute_get (); 941 if ((GNUNET_TIME_absolute_add (cps->deadline, 942 GNUNET_TIME_UNIT_SECONDS).abs_value_us < 943 now.abs_value_us) ) 944 { 945 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 TALER_TESTING_FAIL (ppc->is); 950 } 951 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 merchant_poll_order_cb ( 967 void *cls, 968 const struct TALER_MERCHANT_GetPrivateOrderResponse *osr) 969 { 970 struct MerchantPollOrderStartState *pos = cls; 971 const struct TALER_MERCHANT_HttpResponse *hr = &osr->hr; 972 973 pos->ogh = NULL; 974 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 975 "GET /private/orders/$ID finished with status %u.\n", 976 hr->http_status); 977 pos->http_status = hr->http_status; 978 switch (hr->http_status) 979 { 980 case MHD_HTTP_OK: 981 // FIXME: keep data from 'osr' here for checking? 982 break; 983 default: 984 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 985 "Unhandled HTTP status.\n"); 986 } 987 if (NULL != pos->cs) 988 { 989 GNUNET_SCHEDULER_cancel (pos->cs->task); 990 pos->cs->task = GNUNET_SCHEDULER_add_now (&conclude_task, 991 pos->cs); 992 } 993 } 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 merchant_poll_order_start_run (void *cls, 1005 const struct TALER_TESTING_Command *cmd, 1006 struct TALER_TESTING_Interpreter *is) 1007 { 1008 struct MerchantPollOrderStartState *pos = cls; 1009 1010 /* add 1s grace time to timeout */ 1011 pos->deadline 1012 = GNUNET_TIME_absolute_add (GNUNET_TIME_relative_to_absolute (pos->timeout), 1013 GNUNET_TIME_UNIT_SECONDS); 1014 pos->is = is; 1015 pos->ogh = TALER_MERCHANT_get_private_order_create ( 1016 TALER_TESTING_interpreter_get_context (is), 1017 pos->merchant_url, 1018 pos->order_id); 1019 GNUNET_assert (NULL != pos->ogh); 1020 if (pos->timeout.rel_value_us > 0) 1021 { 1022 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 ec = TALER_MERCHANT_get_private_order_start ( 1031 pos->ogh, 1032 &merchant_poll_order_cb, 1033 pos); 1034 GNUNET_assert (TALER_EC_NONE == ec); 1035 } 1036 /* We CONTINUE to run the interpreter while the long-polled command 1037 completes asynchronously! */ 1038 TALER_TESTING_interpreter_next (pos->is); 1039 } 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 merchant_poll_order_start_cleanup (void *cls, 1051 const struct TALER_TESTING_Command *cmd) 1052 { 1053 struct MerchantPollOrderStartState *pos = cls; 1054 1055 if (NULL != pos->ogh) 1056 { 1057 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1058 "Command `%s' was not terminated\n", 1059 TALER_TESTING_interpreter_get_current_label ( 1060 pos->is)); 1061 TALER_MERCHANT_get_private_order_cancel (pos->ogh); 1062 } 1063 GNUNET_free (pos); 1064 } 1065 1066 1067 struct TALER_TESTING_Command 1068 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 pos = GNUNET_new (struct MerchantPollOrderStartState); 1077 pos->order_id = order_id; 1078 pos->merchant_url = merchant_url; 1079 pos->timeout = timeout; 1080 { 1081 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 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 merchant_poll_order_conclude_run (void *cls, 1102 const struct TALER_TESTING_Command *cmd, 1103 struct TALER_TESTING_Interpreter *is) 1104 { 1105 struct MerchantPollOrderConcludeState *poc = cls; 1106 const struct TALER_TESTING_Command *poll_cmd; 1107 struct MerchantPollOrderStartState *pos; 1108 1109 poc->is = is; 1110 poll_cmd = 1111 TALER_TESTING_interpreter_lookup_command (is, 1112 poc->start_reference); 1113 if (NULL == poll_cmd) 1114 TALER_TESTING_FAIL (poc->is); 1115 GNUNET_assert (poll_cmd->run == &merchant_poll_order_start_run); 1116 pos = poll_cmd->cls; 1117 pos->cs = poc; 1118 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 if (NULL == pos->ogh) 1125 poc->task = GNUNET_SCHEDULER_add_now (&conclude_task, 1126 poc); 1127 else 1128 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 merchant_poll_order_conclude_cleanup (void *cls, 1143 const struct TALER_TESTING_Command *cmd) 1144 { 1145 struct MerchantPollOrderConcludeState *poc = cls; 1146 1147 if (NULL != poc->task) 1148 { 1149 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1150 "Command `%s' was not terminated\n", 1151 TALER_TESTING_interpreter_get_current_label ( 1152 poc->is)); 1153 GNUNET_SCHEDULER_cancel (poc->task); 1154 poc->task = NULL; 1155 } 1156 GNUNET_free (poc); 1157 } 1158 1159 1160 struct TALER_TESTING_Command 1161 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 cps = GNUNET_new (struct MerchantPollOrderConcludeState); 1168 cps->start_reference = poll_start_reference; 1169 cps->expected_http_status = http_status; 1170 { 1171 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 return cmd; 1179 } 1180 } 1181 1182 1183 /* end of testing_api_cmd_merchant_get_order.c */