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