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