merchant

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

test_merchantdb.c (245250B)


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