merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

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                                         &not_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 */