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