test_merchantdb.c (245288B)
1 /* 2 This file is part of TALER 3 (C) 2014-2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Lesser General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file test_merchantdb.c 18 * @brief testcase for merchant's postgres db plugin 19 * @author Marcello Stanisci 20 * @author Christian Grothoff 21 * @author Pricilla Huang 22 */ 23 #include "taler/platform.h" 24 #include "microhttpd.h" 25 #include <taler/taler_util.h> 26 #include <taler/taler_json_lib.h> 27 #include <taler/taler_signatures.h> 28 #include "taler/taler_merchant_util.h" 29 #include "taler/taler_merchantdb_lib.h" 30 31 32 /** 33 * Global return value for the test. Initially -1, set to 0 upon 34 * completion. Other values indicate some kind of error. 35 */ 36 static int result; 37 38 /** 39 * Handle to the plugin we are testing. 40 */ 41 static struct TALER_MERCHANTDB_Plugin *plugin; 42 43 /** 44 * @param test 0 on success, non-zero on failure 45 */ 46 #define TEST_WITH_FAIL_CLAUSE(test, on_fail) \ 47 if ((test)) \ 48 { \ 49 GNUNET_break (0); \ 50 on_fail \ 51 } 52 53 #define TEST_COND_RET_ON_FAIL(cond, msg) \ 54 if (! (cond)) \ 55 { \ 56 GNUNET_break (0); \ 57 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, \ 58 msg); \ 59 return 1; \ 60 } 61 62 /** 63 * @param __test 0 on success, non-zero on failure 64 */ 65 #define TEST_RET_ON_FAIL(__test) \ 66 TEST_WITH_FAIL_CLAUSE (__test, \ 67 return 1; \ 68 ) 69 70 71 /* ********** Instances ********** */ 72 73 74 /** 75 * Container for instance settings along with keys. 76 */ 77 struct InstanceData 78 { 79 /** 80 * The instance settings. 81 */ 82 struct TALER_MERCHANTDB_InstanceSettings instance; 83 84 /** 85 * The public key for the instance. 86 */ 87 struct TALER_MerchantPublicKeyP merchant_pub; 88 89 /** 90 * The private key for the instance. 91 */ 92 struct TALER_MerchantPrivateKeyP merchant_priv; 93 }; 94 95 96 /** 97 * Creates data for an instance to use with testing. 98 * 99 * @param instance_id the identifier for this instance. 100 * @param instance the instance data to be filled. 101 */ 102 static void 103 make_instance (const char *instance_id, 104 struct InstanceData *instance) 105 { 106 memset (instance, 107 0, 108 sizeof (*instance)); 109 GNUNET_CRYPTO_eddsa_key_create (&instance->merchant_priv.eddsa_priv); 110 GNUNET_CRYPTO_eddsa_key_get_public (&instance->merchant_priv.eddsa_priv, 111 &instance->merchant_pub.eddsa_pub); 112 instance->instance.id = (char *) instance_id; 113 instance->instance.name = (char *) "Test"; 114 instance->instance.address = json_array (); 115 GNUNET_assert (NULL != instance->instance.address); 116 GNUNET_assert (0 == json_array_append_new (instance->instance.address, 117 json_string ("123 Example St"))); 118 instance->instance.jurisdiction = json_array (); 119 GNUNET_assert (NULL != instance->instance.jurisdiction); 120 GNUNET_assert (0 == json_array_append_new (instance->instance.jurisdiction, 121 json_string ("Ohio"))); 122 instance->instance.use_stefan = true; 123 instance->instance.default_wire_transfer_delay = 124 GNUNET_TIME_relative_get_minute_ (); 125 instance->instance.default_pay_delay = GNUNET_TIME_UNIT_SECONDS; 126 instance->instance.default_refund_delay = GNUNET_TIME_UNIT_MINUTES; 127 } 128 129 130 /** 131 * Frees memory allocated when creating an instance for testing. 132 * 133 * @param instance the instance containing the memory to be freed. 134 */ 135 static void 136 free_instance_data (struct InstanceData *instance) 137 { 138 json_decref (instance->instance.address); 139 json_decref (instance->instance.jurisdiction); 140 } 141 142 143 /** 144 * Creates an account with test data for an instance. 145 * 146 * @param account the account to initialize. 147 */ 148 static void 149 make_account (struct TALER_MERCHANTDB_AccountDetails *account) 150 { 151 memset (account, 152 0, 153 sizeof (*account)); 154 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, 155 &account->h_wire.hash); 156 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, 157 &account->salt, 158 sizeof (account->salt)); 159 account->payto_uri.full_payto 160 = (char *) "payto://x-taler-bank/bank.demo.taler.net/4"; 161 account->active = true; 162 } 163 164 165 /** 166 * Instance settings along with corresponding accounts. 167 */ 168 struct InstanceWithAccounts 169 { 170 /** 171 * Pointer to the instance settings. 172 */ 173 const struct TALER_MERCHANTDB_InstanceSettings *instance; 174 175 /** 176 * Length of the array of accounts. 177 */ 178 unsigned int accounts_length; 179 180 /** 181 * Pointer to the array of accounts. 182 */ 183 const struct TALER_MERCHANTDB_AccountDetails *accounts; 184 185 }; 186 187 188 /** 189 * Closure for testing instance lookup. 190 */ 191 struct TestLookupInstances_Closure 192 { 193 /** 194 * Number of instances to compare to. 195 */ 196 unsigned int instances_to_cmp_length; 197 198 /** 199 * Pointer to array of instances. 200 */ 201 const struct InstanceWithAccounts *instances_to_cmp; 202 203 /** 204 * Pointer to array of number of matches for each instance. 205 */ 206 unsigned int *results_matching; 207 208 /** 209 * Total number of results returned. 210 */ 211 unsigned int results_length; 212 }; 213 214 215 /** 216 * Compares two instances for equality. 217 * 218 * @param a the first instance. 219 * @param b the second instance. 220 * @return 0 on equality, 1 otherwise. 221 */ 222 static int 223 check_instances_equal (const struct TALER_MERCHANTDB_InstanceSettings *a, 224 const struct TALER_MERCHANTDB_InstanceSettings *b) 225 { 226 if ((0 != strcmp (a->id, 227 b->id)) || 228 (0 != strcmp (a->name, 229 b->name)) || 230 (1 != json_equal (a->address, 231 b->address)) || 232 (1 != json_equal (a->jurisdiction, 233 b->jurisdiction)) || 234 (a->use_stefan != b->use_stefan) || 235 (a->default_wire_transfer_delay.rel_value_us != 236 b->default_wire_transfer_delay.rel_value_us) || 237 (a->default_pay_delay.rel_value_us != b->default_pay_delay.rel_value_us)) 238 return 1; 239 return 0; 240 } 241 242 243 #if 0 244 /** 245 * Compares two accounts for equality. 246 * 247 * @param a the first account. 248 * @param b the second account. 249 * @return 0 on equality, 1 otherwise. 250 */ 251 static int 252 check_accounts_equal (const struct TALER_MERCHANTDB_AccountDetails *a, 253 const struct TALER_MERCHANTDB_AccountDetails *b) 254 { 255 if ((0 != GNUNET_memcmp (&a->h_wire, 256 &b->h_wire)) || 257 (0 != GNUNET_memcmp (&a->salt, 258 &b->salt)) || 259 (0 != TALER_full_payto_cmp (a->payto_uri, 260 b->payto_uri)) || 261 (a->active != b->active)) 262 return 1; 263 return 0; 264 } 265 266 267 #endif 268 269 270 /** 271 * Called after testing 'lookup_instances'. 272 * 273 * @param cls pointer to 'struct TestLookupInstances_Closure'. 274 * @param merchant_pub public key of the instance 275 * @param merchant_priv private key of the instance, NULL if not available 276 * @param is general instance settings 277 * @param ias instance authentication settings 278 */ 279 static void 280 lookup_instances_cb (void *cls, 281 const struct TALER_MerchantPublicKeyP *merchant_pub, 282 const struct TALER_MerchantPrivateKeyP *merchant_priv, 283 const struct TALER_MERCHANTDB_InstanceSettings *is, 284 const struct TALER_MERCHANTDB_InstanceAuthSettings *ias) 285 { 286 struct TestLookupInstances_Closure *cmp = cls; 287 288 if (NULL == cmp) 289 return; 290 cmp->results_length += 1; 291 /* Look through the closure and test each instance for equality */ 292 for (unsigned int i = 0; cmp->instances_to_cmp_length > i; ++i) 293 { 294 if (0 != check_instances_equal (cmp->instances_to_cmp[i].instance, 295 is)) 296 continue; 297 cmp->results_matching[i] += 1; 298 } 299 } 300 301 302 /** 303 * Tests @e insert_instance. 304 * 305 * @param instance the instance data to insert. 306 * @param expected_result the result that should be returned from the plugin. 307 * @return 0 on success, 1 on failure. 308 */ 309 static int 310 test_insert_instance (const struct InstanceData *instance, 311 enum GNUNET_DB_QueryStatus expected_result) 312 { 313 struct TALER_MERCHANTDB_InstanceAuthSettings ias = { 0 }; 314 315 TEST_COND_RET_ON_FAIL (expected_result == 316 plugin->insert_instance (plugin->cls, 317 &instance->merchant_pub, 318 &instance->merchant_priv, 319 &instance->instance, 320 &ias, 321 false), 322 "Insert instance failed\n"); 323 return 0; 324 } 325 326 327 /** 328 * Tests @e update_instance. 329 * 330 * @param updated_data the instance data to update the row in the database to. 331 * @param expected_result the result that should be returned from the plugin. 332 * @return 0 on success, 1 on failure. 333 */ 334 static int 335 test_update_instance (const struct InstanceData *updated_data, 336 enum GNUNET_DB_QueryStatus expected_result) 337 { 338 TEST_COND_RET_ON_FAIL (expected_result == 339 plugin->update_instance (plugin->cls, 340 &updated_data->instance), 341 "Update instance failed\n"); 342 return 0; 343 } 344 345 346 /** 347 * Tests @e lookup_instances. 348 * 349 * @param active_only whether to lookup all instance, or only active ones. 350 * @param instances_length number of instances to compare to in @e instances. 351 * @param instances a list of instances that will be compared with the results 352 * found. 353 * @return 0 on success, 1 otherwise. 354 */ 355 static int 356 test_lookup_instances (bool active_only, 357 unsigned int instances_length, 358 struct InstanceWithAccounts instances[]) 359 { 360 unsigned int results_matching[GNUNET_NZL (instances_length)]; 361 struct TestLookupInstances_Closure cmp = { 362 .instances_to_cmp_length = instances_length, 363 .instances_to_cmp = instances, 364 .results_matching = results_matching, 365 .results_length = 0 366 }; 367 memset (results_matching, 0, sizeof (unsigned int) * instances_length); 368 if (0 > plugin->lookup_instances (plugin->cls, 369 active_only, 370 &lookup_instances_cb, 371 &cmp)) 372 { 373 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 374 "Lookup instances failed\n"); 375 return 1; 376 } 377 if (instances_length != cmp.results_length) 378 { 379 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 380 "Lookup instances failed: incorrect number of results\n"); 381 return 1; 382 } 383 for (unsigned int i = 0; instances_length > i; ++i) 384 { 385 if (1 != cmp.results_matching[i]) 386 { 387 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 388 "Lookup instances failed: mismatched data\n"); 389 return 1; 390 } 391 } 392 return 0; 393 } 394 395 396 /** 397 * Tests removing the private key of the given instance from the database. 398 * 399 * @param instance the instance whose private key to delete. 400 * @param expected_result the result we expect the db to return. 401 * @return 0 on success, 1 otherwise. 402 */ 403 static int 404 test_delete_instance_private_key (const struct InstanceData *instance, 405 enum GNUNET_DB_QueryStatus expected_result) 406 { 407 TEST_COND_RET_ON_FAIL (expected_result == 408 plugin->delete_instance_private_key ( 409 plugin->cls, 410 instance->instance.id), 411 "Delete instance private key failed\n"); 412 return 0; 413 } 414 415 416 /** 417 * Tests purging all data for an instance from the database. 418 * 419 * @param instance the instance to purge. 420 * @param expected_result the result we expect the db to return. 421 * @return 0 on success, 1 otherwise. 422 */ 423 static int 424 test_purge_instance (const struct InstanceData *instance, 425 enum GNUNET_DB_QueryStatus expected_result) 426 { 427 TEST_COND_RET_ON_FAIL (expected_result == 428 plugin->purge_instance (plugin->cls, 429 instance->instance.id), 430 "Purge instance failed\n"); 431 return 0; 432 } 433 434 435 /** 436 * Tests inserting an account for a merchant instance. 437 * 438 * @param instance the instance to associate the account with. 439 * @param account the account to insert. 440 * @param expected_result the result expected from the db. 441 * @return 0 on success, 1 otherwise. 442 */ 443 static int 444 test_insert_account (const struct InstanceData *instance, 445 const struct TALER_MERCHANTDB_AccountDetails *account, 446 enum GNUNET_DB_QueryStatus expected_result) 447 { 448 TEST_COND_RET_ON_FAIL (expected_result == 449 plugin->insert_account (plugin->cls, 450 account), 451 "Insert account failed\n"); 452 return 0; 453 } 454 455 456 /** 457 * Tests deactivating an account. 458 * 459 * @param account the account to deactivate. 460 * @param expected_result the result expected from the plugin. 461 * @return 0 on success, 1 otherwise. 462 */ 463 static int 464 test_inactivate_account (const struct InstanceData *instance, 465 const struct TALER_MERCHANTDB_AccountDetails *account, 466 enum GNUNET_DB_QueryStatus expected_result) 467 { 468 TEST_COND_RET_ON_FAIL (expected_result == 469 plugin->inactivate_account (plugin->cls, 470 instance->instance.id, 471 &account->h_wire), 472 "Deactivate account failed\n"); 473 return 0; 474 } 475 476 477 /** 478 * Closure for instance tests 479 */ 480 struct TestInstances_Closure 481 { 482 /** 483 * The list of instances that we use for testing instances. 484 */ 485 struct InstanceData instances[2]; 486 487 /** 488 * The list of accounts to use with the instances. 489 */ 490 struct TALER_MERCHANTDB_AccountDetails accounts[2]; 491 492 }; 493 494 495 /** 496 * Sets up the data structures used in the instance tests 497 * 498 * @cls the closure to initialize with test data. 499 */ 500 static void 501 pre_test_instances (struct TestInstances_Closure *cls) 502 { 503 /* Instance */ 504 make_instance ("test_instances_inst0", 505 &cls->instances[0]); 506 make_instance ("test_instances_inst1", 507 &cls->instances[1]); 508 509 /* Accounts */ 510 make_account (&cls->accounts[0]); 511 cls->accounts[0].instance_id 512 = cls->instances[0].instance.id; 513 make_account (&cls->accounts[1]); 514 cls->accounts[1].instance_id 515 = cls->instances[1].instance.id; 516 } 517 518 519 /** 520 * Handles all teardown after testing 521 * 522 * @cls the closure to free data from 523 */ 524 static void 525 post_test_instances (struct TestInstances_Closure *cls) 526 { 527 free_instance_data (&cls->instances[0]); 528 free_instance_data (&cls->instances[1]); 529 } 530 531 532 /** 533 * Function that tests instances. 534 * 535 * @param cls closure with config 536 * @return 0 on success, 1 if failure. 537 */ 538 static int 539 run_test_instances (struct TestInstances_Closure *cls) 540 { 541 struct InstanceWithAccounts instances[2] = { 542 { 543 .accounts_length = 0, 544 .accounts = cls->accounts, 545 .instance = &cls->instances[0].instance 546 }, 547 { 548 .accounts_length = 0, 549 .accounts = cls->accounts, 550 .instance = &cls->instances[1].instance 551 } 552 }; 553 uint64_t account_serial; 554 555 /* Test inserting an instance */ 556 TEST_RET_ON_FAIL (test_insert_instance (&cls->instances[0], 557 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 558 /* Test double insertion fails */ 559 TEST_RET_ON_FAIL (test_insert_instance (&cls->instances[0], 560 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 561 /* Test lookup instances- is our new instance there? */ 562 TEST_RET_ON_FAIL (test_lookup_instances (false, 563 1, 564 instances)); 565 /* Test update instance */ 566 cls->instances[0].instance.name = "Test - updated"; 567 json_array_append_new (cls->instances[0].instance.address, 568 json_pack ("{s:s, s:I}", 569 "this", "is", 570 "more data", 47)); 571 json_array_append_new (cls->instances[0].instance.jurisdiction, 572 json_pack ("{s:s}", 573 "vegetables", "bad")); 574 cls->instances[0].instance.use_stefan = false; 575 cls->instances[0].instance.default_wire_transfer_delay = 576 GNUNET_TIME_UNIT_HOURS; 577 cls->instances[0].instance.default_pay_delay = GNUNET_TIME_UNIT_MINUTES; 578 579 TEST_RET_ON_FAIL (test_update_instance (&cls->instances[0], 580 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 581 TEST_RET_ON_FAIL (test_lookup_instances (false, 582 1, 583 instances)); 584 TEST_RET_ON_FAIL (test_update_instance (&cls->instances[1], 585 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 586 /* Test account creation */ 587 TEST_RET_ON_FAIL (test_insert_account (&cls->instances[0], 588 &cls->accounts[0], 589 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 590 /* Test double account insertion fails */ 591 TEST_RET_ON_FAIL (test_insert_account (&cls->instances[1], 592 &cls->accounts[1], 593 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 594 TEST_RET_ON_FAIL (test_insert_account (&cls->instances[0], 595 &cls->accounts[0], 596 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 597 instances[0].accounts_length = 1; 598 TEST_RET_ON_FAIL (test_lookup_instances (false, 599 1, 600 instances)); 601 /* Test deactivate account */ 602 TEST_RET_ON_FAIL (test_inactivate_account (&cls->instances[0], 603 &cls->accounts[0], 604 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 605 TEST_RET_ON_FAIL (test_inactivate_account (&cls->instances[1], 606 &cls->accounts[1], 607 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 608 cls->accounts[0].active = false; 609 TEST_RET_ON_FAIL (test_lookup_instances (false, 610 1, 611 instances)); 612 /* Test lookup account */ 613 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 614 plugin->lookup_account (plugin->cls, 615 cls->instances[0].instance.id, 616 cls->accounts[0].payto_uri, 617 &account_serial)) 618 { 619 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 620 "Lookup account failed\n"); 621 return 1; 622 } 623 if (1 != account_serial) 624 { 625 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 626 "Lookup account failed: incorrect serial number found\n"); 627 return 1; 628 } 629 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 630 plugin->lookup_account (plugin->cls, 631 cls->instances[0].instance.id, 632 (struct TALER_FullPayto) { 633 (char *) "payto://other-uri" 634 }, 635 &account_serial)) 636 { 637 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 638 "Lookup account failed: account found where there is none\n"); 639 return 1; 640 } 641 /* Test instance private key deletion */ 642 TEST_RET_ON_FAIL (test_delete_instance_private_key (&cls->instances[0], 643 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 644 TEST_RET_ON_FAIL (test_delete_instance_private_key (&cls->instances[1], 645 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 646 TEST_RET_ON_FAIL (test_lookup_instances (true, 647 0, 648 NULL)); 649 TEST_RET_ON_FAIL (test_lookup_instances (false, 650 1, 651 instances)); 652 TEST_RET_ON_FAIL (test_insert_instance (&cls->instances[1], 653 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 654 TEST_RET_ON_FAIL (test_lookup_instances (false, 655 2, 656 instances)); 657 TEST_RET_ON_FAIL (test_lookup_instances (true, 658 1, 659 &instances[1])); 660 TEST_RET_ON_FAIL (test_purge_instance (&cls->instances[1], 661 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 662 TEST_RET_ON_FAIL (test_purge_instance (&cls->instances[1], 663 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 664 /* Test that the instance is gone. */ 665 TEST_RET_ON_FAIL (test_lookup_instances (false, 666 1, 667 instances)); 668 return 0; 669 } 670 671 672 /** 673 * Function that tests instances. 674 * 675 * @return 0 on success, 1 otherwise. 676 */ 677 static int 678 test_instances (void) 679 { 680 struct TestInstances_Closure test_cls; 681 int test_result; 682 683 pre_test_instances (&test_cls); 684 test_result = run_test_instances (&test_cls); 685 post_test_instances (&test_cls); 686 return test_result; 687 } 688 689 690 /* *********** Products ********** */ 691 692 693 /** 694 * A container for data relevant to a product. 695 */ 696 struct ProductData 697 { 698 /** 699 * The identifier of the product. 700 */ 701 const char *id; 702 703 /** 704 * The details of the product. 705 */ 706 struct TALER_MERCHANTDB_ProductDetails product; 707 }; 708 709 710 /** 711 * Creates a product for testing with. 712 * 713 * @param id the id of the product. 714 * @param product the product data to fill. 715 */ 716 static void 717 make_product (const char *id, 718 struct ProductData *product) 719 { 720 static struct TALER_Amount htwenty40; 721 722 GNUNET_assert (GNUNET_OK == 723 TALER_string_to_amount ("EUR:120.40", 724 &htwenty40)); 725 726 memset (product, 727 0, 728 sizeof (*product)); 729 product->id = id; 730 product->product.product_name = "Test product"; 731 product->product.description = "This is a test product"; 732 product->product.description_i18n = json_array (); 733 GNUNET_assert (NULL != product->product.description_i18n); 734 product->product.unit = "boxes"; 735 product->product.minimum_age = 0; 736 product->product.price_array = &htwenty40; 737 product->product.price_array_length = 1; 738 product->product.taxes = json_array (); 739 GNUNET_assert (NULL != product->product.taxes); 740 product->product.total_stock = 55; 741 product->product.total_sold = 0; 742 product->product.total_lost = 0; 743 product->product.image = GNUNET_strdup (""); 744 GNUNET_assert (NULL != product->product.image); 745 product->product.address = json_array (); 746 GNUNET_assert (NULL != product->product.address); 747 product->product.next_restock = GNUNET_TIME_UNIT_ZERO_TS; 748 } 749 750 751 /** 752 * Frees memory associated with @e ProductData. 753 * 754 * @param product the container to free. 755 */ 756 static void 757 free_product_data (struct ProductData *product) 758 { 759 json_decref (product->product.description_i18n); 760 json_decref (product->product.taxes); 761 GNUNET_free (product->product.image); 762 json_decref (product->product.address); 763 } 764 765 766 /** 767 * Compare two products for equality. 768 * 769 * @param a the first product. 770 * @param b the second product. 771 * @return 0 on equality, 1 otherwise. 772 */ 773 static int 774 check_products_equal (const struct TALER_MERCHANTDB_ProductDetails *a, 775 const struct TALER_MERCHANTDB_ProductDetails *b) 776 { 777 if ((0 != strcmp (a->description, 778 b->description)) || 779 (1 != json_equal (a->description_i18n, 780 b->description_i18n)) || 781 (0 != strcmp (a->unit, 782 b->unit)) || 783 (a->price_array_length != b->price_array_length) || 784 (1 != json_equal (a->taxes, 785 b->taxes)) || 786 (a->total_stock != b->total_stock) || 787 (a->total_sold != b->total_sold) || 788 (a->total_lost != b->total_lost) || 789 (0 != strcmp (a->image, 790 b->image)) || 791 (1 != json_equal (a->address, 792 b->address)) || 793 (GNUNET_TIME_timestamp_cmp (a->next_restock, 794 !=, 795 b->next_restock))) 796 797 return 1; 798 for (size_t i = 0; i<a->price_array_length; i++) 799 if ( (GNUNET_OK != 800 TALER_amount_cmp_currency (&a->price_array[i], 801 &b->price_array[i])) || 802 (0 != TALER_amount_cmp (&a->price_array[i], 803 &b->price_array[i])) ) 804 return 1; 805 return 0; 806 } 807 808 809 /** 810 * Tests inserting product data into the database. 811 * 812 * @param instance the instance to insert the product for. 813 * @param product the product data to insert. 814 * @param num_cats length of the @a cats array 815 * @param cats array of categories for the product 816 * @param expected_result the result we expect the db to return. 817 * @param expect_conflict expected conflict status 818 * @param expect_no_instance expected instance missing status 819 * @param expected_no_cat expected category missing index 820 * @return 0 when successful, 1 otherwise. 821 */ 822 static int 823 test_insert_product (const struct InstanceData *instance, 824 const struct ProductData *product, 825 unsigned int num_cats, 826 const uint64_t *cats, 827 enum GNUNET_DB_QueryStatus expected_result, 828 bool expect_conflict, 829 bool expect_no_instance, 830 ssize_t expected_no_cat) 831 { 832 bool conflict; 833 bool no_instance; 834 ssize_t no_cat; 835 bool no_group; 836 bool no_pot; 837 838 TEST_COND_RET_ON_FAIL (expected_result == 839 plugin->insert_product (plugin->cls, 840 instance->instance.id, 841 product->id, 842 &product->product, 843 num_cats, 844 cats, 845 &no_instance, 846 &conflict, 847 &no_cat, 848 &no_group, 849 &no_pot), 850 "Insert product failed\n"); 851 if (expected_result > 0) 852 { 853 TEST_COND_RET_ON_FAIL (no_instance == expect_no_instance, 854 "No instance wrong"); 855 TEST_COND_RET_ON_FAIL (conflict == expect_conflict, 856 "Conflict wrong"); 857 TEST_COND_RET_ON_FAIL (no_cat == expected_no_cat, 858 "Wrong category missing returned"); 859 } 860 return 0; 861 } 862 863 864 /** 865 * Tests updating product data in the database. 866 * 867 * @param instance the instance to update the product for. 868 * @param product the product data to update. 869 * @param expected_result the result we expect the db to return. 870 * @return 0 when successful, 1 otherwise. 871 */ 872 static int 873 test_update_product (const struct InstanceData *instance, 874 const struct ProductData *product, 875 unsigned int num_cats, 876 const uint64_t *cats, 877 enum GNUNET_DB_QueryStatus expected_result, 878 bool expect_no_instance, 879 bool expect_no_product, 880 bool expect_lost_reduced, 881 bool expect_sold_reduced, 882 bool expect_stocked_reduced, 883 ssize_t expected_no_cat) 884 885 { 886 bool no_instance; 887 ssize_t no_cat; 888 bool no_product; 889 bool lost_reduced; 890 bool sold_reduced; 891 bool stocked_reduced; 892 bool no_group; 893 bool no_pot; 894 895 TEST_COND_RET_ON_FAIL ( 896 expected_result == 897 plugin->update_product (plugin->cls, 898 instance->instance.id, 899 product->id, 900 &product->product, 901 num_cats, 902 cats, 903 &no_instance, 904 &no_cat, 905 &no_product, 906 &lost_reduced, 907 &sold_reduced, 908 &stocked_reduced, 909 &no_group, 910 &no_pot), 911 "Update product failed\n"); 912 if (expected_result > 0) 913 { 914 TEST_COND_RET_ON_FAIL (no_instance == expect_no_instance, 915 "No instance wrong"); 916 TEST_COND_RET_ON_FAIL (no_product == expect_no_product, 917 "No product wrong"); 918 TEST_COND_RET_ON_FAIL (lost_reduced == expect_lost_reduced, 919 "No product wrong"); 920 TEST_COND_RET_ON_FAIL (stocked_reduced == expect_stocked_reduced, 921 "Stocked reduced wrong"); 922 TEST_COND_RET_ON_FAIL (sold_reduced == expect_sold_reduced, 923 "Sold reduced wrong"); 924 TEST_COND_RET_ON_FAIL (no_cat == expected_no_cat, 925 "Wrong category missing returned"); 926 } 927 return 0; 928 } 929 930 931 /** 932 * Tests looking up a product from the db. 933 * 934 * @param instance the instance to query from. 935 * @param product the product to query and compare to. 936 * @return 0 when successful, 1 otherwise. 937 */ 938 static int 939 test_lookup_product (const struct InstanceData *instance, 940 const struct ProductData *product) 941 { 942 struct TALER_MERCHANTDB_ProductDetails lookup_result; 943 size_t num_categories = 0; 944 uint64_t *categories = NULL; 945 946 if (0 > plugin->lookup_product (plugin->cls, 947 instance->instance.id, 948 product->id, 949 &lookup_result, 950 &num_categories, 951 &categories)) 952 { 953 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 954 "Lookup product failed\n"); 955 TALER_MERCHANTDB_product_details_free (&lookup_result); 956 return 1; 957 } 958 GNUNET_free (categories); 959 { 960 const struct TALER_MERCHANTDB_ProductDetails *to_cmp = &product->product; 961 962 if (0 != check_products_equal (&lookup_result, 963 to_cmp)) 964 { 965 GNUNET_break (0); 966 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 967 "Lookup product failed: incorrect product returned\n"); 968 TALER_MERCHANTDB_product_details_free (&lookup_result); 969 return 1; 970 } 971 } 972 TALER_MERCHANTDB_product_details_free (&lookup_result); 973 return 0; 974 } 975 976 977 /** 978 * Closure for testing product lookup 979 */ 980 struct TestLookupProducts_Closure 981 { 982 /** 983 * Number of product ids to compare to 984 */ 985 unsigned int products_to_cmp_length; 986 987 /** 988 * Pointer to array of product ids 989 */ 990 const struct ProductData *products_to_cmp; 991 992 /** 993 * Pointer to array of number of matches for each product 994 */ 995 unsigned int *results_matching; 996 997 /** 998 * Total number of results returned 999 */ 1000 unsigned int results_length; 1001 }; 1002 1003 1004 /** 1005 * Function called after calling @e test_lookup_products 1006 * 1007 * @param cls a pointer to the lookup closure. 1008 * @param product_serial DB row ID 1009 * @param product_id the identifier of the product found. 1010 */ 1011 static void 1012 lookup_products_cb (void *cls, 1013 uint64_t product_serial, 1014 const char *product_id) 1015 { 1016 struct TestLookupProducts_Closure *cmp = cls; 1017 1018 GNUNET_assert (product_serial > 0); 1019 if (NULL == cmp) 1020 return; 1021 cmp->results_length += 1; 1022 for (unsigned int i = 0; cmp->products_to_cmp_length > i; ++i) 1023 { 1024 if (0 == strcmp (cmp->products_to_cmp[i].id, 1025 product_id)) 1026 cmp->results_matching[i] += 1; 1027 } 1028 } 1029 1030 1031 /** 1032 * Tests looking up all products for an instance. 1033 * 1034 * @param instance the instance to query from. 1035 * @param products_length the number of products we are expecting. 1036 * @param products the list of products that we expect to be found. 1037 * @return 0 when successful, 1 otherwise. 1038 */ 1039 static int 1040 test_lookup_products (const struct InstanceData *instance, 1041 unsigned int products_length, 1042 const struct ProductData *products) 1043 { 1044 unsigned int results_matching[GNUNET_NZL (products_length)]; 1045 struct TestLookupProducts_Closure cls = { 1046 .products_to_cmp_length = products_length, 1047 .products_to_cmp = products, 1048 .results_matching = results_matching, 1049 .results_length = 0 1050 }; 1051 memset (results_matching, 0, sizeof (unsigned int) * products_length); 1052 if (0 > plugin->lookup_products (plugin->cls, 1053 instance->instance.id, 1054 0, 1055 20, 1056 NULL, 1057 NULL, 1058 NULL, 1059 0, 1060 &lookup_products_cb, 1061 &cls)) 1062 { 1063 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1064 "Lookup products failed\n"); 1065 return 1; 1066 } 1067 if (products_length != cls.results_length) 1068 { 1069 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1070 "Lookup products failed: incorrect number of results\n"); 1071 return 1; 1072 } 1073 for (unsigned int i = 0; products_length > i; ++i) 1074 { 1075 if (1 != cls.results_matching[i]) 1076 { 1077 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1078 "Lookup products failed: mismatched data\n"); 1079 return 1; 1080 } 1081 } 1082 return 0; 1083 } 1084 1085 1086 /** 1087 * Tests deleting a product. 1088 * 1089 * @param instance the instance to delete the product from. 1090 * @param product the product that should be deleted. 1091 * @param expected_result the result that we expect the plugin to return. 1092 * @return 0 when successful, 1 otherwise. 1093 */ 1094 static int 1095 test_delete_product (const struct InstanceData *instance, 1096 const struct ProductData *product, 1097 enum GNUNET_DB_QueryStatus expected_result) 1098 { 1099 TEST_COND_RET_ON_FAIL (expected_result == 1100 plugin->delete_product (plugin->cls, 1101 instance->instance.id, 1102 product->id), 1103 "Delete product failed\n"); 1104 return 0; 1105 } 1106 1107 1108 /** 1109 * Closure for product tests. 1110 */ 1111 struct TestProducts_Closure 1112 { 1113 /** 1114 * The instance to use for this test. 1115 */ 1116 struct InstanceData instance; 1117 1118 /** 1119 * The array of products. 1120 */ 1121 struct ProductData products[2]; 1122 }; 1123 1124 1125 /** 1126 * Sets up the data structures used in the product tests. 1127 * 1128 * @param cls the closure to fill with test data. 1129 */ 1130 static void 1131 pre_test_products (struct TestProducts_Closure *cls) 1132 { 1133 static struct TALER_Amount four95; 1134 1135 /* Instance */ 1136 make_instance ("test_inst_products", 1137 &cls->instance); 1138 1139 /* Products */ 1140 make_product ("test_products_pd_0", 1141 &cls->products[0]); 1142 1143 make_product ("test_products_pd_1", 1144 &cls->products[1]); 1145 cls->products[1].product.description = "This is a another test product"; 1146 cls->products[1].product.unit = "cans"; 1147 cls->products[1].product.minimum_age = 0; 1148 1149 GNUNET_assert (GNUNET_OK == 1150 TALER_string_to_amount ("EUR:4.95", 1151 &four95)); 1152 cls->products[1].product.price_array = &four95; 1153 cls->products[1].product.price_array_length = 1; 1154 cls->products[1].product.total_stock = 5001; 1155 } 1156 1157 1158 /** 1159 * Handles all teardown after testing. 1160 * 1161 * @param cls the closure containing memory to be freed. 1162 */ 1163 static void 1164 post_test_products (struct TestProducts_Closure *cls) 1165 { 1166 free_instance_data (&cls->instance); 1167 free_product_data (&cls->products[0]); 1168 free_product_data (&cls->products[1]); 1169 } 1170 1171 1172 /** 1173 * Runs the tests for products. 1174 * 1175 * @param cls the container of the test data. 1176 * @return 0 on success, 1 otherwise. 1177 */ 1178 static int 1179 run_test_products (struct TestProducts_Closure *cls) 1180 { 1181 struct GNUNET_Uuid uuid; 1182 struct GNUNET_TIME_Timestamp refund_deadline = 1183 GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_WEEKS); 1184 1185 /* Test that insert without an instance fails */ 1186 TEST_RET_ON_FAIL (test_insert_product (&cls->instance, 1187 &cls->products[0], 1188 0, 1189 NULL, 1190 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, 1191 false, 1192 true, 1193 -1)); 1194 /* Insert the instance */ 1195 TEST_RET_ON_FAIL (test_insert_instance (&cls->instance, 1196 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 1197 /* Test inserting a product */ 1198 TEST_RET_ON_FAIL (test_insert_product (&cls->instance, 1199 &cls->products[0], 1200 0, 1201 NULL, 1202 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, 1203 false, 1204 false, 1205 -1)); 1206 /* Test that double insert succeeds */ 1207 TEST_RET_ON_FAIL (test_insert_product (&cls->instance, 1208 &cls->products[0], 1209 0, 1210 NULL, 1211 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, 1212 false, 1213 false, 1214 -1)); 1215 /* Test that conflicting insert fails */ 1216 { 1217 uint64_t cat = 42; 1218 1219 TEST_RET_ON_FAIL (test_insert_product (&cls->instance, 1220 &cls->products[0], 1221 1, 1222 &cat, 1223 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, 1224 true, 1225 false, 1226 -1)); 1227 } 1228 /* Test lookup of individual products */ 1229 TEST_RET_ON_FAIL (test_lookup_product (&cls->instance, 1230 &cls->products[0])); 1231 /* Make sure it fails correctly for products that don't exist */ 1232 { 1233 size_t num_categories = 0; 1234 uint64_t *categories = NULL; 1235 1236 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 1237 plugin->lookup_product (plugin->cls, 1238 cls->instance.instance.id, 1239 "nonexistent_product", 1240 NULL, 1241 &num_categories, 1242 &categories)) 1243 { 1244 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1245 "Lookup product failed\n"); 1246 return 1; 1247 } 1248 GNUNET_free (categories); 1249 } 1250 /* Test product update */ 1251 cls->products[0].product.description = 1252 "This is a test product that has been updated!"; 1253 GNUNET_assert (0 == 1254 json_array_append_new ( 1255 cls->products[0].product.description_i18n, 1256 json_string ( 1257 "description in another language"))); 1258 cls->products[0].product.unit = "barrels"; 1259 { 1260 static struct TALER_Amount seven68; 1261 1262 GNUNET_assert (GNUNET_OK == 1263 TALER_string_to_amount ("EUR:7.68", 1264 &seven68)); 1265 cls->products[0].product.price_array = &seven68; 1266 cls->products[0].product.price_array_length = 1; 1267 } 1268 GNUNET_assert (0 == 1269 json_array_append_new (cls->products[0].product.taxes, 1270 json_string ("2% sales tax"))); 1271 cls->products[0].product.total_stock = 100; 1272 cls->products[0].product.total_sold = 0; /* will be ignored! */ 1273 cls->products[0].product.total_lost = 7; 1274 GNUNET_free (cls->products[0].product.image); 1275 cls->products[0].product.image = GNUNET_strdup ("image"); 1276 GNUNET_assert (0 == 1277 json_array_append_new (cls->products[0].product.address, 1278 json_string ("444 Some Street"))); 1279 cls->products[0].product.next_restock = GNUNET_TIME_timestamp_get (); 1280 TEST_RET_ON_FAIL (test_update_product ( 1281 &cls->instance, 1282 &cls->products[0], 1283 0, 1284 NULL, 1285 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, 1286 false, 1287 false, 1288 false, 1289 false, 1290 false, 1291 -1)); 1292 1293 { 1294 struct ProductData stock_dec = cls->products[0]; 1295 1296 stock_dec.product.total_stock = 40; 1297 TEST_RET_ON_FAIL (test_update_product ( 1298 &cls->instance, 1299 &stock_dec, 1300 0, 1301 NULL, 1302 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, 1303 false, 1304 false, 1305 false, 1306 false, 1307 true, 1308 -1)); 1309 } 1310 { 1311 struct ProductData lost_dec = cls->products[0]; 1312 1313 lost_dec.product.total_lost = 1; 1314 TEST_RET_ON_FAIL (test_update_product ( 1315 &cls->instance, 1316 &lost_dec, 1317 0, 1318 NULL, 1319 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, 1320 false, 1321 false, 1322 true, 1323 false, 1324 false, 1325 -1)); 1326 } 1327 TEST_RET_ON_FAIL (test_lookup_product (&cls->instance, 1328 &cls->products[0])); 1329 TEST_RET_ON_FAIL (test_update_product ( 1330 &cls->instance, 1331 &cls->products[1], 1332 0, 1333 NULL, 1334 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, 1335 false, 1336 true, 1337 false, 1338 false, 1339 false, 1340 -1)); 1341 /* Test collective product lookup */ 1342 TEST_RET_ON_FAIL (test_insert_product (&cls->instance, 1343 &cls->products[1], 1344 0, 1345 NULL, 1346 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, 1347 false, 1348 false, 1349 -1)); 1350 TEST_RET_ON_FAIL (test_lookup_products (&cls->instance, 1351 2, 1352 cls->products)); 1353 /* Test locking */ 1354 uuid.value[0] = 0x1287346a; 1355 if (0 != plugin->lock_product (plugin->cls, 1356 cls->instance.instance.id, 1357 cls->products[0].id, 1358 &uuid, 1359 256, 1360 0, 1361 refund_deadline)) 1362 { 1363 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1364 "Lock product failed\n"); 1365 return 1; 1366 } 1367 if (1 != plugin->lock_product (plugin->cls, 1368 cls->instance.instance.id, 1369 cls->products[0].id, 1370 &uuid, 1371 1, 1372 0, 1373 refund_deadline)) 1374 { 1375 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1376 "Lock product failed\n"); 1377 return 1; 1378 } 1379 /* Test product deletion */ 1380 TEST_RET_ON_FAIL (test_delete_product (&cls->instance, 1381 &cls->products[1], 1382 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 1383 /* Test double deletion fails */ 1384 TEST_RET_ON_FAIL (test_delete_product (&cls->instance, 1385 &cls->products[1], 1386 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 1387 TEST_RET_ON_FAIL (test_lookup_products (&cls->instance, 1388 1, 1389 cls->products)); 1390 /* Test unlocking */ 1391 if (1 != plugin->unlock_inventory (plugin->cls, 1392 &uuid)) 1393 { 1394 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1395 "Unlock inventory failed\n"); 1396 return 1; 1397 } 1398 if (0 != plugin->unlock_inventory (plugin->cls, 1399 &uuid)) 1400 { 1401 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1402 "Unlock inventory failed\n"); 1403 return 1; 1404 } 1405 TEST_RET_ON_FAIL (test_delete_product (&cls->instance, 1406 &cls->products[0], 1407 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 1408 TEST_RET_ON_FAIL (test_lookup_products (&cls->instance, 1409 0, 1410 NULL)); 1411 return 0; 1412 } 1413 1414 1415 /** 1416 * Takes care of product testing. 1417 * 1418 * @return 0 on success, 1 otherwise. 1419 */ 1420 static int 1421 test_products (void) 1422 { 1423 struct TestProducts_Closure test_cls; 1424 int test_result; 1425 1426 pre_test_products (&test_cls); 1427 test_result = run_test_products (&test_cls); 1428 post_test_products (&test_cls); 1429 return test_result; 1430 } 1431 1432 1433 /* ********** Orders ********** */ 1434 1435 1436 /** 1437 * Container for order data 1438 */ 1439 struct OrderData 1440 { 1441 /** 1442 * The id of the order 1443 */ 1444 const char *id; 1445 1446 /** 1447 * The pay deadline for the order 1448 */ 1449 struct GNUNET_TIME_Timestamp pay_deadline; 1450 1451 /** 1452 * The contract of the order 1453 */ 1454 json_t *contract; 1455 1456 /** 1457 * The claim token for the order. 1458 */ 1459 struct TALER_ClaimTokenP claim_token; 1460 }; 1461 1462 1463 /** 1464 * Builds an order for testing. 1465 * 1466 * @param order_id the identifier to use for the order. 1467 * @param order the container to fill with data. 1468 */ 1469 static void 1470 make_order (const char *order_id, 1471 struct OrderData *order) 1472 { 1473 struct GNUNET_TIME_Timestamp refund_deadline; 1474 1475 order->id = order_id; 1476 order->contract = json_object (); 1477 GNUNET_assert (NULL != order->contract); 1478 order->pay_deadline = GNUNET_TIME_relative_to_timestamp ( 1479 GNUNET_TIME_UNIT_DAYS); 1480 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, 1481 &order->claim_token, 1482 sizeof (order->claim_token)); 1483 refund_deadline = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_WEEKS); 1484 GNUNET_assert (0 == 1485 json_object_set_new (order->contract, 1486 "fulfillment_url", 1487 json_string ("a"))); 1488 GNUNET_assert (0 == 1489 json_object_set_new (order->contract, 1490 "summary", 1491 json_string ("Test order"))); 1492 GNUNET_assert (0 == 1493 json_object_set_new (order->contract, 1494 "order_id", 1495 json_string (order_id))); 1496 GNUNET_assert (0 == 1497 json_object_set_new ( 1498 order->contract, 1499 "pay_deadline", 1500 GNUNET_JSON_from_timestamp (order->pay_deadline)) 1501 ); 1502 GNUNET_assert (0 == 1503 json_object_set_new (order->contract, 1504 "refund_deadline", 1505 GNUNET_JSON_from_timestamp ( 1506 refund_deadline))); 1507 } 1508 1509 1510 /** 1511 * Frees memory associated with an order. 1512 * 1513 * @param the order to free. 1514 */ 1515 static void 1516 free_order_data (struct OrderData *order) 1517 { 1518 json_decref (order->contract); 1519 } 1520 1521 1522 /** 1523 * Tests inserting an order into the database. 1524 * 1525 * @param instance the instance to insert the order for. 1526 * @param order the order to insert. 1527 * @param expected_result the value we expect the db to return. 1528 * @return 0 on success, 1 otherwise. 1529 */ 1530 static int 1531 test_insert_order (const struct InstanceData *instance, 1532 const struct OrderData *order, 1533 enum GNUNET_DB_QueryStatus expected_result) 1534 { 1535 struct TALER_MerchantPostDataHashP h_post; 1536 1537 memset (&h_post, 1538 42, 1539 sizeof (h_post)); 1540 TEST_COND_RET_ON_FAIL (expected_result == 1541 plugin->insert_order (plugin->cls, 1542 instance->instance.id, 1543 order->id, 1544 NULL, /* session_id */ 1545 &h_post, 1546 order->pay_deadline, 1547 &order->claim_token, 1548 order->contract, 1549 NULL, 1550 0), 1551 "Insert order failed\n"); 1552 return 0; 1553 } 1554 1555 1556 /** 1557 * Tests looking up an order in the database. 1558 * 1559 * @param instance the instance to lookup from. 1560 * @param order the order that should be looked up. 1561 * @return 0 on success, 1 otherwise. 1562 */ 1563 static int 1564 test_lookup_order (const struct InstanceData *instance, 1565 const struct OrderData *order) 1566 { 1567 struct TALER_ClaimTokenP ct; 1568 json_t *lookup_terms = NULL; 1569 struct TALER_MerchantPostDataHashP oh; 1570 struct TALER_MerchantPostDataHashP wh; 1571 1572 memset (&wh, 1573 42, 1574 sizeof (wh)); 1575 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 1576 plugin->lookup_order (plugin->cls, 1577 instance->instance.id, 1578 order->id, 1579 &ct, 1580 &oh, 1581 &lookup_terms)) 1582 { 1583 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1584 "Lookup order failed\n"); 1585 if (NULL != lookup_terms) 1586 json_decref (lookup_terms); 1587 return 1; 1588 } 1589 if ( (1 != json_equal (order->contract, 1590 lookup_terms)) || 1591 (0 != GNUNET_memcmp (&order->claim_token, 1592 &ct)) || 1593 (0 != GNUNET_memcmp (&oh, 1594 &wh)) ) 1595 { 1596 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1597 "Lookup order failed: incorrect order returned\n"); 1598 if (NULL != lookup_terms) 1599 json_decref (lookup_terms); 1600 return 1; 1601 } 1602 json_decref (lookup_terms); 1603 return 0; 1604 } 1605 1606 1607 /** 1608 * Closure for testing order lookup 1609 */ 1610 struct TestLookupOrders_Closure 1611 { 1612 /** 1613 * Number of orders to compare to 1614 */ 1615 unsigned int orders_to_cmp_length; 1616 1617 /** 1618 * Pointer to (ordered) array of order ids 1619 */ 1620 const struct OrderData *orders_to_cmp; 1621 1622 /** 1623 * Pointer to array of bools indicating matches in the correct index 1624 */ 1625 bool *results_match; 1626 1627 /** 1628 * Total number of results returned 1629 */ 1630 unsigned int results_length; 1631 }; 1632 1633 1634 /** 1635 * Called after @e test_lookup_orders. 1636 * 1637 * @param cls the lookup closure. 1638 * @param order_id the identifier of the order found. 1639 * @param order_serial the row number of the order found. 1640 * @param timestamp when the order was added to the database. 1641 */ 1642 static void 1643 lookup_orders_cb (void *cls, 1644 const char *order_id, 1645 uint64_t order_serial, 1646 struct GNUNET_TIME_Timestamp timestamp) 1647 { 1648 struct TestLookupOrders_Closure *cmp = cls; 1649 unsigned int i; 1650 1651 if (NULL == cmp) 1652 return; 1653 i = cmp->results_length; 1654 cmp->results_length += 1; 1655 if (cmp->orders_to_cmp_length > i) 1656 { 1657 /* Compare the orders */ 1658 if (0 == strcmp (cmp->orders_to_cmp[i].id, 1659 order_id)) 1660 cmp->results_match[i] = true; 1661 else 1662 cmp->results_match[i] = false; 1663 } 1664 } 1665 1666 1667 /** 1668 * Tests looking up orders for an instance. 1669 * 1670 * @param instance the instance. 1671 * @param filter the filters applied on the lookup. 1672 * @param orders_length the number of orders we expect to find. 1673 * @param orders the orders we expect to find. 1674 * @return 0 on success, 1 otherwise. 1675 */ 1676 static int 1677 test_lookup_orders (const struct InstanceData *instance, 1678 const struct TALER_MERCHANTDB_OrderFilter *filter, 1679 unsigned int orders_length, 1680 const struct OrderData *orders) 1681 { 1682 bool results_match[GNUNET_NZL (orders_length)]; 1683 struct TestLookupOrders_Closure cls = { 1684 .orders_to_cmp_length = orders_length, 1685 .orders_to_cmp = orders, 1686 .results_match = results_match, 1687 .results_length = 0 1688 }; 1689 memset (results_match, 1690 0, 1691 sizeof (bool) * orders_length); 1692 if (0 > plugin->lookup_orders (plugin->cls, 1693 instance->instance.id, 1694 filter, 1695 &lookup_orders_cb, 1696 &cls)) 1697 { 1698 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1699 "Lookup orders failed\n"); 1700 return 1; 1701 } 1702 if (orders_length != cls.results_length) 1703 { 1704 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1705 "Lookup orders failed: incorrect number of results (%d)\n", 1706 cls.results_length); 1707 return 1; 1708 } 1709 for (unsigned int i = 0; orders_length > i; ++i) 1710 { 1711 if (false == cls.results_match[i]) 1712 { 1713 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1714 "Lookup orders failed: mismatched data (index %d)\n", 1715 i); 1716 return 1; 1717 } 1718 } 1719 return 0; 1720 } 1721 1722 1723 /** 1724 * Container for data used for looking up the row number of an order. 1725 */ 1726 struct LookupOrderSerial_Closure 1727 { 1728 /** 1729 * The order that is being looked up. 1730 */ 1731 const struct OrderData *order; 1732 1733 /** 1734 * The serial of the order that was found. 1735 */ 1736 uint64_t serial; 1737 }; 1738 1739 1740 /** 1741 * Called after @e test_lookup_orders. 1742 * 1743 * @param cls the lookup closure. 1744 * @param order_id the identifier of the order found. 1745 * @param order_serial the row number of the order found. 1746 * @param timestamp when the order was added to the database. 1747 */ 1748 static void 1749 get_order_serial_cb (void *cls, 1750 const char *order_id, 1751 uint64_t order_serial, 1752 struct GNUNET_TIME_Timestamp timestamp) 1753 { 1754 struct LookupOrderSerial_Closure *lookup_cls = cls; 1755 if (NULL == lookup_cls) 1756 return; 1757 if (0 == strcmp (lookup_cls->order->id, 1758 order_id)) 1759 lookup_cls->serial = order_serial; 1760 } 1761 1762 1763 /** 1764 * Convenience function for getting the row number of an order. 1765 * 1766 * @param instance the instance to look up from. 1767 * @param order the order to lookup the serial for. 1768 * @return the row number of the order. 1769 */ 1770 static uint64_t 1771 get_order_serial (const struct InstanceData *instance, 1772 const struct OrderData *order) 1773 { 1774 struct LookupOrderSerial_Closure lookup_cls = { 1775 .order = order, 1776 .serial = 0 1777 }; 1778 struct TALER_MERCHANTDB_OrderFilter filter = { 1779 .paid = TALER_EXCHANGE_YNA_ALL, 1780 .refunded = TALER_EXCHANGE_YNA_ALL, 1781 .wired = TALER_EXCHANGE_YNA_ALL, 1782 .date = GNUNET_TIME_UNIT_ZERO_TS, 1783 .start_row = 0, 1784 .delta = 256 1785 }; 1786 1787 GNUNET_assert (0 < plugin->lookup_orders (plugin->cls, 1788 instance->instance.id, 1789 &filter, 1790 &get_order_serial_cb, 1791 &lookup_cls)); 1792 GNUNET_assert (0 != lookup_cls.serial); 1793 1794 return lookup_cls.serial; 1795 } 1796 1797 1798 /** 1799 * Tests deleting an order from the database. 1800 * 1801 * @param instance the instance to delete the order from. 1802 * @param order the order to delete. 1803 * @param expected_result the result we expect to receive. 1804 * @return 0 on success, 1 otherwise. 1805 */ 1806 static int 1807 test_delete_order (const struct InstanceData *instance, 1808 const struct OrderData *order, 1809 enum GNUNET_DB_QueryStatus expected_result) 1810 { 1811 TEST_COND_RET_ON_FAIL (expected_result == 1812 plugin->delete_order (plugin->cls, 1813 instance->instance.id, 1814 order->id, 1815 false), 1816 "Delete order failed\n"); 1817 return 0; 1818 } 1819 1820 1821 /** 1822 * Test inserting contract terms for an order. 1823 * 1824 * @param instance the instance. 1825 * @param order the order containing the contract terms. 1826 * @param expected_result the result we expect to receive. 1827 * @return 0 on success, 1 otherwise. 1828 */ 1829 static int 1830 test_insert_contract_terms (const struct InstanceData *instance, 1831 const struct OrderData *order, 1832 enum GNUNET_DB_QueryStatus expected_result) 1833 { 1834 uint64_t os; 1835 1836 TEST_COND_RET_ON_FAIL (expected_result == 1837 plugin->insert_contract_terms (plugin->cls, 1838 instance->instance.id, 1839 order->id, 1840 order->contract, 1841 &os), 1842 "Insert contract terms failed\n"); 1843 return 0; 1844 } 1845 1846 1847 /** 1848 * Test updating contract terms for an order. 1849 * 1850 * @param instance the instance. 1851 * @param order the order containing the contract terms. 1852 * @param expected_result the result we expect to receive. 1853 * @return 0 on success, 1 otherwise. 1854 */ 1855 static int 1856 test_update_contract_terms (const struct InstanceData *instance, 1857 const struct OrderData *order, 1858 enum GNUNET_DB_QueryStatus expected_result) 1859 { 1860 TEST_COND_RET_ON_FAIL (expected_result == 1861 plugin->update_contract_terms (plugin->cls, 1862 instance->instance.id, 1863 order->id, 1864 order->contract), 1865 "Update contract terms failed\n"); 1866 return 0; 1867 } 1868 1869 1870 /** 1871 * Tests lookup of contract terms 1872 * 1873 * @param instance the instance to lookup from. 1874 * @param order the order to lookup for. 1875 * @return 0 on success, 1 otherwise. 1876 */ 1877 static int 1878 test_lookup_contract_terms (const struct InstanceData *instance, 1879 const struct OrderData *order) 1880 { 1881 json_t *contract = NULL; 1882 uint64_t order_serial; 1883 1884 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 1885 plugin->lookup_contract_terms (plugin->cls, 1886 instance->instance.id, 1887 order->id, 1888 &contract, 1889 &order_serial, 1890 NULL)) 1891 { 1892 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1893 "Lookup contract terms failed\n"); 1894 GNUNET_assert (NULL == contract); 1895 return 1; 1896 } 1897 if (1 != json_equal (order->contract, 1898 contract)) 1899 { 1900 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1901 "Lookup contract terms failed: mismatched data\n"); 1902 json_decref (contract); 1903 return 1; 1904 } 1905 json_decref (contract); 1906 return 0; 1907 } 1908 1909 1910 /** 1911 * Tests deleting contract terms for an order. 1912 * 1913 * @param instance the instance to delete from. 1914 * @param order the order whose contract terms we should delete. 1915 * @param legal_expiration how long we must wait after creating an order to delete it 1916 * @param expected_result the result we expect to receive. 1917 * @return 0 on success, 1 otherwise. 1918 */ 1919 static int 1920 test_delete_contract_terms (const struct InstanceData *instance, 1921 const struct OrderData *order, 1922 struct GNUNET_TIME_Relative legal_expiration, 1923 enum GNUNET_DB_QueryStatus expected_result) 1924 { 1925 TEST_COND_RET_ON_FAIL (expected_result == 1926 plugin->delete_contract_terms (plugin->cls, 1927 instance->instance.id, 1928 order->id, 1929 legal_expiration), 1930 "Delete contract terms failed\n"); 1931 return 0; 1932 } 1933 1934 1935 /** 1936 * Test marking a contract as paid in the database. 1937 * 1938 * @param instance the instance to use. 1939 * @param order the order whose contract to use. 1940 * @param expected_result the result we expect to receive. 1941 * @return 0 on success, 1 otherwise. 1942 */ 1943 static int 1944 test_mark_contract_paid (const struct InstanceData *instance, 1945 const struct OrderData *order, 1946 enum GNUNET_DB_QueryStatus expected_result) 1947 { 1948 struct TALER_PrivateContractHashP h_contract_terms; 1949 1950 GNUNET_assert (GNUNET_OK == 1951 TALER_JSON_contract_hash (order->contract, 1952 &h_contract_terms)); 1953 TEST_COND_RET_ON_FAIL (expected_result == 1954 plugin->mark_contract_paid (plugin->cls, 1955 instance->instance.id, 1956 &h_contract_terms, 1957 "test_orders_session", 1958 -1), 1959 "Mark contract paid failed\n"); 1960 return 0; 1961 } 1962 1963 1964 /** 1965 * Tests looking up the status of an order. 1966 * 1967 * @param instance the instance to lookup from. 1968 * @param order the order to lookup. 1969 * @param expected_paid whether the order was paid or not. 1970 * @return 0 on success, 1 otherwise. 1971 */ 1972 static int 1973 test_lookup_order_status (const struct InstanceData *instance, 1974 const struct OrderData *order, 1975 bool expected_paid) 1976 { 1977 struct TALER_PrivateContractHashP h_contract_terms_expected; 1978 struct TALER_PrivateContractHashP h_contract_terms; 1979 bool order_paid = false; 1980 1981 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 1982 plugin->lookup_order_status (plugin->cls, 1983 instance->instance.id, 1984 order->id, 1985 &h_contract_terms, 1986 &order_paid)) 1987 { 1988 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1989 "Lookup order status failed\n"); 1990 return 1; 1991 } 1992 GNUNET_assert (GNUNET_OK == 1993 TALER_JSON_contract_hash (order->contract, 1994 &h_contract_terms_expected)); 1995 if ((expected_paid != order_paid) || 1996 (0 != GNUNET_memcmp (&h_contract_terms, 1997 &h_contract_terms_expected))) 1998 { 1999 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2000 "Lookup order status/deposit failed: mismatched data\n"); 2001 return 1; 2002 } 2003 return 0; 2004 } 2005 2006 2007 /** 2008 * Test looking up an order by its fulfillment. 2009 * 2010 * @param instance the instance to lookup from. 2011 * @param order the order to lookup. 2012 * @param the session id associated with the payment. 2013 * @return 0 on success, 1 otherwise. 2014 */ 2015 static int 2016 test_lookup_order_by_fulfillment (const struct InstanceData *instance, 2017 const struct OrderData *order, 2018 const char *session_id) 2019 { 2020 char *order_id; 2021 const char *fulfillment_url = 2022 json_string_value (json_object_get (order->contract, 2023 "fulfillment_url")); 2024 GNUNET_assert (NULL != fulfillment_url); 2025 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 2026 plugin->lookup_order_by_fulfillment (plugin->cls, 2027 instance->instance.id, 2028 fulfillment_url, 2029 session_id, 2030 false, 2031 &order_id)) 2032 { 2033 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2034 "Lookup order by fulfillment failed\n"); 2035 GNUNET_free (order_id); 2036 return 1; 2037 } 2038 if (0 != strcmp (order->id, 2039 order_id)) 2040 { 2041 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2042 "Lookup order by fulfillment failed\n"); 2043 GNUNET_free (order_id); 2044 return 1; 2045 } 2046 GNUNET_free (order_id); 2047 return 0; 2048 } 2049 2050 2051 /** 2052 * Test looking up the status of an order. 2053 * 2054 * @param order_id the row of the order in the database. 2055 * @param session_id the session id associated with the payment. 2056 * @param expected_paid whether the order was paid or not. 2057 * @param expected_wired whether the order was wired or not. 2058 * @return 0 on success, 1 otherwise. 2059 */ 2060 static int 2061 test_lookup_payment_status (const char *instance_id, 2062 const char *order_id, 2063 const char *session_id, 2064 bool expected_paid, 2065 bool expected_wired) 2066 { 2067 bool paid; 2068 bool wired; 2069 bool matches; 2070 uint64_t os; 2071 int16_t choice_index; 2072 2073 TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 2074 plugin->lookup_contract_terms3 (plugin->cls, 2075 instance_id, 2076 order_id, 2077 session_id, 2078 NULL, 2079 &os, 2080 &paid, 2081 &wired, 2082 &matches, 2083 NULL, 2084 &choice_index), 2085 "Lookup payment status failed\n"); 2086 if ( (NULL != session_id) && (! matches) ) 2087 { 2088 paid = false; 2089 wired = false; 2090 } 2091 if (expected_wired != wired) 2092 { 2093 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2094 "Lookup payment status failed: wired status is wrong\n"); 2095 return 1; 2096 } 2097 if (expected_paid != paid) 2098 { 2099 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2100 "Lookup payment status failed: paid status is wrong\n"); 2101 return 1; 2102 } 2103 return 0; 2104 } 2105 2106 2107 /** 2108 * Test marking an order as being wired. 2109 * 2110 * @param order_id the row of the order in the database. 2111 * @param expected_result the result we expect the plugin to return. 2112 * @return 0 on success, 1 otherwise. 2113 */ 2114 static int 2115 test_mark_order_wired (uint64_t order_id, 2116 enum GNUNET_DB_QueryStatus expected_result) 2117 { 2118 TEST_COND_RET_ON_FAIL (expected_result == 2119 plugin->mark_order_wired (plugin->cls, 2120 order_id), 2121 "Mark order wired failed\n"); 2122 return 0; 2123 } 2124 2125 2126 /** 2127 * Closure for order tests. 2128 */ 2129 struct TestOrders_Closure 2130 { 2131 /** 2132 * The instance to use for the order tests. 2133 */ 2134 struct InstanceData instance; 2135 2136 /** 2137 * A product to use for the order tests. 2138 */ 2139 struct ProductData product; 2140 2141 /** 2142 * The array of orders 2143 */ 2144 struct OrderData orders[3]; 2145 }; 2146 2147 2148 /** 2149 * Initializes order test data. 2150 * 2151 * @param cls the order test closure. 2152 */ 2153 static void 2154 pre_test_orders (struct TestOrders_Closure *cls) 2155 { 2156 /* Instance */ 2157 make_instance ("test_inst_orders", 2158 &cls->instance); 2159 2160 /* Product */ 2161 make_product ("test_orders_pd_0", 2162 &cls->product); 2163 2164 /* Orders */ 2165 make_order ("test_orders_od_0", 2166 &cls->orders[0]); 2167 make_order ("test_orders_od_1", 2168 &cls->orders[1]); 2169 make_order ("test_orders_od_2", 2170 &cls->orders[2]); 2171 2172 GNUNET_assert (0 == 2173 json_object_set_new (cls->orders[1].contract, 2174 "other_field", 2175 json_string ("Second contract"))); 2176 2177 cls->orders[2].pay_deadline = GNUNET_TIME_UNIT_ZERO_TS; 2178 GNUNET_assert (0 == 2179 json_object_set_new ( 2180 cls->orders[2].contract, 2181 "pay_deadline", 2182 GNUNET_JSON_from_timestamp (cls->orders[2].pay_deadline))); 2183 } 2184 2185 2186 /** 2187 * Frees memory after order tests. 2188 * 2189 * @param cls the order test closure. 2190 */ 2191 static void 2192 post_test_orders (struct TestOrders_Closure *cls) 2193 { 2194 free_instance_data (&cls->instance); 2195 free_product_data (&cls->product); 2196 free_order_data (&cls->orders[0]); 2197 free_order_data (&cls->orders[1]); 2198 free_order_data (&cls->orders[2]); 2199 } 2200 2201 2202 /** 2203 * Run the tests for orders. 2204 * 2205 * @param cls the order test closure. 2206 * @return 0 on success, 1 on failure. 2207 */ 2208 static int 2209 run_test_orders (struct TestOrders_Closure *cls) 2210 { 2211 struct TALER_MERCHANTDB_OrderFilter filter = { 2212 .paid = TALER_EXCHANGE_YNA_ALL, 2213 .refunded = TALER_EXCHANGE_YNA_ALL, 2214 .wired = TALER_EXCHANGE_YNA_ALL, 2215 .date = GNUNET_TIME_UNIT_ZERO_TS, 2216 .start_row = 0, 2217 .delta = 8 2218 }; 2219 uint64_t serial; 2220 2221 /* Insert the instance */ 2222 TEST_RET_ON_FAIL (test_insert_instance (&cls->instance, 2223 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 2224 /* Test inserting an order */ 2225 TEST_RET_ON_FAIL (test_insert_order (&cls->instance, 2226 &cls->orders[0], 2227 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 2228 /* Test double insert fails */ 2229 TEST_RET_ON_FAIL (test_insert_order (&cls->instance, 2230 &cls->orders[0], 2231 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 2232 /* Test lookup order */ 2233 TEST_RET_ON_FAIL (test_lookup_order (&cls->instance, 2234 &cls->orders[0])); 2235 /* Make sure it fails correctly for nonexistent orders */ 2236 { 2237 struct TALER_MerchantPostDataHashP unused; 2238 2239 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 2240 plugin->lookup_order (plugin->cls, 2241 cls->instance.instance.id, 2242 cls->orders[1].id, 2243 NULL, 2244 &unused, 2245 NULL)) 2246 { 2247 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2248 "Lookup order failed\n"); 2249 return 1; 2250 } 2251 } 2252 /* Test lookups on multiple orders */ 2253 TEST_RET_ON_FAIL (test_insert_order (&cls->instance, 2254 &cls->orders[1], 2255 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 2256 serial = get_order_serial (&cls->instance, 2257 &cls->orders[0]); 2258 TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance, 2259 &filter, 2260 2, 2261 cls->orders)); 2262 /* Test inserting contract terms */ 2263 TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance, 2264 &cls->orders[0], 2265 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 2266 /* Test double insert fails */ 2267 TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance, 2268 &cls->orders[0], 2269 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 2270 /* Test order lock */ 2271 TEST_RET_ON_FAIL (test_insert_product (&cls->instance, 2272 &cls->product, 2273 0, 2274 NULL, 2275 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, 2276 false, 2277 false, 2278 -1)); 2279 if (1 != plugin->insert_order_lock (plugin->cls, 2280 cls->instance.instance.id, 2281 cls->orders[0].id, 2282 cls->product.id, 2283 5, 2284 0)) 2285 { 2286 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2287 "Insert order lock failed\n"); 2288 return 1; 2289 } 2290 /* Test lookup contract terms */ 2291 TEST_RET_ON_FAIL (test_lookup_contract_terms (&cls->instance, 2292 &cls->orders[0])); 2293 /* Test lookup fails for nonexistent contract terms */ 2294 { 2295 json_t *lookup_contract = NULL; 2296 uint64_t lookup_order_serial; 2297 2298 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 2299 plugin->lookup_contract_terms (plugin->cls, 2300 cls->instance.instance.id, 2301 cls->orders[1].id, 2302 &lookup_contract, 2303 &lookup_order_serial, 2304 NULL)) 2305 { 2306 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2307 "Lookup contract terms failed\n"); 2308 GNUNET_assert (NULL == lookup_contract); 2309 return 1; 2310 } 2311 } 2312 /* Test update contract terms */ 2313 GNUNET_assert (0 == 2314 json_object_set_new (cls->orders[0].contract, 2315 "some_new_field", 2316 json_string ("another value"))); 2317 TEST_RET_ON_FAIL (test_update_contract_terms (&cls->instance, 2318 &cls->orders[0], 2319 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 2320 TEST_RET_ON_FAIL (test_lookup_contract_terms (&cls->instance, 2321 &cls->orders[0])); 2322 /* Test lookup order status */ 2323 TEST_RET_ON_FAIL (test_lookup_order_status (&cls->instance, 2324 &cls->orders[0], 2325 false)); 2326 { 2327 struct TALER_PrivateContractHashP h_contract_terms; 2328 bool order_paid = false; 2329 2330 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 2331 plugin->lookup_order_status (plugin->cls, 2332 cls->instance.instance.id, 2333 cls->orders[1].id, 2334 &h_contract_terms, 2335 &order_paid)) 2336 { 2337 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2338 "Lookup order status failed\n"); 2339 return 1; 2340 } 2341 } 2342 /* Test lookup payment status */ 2343 TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id, 2344 cls->orders[0].id, 2345 NULL, 2346 false, 2347 false)); 2348 /* Test lookup order status fails for nonexistent order */ 2349 { 2350 struct TALER_PrivateContractHashP h_contract_terms; 2351 bool order_paid; 2352 2353 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 2354 plugin->lookup_order_status (plugin->cls, 2355 cls->instance.instance.id, 2356 cls->orders[1].id, 2357 &h_contract_terms, 2358 &order_paid)) 2359 { 2360 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2361 "Lookup order status failed\n"); 2362 return 1; 2363 } 2364 } 2365 /* Test marking contracts as paid */ 2366 TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance, 2367 &cls->orders[0], 2368 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 2369 TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id, 2370 cls->orders[0].id, 2371 NULL, 2372 true, 2373 false)); 2374 TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id, 2375 cls->orders[0].id, 2376 "test_orders_session", 2377 true, 2378 false)); 2379 TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id, 2380 cls->orders[0].id, 2381 "bad_session", 2382 false, 2383 false)); 2384 /* Test lookup order by fulfillment */ 2385 TEST_RET_ON_FAIL (test_lookup_order_by_fulfillment (&cls->instance, 2386 &cls->orders[0], 2387 "test_orders_session")); 2388 { 2389 char *order_id; 2390 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 2391 plugin->lookup_order_by_fulfillment (plugin->cls, 2392 cls->instance.instance.id, 2393 "fulfillment_url", 2394 "test_orders_session", 2395 false, 2396 &order_id)) 2397 { 2398 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2399 "Lookup order by fulfillment failed\n"); 2400 GNUNET_free (order_id); 2401 return 1; 2402 } 2403 } 2404 /* Test mark as paid fails for nonexistent order */ 2405 TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance, 2406 &cls->orders[1], 2407 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 2408 TEST_RET_ON_FAIL (test_lookup_order_status (&cls->instance, 2409 &cls->orders[0], 2410 true)); 2411 filter.paid = TALER_EXCHANGE_YNA_YES; 2412 TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance, 2413 &filter, 2414 1, 2415 cls->orders)); 2416 /* Test marking orders as wired */ 2417 TEST_RET_ON_FAIL (test_mark_order_wired (serial, 2418 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)) 2419 ; 2420 TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id, 2421 cls->orders[0].id, 2422 NULL, 2423 true, 2424 true)); 2425 TEST_RET_ON_FAIL (test_mark_order_wired (1007, 2426 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)) 2427 ; 2428 /* If an order has been claimed and we aren't past 2429 the pay deadline, we can't delete it. */ 2430 TEST_RET_ON_FAIL (test_delete_order (&cls->instance, 2431 &cls->orders[0], 2432 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 2433 /* Test we can't delete before the legal expiration */ 2434 TEST_RET_ON_FAIL (test_delete_contract_terms (&cls->instance, 2435 &cls->orders[0], 2436 GNUNET_TIME_UNIT_MONTHS, 2437 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 2438 /* Test deleting contract terms */ 2439 TEST_RET_ON_FAIL (test_delete_contract_terms (&cls->instance, 2440 &cls->orders[0], 2441 GNUNET_TIME_UNIT_ZERO, 2442 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 2443 /* Test we can't delete something that doesn't exist */ 2444 TEST_RET_ON_FAIL (test_delete_contract_terms (&cls->instance, 2445 &cls->orders[0], 2446 GNUNET_TIME_UNIT_ZERO, 2447 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 2448 /* Test delete order where we aren't past 2449 the deadline, but the order is unclaimed. */ 2450 TEST_RET_ON_FAIL (test_delete_order (&cls->instance, 2451 &cls->orders[1], 2452 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 2453 TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance, 2454 &filter, 2455 0, 2456 NULL)); 2457 /* Test we can't delete something that doesn't exist */ 2458 TEST_RET_ON_FAIL (test_delete_order (&cls->instance, 2459 &cls->orders[1], 2460 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 2461 2462 /* Test we can also delete a claimed order that's past the pay deadline. */ 2463 TEST_RET_ON_FAIL (test_insert_order (&cls->instance, 2464 &cls->orders[2], 2465 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 2466 TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance, 2467 &cls->orders[2], 2468 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 2469 TEST_RET_ON_FAIL (test_delete_order (&cls->instance, 2470 &cls->orders[2], 2471 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 2472 return 0; 2473 } 2474 2475 2476 /** 2477 * Does all tasks for testing orders. 2478 * 2479 * @return 0 when successful, 1 otherwise. 2480 */ 2481 static int 2482 test_orders (void) 2483 { 2484 struct TestOrders_Closure test_cls; 2485 int test_result; 2486 2487 pre_test_orders (&test_cls); 2488 test_result = run_test_orders (&test_cls); 2489 post_test_orders (&test_cls); 2490 return test_result; 2491 } 2492 2493 2494 /* ********** Deposits ********** */ 2495 2496 2497 /** 2498 * A container for exchange signing key data. 2499 */ 2500 struct ExchangeSignkeyData 2501 { 2502 /** 2503 * The master private key of the exchange. 2504 */ 2505 struct TALER_MasterPrivateKeyP master_priv; 2506 2507 /** 2508 * The master public key of the exchange. 2509 */ 2510 struct TALER_MasterPublicKeyP master_pub; 2511 2512 /** 2513 * A signature made with the master keys. 2514 */ 2515 struct TALER_MasterSignatureP master_sig; 2516 2517 /** 2518 * The private key of the exchange. 2519 */ 2520 struct TALER_ExchangePrivateKeyP exchange_priv; 2521 2522 /** 2523 * The public key of the exchange. 2524 */ 2525 struct TALER_ExchangePublicKeyP exchange_pub; 2526 2527 /** 2528 * When the signing key becomes valid. 2529 */ 2530 struct GNUNET_TIME_Timestamp start_date; 2531 2532 /** 2533 * When the signing key stops being used. 2534 */ 2535 struct GNUNET_TIME_Timestamp expire_date; 2536 2537 /** 2538 * When the signing key becomes invalid for proof. 2539 */ 2540 struct GNUNET_TIME_Timestamp end_date; 2541 }; 2542 2543 2544 /** 2545 * Creates an exchange signing key. 2546 * 2547 * @param signkey the signing key data to fill. 2548 */ 2549 static void 2550 make_exchange_signkey (struct ExchangeSignkeyData *signkey) 2551 { 2552 struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); 2553 2554 GNUNET_CRYPTO_eddsa_key_create (&signkey->exchange_priv.eddsa_priv); 2555 GNUNET_CRYPTO_eddsa_key_get_public (&signkey->exchange_priv.eddsa_priv, 2556 &signkey->exchange_pub.eddsa_pub); 2557 GNUNET_CRYPTO_eddsa_key_create (&signkey->master_priv.eddsa_priv); 2558 GNUNET_CRYPTO_eddsa_key_get_public (&signkey->master_priv.eddsa_priv, 2559 &signkey->master_pub.eddsa_pub); 2560 signkey->start_date = now; 2561 signkey->expire_date = now; 2562 signkey->end_date = now; 2563 TALER_exchange_offline_signkey_validity_sign ( 2564 &signkey->exchange_pub, 2565 signkey->start_date, 2566 signkey->expire_date, 2567 signkey->end_date, 2568 &signkey->master_priv, 2569 &signkey->master_sig); 2570 } 2571 2572 2573 /** 2574 * A container for deposit data. 2575 */ 2576 struct DepositData 2577 { 2578 /** 2579 * When the deposit was made. 2580 */ 2581 struct GNUNET_TIME_Timestamp timestamp; 2582 2583 /** 2584 * Hash of the associated order's contract terms. 2585 */ 2586 struct TALER_PrivateContractHashP h_contract_terms; 2587 2588 /** 2589 * Public key of the coin that has been deposited. 2590 */ 2591 struct TALER_CoinSpendPublicKeyP coin_pub; 2592 2593 /** 2594 * Signature of the coin that has been deposited. 2595 */ 2596 struct TALER_CoinSpendSignatureP coin_sig; 2597 2598 /** 2599 * URL of the exchange. 2600 */ 2601 const char *exchange_url; 2602 2603 /** 2604 * Value of the coin with fees applied. 2605 */ 2606 struct TALER_Amount amount_with_fee; 2607 2608 /** 2609 * Fee charged for deposit. 2610 */ 2611 struct TALER_Amount deposit_fee; 2612 2613 /** 2614 * Fee to be charged in case of a refund. 2615 */ 2616 struct TALER_Amount refund_fee; 2617 2618 /** 2619 * Fee charged after the money is wired. 2620 */ 2621 struct TALER_Amount wire_fee; 2622 2623 /** 2624 * Hash of the wire details. 2625 */ 2626 struct TALER_MerchantWireHashP h_wire; 2627 2628 /** 2629 * Signature the exchange made on this deposit. 2630 */ 2631 struct TALER_ExchangeSignatureP exchange_sig; 2632 2633 }; 2634 2635 2636 /** 2637 * Generates deposit data for an order. 2638 * 2639 * @param instance the instance to make the deposit to. 2640 * @param account the merchant account to use. 2641 * @param order the order this deposit is for. 2642 * @param signkey the signing key to use. 2643 * @param deposit the deposit data to fill. 2644 */ 2645 static void 2646 make_deposit (const struct InstanceData *instance, 2647 const struct TALER_MERCHANTDB_AccountDetails *account, 2648 const struct OrderData *order, 2649 const struct ExchangeSignkeyData *signkey, 2650 struct DepositData *deposit) 2651 { 2652 struct TALER_CoinSpendPrivateKeyP coin_priv; 2653 struct GNUNET_TIME_Timestamp now; 2654 struct TALER_Amount amount_without_fee; 2655 2656 now = GNUNET_TIME_timestamp_get (); 2657 deposit->timestamp = now; 2658 GNUNET_assert (GNUNET_OK == 2659 TALER_JSON_contract_hash (order->contract, 2660 &deposit->h_contract_terms)); 2661 GNUNET_CRYPTO_eddsa_key_create (&coin_priv.eddsa_priv); 2662 GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv, 2663 &deposit->coin_pub.eddsa_pub); 2664 deposit->exchange_url = "https://test-exchange/"; 2665 GNUNET_assert (GNUNET_OK == 2666 TALER_string_to_amount ("EUR:50.00", 2667 &deposit->amount_with_fee)); 2668 GNUNET_assert (GNUNET_OK == 2669 TALER_string_to_amount ("EUR:1.00", 2670 &deposit->deposit_fee)); 2671 GNUNET_assert (GNUNET_OK == 2672 TALER_string_to_amount ("EUR:1.50", 2673 &deposit->refund_fee)); 2674 GNUNET_assert (GNUNET_OK == 2675 TALER_string_to_amount ("EUR:2.00", 2676 &deposit->wire_fee)); 2677 GNUNET_assert (0 <= 2678 TALER_amount_subtract (&amount_without_fee, 2679 &deposit->amount_with_fee, 2680 &deposit->deposit_fee)); 2681 deposit->h_wire = account->h_wire; 2682 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, 2683 &deposit->exchange_sig, 2684 sizeof (deposit->exchange_sig)); 2685 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, 2686 &deposit->coin_sig, 2687 sizeof (deposit->coin_sig)); 2688 } 2689 2690 2691 /** 2692 * Tests inserting an exchange signing key into the database. 2693 * 2694 * @param signkey the signing key to insert. 2695 * @param expected_result the result we expect the database to return. 2696 * @return 0 on success, 1 otherwise. 2697 */ 2698 static int 2699 test_insert_exchange_signkey (const struct ExchangeSignkeyData *signkey, 2700 enum GNUNET_DB_QueryStatus expected_result) 2701 { 2702 TEST_COND_RET_ON_FAIL (expected_result == 2703 plugin->insert_exchange_signkey (plugin->cls, 2704 &signkey->master_pub, 2705 &signkey->exchange_pub 2706 , 2707 signkey->start_date, 2708 signkey->expire_date, 2709 signkey->end_date, 2710 &signkey->master_sig), 2711 "Insert exchange signkey failed\n"); 2712 return 0; 2713 } 2714 2715 2716 /** 2717 * Tests inserting a deposit into the database. 2718 * 2719 * @param instance the instance the deposit was made to. 2720 * @param signkey the signing key used. 2721 * @param deposit the deposit information to insert. 2722 * @param expected_result the result we expect the database to return. 2723 * @return 0 on success, 1 otherwise. 2724 */ 2725 static int 2726 test_insert_deposit (const struct InstanceData *instance, 2727 const struct ExchangeSignkeyData *signkey, 2728 const struct DepositData *deposit, 2729 enum GNUNET_DB_QueryStatus expected_result) 2730 { 2731 uint64_t row; 2732 struct TALER_Amount awf; 2733 2734 GNUNET_assert (0 <= 2735 TALER_amount_subtract (&awf, 2736 &deposit->amount_with_fee, 2737 &deposit->deposit_fee)); 2738 TEST_COND_RET_ON_FAIL ( 2739 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 2740 plugin->insert_deposit_confirmation (plugin->cls, 2741 instance->instance.id, 2742 deposit->timestamp, 2743 &deposit->h_contract_terms, 2744 deposit->exchange_url, 2745 deposit->timestamp, 2746 &awf, 2747 &deposit->wire_fee, 2748 &deposit->h_wire, 2749 &deposit->exchange_sig, 2750 &signkey->exchange_pub, 2751 &row), 2752 "Insert deposit confirmation failed\n"); 2753 TEST_COND_RET_ON_FAIL ( 2754 expected_result == 2755 plugin->insert_deposit (plugin->cls, 2756 0, 2757 row, 2758 &deposit->coin_pub, 2759 &deposit->coin_sig, 2760 &deposit->amount_with_fee, 2761 &deposit->deposit_fee, 2762 &deposit->refund_fee, 2763 GNUNET_TIME_absolute_get ()), 2764 "Insert deposit failed\n"); 2765 return 0; 2766 } 2767 2768 2769 /** 2770 * Closure for testing deposit lookup 2771 */ 2772 struct TestLookupDeposits_Closure 2773 { 2774 /** 2775 * Number of deposits to compare to 2776 */ 2777 unsigned int deposits_to_cmp_length; 2778 2779 /** 2780 * Pointer to array of deposit data 2781 */ 2782 const struct DepositData *deposits_to_cmp; 2783 2784 /** 2785 * Pointer to array of number of matches per deposit 2786 */ 2787 unsigned int *results_matching; 2788 2789 /** 2790 * Total number of results returned 2791 */ 2792 unsigned int results_length; 2793 }; 2794 2795 2796 /** 2797 * Called after 'test_lookup_deposits'. 2798 * 2799 * @param cls pointer to the test lookup closure. 2800 * @param coin_pub public key of the coin deposited. 2801 * @param amount_with_fee amount of the deposit with fees. 2802 * @param deposit_fee fee charged for the deposit. 2803 * @param refund_fee fee charged in case of a refund. 2804 */ 2805 static void 2806 lookup_deposits_cb (void *cls, 2807 const char *exchange_url, 2808 const struct TALER_CoinSpendPublicKeyP *coin_pub, 2809 const struct TALER_Amount *amount_with_fee, 2810 const struct TALER_Amount *deposit_fee, 2811 const struct TALER_Amount *refund_fee) 2812 { 2813 struct TestLookupDeposits_Closure *cmp = cls; 2814 if (NULL == cmp) 2815 return; 2816 cmp->results_length += 1; 2817 for (unsigned int i = 0; cmp->deposits_to_cmp_length > i; ++i) 2818 { 2819 if ((GNUNET_OK == 2820 TALER_amount_cmp_currency ( 2821 &cmp->deposits_to_cmp[i].amount_with_fee, 2822 amount_with_fee)) && 2823 (0 == 2824 TALER_amount_cmp (&cmp->deposits_to_cmp[i].amount_with_fee, 2825 amount_with_fee)) && 2826 (GNUNET_OK == 2827 TALER_amount_cmp_currency ( 2828 &cmp->deposits_to_cmp[i].deposit_fee, 2829 deposit_fee)) && 2830 (0 == 2831 TALER_amount_cmp (&cmp->deposits_to_cmp[i].deposit_fee, 2832 deposit_fee)) && 2833 (GNUNET_OK == 2834 TALER_amount_cmp_currency ( 2835 &cmp->deposits_to_cmp[i].refund_fee, 2836 refund_fee)) && 2837 (0 == 2838 TALER_amount_cmp (&cmp->deposits_to_cmp[i].refund_fee, 2839 refund_fee))) 2840 { 2841 cmp->results_matching[i] += 1; 2842 } 2843 2844 } 2845 } 2846 2847 2848 /** 2849 * Tests looking up deposits from the database. 2850 * 2851 * @param instance the instance to lookup deposits from. 2852 * @param h_contract_terms the contract terms that the deposits should have. 2853 * @param deposits_length length of @e deposits. 2854 * @param deposits the deposits we expect to be found. 2855 * @return 0 on success, 1 otherwise. 2856 */ 2857 static int 2858 test_lookup_deposits (const struct InstanceData *instance, 2859 const struct TALER_PrivateContractHashP *h_contract_terms, 2860 unsigned int deposits_length, 2861 const struct DepositData *deposits) 2862 { 2863 unsigned int results_matching[GNUNET_NZL (deposits_length)]; 2864 struct TestLookupDeposits_Closure cmp = { 2865 .deposits_to_cmp_length = deposits_length, 2866 .deposits_to_cmp = deposits, 2867 .results_matching = results_matching, 2868 .results_length = 0 2869 }; 2870 memset (results_matching, 2871 0, 2872 sizeof (unsigned int) * deposits_length); 2873 TEST_COND_RET_ON_FAIL (0 <= 2874 plugin->lookup_deposits (plugin->cls, 2875 instance->instance.id, 2876 h_contract_terms, 2877 &lookup_deposits_cb, 2878 &cmp), 2879 "Lookup deposits failed\n"); 2880 if (deposits_length != cmp.results_length) 2881 { 2882 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2883 "Lookup deposits failed: incorrect number of results returned (%d)\n", 2884 cmp.results_length); 2885 return 1; 2886 } 2887 for (unsigned int i = 0; deposits_length > i; ++i) 2888 { 2889 if (cmp.results_matching[i] != 1) 2890 { 2891 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2892 "Lookup deposits failed: mismatched data\n"); 2893 return 1; 2894 } 2895 } 2896 return 0; 2897 } 2898 2899 2900 /** 2901 * Called after 'test_lookup_deposits_contract_and_coin'. 2902 * 2903 * @param cls pointer to the test lookup closure. 2904 * @param exchange_url URL to the exchange 2905 * @param amount_with_fee amount of the deposit with fees. 2906 * @param deposit_fee fee charged for the deposit. 2907 * @param refund_fee fee charged in case of a refund. 2908 * @param wire_fee fee charged when the money is wired. 2909 * @param h_wire hash of the wire transfer details. 2910 * @param deposit_timestamp when the deposit was made. 2911 * @param refund_deadline deadline for refunding the deposit. 2912 * @param exchange_sig signature the exchange made on the deposit. 2913 * @param exchange_pub public key of the exchange. 2914 */ 2915 static void 2916 lookup_deposits_contract_coin_cb ( 2917 void *cls, 2918 const char *exchange_url, 2919 const struct TALER_Amount *amount_with_fee, 2920 const struct TALER_Amount *deposit_fee, 2921 const struct TALER_Amount *refund_fee, 2922 const struct TALER_Amount *wire_fee, 2923 const struct TALER_MerchantWireHashP *h_wire, 2924 struct GNUNET_TIME_Timestamp deposit_timestamp, 2925 struct GNUNET_TIME_Timestamp refund_deadline, 2926 const struct TALER_ExchangeSignatureP *exchange_sig, 2927 const struct TALER_ExchangePublicKeyP *exchange_pub) 2928 { 2929 struct TestLookupDeposits_Closure *cmp = cls; 2930 2931 if (NULL == cmp) 2932 return; 2933 cmp->results_length++; 2934 for (unsigned int i = 0; cmp->deposits_to_cmp_length > i; ++i) 2935 { 2936 if ((GNUNET_TIME_timestamp_cmp (cmp->deposits_to_cmp[i].timestamp, 2937 ==, 2938 deposit_timestamp)) && 2939 (0 == strcmp (cmp->deposits_to_cmp[i].exchange_url, 2940 exchange_url)) && 2941 (GNUNET_OK == TALER_amount_cmp_currency ( 2942 &cmp->deposits_to_cmp[i].amount_with_fee, 2943 amount_with_fee)) && 2944 (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].amount_with_fee, 2945 amount_with_fee)) && 2946 (GNUNET_OK == TALER_amount_cmp_currency ( 2947 &cmp->deposits_to_cmp[i].deposit_fee, 2948 deposit_fee)) && 2949 (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].deposit_fee, 2950 deposit_fee)) && 2951 (GNUNET_OK == TALER_amount_cmp_currency ( 2952 &cmp->deposits_to_cmp[i].refund_fee, 2953 refund_fee)) && 2954 (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].refund_fee, 2955 refund_fee)) && 2956 (GNUNET_OK == TALER_amount_cmp_currency ( 2957 &cmp->deposits_to_cmp[i].wire_fee, 2958 wire_fee)) && 2959 (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].wire_fee, 2960 wire_fee)) && 2961 (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].h_wire, 2962 h_wire)) && 2963 (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].exchange_sig, 2964 exchange_sig))) 2965 { 2966 cmp->results_matching[i]++; 2967 } 2968 } 2969 } 2970 2971 2972 /** 2973 * Tests lookup of deposits by contract and coin. 2974 * 2975 * @param instance the instance to lookup from. 2976 * @param h_contract the contract terms the deposits should have. 2977 * @param coin_pub the public key of the coin the deposits should have. 2978 * @param deposits_length length of @e deposits. 2979 * @param deposits the deposits the db is expected to find. 2980 * @return 0 on success, 1 otherwise. 2981 */ 2982 static int 2983 test_lookup_deposits_contract_and_coin ( 2984 const struct InstanceData *instance, 2985 const struct TALER_PrivateContractHashP *h_contract, 2986 const struct TALER_CoinSpendPublicKeyP *coin_pub, 2987 unsigned int deposits_length, 2988 const struct DepositData *deposits) 2989 { 2990 unsigned int results_matching[deposits_length]; 2991 struct TestLookupDeposits_Closure cmp = { 2992 .deposits_to_cmp_length = deposits_length, 2993 .deposits_to_cmp = deposits, 2994 .results_matching = results_matching, 2995 .results_length = 0 2996 }; 2997 memset (results_matching, 2998 0, 2999 sizeof (unsigned int) * deposits_length); 3000 TEST_COND_RET_ON_FAIL ( 3001 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 3002 plugin->lookup_deposits_by_contract_and_coin ( 3003 plugin->cls, 3004 instance->instance.id, 3005 h_contract, 3006 coin_pub, 3007 &lookup_deposits_contract_coin_cb, 3008 &cmp), 3009 "Lookup deposits by contract and coin failed\n"); 3010 if (deposits_length != cmp.results_length) 3011 { 3012 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3013 "Lookup deposits failed: incorrect number of results returned\n"); 3014 return 1; 3015 } 3016 for (unsigned int i = 0; deposits_length > i; ++i) 3017 { 3018 if (cmp.results_matching[i] != 1) 3019 { 3020 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3021 "Lookup deposits failed: mismatched data\n"); 3022 return 1; 3023 } 3024 } 3025 return 0; 3026 } 3027 3028 3029 /** 3030 * Called after 'test_lookup_deposits_by_order'. 3031 * 3032 * @param cls pointer to the test lookup closure. 3033 * @param deposit_serial row number of the deposit in the database. 3034 * @param exchange_url URL to the exchange 3035 * @param h_wire hash of the wire transfer details. 3036 * @param deposit_timestamp when was the deposit made 3037 * @param amount_with_fee amount of the deposit with fees. 3038 * @param deposit_fee fee charged for the deposit. 3039 * @param coin_pub public key of the coin deposited. 3040 */ 3041 static void 3042 lookup_deposits_order_cb (void *cls, 3043 uint64_t deposit_serial, 3044 const char *exchange_url, 3045 const struct TALER_MerchantWireHashP *h_wire, 3046 struct GNUNET_TIME_Timestamp deposit_timestamp, 3047 const struct TALER_Amount *amount_with_fee, 3048 const struct TALER_Amount *deposit_fee, 3049 const struct TALER_CoinSpendPublicKeyP *coin_pub) 3050 { 3051 struct TestLookupDeposits_Closure *cmp = cls; 3052 3053 if (NULL == cmp) 3054 return; 3055 cmp->results_length += 1; 3056 for (unsigned int i = 0; i < cmp->deposits_to_cmp_length; ++i) 3057 { 3058 if ((0 == strcmp (cmp->deposits_to_cmp[i].exchange_url, 3059 exchange_url)) && 3060 (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].h_wire, 3061 h_wire)) && 3062 (GNUNET_OK == TALER_amount_cmp_currency ( 3063 &cmp->deposits_to_cmp[i].amount_with_fee, 3064 amount_with_fee)) && 3065 (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].amount_with_fee, 3066 amount_with_fee)) && 3067 (GNUNET_OK == TALER_amount_cmp_currency ( 3068 &cmp->deposits_to_cmp[i].deposit_fee, 3069 deposit_fee)) && 3070 (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].deposit_fee, 3071 deposit_fee)) && 3072 (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].coin_pub, 3073 coin_pub))) 3074 cmp->results_matching[i] += 1; 3075 } 3076 } 3077 3078 3079 /** 3080 * Tests looking up deposits by associated order. 3081 * 3082 * @param order_serial row number of the order to lookup for. 3083 * @param deposits_length length of @e deposits_length. 3084 * @param deposits the deposits we expect to be found. 3085 * @return 0 on success, 1 otherwise. 3086 */ 3087 static int 3088 test_lookup_deposits_by_order (uint64_t order_serial, 3089 unsigned int deposits_length, 3090 const struct DepositData *deposits) 3091 { 3092 unsigned int results_matching[deposits_length]; 3093 struct TestLookupDeposits_Closure cmp = { 3094 .deposits_to_cmp_length = deposits_length, 3095 .deposits_to_cmp = deposits, 3096 .results_matching = results_matching, 3097 .results_length = 0 3098 }; 3099 memset (results_matching, 3100 0, 3101 sizeof (unsigned int) * deposits_length); 3102 if (deposits_length != 3103 plugin->lookup_deposits_by_order (plugin->cls, 3104 order_serial, 3105 &lookup_deposits_order_cb, 3106 &cmp)) 3107 { 3108 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3109 "Lookup deposits by order failed\n"); 3110 return 1; 3111 } 3112 if (deposits_length != cmp.results_length) 3113 { 3114 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3115 "Lookup deposits by order failed: incorrect number of results\n"); 3116 return 1; 3117 } 3118 for (unsigned int i = 0; i < deposits_length; ++i) 3119 { 3120 if (1 != cmp.results_matching[i]) 3121 { 3122 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3123 "Lookup deposits by order failed: mismatched data\n"); 3124 return 1; 3125 } 3126 } 3127 return 0; 3128 } 3129 3130 3131 /** 3132 * Container for information for looking up the row number of a deposit. 3133 */ 3134 struct LookupDepositSerial_Closure 3135 { 3136 /** 3137 * The deposit we're looking for. 3138 */ 3139 const struct DepositData *deposit; 3140 3141 /** 3142 * The serial found. 3143 */ 3144 uint64_t serial; 3145 }; 3146 3147 3148 /** 3149 * Called after 'get_deposit_serial'. 3150 * 3151 * @param cls pointer to the test lookup closure. 3152 * @param deposit_serial row number of the deposit in the database. 3153 * @param exchange_url URL to the exchange 3154 * @param h_wire hash of the wire transfer details. 3155 * @param deposit_timestamp when was the deposit made. 3156 * @param amount_with_fee amount of the deposit with fees. 3157 * @param deposit_fee fee charged for the deposit. 3158 * @param coin_pub public key of the coin deposited. 3159 */ 3160 static void 3161 get_deposit_serial_cb (void *cls, 3162 uint64_t deposit_serial, 3163 const char *exchange_url, 3164 const struct TALER_MerchantWireHashP *h_wire, 3165 struct GNUNET_TIME_Timestamp deposit_timestamp, 3166 const struct TALER_Amount *amount_with_fee, 3167 const struct TALER_Amount *deposit_fee, 3168 const struct TALER_CoinSpendPublicKeyP *coin_pub) 3169 { 3170 struct LookupDepositSerial_Closure *lookup_cls = cls; 3171 3172 (void) deposit_timestamp; 3173 if (NULL == lookup_cls) 3174 return; 3175 if ((0 == strcmp (lookup_cls->deposit->exchange_url, 3176 exchange_url)) && 3177 (0 == GNUNET_memcmp (&lookup_cls->deposit->h_wire, 3178 h_wire)) && 3179 (GNUNET_OK == TALER_amount_cmp_currency ( 3180 &lookup_cls->deposit->amount_with_fee, 3181 amount_with_fee)) && 3182 (0 == TALER_amount_cmp (&lookup_cls->deposit->amount_with_fee, 3183 amount_with_fee)) && 3184 (GNUNET_OK == TALER_amount_cmp_currency ( 3185 &lookup_cls->deposit->deposit_fee, 3186 deposit_fee)) && 3187 (0 == TALER_amount_cmp (&lookup_cls->deposit->deposit_fee, 3188 deposit_fee)) && 3189 (0 == GNUNET_memcmp (&lookup_cls->deposit->coin_pub, 3190 coin_pub))) 3191 lookup_cls->serial = deposit_serial; 3192 } 3193 3194 3195 /** 3196 * Convenience function to retrieve the row number of a deposit in the database. 3197 * 3198 * @param instance the instance to get deposits from. 3199 * @param order the order associated with the deposit. 3200 * @param deposit the deposit to lookup the serial for. 3201 * @return the row number of the deposit. 3202 */ 3203 static uint64_t 3204 get_deposit_serial (const struct InstanceData *instance, 3205 const struct OrderData *order, 3206 const struct DepositData *deposit) 3207 { 3208 uint64_t order_serial = get_order_serial (instance, 3209 order); 3210 struct LookupDepositSerial_Closure lookup_cls = { 3211 .deposit = deposit, 3212 .serial = 0 3213 }; 3214 3215 GNUNET_assert (0 < 3216 plugin->lookup_deposits_by_order (plugin->cls, 3217 order_serial, 3218 &get_deposit_serial_cb, 3219 &lookup_cls)); 3220 GNUNET_assert (0 != lookup_cls.serial); 3221 3222 return lookup_cls.serial; 3223 } 3224 3225 3226 /** 3227 * Closure for deposit tests. 3228 */ 3229 struct TestDeposits_Closure 3230 { 3231 /** 3232 * The instance settings 3233 */ 3234 struct InstanceData instance; 3235 3236 /** 3237 * The merchant account 3238 */ 3239 struct TALER_MERCHANTDB_AccountDetails account; 3240 3241 /** 3242 * The exchange signing key 3243 */ 3244 struct ExchangeSignkeyData signkey; 3245 3246 /** 3247 * The order data 3248 */ 3249 struct OrderData orders[2]; 3250 3251 /** 3252 * The array of deposits 3253 */ 3254 struct DepositData deposits[3]; 3255 }; 3256 3257 3258 /** 3259 * Initializes data for testing deposits. 3260 * 3261 * @param cls the test closure to initialize. 3262 */ 3263 static void 3264 pre_test_deposits (struct TestDeposits_Closure *cls) 3265 { 3266 /* Instance */ 3267 make_instance ("test_inst_deposits", 3268 &cls->instance); 3269 3270 /* Account */ 3271 make_account (&cls->account); 3272 cls->account.instance_id = cls->instance.instance.id; 3273 /* Signing key */ 3274 make_exchange_signkey (&cls->signkey); 3275 3276 /* Order */ 3277 make_order ("test_deposits_od_1", 3278 &cls->orders[0]); 3279 make_order ("test_deposits_od_2", 3280 &cls->orders[1]); 3281 3282 /* Deposit */ 3283 make_deposit (&cls->instance, 3284 &cls->account, 3285 &cls->orders[0], 3286 &cls->signkey, 3287 &cls->deposits[0]); 3288 make_deposit (&cls->instance, 3289 &cls->account, 3290 &cls->orders[0], 3291 &cls->signkey, 3292 &cls->deposits[1]); 3293 GNUNET_assert (GNUNET_OK == 3294 TALER_string_to_amount ("EUR:29.00", 3295 &cls->deposits[1].amount_with_fee)); 3296 make_deposit (&cls->instance, 3297 &cls->account, 3298 &cls->orders[1], 3299 &cls->signkey, 3300 &cls->deposits[2]); 3301 } 3302 3303 3304 /** 3305 * Cleans up memory after testing deposits. 3306 * 3307 * @param cls the closure containing memory to free. 3308 */ 3309 static void 3310 post_test_deposits (struct TestDeposits_Closure *cls) 3311 { 3312 free_instance_data (&cls->instance); 3313 json_decref (cls->orders[0].contract); 3314 json_decref (cls->orders[1].contract); 3315 } 3316 3317 3318 /** 3319 * Runs tests for deposits. 3320 * 3321 * @param cls the closure containing test data. 3322 * @return 0 on success, 1 otherwise. 3323 */ 3324 static int 3325 run_test_deposits (struct TestDeposits_Closure *cls) 3326 { 3327 /* Insert the instance */ 3328 TEST_RET_ON_FAIL (test_insert_instance (&cls->instance, 3329 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 3330 /* Insert an account */ 3331 TEST_RET_ON_FAIL (test_insert_account (&cls->instance, 3332 &cls->account, 3333 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 3334 /* Insert a signing key */ 3335 TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey, 3336 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 3337 TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey, 3338 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 3339 /* Insert an order */ 3340 TEST_RET_ON_FAIL (test_insert_order (&cls->instance, 3341 &cls->orders[0], 3342 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 3343 /* Insert contract terms */ 3344 TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance, 3345 &cls->orders[0], 3346 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 3347 /* Test inserting a deposit */ 3348 TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance, 3349 &cls->signkey, 3350 &cls->deposits[0], 3351 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 3352 /* Test double inserts are idempotent */ 3353 TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance, 3354 &cls->signkey, 3355 &cls->deposits[0], 3356 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 3357 /* Test lookup deposits */ 3358 TEST_RET_ON_FAIL (test_lookup_deposits (&cls->instance, 3359 &cls->deposits[0].h_contract_terms, 3360 1, 3361 cls->deposits)); 3362 TEST_RET_ON_FAIL (test_lookup_deposits (&cls->instance, 3363 &cls->deposits[2].h_contract_terms, 3364 0, 3365 NULL)); 3366 /* Test lookup deposits by contract and coins */ 3367 TEST_RET_ON_FAIL (test_lookup_deposits_contract_and_coin ( 3368 &cls->instance, 3369 &cls->deposits[0].h_contract_terms, 3370 &cls->deposits[0].coin_pub, 3371 1, 3372 cls->deposits)); 3373 /* Test multiple deposits */ 3374 TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance, 3375 &cls->signkey, 3376 &cls->deposits[1], 3377 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 3378 TEST_RET_ON_FAIL (test_insert_order (&cls->instance, 3379 &cls->orders[1], 3380 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 3381 TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance, 3382 &cls->orders[1], 3383 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 3384 TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance, 3385 &cls->signkey, 3386 &cls->deposits[2], 3387 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 3388 TEST_RET_ON_FAIL (test_lookup_deposits (&cls->instance, 3389 &cls->deposits[0].h_contract_terms, 3390 2, 3391 cls->deposits)); 3392 TEST_RET_ON_FAIL (test_lookup_deposits (&cls->instance, 3393 &cls->deposits[2].h_contract_terms, 3394 1, 3395 &cls->deposits[2])); 3396 /* Test lookup deposits by order */ 3397 { 3398 uint64_t order_serial = get_order_serial (&cls->instance, 3399 &cls->orders[0]); 3400 TEST_RET_ON_FAIL (test_lookup_deposits_by_order (order_serial, 3401 2, 3402 cls->deposits)); 3403 order_serial = get_order_serial (&cls->instance, 3404 &cls->orders[1]); 3405 TEST_RET_ON_FAIL (test_lookup_deposits_by_order (order_serial, 3406 1, 3407 &cls->deposits[2])); 3408 } 3409 return 0; 3410 } 3411 3412 3413 /** 3414 * Handles functionality for testing deposits. 3415 * 3416 * @return 0 on success, 1 otherwise. 3417 */ 3418 static int 3419 test_deposits (void) 3420 { 3421 struct TestDeposits_Closure test_cls; 3422 int test_result; 3423 3424 pre_test_deposits (&test_cls); 3425 test_result = run_test_deposits (&test_cls); 3426 post_test_deposits (&test_cls); 3427 return test_result; 3428 } 3429 3430 3431 /* *********** Transfers ********** */ 3432 3433 3434 /** 3435 * Container for wire fee data for an exchange. 3436 */ 3437 struct WireFeeData 3438 { 3439 /** 3440 * The method used. 3441 */ 3442 const char *wire_method; 3443 3444 /** 3445 * Hash of the wire method. 3446 */ 3447 struct GNUNET_HashCode h_wire_method; 3448 3449 /** 3450 * Wire fees charged. 3451 */ 3452 struct TALER_WireFeeSet fees; 3453 3454 /** 3455 * Start date of the wire fee. 3456 */ 3457 struct GNUNET_TIME_Timestamp wire_fee_start; 3458 3459 /** 3460 * End date of the wire fee. 3461 */ 3462 struct GNUNET_TIME_Timestamp wire_fee_end; 3463 3464 /** 3465 * Signature on the wire fee. 3466 */ 3467 struct TALER_MasterSignatureP fee_sig; 3468 }; 3469 3470 3471 /** 3472 * Creates data for an exchange wire fee. 3473 * 3474 * @param signkey the exchange signing key data. 3475 * @param wire_fee where to store the wire fee data. 3476 */ 3477 static void 3478 make_wire_fee (const struct ExchangeSignkeyData *signkey, 3479 struct WireFeeData *wire_fee) 3480 { 3481 wire_fee->wire_method = "wire-method"; 3482 GNUNET_CRYPTO_hash (wire_fee->wire_method, 3483 strlen (wire_fee->wire_method) + 1, 3484 &wire_fee->h_wire_method); 3485 GNUNET_assert (GNUNET_OK == 3486 TALER_string_to_amount ("EUR:0.49", 3487 &wire_fee->fees.wire)); 3488 GNUNET_assert (GNUNET_OK == 3489 TALER_string_to_amount ("EUR:0.49", 3490 &wire_fee->fees.closing)); 3491 wire_fee->wire_fee_start = GNUNET_TIME_timestamp_get (); 3492 wire_fee->wire_fee_end = GNUNET_TIME_relative_to_timestamp ( 3493 GNUNET_TIME_UNIT_MONTHS); 3494 TALER_exchange_offline_wire_fee_sign ( 3495 wire_fee->wire_method, 3496 wire_fee->wire_fee_start, 3497 wire_fee->wire_fee_end, 3498 &wire_fee->fees, 3499 &signkey->master_priv, 3500 &wire_fee->fee_sig); 3501 } 3502 3503 3504 /** 3505 * Container for wire transfer data. 3506 */ 3507 struct TransferData 3508 { 3509 /** 3510 * Id of the transfer. 3511 */ 3512 struct TALER_WireTransferIdentifierRawP wtid; 3513 3514 /** 3515 * The main data for the transfer. 3516 */ 3517 struct TALER_EXCHANGE_TransferData data; 3518 3519 /** 3520 * URL to the exchange the transfer was made through. 3521 */ 3522 const char *exchange_url; 3523 3524 /** 3525 * How much the fee for the deposit was. 3526 */ 3527 struct TALER_Amount deposit_fee; 3528 3529 /** 3530 * Whether the transfer has been confirmed. 3531 */ 3532 bool confirmed; 3533 3534 /** 3535 * Whether the transfer has been verified. 3536 */ 3537 bool verified; 3538 }; 3539 3540 3541 /** 3542 * Creates a transfer for use with testing. 3543 * 3544 * @param deposits_length length of @e deposits. 3545 * @param deposits list of deposits to combine into one transfer. 3546 * @param transfer where to write the transfer data. 3547 */ 3548 static void 3549 make_transfer (const struct ExchangeSignkeyData *signkey, 3550 unsigned int deposits_length, 3551 const struct DepositData deposits[static deposits_length], 3552 struct TransferData *transfer) 3553 { 3554 struct TALER_TrackTransferDetails *details = NULL; 3555 3556 GNUNET_CRYPTO_seed_weak_random (585); 3557 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, 3558 &transfer->wtid, 3559 sizeof (struct TALER_WireTransferIdentifierRawP)); 3560 transfer->exchange_url = deposits[0].exchange_url; 3561 transfer->verified = false; 3562 transfer->confirmed = false; 3563 transfer->data.details_length = 0; 3564 GNUNET_assert (GNUNET_OK == 3565 TALER_amount_set_zero (deposits[0].amount_with_fee.currency, 3566 &transfer->data.total_amount)); 3567 GNUNET_assert (GNUNET_OK == 3568 TALER_amount_set_zero (deposits[0].amount_with_fee.currency, 3569 &transfer->deposit_fee)); 3570 for (unsigned int i = 0; i < deposits_length; ++i) 3571 { 3572 GNUNET_array_grow (details, 3573 transfer->data.details_length, 3574 i + 1); 3575 details[i].h_contract_terms = deposits[i].h_contract_terms; 3576 details[i].coin_pub = deposits[i].coin_pub; 3577 details[i].coin_value = deposits[i].amount_with_fee; 3578 details[i].coin_fee = deposits[i].deposit_fee; 3579 GNUNET_assert (0 <= 3580 TALER_amount_add (&transfer->data.total_amount, 3581 &transfer->data.total_amount, 3582 &deposits[i].amount_with_fee)); 3583 GNUNET_assert (0 <= 3584 TALER_amount_add (&transfer->deposit_fee, 3585 &transfer->deposit_fee, 3586 &deposits[i].deposit_fee)); 3587 } 3588 transfer->data.exchange_pub = signkey->exchange_pub; 3589 transfer->data.execution_time = GNUNET_TIME_timestamp_get (); 3590 transfer->data.details = details; 3591 GNUNET_assert (GNUNET_OK == 3592 TALER_string_to_amount ("EUR:0.50", 3593 &transfer->data.wire_fee)); 3594 } 3595 3596 3597 /** 3598 * Closure for testing 'lookup_transfer_summary' 3599 */ 3600 struct TestLookupTransferSummary_Closure 3601 { 3602 /** 3603 * Id of the order the transfer was made for. 3604 */ 3605 const char *order_id; 3606 3607 /** 3608 * The value of the deposit made. 3609 */ 3610 const struct TALER_Amount *deposit_value; 3611 3612 /** 3613 * The fee on the deposit made. 3614 */ 3615 const struct TALER_Amount *deposit_fee; 3616 3617 /** 3618 * 0 if the comparison is true, 1 if false. 3619 */ 3620 int result; 3621 }; 3622 3623 3624 /** 3625 * Called after 'test_lookup_transfer_summary'. 3626 * 3627 * @param cls pointer to 'TestLookupTransferSummary_Closure'. 3628 * @param order_id id of the order the transfer was made for. 3629 * @param deposit_value the value of the deposit made. 3630 * @param deposit_fee the fee on the deposit made. 3631 */ 3632 static void 3633 lookup_transfer_summary_cb (void *cls, 3634 const char *order_id, 3635 const struct TALER_Amount *deposit_value, 3636 const struct TALER_Amount *deposit_fee) 3637 { 3638 struct TestLookupTransferSummary_Closure *cmp = cls; 3639 if (NULL == cmp) 3640 return; 3641 if ((0 == strcmp (cmp->order_id, 3642 order_id)) && 3643 (GNUNET_OK == TALER_amount_cmp_currency (cmp->deposit_value, 3644 deposit_value)) && 3645 (0 == TALER_amount_cmp (cmp->deposit_value, 3646 deposit_value)) && 3647 (GNUNET_OK == TALER_amount_cmp_currency (cmp->deposit_fee, 3648 deposit_fee)) && 3649 (0 == TALER_amount_cmp (cmp->deposit_fee, 3650 deposit_fee))) 3651 cmp->result = 0; 3652 else 3653 cmp->result = 1; 3654 } 3655 3656 3657 /** 3658 * Tests looking up a transfer's summary. 3659 * 3660 * @param exchange_url url to the exchange for the transfer. 3661 * @param wtid identifier of the transfer. 3662 * @param expected_order_id the id of the order associated with the transfer. 3663 * @param expected_deposit_value the amount of the deposit made for the transfer. 3664 * @param expected_deposit_fee the fee on the deposit made for the transfer. 3665 * @return 1 on success, 0 otherwise. 3666 */ 3667 static int 3668 test_lookup_transfer_summary ( 3669 const char *exchange_url, 3670 const struct TALER_WireTransferIdentifierRawP *wtid, 3671 const char *expected_order_id, 3672 const struct TALER_Amount *expected_deposit_value, 3673 const struct TALER_Amount *expected_deposit_fee) 3674 { 3675 struct TestLookupTransferSummary_Closure cmp = { 3676 .order_id = expected_order_id, 3677 .deposit_value = expected_deposit_value, 3678 .deposit_fee = expected_deposit_fee, 3679 .result = 0 3680 }; 3681 if (1 != plugin->lookup_transfer_summary (plugin->cls, 3682 exchange_url, 3683 wtid, 3684 &lookup_transfer_summary_cb, 3685 &cmp)) 3686 { 3687 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3688 "Lookup transfer summary failed\n"); 3689 return 1; 3690 } 3691 if (0 != cmp.result) 3692 { 3693 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3694 "Lookup transfer summary failed: mismatched data\n"); 3695 return 1; 3696 } 3697 return 0; 3698 } 3699 3700 3701 /** 3702 * Closure for testing 'lookup_transfer_details'. 3703 */ 3704 struct TestLookupTransferDetails_Closure 3705 { 3706 /** 3707 * Length of @e details_to_cmp. 3708 */ 3709 unsigned int details_to_cmp_length; 3710 3711 /** 3712 * The details we expect to find. 3713 */ 3714 const struct TALER_TrackTransferDetails *details_to_cmp; 3715 3716 /** 3717 * Number of results matching each detail in @e details_to_cmp. 3718 */ 3719 unsigned int *results_matching; 3720 3721 /** 3722 * Total number of results found. 3723 */ 3724 unsigned int results_length; 3725 }; 3726 3727 3728 /** 3729 * Called after 'test_lookup_transfer_details'. 3730 * 3731 * @param cls pointer to 'TestLookupTransferDetails_Closure'. 3732 * @param current_offset offset within transfer details. 3733 * @param details the details that were found. 3734 */ 3735 static void 3736 lookup_transfer_details_cb (void *cls, 3737 unsigned int current_offset, 3738 const struct TALER_TrackTransferDetails *details) 3739 { 3740 struct TestLookupTransferDetails_Closure *cmp = cls; 3741 if (NULL == cmp) 3742 return; 3743 for (unsigned int i = 0; cmp->details_to_cmp_length > i; ++i) 3744 { 3745 if ((0 == GNUNET_memcmp (&cmp->details_to_cmp[i].h_contract_terms, 3746 &details->h_contract_terms)) && 3747 (0 == GNUNET_memcmp (&cmp->details_to_cmp[i].coin_pub, 3748 &details->coin_pub)) && 3749 (GNUNET_OK == TALER_amount_cmp_currency ( 3750 &cmp->details_to_cmp[i].coin_value, 3751 &details->coin_value)) && 3752 (0 == TALER_amount_cmp (&cmp->details_to_cmp[i].coin_value, 3753 &details->coin_value)) && 3754 (GNUNET_OK == TALER_amount_cmp_currency ( 3755 &cmp->details_to_cmp[i].coin_fee, 3756 &details->coin_fee)) && 3757 (0 == TALER_amount_cmp (&cmp->details_to_cmp[i].coin_fee, 3758 &details->coin_fee))) 3759 { 3760 cmp->results_matching[i] += 1; 3761 } 3762 } 3763 cmp->results_length += 1; 3764 } 3765 3766 3767 /** 3768 * Tests looking up details for a wire transfer. 3769 * 3770 * @param exchange_url url to the exchange. 3771 * @param wtid id of the transfer. 3772 * @param details_length the length of @e details. 3773 * @param details the details we expect to be returned. 3774 * @return 1 on success, 0 otherwise. 3775 */ 3776 static int 3777 test_lookup_transfer_details ( 3778 const char *exchange_url, 3779 const struct TALER_WireTransferIdentifierRawP *wtid, 3780 unsigned int details_length, 3781 const struct TALER_TrackTransferDetails *details) 3782 { 3783 unsigned int results_matching[details_length]; 3784 struct TestLookupTransferDetails_Closure cmp = { 3785 .details_to_cmp_length = details_length, 3786 .details_to_cmp = details, 3787 .results_matching = results_matching, 3788 .results_length = 0 3789 }; 3790 memset (results_matching, 3791 0, 3792 sizeof (unsigned int) * details_length); 3793 if (1 != plugin->lookup_transfer_details (plugin->cls, 3794 exchange_url, 3795 wtid, 3796 &lookup_transfer_details_cb, 3797 &cmp)) 3798 { 3799 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3800 "Lookup transfer details failed\n"); 3801 return 1; 3802 } 3803 if (details_length != cmp.results_length) 3804 { 3805 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3806 "Lookup transfer details failed: incorrect number of results (%d)\n", 3807 cmp.results_length); 3808 return 1; 3809 } 3810 for (unsigned int i = 0; details_length > i; ++i) 3811 { 3812 if (1 != cmp.results_matching[i]) 3813 { 3814 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3815 "Lookup transfer details failed: mismatched data\n"); 3816 return 1; 3817 } 3818 } 3819 return 0; 3820 } 3821 3822 3823 /** 3824 * Closure for 'lookup_transfer_details_by_order'. 3825 */ 3826 struct TestLookupTransferDetailsByOrder_Closure 3827 { 3828 /** 3829 * Length of @e transfers_to_cmp. 3830 */ 3831 unsigned int transfers_to_cmp_length; 3832 3833 /** 3834 * List of transfers that we expect to find. 3835 */ 3836 const struct TransferData *transfers_to_cmp; 3837 3838 /** 3839 * How many results match the corresponding element of @e transfers_to_cmp. 3840 */ 3841 unsigned int *results_matching; 3842 3843 /** 3844 * Total number of results found. 3845 */ 3846 unsigned int results_length; 3847 }; 3848 3849 3850 /** 3851 * Called after 'test_lookup_transfer_details_by_order'. 3852 * 3853 * @param cls pointer to 'TestLookupTransferDetailsByOrder_Closure'. 3854 * @param wtid identifier of the transfer found. 3855 * @param exchange_url exchange url of the transfer found. 3856 * @param execution_time when the transfer found occurred. 3857 * @param deposit_value amount of the deposit for the transfer found. 3858 * @param deposit_fee amount of the fee for the deposit of the transfer. 3859 * @param transfer_confirmed did the merchant confirm that a wire transfer with 3860 * @a wtid over the total amount happened? 3861 */ 3862 static void 3863 lookup_transfer_details_order_cb ( 3864 void *cls, 3865 const struct TALER_WireTransferIdentifierRawP *wtid, 3866 const char *exchange_url, 3867 struct GNUNET_TIME_Timestamp execution_time, 3868 const struct TALER_Amount *deposit_value, 3869 const struct TALER_Amount *deposit_fee, 3870 bool transfer_confirmed, 3871 uint64_t expected_transfer_serial_id) 3872 { 3873 struct TestLookupTransferDetailsByOrder_Closure *cmp = cls; 3874 3875 if (NULL == cmp) 3876 return; 3877 cmp->results_length += 1; 3878 for (unsigned int i = 0; i < cmp->transfers_to_cmp_length; ++i) 3879 { 3880 /* Right now lookup_transfer_details_by_order leaves execution_time 3881 uninitialized */ 3882 if ((0 == GNUNET_memcmp (&cmp->transfers_to_cmp[i].wtid, 3883 wtid)) && 3884 (0 == strcmp (cmp->transfers_to_cmp[i].exchange_url, 3885 exchange_url)) && 3886 (GNUNET_OK == 3887 TALER_amount_cmp_currency ( 3888 &cmp->transfers_to_cmp[i].data.total_amount, 3889 deposit_value)) && 3890 (0 == 3891 TALER_amount_cmp (&cmp->transfers_to_cmp[i].data.total_amount, 3892 deposit_value)) && 3893 (GNUNET_OK == 3894 TALER_amount_cmp_currency ( 3895 &cmp->transfers_to_cmp[i].deposit_fee, 3896 deposit_fee)) && 3897 (0 == 3898 TALER_amount_cmp (&cmp->transfers_to_cmp[i].deposit_fee, 3899 deposit_fee)) ) 3900 cmp->results_matching[i] += 1; 3901 } 3902 } 3903 3904 3905 /** 3906 * Tests looking up wire transfers associated with an order. 3907 * 3908 * @param order_serial the order to be queried. 3909 * @param transfers_length length of @e transfers. 3910 * @param transfers the transfers we expect to be found. 3911 * @return 0 on success, 1 otherwise. 3912 */ 3913 static int 3914 test_lookup_transfer_details_by_order ( 3915 uint64_t order_serial, 3916 unsigned int transfers_length, 3917 const struct TransferData *transfers) 3918 { 3919 unsigned int results_matching[transfers_length]; 3920 struct TestLookupTransferDetailsByOrder_Closure cmp = { 3921 .transfers_to_cmp_length = transfers_length, 3922 .transfers_to_cmp = transfers, 3923 .results_matching = results_matching, 3924 .results_length = 0 3925 }; 3926 memset (results_matching, 3927 0, 3928 sizeof (unsigned int) * transfers_length); 3929 if (transfers_length != 3930 plugin->lookup_transfer_details_by_order ( 3931 plugin->cls, 3932 order_serial, 3933 &lookup_transfer_details_order_cb, 3934 &cmp)) 3935 { 3936 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3937 "Lookup transfer details by order failed\n"); 3938 return 1; 3939 } 3940 if (transfers_length != cmp.results_length) 3941 { 3942 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3943 "Lookup transfer details by order failed: incorrect number of results\n"); 3944 return 1; 3945 } 3946 for (unsigned int i = 0; i < transfers_length; ++i) 3947 { 3948 if (1 != cmp.results_matching[i]) 3949 { 3950 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3951 "Lookup transfer details by order failed: mismatched data\n"); 3952 return 1; 3953 } 3954 } 3955 return 0; 3956 } 3957 3958 3959 /** 3960 * Tests inserting wire fee data for an exchange. 3961 * 3962 * @param signkey the signing key for the exchange. 3963 * @param wire_fee the wire fee data. 3964 * @param expected_result what the database should return. 3965 * @return 0 on success, 1 otherwise. 3966 */ 3967 static int 3968 test_insert_wire_fee (const struct ExchangeSignkeyData *signkey, 3969 const struct WireFeeData *wire_fee, 3970 enum GNUNET_DB_QueryStatus expected_result) 3971 { 3972 TEST_COND_RET_ON_FAIL (expected_result == 3973 plugin->store_wire_fee_by_exchange ( 3974 plugin->cls, 3975 &signkey->master_pub, 3976 &wire_fee->h_wire_method, 3977 &wire_fee->fees, 3978 wire_fee->wire_fee_start, 3979 wire_fee->wire_fee_end, 3980 &wire_fee->fee_sig), 3981 "Store wire fee by exchange failed\n"); 3982 return 0; 3983 } 3984 3985 3986 /** 3987 * Tests looking up wire fee data for an exchange. 3988 * 3989 * @param signkey the signing key to use for lookup. 3990 * @param wire_fee_data the wire fee data we expect to find. 3991 * @return 0 on success, 1 otherwise. 3992 */ 3993 static int 3994 test_lookup_wire_fee (const struct ExchangeSignkeyData *signkey, 3995 const struct WireFeeData *wire_fee_data) 3996 { 3997 struct TALER_WireFeeSet fees; 3998 struct GNUNET_TIME_Timestamp start_date; 3999 struct GNUNET_TIME_Timestamp end_date; 4000 struct TALER_MasterSignatureP master_sig; 4001 if (1 != plugin->lookup_wire_fee (plugin->cls, 4002 &signkey->master_pub, 4003 wire_fee_data->wire_method, 4004 GNUNET_TIME_timestamp_get (), 4005 &fees, 4006 &start_date, 4007 &end_date, 4008 &master_sig)) 4009 { 4010 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4011 "Lookup wire fee failed\n"); 4012 return 1; 4013 } 4014 if ((0 != 4015 TALER_wire_fee_set_cmp (&wire_fee_data->fees, 4016 &fees)) || 4017 (GNUNET_TIME_timestamp_cmp (wire_fee_data->wire_fee_start, 4018 !=, 4019 start_date)) || 4020 (GNUNET_TIME_timestamp_cmp (wire_fee_data->wire_fee_end, 4021 !=, 4022 end_date)) || 4023 (0 != GNUNET_memcmp (&wire_fee_data->fee_sig, 4024 &master_sig))) 4025 { 4026 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4027 "Lookup wire fee failed: mismatched data\n"); 4028 return 1; 4029 } 4030 return 0; 4031 } 4032 4033 4034 /** 4035 * Closure for 'lookup_transfers'. 4036 */ 4037 struct TestLookupTransfers_Closure 4038 { 4039 /** 4040 * Length of @e transfers_to_cmp. 4041 */ 4042 unsigned int transfers_to_cmp_length; 4043 4044 /** 4045 * The transfers we expect to find. 4046 */ 4047 const struct TransferData *transfers_to_cmp; 4048 4049 /** 4050 * Number of results matching each transfer. 4051 */ 4052 unsigned int *results_matching; 4053 4054 /** 4055 * Total number of results found. 4056 */ 4057 unsigned int results_length; 4058 }; 4059 4060 4061 /** 4062 * Function called after 'test_lookup_transfers'. 4063 * 4064 * @param cls pointer to 'TestLookupTransfers_Closure'. 4065 * @param credit_amount how much was wired to the merchant (minus fees) 4066 * @param wtid wire transfer identifier 4067 * @param payto_uri target account that received the wire transfer 4068 * @param exchange_url base URL of the exchange that made the wire transfer 4069 * @param transfer_serial_id serial number identifying the transfer in the backend 4070 * @param expected_transfer_serial_id serial number identifying the expected transfer in the backend, 0 if not @a expected 4071 * @param execution_time when did the exchange make the transfer, #GNUNET_TIME_UNIT_FOREVER_TS 4072 * if it did not yet happen 4073 * @param expected true if the merchant acknowledged the wire transfer reception 4074 */ 4075 static void 4076 lookup_transfers_cb (void *cls, 4077 const struct TALER_Amount *credit_amount, 4078 const struct TALER_WireTransferIdentifierRawP *wtid, 4079 struct TALER_FullPayto payto_uri, 4080 const char *exchange_url, 4081 uint64_t transfer_serial_id, 4082 uint64_t expected_transfer_serial_id, 4083 struct GNUNET_TIME_Absolute execution_time, 4084 bool expected) 4085 { 4086 struct TestLookupTransfers_Closure *cmp = cls; 4087 if (NULL == cmp) 4088 return; 4089 for (unsigned int i = 0; cmp->transfers_to_cmp_length > i; ++i) 4090 { 4091 if ( (GNUNET_OK == 4092 TALER_amount_cmp_currency ( 4093 &cmp->transfers_to_cmp[i].data.total_amount, 4094 credit_amount)) && 4095 (0 == TALER_amount_cmp (&cmp->transfers_to_cmp[i].data.total_amount, 4096 credit_amount)) ) 4097 { 4098 cmp->results_matching[i]++; 4099 } 4100 } 4101 cmp->results_length++; 4102 } 4103 4104 4105 /** 4106 * Tests looking up transfers from the database. 4107 * 4108 * @param instance the instance to lookup from. 4109 * @param account the account the transfer was made to. 4110 * @param before do not return transfers before this time. 4111 * @param after do not return transfers after this time. 4112 * @param limit maximum number of transfers to return. 4113 * @param offset row in the database to start with. 4114 * @param filter_verified how to filter verified transfers. 4115 * @param transfers_length length of @e transfers. 4116 * @param transfers the transfers we expect to find. 4117 * @return 0 on success, 1 otherwise. 4118 */ 4119 static int 4120 test_lookup_transfers (const struct InstanceData *instance, 4121 const struct TALER_MERCHANTDB_AccountDetails *account, 4122 struct GNUNET_TIME_Timestamp before, 4123 struct GNUNET_TIME_Timestamp after, 4124 int64_t limit, 4125 uint64_t offset, 4126 unsigned int transfers_length, 4127 const struct TransferData *transfers) 4128 { 4129 unsigned int results_matching[transfers_length]; 4130 struct TestLookupTransfers_Closure cmp = { 4131 .transfers_to_cmp_length = transfers_length, 4132 .transfers_to_cmp = transfers, 4133 .results_matching = results_matching, 4134 .results_length = 0 4135 }; 4136 memset (results_matching, 4137 0, 4138 sizeof (unsigned int) * transfers_length); 4139 if (1 != plugin->lookup_transfers (plugin->cls, 4140 instance->instance.id, 4141 account->payto_uri, 4142 before, 4143 after, 4144 limit, 4145 offset, 4146 TALER_EXCHANGE_YNA_ALL, 4147 &lookup_transfers_cb, 4148 &cmp)) 4149 { 4150 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4151 "Lookup transfers failed\n"); 4152 return 1; 4153 } 4154 if (transfers_length != cmp.results_length) 4155 { 4156 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4157 "Lookup transfers failed: incorrect number of results\n"); 4158 return 1; 4159 } 4160 for (unsigned int i = 0; transfers_length > i; ++i) 4161 { 4162 if (1 != cmp.results_matching[i]) 4163 { 4164 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4165 "Lookup transfers failed: mismatched data\n"); 4166 return 1; 4167 } 4168 } 4169 return 0; 4170 } 4171 4172 4173 /** 4174 * Tests inserting a transfer into the database. 4175 * 4176 * @param instance the instance to use. 4177 * @param account the account to transfer to. 4178 * @param transfer the transfer to insert. 4179 * @param expected_result the result we expect the db to return. 4180 * @return 0 on success, 1 otherwise. 4181 */ 4182 static int 4183 test_insert_transfer (const struct InstanceData *instance, 4184 const struct TALER_MERCHANTDB_AccountDetails *account, 4185 const struct TransferData *transfer, 4186 enum GNUNET_DB_QueryStatus expected_result) 4187 { 4188 bool no_instance; 4189 bool no_account; 4190 bool conflict; 4191 4192 TEST_COND_RET_ON_FAIL (expected_result == 4193 plugin->insert_transfer (plugin->cls, 4194 instance->instance.id, 4195 transfer->exchange_url, 4196 &transfer->wtid, 4197 &transfer->data.total_amount, 4198 account->payto_uri, 4199 transfer->confirmed, 4200 &no_instance, 4201 &no_account, 4202 &conflict), 4203 "Insert transfer failed\n"); 4204 return 0; 4205 } 4206 4207 4208 /** 4209 * Tests linking a deposit to a transfer. 4210 * 4211 * @param instance the instance that the deposit and transfer are for. 4212 * @param signkey the signing used on the deposit. 4213 * @param order the order the deposit and transfer were made for. 4214 * @param transfer the transfer. 4215 * @param expected_result the result the database should return. 4216 * @param expected_cleared clearance status the database should return. 4217 * @return 0 on success, 1 otherwise. 4218 */ 4219 static int 4220 test_insert_deposit_to_transfer (const struct InstanceData *instance, 4221 const struct ExchangeSignkeyData *signkey, 4222 const struct OrderData *order, 4223 const struct DepositData *deposit, 4224 const struct TransferData *transfer, 4225 enum GNUNET_DB_QueryStatus expected_result, 4226 bool expect_cleared) 4227 { 4228 const struct TALER_EXCHANGE_DepositData deposit_data = { 4229 .exchange_pub = signkey->exchange_pub, 4230 .exchange_sig = deposit->exchange_sig, 4231 .wtid = transfer->wtid, 4232 .execution_time = transfer->data.execution_time, 4233 .coin_contribution = deposit->amount_with_fee 4234 }; 4235 uint64_t deposit_serial = get_deposit_serial (instance, 4236 order, 4237 deposit); 4238 4239 TEST_COND_RET_ON_FAIL (expected_result == 4240 plugin->insert_deposit_to_transfer ( 4241 plugin->cls, 4242 deposit_serial, 4243 &deposit->h_wire, 4244 deposit->exchange_url, 4245 &deposit_data), 4246 "insert deposit to transfer failed\n"); 4247 return 0; 4248 } 4249 4250 4251 /** 4252 * Inserts details for a transfer into the database. 4253 * 4254 * @param instance the instance the transfer is in. 4255 * @param account the destination account for the transfer. 4256 * @param transfer the transfer we are adding details to. 4257 * @param expected_result the result expected from the db. 4258 * @return 0 on success, 1 otherwise. 4259 */ 4260 static int 4261 test_insert_transfer_details ( 4262 const struct InstanceData *instance, 4263 const struct TALER_MERCHANTDB_AccountDetails *account, 4264 const struct TransferData *transfer, 4265 enum GNUNET_DB_QueryStatus expected_result) 4266 { 4267 TEST_COND_RET_ON_FAIL (expected_result == 4268 plugin->insert_transfer_details ( 4269 plugin->cls, 4270 instance->instance.id, 4271 transfer->exchange_url, 4272 account->payto_uri, 4273 &transfer->wtid, 4274 &transfer->data), 4275 "Insert transfer details failed\n"); 4276 return 0; 4277 } 4278 4279 4280 /** 4281 * Container for data used when testing transfers. 4282 */ 4283 struct TestTransfers_Closure 4284 { 4285 /** 4286 * The instance. 4287 */ 4288 struct InstanceData instance; 4289 4290 /** 4291 * The account. 4292 */ 4293 struct TALER_MERCHANTDB_AccountDetails account; 4294 4295 /** 4296 * The exchange signing key. 4297 */ 4298 struct ExchangeSignkeyData signkey; 4299 4300 /** 4301 * The order data. 4302 */ 4303 struct OrderData order; 4304 4305 /** 4306 * The deposit data. 4307 */ 4308 struct DepositData deposit; 4309 4310 /** 4311 * Wire fee data. 4312 */ 4313 struct WireFeeData wire_fee[2]; 4314 4315 /** 4316 * The transfers. 4317 */ 4318 struct TransferData transfers[1]; 4319 }; 4320 4321 4322 /** 4323 * Prepares for testing transfers. 4324 * 4325 * @param cls the test data. 4326 */ 4327 static void 4328 pre_test_transfers (struct TestTransfers_Closure *cls) 4329 { 4330 /* Instance */ 4331 make_instance ("test_inst_transfers", 4332 &cls->instance); 4333 4334 /* Account */ 4335 make_account (&cls->account); 4336 cls->account.instance_id = cls->instance.instance.id; 4337 /* Order */ 4338 make_order ("test_transfers_od_1", 4339 &cls->order); 4340 4341 /* Signing key */ 4342 make_exchange_signkey (&cls->signkey); 4343 4344 /* Deposit */ 4345 make_deposit (&cls->instance, 4346 &cls->account, 4347 &cls->order, 4348 &cls->signkey, 4349 &cls->deposit); 4350 4351 /* Wire fee */ 4352 make_wire_fee (&cls->signkey, 4353 &cls->wire_fee[0]); 4354 make_wire_fee (&cls->signkey, 4355 &cls->wire_fee[1]); 4356 cls->wire_fee[1].wire_method = "wire-method-2"; 4357 GNUNET_CRYPTO_hash (cls->wire_fee[1].wire_method, 4358 strlen (cls->wire_fee[1].wire_method) + 1, 4359 &cls->wire_fee[1].h_wire_method); 4360 4361 /* Transfers */ 4362 make_transfer (&cls->signkey, 4363 1, 4364 &cls->deposit, 4365 &cls->transfers[0]); 4366 cls->transfers[0].confirmed = true; 4367 } 4368 4369 4370 /** 4371 * Cleans up after testing transfers. 4372 * 4373 * @param cls the test data. 4374 */ 4375 static void 4376 post_test_transfers (struct TestTransfers_Closure *cls) 4377 { 4378 GNUNET_array_grow (cls->transfers->data.details, 4379 cls->transfers->data.details_length, 4380 0); 4381 free_instance_data (&cls->instance); 4382 free_order_data (&cls->order); 4383 } 4384 4385 4386 /** 4387 * Runs the tests for transfers. 4388 * 4389 * @param cls the test data. 4390 * @return 0 on success, 1 otherwise. 4391 */ 4392 static int 4393 run_test_transfers (struct TestTransfers_Closure *cls) 4394 { 4395 uint64_t order_serial; 4396 struct TALER_WireFeeSet fees; 4397 4398 /* Test lookup wire fee fails when it isn't in the db */ 4399 TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == 4400 plugin->lookup_wire_fee (plugin->cls, 4401 &cls->signkey.master_pub, 4402 cls->wire_fee[0].wire_method, 4403 GNUNET_TIME_timestamp_get (), 4404 &fees, 4405 NULL, 4406 NULL, 4407 NULL), 4408 "Lookup wire fee failed\n"); 4409 /* Test store wire fee by exchange */ 4410 TEST_RET_ON_FAIL (test_insert_wire_fee (&cls->signkey, 4411 &cls->wire_fee[0], 4412 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 4413 /* Test double insertion fails */ 4414 TEST_RET_ON_FAIL (test_insert_wire_fee (&cls->signkey, 4415 &cls->wire_fee[0], 4416 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 4417 /* Test lookup wire fee by exchange */ 4418 TEST_RET_ON_FAIL (test_lookup_wire_fee (&cls->signkey, 4419 &cls->wire_fee[0])); 4420 /* Test different wire fees for different methods. */ 4421 TEST_RET_ON_FAIL (test_insert_wire_fee (&cls->signkey, 4422 &cls->wire_fee[1], 4423 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 4424 TEST_RET_ON_FAIL (test_lookup_wire_fee (&cls->signkey, 4425 &cls->wire_fee[1])); 4426 /* Insert the instance */ 4427 TEST_RET_ON_FAIL (test_insert_instance (&cls->instance, 4428 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 4429 /* Insert the account */ 4430 TEST_RET_ON_FAIL (test_insert_account (&cls->instance, 4431 &cls->account, 4432 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 4433 /* Insert a signing key */ 4434 TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey, 4435 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 4436 /* Insert an order */ 4437 TEST_RET_ON_FAIL (test_insert_order (&cls->instance, 4438 &cls->order, 4439 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 4440 /* Insert contract terms */ 4441 TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance, 4442 &cls->order, 4443 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 4444 order_serial = get_order_serial (&cls->instance, 4445 &cls->order); 4446 /* Insert the deposit */ 4447 TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance, 4448 &cls->signkey, 4449 &cls->deposit, 4450 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 4451 /* Insert the transfer */ 4452 TEST_RET_ON_FAIL (test_insert_transfer (&cls->instance, 4453 &cls->account, 4454 &cls->transfers[0], 4455 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 4456 TEST_RET_ON_FAIL (test_insert_transfer (&cls->instance, 4457 &cls->account, 4458 &cls->transfers[0], 4459 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)) 4460 ; 4461 TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance, 4462 &cls->signkey, 4463 &cls->order, 4464 &cls->deposit, 4465 &cls->transfers[0], 4466 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, 4467 false)); 4468 TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance, 4469 &cls->signkey, 4470 &cls->order, 4471 &cls->deposit, 4472 &cls->transfers[0], 4473 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, 4474 false)); 4475 TEST_RET_ON_FAIL (test_insert_transfer_details (&cls->instance, 4476 &cls->account, 4477 &cls->transfers[0], 4478 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 4479 TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id, 4480 cls->order.id, 4481 NULL, 4482 false, 4483 false)); 4484 TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance, 4485 &cls->signkey, 4486 &cls->order, 4487 &cls->deposit, 4488 &cls->transfers[0], 4489 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, 4490 true)); 4491 4492 TEST_RET_ON_FAIL (test_lookup_transfer_summary (cls->deposit.exchange_url, 4493 &cls->transfers[0].wtid, 4494 cls->order.id, 4495 &cls->deposit.amount_with_fee, 4496 &cls->deposit.deposit_fee)); 4497 TEST_RET_ON_FAIL (test_lookup_transfer_details (cls->deposit.exchange_url, 4498 &cls->transfers[0].wtid, 4499 1, 4500 &cls->transfers[0].data. 4501 details[0])); 4502 TEST_RET_ON_FAIL (test_lookup_transfer_details_by_order (order_serial, 4503 1, 4504 &cls->transfers[0])); 4505 TEST_RET_ON_FAIL (test_lookup_transfers (&cls->instance, 4506 &cls->account, 4507 GNUNET_TIME_UNIT_FOREVER_TS, 4508 GNUNET_TIME_UNIT_ZERO_TS, 4509 8, 4510 0, 4511 1, 4512 &cls->transfers[0])); 4513 return 0; 4514 } 4515 4516 4517 /** 4518 * Takes care of all work for testing transfers. 4519 * 4520 * @return 0 on success, 1 otherwise. 4521 */ 4522 static int 4523 test_transfers (void) 4524 { 4525 struct TestTransfers_Closure test_cls; 4526 int test_result; 4527 4528 pre_test_transfers (&test_cls); 4529 test_result = run_test_transfers (&test_cls); 4530 post_test_transfers (&test_cls); 4531 return test_result; 4532 } 4533 4534 4535 /** 4536 * Closure for testing lookup_refunds. 4537 */ 4538 struct TestLookupRefunds_Closure 4539 { 4540 /** 4541 * Length of @e coin_pub_to_cmp and @e refund_amount_to_cmp. 4542 */ 4543 unsigned int refunds_to_cmp_length; 4544 4545 /** 4546 * Public keys of the refunded coins. 4547 */ 4548 const struct TALER_CoinSpendPublicKeyP *coin_pub_to_cmp; 4549 4550 /** 4551 * Amount of each refund. 4552 */ 4553 const struct TALER_Amount *refund_amount_to_cmp; 4554 4555 /** 4556 * Number of results matching each refund provided. 4557 */ 4558 unsigned int *results_matching; 4559 4560 /** 4561 * Total number of results returned; 4562 */ 4563 unsigned int results_length; 4564 }; 4565 4566 4567 /** 4568 * Called after test_lookup_refunds. 4569 * @param cls pointer to a TestLookupRefunds_Closure. 4570 * @param coin_pub the public key of the coin for the refund found. 4571 * @param refund_amount the amount of the refund found. 4572 */ 4573 static void 4574 lookup_refunds_cb (void *cls, 4575 const struct TALER_CoinSpendPublicKeyP *coin_pub, 4576 const struct TALER_Amount *refund_amount) 4577 { 4578 struct TestLookupRefunds_Closure *cmp = cls; 4579 if (NULL == cmp) 4580 return; 4581 cmp->results_length += 1; 4582 for (unsigned int i = 0; cmp->refunds_to_cmp_length > i; ++i) 4583 { 4584 if ((0 == GNUNET_memcmp (&cmp->coin_pub_to_cmp[i], 4585 coin_pub)) && 4586 (GNUNET_OK == TALER_amount_cmp_currency (&cmp->refund_amount_to_cmp[i], 4587 refund_amount)) && 4588 (0 == TALER_amount_cmp (&cmp->refund_amount_to_cmp[i], 4589 refund_amount))) 4590 { 4591 cmp->results_matching[i] += 1; 4592 } 4593 } 4594 } 4595 4596 4597 /** 4598 * Tests looking up refunds from the database. 4599 * @param instance the instance to look up refunds for. 4600 * @param h_contract_terms hash of the contract terms the refunds are for. 4601 * @param refunds_length length of @e coin_pubs and @e refund_amounts. 4602 * @param coin_pubs the public keys of the coins that were refunded. 4603 * @param refund_amounts the amounts of the coins that were refunded. 4604 * 4605 * @return 0 on success, 1 otherwise. 4606 */ 4607 static int 4608 test_lookup_refunds (const struct InstanceData *instance, 4609 const struct TALER_PrivateContractHashP *h_contract_terms, 4610 unsigned int refunds_length, 4611 const struct TALER_CoinSpendPublicKeyP *coin_pubs, 4612 const struct TALER_Amount *refund_amounts) 4613 { 4614 unsigned int results_matching[refunds_length]; 4615 struct TestLookupRefunds_Closure cmp = { 4616 .refunds_to_cmp_length = refunds_length, 4617 .coin_pub_to_cmp = coin_pubs, 4618 .refund_amount_to_cmp = refund_amounts, 4619 .results_matching = results_matching, 4620 .results_length = 0 4621 }; 4622 memset (results_matching, 0, sizeof (unsigned int) * refunds_length); 4623 if (1 != plugin->lookup_refunds (plugin->cls, 4624 instance->instance.id, 4625 h_contract_terms, 4626 &lookup_refunds_cb, 4627 &cmp)) 4628 { 4629 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4630 "Lookup refunds failed\n"); 4631 return 1; 4632 } 4633 if (refunds_length != cmp.results_length) 4634 { 4635 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4636 "Lookup refunds failed: incorrect number of results returned\n") 4637 ; 4638 return 1; 4639 } 4640 for (unsigned int i = 0; refunds_length > i; ++i) 4641 { 4642 if (1 != cmp.results_matching[i]) 4643 { 4644 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4645 "Lookup refunds failed: mismatched data\n"); 4646 return 1; 4647 } 4648 } 4649 return 0; 4650 } 4651 4652 4653 /** 4654 * Container for refund data. 4655 */ 4656 struct RefundData 4657 { 4658 /** 4659 * When the refund occurred. 4660 */ 4661 struct GNUNET_TIME_Timestamp timestamp; 4662 4663 /** 4664 * Reason for the refund. 4665 */ 4666 const char *reason; 4667 4668 /** 4669 * Amount of the refund. 4670 */ 4671 struct TALER_Amount refund_amount; 4672 4673 /** 4674 * Public key of the coin that was refunded. 4675 */ 4676 const struct TALER_CoinSpendPublicKeyP *coin_pub; 4677 4678 /** 4679 * URL to the exchange that did the refund. 4680 */ 4681 const char *exchange_url; 4682 }; 4683 4684 4685 /** 4686 * Creates a refund for testing with. 4687 * @param deposit the deposit being refunded. 4688 * @param refund the data to set. 4689 */ 4690 static void 4691 make_refund (const struct DepositData *deposit, 4692 struct RefundData *refund) 4693 { 4694 refund->timestamp = GNUNET_TIME_timestamp_get (); 4695 refund->reason = "some reason"; 4696 refund->refund_amount = deposit->amount_with_fee; 4697 refund->coin_pub = &deposit->coin_pub; 4698 refund->exchange_url = deposit->exchange_url; 4699 } 4700 4701 4702 /** 4703 * Container for proof of a refund. 4704 */ 4705 struct RefundProofData 4706 { 4707 /** 4708 * Fee charged for the refund. 4709 */ 4710 struct TALER_Amount refund_fee; 4711 4712 /** 4713 * The exchange's signature on the refund. 4714 */ 4715 struct TALER_ExchangeSignatureP exchange_sig; 4716 }; 4717 4718 4719 /** 4720 * Closure for testing lookup_refunds_detailed. 4721 */ 4722 struct TestLookupRefundsDetailed_Closure 4723 { 4724 /** 4725 * Length of @e refunds_to_cmp. 4726 */ 4727 unsigned int refunds_to_cmp_length; 4728 4729 /** 4730 * The refunds we expect to find. 4731 */ 4732 const struct RefundData *refunds_to_cmp; 4733 4734 /** 4735 * Whether to compare the timestamps or not (if we don't have direct control 4736 * of the timestamps, there will be differences on the order of microseconds 4737 * that can be ignored). 4738 */ 4739 bool cmp_timestamps; 4740 4741 /** 4742 * The number of results matching each refund. 4743 */ 4744 unsigned int *results_matching; 4745 4746 /** 4747 * The total number of results from the db. 4748 */ 4749 unsigned int results_length; 4750 }; 4751 4752 4753 /** 4754 * Called after test_lookup_refunds_detailed. 4755 * @param cls pointer to a TestLookupRefundsDetailed_Closure. 4756 * @param refund_serial unique serial number of the refund 4757 * @param timestamp time of the refund (for grouping of refunds in the wallet UI) 4758 * @param coin_pub public coin from which the refund comes from 4759 * @param exchange_url URL of the exchange that issued @a coin_pub 4760 * @param rtransaction_id identificator of the refund 4761 * @param reason human-readable explanation of the refund 4762 * @param refund_amount refund amount which is being taken from @a coin_pub 4763 * @param pending true if this refund has not been processed by the wallet/exchange 4764 */ 4765 static void 4766 lookup_refunds_detailed_cb (void *cls, 4767 uint64_t refund_serial, 4768 struct GNUNET_TIME_Timestamp timestamp, 4769 const struct TALER_CoinSpendPublicKeyP *coin_pub, 4770 const char *exchange_url, 4771 uint64_t rtransaction_id, 4772 const char *reason, 4773 const struct TALER_Amount *refund_amount, 4774 bool pending) 4775 { 4776 struct TestLookupRefundsDetailed_Closure *cmp = cls; 4777 if (NULL == cmp) 4778 return; 4779 cmp->results_length += 1; 4780 for (unsigned int i = 0; cmp->refunds_to_cmp_length > i; ++i) 4781 { 4782 if (((GNUNET_TIME_timestamp_cmp (cmp->refunds_to_cmp[i].timestamp, 4783 ==, 4784 timestamp)) || 4785 ! cmp->cmp_timestamps) && 4786 (0 == GNUNET_memcmp (cmp->refunds_to_cmp[i].coin_pub, 4787 coin_pub)) && 4788 (0 == strcmp (cmp->refunds_to_cmp[i].exchange_url, 4789 exchange_url)) && 4790 (0 == strcmp (cmp->refunds_to_cmp[i].reason, 4791 reason)) && 4792 (GNUNET_OK == 4793 TALER_amount_cmp_currency ( 4794 &cmp->refunds_to_cmp[i].refund_amount, 4795 refund_amount)) && 4796 (0 == TALER_amount_cmp (&cmp->refunds_to_cmp[i].refund_amount, 4797 refund_amount))) 4798 { 4799 cmp->results_matching[i] += 1; 4800 } 4801 } 4802 } 4803 4804 4805 /** 4806 * Tests looking up refunds with details from the database. 4807 * @param instance the instance to lookup from. 4808 * @param h_contract_terms the contract terms the refunds were made for. 4809 * @param cmp_timestamps whether to compare timestamps or not. 4810 * @param refunds_length length of @e refunds. 4811 * @param refunds the refunds we expect to be returned. 4812 * 4813 * @return 0 on success, 1 otherwise. 4814 */ 4815 static int 4816 test_lookup_refunds_detailed ( 4817 const struct InstanceData *instance, 4818 const struct TALER_PrivateContractHashP *h_contract_terms, 4819 bool cmp_timestamps, 4820 unsigned int refunds_length, 4821 const struct RefundData *refunds) 4822 { 4823 unsigned int results_matching[refunds_length]; 4824 struct TestLookupRefundsDetailed_Closure cmp = { 4825 .refunds_to_cmp_length = refunds_length, 4826 .refunds_to_cmp = refunds, 4827 .cmp_timestamps = cmp_timestamps, 4828 .results_matching = results_matching, 4829 .results_length = 0 4830 }; 4831 memset (results_matching, 0, sizeof (unsigned int) * refunds_length); 4832 if (0 > plugin->lookup_refunds_detailed (plugin->cls, 4833 instance->instance.id, 4834 h_contract_terms, 4835 &lookup_refunds_detailed_cb, 4836 &cmp)) 4837 { 4838 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4839 "Lookup refunds detailed failed\n"); 4840 return 1; 4841 } 4842 if (refunds_length != cmp.results_length) 4843 { 4844 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4845 "Lookup refunds detailed failed: incorrect number of results\n") 4846 ; 4847 return 1; 4848 } 4849 for (unsigned int i = 0; refunds_length > i; ++i) 4850 { 4851 if (1 != cmp.results_matching[i]) 4852 { 4853 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4854 "Lookup refunds detailed failed: mismatched data\n"); 4855 return 1; 4856 } 4857 } 4858 return 0; 4859 } 4860 4861 4862 /** 4863 * Closure for get_refund_serial. 4864 */ 4865 struct LookupRefundSerial_Closure 4866 { 4867 /** 4868 * The refund we are looking up the id for. 4869 */ 4870 const struct RefundData *refund; 4871 4872 /** 4873 * The row number found. 4874 */ 4875 uint64_t serial; 4876 }; 4877 4878 4879 /** 4880 * Called after get_refund_serial. 4881 * @param cls pointer to a LookupRefundSerial_Closure. 4882 * @param refund_serial unique serial number of the refund 4883 * @param timestamp time of the refund (for grouping of refunds in the wallet UI) 4884 * @param coin_pub public coin from which the refund comes from 4885 * @param exchange_url URL of the exchange that issued @a coin_pub 4886 * @param rtransaction_id identificator of the refund 4887 * @param reason human-readable explanation of the refund 4888 * @param refund_amount refund amount which is being taken from @a coin_pub 4889 * @param pending true if this refund has not been processed by the wallet/exchange 4890 */ 4891 static void 4892 get_refund_serial_cb (void *cls, 4893 uint64_t refund_serial, 4894 struct GNUNET_TIME_Timestamp timestamp, 4895 const struct TALER_CoinSpendPublicKeyP *coin_pub, 4896 const char *exchange_url, 4897 uint64_t rtransaction_id, 4898 const char *reason, 4899 const struct TALER_Amount *refund_amount, 4900 bool pending) 4901 { 4902 struct LookupRefundSerial_Closure *lookup_cls = cls; 4903 if (NULL == lookup_cls) 4904 return; 4905 if ((GNUNET_TIME_timestamp_cmp (lookup_cls->refund->timestamp, 4906 ==, 4907 timestamp)) && 4908 (0 == GNUNET_memcmp (lookup_cls->refund->coin_pub, 4909 coin_pub)) && 4910 (0 == strcmp (lookup_cls->refund->exchange_url, 4911 exchange_url)) && 4912 (0 == strcmp (lookup_cls->refund->reason, 4913 reason)) && 4914 (GNUNET_OK == 4915 TALER_amount_cmp_currency ( 4916 &lookup_cls->refund->refund_amount, 4917 refund_amount)) && 4918 (0 == TALER_amount_cmp (&lookup_cls->refund->refund_amount, 4919 refund_amount))) 4920 lookup_cls->serial = refund_serial; 4921 } 4922 4923 4924 /** 4925 * Utility function for getting the database row number of a refund. 4926 * @param instance the instance associated with the refund. 4927 * @param h_contract_terms the contract terms the refund was made with. 4928 * @param refund the refund we are querying the row number of. 4929 * 4930 * @return the row number of the refund. 4931 */ 4932 static uint64_t 4933 get_refund_serial (const struct InstanceData *instance, 4934 const struct TALER_PrivateContractHashP *h_contract_terms, 4935 const struct RefundData *refund) 4936 { 4937 struct LookupRefundSerial_Closure lookup_cls = { 4938 .refund = refund, 4939 .serial = 0 4940 }; 4941 4942 GNUNET_assert (0 < plugin->lookup_refunds_detailed (plugin->cls, 4943 instance->instance.id, 4944 h_contract_terms, 4945 &get_refund_serial_cb, 4946 &lookup_cls)); 4947 GNUNET_assert (0 != lookup_cls.serial); 4948 4949 return lookup_cls.serial; 4950 } 4951 4952 4953 /** 4954 * Tests looking up proof of a refund. 4955 * @param refund_serial the row number of the refund. 4956 * @param expected_exchange_sig the exchange signature we are expecting. 4957 * @param expected_exchange_pub the exchange public key we are expecting. 4958 * 4959 * @return 0 on success, 1 otherwise. 4960 */ 4961 static int 4962 test_lookup_refund_proof (uint64_t refund_serial, 4963 const struct 4964 TALER_ExchangeSignatureP *expected_exchange_sig, 4965 const struct 4966 TALER_ExchangePublicKeyP *expected_exchange_pub) 4967 { 4968 struct TALER_ExchangeSignatureP exchange_sig; 4969 struct TALER_ExchangePublicKeyP exchange_pub; 4970 if (1 != plugin->lookup_refund_proof (plugin->cls, 4971 refund_serial, 4972 &exchange_sig, 4973 &exchange_pub)) 4974 { 4975 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4976 "Lookup refund proof failed\n"); 4977 return 1; 4978 } 4979 if ((0 != GNUNET_memcmp (expected_exchange_sig, 4980 &exchange_sig)) || 4981 (0 != GNUNET_memcmp (expected_exchange_pub, 4982 &exchange_pub))) 4983 { 4984 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4985 "Lookup refund proof failed: mismatched data\n"); 4986 return 1; 4987 } 4988 return 0; 4989 } 4990 4991 4992 /** 4993 * Closure for testing refund functionality. 4994 */ 4995 struct TestRefunds_Closure 4996 { 4997 /** 4998 * The instance. 4999 */ 5000 struct InstanceData instance; 5001 5002 /** 5003 * The merchant account. 5004 */ 5005 struct TALER_MERCHANTDB_AccountDetails account; 5006 5007 /** 5008 * The exchange signing key. 5009 */ 5010 struct ExchangeSignkeyData signkey; 5011 5012 /** 5013 * The order data. 5014 */ 5015 struct OrderData orders[2]; 5016 5017 /** 5018 * The deposit data. 5019 */ 5020 struct DepositData deposits[3]; 5021 5022 /** 5023 * The refund data. 5024 */ 5025 struct RefundData refunds[3]; 5026 5027 /** 5028 * The refund proof data. 5029 */ 5030 struct RefundProofData refund_proof; 5031 }; 5032 5033 5034 /** 5035 * Prepares for testing refunds. 5036 * @param cls the closure to initialize. 5037 */ 5038 static void 5039 pre_test_refunds (struct TestRefunds_Closure *cls) 5040 { 5041 /* Instance */ 5042 make_instance ("test_inst_refunds", 5043 &cls->instance); 5044 5045 /* Account */ 5046 make_account (&cls->account); 5047 cls->account.instance_id = cls->instance.instance.id; 5048 /* Signing key */ 5049 make_exchange_signkey (&cls->signkey); 5050 5051 /* Order */ 5052 make_order ("test_refunds_od_0", 5053 &cls->orders[0]); 5054 make_order ("test_refunds_od_1", 5055 &cls->orders[1]); 5056 5057 /* Deposit */ 5058 make_deposit (&cls->instance, 5059 &cls->account, 5060 &cls->orders[0], 5061 &cls->signkey, 5062 &cls->deposits[0]); 5063 make_deposit (&cls->instance, 5064 &cls->account, 5065 &cls->orders[0], 5066 &cls->signkey, 5067 &cls->deposits[1]); 5068 make_deposit (&cls->instance, 5069 &cls->account, 5070 &cls->orders[1], 5071 &cls->signkey, 5072 &cls->deposits[2]); 5073 5074 /* Refund */ 5075 make_refund (&cls->deposits[0], 5076 &cls->refunds[0]); 5077 make_refund (&cls->deposits[2], 5078 &cls->refunds[1]); 5079 make_refund (&cls->deposits[2], 5080 &cls->refunds[2]); 5081 GNUNET_assert (GNUNET_OK == 5082 TALER_string_to_amount ("EUR:10.00", 5083 &cls->refunds[1].refund_amount)); 5084 cls->refunds[1].reason = "refund 1"; 5085 GNUNET_assert (GNUNET_OK == 5086 TALER_string_to_amount ("EUR:10.00", 5087 &cls->refunds[2].refund_amount)); 5088 cls->refunds[2].reason = "refund 2"; 5089 5090 /* Refund proof */ 5091 GNUNET_assert (GNUNET_OK == 5092 TALER_string_to_amount ("EUR:0.02", 5093 &cls->refund_proof.refund_fee)); 5094 memset (&cls->refund_proof.exchange_sig, 5095 42, 5096 sizeof (cls->refund_proof.exchange_sig)); 5097 } 5098 5099 5100 /** 5101 * Cleans up after testing refunds. 5102 * @param cls the closure. 5103 */ 5104 static void 5105 post_test_refunds (struct TestRefunds_Closure *cls) 5106 { 5107 free_instance_data (&cls->instance); 5108 free_order_data (&cls->orders[0]); 5109 free_order_data (&cls->orders[1]); 5110 } 5111 5112 5113 /** 5114 * Runs the refund tests. 5115 * @param cls the closure. 5116 * 5117 * @return 0 on success, 1 otherwise. 5118 */ 5119 static int 5120 run_test_refunds (struct TestRefunds_Closure *cls) 5121 { 5122 struct TALER_Amount inc; 5123 uint64_t refund_serial; 5124 5125 /* Insert an instance */ 5126 TEST_RET_ON_FAIL (test_insert_instance (&cls->instance, 5127 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5128 /* Insert an account */ 5129 TEST_RET_ON_FAIL (test_insert_account (&cls->instance, 5130 &cls->account, 5131 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5132 /* Insert an order */ 5133 TEST_RET_ON_FAIL (test_insert_order (&cls->instance, 5134 &cls->orders[0], 5135 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5136 /* Insert contract terms */ 5137 TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance, 5138 &cls->orders[0], 5139 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5140 /* Insert a signing key */ 5141 TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey, 5142 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5143 /* Insert a deposit */ 5144 TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance, 5145 &cls->signkey, 5146 &cls->deposits[0], 5147 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5148 TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance, 5149 &cls->signkey, 5150 &cls->deposits[1], 5151 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5152 /* Mark as paid */ 5153 TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance, 5154 &cls->orders[0], 5155 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5156 /* Test refund coin */ 5157 TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 5158 plugin->refund_coin (plugin->cls, 5159 cls->instance.instance.id, 5160 &cls->deposits[0].h_contract_terms 5161 , 5162 cls->refunds[0].timestamp, 5163 cls->refunds[0].coin_pub, 5164 cls->refunds[0].reason), 5165 "Refund coin failed\n"); 5166 refund_serial = get_refund_serial (&cls->instance, 5167 &cls->deposits[0].h_contract_terms, 5168 &cls->refunds[0]); 5169 /* Test double refund fails */ 5170 TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == 5171 plugin->refund_coin (plugin->cls, 5172 cls->instance.instance.id, 5173 &cls->deposits[0].h_contract_terms 5174 , 5175 cls->refunds[0].timestamp, 5176 cls->refunds[0].coin_pub, 5177 cls->refunds[0].reason), 5178 "Refund coin failed\n"); 5179 /* Test lookup refunds */ 5180 TEST_RET_ON_FAIL (test_lookup_refunds (&cls->instance, 5181 &cls->deposits[0].h_contract_terms, 5182 1, 5183 cls->refunds[0].coin_pub, 5184 &cls->refunds[0].refund_amount)); 5185 /* Test lookup refunds detailed */ 5186 TEST_RET_ON_FAIL (test_lookup_refunds_detailed (&cls->instance, 5187 &cls->deposits[0]. 5188 h_contract_terms, 5189 true, 5190 1, 5191 &cls->refunds[0])); 5192 /* Test insert refund proof */ 5193 TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 5194 plugin->insert_refund_proof (plugin->cls, 5195 refund_serial, 5196 &cls->refund_proof. 5197 exchange_sig, 5198 &cls->signkey.exchange_pub 5199 ), 5200 "Insert refund proof failed\n"); 5201 TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == 5202 plugin->insert_refund_proof (plugin->cls, 5203 refund_serial, 5204 &cls->refund_proof. 5205 exchange_sig, 5206 &cls->signkey.exchange_pub 5207 ), 5208 "Insert refund proof failed\n"); 5209 /* Test that we can't give too much in refunds */ 5210 GNUNET_assert (GNUNET_OK == 5211 TALER_string_to_amount ("EUR:1000.00", 5212 &inc)); 5213 TEST_COND_RET_ON_FAIL (TALER_MERCHANTDB_RS_TOO_HIGH == 5214 plugin->increase_refund (plugin->cls, 5215 cls->instance.instance.id, 5216 cls->orders[0].id, 5217 &inc, 5218 NULL, 5219 NULL, 5220 "more"), 5221 "Increase refund failed\n"); 5222 /* Test increase refund */ 5223 GNUNET_assert (GNUNET_OK == 5224 TALER_string_to_amount ("EUR:1.00", 5225 &inc)); 5226 TEST_COND_RET_ON_FAIL (TALER_MERCHANTDB_RS_SUCCESS == 5227 plugin->increase_refund (plugin->cls, 5228 cls->instance.instance.id, 5229 cls->orders[0].id, 5230 &inc, 5231 NULL, 5232 NULL, 5233 "more"), 5234 "Increase refund failed\n"); 5235 /* Test lookup refund proof */ 5236 TEST_RET_ON_FAIL (test_lookup_refund_proof (1, 5237 &cls->refund_proof.exchange_sig, 5238 &cls->signkey.exchange_pub)); 5239 TEST_RET_ON_FAIL (test_insert_order (&cls->instance, 5240 &cls->orders[1], 5241 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5242 TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance, 5243 &cls->orders[1], 5244 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5245 TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance, 5246 &cls->signkey, 5247 &cls->deposits[2], 5248 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5249 TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance, 5250 &cls->orders[1], 5251 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5252 /* Test refunding a small amount of the coin, then increasing it */ 5253 GNUNET_assert (GNUNET_OK == 5254 TALER_string_to_amount ("EUR:10.00", 5255 &inc)); 5256 TEST_COND_RET_ON_FAIL (TALER_MERCHANTDB_RS_SUCCESS == 5257 plugin->increase_refund (plugin->cls, 5258 cls->instance.instance.id, 5259 cls->orders[1].id, 5260 &inc, 5261 NULL, 5262 NULL, 5263 cls->refunds[1].reason), 5264 "Increase refund failed\n"); 5265 GNUNET_assert (GNUNET_OK == 5266 TALER_string_to_amount ("EUR:20.00", 5267 &inc)); 5268 TEST_COND_RET_ON_FAIL (TALER_MERCHANTDB_RS_SUCCESS == 5269 plugin->increase_refund (plugin->cls, 5270 cls->instance.instance.id, 5271 cls->orders[1].id, 5272 &inc, 5273 NULL, 5274 NULL, 5275 cls->refunds[2].reason), 5276 "Increase refund failed\n"); 5277 TEST_RET_ON_FAIL (test_lookup_refunds_detailed (&cls->instance, 5278 &cls->deposits[2]. 5279 h_contract_terms, 5280 false, 5281 2, 5282 &cls->refunds[1])); 5283 return 0; 5284 } 5285 5286 5287 /** 5288 * All logic for testing refunds. 5289 * 5290 * @return 0 on success, 1 otherwise. 5291 */ 5292 static int 5293 test_refunds (void) 5294 { 5295 struct TestRefunds_Closure test_cls; 5296 int test_result; 5297 5298 pre_test_refunds (&test_cls); 5299 test_result = run_test_refunds (&test_cls); 5300 post_test_refunds (&test_cls); 5301 return test_result; 5302 } 5303 5304 5305 /** 5306 * Convenience function that reverses an array of orders. 5307 * @param orders_length length of @e orders. 5308 * @param orders the array to reverse. 5309 */ 5310 static void 5311 reverse_order_data_array (unsigned int orders_length, 5312 struct OrderData *orders) 5313 { 5314 struct OrderData tmp[orders_length]; 5315 for (unsigned int i = 0; i < orders_length; ++i) 5316 tmp[i] = orders[orders_length - 1 - i]; 5317 for (unsigned int i = 0; i < orders_length; ++i) 5318 orders[i] = tmp[i]; 5319 } 5320 5321 5322 /** 5323 * Closure for testing all the filters for looking up orders. 5324 */ 5325 struct TestLookupOrdersAllFilters_Closure 5326 { 5327 /** 5328 * The instance. 5329 */ 5330 struct InstanceData instance; 5331 5332 /** 5333 * The merchant account. 5334 */ 5335 struct TALER_MERCHANTDB_AccountDetails account; 5336 5337 /** 5338 * The exchange signing key. 5339 */ 5340 struct ExchangeSignkeyData signkey; 5341 5342 /** 5343 * The array of order ids. 5344 */ 5345 char *order_ids[64]; 5346 5347 /** 5348 * The array of orders. 5349 */ 5350 struct OrderData orders[64]; 5351 5352 /** 5353 * The array of deposits. 5354 */ 5355 struct DepositData deposits[64]; 5356 5357 /** 5358 * The array of refunds. 5359 */ 5360 struct RefundData refunds[64]; 5361 }; 5362 5363 5364 /** 5365 * Sets up for testing lookup order filters. 5366 * @param cls the closure. 5367 */ 5368 static void 5369 pre_test_lookup_orders_all_filters ( 5370 struct TestLookupOrdersAllFilters_Closure *cls) 5371 { 5372 make_instance ("test_inst_lookup_orders_all_filters", 5373 &cls->instance); 5374 make_account (&cls->account); 5375 cls->account.instance_id = cls->instance.instance.id; 5376 make_exchange_signkey (&cls->signkey); 5377 for (unsigned int i = 0; i < 64; ++i) 5378 { 5379 (void) GNUNET_asprintf (&cls->order_ids[i], 5380 "test_orders_filter_od_%u", 5381 i); 5382 make_order (cls->order_ids[i], 5383 &cls->orders[i]); 5384 GNUNET_assert (0 == 5385 json_object_set_new (cls->orders[i].contract, 5386 "order_id", 5387 json_string (cls->order_ids[i]))); 5388 make_deposit (&cls->instance, 5389 &cls->account, 5390 &cls->orders[i], 5391 &cls->signkey, 5392 &cls->deposits[i]); 5393 make_refund (&cls->deposits[i], 5394 &cls->refunds[i]); 5395 } 5396 } 5397 5398 5399 /** 5400 * Cleans up after testing lookup order filters. 5401 * @param cls the closure. 5402 */ 5403 static void 5404 post_test_lookup_orders_all_filters ( 5405 struct TestLookupOrdersAllFilters_Closure *cls) 5406 { 5407 free_instance_data (&cls->instance); 5408 for (unsigned int i = 0; i < 64; ++i) 5409 { 5410 free_order_data (&cls->orders[i]); 5411 GNUNET_free (cls->order_ids[i]); 5412 } 5413 } 5414 5415 5416 /** 5417 * Runs the tests for lookup order filters. 5418 * @param cls the closure. 5419 * 5420 * @return 0 on success, 1 otherwise. 5421 */ 5422 static int 5423 run_test_lookup_orders_all_filters ( 5424 struct TestLookupOrdersAllFilters_Closure *cls) 5425 { 5426 /* Order filter extravaganza */ 5427 struct 5428 { 5429 bool claimed; 5430 bool paid; 5431 bool refunded; 5432 bool wired; 5433 } order_status[64]; 5434 unsigned int *permutation; 5435 5436 /* Pseudorandomly generate variations for the filter to differentiate */ 5437 GNUNET_CRYPTO_seed_weak_random (1); 5438 permutation = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, 5439 64); 5440 for (unsigned int i = 0; i < 64; ++i) 5441 { 5442 unsigned int dest = permutation[i]; 5443 order_status[dest].claimed = (i & 1) ? true : false; 5444 order_status[dest].paid = (3 == (i & 3)) ? true : false; 5445 order_status[dest].refunded = (5 == (i & 5)) ? true : false; 5446 order_status[dest].wired = (9 == (i & 9)) ? true : false; 5447 } 5448 GNUNET_free (permutation); 5449 5450 5451 TEST_RET_ON_FAIL (test_insert_instance (&cls->instance, 5452 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5453 TEST_RET_ON_FAIL (test_insert_account (&cls->instance, 5454 &cls->account, 5455 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5456 TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey, 5457 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5458 for (unsigned int i = 0; i < 64; ++i) 5459 { 5460 uint64_t order_serial; 5461 5462 TEST_RET_ON_FAIL (test_insert_order (&cls->instance, 5463 &cls->orders[i], 5464 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5465 order_serial = get_order_serial (&cls->instance, 5466 &cls->orders[i]); 5467 5468 if (order_status[i].claimed) 5469 { 5470 TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance, 5471 &cls->orders[i], 5472 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5473 } 5474 else 5475 { 5476 continue; 5477 } 5478 5479 if (order_status[i].paid) 5480 TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance, 5481 &cls->orders[i], 5482 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5483 if (order_status[i].refunded) 5484 { 5485 TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance, 5486 &cls->signkey, 5487 &cls->deposits[i], 5488 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5489 TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 5490 plugin->refund_coin (plugin->cls, 5491 cls->instance.instance.id, 5492 &cls->deposits[i]. 5493 h_contract_terms, 5494 cls->refunds[i].timestamp, 5495 cls->refunds[i].coin_pub, 5496 cls->refunds[i].reason), 5497 "Refund coin failed\n"); 5498 } 5499 5500 if (order_status[i].wired) 5501 TEST_RET_ON_FAIL (test_mark_order_wired (order_serial, 5502 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5503 } 5504 5505 /* There are 3^3 = 27 possibilities here, not counting inc/dec and start row */ 5506 for (unsigned int i = 0; i < 27; ++i) 5507 { 5508 struct TALER_MERCHANTDB_OrderFilter filter = { 5509 .paid = (i % 3) + 1, 5510 .refunded = ((i / 3) % 3) + 1, 5511 .wired = ((i / 9) % 3) + 1, 5512 .date = GNUNET_TIME_UNIT_ZERO_TS, 5513 .start_row = 0, 5514 .delta = 64 5515 }; 5516 unsigned int orders_length = 0; 5517 struct OrderData orders[64]; 5518 5519 /* Now figure out which orders should make it through the filter */ 5520 for (unsigned int j = 0; j < 64; ++j) 5521 { 5522 if (((TALER_EXCHANGE_YNA_YES == filter.paid) && 5523 (true != order_status[j].paid)) || 5524 ((TALER_EXCHANGE_YNA_NO == filter.paid) && 5525 (false != order_status[j].paid)) || 5526 ((TALER_EXCHANGE_YNA_YES == filter.refunded) && 5527 (true != order_status[j].refunded)) || 5528 ((TALER_EXCHANGE_YNA_NO == filter.refunded) && 5529 (false != order_status[j].refunded)) || 5530 ((TALER_EXCHANGE_YNA_YES == filter.wired) && 5531 (true != order_status[j].wired)) || 5532 ((TALER_EXCHANGE_YNA_NO == filter.wired) && 5533 (false != order_status[j].wired))) 5534 continue; 5535 orders[orders_length] = cls->orders[j]; 5536 orders_length += 1; 5537 } 5538 5539 /* Test the lookup */ 5540 TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance, 5541 &filter, 5542 orders_length, 5543 orders)); 5544 5545 /* Now test decreasing */ 5546 filter.start_row = 256; 5547 filter.date = GNUNET_TIME_UNIT_FOREVER_TS; 5548 filter.delta = -64; 5549 5550 reverse_order_data_array (orders_length, 5551 orders); 5552 5553 TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance, 5554 &filter, 5555 orders_length, 5556 orders)); 5557 } 5558 5559 return 0; 5560 } 5561 5562 5563 /** 5564 * Handles all logic for testing lookup order filters. 5565 * 5566 * @return 0 on success, 1 otherwise. 5567 */ 5568 static int 5569 test_lookup_orders_all_filters (void) 5570 { 5571 struct TestLookupOrdersAllFilters_Closure test_cls; 5572 int test_result; 5573 5574 memset (&test_cls, 5575 0, 5576 sizeof (test_cls)); 5577 pre_test_lookup_orders_all_filters (&test_cls); 5578 test_result = run_test_lookup_orders_all_filters (&test_cls); 5579 post_test_lookup_orders_all_filters (&test_cls); 5580 return test_result; 5581 } 5582 5583 5584 static void 5585 kyc_status_ok ( 5586 void *cls, 5587 const struct TALER_MerchantWireHashP *h_wire, 5588 struct TALER_FullPayto payto_uri, 5589 const char *exchange_url, 5590 struct GNUNET_TIME_Timestamp last_check, 5591 bool kyc_ok, 5592 const struct TALER_AccountAccessTokenP *access_token, 5593 unsigned int last_http_status, 5594 enum TALER_ErrorCode last_ec, 5595 bool in_aml_review, 5596 const json_t *jlimits) 5597 { 5598 bool *fail = cls; 5599 5600 if (kyc_ok) 5601 *fail = false; 5602 } 5603 5604 5605 static void 5606 kyc_status_fail ( 5607 void *cls, 5608 const struct TALER_MerchantWireHashP *h_wire, 5609 struct TALER_FullPayto payto_uri, 5610 const char *exchange_url, 5611 struct GNUNET_TIME_Timestamp last_check, 5612 bool kyc_ok, 5613 const struct TALER_AccountAccessTokenP *access_token, 5614 unsigned int last_http_status, 5615 enum TALER_ErrorCode last_ec, 5616 bool in_aml_review, 5617 const json_t *jlimits) 5618 { 5619 bool *fail = cls; 5620 5621 if (! kyc_ok) 5622 *fail = false; 5623 } 5624 5625 5626 /** 5627 * Function that tests the KYC table. 5628 * 5629 * @return 0 on success, 1 otherwise. 5630 */ 5631 static int 5632 test_kyc (void) 5633 { 5634 struct InstanceData instance; 5635 struct TALER_MERCHANTDB_AccountDetails account; 5636 bool fail; 5637 struct GNUNET_TIME_Timestamp now; 5638 5639 make_instance ("test_kyc", 5640 &instance); 5641 make_account (&account); 5642 account.instance_id = instance.instance.id; 5643 TEST_RET_ON_FAIL (test_insert_instance (&instance, 5644 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5645 TEST_RET_ON_FAIL (test_insert_account (&instance, 5646 &account, 5647 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 5648 now = GNUNET_TIME_timestamp_get (); 5649 TEST_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 5650 plugin->account_kyc_set_status (plugin->cls, 5651 instance.instance.id, 5652 &account.h_wire, 5653 "https://exchange.net/", 5654 now, 5655 GNUNET_TIME_UNIT_FOREVER_ABS, 5656 GNUNET_TIME_UNIT_HOURS, 5657 MHD_HTTP_OK, 5658 TALER_EC_NONE, 5659 42, 5660 NULL, 5661 NULL, 5662 false, 5663 false)); 5664 TEST_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 5665 plugin->account_kyc_set_status (plugin->cls, 5666 instance.instance.id, 5667 &account.h_wire, 5668 "https://exchange2.com/", 5669 now, 5670 GNUNET_TIME_UNIT_FOREVER_ABS, 5671 GNUNET_TIME_UNIT_HOURS, 5672 MHD_HTTP_OK, 5673 TALER_EC_NONE, 5674 42, 5675 NULL, 5676 NULL, 5677 false, 5678 false)); 5679 TEST_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 5680 plugin->account_kyc_set_status (plugin->cls, 5681 instance.instance.id, 5682 &account.h_wire, 5683 "https://exchange.net/", 5684 now, 5685 GNUNET_TIME_UNIT_FOREVER_ABS, 5686 GNUNET_TIME_UNIT_HOURS, 5687 MHD_HTTP_OK, 5688 TALER_EC_NONE, 5689 42, 5690 NULL, 5691 NULL, 5692 false, 5693 true)); 5694 fail = true; 5695 TEST_RET_ON_FAIL (1 != 5696 plugin->account_kyc_get_status (plugin->cls, 5697 instance.instance.id, 5698 &account.h_wire, 5699 "https://exchange.net/", 5700 &kyc_status_ok, 5701 &fail)); 5702 TEST_RET_ON_FAIL (fail); 5703 fail = true; 5704 TEST_RET_ON_FAIL (1 != 5705 plugin->account_kyc_get_status (plugin->cls, 5706 instance.instance.id, 5707 NULL, 5708 "https://exchange2.com/", 5709 &kyc_status_fail, 5710 &fail)); 5711 TEST_RET_ON_FAIL (fail); 5712 fail = true; 5713 TEST_RET_ON_FAIL (2 != 5714 plugin->account_kyc_get_status (plugin->cls, 5715 instance.instance.id, 5716 NULL, 5717 NULL, 5718 &kyc_status_fail, 5719 &fail)); 5720 TEST_RET_ON_FAIL (fail); 5721 fail = true; 5722 TEST_RET_ON_FAIL (2 != 5723 plugin->account_kyc_get_status (plugin->cls, 5724 instance.instance.id, 5725 NULL, 5726 NULL, 5727 &kyc_status_ok, 5728 &fail)); 5729 TEST_RET_ON_FAIL (fail); 5730 json_decref (instance.instance.address); 5731 json_decref (instance.instance.jurisdiction); 5732 return 0; 5733 } 5734 5735 5736 /* *********** Templates ********** */ 5737 5738 /** 5739 * A container for data relevant to a template. 5740 */ 5741 struct TemplateData 5742 { 5743 /** 5744 * The identifier of the template. 5745 */ 5746 const char *id; 5747 5748 /** 5749 * The details of the template. 5750 */ 5751 struct TALER_MERCHANTDB_TemplateDetails template; 5752 }; 5753 5754 5755 /** 5756 * Creates a template for testing with. 5757 * 5758 * @param id the id of the template. 5759 * @param template the template data to fill. 5760 */ 5761 static void 5762 make_template (const char *id, 5763 struct TemplateData *template) 5764 { 5765 template->id = id; 5766 template->template.template_description = "This is a test template"; 5767 template->template.otp_id = NULL; 5768 template->template.template_contract = json_array (); 5769 GNUNET_assert (NULL != template->template.template_contract); 5770 } 5771 5772 5773 /** 5774 * Frees memory associated with @e TemplateData. 5775 * 5776 * @param template the container to free. 5777 */ 5778 static void 5779 free_template_data (struct TemplateData *template) 5780 { 5781 GNUNET_free (template->template.otp_id); 5782 json_decref (template->template.template_contract); 5783 } 5784 5785 5786 /** 5787 * Compare two templates for equality. 5788 * 5789 * @param a the first template. 5790 * @param b the second template. 5791 * @return 0 on equality, 1 otherwise. 5792 */ 5793 static int 5794 check_templates_equal (const struct TALER_MERCHANTDB_TemplateDetails *a, 5795 const struct TALER_MERCHANTDB_TemplateDetails *b) 5796 { 5797 if ((0 != strcmp (a->template_description, 5798 b->template_description)) || 5799 ( (NULL == a->otp_id) && (NULL != b->otp_id)) || 5800 ( (NULL != a->otp_id) && (NULL == b->otp_id)) || 5801 ( (NULL != a->otp_id) && (0 != strcmp (a->otp_id, 5802 b->otp_id))) || 5803 (1 != json_equal (a->template_contract, 5804 b->template_contract))) 5805 return 1; 5806 return 0; 5807 } 5808 5809 5810 /** 5811 * Tests inserting template data into the database. 5812 * 5813 * @param instance the instance to insert the template for. 5814 * @param template the template data to insert. 5815 * @param expected_result the result we expect the db to return. 5816 * @return 0 when successful, 1 otherwise. 5817 */ 5818 static int 5819 test_insert_template (const struct InstanceData *instance, 5820 const struct TemplateData *template, 5821 enum GNUNET_DB_QueryStatus expected_result) 5822 { 5823 TEST_COND_RET_ON_FAIL (expected_result == 5824 plugin->insert_template (plugin->cls, 5825 instance->instance.id, 5826 template->id, 5827 0, 5828 &template->template), 5829 "Insert template failed\n"); 5830 return 0; 5831 } 5832 5833 5834 /** 5835 * Tests updating template data in the database. 5836 * 5837 * @param instance the instance to update the template for. 5838 * @param template the template data to update. 5839 * @param expected_result the result we expect the db to return. 5840 * @return 0 when successful, 1 otherwise. 5841 */ 5842 static int 5843 test_update_template (const struct InstanceData *instance, 5844 const struct TemplateData *template, 5845 enum GNUNET_DB_QueryStatus expected_result) 5846 { 5847 TEST_COND_RET_ON_FAIL (expected_result == 5848 plugin->update_template (plugin->cls, 5849 instance->instance.id, 5850 template->id, 5851 &template->template), 5852 "Update template failed\n"); 5853 return 0; 5854 } 5855 5856 5857 /** 5858 * Tests looking up a template from the db. 5859 * 5860 * @param instance the instance to query from. 5861 * @param template the template to query and compare to. 5862 * @return 0 when successful, 1 otherwise. 5863 */ 5864 static int 5865 test_lookup_template (const struct InstanceData *instance, 5866 const struct TemplateData *template) 5867 { 5868 const struct TALER_MERCHANTDB_TemplateDetails *to_cmp 5869 = &template->template; 5870 struct TALER_MERCHANTDB_TemplateDetails lookup_result; 5871 5872 if (0 > plugin->lookup_template (plugin->cls, 5873 instance->instance.id, 5874 template->id, 5875 &lookup_result)) 5876 { 5877 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5878 "Lookup template failed\n"); 5879 TALER_MERCHANTDB_template_details_free (&lookup_result); 5880 return 1; 5881 } 5882 if (0 != check_templates_equal (&lookup_result, 5883 to_cmp)) 5884 { 5885 GNUNET_break (0); 5886 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5887 "Lookup template failed: incorrect template returned\n"); 5888 TALER_MERCHANTDB_template_details_free (&lookup_result); 5889 return 1; 5890 } 5891 TALER_MERCHANTDB_template_details_free (&lookup_result); 5892 return 0; 5893 } 5894 5895 5896 /** 5897 * Closure for testing template lookup 5898 */ 5899 struct TestLookupTemplates_Closure 5900 { 5901 /** 5902 * Number of template ids to compare to 5903 */ 5904 unsigned int templates_to_cmp_length; 5905 5906 /** 5907 * Pointer to array of template ids 5908 */ 5909 const struct TemplateData *templates_to_cmp; 5910 5911 /** 5912 * Pointer to array of number of matches for each template 5913 */ 5914 unsigned int *results_matching; 5915 5916 /** 5917 * Total number of results returned 5918 */ 5919 unsigned int results_length; 5920 }; 5921 5922 5923 /** 5924 * Function called after calling @e test_lookup_templates 5925 * 5926 * @param cls a pointer to the lookup closure. 5927 * @param template_id the identifier of the template found. 5928 */ 5929 static void 5930 lookup_templates_cb (void *cls, 5931 const char *template_id, 5932 const char *template_description) 5933 { 5934 struct TestLookupTemplates_Closure *cmp = cls; 5935 5936 if (NULL == cmp) 5937 return; 5938 cmp->results_length += 1; 5939 for (unsigned int i = 0; cmp->templates_to_cmp_length > i; ++i) 5940 { 5941 if ( (0 == strcmp (cmp->templates_to_cmp[i].id, 5942 template_id)) && 5943 (0 == strcmp (cmp->templates_to_cmp[i].template.template_description, 5944 template_description)) ) 5945 cmp->results_matching[i] += 1; 5946 } 5947 } 5948 5949 5950 /** 5951 * Tests looking up all templates for an instance. 5952 * 5953 * @param instance the instance to query from. 5954 * @param templates_length the number of templates we are expecting. 5955 * @param templates the list of templates that we expect to be found. 5956 * @return 0 when successful, 1 otherwise. 5957 */ 5958 static int 5959 test_lookup_templates (const struct InstanceData *instance, 5960 unsigned int templates_length, 5961 const struct TemplateData *templates) 5962 { 5963 unsigned int results_matching[templates_length]; 5964 struct TestLookupTemplates_Closure cls = { 5965 .templates_to_cmp_length = templates_length, 5966 .templates_to_cmp = templates, 5967 .results_matching = results_matching, 5968 .results_length = 0 5969 }; 5970 5971 memset (results_matching, 5972 0, 5973 sizeof (unsigned int) * templates_length); 5974 if (0 > plugin->lookup_templates (plugin->cls, 5975 instance->instance.id, 5976 &lookup_templates_cb, 5977 &cls)) 5978 { 5979 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5980 "Lookup templates failed\n"); 5981 return 1; 5982 } 5983 if (templates_length != cls.results_length) 5984 { 5985 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5986 "Lookup templates failed: incorrect number of results\n"); 5987 return 1; 5988 } 5989 for (unsigned int i = 0; templates_length > i; ++i) 5990 { 5991 if (1 != cls.results_matching[i]) 5992 { 5993 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5994 "Lookup templates failed: mismatched data\n"); 5995 return 1; 5996 } 5997 } 5998 return 0; 5999 } 6000 6001 6002 /** 6003 * Tests deleting a template. 6004 * 6005 * @param instance the instance to delete the template from. 6006 * @param template the template that should be deleted. 6007 * @param expected_result the result that we expect the plugin to return. 6008 * @return 0 when successful, 1 otherwise. 6009 */ 6010 static int 6011 test_delete_template (const struct InstanceData *instance, 6012 const struct TemplateData *template, 6013 enum GNUNET_DB_QueryStatus expected_result) 6014 { 6015 TEST_COND_RET_ON_FAIL (expected_result == 6016 plugin->delete_template (plugin->cls, 6017 instance->instance.id, 6018 template->id), 6019 "Delete template failed\n"); 6020 return 0; 6021 } 6022 6023 6024 /** 6025 * Closure for template tests. 6026 */ 6027 struct TestTemplates_Closure 6028 { 6029 /** 6030 * The instance to use for this test. 6031 */ 6032 struct InstanceData instance; 6033 6034 /** 6035 * The array of templates. 6036 */ 6037 struct TemplateData templates[2]; 6038 }; 6039 6040 6041 /** 6042 * Sets up the data structures used in the template tests. 6043 * 6044 * @param cls the closure to fill with test data. 6045 */ 6046 static void 6047 pre_test_templates (struct TestTemplates_Closure *cls) 6048 { 6049 /* Instance */ 6050 make_instance ("test_inst_templates", 6051 &cls->instance); 6052 6053 /* Templates */ 6054 make_template ("test_templates_pd_0", 6055 &cls->templates[0]); 6056 6057 make_template ("test_templates_pd_1", 6058 &cls->templates[1]); 6059 cls->templates[1].template.template_description = 6060 "This is a another test template"; 6061 } 6062 6063 6064 /** 6065 * Handles all teardown after testing. 6066 * 6067 * @param cls the closure containing memory to be freed. 6068 */ 6069 static void 6070 post_test_templates (struct TestTemplates_Closure *cls) 6071 { 6072 free_instance_data (&cls->instance); 6073 free_template_data (&cls->templates[0]); 6074 free_template_data (&cls->templates[1]); 6075 } 6076 6077 6078 /** 6079 * Runs the tests for templates. 6080 * 6081 * @param cls the container of the test data. 6082 * @return 0 on success, 1 otherwise. 6083 */ 6084 static int 6085 run_test_templates (struct TestTemplates_Closure *cls) 6086 { 6087 6088 /* Test that insert without an instance fails */ 6089 TEST_RET_ON_FAIL (test_insert_template (&cls->instance, 6090 &cls->templates[0], 6091 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 6092 /* Insert the instance */ 6093 TEST_RET_ON_FAIL (test_insert_instance (&cls->instance, 6094 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 6095 /* Test inserting a template */ 6096 TEST_RET_ON_FAIL (test_insert_template (&cls->instance, 6097 &cls->templates[0], 6098 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 6099 /* Test that double insert fails */ 6100 TEST_RET_ON_FAIL (test_insert_template (&cls->instance, 6101 &cls->templates[0], 6102 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 6103 /* Test lookup of individual templates */ 6104 TEST_RET_ON_FAIL (test_lookup_template (&cls->instance, 6105 &cls->templates[0])); 6106 /* Make sure it fails correctly for templates that don't exist */ 6107 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 6108 plugin->lookup_template (plugin->cls, 6109 cls->instance.instance.id, 6110 "nonexistent_template", 6111 NULL)) 6112 { 6113 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 6114 "Lookup template failed\n"); 6115 return 1; 6116 } 6117 /* Test template update */ 6118 cls->templates[0].template.template_description = 6119 "This is a test template that has been updated!"; 6120 GNUNET_free (cls->templates[0].template.otp_id); 6121 cls->templates[0].template.otp_id = GNUNET_strdup ("otp_id"); 6122 { 6123 /* ensure OTP device exists */ 6124 struct TALER_MERCHANTDB_OtpDeviceDetails td = { 6125 .otp_description = "my otp", 6126 .otp_key = "my key", 6127 .otp_algorithm = 1, 6128 .otp_ctr = 42 6129 }; 6130 GNUNET_assert (0 <= 6131 plugin->insert_otp (plugin->cls, 6132 cls->instance.instance.id, 6133 "otp_id", 6134 &td)); 6135 } 6136 GNUNET_assert (0 == 6137 json_array_append_new ( 6138 cls->templates[0].template.template_contract, 6139 json_string ("This is a test. 3CH."))); 6140 TEST_RET_ON_FAIL (test_update_template (&cls->instance, 6141 &cls->templates[0], 6142 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 6143 TEST_RET_ON_FAIL (test_lookup_template (&cls->instance, 6144 &cls->templates[0])); 6145 TEST_RET_ON_FAIL (test_update_template (&cls->instance, 6146 &cls->templates[1], 6147 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 6148 /* Test collective template lookup */ 6149 TEST_RET_ON_FAIL (test_insert_template (&cls->instance, 6150 &cls->templates[1], 6151 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 6152 TEST_RET_ON_FAIL (test_lookup_templates (&cls->instance, 6153 2, 6154 cls->templates)); 6155 6156 /* Test template deletion */ 6157 TEST_RET_ON_FAIL (test_delete_template (&cls->instance, 6158 &cls->templates[1], 6159 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 6160 /* Test double deletion fails */ 6161 TEST_RET_ON_FAIL (test_delete_template (&cls->instance, 6162 &cls->templates[1], 6163 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 6164 TEST_RET_ON_FAIL (test_lookup_templates (&cls->instance, 6165 1, 6166 cls->templates)); 6167 return 0; 6168 } 6169 6170 6171 /** 6172 * Takes care of template testing. 6173 * 6174 * @return 0 on success, 1 otherwise. 6175 */ 6176 static int 6177 test_templates (void) 6178 { 6179 struct TestTemplates_Closure test_cls; 6180 int test_result; 6181 6182 memset (&test_cls, 6183 0, 6184 sizeof (test_cls)); 6185 pre_test_templates (&test_cls); 6186 test_result = run_test_templates (&test_cls); 6187 post_test_templates (&test_cls); 6188 return test_result; 6189 } 6190 6191 6192 /* *********** Webhooks ********** */ 6193 6194 /** 6195 * A container for data relevant to a webhook. 6196 */ 6197 struct WebhookData 6198 { 6199 6200 /** 6201 * The identifier of the webhook. 6202 */ 6203 const char *id; 6204 6205 /** 6206 * The details of the webhook. 6207 */ 6208 struct TALER_MERCHANTDB_WebhookDetails webhook; 6209 }; 6210 6211 6212 /** 6213 * Creates a webhook for testing with. 6214 * 6215 * @param id the id of the webhook. 6216 * @param webhook the webhook data to fill. 6217 */ 6218 static void 6219 make_webhook (const char *id, 6220 struct WebhookData *webhook) 6221 { 6222 webhook->id = id; 6223 webhook->webhook.event_type = "Paid"; 6224 webhook->webhook.url = "https://exampletest.com"; 6225 webhook->webhook.http_method = "POST"; 6226 webhook->webhook.header_template = "Authorization:XYJAORKJEO"; 6227 webhook->webhook.body_template = "$Amount"; 6228 } 6229 6230 6231 /** 6232 * Compare two webhooks for equality. 6233 * 6234 * @param a the first webhook. 6235 * @param b the second webhook. 6236 * @return 0 on equality, 1 otherwise. 6237 */ 6238 static int 6239 check_webhooks_equal (const struct TALER_MERCHANTDB_WebhookDetails *a, 6240 const struct TALER_MERCHANTDB_WebhookDetails *b) 6241 { 6242 if ((0 != strcmp (a->event_type, 6243 b->event_type)) || 6244 (0 != strcmp (a->url, 6245 b->url)) || 6246 (0 != strcmp (a->http_method, 6247 b->http_method)) || 6248 (0 != strcmp (a->header_template, 6249 b->header_template)) || 6250 (0 != strcmp (a->body_template, 6251 b->body_template))) 6252 return 1; 6253 return 0; 6254 } 6255 6256 6257 /** 6258 * Tests inserting webhook data into the database. 6259 * 6260 * @param instance the instance to insert the webhook for. 6261 * @param webhook the webhook data to insert. 6262 * @param expected_result the result we expect the db to return. 6263 * @return 0 when successful, 1 otherwise. 6264 */ 6265 static int 6266 test_insert_webhook (const struct InstanceData *instance, 6267 const struct WebhookData *webhook, 6268 enum GNUNET_DB_QueryStatus expected_result) 6269 { 6270 TEST_COND_RET_ON_FAIL (expected_result == 6271 plugin->insert_webhook (plugin->cls, 6272 instance->instance.id, 6273 webhook->id, 6274 &webhook->webhook), 6275 "Insert webhook failed\n"); 6276 return 0; 6277 } 6278 6279 6280 /** 6281 * Tests updating webhook data in the database. 6282 * 6283 * @param instance the instance to update the webhook for. 6284 * @param webhook the webhook data to update. 6285 * @param expected_result the result we expect the db to return. 6286 * @return 0 when successful, 1 otherwise. 6287 */ 6288 static int 6289 test_update_webhook (const struct InstanceData *instance, 6290 const struct WebhookData *webhook, 6291 enum GNUNET_DB_QueryStatus expected_result) 6292 { 6293 TEST_COND_RET_ON_FAIL (expected_result == 6294 plugin->update_webhook (plugin->cls, 6295 instance->instance.id, 6296 webhook->id, 6297 &webhook->webhook), 6298 "Update webhook failed\n"); 6299 return 0; 6300 } 6301 6302 6303 /** 6304 * Tests looking up a webhook from the db. 6305 * 6306 * @param instance the instance to query from. 6307 * @param webhook the webhook to query and compare to. 6308 * @return 0 when successful, 1 otherwise. 6309 */ 6310 static int 6311 test_lookup_webhook (const struct InstanceData *instance, 6312 const struct WebhookData *webhook) 6313 { 6314 const struct TALER_MERCHANTDB_WebhookDetails *to_cmp = &webhook->webhook; 6315 struct TALER_MERCHANTDB_WebhookDetails lookup_result; 6316 6317 if (0 > plugin->lookup_webhook (plugin->cls, 6318 instance->instance.id, 6319 webhook->id, 6320 &lookup_result)) 6321 { 6322 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 6323 "Lookup webhook failed\n"); 6324 TALER_MERCHANTDB_webhook_details_free (&lookup_result); 6325 return 1; 6326 } 6327 if (0 != check_webhooks_equal (&lookup_result, 6328 to_cmp)) 6329 { 6330 GNUNET_break (0); 6331 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 6332 "Lookup webhook failed: incorrect webhook returned\n"); 6333 TALER_MERCHANTDB_webhook_details_free (&lookup_result); 6334 return 1; 6335 } 6336 TALER_MERCHANTDB_webhook_details_free (&lookup_result); 6337 return 0; 6338 } 6339 6340 6341 /** 6342 * Closure for testing webhook lookup 6343 */ 6344 struct TestLookupWebhooks_Closure 6345 { 6346 /** 6347 * Number of webhook ids to compare to 6348 */ 6349 unsigned int webhooks_to_cmp_length; 6350 6351 /** 6352 * Pointer to array of webhook ids 6353 */ 6354 const struct WebhookData *webhooks_to_cmp; 6355 6356 /** 6357 * Pointer to array of number of matches for each webhook 6358 */ 6359 unsigned int *results_matching; 6360 6361 /** 6362 * Total number of results returned 6363 */ 6364 unsigned int results_length; 6365 }; 6366 6367 6368 /** 6369 * Function called after calling @e test_lookup_webhooks 6370 * 6371 * @param cls a pointer to the lookup closure. 6372 * @param webhook_id the identifier of the webhook found. 6373 */ 6374 static void 6375 lookup_webhooks_cb (void *cls, 6376 const char *webhook_id, 6377 const char *event_type) 6378 { 6379 struct TestLookupWebhooks_Closure *cmp = cls; 6380 if (NULL == cmp) 6381 return; 6382 cmp->results_length += 1; 6383 for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i) 6384 { 6385 if ((0 == strcmp (cmp->webhooks_to_cmp[i].id, 6386 webhook_id)) && 6387 (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.event_type, 6388 event_type)) ) 6389 cmp->results_matching[i] += 1; 6390 } 6391 } 6392 6393 6394 /** 6395 * Tests looking up all webhooks for an instance. 6396 * 6397 * @param instance the instance to query from. 6398 * @param webhooks_length the number of webhooks we are expecting. 6399 * @param webhooks the list of webhooks that we expect to be found. 6400 * @return 0 when successful, 1 otherwise. 6401 */ 6402 static int 6403 test_lookup_webhooks (const struct InstanceData *instance, 6404 unsigned int webhooks_length, 6405 const struct WebhookData *webhooks) 6406 { 6407 unsigned int results_matching[webhooks_length]; 6408 struct TestLookupWebhooks_Closure cls = { 6409 .webhooks_to_cmp_length = webhooks_length, 6410 .webhooks_to_cmp = webhooks, 6411 .results_matching = results_matching, 6412 .results_length = 0 6413 }; 6414 memset (results_matching, 0, sizeof (unsigned int) * webhooks_length); 6415 if (0 > plugin->lookup_webhooks (plugin->cls, 6416 instance->instance.id, 6417 &lookup_webhooks_cb, 6418 &cls)) 6419 { 6420 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 6421 "Lookup webhooks failed\n"); 6422 return 1; 6423 } 6424 if (webhooks_length != cls.results_length) 6425 { 6426 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 6427 "Lookup webhooks failed: incorrect number of results\n"); 6428 return 1; 6429 } 6430 for (unsigned int i = 0; webhooks_length > i; ++i) 6431 { 6432 if (1 != cls.results_matching[i]) 6433 { 6434 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 6435 "Lookup webhooks failed: mismatched data\n"); 6436 return 1; 6437 } 6438 } 6439 return 0; 6440 } 6441 6442 6443 /** 6444 * Function called after calling @e test_lookup_webhooks 6445 * 6446 * @param cls a pointer to the lookup closure. 6447 * @param webhook_id the identifier of the webhook found. 6448 */ 6449 static void 6450 lookup_webhook_by_event_cb (void *cls, 6451 uint64_t webhook_serial, 6452 const char *event_type, 6453 const char *url, 6454 const char *http_method, 6455 const char *header_template, 6456 const char *body_template) 6457 { 6458 struct TestLookupWebhooks_Closure *cmp = cls; 6459 if (NULL == cmp) 6460 return; 6461 cmp->results_length += 1; 6462 for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i) 6463 { 6464 if ((0 == strcmp (cmp->webhooks_to_cmp[i].webhook.event_type, 6465 event_type)) && 6466 (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.url, 6467 url)) && 6468 (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.http_method, 6469 http_method)) && 6470 (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.header_template, 6471 header_template)) && 6472 (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.body_template, 6473 body_template)) ) 6474 cmp->results_matching[i] += 1; 6475 } 6476 } 6477 6478 6479 /** 6480 * Tests looking up webhooks by event for an instance. 6481 * 6482 * @param instance the instance to query from. 6483 * @param webhooks_length the number of webhooks we are expecting. 6484 * @param webhooks the list of webhooks that we expect to be found. 6485 * @return 0 when successful, 1 otherwise. 6486 */ 6487 static int 6488 test_lookup_webhook_by_event (const struct InstanceData *instance, 6489 unsigned int webhooks_length, 6490 const struct WebhookData *webhooks) 6491 { 6492 unsigned int results_matching[webhooks_length]; 6493 struct TestLookupWebhooks_Closure cls = { 6494 .webhooks_to_cmp_length = webhooks_length, 6495 .webhooks_to_cmp = webhooks, 6496 .results_matching = results_matching, 6497 .results_length = 0 6498 }; 6499 memset (results_matching, 0, sizeof (unsigned int) * webhooks_length); 6500 if (0 > plugin->lookup_webhook_by_event (plugin->cls, 6501 instance->instance.id, 6502 webhooks->webhook.event_type, 6503 &lookup_webhook_by_event_cb, 6504 &cls)) 6505 { 6506 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 6507 "Lookup webhooks by event failed\n"); 6508 return 1; 6509 } 6510 6511 if (webhooks_length != cls.results_length) 6512 { 6513 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 6514 "Lookup webhooks by event failed: incorrect number of results\n"); 6515 return 1; 6516 } 6517 for (unsigned int i = 0; webhooks_length > i; ++i) 6518 { 6519 if (1 != cls.results_matching[i]) 6520 { 6521 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 6522 "Lookup webhooks by event failed: mismatched data\n"); 6523 return 1; 6524 } 6525 } 6526 return 0; 6527 } 6528 6529 6530 /** 6531 * Tests deleting a webhook. 6532 * 6533 * @param instance the instance to delete the webhook from. 6534 * @param webhook the webhook that should be deleted. 6535 * @param expected_result the result that we expect the plugin to return. 6536 * @return 0 when successful, 1 otherwise. 6537 */ 6538 static int 6539 test_delete_webhook (const struct InstanceData *instance, 6540 const struct WebhookData *webhook, 6541 enum GNUNET_DB_QueryStatus expected_result) 6542 { 6543 TEST_COND_RET_ON_FAIL (expected_result == 6544 plugin->delete_webhook (plugin->cls, 6545 instance->instance.id, 6546 webhook->id), 6547 "Delete webhook failed\n"); 6548 return 0; 6549 } 6550 6551 6552 /** 6553 * Closure for webhook tests. 6554 */ 6555 struct TestWebhooks_Closure 6556 { 6557 /** 6558 * The instance to use for this test. 6559 */ 6560 struct InstanceData instance; 6561 6562 /** 6563 * The array of webhooks. 6564 */ 6565 struct WebhookData webhooks[3]; 6566 }; 6567 6568 6569 /** 6570 * Sets up the data structures used in the webhook tests. 6571 * 6572 * @param cls the closure to fill with test data. 6573 */ 6574 static void 6575 pre_test_webhooks (struct TestWebhooks_Closure *cls) 6576 { 6577 /* Instance */ 6578 make_instance ("test_inst_webhooks", 6579 &cls->instance); 6580 6581 /* Webhooks */ 6582 make_webhook ("test_webhooks_wb_0", 6583 &cls->webhooks[0]); 6584 6585 make_webhook ("test_webhooks_wb_1", 6586 &cls->webhooks[1]); 6587 cls->webhooks[1].webhook.event_type = "Test paid"; 6588 cls->webhooks[1].webhook.url = "https://example.com"; 6589 cls->webhooks[1].webhook.http_method = "POST"; 6590 cls->webhooks[1].webhook.header_template = "Authorization:1XYJAOR493O"; 6591 cls->webhooks[1].webhook.body_template = "$Amount"; 6592 6593 make_webhook ("test_webhooks_wb_2", 6594 &cls->webhooks[2]); 6595 cls->webhooks[2].webhook.event_type = "Test paid"; 6596 cls->webhooks[2].webhook.url = "https://examplerefund.com"; 6597 cls->webhooks[2].webhook.http_method = "POST"; 6598 cls->webhooks[2].webhook.header_template = "Authorization:XY6ORK52JEO"; 6599 cls->webhooks[2].webhook.body_template = "$Amount"; 6600 } 6601 6602 6603 /** 6604 * Handles all teardown after testing. 6605 * 6606 * @param cls the closure containing memory to be freed. 6607 */ 6608 static void 6609 post_test_webhooks (struct TestWebhooks_Closure *cls) 6610 { 6611 free_instance_data (&cls->instance); 6612 } 6613 6614 6615 /** 6616 * Runs the tests for webhooks. 6617 * 6618 * @param cls the container of the test data. 6619 * @return 0 on success, 1 otherwise. 6620 */ 6621 static int 6622 run_test_webhooks (struct TestWebhooks_Closure *cls) 6623 { 6624 6625 /* Test that insert without an instance fails */ 6626 TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance, 6627 &cls->webhooks[0], 6628 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 6629 /* Insert the instance */ 6630 TEST_RET_ON_FAIL (test_insert_instance (&cls->instance, 6631 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 6632 /* Test inserting a webhook */ 6633 TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance, 6634 &cls->webhooks[0], 6635 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 6636 /* Test that double insert fails */ 6637 TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance, 6638 &cls->webhooks[0], 6639 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 6640 /* Test lookup of individual webhooks */ 6641 TEST_RET_ON_FAIL (test_lookup_webhook (&cls->instance, 6642 &cls->webhooks[0])); 6643 /* Make sure it fails correctly for webhooks that don't exist */ 6644 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 6645 plugin->lookup_webhook (plugin->cls, 6646 cls->instance.instance.id, 6647 "nonexistent_webhook", 6648 NULL)) 6649 { 6650 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 6651 "Lookup webhook failed\n"); 6652 return 1; 6653 } 6654 /* Test webhook update */ 6655 cls->webhooks[0].webhook.event_type = 6656 "Test paid"; 6657 cls->webhooks[0].webhook.url = 6658 "example.com"; 6659 cls->webhooks[0].webhook.http_method = 6660 "POST"; 6661 cls->webhooks[0].webhook.header_template = 6662 "Authorization:WEKFOEKEXZ"; 6663 cls->webhooks[0].webhook.body_template = 6664 "$Amount"; 6665 TEST_RET_ON_FAIL (test_update_webhook (&cls->instance, 6666 &cls->webhooks[0], 6667 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 6668 6669 TEST_RET_ON_FAIL (test_lookup_webhook (&cls->instance, 6670 &cls->webhooks[0])); 6671 TEST_RET_ON_FAIL (test_update_webhook (&cls->instance, 6672 &cls->webhooks[1], 6673 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 6674 /* Test collective webhook lookup */ 6675 TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance, 6676 &cls->webhooks[1], 6677 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 6678 TEST_RET_ON_FAIL (test_lookup_webhooks (&cls->instance, 6679 2, 6680 cls->webhooks)); 6681 TEST_RET_ON_FAIL (test_lookup_webhook_by_event (&cls->instance, 6682 2, 6683 cls->webhooks)); 6684 TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance, 6685 &cls->webhooks[2], 6686 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 6687 6688 /* Test webhook deletion */ 6689 TEST_RET_ON_FAIL (test_delete_webhook (&cls->instance, 6690 &cls->webhooks[1], 6691 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 6692 /* Test double deletion fails */ 6693 TEST_RET_ON_FAIL (test_delete_webhook (&cls->instance, 6694 &cls->webhooks[1], 6695 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 6696 cls->webhooks[1] = cls->webhooks[2]; 6697 TEST_RET_ON_FAIL (test_lookup_webhooks (&cls->instance, 6698 2, 6699 cls->webhooks)); 6700 TEST_RET_ON_FAIL (test_lookup_webhook_by_event (&cls->instance, 6701 2, 6702 cls->webhooks)); 6703 return 0; 6704 } 6705 6706 6707 /** 6708 * Takes care of webhook testing. 6709 * 6710 * @return 0 on success, 1 otherwise. 6711 */ 6712 static int 6713 test_webhooks (void) 6714 { 6715 struct TestWebhooks_Closure test_cls; 6716 int test_result; 6717 6718 pre_test_webhooks (&test_cls); 6719 test_result = run_test_webhooks (&test_cls); 6720 post_test_webhooks (&test_cls); 6721 return test_result; 6722 } 6723 6724 6725 /* *********** Pending Webhooks ********** */ 6726 6727 /** 6728 * A container for data relevant to a pending webhook. 6729 */ 6730 struct PendingWebhookData 6731 { 6732 /** 6733 * Reference to the configured webhook template. 6734 */ 6735 uint64_t webhook_serial; 6736 6737 /** 6738 * The details of the pending webhook. 6739 */ 6740 struct TALER_MERCHANTDB_PendingWebhookDetails pwebhook; 6741 }; 6742 6743 6744 /** 6745 * Creates a pending webhook for testing with. 6746 * 6747 * @param serial reference to the configured webhook template. 6748 * @param pwebhook the pending webhook data to fill. 6749 */ 6750 static void 6751 make_pending_webhook (uint64_t webhook_serial, 6752 struct PendingWebhookData *pwebhook) 6753 { 6754 pwebhook->webhook_serial = webhook_serial; 6755 pwebhook->pwebhook.next_attempt = GNUNET_TIME_UNIT_ZERO_ABS; 6756 pwebhook->pwebhook.retries = 0; 6757 pwebhook->pwebhook.url = "https://exampletest.com"; 6758 pwebhook->pwebhook.http_method = "POST"; 6759 pwebhook->pwebhook.header = "Authorization:XYJAORKJEO"; 6760 pwebhook->pwebhook.body = "$Amount"; 6761 } 6762 6763 6764 /** 6765 * Tests inserting pending webhook data into the database. 6766 * 6767 * @param instance the instance to insert the pending webhook for. 6768 * @param pending webhook the pending webhook data to insert. 6769 * @param expected_result the result we expect the db to return. 6770 * @return 0 when successful, 1 otherwise. 6771 */ 6772 static int 6773 test_insert_pending_webhook (const struct InstanceData *instance, 6774 struct PendingWebhookData *pwebhook, 6775 enum GNUNET_DB_QueryStatus expected_result) 6776 { 6777 6778 TEST_COND_RET_ON_FAIL (expected_result == 6779 plugin->insert_pending_webhook (plugin->cls, 6780 instance->instance.id, 6781 pwebhook-> 6782 webhook_serial, 6783 pwebhook->pwebhook.url, 6784 pwebhook->pwebhook. 6785 http_method, 6786 pwebhook->pwebhook. 6787 header, 6788 pwebhook->pwebhook.body 6789 ), 6790 "Insert pending webhook failed\n"); 6791 return 0; 6792 } 6793 6794 6795 /** 6796 * Tests updating pending webhook data in the database. 6797 * 6798 * @param instance the instance to update the pending webhook for. 6799 * @param pending webhook the pending webhook data to update. 6800 * @param expected_result the result we expect the db to return. 6801 * @return 0 when successful, 1 otherwise. 6802 */ 6803 static int 6804 test_update_pending_webhook (const struct InstanceData *instance, 6805 struct PendingWebhookData *pwebhook, 6806 enum GNUNET_DB_QueryStatus expected_result) 6807 { 6808 pwebhook->pwebhook.next_attempt = GNUNET_TIME_relative_to_absolute ( 6809 GNUNET_TIME_UNIT_HOURS); 6810 pwebhook->pwebhook.retries++; 6811 TEST_COND_RET_ON_FAIL (expected_result == 6812 plugin->update_pending_webhook (plugin->cls, 6813 pwebhook-> 6814 webhook_serial, 6815 pwebhook->pwebhook. 6816 next_attempt), 6817 "Update pending webhook failed\n"); 6818 return 0; 6819 } 6820 6821 6822 /** 6823 * Container for information for looking up the row number of a deposit. 6824 */ 6825 struct LookupPendingWebhookSerial_Closure 6826 { 6827 /** 6828 * The pending webhook we're looking for. 6829 */ 6830 const struct PendingWebhookData *pwebhook; 6831 6832 /** 6833 * The serial found. 6834 */ 6835 uint64_t webhook_pending_serial; 6836 }; 6837 6838 6839 /** 6840 * Function called after calling @e test_lookup_all_webhook, 6841 * test_lookup_future_webhook and test_lookup_pending_webhook 6842 * 6843 * @param cls a pointer to the lookup closure. 6844 * @param webhook_serial reference to the configured webhook template. 6845 */ 6846 static void 6847 get_pending_serial_cb (void *cls, 6848 uint64_t webhook_pending_serial, 6849 struct GNUNET_TIME_Absolute next_attempt, 6850 uint32_t retries, 6851 const char *url, 6852 const char *http_method, 6853 const char *header, 6854 const char *body) 6855 { 6856 struct LookupPendingWebhookSerial_Closure *lpw = cls; 6857 6858 if ((0 == strcmp (lpw->pwebhook->pwebhook.url, 6859 url)) && 6860 (0 == strcmp (lpw->pwebhook->pwebhook.http_method, 6861 http_method)) && 6862 (0 == strcmp (lpw->pwebhook->pwebhook.header, 6863 header)) && 6864 (0 == strcmp (lpw->pwebhook->pwebhook.body, 6865 body)) ) 6866 { 6867 lpw->webhook_pending_serial = webhook_pending_serial; 6868 } 6869 /* else 6870 { 6871 fprintf(stdout, "next_attempt: %lu vs %lu\n", lpw->pwebhook->pwebhook.next_attempt.abs_value_us, next_attempt.abs_value_us); 6872 fprintf(stdout, "retries: %d vs %d\n", lpw->pwebhook->pwebhook.retries, retries); 6873 fprintf(stdout, "url: %s vs %s\n", lpw->pwebhook->pwebhook.url, url); 6874 fprintf(stdout, "http_method: %s vs %s\n", lpw->pwebhook->pwebhook.http_method, http_method); 6875 fprintf(stdout, "header: %s vs %s\n", lpw->pwebhook->pwebhook.header, header); 6876 fprintf(stdout, "body: %s vs %s\n", lpw->pwebhook->pwebhook.body, body); 6877 }*/ 6878 } 6879 6880 6881 /** 6882 * Convenience function to retrieve the row number of a webhook pending in the database. 6883 * 6884 * @param instance the instance to get webhook pending(wp) from. 6885 * @param webhook pending the wp to lookup the serial for. 6886 * @return the row number of the deposit. 6887 */ 6888 static uint64_t 6889 get_pending_serial (const struct InstanceData *instance, 6890 const struct PendingWebhookData *pwebhook) 6891 { 6892 struct LookupPendingWebhookSerial_Closure lpw = { 6893 .pwebhook = pwebhook, 6894 .webhook_pending_serial = 0 6895 }; 6896 6897 GNUNET_assert (0 < 6898 plugin->lookup_all_webhooks (plugin->cls, 6899 instance->instance.id, 6900 0, 6901 INT_MAX, 6902 &get_pending_serial_cb, 6903 &lpw)); 6904 GNUNET_assert (0 != lpw.webhook_pending_serial); 6905 6906 return lpw.webhook_pending_serial; 6907 } 6908 6909 6910 /** 6911 * Closure for testing pending webhook lookup 6912 */ 6913 struct TestLookupPendingWebhooks_Closure 6914 { 6915 /** 6916 * Number of webhook serial to compare to 6917 */ 6918 unsigned int webhooks_to_cmp_length; 6919 6920 /** 6921 * Pointer to array of webhook serials 6922 */ 6923 const struct PendingWebhookData *webhooks_to_cmp; 6924 6925 /** 6926 * Pointer to array of number of matches for each pending webhook 6927 */ 6928 unsigned int *results_matching; 6929 6930 /** 6931 * Total number of results returned 6932 */ 6933 unsigned int results_length; 6934 }; 6935 6936 6937 /** 6938 * Function called after calling @e test_lookup_all_webhook, 6939 * test_lookup_future_webhook and test_lookup_pending_webhook 6940 * 6941 * @param cls a pointer to the lookup closure. 6942 * @param webhook_serial reference to the configured webhook template. 6943 */ 6944 static void 6945 lookup_pending_webhooks_cb (void *cls, 6946 uint64_t webhook_pending_serial, 6947 struct GNUNET_TIME_Absolute next_attempt, 6948 uint32_t retries, 6949 const char *url, 6950 const char *http_method, 6951 const char *header, 6952 const char *body) 6953 { 6954 struct TestLookupPendingWebhooks_Closure *cmp = cls; 6955 6956 cmp->results_length++; 6957 for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i) 6958 { 6959 if ((0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.url, 6960 url)) && 6961 (0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.http_method, 6962 http_method)) && 6963 (0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.header, 6964 header)) && 6965 (0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.body, 6966 body)) ) 6967 { 6968 cmp->results_matching[i]++; 6969 } 6970 } 6971 } 6972 6973 6974 /** 6975 * Tests looking up the pending webhook for an instance. 6976 * 6977 * @param instance the instance to query from. 6978 * @param pwebhooks_length the number of pending webhook we are expecting. 6979 * @param pwebhooks the list of pending webhooks that we expect to be found. 6980 * @return 0 when successful, 1 otherwise. 6981 */ 6982 static int 6983 test_lookup_pending_webhooks (const struct InstanceData *instance, 6984 unsigned int pwebhooks_length, 6985 const struct PendingWebhookData *pwebhooks) 6986 { 6987 unsigned int results_matching[pwebhooks_length]; 6988 struct TestLookupPendingWebhooks_Closure cls = { 6989 .webhooks_to_cmp_length = pwebhooks_length, 6990 .webhooks_to_cmp = pwebhooks, 6991 .results_matching = results_matching, 6992 .results_length = 0 6993 }; 6994 6995 memset (results_matching, 0, sizeof (results_matching)); 6996 if (0 > plugin->lookup_pending_webhooks (plugin->cls, 6997 &lookup_pending_webhooks_cb, 6998 &cls)) 6999 { 7000 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 7001 "Lookup pending webhook failed\n"); 7002 return 1; 7003 } 7004 if (pwebhooks_length != cls.results_length) 7005 { 7006 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 7007 "Lookup pending webhook failed: incorrect number of results\n"); 7008 return 1; 7009 } 7010 for (unsigned int i = 0; i < pwebhooks_length; i++) 7011 { 7012 if (1 != cls.results_matching[i]) 7013 { 7014 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 7015 "Lookup pending webhook failed: mismatched data\n"); 7016 return 1; 7017 } 7018 } 7019 return 0; 7020 } 7021 7022 7023 /** 7024 * Tests looking up the future webhook to send for an instance. 7025 * 7026 * @param instance the instance to query from. 7027 * @param pwebhooks_length the number of pending webhook we are expecting. 7028 * @param pwebhooks the list of pending webhooks that we expect to be found. 7029 * @return 0 when successful, 1 otherwise. 7030 */ 7031 static int 7032 test_lookup_future_webhook (const struct InstanceData *instance, 7033 unsigned int pwebhooks_length, 7034 const struct PendingWebhookData *pwebhooks) 7035 { 7036 unsigned int results_matching[pwebhooks_length]; 7037 struct TestLookupPendingWebhooks_Closure cls = { 7038 .webhooks_to_cmp_length = pwebhooks_length, 7039 .webhooks_to_cmp = pwebhooks, 7040 .results_matching = results_matching, 7041 .results_length = 0 7042 }; 7043 7044 memset (results_matching, 0, sizeof (results_matching)); 7045 if (0 > plugin->lookup_future_webhook (plugin->cls, 7046 &lookup_pending_webhooks_cb, 7047 &cls)) 7048 { 7049 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 7050 "Lookup future webhook failed\n"); 7051 return 1; 7052 } 7053 if (pwebhooks_length != cls.results_length) 7054 { 7055 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 7056 "Lookup future webhook failed: incorrect number of results\n"); 7057 return 1; 7058 } 7059 for (unsigned int i = 0; pwebhooks_length > i; ++i) 7060 { 7061 if (1 != cls.results_matching[i]) 7062 { 7063 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 7064 "Lookup future webhook failed: mismatched data\n"); 7065 return 1; 7066 } 7067 } 7068 return 0; 7069 } 7070 7071 7072 /** 7073 * Tests looking up all the pending webhook for an instance. 7074 * 7075 * @param instance the instance to query from. 7076 * @param pwebhooks_length the number of pending webhook we are expecting. 7077 * @param pwebhooks the list of pending webhooks that we expect to be found. 7078 * @return 0 when successful, 1 otherwise. 7079 */ 7080 static int 7081 test_lookup_all_webhooks (const struct InstanceData *instance, 7082 unsigned int pwebhooks_length, 7083 const struct PendingWebhookData *pwebhooks) 7084 { 7085 uint64_t max_results = 2; 7086 uint64_t min_row = 0; 7087 unsigned int results_matching[GNUNET_NZL (pwebhooks_length)]; 7088 struct TestLookupPendingWebhooks_Closure cls = { 7089 .webhooks_to_cmp_length = pwebhooks_length, 7090 .webhooks_to_cmp = pwebhooks, 7091 .results_matching = results_matching, 7092 .results_length = 0 7093 }; 7094 7095 memset (results_matching, 7096 0, 7097 sizeof (results_matching)); 7098 if (0 > plugin->lookup_all_webhooks (plugin->cls, 7099 instance->instance.id, 7100 min_row, 7101 max_results, 7102 &lookup_pending_webhooks_cb, 7103 &cls)) 7104 { 7105 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 7106 "Lookup all webhooks failed\n"); 7107 return 1; 7108 } 7109 if (pwebhooks_length != cls.results_length) 7110 { 7111 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 7112 "Lookup all webhooks failed: incorrect number of results\n"); 7113 return 1; 7114 } 7115 for (unsigned int i = 0; pwebhooks_length > i; ++i) 7116 { 7117 if (1 != cls.results_matching[i]) 7118 { 7119 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 7120 "Lookup all webhooks failed: mismatched data\n"); 7121 return 1; 7122 } 7123 } 7124 return 0; 7125 } 7126 7127 7128 /** 7129 * Tests deleting a pending webhook. 7130 * 7131 * @param instance the instance to delete the pending webhook from. 7132 * @param pwebhook the pending webhook that should be deleted. 7133 * @param expected_result the result that we expect the plugin to return. 7134 * @return 0 when successful, 1 otherwise. 7135 */ 7136 static int 7137 test_delete_pending_webhook (uint64_t webhooks_pending_serial, 7138 enum GNUNET_DB_QueryStatus expected_result) 7139 { 7140 7141 TEST_COND_RET_ON_FAIL (expected_result == 7142 plugin->delete_pending_webhook (plugin->cls, 7143 webhooks_pending_serial), 7144 "Delete webhook failed\n"); 7145 return 0; 7146 } 7147 7148 7149 /** 7150 * Closure for pending webhook tests. 7151 */ 7152 struct TestPendingWebhooks_Closure 7153 { 7154 /** 7155 * The instance to use for this test. 7156 */ 7157 struct InstanceData instance; 7158 7159 /** 7160 * The array of pending webhooks. 7161 */ 7162 struct PendingWebhookData pwebhooks[2]; 7163 }; 7164 7165 7166 /** 7167 * Sets up the data structures used in the pending webhook tests. 7168 * 7169 * @param cls the closure to fill with test data. 7170 */ 7171 static void 7172 pre_test_pending_webhooks (struct TestPendingWebhooks_Closure *cls) 7173 { 7174 /* Instance */ 7175 make_instance ("test_inst_pending_webhooks", 7176 &cls->instance); 7177 7178 /* Webhooks */ 7179 make_pending_webhook (1, 7180 &cls->pwebhooks[0]); 7181 7182 make_pending_webhook (4, 7183 &cls->pwebhooks[1]); 7184 cls->pwebhooks[1].pwebhook.url = "https://test.com"; 7185 cls->pwebhooks[1].pwebhook.http_method = "POST"; 7186 cls->pwebhooks[1].pwebhook.header = "Authorization:XYJAO5R06EO"; 7187 cls->pwebhooks[1].pwebhook.body = "$Amount"; 7188 } 7189 7190 7191 /** 7192 * Handles all teardown after testing. 7193 * 7194 * @param cls the closure containing memory to be freed. 7195 */ 7196 static void 7197 post_test_pending_webhooks (struct TestPendingWebhooks_Closure *cls) 7198 { 7199 free_instance_data (&cls->instance); 7200 } 7201 7202 7203 /** 7204 * Runs the tests for pending webhooks. 7205 * 7206 * @param cls the container of the test data. 7207 * @return 0 on success, 1 otherwise. 7208 */ 7209 static int 7210 run_test_pending_webhooks (struct TestPendingWebhooks_Closure *cls) 7211 { 7212 uint64_t webhook_pending_serial0; 7213 uint64_t webhook_pending_serial1; 7214 7215 /* Test that insert without an instance fails */ 7216 TEST_RET_ON_FAIL (test_insert_pending_webhook (&cls->instance, 7217 &cls->pwebhooks[0], 7218 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 7219 7220 /* Insert the instance */ 7221 TEST_RET_ON_FAIL (test_insert_instance (&cls->instance, 7222 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 7223 7224 /* Test inserting a pending webhook */ 7225 TEST_RET_ON_FAIL (test_insert_pending_webhook (&cls->instance, 7226 &cls->pwebhooks[0], 7227 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 7228 TEST_RET_ON_FAIL (test_insert_pending_webhook (&cls->instance, 7229 &cls->pwebhooks[1], 7230 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 7231 /* Test collective pending webhook lookup */ 7232 TEST_RET_ON_FAIL (test_lookup_pending_webhooks (&cls->instance, 7233 2, 7234 cls->pwebhooks)); 7235 /* Test pending webhook update */ 7236 TEST_RET_ON_FAIL (test_update_pending_webhook (&cls->instance, 7237 &cls->pwebhooks[0], 7238 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 7239 TEST_RET_ON_FAIL (test_lookup_future_webhook (&cls->instance, 7240 1, 7241 &cls->pwebhooks[1])); 7242 TEST_RET_ON_FAIL (test_update_pending_webhook (&cls->instance, 7243 &cls->pwebhooks[1], 7244 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 7245 // ??? 7246 TEST_RET_ON_FAIL (test_lookup_all_webhooks (&cls->instance, 7247 2, 7248 cls->pwebhooks)); 7249 7250 webhook_pending_serial0 = get_pending_serial (&cls->instance, 7251 &cls->pwebhooks[0]); 7252 webhook_pending_serial1 = get_pending_serial (&cls->instance, 7253 &cls->pwebhooks[1]); 7254 7255 /* Test webhook deletion */ 7256 TEST_RET_ON_FAIL (test_delete_pending_webhook (webhook_pending_serial1, 7257 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 7258 /* Test double deletion fails */ 7259 TEST_RET_ON_FAIL (test_delete_pending_webhook (webhook_pending_serial1, 7260 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS)); 7261 TEST_RET_ON_FAIL (test_delete_pending_webhook (webhook_pending_serial0, 7262 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)); 7263 TEST_RET_ON_FAIL (test_lookup_all_webhooks (&cls->instance, 7264 0, 7265 NULL)); 7266 return 0; 7267 } 7268 7269 7270 /** 7271 * Takes care of pending webhook testing. 7272 * 7273 * @return 0 on success, 1 otherwise. 7274 */ 7275 static int 7276 test_pending_webhooks (void) 7277 { 7278 struct TestPendingWebhooks_Closure test_cls; 7279 int test_result; 7280 7281 pre_test_pending_webhooks (&test_cls); 7282 test_result = run_test_pending_webhooks (&test_cls); 7283 post_test_pending_webhooks (&test_cls); 7284 return test_result; 7285 } 7286 7287 7288 /** 7289 * Function that runs all tests. 7290 * 7291 * @return 0 on success, 1 otherwise. 7292 */ 7293 static int 7294 run_tests (void) 7295 { 7296 TEST_RET_ON_FAIL (test_instances ()); 7297 TEST_RET_ON_FAIL (test_products ()); 7298 TEST_RET_ON_FAIL (test_orders ()); 7299 TEST_RET_ON_FAIL (test_deposits ()); 7300 TEST_RET_ON_FAIL (test_transfers ()); 7301 TEST_RET_ON_FAIL (test_refunds ()); 7302 TEST_RET_ON_FAIL (test_lookup_orders_all_filters ()); 7303 TEST_RET_ON_FAIL (test_kyc ()); 7304 TEST_RET_ON_FAIL (test_templates ()); 7305 TEST_RET_ON_FAIL (test_webhooks ()); 7306 TEST_RET_ON_FAIL (test_pending_webhooks ()); 7307 return 0; 7308 } 7309 7310 7311 /** 7312 * Main function that will be run by the scheduler. 7313 * 7314 * @param cls closure with config 7315 */ 7316 static void 7317 run (void *cls) 7318 { 7319 struct GNUNET_CONFIGURATION_Handle *cfg = cls; 7320 /* Data for 'store_payment()' */ 7321 7322 /* Drop the tables to cleanup anything that might cause issues */ 7323 if (NULL == (plugin = TALER_MERCHANTDB_plugin_load (cfg))) 7324 { 7325 result = 77; 7326 return; 7327 } 7328 (void) plugin->drop_tables (plugin->cls); 7329 if (GNUNET_OK != 7330 plugin->create_tables (plugin->cls)) 7331 { 7332 result = 77; 7333 return; 7334 } 7335 if (GNUNET_OK != 7336 plugin->connect (plugin->cls)) 7337 { 7338 GNUNET_break (0); 7339 result = 17; 7340 return; 7341 } 7342 7343 /* Run the preflight */ 7344 plugin->preflight (plugin->cls); 7345 7346 result = run_tests (); 7347 if (0 == result) 7348 /** result = run_test_templates (); 7349 if (0 == result)*/ 7350 { 7351 /* Test dropping tables */ 7352 if (GNUNET_OK != 7353 plugin->drop_tables (plugin->cls)) 7354 { 7355 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 7356 "Dropping tables failed\n"); 7357 result = 77; 7358 return; 7359 } 7360 } 7361 7362 TALER_MERCHANTDB_plugin_unload (plugin); 7363 plugin = NULL; 7364 } 7365 7366 7367 /** 7368 * Entry point for the tests. 7369 */ 7370 int 7371 main (int argc, 7372 char *const argv[]) 7373 { 7374 const char *plugin_name; 7375 char *config_filename; 7376 char *testname; 7377 struct GNUNET_CONFIGURATION_Handle *cfg; 7378 7379 result = -1; 7380 if (NULL == (plugin_name = strrchr (argv[0], 7381 (int) '-'))) 7382 { 7383 GNUNET_break (0); 7384 return -1; 7385 } 7386 GNUNET_log_setup (argv[0], "DEBUG", NULL); 7387 plugin_name++; 7388 (void) GNUNET_asprintf (&testname, 7389 "test-merchantdb-%s", 7390 plugin_name); 7391 (void) GNUNET_asprintf (&config_filename, 7392 "%s.conf", 7393 testname); 7394 fprintf (stdout, "Using %s\n", config_filename); 7395 cfg = GNUNET_CONFIGURATION_create (TALER_MERCHANT_project_data ()); 7396 if (GNUNET_OK != 7397 GNUNET_CONFIGURATION_parse (cfg, 7398 config_filename)) 7399 { 7400 GNUNET_break (0); 7401 GNUNET_free (config_filename); 7402 GNUNET_free (testname); 7403 return 2; 7404 } 7405 GNUNET_SCHEDULER_run (&run, 7406 cfg); 7407 GNUNET_CONFIGURATION_destroy (cfg); 7408 GNUNET_free (config_filename); 7409 GNUNET_free (testname); 7410 return result; 7411 } 7412 7413 7414 /* end of test_merchantdb.c */