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