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