merchant

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

test_merchantdb.c (389226B)


      1 /*
      2   This file is part of TALER
      3   (C) 2014-2024 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Lesser General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file src/backenddb/test_merchantdb.c
     18  * @brief testcase for merchant's postgres db plugin
     19  * @author Marcello Stanisci
     20  * @author Christian Grothoff
     21  * @author Pricilla Huang
     22  */
     23 #include "platform.h"
     24 #include "microhttpd.h"
     25 #include <taler/taler_util.h>
     26 #include <taler/taler_json_lib.h>
     27 #include <taler/taler_pq_lib.h>
     28 #include <taler/taler_signatures.h>
     29 #include "taler/taler_merchant_util.h"
     30 #include "merchantdb_lib.h"
     31 #include "helper.h"
     32 #include "merchant-database/gc.h"
     33 #include "merchant-database/start.h"
     34 #include "merchant-database/iterate_statistic_bucket_amounts.h"
     35 #include "merchant-database/iterate_statistic_bucket_counters.h"
     36 #include "merchant-database/iterate_kyc_statuses.h"
     37 #include "merchant-database/insert_kyc_status.h"
     38 #include "merchant-database/delete_contract_terms.h"
     39 #include "merchant-database/update_to_instance_private_key_deleted.h"
     40 #include "merchant-database/delete_order.h"
     41 #include "merchant-database/delete_pending_webhook.h"
     42 #include "merchant-database/delete_product.h"
     43 #include "merchant-database/delete_template.h"
     44 #include "merchant-database/delete_webhook.h"
     45 #include "merchant-database/update_to_account_inactive.h"
     46 #include "merchant-database/do_increase_refund.h"
     47 #include "merchant-database/insert_account.h"
     48 #include "merchant-database/insert_category.h"
     49 #include "merchant-database/update_category.h"
     50 #include "merchant-database/insert_contract_terms.h"
     51 #include "merchant-database/insert_deposit.h"
     52 #include "merchant-database/insert_deposit_confirmation.h"
     53 #include "merchant-database/insert_deposit_to_transfer.h"
     54 #include "merchant-database/insert_exchange_signing_key.h"
     55 #include "merchant-database/insert_instance.h"
     56 #include "merchant-database/insert_mfa_challenge.h"
     57 #include "merchant-database/do_solve_mfa_challenge.h"
     58 #include "merchant-database/insert_order.h"
     59 #include "merchant-database/insert_order_lock.h"
     60 #include "merchant-database/insert_otp_device.h"
     61 #include "merchant-database/insert_pending_webhook.h"
     62 #include "merchant-database/insert_product.h"
     63 #include "merchant-database/insert_refund_proof.h"
     64 #include "merchant-database/insert_report.h"
     65 #include "merchant-database/insert_template.h"
     66 #include "merchant-database/insert_transfer.h"
     67 #include "merchant-database/insert_transfer_details.h"
     68 #include "merchant-database/insert_webhook.h"
     69 #include "merchant-database/insert_inventory_lock.h"
     70 #include "merchant-database/insert_issued_token.h"
     71 #include "merchant-database/insert_used_token.h"
     72 #include "merchant-database/insert_token_family.h"
     73 #include "merchant-database/insert_token_family_key.h"
     74 #include "merchant-database/get_token_family.h"
     75 #include "merchant-database/delete_token_family.h"
     76 #include "merchant-database/get_account_serial.h"
     77 #include "merchant-database/get_contract_terms.h"
     78 #include "merchant-database/get_contract_terms_status.h"
     79 #include "merchant-database/iterate_deposits.h"
     80 #include "merchant-database/iterate_deposits_by_contract_and_coin.h"
     81 #include "merchant-database/iterate_deposits_by_order.h"
     82 #include "merchant-database/iterate_instances.h"
     83 #include "merchant-database/get_order.h"
     84 #include "merchant-database/get_order_by_fulfillment.h"
     85 #include "merchant-database/get_order_status.h"
     86 #include "merchant-database/iterate_orders.h"
     87 #include "merchant-database/iterate_pending_deposits.h"
     88 #include "merchant-database/iterate_pending_webhooks_above_serial_id.h"
     89 #include "merchant-database/iterate_pending_webhooks.h"
     90 #include "merchant-database/get_product.h"
     91 #include "merchant-database/iterate_products.h"
     92 #include "merchant-database/get_refund_proof.h"
     93 #include "merchant-database/iterate_refunds.h"
     94 #include "merchant-database/iterate_refunds_detailed.h"
     95 #include "merchant-database/get_template.h"
     96 #include "merchant-database/iterate_templates.h"
     97 #include "merchant-database/iterate_transfer_details.h"
     98 #include "merchant-database/iterate_transfer_details_by_order.h"
     99 #include "merchant-database/iterate_transfer_summaries.h"
    100 #include "merchant-database/iterate_transfers.h"
    101 #include "merchant-database/get_webhook.h"
    102 #include "merchant-database/iterate_webhooks_by_event.h"
    103 #include "merchant-database/iterate_webhooks.h"
    104 #include "merchant-database/get_exchange_wire_fee.h"
    105 #include "merchant-database/get_instance_auth.h"
    106 #include "merchant-database/update_to_contract_terms_paid.h"
    107 #include "merchant-database/update_to_contract_terms_wired.h"
    108 #include "merchant-database/insert_refund_by_coin.h"
    109 #include "merchant-database/set_instance.h"
    110 #include "merchant-database/insert_exchange_wire_fee.h"
    111 #include "merchant-database/delete_inventory_lock.h"
    112 #include "merchant-database/update_contract_terms.h"
    113 #include "merchant-database/update_instance.h"
    114 #include "merchant-database/update_pending_webhook.h"
    115 #include "merchant-database/update_product.h"
    116 #include "merchant-database/update_template.h"
    117 #include "merchant-database/update_webhook.h"
    118 #include "merchant-database/create_tables.h"
    119 #include "merchant-database/drop_tables.h"
    120 #include "merchant-database/preflight.h"
    121 #include "merchant-database/delete_instance.h"
    122 
    123 
    124 /**
    125  * Global return value for the test.  Initially -1, set to 0 upon
    126  * completion.  Other values indicate some kind of error.
    127  */
    128 static int result;
    129 
    130 /**
    131  * Handle to the database we are testing.
    132  */
    133 static struct TALER_MERCHANTDB_PostgresContext *pg;
    134 
    135 /**
    136  * Configuration of the database we are testing.  Used by tests that
    137  * need a second, concurrent connection.
    138  */
    139 static const struct GNUNET_CONFIGURATION_Handle *test_cfg;
    140 
    141 /**
    142  * @param test 0 on success, non-zero on failure
    143  */
    144 #define TEST_WITH_FAIL_CLAUSE(test, on_fail) \
    145         if ((test)) \
    146         { \
    147           GNUNET_break (0); \
    148           on_fail \
    149         }
    150 
    151 #define TEST_COND_RET_ON_FAIL(cond, msg) \
    152         if (! (cond)) \
    153         { \
    154           GNUNET_break (0); \
    155           GNUNET_log (GNUNET_ERROR_TYPE_ERROR, \
    156                       msg); \
    157           return 1; \
    158         }
    159 
    160 /**
    161  * @param __test 0 on success, non-zero on failure
    162  */
    163 #define TEST_RET_ON_FAIL(__test) \
    164         TEST_WITH_FAIL_CLAUSE (__test, \
    165                                return 1; \
    166                                )
    167 
    168 /**
    169  * Activate the per-instance schema for @a iid before invoking any
    170  * per-instance MERCHANTDB function.  Returns 1 from the enclosing
    171  * test helper if routing fails for an unexpected reason.
    172  *
    173  * If @a iid does not resolve to an existing instance, set_instance
    174  * returns NO_RESULTS.  In that case the underlying per-instance call
    175  * could never have run, so we treat it as the test outcome: when the
    176  * caller expected NO_RESULTS, return 0 (pass); otherwise return 1.
    177  *
    178  * @param iid C-string instance identifier
    179  * @param expected expected GNUNET_DB_QueryStatus from the wrapped call
    180  */
    181 #define TEST_SET_INSTANCE(iid, expected) \
    182         do { \
    183           enum GNUNET_DB_QueryStatus _si_qs; \
    184           _si_qs = TALER_MERCHANTDB_set_instance (pg, (iid)); \
    185           if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == _si_qs) \
    186           { \
    187             if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == (expected)) \
    188             return 0; \
    189             GNUNET_break (0); \
    190             GNUNET_log (GNUNET_ERROR_TYPE_ERROR, \
    191                         "set_instance(%s): instance missing, " \
    192                         "expected qs=%d\n", \
    193                         (iid), \
    194                         (int) (expected)); \
    195             return 1; \
    196           } \
    197           if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != _si_qs) \
    198           { \
    199             GNUNET_break (0); \
    200             GNUNET_log (GNUNET_ERROR_TYPE_ERROR, \
    201                         "set_instance(%s) failed (qs=%d)\n", \
    202                         (iid), \
    203                         (int) _si_qs); \
    204             return 1; \
    205           } \
    206         } while (0)
    207 
    208 
    209 /**
    210  * Runs @a sql directly on the database connection of the test.  Used to
    211  * set up or inspect state that has no dedicated MERCHANTDB entry point,
    212  * such as the notification settings of an instance or the rows queued by
    213  * a trigger.
    214  *
    215  * @param sql the SQL statement(s) to execute
    216  * @return 0 on success, 1 otherwise.
    217  */
    218 static int
    219 exec_sql (const char *sql)
    220 {
    221   struct GNUNET_PQ_ExecuteStatement es[] = {
    222     GNUNET_PQ_make_execute (sql),
    223     GNUNET_PQ_EXECUTE_STATEMENT_END
    224   };
    225 
    226   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
    227                          GNUNET_PQ_exec_statements (pg->conn,
    228                                                     es),
    229                          "Direct SQL execution failed\n");
    230   return 0;
    231 }
    232 
    233 
    234 /**
    235  * Evaluates @a sql, which must be a parameter-less query returning
    236  * exactly one row with a single INT8 column named "num".
    237  *
    238  * @param sql the query to run
    239  * @param[out] num set to the value returned
    240  * @return 0 on success, 1 otherwise.
    241  */
    242 static int
    243 query_sql_num (const char *sql,
    244                uint64_t *num)
    245 {
    246   struct GNUNET_PQ_QueryParam params[] = {
    247     GNUNET_PQ_query_param_end
    248   };
    249   struct GNUNET_PQ_ResultSpec rs[] = {
    250     GNUNET_PQ_result_spec_uint64 ("num",
    251                                   num),
    252     GNUNET_PQ_result_spec_end
    253   };
    254 
    255   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
    256                          GNUNET_PQ_prepare_anon (pg->conn,
    257                                                  sql),
    258                          "Preparing direct query failed\n");
    259   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    260                          GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    261                                                                    "",
    262                                                                    params,
    263                                                                    rs),
    264                          "Direct query failed\n");
    265   return 0;
    266 }
    267 
    268 
    269 /**
    270  * Evaluates @a sql, which must be a parameter-less query returning
    271  * exactly one row with a single TEXT column named "txt".
    272  *
    273  * @param sql the query to run
    274  * @param[out] txt set to the value returned, to be freed by the caller
    275  * @return 0 on success, 1 otherwise.
    276  */
    277 static int
    278 query_sql_text (const char *sql,
    279                 char **txt)
    280 {
    281   struct GNUNET_PQ_QueryParam params[] = {
    282     GNUNET_PQ_query_param_end
    283   };
    284   struct GNUNET_PQ_ResultSpec rs[] = {
    285     GNUNET_PQ_result_spec_string ("txt",
    286                                   txt),
    287     GNUNET_PQ_result_spec_end
    288   };
    289 
    290   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
    291                          GNUNET_PQ_prepare_anon (pg->conn,
    292                                                  sql),
    293                          "Preparing direct query failed\n");
    294   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    295                          GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    296                                                                    "",
    297                                                                    params,
    298                                                                    rs),
    299                          "Direct query failed\n");
    300   return 0;
    301 }
    302 
    303 
    304 /* ********** Instances ********** */
    305 
    306 
    307 /**
    308  * Container for instance settings along with keys.
    309  */
    310 struct InstanceData
    311 {
    312   /**
    313    * The instance settings.
    314    */
    315   struct TALER_MERCHANTDB_InstanceSettings instance;
    316 
    317   /**
    318    * The public key for the instance.
    319    */
    320   struct TALER_MerchantPublicKeyP merchant_pub;
    321 
    322   /**
    323    * The private key for the instance.
    324    */
    325   struct TALER_MerchantPrivateKeyP merchant_priv;
    326 };
    327 
    328 
    329 /**
    330  * Creates data for an instance to use with testing.
    331  *
    332  * @param instance_id the identifier for this instance.
    333  * @param instance the instance data to be filled.
    334  */
    335 static void
    336 make_instance (const char *instance_id,
    337                struct InstanceData *instance)
    338 {
    339   memset (instance,
    340           0,
    341           sizeof (*instance));
    342   GNUNET_CRYPTO_eddsa_key_create (&instance->merchant_priv.eddsa_priv);
    343   GNUNET_CRYPTO_eddsa_key_get_public (&instance->merchant_priv.eddsa_priv,
    344                                       &instance->merchant_pub.eddsa_pub);
    345   instance->instance.id = (char *) instance_id;
    346   instance->instance.name = (char *) "Test";
    347   instance->instance.address = json_array ();
    348   GNUNET_assert (NULL != instance->instance.address);
    349   GNUNET_assert (0 == json_array_append_new (instance->instance.address,
    350                                              json_string ("123 Example St")));
    351   instance->instance.jurisdiction = json_array ();
    352   GNUNET_assert (NULL != instance->instance.jurisdiction);
    353   GNUNET_assert (0 == json_array_append_new (instance->instance.jurisdiction,
    354                                              json_string ("Ohio")));
    355   instance->instance.use_stefan = true;
    356   instance->instance.default_wire_transfer_delay =
    357     GNUNET_TIME_relative_get_minute_ ();
    358   instance->instance.default_pay_delay = GNUNET_TIME_UNIT_SECONDS;
    359   instance->instance.default_refund_delay = GNUNET_TIME_UNIT_MINUTES;
    360 }
    361 
    362 
    363 /**
    364  * Frees memory allocated when creating an instance for testing.
    365  *
    366  * @param instance the instance containing the memory to be freed.
    367  */
    368 static void
    369 free_instance_data (struct InstanceData *instance)
    370 {
    371   json_decref (instance->instance.address);
    372   json_decref (instance->instance.jurisdiction);
    373 }
    374 
    375 
    376 /**
    377  * Creates an account with test data for an instance.
    378  *
    379  * @param account the account to initialize.
    380  */
    381 static void
    382 make_account (struct TALER_MERCHANTDB_AccountDetails *account)
    383 {
    384   memset (account,
    385           0,
    386           sizeof (*account));
    387   GNUNET_CRYPTO_random_block (&account->h_wire.hash,
    388                               sizeof account->h_wire.hash);
    389   GNUNET_CRYPTO_random_block (&account->salt,
    390                               sizeof (account->salt));
    391   account->payto_uri.full_payto
    392     = (char *) "payto://x-taler-bank/bank.demo.taler.net/4";
    393   account->active = true;
    394 }
    395 
    396 
    397 /**
    398  * Instance settings along with corresponding accounts.
    399  */
    400 struct InstanceWithAccounts
    401 {
    402   /**
    403    * Pointer to the instance settings.
    404    */
    405   const struct TALER_MERCHANTDB_InstanceSettings *instance;
    406 
    407   /**
    408    * Length of the array of accounts.
    409    */
    410   unsigned int accounts_length;
    411 
    412   /**
    413    * Pointer to the array of accounts.
    414    */
    415   const struct TALER_MERCHANTDB_AccountDetails *accounts;
    416 
    417 };
    418 
    419 
    420 /**
    421  * Closure for testing instance lookup.
    422  */
    423 struct TestLookupInstances_Closure
    424 {
    425   /**
    426    * Number of instances to compare to.
    427    */
    428   unsigned int instances_to_cmp_length;
    429 
    430   /**
    431    * Pointer to array of instances.
    432    */
    433   const struct InstanceWithAccounts *instances_to_cmp;
    434 
    435   /**
    436    * Pointer to array of number of matches for each instance.
    437    */
    438   unsigned int *results_matching;
    439 
    440   /**
    441    * Total number of results returned.
    442    */
    443   unsigned int results_length;
    444 };
    445 
    446 
    447 /**
    448  * Compares two instances for equality.
    449  *
    450  * @param a the first instance.
    451  * @param b the second instance.
    452  * @return 0 on equality, 1 otherwise.
    453  */
    454 static int
    455 check_instances_equal (const struct TALER_MERCHANTDB_InstanceSettings *a,
    456                        const struct TALER_MERCHANTDB_InstanceSettings *b)
    457 {
    458   if ((0 != strcmp (a->id,
    459                     b->id)) ||
    460       (0 != strcmp (a->name,
    461                     b->name)) ||
    462       (1 != json_equal (a->address,
    463                         b->address)) ||
    464       (1 != json_equal (a->jurisdiction,
    465                         b->jurisdiction)) ||
    466       (a->use_stefan != b->use_stefan) ||
    467       (a->default_wire_transfer_delay.rel_value_us !=
    468        b->default_wire_transfer_delay.rel_value_us) ||
    469       (a->default_pay_delay.rel_value_us != b->default_pay_delay.rel_value_us))
    470     return 1;
    471   return 0;
    472 }
    473 
    474 
    475 #if 0
    476 /**
    477  * Compares two accounts for equality.
    478  *
    479  * @param a the first account.
    480  * @param b the second account.
    481  * @return 0 on equality, 1 otherwise.
    482  */
    483 static int
    484 check_accounts_equal (const struct TALER_MERCHANTDB_AccountDetails *a,
    485                       const struct TALER_MERCHANTDB_AccountDetails *b)
    486 {
    487   if ((0 != GNUNET_memcmp (&a->h_wire,
    488                            &b->h_wire)) ||
    489       (0 != GNUNET_memcmp (&a->salt,
    490                            &b->salt)) ||
    491       (0 != TALER_full_payto_cmp (a->payto_uri,
    492                                   b->payto_uri)) ||
    493       (a->active != b->active))
    494     return 1;
    495   return 0;
    496 }
    497 
    498 
    499 #endif
    500 
    501 
    502 /**
    503  * Called after testing 'iterate_instances'.
    504  *
    505  * @param cls pointer to 'struct TestLookupInstances_Closure'.
    506  * @param merchant_pub public key of the instance
    507  * @param merchant_priv private key of the instance, NULL if not available
    508  * @param is general instance settings
    509  * @param ias instance authentication settings
    510 */
    511 static void
    512 lookup_instances_cb (void *cls,
    513                      const struct TALER_MerchantPublicKeyP *merchant_pub,
    514                      const struct TALER_MerchantPrivateKeyP *merchant_priv,
    515                      const struct TALER_MERCHANTDB_InstanceSettings *is,
    516                      const struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
    517 {
    518   struct TestLookupInstances_Closure *cmp = cls;
    519 
    520   if (NULL == cmp)
    521     return;
    522   cmp->results_length += 1;
    523   /* Look through the closure and test each instance for equality */
    524   for (unsigned int i = 0; cmp->instances_to_cmp_length > i; ++i)
    525   {
    526     if (0 != check_instances_equal (cmp->instances_to_cmp[i].instance,
    527                                     is))
    528       continue;
    529     cmp->results_matching[i] += 1;
    530   }
    531 }
    532 
    533 
    534 /**
    535  * Tests @e insert_instance.
    536  *
    537  * @param instance the instance data to insert.
    538  * @param expected_result the result that should be returned from the DB.
    539  * @return 0 on success, 1 on failure.
    540  */
    541 static int
    542 test_insert_instance (const struct InstanceData *instance,
    543                       enum GNUNET_DB_QueryStatus expected_result)
    544 {
    545   struct TALER_MERCHANTDB_InstanceAuthSettings ias = { 0 };
    546 
    547   TEST_COND_RET_ON_FAIL (expected_result ==
    548                          TALER_MERCHANTDB_insert_instance (pg,
    549                                                            &instance->merchant_pub,
    550                                                            &instance->merchant_priv,
    551                                                            &instance->instance,
    552                                                            &ias),
    553                          "Insert instance failed\n");
    554   return 0;
    555 }
    556 
    557 
    558 /**
    559  * Tests @e update_instance.
    560  *
    561  * @param updated_data the instance data to update the row in the database to.
    562  * @param expected_result the result that should be returned from the DB.
    563  * @return 0 on success, 1 on failure.
    564  */
    565 static int
    566 test_update_instance (const struct InstanceData *updated_data,
    567                       enum GNUNET_DB_QueryStatus expected_result)
    568 {
    569   TEST_COND_RET_ON_FAIL (expected_result ==
    570                          TALER_MERCHANTDB_update_instance (pg,
    571                                                            &updated_data->instance),
    572                          "Update instance failed\n");
    573   return 0;
    574 }
    575 
    576 
    577 /**
    578  * Tests @e iterate_instances.
    579  *
    580  * @param active_only whether to lookup all instance, or only active ones.
    581  * @param instances_length number of instances to compare to in @e instances.
    582  * @param instances a list of instances that will be compared with the results
    583  *        found.
    584  * @return 0 on success, 1 otherwise.
    585  */
    586 static int
    587 test_lookup_instances (bool active_only,
    588                        unsigned int instances_length,
    589                        struct InstanceWithAccounts instances[])
    590 {
    591   unsigned int results_matching[GNUNET_NZL (instances_length)];
    592   struct TestLookupInstances_Closure cmp = {
    593     .instances_to_cmp_length = instances_length,
    594     .instances_to_cmp = instances,
    595     .results_matching = results_matching,
    596     .results_length = 0
    597   };
    598   memset (results_matching, 0, sizeof (unsigned int) * instances_length);
    599   if (0 > TALER_MERCHANTDB_iterate_instances (pg,
    600                                               active_only,
    601                                               &lookup_instances_cb,
    602                                               &cmp))
    603   {
    604     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    605                 "Lookup instances failed\n");
    606     return 1;
    607   }
    608   if (instances_length != cmp.results_length)
    609   {
    610     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    611                 "Lookup instances failed: incorrect number of results\n");
    612     return 1;
    613   }
    614   for (unsigned int i = 0; instances_length > i; ++i)
    615   {
    616     if (1 != cmp.results_matching[i])
    617     {
    618       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    619                   "Lookup instances failed: mismatched data\n");
    620       return 1;
    621     }
    622   }
    623   return 0;
    624 }
    625 
    626 
    627 /**
    628  * Tests removing the private key of the given instance from the database.
    629  *
    630  * @param instance the instance whose private key to delete.
    631  * @param expected_result the result we expect the db to return.
    632  * @return 0 on success, 1 otherwise.
    633  */
    634 static int
    635 test_delete_instance_private_key (const struct InstanceData *instance,
    636                                   enum GNUNET_DB_QueryStatus expected_result)
    637 {
    638   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    639   TEST_COND_RET_ON_FAIL (expected_result ==
    640                          TALER_MERCHANTDB_update_to_instance_private_key_deleted (
    641                            pg,
    642                            instance->instance.id),
    643                          "Delete instance private key failed\n");
    644   return 0;
    645 }
    646 
    647 
    648 /**
    649  * Tests purging all data for an instance from the database.
    650  *
    651  * @param instance the instance to purge.
    652  * @param expected_result the result we expect the db to return.
    653  * @return 0 on success, 1 otherwise.
    654  */
    655 static int
    656 test_purge_instance (const struct InstanceData *instance,
    657                      enum GNUNET_DB_QueryStatus expected_result)
    658 {
    659   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    660   TEST_COND_RET_ON_FAIL (expected_result ==
    661                          TALER_MERCHANTDB_delete_instance (pg,
    662                                                            instance->instance.id),
    663                          "Purge instance failed\n");
    664   return 0;
    665 }
    666 
    667 
    668 /**
    669  * Tests inserting an account for a merchant instance.
    670  *
    671  * @param instance the instance to associate the account with.
    672  * @param account the account to insert.
    673  * @param expected_result the result expected from the db.
    674  * @return 0 on success, 1 otherwise.
    675  */
    676 static int
    677 test_insert_account (const struct InstanceData *instance,
    678                      const struct TALER_MERCHANTDB_AccountDetails *account,
    679                      enum GNUNET_DB_QueryStatus expected_result)
    680 {
    681   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    682   TEST_COND_RET_ON_FAIL (expected_result ==
    683                          TALER_MERCHANTDB_insert_account (pg,
    684                                                           account),
    685                          "Insert account failed\n");
    686   return 0;
    687 }
    688 
    689 
    690 /**
    691  * Tests deactivating an account.
    692  *
    693  * @param account the account to deactivate.
    694  * @param expected_result the result expected from the DB.
    695  * @return 0 on success, 1 otherwise.
    696  */
    697 static int
    698 test_inactivate_account (const struct InstanceData *instance,
    699                          const struct TALER_MERCHANTDB_AccountDetails *account,
    700                          enum GNUNET_DB_QueryStatus expected_result)
    701 {
    702   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    703   TEST_COND_RET_ON_FAIL (expected_result ==
    704                          TALER_MERCHANTDB_update_to_account_inactive (pg,
    705                                                                       instance->instance.id,
    706                                                                       &account->h_wire),
    707                          "Deactivate account failed\n");
    708   return 0;
    709 }
    710 
    711 
    712 /**
    713  * Closure for instance tests
    714  */
    715 struct TestInstances_Closure
    716 {
    717   /**
    718    * The list of instances that we use for testing instances.
    719    */
    720   struct InstanceData instances[2];
    721 
    722   /**
    723    * The list of accounts to use with the instances.
    724    */
    725   struct TALER_MERCHANTDB_AccountDetails accounts[2];
    726 
    727 };
    728 
    729 
    730 /**
    731  * Sets up the data structures used in the instance tests
    732  *
    733  * @cls the closure to initialize with test data.
    734  */
    735 static void
    736 pre_test_instances (struct TestInstances_Closure *cls)
    737 {
    738   /* Instance */
    739   make_instance ("test_instances_inst0",
    740                  &cls->instances[0]);
    741   make_instance ("test_instances_inst1",
    742                  &cls->instances[1]);
    743 
    744   /* Accounts */
    745   make_account (&cls->accounts[0]);
    746   cls->accounts[0].instance_id
    747     = cls->instances[0].instance.id;
    748   make_account (&cls->accounts[1]);
    749   cls->accounts[1].instance_id
    750     = cls->instances[1].instance.id;
    751 }
    752 
    753 
    754 /**
    755  * Handles all teardown after testing
    756  *
    757  * @cls the closure to free data from
    758  */
    759 static void
    760 post_test_instances (struct TestInstances_Closure *cls)
    761 {
    762   free_instance_data (&cls->instances[0]);
    763   free_instance_data (&cls->instances[1]);
    764 }
    765 
    766 
    767 /**
    768  * Tests that a per-instance operation still resolves against the
    769  * per-instance schema after the database connection was dropped and
    770  * re-established.
    771  *
    772  * Regression test: reconnect_cb() re-issues 'SET search_path TO
    773  * merchant' but used to leave @e current_merchant_id set, so the
    774  * TALER_MERCHANTDB_set_instance() the dispatcher does for the next
    775  * request found the instance already selected and returned without
    776  * re-issuing the per-instance search_path.  Every unqualified
    777  * statement of that instance then hit schema 'merchant' and failed
    778  * with 42P01 (HTTP 500) until a different instance was touched.
    779  *
    780  * @param instance the instance to run the test against.
    781  * @return 0 when successful, 1 otherwise.
    782  */
    783 static int
    784 test_reconnect_search_path (const struct InstanceData *instance)
    785 {
    786   json_t *i18n = json_object ();
    787   struct TALER_MERCHANTDB_PostgresContext *cpg;
    788   uint64_t backend_pid;
    789   uint64_t gen;
    790   uint64_t cat;
    791   char sql[128];
    792   int ret = 1;
    793 
    794   GNUNET_assert (NULL != i18n);
    795   TEST_SET_INSTANCE (instance->instance.id,
    796                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    797   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    798       TALER_MERCHANTDB_insert_category (pg,
    799                                         instance->instance.id,
    800                                         "reconnect-before",
    801                                         i18n,
    802                                         &cat))
    803   {
    804     GNUNET_break (0);
    805     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    806                 "Insert category before reconnect failed\n");
    807     goto cleanup;
    808   }
    809   /* Find out which backend serves our connection. */
    810   {
    811     struct GNUNET_PQ_QueryParam params[] = {
    812       GNUNET_PQ_query_param_end
    813     };
    814     struct GNUNET_PQ_ResultSpec rs[] = {
    815       GNUNET_PQ_result_spec_uint64 ("pid",
    816                                     &backend_pid),
    817       GNUNET_PQ_result_spec_end
    818     };
    819 
    820     if ( (GNUNET_OK !=
    821           GNUNET_PQ_prepare_anon (pg->conn,
    822                                   "SELECT pg_backend_pid()::INT8 AS pid")) ||
    823          (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    824           GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    825                                                     "",
    826                                                     params,
    827                                                     rs)) )
    828     {
    829       GNUNET_break (0);
    830       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    831                   "Failed to determine backend PID\n");
    832       goto cleanup;
    833     }
    834   }
    835   /* Kill our own backend from a second connection, the way a
    836      PostgreSQL restart or a pgbouncer recycle would. */
    837   GNUNET_snprintf (sql,
    838                    sizeof (sql),
    839                    "DO $$ BEGIN"
    840                    " PERFORM pg_terminate_backend(%llu);"
    841                    " END $$",
    842                    (unsigned long long) backend_pid);
    843   cpg = TALER_MERCHANTDB_connect (test_cfg);
    844   if (NULL == cpg)
    845   {
    846     GNUNET_break (0);
    847     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    848                 "Failed to open second database connection\n");
    849     goto cleanup;
    850   }
    851   {
    852     struct GNUNET_PQ_ExecuteStatement es[] = {
    853       GNUNET_PQ_make_execute (sql),
    854       GNUNET_PQ_EXECUTE_STATEMENT_END
    855     };
    856     enum GNUNET_GenericReturnValue res;
    857 
    858     res = GNUNET_PQ_exec_statements (cpg->conn,
    859                                      es);
    860     TALER_MERCHANTDB_disconnect (cpg);
    861     if (GNUNET_OK != res)
    862     {
    863       GNUNET_break (0);
    864       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    865                   "Failed to terminate our own backend\n");
    866       goto cleanup;
    867     }
    868   }
    869   gen = TMH_PG_prep_gen_;
    870   for (unsigned int i = 0; i<10; i++)
    871   {
    872     GNUNET_PQ_reconnect_if_down (pg->conn);
    873     if (gen != TMH_PG_prep_gen_)
    874       break;
    875     sleep (1);
    876   }
    877   if (gen == TMH_PG_prep_gen_)
    878   {
    879     GNUNET_break (0);
    880     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    881                 "Database did not reconnect\n");
    882     goto cleanup;
    883   }
    884   /* This is what the dispatcher does for the next request; it must
    885      restore the per-instance search_path. */
    886   TEST_SET_INSTANCE (instance->instance.id,
    887                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    888   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    889       TALER_MERCHANTDB_insert_category (pg,
    890                                         instance->instance.id,
    891                                         "reconnect-after",
    892                                         i18n,
    893                                         &cat))
    894   {
    895     GNUNET_break (0);
    896     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    897                 "Insert category after reconnect failed\n");
    898     goto cleanup;
    899   }
    900   ret = 0;
    901 cleanup:
    902   json_decref (i18n);
    903   return ret;
    904 }
    905 
    906 
    907 /**
    908  * Tests that an instance whose 'auth_hash'/'auth_salt' are NULL is
    909  * read back as "no password configured" instead of failing.
    910  *
    911  * Both columns are nullable, but the result spec did not allow NULL,
    912  * so such a row made TALER_MERCHANTDB_get_instance_auth() return a
    913  * hard error (HTTP 500 on every authenticated request of that
    914  * instance).  No code path in the tree writes NULL today -- the
    915  * "no password" state is an all-zero hash -- so the NULL is set here
    916  * directly via SQL.
    917  *
    918  * @param instance the instance to run the test against.
    919  * @return 0 on success, 1 otherwise.
    920  */
    921 static int
    922 test_get_instance_auth_null (const struct InstanceData *instance)
    923 {
    924   struct TALER_MERCHANTDB_InstanceAuthSettings ias;
    925   char sql[256];
    926 
    927   GNUNET_snprintf (sql,
    928                    sizeof (sql),
    929                    "UPDATE merchant.merchant_instances"
    930                    " SET auth_hash=NULL"
    931                    "    ,auth_salt=NULL"
    932                    " WHERE merchant_id='%s'",
    933                    instance->instance.id);
    934   {
    935     struct GNUNET_PQ_ExecuteStatement es[] = {
    936       GNUNET_PQ_make_execute (sql),
    937       GNUNET_PQ_EXECUTE_STATEMENT_END
    938     };
    939 
    940     TEST_COND_RET_ON_FAIL (GNUNET_OK ==
    941                            GNUNET_PQ_exec_statements (pg->conn,
    942                                                       es),
    943                            "Failed to NULL out the instance authentication\n");
    944   }
    945   memset (&ias,
    946           42,
    947           sizeof (ias));
    948   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    949                          TALER_MERCHANTDB_get_instance_auth (
    950                            pg,
    951                            instance->instance.id,
    952                            &ias),
    953                          "Lookup of NULL instance authentication failed\n");
    954   TEST_COND_RET_ON_FAIL (GNUNET_is_zero (&ias.auth_hash) &&
    955                          GNUNET_is_zero (&ias.auth_salt),
    956                          "NULL instance authentication was not zeroed out\n");
    957   return 0;
    958 }
    959 
    960 
    961 /**
    962  * Tests that storing a report always yields a fresh serial.
    963  *
    964  * 'merchant_reports' has no unique key other than the identity
    965  * column 'report_serial' that the INSERT never supplies, so the
    966  * 'ON CONFLICT DO NOTHING' the INSERT used to carry could never
    967  * fire.  Should a deduplicating key ever be introduced, the INSERT
    968  * must report it (as 'no results') rather than silently return no
    969  * serial: POST /private/reports would otherwise answer 200 OK with
    970  * an uninitialised 'report_serial_id'.
    971  *
    972  * @param instance the instance to store the reports for.
    973  * @return 0 on success, 1 otherwise.
    974  */
    975 static int
    976 test_insert_report_serial (const struct InstanceData *instance)
    977 {
    978   uint64_t report_ids[3];
    979 
    980   TEST_SET_INSTANCE (instance->instance.id,
    981                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    982   for (unsigned int i = 0; i<3; i++)
    983   {
    984     report_ids[i] = 0;
    985     TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    986                            TALER_MERCHANTDB_insert_report (
    987                              pg,
    988                              instance->instance.id,
    989                              "report-generator-test",
    990                              "a test report",
    991                              "text/plain",
    992                              "/private/orders",
    993                              "test@example.com",
    994                              GNUNET_TIME_UNIT_DAYS,
    995                              GNUNET_TIME_UNIT_ZERO,
    996                              &report_ids[i]),
    997                            "Insert report failed\n");
    998     TEST_COND_RET_ON_FAIL (0 != report_ids[i],
    999                            "Insert report did not return a serial\n");
   1000     for (unsigned int j = 0; j<i; j++)
   1001       TEST_COND_RET_ON_FAIL (report_ids[i] != report_ids[j],
   1002                              "Insert report returned a duplicate serial\n");
   1003   }
   1004   return 0;
   1005 }
   1006 
   1007 
   1008 /**
   1009  * Function that tests instances.
   1010  *
   1011  * @param cls closure with config
   1012  * @return 0 on success, 1 if failure.
   1013  */
   1014 static int
   1015 run_test_instances (struct TestInstances_Closure *cls)
   1016 {
   1017   struct InstanceWithAccounts instances[2] = {
   1018     {
   1019       .accounts_length = 0,
   1020       .accounts = cls->accounts,
   1021       .instance = &cls->instances[0].instance
   1022     },
   1023     {
   1024       .accounts_length = 0,
   1025       .accounts = cls->accounts,
   1026       .instance = &cls->instances[1].instance
   1027     }
   1028   };
   1029   uint64_t account_serial;
   1030 
   1031   /* Test inserting an instance */
   1032   TEST_RET_ON_FAIL (test_insert_instance (&cls->instances[0],
   1033                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   1034   /* Test double insertion fails */
   1035   TEST_RET_ON_FAIL (test_insert_instance (&cls->instances[0],
   1036                                           GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   1037   /* Test lookup instances- is our new instance there? */
   1038   TEST_RET_ON_FAIL (test_lookup_instances (false,
   1039                                            1,
   1040                                            instances));
   1041   /* Test update instance */
   1042   cls->instances[0].instance.name = "Test - updated";
   1043   json_array_append_new (cls->instances[0].instance.address,
   1044                          json_pack ("{s:s, s:I}",
   1045                                     "this", "is",
   1046                                     "more data", 47));
   1047   json_array_append_new (cls->instances[0].instance.jurisdiction,
   1048                          json_pack ("{s:s}",
   1049                                     "vegetables", "bad"));
   1050   cls->instances[0].instance.use_stefan = false;
   1051   cls->instances[0].instance.default_wire_transfer_delay =
   1052     GNUNET_TIME_UNIT_HOURS;
   1053   cls->instances[0].instance.default_pay_delay = GNUNET_TIME_UNIT_MINUTES;
   1054 
   1055   TEST_RET_ON_FAIL (test_update_instance (&cls->instances[0],
   1056                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   1057   TEST_RET_ON_FAIL (test_lookup_instances (false,
   1058                                            1,
   1059                                            instances));
   1060   TEST_RET_ON_FAIL (test_update_instance (&cls->instances[1],
   1061                                           GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   1062   /* Test account creation */
   1063   TEST_RET_ON_FAIL (test_insert_account (&cls->instances[0],
   1064                                          &cls->accounts[0],
   1065                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   1066   /* Test double account insertion fails */
   1067   TEST_RET_ON_FAIL (test_insert_account (&cls->instances[1],
   1068                                          &cls->accounts[1],
   1069                                          GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   1070   TEST_RET_ON_FAIL (test_insert_account (&cls->instances[0],
   1071                                          &cls->accounts[0],
   1072                                          GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   1073   instances[0].accounts_length = 1;
   1074   TEST_RET_ON_FAIL (test_lookup_instances (false,
   1075                                            1,
   1076                                            instances));
   1077   /* Test deactivate account */
   1078   TEST_RET_ON_FAIL (test_inactivate_account (&cls->instances[0],
   1079                                              &cls->accounts[0],
   1080                                              GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   1081   TEST_RET_ON_FAIL (test_inactivate_account (&cls->instances[1],
   1082                                              &cls->accounts[1],
   1083                                              GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   1084   cls->accounts[0].active = false;
   1085   TEST_RET_ON_FAIL (test_lookup_instances (false,
   1086                                            1,
   1087                                            instances));
   1088   /* Test lookup account */
   1089   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   1090       TALER_MERCHANTDB_get_account_serial (pg,
   1091                                            cls->instances[0].instance.id,
   1092                                            cls->accounts[0].payto_uri,
   1093                                            &account_serial))
   1094   {
   1095     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1096                 "Lookup account failed\n");
   1097     return 1;
   1098   }
   1099   if (1 != account_serial)
   1100   {
   1101     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1102                 "Lookup account failed: incorrect serial number found\n");
   1103     return 1;
   1104   }
   1105   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
   1106       TALER_MERCHANTDB_get_account_serial (pg,
   1107                                            cls->instances[0].instance.id,
   1108                                            (struct TALER_FullPayto) {
   1109     (char *) "payto://other-uri"
   1110   },
   1111                                            &account_serial))
   1112   {
   1113     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1114                 "Lookup account failed: account found where there is none\n");
   1115     return 1;
   1116   }
   1117   /* Test that a reconnect does not lose the per-instance search_path */
   1118   TEST_RET_ON_FAIL (test_reconnect_search_path (&cls->instances[0]));
   1119   /* Test that every stored report gets its own serial */
   1120   TEST_RET_ON_FAIL (test_insert_report_serial (&cls->instances[0]));
   1121   /* Test that a NULL authentication hash/salt is not an error */
   1122   TEST_RET_ON_FAIL (test_get_instance_auth_null (&cls->instances[0]));
   1123   /* Test instance private key deletion */
   1124   TEST_RET_ON_FAIL (test_delete_instance_private_key (&cls->instances[0],
   1125                                                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   1126   TEST_RET_ON_FAIL (test_delete_instance_private_key (&cls->instances[1],
   1127                                                       GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   1128   TEST_RET_ON_FAIL (test_lookup_instances (true,
   1129                                            0,
   1130                                            NULL));
   1131   TEST_RET_ON_FAIL (test_lookup_instances (false,
   1132                                            1,
   1133                                            instances));
   1134   TEST_RET_ON_FAIL (test_insert_instance (&cls->instances[1],
   1135                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   1136   TEST_RET_ON_FAIL (test_lookup_instances (false,
   1137                                            2,
   1138                                            instances));
   1139   TEST_RET_ON_FAIL (test_lookup_instances (true,
   1140                                            1,
   1141                                            &instances[1]));
   1142   TEST_RET_ON_FAIL (test_purge_instance (&cls->instances[1],
   1143                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   1144   TEST_RET_ON_FAIL (test_purge_instance (&cls->instances[1],
   1145                                          GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   1146   /* Test that the instance is gone. */
   1147   TEST_RET_ON_FAIL (test_lookup_instances (false,
   1148                                            1,
   1149                                            instances));
   1150   return 0;
   1151 }
   1152 
   1153 
   1154 /**
   1155  * Function that tests instances.
   1156  *
   1157  * @return 0 on success, 1 otherwise.
   1158  */
   1159 static int
   1160 test_instances (void)
   1161 {
   1162   struct TestInstances_Closure test_cls;
   1163   int test_result;
   1164 
   1165   pre_test_instances (&test_cls);
   1166   test_result = run_test_instances (&test_cls);
   1167   post_test_instances (&test_cls);
   1168   return test_result;
   1169 }
   1170 
   1171 
   1172 /* *********** Products ********** */
   1173 
   1174 
   1175 /**
   1176  * A container for data relevant to a product.
   1177  */
   1178 struct ProductData
   1179 {
   1180   /**
   1181    * The identifier of the product.
   1182    */
   1183   const char *id;
   1184 
   1185   /**
   1186    * The details of the product.
   1187    */
   1188   struct TALER_MERCHANTDB_ProductDetails product;
   1189 };
   1190 
   1191 
   1192 /**
   1193  * Creates a product for testing with.
   1194  *
   1195  * @param id the id of the product.
   1196  * @param product the product data to fill.
   1197  */
   1198 static void
   1199 make_product (const char *id,
   1200               struct ProductData *product)
   1201 {
   1202   static struct TALER_Amount htwenty40;
   1203 
   1204   GNUNET_assert (GNUNET_OK ==
   1205                  TALER_string_to_amount ("EUR:120.40",
   1206                                          &htwenty40));
   1207 
   1208   memset (product,
   1209           0,
   1210           sizeof (*product));
   1211   product->id = id;
   1212   product->product.product_name = "Test product";
   1213   product->product.description = "This is a test product";
   1214   product->product.description_i18n = json_array ();
   1215   GNUNET_assert (NULL != product->product.description_i18n);
   1216   product->product.unit = "boxes";
   1217   product->product.minimum_age = 0;
   1218   product->product.price_array = &htwenty40;
   1219   product->product.price_array_length = 1;
   1220   product->product.taxes = json_array ();
   1221   GNUNET_assert (NULL != product->product.taxes);
   1222   product->product.total_stock = 55;
   1223   product->product.total_sold = 0;
   1224   product->product.total_lost = 0;
   1225   product->product.image = GNUNET_strdup ("");
   1226   GNUNET_assert (NULL != product->product.image);
   1227   product->product.address = json_array ();
   1228   GNUNET_assert (NULL != product->product.address);
   1229   product->product.next_restock = GNUNET_TIME_UNIT_ZERO_TS;
   1230 }
   1231 
   1232 
   1233 /**
   1234  * Frees memory associated with @e ProductData.
   1235  *
   1236  * @param product the container to free.
   1237  */
   1238 static void
   1239 free_product_data (struct ProductData *product)
   1240 {
   1241   json_decref (product->product.description_i18n);
   1242   json_decref (product->product.taxes);
   1243   GNUNET_free (product->product.image);
   1244   json_decref (product->product.address);
   1245 }
   1246 
   1247 
   1248 /**
   1249  * Compare two products for equality.
   1250  *
   1251  * @param a the first product.
   1252  * @param b the second product.
   1253  * @return 0 on equality, 1 otherwise.
   1254  */
   1255 static int
   1256 check_products_equal (const struct TALER_MERCHANTDB_ProductDetails *a,
   1257                       const struct TALER_MERCHANTDB_ProductDetails *b)
   1258 {
   1259   if ((0 != strcmp (a->description,
   1260                     b->description)) ||
   1261       (1 != json_equal (a->description_i18n,
   1262                         b->description_i18n)) ||
   1263       (0 != strcmp (a->unit,
   1264                     b->unit)) ||
   1265       (a->price_array_length != b->price_array_length) ||
   1266       (1 != json_equal (a->taxes,
   1267                         b->taxes)) ||
   1268       (a->total_stock != b->total_stock) ||
   1269       (a->total_sold != b->total_sold) ||
   1270       (a->total_lost != b->total_lost) ||
   1271       (0 != strcmp (a->image,
   1272                     b->image)) ||
   1273       (1 != json_equal (a->address,
   1274                         b->address)) ||
   1275       (GNUNET_TIME_timestamp_cmp (a->next_restock,
   1276                                   !=,
   1277                                   b->next_restock)))
   1278 
   1279     return 1;
   1280   for (size_t i = 0; i<a->price_array_length; i++)
   1281     if ( (GNUNET_OK !=
   1282           TALER_amount_cmp_currency (&a->price_array[i],
   1283                                      &b->price_array[i])) ||
   1284          (0 != TALER_amount_cmp (&a->price_array[i],
   1285                                  &b->price_array[i])) )
   1286       return 1;
   1287   return 0;
   1288 }
   1289 
   1290 
   1291 /**
   1292  * Tests inserting product data into the database.
   1293  *
   1294  * @param instance the instance to insert the product for.
   1295  * @param product the product data to insert.
   1296  * @param num_cats length of the @a cats array
   1297  * @param cats array of categories for the product
   1298  * @param expected_result the result we expect the db to return.
   1299  * @param expect_conflict expected conflict status
   1300  * @param expect_no_instance expected instance missing status
   1301  * @param expected_no_cat expected category missing index
   1302  * @return 0 when successful, 1 otherwise.
   1303  */
   1304 static int
   1305 test_insert_product (const struct InstanceData *instance,
   1306                      const struct ProductData *product,
   1307                      unsigned int num_cats,
   1308                      const uint64_t *cats,
   1309                      enum GNUNET_DB_QueryStatus expected_result,
   1310                      bool expect_conflict,
   1311                      bool expect_no_instance,
   1312                      ssize_t expected_no_cat)
   1313 {
   1314   bool conflict;
   1315   ssize_t no_cat;
   1316   bool no_group;
   1317   bool no_pot;
   1318 
   1319   if (expect_no_instance)
   1320   {
   1321     TEST_COND_RET_ON_FAIL (
   1322       GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
   1323       TALER_MERCHANTDB_set_instance (pg, instance->instance.id),
   1324       "Expected missing instance, but set_instance succeeded\n");
   1325     return 0;
   1326   }
   1327   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   1328   TEST_COND_RET_ON_FAIL (expected_result ==
   1329                          TALER_MERCHANTDB_insert_product (pg,
   1330                                                           instance->instance.id,
   1331                                                           product->id,
   1332                                                           &product->product,
   1333                                                           num_cats,
   1334                                                           cats,
   1335                                                           &conflict,
   1336                                                           &no_cat,
   1337                                                           &no_group,
   1338                                                           &no_pot),
   1339                          "Insert product failed\n");
   1340   if (expected_result > 0)
   1341   {
   1342     TEST_COND_RET_ON_FAIL (conflict == expect_conflict,
   1343                            "Conflict wrong");
   1344     TEST_COND_RET_ON_FAIL (no_cat == expected_no_cat,
   1345                            "Wrong category missing returned");
   1346   }
   1347   return 0;
   1348 }
   1349 
   1350 
   1351 /**
   1352  * Tests updating product data in the database.
   1353  *
   1354  * @param instance the instance to update the product for.
   1355  * @param product the product data to update.
   1356  * @param expected_result the result we expect the db to return.
   1357  * @return 0 when successful, 1 otherwise.
   1358  */
   1359 static int
   1360 test_update_product (const struct InstanceData *instance,
   1361                      const struct ProductData *product,
   1362                      unsigned int num_cats,
   1363                      const uint64_t *cats,
   1364                      enum GNUNET_DB_QueryStatus expected_result,
   1365                      bool expect_no_instance,
   1366                      bool expect_no_product,
   1367                      bool expect_lost_reduced,
   1368                      bool expect_sold_reduced,
   1369                      bool expect_stocked_reduced,
   1370                      ssize_t expected_no_cat)
   1371 
   1372 {
   1373   ssize_t no_cat;
   1374   bool no_product;
   1375   bool lost_reduced;
   1376   bool sold_reduced;
   1377   bool stocked_reduced;
   1378   bool no_group;
   1379   bool no_pot;
   1380 
   1381   if (expect_no_instance)
   1382   {
   1383     TEST_COND_RET_ON_FAIL (
   1384       GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
   1385       TALER_MERCHANTDB_set_instance (pg, instance->instance.id),
   1386       "Expected missing instance, but set_instance succeeded\n");
   1387     return 0;
   1388   }
   1389   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   1390   TEST_COND_RET_ON_FAIL (
   1391     expected_result ==
   1392     TALER_MERCHANTDB_update_product (pg,
   1393                                      instance->instance.id,
   1394                                      product->id,
   1395                                      &product->product,
   1396                                      num_cats,
   1397                                      cats,
   1398                                      &no_cat,
   1399                                      &no_product,
   1400                                      &lost_reduced,
   1401                                      &sold_reduced,
   1402                                      &stocked_reduced,
   1403                                      &no_group,
   1404                                      &no_pot),
   1405     "Update product failed\n");
   1406   if (expected_result > 0)
   1407   {
   1408     TEST_COND_RET_ON_FAIL (no_product == expect_no_product,
   1409                            "No product wrong");
   1410     TEST_COND_RET_ON_FAIL (lost_reduced == expect_lost_reduced,
   1411                            "No product wrong");
   1412     TEST_COND_RET_ON_FAIL (stocked_reduced == expect_stocked_reduced,
   1413                            "Stocked reduced wrong");
   1414     TEST_COND_RET_ON_FAIL (sold_reduced == expect_sold_reduced,
   1415                            "Sold reduced wrong");
   1416     TEST_COND_RET_ON_FAIL (no_cat == expected_no_cat,
   1417                            "Wrong category missing returned");
   1418   }
   1419   return 0;
   1420 }
   1421 
   1422 
   1423 /**
   1424  * Tests looking up a product from the db.
   1425  *
   1426  * @param instance the instance to query from.
   1427  * @param product the product to query and compare to.
   1428  * @return 0 when successful, 1 otherwise.
   1429  */
   1430 static int
   1431 test_lookup_product (const struct InstanceData *instance,
   1432                      const struct ProductData *product)
   1433 {
   1434   struct TALER_MERCHANTDB_ProductDetails lookup_result;
   1435   size_t num_categories = 0;
   1436   uint64_t *categories = NULL;
   1437 
   1438   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   1439   if (0 > TALER_MERCHANTDB_get_product (pg,
   1440                                         instance->instance.id,
   1441                                         product->id,
   1442                                         &lookup_result,
   1443                                         &num_categories,
   1444                                         &categories))
   1445   {
   1446     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1447                 "Lookup product failed\n");
   1448     TALER_MERCHANTDB_product_details_free (&lookup_result);
   1449     return 1;
   1450   }
   1451   GNUNET_free (categories);
   1452   {
   1453     const struct TALER_MERCHANTDB_ProductDetails *to_cmp = &product->product;
   1454 
   1455     if (0 != check_products_equal (&lookup_result,
   1456                                    to_cmp))
   1457     {
   1458       GNUNET_break (0);
   1459       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1460                   "Lookup product failed: incorrect product returned\n");
   1461       TALER_MERCHANTDB_product_details_free (&lookup_result);
   1462       return 1;
   1463     }
   1464   }
   1465   TALER_MERCHANTDB_product_details_free (&lookup_result);
   1466   return 0;
   1467 }
   1468 
   1469 
   1470 /**
   1471  * Closure for testing product lookup
   1472  */
   1473 struct TestLookupProducts_Closure
   1474 {
   1475   /**
   1476    * Number of product ids to compare to
   1477    */
   1478   unsigned int products_to_cmp_length;
   1479 
   1480   /**
   1481    * Pointer to array of product ids
   1482    */
   1483   const struct ProductData *products_to_cmp;
   1484 
   1485   /**
   1486    * Pointer to array of number of matches for each product
   1487    */
   1488   unsigned int *results_matching;
   1489 
   1490   /**
   1491    * Total number of results returned
   1492    */
   1493   unsigned int results_length;
   1494 };
   1495 
   1496 
   1497 /**
   1498  * Function called after calling @e test_lookup_products
   1499  *
   1500  * @param cls a pointer to the lookup closure.
   1501  * @param product_serial DB row ID
   1502  * @param product_id the identifier of the product found.
   1503  */
   1504 static void
   1505 lookup_products_cb (void *cls,
   1506                     uint64_t product_serial,
   1507                     const char *product_id)
   1508 {
   1509   struct TestLookupProducts_Closure *cmp = cls;
   1510 
   1511   GNUNET_assert (product_serial > 0);
   1512   if (NULL == cmp)
   1513     return;
   1514   cmp->results_length += 1;
   1515   for (unsigned int i = 0; cmp->products_to_cmp_length > i; ++i)
   1516   {
   1517     if (0 == strcmp (cmp->products_to_cmp[i].id,
   1518                      product_id))
   1519       cmp->results_matching[i] += 1;
   1520   }
   1521 }
   1522 
   1523 
   1524 /**
   1525  * Tests looking up all products for an instance.
   1526  *
   1527  * @param instance the instance to query from.
   1528  * @param products_length the number of products we are expecting.
   1529  * @param products the list of products that we expect to be found.
   1530  * @return 0 when successful, 1 otherwise.
   1531  */
   1532 static int
   1533 test_lookup_products (const struct InstanceData *instance,
   1534                       unsigned int products_length,
   1535                       const struct ProductData *products)
   1536 {
   1537   unsigned int results_matching[GNUNET_NZL (products_length)];
   1538   struct TestLookupProducts_Closure cls = {
   1539     .products_to_cmp_length = products_length,
   1540     .products_to_cmp = products,
   1541     .results_matching = results_matching,
   1542     .results_length = 0
   1543   };
   1544   memset (results_matching, 0, sizeof (unsigned int) * products_length);
   1545   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   1546   if (0 > TALER_MERCHANTDB_iterate_products (pg,
   1547                                              instance->instance.id,
   1548                                              0,
   1549                                              20,
   1550                                              NULL,
   1551                                              NULL,
   1552                                              NULL,
   1553                                              0,
   1554                                              &lookup_products_cb,
   1555                                              &cls))
   1556   {
   1557     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1558                 "Lookup products failed\n");
   1559     return 1;
   1560   }
   1561   if (products_length != cls.results_length)
   1562   {
   1563     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1564                 "Lookup products failed: incorrect number of results\n");
   1565     return 1;
   1566   }
   1567   for (unsigned int i = 0; products_length > i; ++i)
   1568   {
   1569     if (1 != cls.results_matching[i])
   1570     {
   1571       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1572                   "Lookup products failed: mismatched data\n");
   1573       return 1;
   1574     }
   1575   }
   1576   return 0;
   1577 }
   1578 
   1579 
   1580 /**
   1581  * Tests deleting a product.
   1582  *
   1583  * @param instance the instance to delete the product from.
   1584  * @param product the product that should be deleted.
   1585  * @param force whether to force deletion of a locked product.
   1586  * @param expect_not_found whether we expect the product to be reported as
   1587  *        unknown.
   1588  * @param expect_locked whether we expect deletion to be refused because the
   1589  *        product is locked.
   1590  * @return 0 when successful, 1 otherwise.
   1591  */
   1592 static int
   1593 test_delete_product (const struct InstanceData *instance,
   1594                      const struct ProductData *product,
   1595                      bool force,
   1596                      bool expect_not_found,
   1597                      bool expect_locked)
   1598 {
   1599   bool not_found = false;
   1600   bool locked = false;
   1601   enum GNUNET_DB_QueryStatus qs;
   1602 
   1603   TEST_SET_INSTANCE (instance->instance.id,
   1604                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   1605   qs = TALER_MERCHANTDB_delete_product (pg,
   1606                                         instance->instance.id,
   1607                                         product->id,
   1608                                         force,
   1609                                         &not_found,
   1610                                         &locked);
   1611   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs,
   1612                          "Delete product failed\n");
   1613   TEST_COND_RET_ON_FAIL (expect_not_found == not_found,
   1614                          "Delete product 'not_found' mismatch\n");
   1615   TEST_COND_RET_ON_FAIL (expect_locked == locked,
   1616                          "Delete product 'locked' mismatch\n");
   1617   return 0;
   1618 }
   1619 
   1620 
   1621 /**
   1622  * Closure for product tests.
   1623  */
   1624 struct TestProducts_Closure
   1625 {
   1626   /**
   1627    * The instance to use for this test.
   1628    */
   1629   struct InstanceData instance;
   1630 
   1631   /**
   1632    * The array of products.
   1633    */
   1634   struct ProductData products[2];
   1635 };
   1636 
   1637 
   1638 /**
   1639  * Sets up the data structures used in the product tests.
   1640  *
   1641  * @param cls the closure to fill with test data.
   1642  */
   1643 static void
   1644 pre_test_products (struct TestProducts_Closure *cls)
   1645 {
   1646   static struct TALER_Amount four95;
   1647 
   1648   /* Instance */
   1649   make_instance ("test_inst_products",
   1650                  &cls->instance);
   1651 
   1652   /* Products */
   1653   make_product ("test_products_pd_0",
   1654                 &cls->products[0]);
   1655 
   1656   make_product ("test_products_pd_1",
   1657                 &cls->products[1]);
   1658   cls->products[1].product.description = "This is a another test product";
   1659   cls->products[1].product.unit = "cans";
   1660   cls->products[1].product.minimum_age = 0;
   1661 
   1662   GNUNET_assert (GNUNET_OK ==
   1663                  TALER_string_to_amount ("EUR:4.95",
   1664                                          &four95));
   1665   cls->products[1].product.price_array = &four95;
   1666   cls->products[1].product.price_array_length = 1;
   1667   cls->products[1].product.total_stock = 5001;
   1668 }
   1669 
   1670 
   1671 /**
   1672  * Handles all teardown after testing.
   1673  *
   1674  * @param cls the closure containing memory to be freed.
   1675  */
   1676 static void
   1677 post_test_products (struct TestProducts_Closure *cls)
   1678 {
   1679   free_instance_data (&cls->instance);
   1680   free_product_data (&cls->products[0]);
   1681   free_product_data (&cls->products[1]);
   1682 }
   1683 
   1684 
   1685 /**
   1686  * Tests that inserting a category, an OTP device or a webhook that
   1687  * already exists is reported as #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
   1688  * and, crucially, does *not* abort the enclosing transaction.
   1689  *
   1690  * Regression test: these three used to be plain INSERTs without an
   1691  * ON CONFLICT clause.  A duplicate key raises 23505, which GNUnet
   1692  * maps to SUCCESS_NO_RESULTS (0); the POST handlers have no branch
   1693  * for 0 and fell through to 'GNUNET_assert (SOFT_ERROR == qs)',
   1694  * aborting taler-merchant-httpd.  On top of that the failed INSERT
   1695  * left the transaction in the aborted state, so every following
   1696  * statement failed with 25P02 -- which is what this test checks for,
   1697  * as the query status alone cannot tell the two variants apart.
   1698  *
   1699  * @param instance the instance to run the test against.
   1700  * @return 0 when successful, 1 otherwise.
   1701  */
   1702 static int
   1703 test_insert_duplicate_conflicts (const struct InstanceData *instance)
   1704 {
   1705   json_t *i18n = json_object ();
   1706   struct TALER_MERCHANTDB_OtpDeviceDetails td = {
   1707     .otp_description = (char *) "conflict test device",
   1708     .otp_key = (char *) "my key",
   1709     .otp_algorithm = 1,
   1710     .otp_ctr = 42
   1711   };
   1712   struct TALER_MERCHANTDB_WebhookDetails wb = {
   1713     .event_type = (char *) "pay",
   1714     .url = (char *) "http://example.com/",
   1715     .http_method = (char *) "POST",
   1716     .header_template = (char *) "",
   1717     .body_template = (char *) ""
   1718   };
   1719   uint64_t cat;
   1720   int ret = 1;
   1721 
   1722   GNUNET_assert (NULL != i18n);
   1723   TEST_SET_INSTANCE (instance->instance.id,
   1724                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   1725   if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   1726         TALER_MERCHANTDB_insert_category (pg,
   1727                                           instance->instance.id,
   1728                                           "duplicate-conflict",
   1729                                           i18n,
   1730                                           &cat)) ||
   1731        (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   1732         TALER_MERCHANTDB_insert_otp_device (pg,
   1733                                             instance->instance.id,
   1734                                             "duplicate-conflict",
   1735                                             &td)) ||
   1736        (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   1737         TALER_MERCHANTDB_insert_webhook (pg,
   1738                                          instance->instance.id,
   1739                                          "duplicate-conflict",
   1740                                          &wb)) )
   1741   {
   1742     GNUNET_break (0);
   1743     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1744                 "Initial insert for the conflict test failed\n");
   1745     goto cleanup;
   1746   }
   1747   if (GNUNET_OK !=
   1748       TALER_MERCHANTDB_start (pg,
   1749                               "insert duplicate conflicts"))
   1750   {
   1751     GNUNET_break (0);
   1752     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1753                 "Failed to start transaction\n");
   1754     goto cleanup;
   1755   }
   1756   /* Each of these is a duplicate: expected to be 'no results', and the
   1757      transaction must survive, otherwise the next one fails with a hard
   1758      error (25P02) instead. */
   1759   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
   1760       TALER_MERCHANTDB_insert_category (pg,
   1761                                         instance->instance.id,
   1762                                         "duplicate-conflict",
   1763                                         i18n,
   1764                                         &cat))
   1765   {
   1766     GNUNET_break (0);
   1767     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1768                 "Duplicate category insert not reported as 'no results'\n");
   1769     goto rollback;
   1770   }
   1771   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
   1772       TALER_MERCHANTDB_insert_otp_device (pg,
   1773                                           instance->instance.id,
   1774                                           "duplicate-conflict",
   1775                                           &td))
   1776   {
   1777     GNUNET_break (0);
   1778     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1779                 "Duplicate OTP device insert not reported as 'no results'\n");
   1780     goto rollback;
   1781   }
   1782   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
   1783       TALER_MERCHANTDB_insert_webhook (pg,
   1784                                        instance->instance.id,
   1785                                        "duplicate-conflict",
   1786                                        &wb))
   1787   {
   1788     GNUNET_break (0);
   1789     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1790                 "Duplicate webhook insert not reported as 'no results'\n");
   1791     goto rollback;
   1792   }
   1793   /* ... and the transaction must still be able to do useful work. */
   1794   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   1795       TALER_MERCHANTDB_insert_category (pg,
   1796                                         instance->instance.id,
   1797                                         "duplicate-conflict-other",
   1798                                         i18n,
   1799                                         &cat))
   1800   {
   1801     GNUNET_break (0);
   1802     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1803                 "Insert after duplicate conflict failed\n");
   1804     goto rollback;
   1805   }
   1806   if (0 >
   1807       TALER_MERCHANTDB_commit (pg))
   1808   {
   1809     GNUNET_break (0);
   1810     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1811                 "Commit after duplicate conflict failed\n");
   1812     goto cleanup;
   1813   }
   1814   ret = 0;
   1815   goto cleanup;
   1816 rollback:
   1817   TALER_MERCHANTDB_rollback (pg);
   1818 cleanup:
   1819   json_decref (i18n);
   1820   return ret;
   1821 }
   1822 
   1823 
   1824 /**
   1825  * Tests that renaming a category onto the name of an existing
   1826  * category is reported as a conflict (and not as
   1827  * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS, which the HTTP layer would
   1828  * turn into a bogus 404 "category unknown"), and that the failed
   1829  * rename does not poison the enclosing transaction.
   1830  *
   1831  * @param instance the instance to run the test against.
   1832  * @return 0 when successful, 1 otherwise.
   1833  */
   1834 static int
   1835 test_update_category_conflict (const struct InstanceData *instance)
   1836 {
   1837   json_t *i18n = json_object ();
   1838   uint64_t cat1;
   1839   uint64_t cat2;
   1840   bool conflict = true;
   1841   int ret = 1;
   1842 
   1843   GNUNET_assert (NULL != i18n);
   1844   TEST_SET_INSTANCE (instance->instance.id,
   1845                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   1846   if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   1847         TALER_MERCHANTDB_insert_category (pg,
   1848                                           instance->instance.id,
   1849                                           "category-conflict-a",
   1850                                           i18n,
   1851                                           &cat1)) ||
   1852        (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   1853         TALER_MERCHANTDB_insert_category (pg,
   1854                                           instance->instance.id,
   1855                                           "category-conflict-b",
   1856                                           i18n,
   1857                                           &cat2)) )
   1858   {
   1859     GNUNET_break (0);
   1860     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1861                 "Insert category failed\n");
   1862     goto cleanup;
   1863   }
   1864   /* Renaming 'a' to 'b' must be a conflict, not 'unknown category'. */
   1865   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   1866       TALER_MERCHANTDB_update_category (pg,
   1867                                         instance->instance.id,
   1868                                         cat1,
   1869                                         "category-conflict-b",
   1870                                         i18n,
   1871                                         &conflict))
   1872   {
   1873     GNUNET_break (0);
   1874     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1875                 "Conflicting category rename not reported as conflict\n");
   1876     goto cleanup;
   1877   }
   1878   if (! conflict)
   1879   {
   1880     GNUNET_break (0);
   1881     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1882                 "Conflicting category rename did not set 'conflict'\n");
   1883     goto cleanup;
   1884   }
   1885   /* ... and the connection must still be usable afterwards. */
   1886   conflict = true;
   1887   if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   1888         TALER_MERCHANTDB_update_category (pg,
   1889                                           instance->instance.id,
   1890                                           cat1,
   1891                                           "category-conflict-c",
   1892                                           i18n,
   1893                                           &conflict)) ||
   1894        conflict)
   1895   {
   1896     GNUNET_break (0);
   1897     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1898                 "Category rename after conflict failed\n");
   1899     goto cleanup;
   1900   }
   1901   /* Unknown category must still be reported as 'no results'. */
   1902   conflict = true;
   1903   if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
   1904         TALER_MERCHANTDB_update_category (pg,
   1905                                           instance->instance.id,
   1906                                           cat1 + cat2 + 424242,
   1907                                           "category-conflict-d",
   1908                                           i18n,
   1909                                           &conflict)) ||
   1910        conflict)
   1911   {
   1912     GNUNET_break (0);
   1913     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1914                 "Rename of unknown category not reported as 'no results'\n");
   1915     goto cleanup;
   1916   }
   1917   ret = 0;
   1918 cleanup:
   1919   json_decref (i18n);
   1920   return ret;
   1921 }
   1922 
   1923 
   1924 /**
   1925  * Runs the tests for products.
   1926  *
   1927  * @param cls the container of the test data.
   1928  * @return 0 on success, 1 otherwise.
   1929  */
   1930 static int
   1931 run_test_products (struct TestProducts_Closure *cls)
   1932 {
   1933   struct GNUNET_Uuid uuid;
   1934   struct GNUNET_TIME_Timestamp refund_deadline =
   1935     GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_WEEKS);
   1936 
   1937   /* Test that insert without an instance fails */
   1938   TEST_RET_ON_FAIL (test_insert_product (&cls->instance,
   1939                                          &cls->products[0],
   1940                                          0,
   1941                                          NULL,
   1942                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   1943                                          false,
   1944                                          true,
   1945                                          -1));
   1946   /* Insert the instance */
   1947   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
   1948                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   1949   /* Test that a conflicting category rename is reported as a conflict */
   1950   TEST_RET_ON_FAIL (test_update_category_conflict (&cls->instance));
   1951   /* Test that duplicate inserts do not poison the transaction */
   1952   TEST_RET_ON_FAIL (test_insert_duplicate_conflicts (&cls->instance));
   1953   /* Test inserting a product */
   1954   TEST_RET_ON_FAIL (test_insert_product (&cls->instance,
   1955                                          &cls->products[0],
   1956                                          0,
   1957                                          NULL,
   1958                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   1959                                          false,
   1960                                          false,
   1961                                          -1));
   1962   /* Test that double insert succeeds */
   1963   TEST_RET_ON_FAIL (test_insert_product (&cls->instance,
   1964                                          &cls->products[0],
   1965                                          0,
   1966                                          NULL,
   1967                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   1968                                          false,
   1969                                          false,
   1970                                          -1));
   1971   /* Test that an insert naming a category that does not exist is
   1972      reported via 'no_cat'.
   1973 
   1974      NOTE: the category existence check in insert_product.sql runs
   1975      *before* any modification of the database (so that a rejected
   1976      request leaves no trace), and therefore also before the
   1977      idempotency/conflict check.  An unknown category consequently takes
   1978      precedence over a conflict: 'no_cat' is the 1-based index into the
   1979      supplied category array and 'conflict' stays false. */
   1980   {
   1981     uint64_t cat = 42;
   1982 
   1983     TEST_RET_ON_FAIL (test_insert_product (&cls->instance,
   1984                                            &cls->products[0],
   1985                                            1,
   1986                                            &cat,
   1987                                            GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   1988                                            false,
   1989                                            false,
   1990                                            1));
   1991   }
   1992   /* Test that a genuinely conflicting insert -- same product_id, but
   1993      different product data -- is reported via 'conflict'.
   1994 
   1995      This case supplies no categories at all, so it cannot be masked by
   1996      the category pre-check.  The previous version of this test relied on
   1997      a non-existent category to provoke the conflict, which stopped
   1998      exercising the conflict path once that check was hoisted above the
   1999      insert. */
   2000   {
   2001     struct ProductData conflicting = cls->products[1];
   2002 
   2003     conflicting.id = cls->products[0].id;
   2004     TEST_RET_ON_FAIL (test_insert_product (&cls->instance,
   2005                                            &conflicting,
   2006                                            0,
   2007                                            NULL,
   2008                                            GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   2009                                            true,
   2010                                            false,
   2011                                            -1));
   2012   }
   2013   /* Test lookup of individual products */
   2014   TEST_RET_ON_FAIL (test_lookup_product (&cls->instance,
   2015                                          &cls->products[0]));
   2016   /* Make sure it fails correctly for products that don't exist */
   2017   {
   2018     size_t num_categories = 0;
   2019     uint64_t *categories = NULL;
   2020 
   2021     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
   2022         TALER_MERCHANTDB_get_product (pg,
   2023                                       cls->instance.instance.id,
   2024                                       "nonexistent_product",
   2025                                       NULL,
   2026                                       &num_categories,
   2027                                       &categories))
   2028     {
   2029       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2030                   "Lookup product failed\n");
   2031       return 1;
   2032     }
   2033     GNUNET_free (categories);
   2034   }
   2035   /* Test product update */
   2036   cls->products[0].product.description =
   2037     "This is a test product that has been updated!";
   2038   GNUNET_assert (0 ==
   2039                  json_array_append_new (
   2040                    cls->products[0].product.description_i18n,
   2041                    json_string (
   2042                      "description in another language")));
   2043   cls->products[0].product.unit = "barrels";
   2044   {
   2045     static struct TALER_Amount seven68;
   2046 
   2047     GNUNET_assert (GNUNET_OK ==
   2048                    TALER_string_to_amount ("EUR:7.68",
   2049                                            &seven68));
   2050     cls->products[0].product.price_array = &seven68;
   2051     cls->products[0].product.price_array_length = 1;
   2052   }
   2053   GNUNET_assert (0 ==
   2054                  json_array_append_new (cls->products[0].product.taxes,
   2055                                         json_string ("2% sales tax")));
   2056   cls->products[0].product.total_stock = 100;
   2057   cls->products[0].product.total_sold = 0; /* will be ignored! */
   2058   cls->products[0].product.total_lost = 7;
   2059   GNUNET_free (cls->products[0].product.image);
   2060   cls->products[0].product.image = GNUNET_strdup ("image");
   2061   GNUNET_assert (0 ==
   2062                  json_array_append_new (cls->products[0].product.address,
   2063                                         json_string ("444 Some Street")));
   2064   cls->products[0].product.next_restock = GNUNET_TIME_timestamp_get ();
   2065   TEST_RET_ON_FAIL (test_update_product (
   2066                       &cls->instance,
   2067                       &cls->products[0],
   2068                       0,
   2069                       NULL,
   2070                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   2071                       false,
   2072                       false,
   2073                       false,
   2074                       false,
   2075                       false,
   2076                       -1));
   2077 
   2078   /* Stock is the pair (total_stock, total_stock_frac): raise the
   2079      fractional part, then check that lowering only the fractional part
   2080      is refused just like lowering the integer part. */
   2081   cls->products[0].product.total_stock_frac = 500000;
   2082   TEST_RET_ON_FAIL (test_update_product (
   2083                       &cls->instance,
   2084                       &cls->products[0],
   2085                       0,
   2086                       NULL,
   2087                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   2088                       false,
   2089                       false,
   2090                       false,
   2091                       false,
   2092                       false,
   2093                       -1));
   2094   {
   2095     struct ProductData frac_dec = cls->products[0];
   2096 
   2097     frac_dec.product.total_stock_frac = 400000;
   2098     TEST_RET_ON_FAIL (test_update_product (
   2099                         &cls->instance,
   2100                         &frac_dec,
   2101                         0,
   2102                         NULL,
   2103                         GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   2104                         false,
   2105                         false,
   2106                         false,
   2107                         false,
   2108                         true,
   2109                         -1));
   2110   }
   2111   {
   2112     struct ProductData stock_dec = cls->products[0];
   2113 
   2114     stock_dec.product.total_stock = 40;
   2115     TEST_RET_ON_FAIL (test_update_product (
   2116                         &cls->instance,
   2117                         &stock_dec,
   2118                         0,
   2119                         NULL,
   2120                         GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   2121                         false,
   2122                         false,
   2123                         false,
   2124                         false,
   2125                         true,
   2126                         -1));
   2127   }
   2128   {
   2129     struct ProductData lost_dec = cls->products[0];
   2130 
   2131     lost_dec.product.total_lost = 1;
   2132     TEST_RET_ON_FAIL (test_update_product (
   2133                         &cls->instance,
   2134                         &lost_dec,
   2135                         0,
   2136                         NULL,
   2137                         GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   2138                         false,
   2139                         false,
   2140                         true,
   2141                         false,
   2142                         false,
   2143                         -1));
   2144   }
   2145   TEST_RET_ON_FAIL (test_lookup_product (&cls->instance,
   2146                                          &cls->products[0]));
   2147   TEST_RET_ON_FAIL (test_update_product (
   2148                       &cls->instance,
   2149                       &cls->products[1],
   2150                       0,
   2151                       NULL,
   2152                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   2153                       false,
   2154                       true,
   2155                       false,
   2156                       false,
   2157                       false,
   2158                       -1));
   2159   /* Test collective product lookup */
   2160   TEST_RET_ON_FAIL (test_insert_product (&cls->instance,
   2161                                          &cls->products[1],
   2162                                          0,
   2163                                          NULL,
   2164                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   2165                                          false,
   2166                                          false,
   2167                                          -1));
   2168   TEST_RET_ON_FAIL (test_lookup_products (&cls->instance,
   2169                                           2,
   2170                                           cls->products));
   2171   /* Test locking */
   2172   uuid.value[0] = 0x1287346a;
   2173   if (0 != TALER_MERCHANTDB_insert_inventory_lock (pg,
   2174                                                    cls->instance.instance.id,
   2175                                                    cls->products[0].id,
   2176                                                    &uuid,
   2177                                                    256,
   2178                                                    0,
   2179                                                    refund_deadline))
   2180   {
   2181     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2182                 "Lock product failed\n");
   2183     return 1;
   2184   }
   2185   if (1 != TALER_MERCHANTDB_insert_inventory_lock (pg,
   2186                                                    cls->instance.instance.id,
   2187                                                    cls->products[0].id,
   2188                                                    &uuid,
   2189                                                    1,
   2190                                                    0,
   2191                                                    refund_deadline))
   2192   {
   2193     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2194                 "Lock product failed\n");
   2195     return 1;
   2196   }
   2197   /* Test product deletion of an unlocked product */
   2198   TEST_RET_ON_FAIL (test_delete_product (&cls->instance,
   2199                                          &cls->products[1],
   2200                                          false,
   2201                                          false,
   2202                                          false));
   2203   /* Test double deletion reports 'not found' */
   2204   TEST_RET_ON_FAIL (test_delete_product (&cls->instance,
   2205                                          &cls->products[1],
   2206                                          false,
   2207                                          true,
   2208                                          false));
   2209   TEST_RET_ON_FAIL (test_lookup_products (&cls->instance,
   2210                                           1,
   2211                                           cls->products));
   2212   /* Test unlocking */
   2213   if (1 != TALER_MERCHANTDB_delete_inventory_lock (pg,
   2214                                                    &uuid))
   2215   {
   2216     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2217                 "Unlock inventory failed\n");
   2218     return 1;
   2219   }
   2220   if (0 != TALER_MERCHANTDB_delete_inventory_lock (pg,
   2221                                                    &uuid))
   2222   {
   2223     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2224                 "Unlock inventory failed\n");
   2225     return 1;
   2226   }
   2227   /* Re-lock products[0] to exercise deletion of a locked product */
   2228   if (1 != TALER_MERCHANTDB_insert_inventory_lock (pg,
   2229                                                    cls->instance.instance.id,
   2230                                                    cls->products[0].id,
   2231                                                    &uuid,
   2232                                                    1,
   2233                                                    0,
   2234                                                    refund_deadline))
   2235   {
   2236     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2237                 "Lock product failed\n");
   2238     return 1;
   2239   }
   2240   /* Deletion without force must be refused while the product is locked */
   2241   TEST_RET_ON_FAIL (test_delete_product (&cls->instance,
   2242                                          &cls->products[0],
   2243                                          false,
   2244                                          false,
   2245                                          true));
   2246   TEST_RET_ON_FAIL (test_lookup_products (&cls->instance,
   2247                                           1,
   2248                                           cls->products));
   2249   /* Forced deletion releases the lock and removes the product */
   2250   TEST_RET_ON_FAIL (test_delete_product (&cls->instance,
   2251                                          &cls->products[0],
   2252                                          true,
   2253                                          false,
   2254                                          false));
   2255   TEST_RET_ON_FAIL (test_lookup_products (&cls->instance,
   2256                                           0,
   2257                                           NULL));
   2258   /* Updating a product that has just been deleted must be reported via
   2259      'no_product' (which the HTTP layer turns into a 404), never as a
   2260      hard error.  merchant_do_update_product re-reads the inventory row
   2261      and then ASSERTs that the subsequent UPDATE still finds it; without
   2262      the FOR UPDATE on that re-read, a DELETE committing in the window
   2263      between the two statements turns this into a P0004 assertion
   2264      failure (HTTP 500).  Reproducing that window needs a second
   2265      database connection, which this harness does not have; what is
   2266      asserted here is that the very same code path returns 'no_product'
   2267      when the row is gone. */
   2268   TEST_RET_ON_FAIL (test_update_product (
   2269                       &cls->instance,
   2270                       &cls->products[0],
   2271                       0,
   2272                       NULL,
   2273                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   2274                       false,
   2275                       true,
   2276                       false,
   2277                       false,
   2278                       false,
   2279                       -1));
   2280   return 0;
   2281 }
   2282 
   2283 
   2284 /**
   2285  * Takes care of product testing.
   2286  *
   2287  * @return 0 on success, 1 otherwise.
   2288  */
   2289 static int
   2290 test_products (void)
   2291 {
   2292   struct TestProducts_Closure test_cls;
   2293   int test_result;
   2294 
   2295   pre_test_products (&test_cls);
   2296   test_result = run_test_products (&test_cls);
   2297   post_test_products (&test_cls);
   2298   return test_result;
   2299 }
   2300 
   2301 
   2302 /* ********** Inventory locks ********** */
   2303 
   2304 
   2305 /**
   2306  * Container for the data used by the inventory lock tests.
   2307  */
   2308 struct TestLocks_Closure
   2309 {
   2310   /**
   2311    * The instance to use for this test.
   2312    */
   2313   struct InstanceData instance;
   2314 
   2315   /**
   2316    * The products we lock; [0] has a limited stock, [1] carries the
   2317    * INT64_MAX "unlimited stock" sentinel.
   2318    */
   2319   struct ProductData products[2];
   2320 };
   2321 
   2322 
   2323 /**
   2324  * Checks that @a product has exactly @a expected_locked units locked.
   2325  *
   2326  * @param instance the instance owning the product.
   2327  * @param product the product to inspect.
   2328  * @param expected_locked expected value of total_locked.
   2329  * @param expected_locked_frac expected value of total_locked_frac.
   2330  * @return 0 when successful, 1 otherwise.
   2331  */
   2332 static int
   2333 test_product_locked (const struct InstanceData *instance,
   2334                      const struct ProductData *product,
   2335                      uint64_t expected_locked,
   2336                      uint32_t expected_locked_frac)
   2337 {
   2338   struct TALER_MERCHANTDB_ProductDetails pd;
   2339   size_t num_categories = 0;
   2340   uint64_t *categories = NULL;
   2341   int ret = 0;
   2342 
   2343   memset (&pd,
   2344           0,
   2345           sizeof (pd));
   2346   TEST_SET_INSTANCE (instance->instance.id,
   2347                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   2348   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   2349                          TALER_MERCHANTDB_get_product (pg,
   2350                                                        instance->instance.id,
   2351                                                        product->id,
   2352                                                        &pd,
   2353                                                        &num_categories,
   2354                                                        &categories),
   2355                          "Lookup of locked product failed\n");
   2356   GNUNET_free (categories);
   2357   if ( (expected_locked != pd.total_locked) ||
   2358        (expected_locked_frac != pd.total_locked_frac) )
   2359   {
   2360     GNUNET_break (0);
   2361     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2362                 "Product `%s' has total_locked=%llu.%06u,"
   2363                 " expected %llu.%06u\n",
   2364                 product->id,
   2365                 (unsigned long long) pd.total_locked,
   2366                 (unsigned int) pd.total_locked_frac,
   2367                 (unsigned long long) expected_locked,
   2368                 (unsigned int) expected_locked_frac);
   2369     ret = 1;
   2370   }
   2371   TALER_MERCHANTDB_product_details_free (&pd);
   2372   return ret;
   2373 }
   2374 
   2375 
   2376 /**
   2377  * Tests locking @a quantity units of @a product under @a uuid.
   2378  *
   2379  * @param instance the instance owning the product.
   2380  * @param product the product to lock.
   2381  * @param uuid identifier of the lock.
   2382  * @param quantity how many units to lock.
   2383  * @param expected_result the result we expect the db to return.
   2384  * @return 0 when successful, 1 otherwise.
   2385  */
   2386 static int
   2387 test_insert_inventory_lock (const struct InstanceData *instance,
   2388                             const struct ProductData *product,
   2389                             const struct GNUNET_Uuid *uuid,
   2390                             uint64_t quantity,
   2391                             enum GNUNET_DB_QueryStatus expected_result)
   2392 {
   2393   struct GNUNET_TIME_Timestamp expiration
   2394     = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_WEEKS);
   2395 
   2396   TEST_SET_INSTANCE (instance->instance.id,
   2397                      expected_result);
   2398   TEST_COND_RET_ON_FAIL (expected_result ==
   2399                          TALER_MERCHANTDB_insert_inventory_lock (
   2400                            pg,
   2401                            instance->instance.id,
   2402                            product->id,
   2403                            uuid,
   2404                            quantity,
   2405                            0,
   2406                            expiration),
   2407                          "Insert inventory lock returned unexpected status\n");
   2408   return 0;
   2409 }
   2410 
   2411 
   2412 /**
   2413  * Sets up the data structures used in the inventory lock tests.
   2414  *
   2415  * @param cls the closure to fill with test data.
   2416  */
   2417 static void
   2418 pre_test_locks (struct TestLocks_Closure *cls)
   2419 {
   2420   make_instance ("test_inst_locks",
   2421                  &cls->instance);
   2422   make_product ("test_locks_pd_0",
   2423                 &cls->products[0]);
   2424   cls->products[0].product.total_stock = 100;
   2425   make_product ("test_locks_pd_1",
   2426                 &cls->products[1]);
   2427   cls->products[1].product.total_stock = INT64_MAX;
   2428 }
   2429 
   2430 
   2431 /**
   2432  * Handles all teardown after testing.
   2433  *
   2434  * @param cls the closure containing memory to be freed.
   2435  */
   2436 static void
   2437 post_test_locks (struct TestLocks_Closure *cls)
   2438 {
   2439   free_instance_data (&cls->instance);
   2440   free_product_data (&cls->products[0]);
   2441   free_product_data (&cls->products[1]);
   2442 }
   2443 
   2444 
   2445 /**
   2446  * Runs the tests for inventory locks.
   2447  *
   2448  * @param cls the container of the test data.
   2449  * @return 0 on success, 1 otherwise.
   2450  */
   2451 static int
   2452 run_test_locks (struct TestLocks_Closure *cls)
   2453 {
   2454   struct GNUNET_Uuid uuid1;
   2455   struct GNUNET_Uuid uuid2;
   2456 
   2457   memset (&uuid1,
   2458           0,
   2459           sizeof (uuid1));
   2460   memset (&uuid2,
   2461           0,
   2462           sizeof (uuid2));
   2463   uuid1.value[0] = 0x11111111;
   2464   uuid2.value[0] = 0x22222222;
   2465   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
   2466                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   2467   TEST_RET_ON_FAIL (test_insert_product (&cls->instance,
   2468                                          &cls->products[0],
   2469                                          0,
   2470                                          NULL,
   2471                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   2472                                          false,
   2473                                          false,
   2474                                          -1));
   2475   /* Lock 40 of the 100 units in stock */
   2476   TEST_RET_ON_FAIL (test_insert_inventory_lock (
   2477                       &cls->instance,
   2478                       &cls->products[0],
   2479                       &uuid1,
   2480                       40,
   2481                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   2482   TEST_RET_ON_FAIL (test_product_locked (&cls->instance,
   2483                                          &cls->products[0],
   2484                                          40,
   2485                                          0));
   2486   /* Re-posting a lock for the same UUID *updates* the existing lock,
   2487      the locks must not stack up. */
   2488   TEST_RET_ON_FAIL (test_insert_inventory_lock (
   2489                       &cls->instance,
   2490                       &cls->products[0],
   2491                       &uuid1,
   2492                       10,
   2493                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   2494   TEST_RET_ON_FAIL (test_product_locked (&cls->instance,
   2495                                          &cls->products[0],
   2496                                          10,
   2497                                          0));
   2498   /* A second lock competes with the first one for the remaining stock */
   2499   TEST_RET_ON_FAIL (test_insert_inventory_lock (
   2500                       &cls->instance,
   2501                       &cls->products[0],
   2502                       &uuid2,
   2503                       91,
   2504                       GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   2505   TEST_RET_ON_FAIL (test_product_locked (&cls->instance,
   2506                                          &cls->products[0],
   2507                                          10,
   2508                                          0));
   2509   TEST_RET_ON_FAIL (test_insert_inventory_lock (
   2510                       &cls->instance,
   2511                       &cls->products[0],
   2512                       &uuid2,
   2513                       90,
   2514                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   2515   TEST_RET_ON_FAIL (test_product_locked (&cls->instance,
   2516                                          &cls->products[0],
   2517                                          100,
   2518                                          0));
   2519   /* Locking a quantity of zero releases the lock */
   2520   TEST_RET_ON_FAIL (test_insert_inventory_lock (
   2521                       &cls->instance,
   2522                       &cls->products[0],
   2523                       &uuid1,
   2524                       0,
   2525                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   2526   TEST_RET_ON_FAIL (test_product_locked (&cls->instance,
   2527                                          &cls->products[0],
   2528                                          90,
   2529                                          0));
   2530   /* Unlocking an UUID that holds no lock is a no-op, not an error */
   2531   TEST_RET_ON_FAIL (test_insert_inventory_lock (
   2532                       &cls->instance,
   2533                       &cls->products[0],
   2534                       &uuid1,
   2535                       0,
   2536                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   2537   TEST_RET_ON_FAIL (test_product_locked (&cls->instance,
   2538                                          &cls->products[0],
   2539                                          90,
   2540                                          0));
   2541   /* Explicitly dropping the remaining lock frees the stock again */
   2542   TEST_SET_INSTANCE (cls->instance.instance.id,
   2543                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   2544   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   2545                          TALER_MERCHANTDB_delete_inventory_lock (pg,
   2546                                                                  &uuid2),
   2547                          "Unlock inventory failed\n");
   2548   TEST_RET_ON_FAIL (test_product_locked (&cls->instance,
   2549                                          &cls->products[0],
   2550                                          0,
   2551                                          0));
   2552   /* Locking an unknown product is reported as 'no results' */
   2553   {
   2554     struct ProductData unknown = cls->products[0];
   2555 
   2556     unknown.id = "test_locks_pd_unknown";
   2557     TEST_RET_ON_FAIL (test_insert_inventory_lock (
   2558                         &cls->instance,
   2559                         &unknown,
   2560                         &uuid1,
   2561                         1,
   2562                         GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   2563   }
   2564   /* Products with the INT64_MAX "unlimited stock" sentinel skip the
   2565      availability check, so two large locks can exceed what the
   2566      total_locked counter can represent.  That must saturate, not blow
   2567      up with 'bigint out of range' (which the caller turns into a 500). */
   2568   TEST_RET_ON_FAIL (test_insert_product (&cls->instance,
   2569                                          &cls->products[1],
   2570                                          0,
   2571                                          NULL,
   2572                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   2573                                          false,
   2574                                          false,
   2575                                          -1));
   2576   TEST_RET_ON_FAIL (test_insert_inventory_lock (
   2577                       &cls->instance,
   2578                       &cls->products[1],
   2579                       &uuid1,
   2580                       INT64_MAX,
   2581                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   2582   TEST_RET_ON_FAIL (test_product_locked (&cls->instance,
   2583                                          &cls->products[1],
   2584                                          INT64_MAX,
   2585                                          0));
   2586   TEST_RET_ON_FAIL (test_insert_inventory_lock (
   2587                       &cls->instance,
   2588                       &cls->products[1],
   2589                       &uuid2,
   2590                       INT64_MAX,
   2591                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   2592   TEST_RET_ON_FAIL (test_product_locked (&cls->instance,
   2593                                          &cls->products[1],
   2594                                          INT64_MAX,
   2595                                          999999));
   2596   return 0;
   2597 }
   2598 
   2599 
   2600 /**
   2601  * Takes care of inventory lock testing.
   2602  *
   2603  * @return 0 on success, 1 otherwise.
   2604  */
   2605 static int
   2606 test_inventory_locks (void)
   2607 {
   2608   struct TestLocks_Closure test_cls;
   2609   int test_result;
   2610 
   2611   pre_test_locks (&test_cls);
   2612   test_result = run_test_locks (&test_cls);
   2613   post_test_locks (&test_cls);
   2614   return test_result;
   2615 }
   2616 
   2617 
   2618 /* ********** Tokens ********** */
   2619 
   2620 
   2621 /**
   2622  * Container for the data used by the token tests.
   2623  */
   2624 struct TestTokens_Closure
   2625 {
   2626   /**
   2627    * The instance to use for this test.
   2628    */
   2629   struct InstanceData instance;
   2630 
   2631   /**
   2632    * Details of the token family we operate on.
   2633    */
   2634   struct TALER_MERCHANTDB_TokenFamilyDetails family;
   2635 
   2636   /**
   2637    * Public key of the token family key.
   2638    */
   2639   struct TALER_TokenIssuePublicKey pub;
   2640 
   2641   /**
   2642    * Private key of the token family key.
   2643    */
   2644   struct TALER_TokenIssuePrivateKey priv;
   2645 
   2646   /**
   2647    * Hash of @e pub, identifies the token family key in the DB.
   2648    */
   2649   struct TALER_TokenIssuePublicKeyHashP h_pub;
   2650 
   2651   /**
   2652    * Hash of the contract the tokens are issued for.
   2653    */
   2654   struct TALER_PrivateContractHashP h_contract_terms;
   2655 };
   2656 
   2657 
   2658 /**
   2659  * Fabricate a blinded token issue signature.  Its value is never
   2660  * inspected by the database, so a random CS answer is good enough; we
   2661  * only care that identical inputs yield identical bytes.
   2662  *
   2663  * @param[out] bs storage for the blinded signature
   2664  * @param[out] sig set to point to @a bs
   2665  */
   2666 static void
   2667 make_blinded_token_sig (struct GNUNET_CRYPTO_BlindedSignature *bs,
   2668                         struct TALER_BlindedTokenIssueSignature *sig)
   2669 {
   2670   memset (bs,
   2671           0,
   2672           sizeof (*bs));
   2673   bs->cipher = GNUNET_CRYPTO_BSA_CS;
   2674   bs->rc = 1;
   2675   GNUNET_CRYPTO_random_block (&bs->details.blinded_cs_answer,
   2676                               sizeof (bs->details.blinded_cs_answer));
   2677   sig->signature = bs;
   2678 }
   2679 
   2680 
   2681 /**
   2682  * Fabricate an (unblinded) token issue signature.
   2683  *
   2684  * @param[out] ub storage for the unblinded signature
   2685  * @param[out] sig set to point to @a ub
   2686  */
   2687 static void
   2688 make_token_issue_sig (struct GNUNET_CRYPTO_UnblindedSignature *ub,
   2689                       struct TALER_TokenIssueSignature *sig)
   2690 {
   2691   memset (ub,
   2692           0,
   2693           sizeof (*ub));
   2694   ub->cipher = GNUNET_CRYPTO_BSA_CS;
   2695   ub->rc = 1;
   2696   GNUNET_CRYPTO_random_block (&ub->details.cs_signature,
   2697                               sizeof (ub->details.cs_signature));
   2698   sig->signature = ub;
   2699 }
   2700 
   2701 
   2702 /**
   2703  * Checks the issuance/spending counters of the token family.
   2704  *
   2705  * @param cls the test data.
   2706  * @param expected_issued number of issued tokens we expect.
   2707  * @param expected_used number of spent tokens we expect.
   2708  * @return 0 when successful, 1 otherwise.
   2709  */
   2710 static int
   2711 test_token_family_counters (const struct TestTokens_Closure *cls,
   2712                             uint64_t expected_issued,
   2713                             uint64_t expected_used)
   2714 {
   2715   struct TALER_MERCHANTDB_TokenFamilyDetails details;
   2716   int ret = 0;
   2717 
   2718   memset (&details,
   2719           0,
   2720           sizeof (details));
   2721   TEST_SET_INSTANCE (cls->instance.instance.id,
   2722                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   2723   TEST_COND_RET_ON_FAIL (
   2724     GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   2725     TALER_MERCHANTDB_get_token_family (pg,
   2726                                        cls->instance.instance.id,
   2727                                        cls->family.slug,
   2728                                        &details),
   2729     "Lookup of token family failed\n");
   2730   if ( (expected_issued != details.issued) ||
   2731        (expected_used != details.used) )
   2732   {
   2733     GNUNET_break (0);
   2734     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2735                 "Token family counters are issued=%llu/used=%llu,"
   2736                 " expected issued=%llu/used=%llu\n",
   2737                 (unsigned long long) details.issued,
   2738                 (unsigned long long) details.used,
   2739                 (unsigned long long) expected_issued,
   2740                 (unsigned long long) expected_used);
   2741     ret = 1;
   2742   }
   2743   TALER_MERCHANTDB_token_family_details_free (&details);
   2744   return ret;
   2745 }
   2746 
   2747 
   2748 /**
   2749  * Tests inserting an issued token.
   2750  *
   2751  * @param cls the test data.
   2752  * @param blind_sig the blinded signature of the issued token.
   2753  * @param expected_result the result we expect the db to return.
   2754  * @param expect_no_family whether we expect the token family key to be
   2755  *        reported as unknown.
   2756  * @return 0 when successful, 1 otherwise.
   2757  */
   2758 static int
   2759 test_insert_issued_token (const struct TestTokens_Closure *cls,
   2760                           const struct TALER_BlindedTokenIssueSignature *
   2761                           blind_sig,
   2762                           enum GNUNET_DB_QueryStatus expected_result,
   2763                           bool expect_no_family)
   2764 {
   2765   /* Deliberately poisoned: the DB layer must assign the flag on every
   2766      path, including the ones where it returns early.  Note that the
   2767      'qs <= 0' early return itself is not reachable from here: a stored
   2768      procedure with OUT parameters always yields exactly one row. */
   2769   bool no_family = true;
   2770 
   2771   TEST_SET_INSTANCE (cls->instance.instance.id,
   2772                      expected_result);
   2773   TEST_COND_RET_ON_FAIL (
   2774     expected_result ==
   2775     TALER_MERCHANTDB_insert_issued_token (pg,
   2776                                           &cls->h_contract_terms,
   2777                                           &cls->h_pub,
   2778                                           blind_sig,
   2779                                           &no_family),
   2780     "Insert issued token returned unexpected status\n");
   2781   TEST_COND_RET_ON_FAIL (expect_no_family == no_family,
   2782                          "Insert issued token 'no_family' mismatch\n");
   2783   return 0;
   2784 }
   2785 
   2786 
   2787 /**
   2788  * Tests inserting a spent token.
   2789  *
   2790  * @param cls the test data.
   2791  * @param use_pub public key of the token being spent.
   2792  * @param use_sig signature made with the token use key.
   2793  * @param issue_sig signature of the merchant over the token.
   2794  * @param expected_result the result we expect the db to return.
   2795  * @param expect_no_family whether we expect the token family key to be
   2796  *        reported as unknown.
   2797  * @return 0 when successful, 1 otherwise.
   2798  */
   2799 static int
   2800 test_insert_used_token (const struct TestTokens_Closure *cls,
   2801                         const struct TALER_TokenUsePublicKeyP *use_pub,
   2802                         const struct TALER_TokenUseSignatureP *use_sig,
   2803                         const struct TALER_TokenIssueSignature *issue_sig,
   2804                         enum GNUNET_DB_QueryStatus expected_result,
   2805                         bool expect_no_family)
   2806 {
   2807   /* Deliberately poisoned: see test_insert_issued_token(). */
   2808   bool no_family = true;
   2809 
   2810   TEST_SET_INSTANCE (cls->instance.instance.id,
   2811                      expected_result);
   2812   TEST_COND_RET_ON_FAIL (
   2813     expected_result ==
   2814     TALER_MERCHANTDB_insert_used_token (pg,
   2815                                         &cls->h_contract_terms,
   2816                                         &cls->h_pub,
   2817                                         use_pub,
   2818                                         use_sig,
   2819                                         issue_sig,
   2820                                         &no_family),
   2821     "Insert used token returned unexpected status\n");
   2822   TEST_COND_RET_ON_FAIL (expect_no_family == no_family,
   2823                          "Insert used token 'no_family' mismatch\n");
   2824   return 0;
   2825 }
   2826 
   2827 
   2828 /**
   2829  * Sets up the data structures used in the token tests.
   2830  *
   2831  * @param cls the closure to fill with test data.
   2832  */
   2833 static void
   2834 pre_test_tokens (struct TestTokens_Closure *cls)
   2835 {
   2836   make_instance ("test_inst_tokens",
   2837                  &cls->instance);
   2838   memset (&cls->family,
   2839           0,
   2840           sizeof (cls->family));
   2841   cls->family.slug = (char *) "test_tokens_family";
   2842   cls->family.name = (char *) "Test token family";
   2843   cls->family.description = (char *) "This is a test token family";
   2844   cls->family.description_i18n = json_object ();
   2845   GNUNET_assert (NULL != cls->family.description_i18n);
   2846   cls->family.valid_after = GNUNET_TIME_timestamp_get ();
   2847   cls->family.valid_before
   2848     = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_YEARS);
   2849   cls->family.duration = GNUNET_TIME_UNIT_DAYS;
   2850   cls->family.validity_granularity = GNUNET_TIME_UNIT_DAYS;
   2851   cls->family.start_offset = GNUNET_TIME_UNIT_ZERO;
   2852   cls->family.kind = TALER_MERCHANTDB_TFK_Discount;
   2853   GNUNET_CRYPTO_blind_sign_keys_create (&cls->priv.private_key,
   2854                                         &cls->pub.public_key,
   2855                                         GNUNET_CRYPTO_BSA_CS);
   2856   cls->h_pub.hash = cls->pub.public_key->pub_key_hash;
   2857   GNUNET_CRYPTO_random_block (&cls->h_contract_terms,
   2858                               sizeof (cls->h_contract_terms));
   2859 }
   2860 
   2861 
   2862 /**
   2863  * Handles all teardown after testing.
   2864  *
   2865  * @param cls the closure containing memory to be freed.
   2866  */
   2867 static void
   2868 post_test_tokens (struct TestTokens_Closure *cls)
   2869 {
   2870   GNUNET_CRYPTO_blind_sign_pub_decref (cls->pub.public_key);
   2871   GNUNET_CRYPTO_blind_sign_priv_decref (cls->priv.private_key);
   2872   json_decref (cls->family.description_i18n);
   2873   free_instance_data (&cls->instance);
   2874 }
   2875 
   2876 
   2877 /**
   2878  * Runs the tests for issued and spent tokens.
   2879  *
   2880  * @param cls the container of the test data.
   2881  * @return 0 on success, 1 otherwise.
   2882  */
   2883 static int
   2884 run_test_tokens (struct TestTokens_Closure *cls)
   2885 {
   2886   struct GNUNET_CRYPTO_BlindedSignature bs1;
   2887   struct GNUNET_CRYPTO_BlindedSignature bs2;
   2888   struct TALER_BlindedTokenIssueSignature blind_sig1;
   2889   struct TALER_BlindedTokenIssueSignature blind_sig2;
   2890   struct GNUNET_CRYPTO_UnblindedSignature ub1;
   2891   struct TALER_TokenIssueSignature issue_sig;
   2892   struct TALER_TokenUsePublicKeyP use_pub;
   2893   struct TALER_TokenUseSignatureP use_sig;
   2894   struct TALER_TokenUseSignatureP other_sig;
   2895 
   2896   make_blinded_token_sig (&bs1,
   2897                           &blind_sig1);
   2898   make_blinded_token_sig (&bs2,
   2899                           &blind_sig2);
   2900   make_token_issue_sig (&ub1,
   2901                         &issue_sig);
   2902   GNUNET_CRYPTO_random_block (&use_pub,
   2903                               sizeof (use_pub));
   2904   GNUNET_CRYPTO_random_block (&use_sig,
   2905                               sizeof (use_sig));
   2906   GNUNET_CRYPTO_random_block (&other_sig,
   2907                               sizeof (other_sig));
   2908   /* Set up instance, token family and token family key */
   2909   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
   2910                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   2911   TEST_SET_INSTANCE (cls->instance.instance.id,
   2912                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   2913   TEST_COND_RET_ON_FAIL (
   2914     GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   2915     TALER_MERCHANTDB_insert_token_family (pg,
   2916                                           cls->instance.instance.id,
   2917                                           cls->family.slug,
   2918                                           &cls->family),
   2919     "Insert token family failed\n");
   2920   TEST_SET_INSTANCE (cls->instance.instance.id,
   2921                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   2922   TEST_COND_RET_ON_FAIL (
   2923     GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   2924     TALER_MERCHANTDB_insert_token_family_key (pg,
   2925                                               cls->instance.instance.id,
   2926                                               cls->family.slug,
   2927                                               &cls->pub,
   2928                                               &cls->priv,
   2929                                               cls->family.valid_before,
   2930                                               cls->family.valid_after,
   2931                                               cls->family.valid_before),
   2932     "Insert token family key failed\n");
   2933   TEST_RET_ON_FAIL (test_token_family_counters (cls,
   2934                                                 0,
   2935                                                 0));
   2936   /* Issuing a token bumps the 'issued' counter */
   2937   TEST_RET_ON_FAIL (test_insert_issued_token (
   2938                       cls,
   2939                       &blind_sig1,
   2940                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   2941                       false));
   2942   TEST_RET_ON_FAIL (test_token_family_counters (cls,
   2943                                                 1,
   2944                                                 0));
   2945   /* Re-issuing the very same token must be detected as a duplicate
   2946      and must NOT bump the counter a second time. */
   2947   TEST_RET_ON_FAIL (test_insert_issued_token (
   2948                       cls,
   2949                       &blind_sig1,
   2950                       GNUNET_DB_STATUS_SUCCESS_NO_RESULTS,
   2951                       false));
   2952   TEST_RET_ON_FAIL (test_token_family_counters (cls,
   2953                                                 1,
   2954                                                 0));
   2955   /* A different blind signature is a different token */
   2956   TEST_RET_ON_FAIL (test_insert_issued_token (
   2957                       cls,
   2958                       &blind_sig2,
   2959                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   2960                       false));
   2961   TEST_RET_ON_FAIL (test_token_family_counters (cls,
   2962                                                 2,
   2963                                                 0));
   2964   /* Spending a token bumps the 'used' counter */
   2965   TEST_RET_ON_FAIL (test_insert_used_token (
   2966                       cls,
   2967                       &use_pub,
   2968                       &use_sig,
   2969                       &issue_sig,
   2970                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   2971                       false));
   2972   TEST_RET_ON_FAIL (test_token_family_counters (cls,
   2973                                                 2,
   2974                                                 1));
   2975   /* Replaying the exact same spend is idempotent: reported as success,
   2976      but the 'used' counter must not move a second time. */
   2977   TEST_RET_ON_FAIL (test_insert_used_token (
   2978                       cls,
   2979                       &use_pub,
   2980                       &use_sig,
   2981                       &issue_sig,
   2982                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   2983                       false));
   2984   TEST_RET_ON_FAIL (test_token_family_counters (cls,
   2985                                                 2,
   2986                                                 1));
   2987   /* Reusing the same token key with a different signature is
   2988      double-spending and must be refused */
   2989   TEST_RET_ON_FAIL (test_insert_used_token (
   2990                       cls,
   2991                       &use_pub,
   2992                       &other_sig,
   2993                       &issue_sig,
   2994                       GNUNET_DB_STATUS_SUCCESS_NO_RESULTS,
   2995                       false));
   2996   TEST_RET_ON_FAIL (test_token_family_counters (cls,
   2997                                                 2,
   2998                                                 1));
   2999   /* Deleting the token family cascades to the token family keys.  An
   3000      order created before the deletion may still be paid; the missing
   3001      key must then be reported to the caller (which turns it into a 404
   3002      TOKEN_KEY_UNKNOWN) instead of being an internal hard error. */
   3003   TEST_SET_INSTANCE (cls->instance.instance.id,
   3004                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   3005   TEST_COND_RET_ON_FAIL (
   3006     GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   3007     TALER_MERCHANTDB_delete_token_family (pg,
   3008                                           cls->instance.instance.id,
   3009                                           cls->family.slug),
   3010     "Delete token family failed\n");
   3011   TEST_RET_ON_FAIL (test_insert_issued_token (
   3012                       cls,
   3013                       &blind_sig1,
   3014                       GNUNET_DB_STATUS_SUCCESS_NO_RESULTS,
   3015                       true));
   3016   TEST_RET_ON_FAIL (test_insert_used_token (
   3017                       cls,
   3018                       &use_pub,
   3019                       &use_sig,
   3020                       &issue_sig,
   3021                       GNUNET_DB_STATUS_SUCCESS_NO_RESULTS,
   3022                       true));
   3023   return 0;
   3024 }
   3025 
   3026 
   3027 /**
   3028  * Takes care of token testing.
   3029  *
   3030  * @return 0 on success, 1 otherwise.
   3031  */
   3032 static int
   3033 test_tokens (void)
   3034 {
   3035   struct TestTokens_Closure test_cls;
   3036   int test_result;
   3037 
   3038   pre_test_tokens (&test_cls);
   3039   test_result = run_test_tokens (&test_cls);
   3040   post_test_tokens (&test_cls);
   3041   return test_result;
   3042 }
   3043 
   3044 
   3045 /* ********** Orders ********** */
   3046 
   3047 
   3048 /**
   3049  * Container for order data
   3050  */
   3051 struct OrderData
   3052 {
   3053   /**
   3054    * The id of the order
   3055    */
   3056   const char *id;
   3057 
   3058   /**
   3059    * The pay deadline for the order
   3060    */
   3061   struct GNUNET_TIME_Timestamp pay_deadline;
   3062 
   3063   /**
   3064    * The contract of the order
   3065    */
   3066   json_t *contract;
   3067 
   3068   /**
   3069    * The claim token for the order.
   3070    */
   3071   struct TALER_ClaimTokenP claim_token;
   3072 };
   3073 
   3074 
   3075 /**
   3076  * Builds an order for testing.
   3077  *
   3078  * @param order_id the identifier to use for the order.
   3079  * @param order the container to fill with data.
   3080  */
   3081 static void
   3082 make_order (const char *order_id,
   3083             struct OrderData *order)
   3084 {
   3085   struct GNUNET_TIME_Timestamp refund_deadline;
   3086 
   3087   order->id = order_id;
   3088   order->contract = json_object ();
   3089   GNUNET_assert (NULL != order->contract);
   3090   order->pay_deadline = GNUNET_TIME_relative_to_timestamp (
   3091     GNUNET_TIME_UNIT_DAYS);
   3092   GNUNET_CRYPTO_random_block (&order->claim_token,
   3093                               sizeof (order->claim_token));
   3094   refund_deadline = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_WEEKS);
   3095   GNUNET_assert (0 ==
   3096                  json_object_set_new (order->contract,
   3097                                       "fulfillment_url",
   3098                                       json_string ("a")));
   3099   GNUNET_assert (0 ==
   3100                  json_object_set_new (order->contract,
   3101                                       "summary",
   3102                                       json_string ("Test order")));
   3103   GNUNET_assert (0 ==
   3104                  json_object_set_new (order->contract,
   3105                                       "order_id",
   3106                                       json_string (order_id)));
   3107   GNUNET_assert (0 ==
   3108                  json_object_set_new (
   3109                    order->contract,
   3110                    "pay_deadline",
   3111                    GNUNET_JSON_from_timestamp (order->pay_deadline))
   3112                  );
   3113   GNUNET_assert (0 ==
   3114                  json_object_set_new (order->contract,
   3115                                       "refund_deadline",
   3116                                       GNUNET_JSON_from_timestamp (
   3117                                         refund_deadline)));
   3118 }
   3119 
   3120 
   3121 /**
   3122  * Frees memory associated with an order.
   3123  *
   3124  * @param the order to free.
   3125  */
   3126 static void
   3127 free_order_data (struct OrderData *order)
   3128 {
   3129   json_decref (order->contract);
   3130 }
   3131 
   3132 
   3133 /**
   3134  * Tests inserting an order into the database.
   3135  *
   3136  * @param instance the instance to insert the order for.
   3137  * @param order the order to insert.
   3138  * @param expected_result the value we expect the db to return.
   3139  * @return 0 on success, 1 otherwise.
   3140  */
   3141 static int
   3142 test_insert_order (const struct InstanceData *instance,
   3143                    const struct OrderData *order,
   3144                    enum GNUNET_DB_QueryStatus expected_result)
   3145 {
   3146   struct TALER_MerchantPostDataHashP h_post;
   3147 
   3148   memset (&h_post,
   3149           42,
   3150           sizeof (h_post));
   3151   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   3152   TEST_COND_RET_ON_FAIL (expected_result ==
   3153                          TALER_MERCHANTDB_insert_order (pg,
   3154                                                         instance->instance.id,
   3155                                                         order->id,
   3156                                                         NULL, /* session_id */
   3157                                                         &h_post,
   3158                                                         order->pay_deadline,
   3159                                                         &order->claim_token,
   3160                                                         order->contract,
   3161                                                         NULL,
   3162                                                         0),
   3163                          "Insert order failed\n");
   3164   return 0;
   3165 }
   3166 
   3167 
   3168 /**
   3169  * Tests looking up an order in the database.
   3170  *
   3171  * @param instance the instance to lookup from.
   3172  * @param order the order that should be looked up.
   3173  * @return 0 on success, 1 otherwise.
   3174  */
   3175 static int
   3176 test_lookup_order (const struct InstanceData *instance,
   3177                    const struct OrderData *order)
   3178 {
   3179   struct TALER_ClaimTokenP ct;
   3180   json_t *lookup_terms = NULL;
   3181   struct TALER_MerchantPostDataHashP oh;
   3182   struct TALER_MerchantPostDataHashP wh;
   3183 
   3184   memset (&wh,
   3185           42,
   3186           sizeof (wh));
   3187   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   3188   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   3189       TALER_MERCHANTDB_get_order (pg,
   3190                                   instance->instance.id,
   3191                                   order->id,
   3192                                   &ct,
   3193                                   &oh,
   3194                                   &lookup_terms))
   3195   {
   3196     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3197                 "Lookup order failed\n");
   3198     if (NULL != lookup_terms)
   3199       json_decref (lookup_terms);
   3200     return 1;
   3201   }
   3202   if ( (1 != json_equal (order->contract,
   3203                          lookup_terms)) ||
   3204        (0 != GNUNET_memcmp (&order->claim_token,
   3205                             &ct)) ||
   3206        (0 != GNUNET_memcmp (&oh,
   3207                             &wh)) )
   3208   {
   3209     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3210                 "Lookup order failed: incorrect order returned\n");
   3211     if (NULL != lookup_terms)
   3212       json_decref (lookup_terms);
   3213     return 1;
   3214   }
   3215   json_decref (lookup_terms);
   3216   return 0;
   3217 }
   3218 
   3219 
   3220 /**
   3221  * Closure for testing order lookup
   3222  */
   3223 struct TestLookupOrders_Closure
   3224 {
   3225   /**
   3226    * Number of orders to compare to
   3227    */
   3228   unsigned int orders_to_cmp_length;
   3229 
   3230   /**
   3231    * Pointer to (ordered) array of order ids
   3232    */
   3233   const struct OrderData *orders_to_cmp;
   3234 
   3235   /**
   3236    * Pointer to array of bools indicating matches in the correct index
   3237    */
   3238   bool *results_match;
   3239 
   3240   /**
   3241    * Total number of results returned
   3242    */
   3243   unsigned int results_length;
   3244 };
   3245 
   3246 
   3247 /**
   3248  * Called after @e test_lookup_orders.
   3249  *
   3250  * @param cls the lookup closure.
   3251  * @param order_id the identifier of the order found.
   3252  * @param order_serial the row number of the order found.
   3253  * @param timestamp when the order was added to the database.
   3254  */
   3255 static void
   3256 lookup_orders_cb (void *cls,
   3257                   const char *order_id,
   3258                   uint64_t order_serial,
   3259                   struct GNUNET_TIME_Timestamp timestamp)
   3260 {
   3261   struct TestLookupOrders_Closure *cmp = cls;
   3262   unsigned int i;
   3263 
   3264   if (NULL == cmp)
   3265     return;
   3266   i = cmp->results_length;
   3267   cmp->results_length += 1;
   3268   if (cmp->orders_to_cmp_length > i)
   3269   {
   3270     /* Compare the orders */
   3271     if (0 == strcmp (cmp->orders_to_cmp[i].id,
   3272                      order_id))
   3273       cmp->results_match[i] = true;
   3274     else
   3275       cmp->results_match[i] = false;
   3276   }
   3277 }
   3278 
   3279 
   3280 /**
   3281  * Tests looking up orders for an instance.
   3282  *
   3283  * @param instance the instance.
   3284  * @param filter the filters applied on the lookup.
   3285  * @param orders_length the number of orders we expect to find.
   3286  * @param orders the orders we expect to find.
   3287  * @return 0 on success, 1 otherwise.
   3288  */
   3289 static int
   3290 test_lookup_orders (const struct InstanceData *instance,
   3291                     const struct TALER_MERCHANTDB_OrderFilter *filter,
   3292                     unsigned int orders_length,
   3293                     const struct OrderData *orders)
   3294 {
   3295   bool results_match[GNUNET_NZL (orders_length)];
   3296   struct TestLookupOrders_Closure cls = {
   3297     .orders_to_cmp_length = orders_length,
   3298     .orders_to_cmp = orders,
   3299     .results_match = results_match,
   3300     .results_length = 0
   3301   };
   3302   memset (results_match,
   3303           0,
   3304           sizeof (bool) * orders_length);
   3305   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   3306   if (0 > TALER_MERCHANTDB_iterate_orders (pg,
   3307                                            instance->instance.id,
   3308                                            filter,
   3309                                            &lookup_orders_cb,
   3310                                            &cls))
   3311   {
   3312     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3313                 "Lookup orders failed\n");
   3314     return 1;
   3315   }
   3316   if (orders_length != cls.results_length)
   3317   {
   3318     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3319                 "Lookup orders failed: incorrect number of results (%d)\n",
   3320                 cls.results_length);
   3321     return 1;
   3322   }
   3323   for (unsigned int i = 0; orders_length > i; ++i)
   3324   {
   3325     if (false == cls.results_match[i])
   3326     {
   3327       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3328                   "Lookup orders failed: mismatched data (index %d)\n",
   3329                   i);
   3330       return 1;
   3331     }
   3332   }
   3333   return 0;
   3334 }
   3335 
   3336 
   3337 /**
   3338  * Container for data used for looking up the row number of an order.
   3339  */
   3340 struct LookupOrderSerial_Closure
   3341 {
   3342   /**
   3343    * The order that is being looked up.
   3344    */
   3345   const struct OrderData *order;
   3346 
   3347   /**
   3348    * The serial of the order that was found.
   3349    */
   3350   uint64_t serial;
   3351 };
   3352 
   3353 
   3354 /**
   3355  * Called after @e test_lookup_orders.
   3356  *
   3357  * @param cls the lookup closure.
   3358  * @param order_id the identifier of the order found.
   3359  * @param order_serial the row number of the order found.
   3360  * @param timestamp when the order was added to the database.
   3361  */
   3362 static void
   3363 get_order_serial_cb (void *cls,
   3364                      const char *order_id,
   3365                      uint64_t order_serial,
   3366                      struct GNUNET_TIME_Timestamp timestamp)
   3367 {
   3368   struct LookupOrderSerial_Closure *lookup_cls = cls;
   3369   if (NULL == lookup_cls)
   3370     return;
   3371   if (0 == strcmp (lookup_cls->order->id,
   3372                    order_id))
   3373     lookup_cls->serial = order_serial;
   3374 }
   3375 
   3376 
   3377 /**
   3378  * Convenience function for getting the row number of an order.
   3379  *
   3380  * @param instance the instance to look up from.
   3381  * @param order the order to lookup the serial for.
   3382  * @return the row number of the order.
   3383  */
   3384 static uint64_t
   3385 get_order_serial (const struct InstanceData *instance,
   3386                   const struct OrderData *order)
   3387 {
   3388   struct LookupOrderSerial_Closure lookup_cls = {
   3389     .order = order,
   3390     .serial = 0
   3391   };
   3392   struct TALER_MERCHANTDB_OrderFilter filter = {
   3393     .paid = TALER_EXCHANGE_YNA_ALL,
   3394     .refunded = TALER_EXCHANGE_YNA_ALL,
   3395     .wired = TALER_EXCHANGE_YNA_ALL,
   3396     .expired = TALER_EXCHANGE_YNA_ALL,
   3397     .date = GNUNET_TIME_UNIT_ZERO_TS,
   3398     .start_row = 0,
   3399     .delta = 256
   3400   };
   3401 
   3402   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   3403                  TALER_MERCHANTDB_set_instance (pg,
   3404                                                 instance->instance.id));
   3405   GNUNET_assert (0 < TALER_MERCHANTDB_iterate_orders (pg,
   3406                                                       instance->instance.id,
   3407                                                       &filter,
   3408                                                       &get_order_serial_cb,
   3409                                                       &lookup_cls));
   3410   GNUNET_assert (0 != lookup_cls.serial);
   3411 
   3412   return lookup_cls.serial;
   3413 }
   3414 
   3415 
   3416 /**
   3417  * Tests deleting an order from the database.
   3418  *
   3419  * @param instance the instance to delete the order from.
   3420  * @param order the order to delete.
   3421  * @param expected_result the result we expect to receive.
   3422  * @return 0 on success, 1 otherwise.
   3423  */
   3424 static int
   3425 test_delete_order (const struct InstanceData *instance,
   3426                    const struct OrderData *order,
   3427                    enum GNUNET_DB_QueryStatus expected_result)
   3428 {
   3429   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   3430   TEST_COND_RET_ON_FAIL (expected_result ==
   3431                          TALER_MERCHANTDB_delete_order (pg,
   3432                                                         instance->instance.id,
   3433                                                         order->id,
   3434                                                         false),
   3435                          "Delete order failed\n");
   3436   return 0;
   3437 }
   3438 
   3439 
   3440 /**
   3441  * Runs @a sql directly on the database connection of the test.  Used to
   3442  * install the fault-injection trigger of #test_delete_order_atomic().
   3443  *
   3444  * @param sql the SQL statement(s) to execute
   3445  * @return 0 on success, 1 otherwise.
   3446  */
   3447 static int
   3448 orders_exec_sql (const char *sql)
   3449 {
   3450   struct GNUNET_PQ_ExecuteStatement es[] = {
   3451     GNUNET_PQ_make_execute (sql),
   3452     GNUNET_PQ_EXECUTE_STATEMENT_END
   3453   };
   3454 
   3455   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
   3456                          GNUNET_PQ_exec_statements (pg->conn,
   3457                                                     es),
   3458                          "Direct SQL execution failed\n");
   3459   return 0;
   3460 }
   3461 
   3462 
   3463 /**
   3464  * Tests that a forced order deletion removes the row in
   3465  * merchant_orders and the row in merchant_contract_terms atomically.
   3466  *
   3467  * The caller of TALER_MERCHANTDB_delete_order() does not open a
   3468  * transaction, so doing this in two statements would run them in two
   3469  * separate autocommit transactions: the first would stay committed
   3470  * when the second one fails, leaving orphaned contract terms behind.
   3471  * We provoke exactly that by installing a trigger that makes the
   3472  * deletion of the contract terms fail, and then check that the order
   3473  * itself survived.
   3474  *
   3475  * @param instance the instance to delete the order from.
   3476  * @param order the order to delete; must be claimed and unpaid.
   3477  * @return 0 on success, 1 otherwise.
   3478  */
   3479 static int
   3480 test_delete_order_atomic (const struct InstanceData *instance,
   3481                           const struct OrderData *order)
   3482 {
   3483   struct TALER_MerchantPostDataHashP h_post_data;
   3484 
   3485   TEST_SET_INSTANCE (instance->instance.id,
   3486                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   3487   TEST_RET_ON_FAIL (orders_exec_sql (
   3488                       "CREATE FUNCTION test_block_ct_delete ()"
   3489                       " RETURNS TRIGGER"
   3490                       " LANGUAGE plpgsql"
   3491                       " AS $$"
   3492                       " BEGIN"
   3493                       "   RAISE EXCEPTION"
   3494                       "     'simulated failure deleting contract terms';"
   3495                       " END $$;"
   3496                       "CREATE TRIGGER test_block_ct_delete_trigger"
   3497                       " BEFORE DELETE ON merchant_contract_terms"
   3498                       " FOR EACH ROW"
   3499                       " EXECUTE FUNCTION test_block_ct_delete ();"));
   3500   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_HARD_ERROR ==
   3501                          TALER_MERCHANTDB_delete_order (pg,
   3502                                                         instance->instance.id,
   3503                                                         order->id,
   3504                                                         true),
   3505                          "Deleting the order should have failed\n");
   3506   TEST_RET_ON_FAIL (orders_exec_sql (
   3507                       "DROP TRIGGER test_block_ct_delete_trigger"
   3508                       " ON merchant_contract_terms;"
   3509                       "DROP FUNCTION test_block_ct_delete ();"));
   3510   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   3511                          TALER_MERCHANTDB_get_order (pg,
   3512                                                      instance->instance.id,
   3513                                                      order->id,
   3514                                                      NULL,
   3515                                                      &h_post_data,
   3516                                                      NULL),
   3517                          "Order was deleted even though deleting its"
   3518                          " contract terms failed\n");
   3519   /* Without the trigger in the way, both rows must now go. */
   3520   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   3521                          TALER_MERCHANTDB_delete_order (pg,
   3522                                                         instance->instance.id,
   3523                                                         order->id,
   3524                                                         true),
   3525                          "Forced deletion of a claimed, unpaid order failed\n");
   3526   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
   3527                          TALER_MERCHANTDB_get_order (pg,
   3528                                                      instance->instance.id,
   3529                                                      order->id,
   3530                                                      NULL,
   3531                                                      &h_post_data,
   3532                                                      NULL),
   3533                          "Order survived its deletion\n");
   3534   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
   3535                          TALER_MERCHANTDB_get_contract_terms (
   3536                            pg,
   3537                            instance->instance.id,
   3538                            order->id,
   3539                            NULL,
   3540                            NULL,
   3541                            NULL),
   3542                          "Contract terms survived the order deletion\n");
   3543   return 0;
   3544 }
   3545 
   3546 
   3547 /**
   3548  * Test inserting contract terms for an order.
   3549  *
   3550  * @param instance the instance.
   3551  * @param order the order containing the contract terms.
   3552  * @param expected_result the result we expect to receive.
   3553  * @return 0 on success, 1 otherwise.
   3554  */
   3555 static int
   3556 test_insert_contract_terms (const struct InstanceData *instance,
   3557                             const struct OrderData *order,
   3558                             enum GNUNET_DB_QueryStatus expected_result)
   3559 {
   3560   uint64_t os;
   3561 
   3562   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   3563   TEST_COND_RET_ON_FAIL (expected_result ==
   3564                          TALER_MERCHANTDB_insert_contract_terms (pg,
   3565                                                                  instance->instance.id,
   3566                                                                  order->id,
   3567                                                                  order->contract,
   3568                                                                  &os),
   3569                          "Insert contract terms failed\n");
   3570   return 0;
   3571 }
   3572 
   3573 
   3574 /**
   3575  * Test updating contract terms for an order.
   3576  *
   3577  * @param instance the instance.
   3578  * @param order the order containing the contract terms.
   3579  * @param expected_result the result we expect to receive.
   3580  * @return 0 on success, 1 otherwise.
   3581  */
   3582 static int
   3583 test_update_contract_terms (const struct InstanceData *instance,
   3584                             const struct OrderData *order,
   3585                             enum GNUNET_DB_QueryStatus expected_result)
   3586 {
   3587   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   3588   TEST_COND_RET_ON_FAIL (expected_result ==
   3589                          TALER_MERCHANTDB_update_contract_terms (pg,
   3590                                                                  instance->instance.id,
   3591                                                                  order->id,
   3592                                                                  order->contract),
   3593                          "Update contract terms failed\n");
   3594   return 0;
   3595 }
   3596 
   3597 
   3598 /**
   3599  * Tests lookup of contract terms
   3600  *
   3601  * @param instance the instance to lookup from.
   3602  * @param order the order to lookup for.
   3603  * @return 0 on success, 1 otherwise.
   3604  */
   3605 static int
   3606 test_lookup_contract_terms (const struct InstanceData *instance,
   3607                             const struct OrderData *order)
   3608 {
   3609   json_t *contract = NULL;
   3610   uint64_t order_serial;
   3611 
   3612   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   3613   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   3614       TALER_MERCHANTDB_get_contract_terms (pg,
   3615                                            instance->instance.id,
   3616                                            order->id,
   3617                                            &contract,
   3618                                            &order_serial,
   3619                                            NULL))
   3620   {
   3621     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3622                 "Lookup contract terms failed\n");
   3623     GNUNET_assert (NULL == contract);
   3624     return 1;
   3625   }
   3626   if (1 != json_equal (order->contract,
   3627                        contract))
   3628   {
   3629     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3630                 "Lookup contract terms failed: mismatched data\n");
   3631     json_decref (contract);
   3632     return 1;
   3633   }
   3634   json_decref (contract);
   3635   return 0;
   3636 }
   3637 
   3638 
   3639 /**
   3640  * Tests deleting contract terms for an order.
   3641  *
   3642  * @param instance the instance to delete from.
   3643  * @param order the order whose contract terms we should delete.
   3644  * @param legal_expiration how long we must wait after creating an order to delete it
   3645  * @param expected_result the result we expect to receive.
   3646  * @return 0 on success, 1 otherwise.
   3647  */
   3648 static int
   3649 test_delete_contract_terms (const struct InstanceData *instance,
   3650                             const struct OrderData *order,
   3651                             struct GNUNET_TIME_Relative legal_expiration,
   3652                             enum GNUNET_DB_QueryStatus expected_result)
   3653 {
   3654   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   3655   TEST_COND_RET_ON_FAIL (expected_result ==
   3656                          TALER_MERCHANTDB_delete_contract_terms (pg,
   3657                                                                  instance->instance.id,
   3658                                                                  order->id,
   3659                                                                  legal_expiration),
   3660                          "Delete contract terms failed\n");
   3661   return 0;
   3662 }
   3663 
   3664 
   3665 /**
   3666  * Test marking a contract as paid in the database.
   3667  *
   3668  * @param instance the instance to use.
   3669  * @param order the order whose contract to use.
   3670  * @param expected_result the result we expect to receive.
   3671  * @return 0 on success, 1 otherwise.
   3672  */
   3673 static int
   3674 test_mark_contract_paid (const struct InstanceData *instance,
   3675                          const struct OrderData *order,
   3676                          enum GNUNET_DB_QueryStatus expected_result)
   3677 {
   3678   struct TALER_PrivateContractHashP h_contract_terms;
   3679 
   3680   GNUNET_assert (GNUNET_OK ==
   3681                  TALER_JSON_contract_hash (order->contract,
   3682                                            &h_contract_terms));
   3683   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   3684   TEST_COND_RET_ON_FAIL (expected_result ==
   3685                          TALER_MERCHANTDB_update_to_contract_terms_paid (pg,
   3686                                                                          instance->instance.id,
   3687                                                                          &h_contract_terms,
   3688                                                                          "test_orders_session",
   3689                                                                          -1),
   3690                          "Mark contract paid failed\n");
   3691   return 0;
   3692 }
   3693 
   3694 
   3695 /**
   3696  * Tests looking up the status of an order.
   3697  *
   3698  * @param instance the instance to lookup from.
   3699  * @param order the order to lookup.
   3700  * @param expected_paid whether the order was paid or not.
   3701  * @return 0 on success, 1 otherwise.
   3702  */
   3703 static int
   3704 test_lookup_order_status (const struct InstanceData *instance,
   3705                           const struct OrderData *order,
   3706                           bool expected_paid)
   3707 {
   3708   struct TALER_PrivateContractHashP h_contract_terms_expected;
   3709   struct TALER_PrivateContractHashP h_contract_terms;
   3710   bool order_paid = false;
   3711 
   3712   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   3713   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   3714       TALER_MERCHANTDB_get_order_status (pg,
   3715                                          instance->instance.id,
   3716                                          order->id,
   3717                                          &h_contract_terms,
   3718                                          &order_paid))
   3719   {
   3720     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3721                 "Lookup order status failed\n");
   3722     return 1;
   3723   }
   3724   GNUNET_assert (GNUNET_OK ==
   3725                  TALER_JSON_contract_hash (order->contract,
   3726                                            &h_contract_terms_expected));
   3727   if ((expected_paid != order_paid) ||
   3728       (0 != GNUNET_memcmp (&h_contract_terms,
   3729                            &h_contract_terms_expected)))
   3730   {
   3731     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3732                 "Lookup order status/deposit failed: mismatched data\n");
   3733     return 1;
   3734   }
   3735   return 0;
   3736 }
   3737 
   3738 
   3739 /**
   3740  * Test looking up an order by its fulfillment.
   3741  *
   3742  * @param instance the instance to lookup from.
   3743  * @param order the order to lookup.
   3744  * @param the session id associated with the payment.
   3745  * @return 0 on success, 1 otherwise.
   3746  */
   3747 static int
   3748 test_lookup_order_by_fulfillment (const struct InstanceData *instance,
   3749                                   const struct OrderData *order,
   3750                                   const char *session_id)
   3751 {
   3752   char *order_id;
   3753   const char *fulfillment_url =
   3754     json_string_value (json_object_get (order->contract,
   3755                                         "fulfillment_url"));
   3756   GNUNET_assert (NULL != fulfillment_url);
   3757   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   3758   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   3759       TALER_MERCHANTDB_get_order_by_fulfillment (pg,
   3760                                                  instance->instance.id,
   3761                                                  fulfillment_url,
   3762                                                  session_id,
   3763                                                  false,
   3764                                                  &order_id))
   3765   {
   3766     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3767                 "Lookup order by fulfillment failed\n");
   3768     GNUNET_free (order_id);
   3769     return 1;
   3770   }
   3771   if (0 != strcmp (order->id,
   3772                    order_id))
   3773   {
   3774     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3775                 "Lookup order by fulfillment failed\n");
   3776     GNUNET_free (order_id);
   3777     return 1;
   3778   }
   3779   GNUNET_free (order_id);
   3780   return 0;
   3781 }
   3782 
   3783 
   3784 /**
   3785  * Test looking up the status of an order.
   3786  *
   3787  * @param order_id the row of the order in the database.
   3788  * @param session_id the session id associated with the payment.
   3789  * @param expected_paid whether the order was paid or not.
   3790  * @param expected_wired whether the order was wired or not.
   3791  * @return 0 on success, 1 otherwise.
   3792  */
   3793 static int
   3794 test_lookup_payment_status (const char *instance_id,
   3795                             const char *order_id,
   3796                             const char *session_id,
   3797                             bool expected_paid,
   3798                             bool expected_wired)
   3799 {
   3800   bool paid;
   3801   bool wired;
   3802   bool matches;
   3803   uint64_t os;
   3804   int16_t choice_index;
   3805 
   3806   TEST_SET_INSTANCE (instance_id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   3807   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   3808                          TALER_MERCHANTDB_get_contract_terms_status (pg,
   3809                                                                      instance_id,
   3810                                                                      order_id,
   3811                                                                      session_id,
   3812                                                                      NULL,
   3813                                                                      &os,
   3814                                                                      &paid,
   3815                                                                      &wired,
   3816                                                                      &matches,
   3817                                                                      NULL,
   3818                                                                      &choice_index),
   3819                          "Lookup payment status failed\n");
   3820   if ( (NULL != session_id) && (! matches) )
   3821   {
   3822     paid = false;
   3823     wired = false;
   3824   }
   3825   if (expected_wired != wired)
   3826   {
   3827     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3828                 "Lookup payment status for %s/%s failed: wired status is wrong (expected %d got %d)\n",
   3829                 instance_id,
   3830                 order_id,
   3831                 expected_wired,
   3832                 wired);
   3833     return 1;
   3834   }
   3835   if (expected_paid != paid)
   3836   {
   3837     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3838                 "Lookup payment status failed: paid status is wrong\n");
   3839     return 1;
   3840   }
   3841   return 0;
   3842 }
   3843 
   3844 
   3845 /**
   3846  * Test marking an order as being wired.
   3847  *
   3848  * @param order_id the row of the order in the database.
   3849  * @param expected_result the result we expect the DB to return.
   3850  * @return 0 on success, 1 otherwise.
   3851  */
   3852 static int
   3853 test_mark_order_wired (uint64_t order_id,
   3854                        enum GNUNET_DB_QueryStatus expected_result)
   3855 {
   3856   TEST_COND_RET_ON_FAIL (expected_result ==
   3857                          TALER_MERCHANTDB_update_to_contract_terms_wired (pg,
   3858                                                                           order_id),
   3859                          "Mark order wired failed\n");
   3860   return 0;
   3861 }
   3862 
   3863 
   3864 /**
   3865  * Closure for order tests.
   3866  */
   3867 struct TestOrders_Closure
   3868 {
   3869   /**
   3870    * The instance to use for the order tests.
   3871    */
   3872   struct InstanceData instance;
   3873 
   3874   /**
   3875    * A product to use for the order tests.
   3876    */
   3877   struct ProductData product;
   3878 
   3879   /**
   3880    * The array of orders
   3881    */
   3882   struct OrderData orders[3];
   3883 };
   3884 
   3885 
   3886 /**
   3887  * Initializes order test data.
   3888  *
   3889  * @param cls the order test closure.
   3890  */
   3891 static void
   3892 pre_test_orders (struct TestOrders_Closure *cls)
   3893 {
   3894   /* Instance */
   3895   make_instance ("test_inst_orders",
   3896                  &cls->instance);
   3897 
   3898   /* Product */
   3899   make_product ("test_orders_pd_0",
   3900                 &cls->product);
   3901 
   3902   /* Orders */
   3903   make_order ("test_orders_od_0",
   3904               &cls->orders[0]);
   3905   make_order ("test_orders_od_1",
   3906               &cls->orders[1]);
   3907   make_order ("test_orders_od_2",
   3908               &cls->orders[2]);
   3909 
   3910   GNUNET_assert (0 ==
   3911                  json_object_set_new (cls->orders[1].contract,
   3912                                       "other_field",
   3913                                       json_string ("Second contract")));
   3914 
   3915   cls->orders[2].pay_deadline = GNUNET_TIME_UNIT_ZERO_TS;
   3916   GNUNET_assert (0 ==
   3917                  json_object_set_new (
   3918                    cls->orders[2].contract,
   3919                    "pay_deadline",
   3920                    GNUNET_JSON_from_timestamp (cls->orders[2].pay_deadline)));
   3921 }
   3922 
   3923 
   3924 /**
   3925  * Frees memory after order tests.
   3926  *
   3927  * @param cls the order test closure.
   3928  */
   3929 static void
   3930 post_test_orders (struct TestOrders_Closure *cls)
   3931 {
   3932   free_instance_data (&cls->instance);
   3933   free_product_data (&cls->product);
   3934   free_order_data (&cls->orders[0]);
   3935   free_order_data (&cls->orders[1]);
   3936   free_order_data (&cls->orders[2]);
   3937 }
   3938 
   3939 
   3940 /**
   3941  * Run the tests for orders.
   3942  *
   3943  * @param cls the order test closure.
   3944  * @return 0 on success, 1 on failure.
   3945  */
   3946 static int
   3947 run_test_orders (struct TestOrders_Closure *cls)
   3948 {
   3949   struct TALER_MERCHANTDB_OrderFilter filter = {
   3950     .paid = TALER_EXCHANGE_YNA_ALL,
   3951     .refunded = TALER_EXCHANGE_YNA_ALL,
   3952     .wired = TALER_EXCHANGE_YNA_ALL,
   3953     .expired = TALER_EXCHANGE_YNA_ALL,
   3954     .date = GNUNET_TIME_UNIT_ZERO_TS,
   3955     .start_row = 0,
   3956     .delta = 8
   3957   };
   3958   uint64_t serial;
   3959 
   3960   /* Insert the instance */
   3961   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
   3962                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   3963   /* Test inserting an order */
   3964   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
   3965                                        &cls->orders[0],
   3966                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   3967   /* Test double insert fails */
   3968   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
   3969                                        &cls->orders[0],
   3970                                        GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   3971   /* Test lookup order */
   3972   TEST_RET_ON_FAIL (test_lookup_order (&cls->instance,
   3973                                        &cls->orders[0]));
   3974   /* Make sure it fails correctly for nonexistent orders */
   3975   {
   3976     struct TALER_MerchantPostDataHashP unused;
   3977 
   3978     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
   3979         TALER_MERCHANTDB_get_order (pg,
   3980                                     cls->instance.instance.id,
   3981                                     cls->orders[1].id,
   3982                                     NULL,
   3983                                     &unused,
   3984                                     NULL))
   3985     {
   3986       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3987                   "Lookup order failed\n");
   3988       return 1;
   3989     }
   3990   }
   3991   /* Test lookups on multiple orders */
   3992   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
   3993                                        &cls->orders[1],
   3994                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   3995   filter.expired = TALER_EXCHANGE_YNA_NO;
   3996   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
   3997                                         &filter,
   3998                                         2,
   3999                                         cls->orders));
   4000   filter.expired = TALER_EXCHANGE_YNA_YES;
   4001   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
   4002                                         &filter,
   4003                                         0,
   4004                                         NULL));
   4005   filter.expired = TALER_EXCHANGE_YNA_ALL;
   4006   serial = get_order_serial (&cls->instance,
   4007                              &cls->orders[0]);
   4008   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
   4009                                         &filter,
   4010                                         2,
   4011                                         cls->orders));
   4012   /* Test inserting contract terms */
   4013   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
   4014                                                 &cls->orders[0],
   4015                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   4016   /* Test double insert fails */
   4017   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
   4018                                                 &cls->orders[0],
   4019                                                 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   4020   /* Test order lock */
   4021   TEST_RET_ON_FAIL (test_insert_product (&cls->instance,
   4022                                          &cls->product,
   4023                                          0,
   4024                                          NULL,
   4025                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   4026                                          false,
   4027                                          false,
   4028                                          -1));
   4029   if (1 != TALER_MERCHANTDB_insert_order_lock (pg,
   4030                                                cls->instance.instance.id,
   4031                                                cls->orders[0].id,
   4032                                                cls->product.id,
   4033                                                5,
   4034                                                0))
   4035   {
   4036     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4037                 "Insert order lock failed\n");
   4038     return 1;
   4039   }
   4040   /* Test lookup contract terms */
   4041   TEST_RET_ON_FAIL (test_lookup_contract_terms (&cls->instance,
   4042                                                 &cls->orders[0]));
   4043   /* Test lookup fails for nonexistent contract terms */
   4044   {
   4045     json_t *lookup_contract = NULL;
   4046     uint64_t lookup_order_serial;
   4047 
   4048     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
   4049         TALER_MERCHANTDB_get_contract_terms (pg,
   4050                                              cls->instance.instance.id,
   4051                                              cls->orders[1].id,
   4052                                              &lookup_contract,
   4053                                              &lookup_order_serial,
   4054                                              NULL))
   4055     {
   4056       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4057                   "Lookup contract terms failed\n");
   4058       GNUNET_assert (NULL == lookup_contract);
   4059       return 1;
   4060     }
   4061   }
   4062   /* Test update contract terms */
   4063   GNUNET_assert (0 ==
   4064                  json_object_set_new (cls->orders[0].contract,
   4065                                       "some_new_field",
   4066                                       json_string ("another value")));
   4067   TEST_RET_ON_FAIL (test_update_contract_terms (&cls->instance,
   4068                                                 &cls->orders[0],
   4069                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   4070   TEST_RET_ON_FAIL (test_lookup_contract_terms (&cls->instance,
   4071                                                 &cls->orders[0]));
   4072   /* Test lookup order status */
   4073   TEST_RET_ON_FAIL (test_lookup_order_status (&cls->instance,
   4074                                               &cls->orders[0],
   4075                                               false));
   4076   {
   4077     struct TALER_PrivateContractHashP h_contract_terms;
   4078     bool order_paid = false;
   4079 
   4080     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
   4081         TALER_MERCHANTDB_get_order_status (pg,
   4082                                            cls->instance.instance.id,
   4083                                            cls->orders[1].id,
   4084                                            &h_contract_terms,
   4085                                            &order_paid))
   4086     {
   4087       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4088                   "Lookup order status failed\n");
   4089       return 1;
   4090     }
   4091   }
   4092   /* Test lookup payment status */
   4093   TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
   4094                                                 cls->orders[0].id,
   4095                                                 NULL,
   4096                                                 false,
   4097                                                 false));
   4098   /* Test lookup order status fails for nonexistent order */
   4099   {
   4100     struct TALER_PrivateContractHashP h_contract_terms;
   4101     bool order_paid;
   4102 
   4103     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
   4104         TALER_MERCHANTDB_get_order_status (pg,
   4105                                            cls->instance.instance.id,
   4106                                            cls->orders[1].id,
   4107                                            &h_contract_terms,
   4108                                            &order_paid))
   4109     {
   4110       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4111                   "Lookup order status failed\n");
   4112       return 1;
   4113     }
   4114   }
   4115   /* Test marking contracts as paid */
   4116   TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
   4117                                              &cls->orders[0],
   4118                                              GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   4119   TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
   4120                                                 cls->orders[0].id,
   4121                                                 NULL,
   4122                                                 true,
   4123                                                 false));
   4124   TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
   4125                                                 cls->orders[0].id,
   4126                                                 "test_orders_session",
   4127                                                 true,
   4128                                                 false));
   4129   TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
   4130                                                 cls->orders[0].id,
   4131                                                 "bad_session",
   4132                                                 false,
   4133                                                 false));
   4134   /* Test lookup order by fulfillment */
   4135   TEST_RET_ON_FAIL (test_lookup_order_by_fulfillment (&cls->instance,
   4136                                                       &cls->orders[0],
   4137                                                       "test_orders_session"));
   4138   {
   4139     char *order_id;
   4140     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
   4141         TALER_MERCHANTDB_get_order_by_fulfillment (pg,
   4142                                                    cls->instance.instance.id,
   4143                                                    "fulfillment_url",
   4144                                                    "test_orders_session",
   4145                                                    false,
   4146                                                    &order_id))
   4147     {
   4148       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4149                   "Lookup order by fulfillment failed\n");
   4150       GNUNET_free (order_id);
   4151       return 1;
   4152     }
   4153   }
   4154   /* Test mark as paid fails for nonexistent order */
   4155   TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
   4156                                              &cls->orders[1],
   4157                                              GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   4158   TEST_RET_ON_FAIL (test_lookup_order_status (&cls->instance,
   4159                                               &cls->orders[0],
   4160                                               true));
   4161   filter.paid = TALER_EXCHANGE_YNA_ALL;
   4162   filter.expired = TALER_EXCHANGE_YNA_YES;
   4163   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
   4164                                         &filter,
   4165                                         0,
   4166                                         NULL));
   4167   filter.expired = TALER_EXCHANGE_YNA_ALL;
   4168   filter.paid = TALER_EXCHANGE_YNA_YES;
   4169   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
   4170                                         &filter,
   4171                                         1,
   4172                                         cls->orders));
   4173   /* Test marking orders as wired */
   4174   TEST_RET_ON_FAIL (test_mark_order_wired (serial,
   4175                                            GNUNET_DB_STATUS_SUCCESS_ONE_RESULT))
   4176   ;
   4177   TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
   4178                                                 cls->orders[0].id,
   4179                                                 NULL,
   4180                                                 true,
   4181                                                 true));
   4182   TEST_RET_ON_FAIL (test_mark_order_wired (1007,
   4183                                            GNUNET_DB_STATUS_SUCCESS_NO_RESULTS))
   4184   ;
   4185   /* If an order has been claimed and we aren't past
   4186      the pay deadline, we can't delete it. */
   4187   TEST_RET_ON_FAIL (test_delete_order (&cls->instance,
   4188                                        &cls->orders[0],
   4189                                        GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   4190   /* Test we can't delete before the legal expiration */
   4191   TEST_RET_ON_FAIL (test_delete_contract_terms (&cls->instance,
   4192                                                 &cls->orders[0],
   4193                                                 GNUNET_TIME_UNIT_MONTHS,
   4194                                                 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   4195   /* Test deleting contract terms */
   4196   TEST_RET_ON_FAIL (test_delete_contract_terms (&cls->instance,
   4197                                                 &cls->orders[0],
   4198                                                 GNUNET_TIME_UNIT_ZERO,
   4199                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   4200   /* Test we can't delete something that doesn't exist */
   4201   TEST_RET_ON_FAIL (test_delete_contract_terms (&cls->instance,
   4202                                                 &cls->orders[0],
   4203                                                 GNUNET_TIME_UNIT_ZERO,
   4204                                                 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   4205   /* Test delete order where we aren't past
   4206      the deadline, but the order is unclaimed. */
   4207   TEST_RET_ON_FAIL (test_delete_order (&cls->instance,
   4208                                        &cls->orders[1],
   4209                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   4210   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
   4211                                         &filter,
   4212                                         0,
   4213                                         NULL));
   4214   /* Test we can't delete something that doesn't exist */
   4215   TEST_RET_ON_FAIL (test_delete_order (&cls->instance,
   4216                                        &cls->orders[1],
   4217                                        GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   4218 
   4219   /* Test we can also delete a claimed order that's past the pay deadline. */
   4220   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
   4221                                        &cls->orders[2],
   4222                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   4223   filter.paid = TALER_EXCHANGE_YNA_ALL;
   4224   filter.expired = TALER_EXCHANGE_YNA_YES;
   4225   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
   4226                                         &filter,
   4227                                         1,
   4228                                         &cls->orders[2]));
   4229   filter.expired = TALER_EXCHANGE_YNA_NO;
   4230   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
   4231                                         &filter,
   4232                                         0,
   4233                                         NULL));
   4234   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
   4235                                                 &cls->orders[2],
   4236                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   4237   filter.expired = TALER_EXCHANGE_YNA_YES;
   4238   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
   4239                                         &filter,
   4240                                         1,
   4241                                         &cls->orders[2]));
   4242   filter.expired = TALER_EXCHANGE_YNA_ALL;
   4243   TEST_RET_ON_FAIL (test_delete_order (&cls->instance,
   4244                                        &cls->orders[2],
   4245                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   4246 
   4247   /* Deleting an order and its contract terms must be atomic.
   4248      orders[1] was fully removed above, so we can re-create it, this
   4249      time claimed (=> with contract terms) and unpaid. */
   4250   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
   4251                                        &cls->orders[1],
   4252                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   4253   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
   4254                                                 &cls->orders[1],
   4255                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   4256   TEST_RET_ON_FAIL (test_delete_order_atomic (&cls->instance,
   4257                                               &cls->orders[1]));
   4258   return 0;
   4259 }
   4260 
   4261 
   4262 /**
   4263  * Does all tasks for testing orders.
   4264  *
   4265  * @return 0 when successful, 1 otherwise.
   4266  */
   4267 static int
   4268 test_orders (void)
   4269 {
   4270   struct TestOrders_Closure test_cls;
   4271   int test_result;
   4272 
   4273   pre_test_orders (&test_cls);
   4274   test_result = run_test_orders (&test_cls);
   4275   post_test_orders (&test_cls);
   4276   return test_result;
   4277 }
   4278 
   4279 
   4280 /* ********** Deposits ********** */
   4281 
   4282 
   4283 /**
   4284  * A container for exchange signing key data.
   4285  */
   4286 struct ExchangeSignkeyData
   4287 {
   4288   /**
   4289    * The master private key of the exchange.
   4290    */
   4291   struct TALER_MasterPrivateKeyP master_priv;
   4292 
   4293   /**
   4294    * The master public key of the exchange.
   4295    */
   4296   struct TALER_MasterPublicKeyP master_pub;
   4297 
   4298   /**
   4299    * A signature made with the master keys.
   4300    */
   4301   struct TALER_MasterSignatureP master_sig;
   4302 
   4303   /**
   4304    * The private key of the exchange.
   4305    */
   4306   struct TALER_ExchangePrivateKeyP exchange_priv;
   4307 
   4308   /**
   4309    * The public key of the exchange.
   4310    */
   4311   struct TALER_ExchangePublicKeyP exchange_pub;
   4312 
   4313   /**
   4314    * When the signing key becomes valid.
   4315    */
   4316   struct GNUNET_TIME_Timestamp start_date;
   4317 
   4318   /**
   4319    * When the signing key stops being used.
   4320    */
   4321   struct GNUNET_TIME_Timestamp expire_date;
   4322 
   4323   /**
   4324    * When the signing key becomes invalid for proof.
   4325    */
   4326   struct GNUNET_TIME_Timestamp end_date;
   4327 };
   4328 
   4329 
   4330 /**
   4331  * Creates an exchange signing key.
   4332  *
   4333  * @param signkey the signing key data to fill.
   4334  */
   4335 static void
   4336 make_exchange_signkey (struct ExchangeSignkeyData *signkey)
   4337 {
   4338   struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
   4339 
   4340   GNUNET_CRYPTO_eddsa_key_create (&signkey->exchange_priv.eddsa_priv);
   4341   GNUNET_CRYPTO_eddsa_key_get_public (&signkey->exchange_priv.eddsa_priv,
   4342                                       &signkey->exchange_pub.eddsa_pub);
   4343   GNUNET_CRYPTO_eddsa_key_create (&signkey->master_priv.eddsa_priv);
   4344   GNUNET_CRYPTO_eddsa_key_get_public (&signkey->master_priv.eddsa_priv,
   4345                                       &signkey->master_pub.eddsa_pub);
   4346   signkey->start_date = now;
   4347   signkey->expire_date = now;
   4348   signkey->end_date = now;
   4349   TALER_exchange_offline_signkey_validity_sign (
   4350     &signkey->exchange_pub,
   4351     signkey->start_date,
   4352     signkey->expire_date,
   4353     signkey->end_date,
   4354     &signkey->master_priv,
   4355     &signkey->master_sig);
   4356 }
   4357 
   4358 
   4359 /**
   4360  * A container for deposit data.
   4361  */
   4362 struct DepositData
   4363 {
   4364   /**
   4365    * When the deposit was made.
   4366    */
   4367   struct GNUNET_TIME_Timestamp timestamp;
   4368 
   4369   /**
   4370    * Hash of the associated order's contract terms.
   4371    */
   4372   struct TALER_PrivateContractHashP h_contract_terms;
   4373 
   4374   /**
   4375    * Public key of the coin that has been deposited.
   4376    */
   4377   struct TALER_CoinSpendPublicKeyP coin_pub;
   4378 
   4379   /**
   4380    * Signature of the coin that has been deposited.
   4381    */
   4382   struct TALER_CoinSpendSignatureP coin_sig;
   4383 
   4384   /**
   4385    * URL of the exchange.
   4386    */
   4387   const char *exchange_url;
   4388 
   4389   /**
   4390    * Value of the coin with fees applied.
   4391    */
   4392   struct TALER_Amount amount_with_fee;
   4393 
   4394   /**
   4395    * Fee charged for deposit.
   4396    */
   4397   struct TALER_Amount deposit_fee;
   4398 
   4399   /**
   4400    * Fee to be charged in case of a refund.
   4401    */
   4402   struct TALER_Amount refund_fee;
   4403 
   4404   /**
   4405    * Fee charged after the money is wired.
   4406    */
   4407   struct TALER_Amount wire_fee;
   4408 
   4409   /**
   4410    * Hash of the wire details.
   4411    */
   4412   struct TALER_MerchantWireHashP h_wire;
   4413 
   4414   /**
   4415    * Signature the exchange made on this deposit.
   4416    */
   4417   struct TALER_ExchangeSignatureP exchange_sig;
   4418 
   4419 };
   4420 
   4421 
   4422 /**
   4423  * Generates deposit data for an order.
   4424  *
   4425  * @param instance the instance to make the deposit to.
   4426  * @param account the merchant account to use.
   4427  * @param order the order this deposit is for.
   4428  * @param signkey the signing key to use.
   4429  * @param deposit the deposit data to fill.
   4430  */
   4431 static void
   4432 make_deposit (const struct InstanceData *instance,
   4433               const struct TALER_MERCHANTDB_AccountDetails *account,
   4434               const struct OrderData *order,
   4435               const struct ExchangeSignkeyData *signkey,
   4436               struct DepositData *deposit)
   4437 {
   4438   struct TALER_CoinSpendPrivateKeyP coin_priv;
   4439   struct GNUNET_TIME_Timestamp now;
   4440   struct TALER_Amount amount_without_fee;
   4441 
   4442   now = GNUNET_TIME_timestamp_get ();
   4443   deposit->timestamp = now;
   4444   GNUNET_assert (GNUNET_OK ==
   4445                  TALER_JSON_contract_hash (order->contract,
   4446                                            &deposit->h_contract_terms));
   4447   GNUNET_CRYPTO_eddsa_key_create (&coin_priv.eddsa_priv);
   4448   GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv,
   4449                                       &deposit->coin_pub.eddsa_pub);
   4450   deposit->exchange_url = "https://test-exchange/";
   4451   GNUNET_assert (GNUNET_OK ==
   4452                  TALER_string_to_amount ("EUR:50.00",
   4453                                          &deposit->amount_with_fee));
   4454   GNUNET_assert (GNUNET_OK ==
   4455                  TALER_string_to_amount ("EUR:1.00",
   4456                                          &deposit->deposit_fee));
   4457   GNUNET_assert (GNUNET_OK ==
   4458                  TALER_string_to_amount ("EUR:1.50",
   4459                                          &deposit->refund_fee));
   4460   GNUNET_assert (GNUNET_OK ==
   4461                  TALER_string_to_amount ("EUR:2.00",
   4462                                          &deposit->wire_fee));
   4463   GNUNET_assert (0 <=
   4464                  TALER_amount_subtract (&amount_without_fee,
   4465                                         &deposit->amount_with_fee,
   4466                                         &deposit->deposit_fee));
   4467   deposit->h_wire = account->h_wire;
   4468   GNUNET_CRYPTO_random_block (&deposit->exchange_sig,
   4469                               sizeof (deposit->exchange_sig));
   4470   GNUNET_CRYPTO_random_block (&deposit->coin_sig,
   4471                               sizeof (deposit->coin_sig));
   4472 }
   4473 
   4474 
   4475 /**
   4476  * Tests inserting an exchange signing key into the database.
   4477  *
   4478  * @param signkey the signing key to insert.
   4479  * @param expected_result the result we expect the database to return.
   4480  * @return 0 on success, 1 otherwise.
   4481  */
   4482 static int
   4483 test_insert_exchange_signkey (const struct ExchangeSignkeyData *signkey,
   4484                               enum GNUNET_DB_QueryStatus expected_result)
   4485 {
   4486   TEST_COND_RET_ON_FAIL (expected_result ==
   4487                          TALER_MERCHANTDB_insert_exchange_signing_key (pg,
   4488                                                                        &signkey->master_pub,
   4489                                                                        &signkey->exchange_pub
   4490                                                                        ,
   4491                                                                        signkey->start_date,
   4492                                                                        signkey->expire_date,
   4493                                                                        signkey->end_date,
   4494                                                                        &signkey->master_sig),
   4495                          "Insert exchange signkey failed\n");
   4496   return 0;
   4497 }
   4498 
   4499 
   4500 /**
   4501  * Tests inserting a deposit into the database.
   4502  *
   4503  * @param instance the instance the deposit was made to.
   4504  * @param signkey the signing key used.
   4505  * @param deposit the deposit information to insert.
   4506  * @param expected_result the result we expect the database to return.
   4507  * @return 0 on success, 1 otherwise.
   4508  */
   4509 static int
   4510 test_insert_deposit (const struct InstanceData *instance,
   4511                      const struct ExchangeSignkeyData *signkey,
   4512                      const struct DepositData *deposit,
   4513                      enum GNUNET_DB_QueryStatus expected_result)
   4514 {
   4515   uint64_t row;
   4516   struct TALER_Amount awf;
   4517 
   4518   GNUNET_assert (0 <=
   4519                  TALER_amount_subtract (&awf,
   4520                                         &deposit->amount_with_fee,
   4521                                         &deposit->deposit_fee));
   4522   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   4523   TEST_COND_RET_ON_FAIL (
   4524     TALER_MERCHANTDB_DCS_SUCCESS ==
   4525     TALER_MERCHANTDB_insert_deposit_confirmation (pg,
   4526                                                   instance->instance.id,
   4527                                                   deposit->timestamp,
   4528                                                   &deposit->h_contract_terms,
   4529                                                   deposit->exchange_url,
   4530                                                   deposit->timestamp,
   4531                                                   &awf,
   4532                                                   &deposit->wire_fee,
   4533                                                   &deposit->h_wire,
   4534                                                   &deposit->exchange_sig,
   4535                                                   &signkey->exchange_pub,
   4536                                                   &row),
   4537     "Insert deposit confirmation failed\n");
   4538   TEST_COND_RET_ON_FAIL (
   4539     expected_result ==
   4540     TALER_MERCHANTDB_insert_deposit (pg,
   4541                                      0,
   4542                                      row,
   4543                                      &deposit->coin_pub,
   4544                                      &deposit->coin_sig,
   4545                                      &deposit->amount_with_fee,
   4546                                      &deposit->deposit_fee,
   4547                                      &deposit->refund_fee,
   4548                                      GNUNET_TIME_absolute_get ()),
   4549     "Insert deposit failed\n");
   4550   return 0;
   4551 }
   4552 
   4553 
   4554 /**
   4555  * Tests that storing a deposit confirmation returns a status that
   4556  * identifies the *specific* reason why it could not be stored.
   4557  * Collapsing all of them into #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
   4558  * (as we used to) makes the pay handler commit a batch deposit
   4559  * without any deposit records, silently losing the money.
   4560  *
   4561  * @param instance the instance the deposit was made to.
   4562  * @param deposit the deposit to derive the (valid) defaults from.
   4563  * @param h_contract_terms contract to claim the deposit is for.
   4564  * @param h_wire target account to claim the deposit went to.
   4565  * @param exchange_pub exchange signing key to use.
   4566  * @param wire_fee wire fee to claim was charged.
   4567  * @param expected_status status the database should return.
   4568  * @return 0 on success, 1 otherwise.
   4569  */
   4570 static int
   4571 test_insert_deposit_confirmation_status (
   4572   const struct InstanceData *instance,
   4573   const struct DepositData *deposit,
   4574   const struct TALER_PrivateContractHashP *h_contract_terms,
   4575   const struct TALER_MerchantWireHashP *h_wire,
   4576   const struct TALER_ExchangePublicKeyP *exchange_pub,
   4577   const struct TALER_Amount *wire_fee,
   4578   enum TALER_MERCHANTDB_DepositConfirmationStatus expected_status)
   4579 {
   4580   uint64_t row = 0;
   4581   struct TALER_Amount awf;
   4582 
   4583   GNUNET_assert (0 <=
   4584                  TALER_amount_subtract (&awf,
   4585                                         &deposit->amount_with_fee,
   4586                                         &deposit->deposit_fee));
   4587   TEST_SET_INSTANCE (instance->instance.id,
   4588                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   4589   TEST_COND_RET_ON_FAIL (
   4590     expected_status ==
   4591     TALER_MERCHANTDB_insert_deposit_confirmation (pg,
   4592                                                   instance->instance.id,
   4593                                                   deposit->timestamp,
   4594                                                   h_contract_terms,
   4595                                                   deposit->exchange_url,
   4596                                                   deposit->timestamp,
   4597                                                   &awf,
   4598                                                   wire_fee,
   4599                                                   h_wire,
   4600                                                   &deposit->exchange_sig,
   4601                                                   exchange_pub,
   4602                                                   &row),
   4603     "Insert deposit confirmation returned the wrong status\n");
   4604   return 0;
   4605 }
   4606 
   4607 
   4608 /**
   4609  * Closure for testing deposit lookup
   4610  */
   4611 struct TestLookupDeposits_Closure
   4612 {
   4613   /**
   4614    * Number of deposits to compare to
   4615    */
   4616   unsigned int deposits_to_cmp_length;
   4617 
   4618   /**
   4619    * Pointer to array of deposit data
   4620    */
   4621   const struct DepositData *deposits_to_cmp;
   4622 
   4623   /**
   4624    * Pointer to array of number of matches per deposit
   4625    */
   4626   unsigned int *results_matching;
   4627 
   4628   /**
   4629    * Total number of results returned
   4630    */
   4631   unsigned int results_length;
   4632 };
   4633 
   4634 
   4635 /**
   4636  * Called after 'test_lookup_deposits'.
   4637  *
   4638  * @param cls pointer to the test lookup closure.
   4639  * @param coin_pub public key of the coin deposited.
   4640  * @param amount_with_fee amount of the deposit with fees.
   4641  * @param deposit_fee fee charged for the deposit.
   4642  * @param refund_fee fee charged in case of a refund.
   4643  * @param wire_fee wire transfer fee to be paid
   4644  */
   4645 static void
   4646 lookup_deposits_cb (void *cls,
   4647                     const char *exchange_url,
   4648                     const struct TALER_CoinSpendPublicKeyP *coin_pub,
   4649                     const struct TALER_Amount *amount_with_fee,
   4650                     const struct TALER_Amount *deposit_fee,
   4651                     const struct TALER_Amount *refund_fee,
   4652                     const struct TALER_Amount *wire_fee)
   4653 {
   4654   struct TestLookupDeposits_Closure *cmp = cls;
   4655 
   4656   if (NULL == cmp)
   4657     return;
   4658   cmp->results_length += 1;
   4659   for (unsigned int i = 0; cmp->deposits_to_cmp_length > i; ++i)
   4660   {
   4661     if ((GNUNET_OK ==
   4662          TALER_amount_cmp_currency (
   4663            &cmp->deposits_to_cmp[i].amount_with_fee,
   4664            amount_with_fee)) &&
   4665         (0 ==
   4666          TALER_amount_cmp (&cmp->deposits_to_cmp[i].amount_with_fee,
   4667                            amount_with_fee)) &&
   4668         (GNUNET_OK ==
   4669          TALER_amount_cmp_currency (
   4670            &cmp->deposits_to_cmp[i].deposit_fee,
   4671            deposit_fee)) &&
   4672         (0 ==
   4673          TALER_amount_cmp (&cmp->deposits_to_cmp[i].deposit_fee,
   4674                            deposit_fee)) &&
   4675         (GNUNET_OK ==
   4676          TALER_amount_cmp_currency (
   4677            &cmp->deposits_to_cmp[i].refund_fee,
   4678            refund_fee)) &&
   4679         (0 ==
   4680          TALER_amount_cmp (&cmp->deposits_to_cmp[i].refund_fee,
   4681                            refund_fee)))
   4682     {
   4683       cmp->results_matching[i] += 1;
   4684     }
   4685 
   4686   }
   4687 }
   4688 
   4689 
   4690 /**
   4691  * Tests looking up deposits from the database.
   4692  *
   4693  * @param instance the instance to lookup deposits from.
   4694  * @param h_contract_terms the contract terms that the deposits should have.
   4695  * @param deposits_length length of @e deposits.
   4696  * @param deposits the deposits we expect to be found.
   4697  * @return 0 on success, 1 otherwise.
   4698  */
   4699 static int
   4700 test_lookup_deposits (const struct InstanceData *instance,
   4701                       const struct TALER_PrivateContractHashP *h_contract_terms,
   4702                       unsigned int deposits_length,
   4703                       const struct DepositData *deposits)
   4704 {
   4705   unsigned int results_matching[GNUNET_NZL (deposits_length)];
   4706   struct TestLookupDeposits_Closure cmp = {
   4707     .deposits_to_cmp_length = deposits_length,
   4708     .deposits_to_cmp = deposits,
   4709     .results_matching = results_matching,
   4710     .results_length = 0
   4711   };
   4712   memset (results_matching,
   4713           0,
   4714           sizeof (unsigned int) * deposits_length);
   4715   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   4716   TEST_COND_RET_ON_FAIL (0 <=
   4717                          TALER_MERCHANTDB_iterate_deposits (pg,
   4718                                                             instance->instance.id,
   4719                                                             h_contract_terms,
   4720                                                             &lookup_deposits_cb,
   4721                                                             &cmp),
   4722                          "Lookup deposits failed\n");
   4723   if (deposits_length != cmp.results_length)
   4724   {
   4725     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4726                 "Lookup deposits failed: incorrect number of results returned (%d)\n",
   4727                 cmp.results_length);
   4728     return 1;
   4729   }
   4730   for (unsigned int i = 0; deposits_length > i; ++i)
   4731   {
   4732     if (cmp.results_matching[i] != 1)
   4733     {
   4734       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4735                   "Lookup deposits failed: mismatched data\n");
   4736       return 1;
   4737     }
   4738   }
   4739   return 0;
   4740 }
   4741 
   4742 
   4743 /**
   4744  * Called after 'test_lookup_deposits_contract_and_coin'.
   4745  *
   4746  * @param cls pointer to the test lookup closure.
   4747  * @param exchange_url URL to the exchange
   4748  * @param amount_with_fee amount of the deposit with fees.
   4749  * @param deposit_fee fee charged for the deposit.
   4750  * @param refund_fee fee charged in case of a refund.
   4751  * @param wire_fee fee charged when the money is wired.
   4752  * @param h_wire hash of the wire transfer details.
   4753  * @param deposit_timestamp when the deposit was made.
   4754  * @param refund_deadline deadline for refunding the deposit.
   4755  * @param exchange_sig signature the exchange made on the deposit.
   4756  * @param exchange_pub public key of the exchange.
   4757  */
   4758 static void
   4759 lookup_deposits_contract_coin_cb (
   4760   void *cls,
   4761   const char *exchange_url,
   4762   const struct TALER_Amount *amount_with_fee,
   4763   const struct TALER_Amount *deposit_fee,
   4764   const struct TALER_Amount *refund_fee,
   4765   const struct TALER_Amount *wire_fee,
   4766   const struct TALER_MerchantWireHashP *h_wire,
   4767   struct GNUNET_TIME_Timestamp deposit_timestamp,
   4768   struct GNUNET_TIME_Timestamp refund_deadline,
   4769   const struct TALER_ExchangeSignatureP *exchange_sig,
   4770   const struct TALER_ExchangePublicKeyP *exchange_pub)
   4771 {
   4772   struct TestLookupDeposits_Closure *cmp = cls;
   4773 
   4774   if (NULL == cmp)
   4775     return;
   4776   cmp->results_length++;
   4777   for (unsigned int i = 0; cmp->deposits_to_cmp_length > i; ++i)
   4778   {
   4779     if ((GNUNET_TIME_timestamp_cmp (cmp->deposits_to_cmp[i].timestamp,
   4780                                     ==,
   4781                                     deposit_timestamp)) &&
   4782         (0 == strcmp (cmp->deposits_to_cmp[i].exchange_url,
   4783                       exchange_url)) &&
   4784         (GNUNET_OK == TALER_amount_cmp_currency (
   4785            &cmp->deposits_to_cmp[i].amount_with_fee,
   4786            amount_with_fee)) &&
   4787         (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].amount_with_fee,
   4788                                 amount_with_fee)) &&
   4789         (GNUNET_OK == TALER_amount_cmp_currency (
   4790            &cmp->deposits_to_cmp[i].deposit_fee,
   4791            deposit_fee)) &&
   4792         (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].deposit_fee,
   4793                                 deposit_fee)) &&
   4794         (GNUNET_OK == TALER_amount_cmp_currency (
   4795            &cmp->deposits_to_cmp[i].refund_fee,
   4796            refund_fee)) &&
   4797         (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].refund_fee,
   4798                                 refund_fee)) &&
   4799         (GNUNET_OK == TALER_amount_cmp_currency (
   4800            &cmp->deposits_to_cmp[i].wire_fee,
   4801            wire_fee)) &&
   4802         (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].wire_fee,
   4803                                 wire_fee)) &&
   4804         (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].h_wire,
   4805                              h_wire)) &&
   4806         (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].exchange_sig,
   4807                              exchange_sig)))
   4808     {
   4809       cmp->results_matching[i]++;
   4810     }
   4811   }
   4812 }
   4813 
   4814 
   4815 /**
   4816  * Tests lookup of deposits by contract and coin.
   4817  *
   4818  * @param instance the instance to lookup from.
   4819  * @param h_contract the contract terms the deposits should have.
   4820  * @param coin_pub the public key of the coin the deposits should have.
   4821  * @param deposits_length length of @e deposits.
   4822  * @param deposits the deposits the db is expected to find.
   4823  * @return 0 on success, 1 otherwise.
   4824  */
   4825 static int
   4826 test_lookup_deposits_contract_and_coin (
   4827   const struct InstanceData *instance,
   4828   const struct TALER_PrivateContractHashP *h_contract,
   4829   const struct TALER_CoinSpendPublicKeyP *coin_pub,
   4830   unsigned int deposits_length,
   4831   const struct DepositData *deposits)
   4832 {
   4833   unsigned int results_matching[deposits_length];
   4834   struct TestLookupDeposits_Closure cmp = {
   4835     .deposits_to_cmp_length = deposits_length,
   4836     .deposits_to_cmp = deposits,
   4837     .results_matching = results_matching,
   4838     .results_length = 0
   4839   };
   4840   memset (results_matching,
   4841           0,
   4842           sizeof (unsigned int) * deposits_length);
   4843   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   4844   TEST_COND_RET_ON_FAIL (
   4845     GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   4846     TALER_MERCHANTDB_iterate_deposits_by_contract_and_coin (
   4847       pg,
   4848       instance->instance.id,
   4849       h_contract,
   4850       coin_pub,
   4851       &lookup_deposits_contract_coin_cb,
   4852       &cmp),
   4853     "Lookup deposits by contract and coin failed\n");
   4854   if (deposits_length != cmp.results_length)
   4855   {
   4856     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4857                 "Lookup deposits failed: incorrect number of results returned\n");
   4858     return 1;
   4859   }
   4860   for (unsigned int i = 0; deposits_length > i; ++i)
   4861   {
   4862     if (cmp.results_matching[i] != 1)
   4863     {
   4864       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4865                   "Lookup deposits failed: mismatched data\n");
   4866       return 1;
   4867     }
   4868   }
   4869   return 0;
   4870 }
   4871 
   4872 
   4873 /**
   4874  * Called after 'test_lookup_deposits_by_order'.
   4875  *
   4876  * @param cls pointer to the test lookup closure.
   4877  * @param deposit_serial row number of the deposit in the database.
   4878  * @param exchange_url URL to the exchange
   4879  * @param h_wire hash of the wire transfer details.
   4880  * @param deposit_timestamp when was the deposit made
   4881  * @param amount_with_fee amount of the deposit with fees.
   4882  * @param deposit_fee fee charged for the deposit.
   4883  * @param coin_pub public key of the coin deposited.
   4884  */
   4885 static void
   4886 lookup_deposits_order_cb (void *cls,
   4887                           uint64_t deposit_serial,
   4888                           const char *exchange_url,
   4889                           const struct TALER_MerchantWireHashP *h_wire,
   4890                           struct GNUNET_TIME_Timestamp deposit_timestamp,
   4891                           const struct TALER_Amount *amount_with_fee,
   4892                           const struct TALER_Amount *deposit_fee,
   4893                           const struct TALER_CoinSpendPublicKeyP *coin_pub)
   4894 {
   4895   struct TestLookupDeposits_Closure *cmp = cls;
   4896 
   4897   if (NULL == cmp)
   4898     return;
   4899   cmp->results_length += 1;
   4900   for (unsigned int i = 0; i < cmp->deposits_to_cmp_length; ++i)
   4901   {
   4902     if ((0 == strcmp (cmp->deposits_to_cmp[i].exchange_url,
   4903                       exchange_url)) &&
   4904         (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].h_wire,
   4905                              h_wire)) &&
   4906         (GNUNET_OK == TALER_amount_cmp_currency (
   4907            &cmp->deposits_to_cmp[i].amount_with_fee,
   4908            amount_with_fee)) &&
   4909         (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].amount_with_fee,
   4910                                 amount_with_fee)) &&
   4911         (GNUNET_OK == TALER_amount_cmp_currency (
   4912            &cmp->deposits_to_cmp[i].deposit_fee,
   4913            deposit_fee)) &&
   4914         (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].deposit_fee,
   4915                                 deposit_fee)) &&
   4916         (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].coin_pub,
   4917                              coin_pub)))
   4918       cmp->results_matching[i] += 1;
   4919   }
   4920 }
   4921 
   4922 
   4923 /**
   4924  * Tests looking up deposits by associated order.
   4925  *
   4926  * @param order_serial row number of the order to lookup for.
   4927  * @param deposits_length length of @e deposits_length.
   4928  * @param deposits the deposits we expect to be found.
   4929  * @return 0 on success, 1 otherwise.
   4930  */
   4931 static int
   4932 test_lookup_deposits_by_order (uint64_t order_serial,
   4933                                unsigned int deposits_length,
   4934                                const struct DepositData *deposits)
   4935 {
   4936   unsigned int results_matching[deposits_length];
   4937   struct TestLookupDeposits_Closure cmp = {
   4938     .deposits_to_cmp_length = deposits_length,
   4939     .deposits_to_cmp = deposits,
   4940     .results_matching = results_matching,
   4941     .results_length = 0
   4942   };
   4943   memset (results_matching,
   4944           0,
   4945           sizeof (unsigned int) * deposits_length);
   4946   if (deposits_length !=
   4947       TALER_MERCHANTDB_iterate_deposits_by_order (pg,
   4948                                                   order_serial,
   4949                                                   &lookup_deposits_order_cb,
   4950                                                   &cmp))
   4951   {
   4952     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4953                 "Lookup deposits by order failed\n");
   4954     return 1;
   4955   }
   4956   if (deposits_length != cmp.results_length)
   4957   {
   4958     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4959                 "Lookup deposits by order failed: incorrect number of results\n");
   4960     return 1;
   4961   }
   4962   for (unsigned int i = 0; i < deposits_length; ++i)
   4963   {
   4964     if (1 != cmp.results_matching[i])
   4965     {
   4966       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4967                   "Lookup deposits by order failed: mismatched data\n");
   4968       return 1;
   4969     }
   4970   }
   4971   return 0;
   4972 }
   4973 
   4974 
   4975 /**
   4976  * Container for information for looking up the row number of a deposit.
   4977  */
   4978 struct LookupDepositSerial_Closure
   4979 {
   4980   /**
   4981    * The deposit we're looking for.
   4982    */
   4983   const struct DepositData *deposit;
   4984 
   4985   /**
   4986    * The serial found.
   4987    */
   4988   uint64_t serial;
   4989 };
   4990 
   4991 
   4992 /**
   4993  * Called after 'get_deposit_serial'.
   4994  *
   4995  * @param cls pointer to the test lookup closure.
   4996  * @param deposit_serial row number of the deposit in the database.
   4997  * @param exchange_url URL to the exchange
   4998  * @param h_wire hash of the wire transfer details.
   4999  * @param deposit_timestamp when was the deposit made.
   5000  * @param amount_with_fee amount of the deposit with fees.
   5001  * @param deposit_fee fee charged for the deposit.
   5002  * @param coin_pub public key of the coin deposited.
   5003  */
   5004 static void
   5005 get_deposit_serial_cb (void *cls,
   5006                        uint64_t deposit_serial,
   5007                        const char *exchange_url,
   5008                        const struct TALER_MerchantWireHashP *h_wire,
   5009                        struct GNUNET_TIME_Timestamp deposit_timestamp,
   5010                        const struct TALER_Amount *amount_with_fee,
   5011                        const struct TALER_Amount *deposit_fee,
   5012                        const struct TALER_CoinSpendPublicKeyP *coin_pub)
   5013 {
   5014   struct LookupDepositSerial_Closure *lookup_cls = cls;
   5015 
   5016   (void) deposit_timestamp;
   5017   if (NULL == lookup_cls)
   5018     return;
   5019   if ((0 == strcmp (lookup_cls->deposit->exchange_url,
   5020                     exchange_url)) &&
   5021       (0 == GNUNET_memcmp (&lookup_cls->deposit->h_wire,
   5022                            h_wire)) &&
   5023       (GNUNET_OK == TALER_amount_cmp_currency (
   5024          &lookup_cls->deposit->amount_with_fee,
   5025          amount_with_fee)) &&
   5026       (0 == TALER_amount_cmp (&lookup_cls->deposit->amount_with_fee,
   5027                               amount_with_fee)) &&
   5028       (GNUNET_OK == TALER_amount_cmp_currency (
   5029          &lookup_cls->deposit->deposit_fee,
   5030          deposit_fee)) &&
   5031       (0 == TALER_amount_cmp (&lookup_cls->deposit->deposit_fee,
   5032                               deposit_fee)) &&
   5033       (0 == GNUNET_memcmp (&lookup_cls->deposit->coin_pub,
   5034                            coin_pub)))
   5035     lookup_cls->serial = deposit_serial;
   5036 }
   5037 
   5038 
   5039 /**
   5040  * Convenience function to retrieve the row number of a deposit in the database.
   5041  *
   5042  * @param instance the instance to get deposits from.
   5043  * @param order the order associated with the deposit.
   5044  * @param deposit the deposit to lookup the serial for.
   5045  * @return the row number of the deposit.
   5046  */
   5047 static uint64_t
   5048 get_deposit_serial (const struct InstanceData *instance,
   5049                     const struct OrderData *order,
   5050                     const struct DepositData *deposit)
   5051 {
   5052   uint64_t order_serial = get_order_serial (instance,
   5053                                             order);
   5054   struct LookupDepositSerial_Closure lookup_cls = {
   5055     .deposit = deposit,
   5056     .serial = 0
   5057   };
   5058 
   5059   GNUNET_assert (0 <
   5060                  TALER_MERCHANTDB_iterate_deposits_by_order (pg,
   5061                                                              order_serial,
   5062                                                              &get_deposit_serial_cb,
   5063                                                              &lookup_cls));
   5064   GNUNET_assert (0 != lookup_cls.serial);
   5065 
   5066   return lookup_cls.serial;
   5067 }
   5068 
   5069 
   5070 /**
   5071  * Closure for deposit tests.
   5072  */
   5073 struct TestDeposits_Closure
   5074 {
   5075   /**
   5076    * The instance settings
   5077    */
   5078   struct InstanceData instance;
   5079 
   5080   /**
   5081    * The merchant account
   5082    */
   5083   struct TALER_MERCHANTDB_AccountDetails account;
   5084 
   5085   /**
   5086    * The exchange signing key
   5087    */
   5088   struct ExchangeSignkeyData signkey;
   5089 
   5090   /**
   5091    * The order data
   5092    */
   5093   struct OrderData orders[2];
   5094 
   5095   /**
   5096    * The array of deposits
   5097    */
   5098   struct DepositData deposits[3];
   5099 };
   5100 
   5101 
   5102 /**
   5103  * Initializes data for testing deposits.
   5104  *
   5105  * @param cls the test closure to initialize.
   5106  */
   5107 static void
   5108 pre_test_deposits (struct TestDeposits_Closure *cls)
   5109 {
   5110   /* Instance */
   5111   make_instance ("test_inst_deposits",
   5112                  &cls->instance);
   5113 
   5114   /* Account */
   5115   make_account (&cls->account);
   5116   cls->account.instance_id = cls->instance.instance.id;
   5117   /* Signing key */
   5118   make_exchange_signkey (&cls->signkey);
   5119 
   5120   /* Order */
   5121   make_order ("test_deposits_od_1",
   5122               &cls->orders[0]);
   5123   make_order ("test_deposits_od_2",
   5124               &cls->orders[1]);
   5125 
   5126   /* Deposit */
   5127   make_deposit (&cls->instance,
   5128                 &cls->account,
   5129                 &cls->orders[0],
   5130                 &cls->signkey,
   5131                 &cls->deposits[0]);
   5132   make_deposit (&cls->instance,
   5133                 &cls->account,
   5134                 &cls->orders[0],
   5135                 &cls->signkey,
   5136                 &cls->deposits[1]);
   5137   GNUNET_assert (GNUNET_OK ==
   5138                  TALER_string_to_amount ("EUR:29.00",
   5139                                          &cls->deposits[1].amount_with_fee));
   5140   make_deposit (&cls->instance,
   5141                 &cls->account,
   5142                 &cls->orders[1],
   5143                 &cls->signkey,
   5144                 &cls->deposits[2]);
   5145 }
   5146 
   5147 
   5148 /**
   5149  * Cleans up memory after testing deposits.
   5150  *
   5151  * @param cls the closure containing memory to free.
   5152  */
   5153 static void
   5154 post_test_deposits (struct TestDeposits_Closure *cls)
   5155 {
   5156   free_instance_data (&cls->instance);
   5157   json_decref (cls->orders[0].contract);
   5158   json_decref (cls->orders[1].contract);
   5159 }
   5160 
   5161 
   5162 /**
   5163  * Counts the pending deposits we are called for.
   5164  *
   5165  * @param cls a `unsigned int *` counter
   5166  */
   5167 static void
   5168 count_pending_deposits_cb (
   5169   void *cls,
   5170   uint64_t deposit_serial,
   5171   struct GNUNET_TIME_Absolute wire_deadline,
   5172   struct GNUNET_TIME_Absolute retry_time,
   5173   const struct TALER_PrivateContractHashP *h_contract_terms,
   5174   const struct TALER_MerchantPrivateKeyP *merchant_priv,
   5175   const char *instance_id,
   5176   const struct TALER_MerchantWireHashP *h_wire,
   5177   const struct TALER_Amount *amount_with_fee,
   5178   const struct TALER_Amount *deposit_fee,
   5179   const struct TALER_CoinSpendPublicKeyP *coin_pub)
   5180 {
   5181   unsigned int *count = cls;
   5182 
   5183   (void) deposit_serial;
   5184   (void) wire_deadline;
   5185   (void) retry_time;
   5186   (void) h_contract_terms;
   5187   (void) merchant_priv;
   5188   (void) instance_id;
   5189   (void) h_wire;
   5190   (void) amount_with_fee;
   5191   (void) deposit_fee;
   5192   (void) coin_pub;
   5193   (*count)++;
   5194 }
   5195 
   5196 
   5197 /**
   5198  * Tests that pending deposits of an instance whose private key was
   5199  * deleted do not break the iteration.
   5200  *
   5201  * Regression test: 'merchant_priv' is nullable and DELETE
   5202  * /management/instances/$ID sets it to NULL, but the result spec did
   5203  * not allow NULL.  Extraction then failed with a hard error, which
   5204  * makes taler-merchant-depositcheck shut down merchant-wide and
   5205  * crash-loop on the very same row.
   5206  *
   5207  * @param instance the instance whose private key gets deleted.
   5208  * @param exchange_url the exchange the deposits were made to.
   5209  * @return 0 on success, 1 otherwise.
   5210  */
   5211 static int
   5212 test_pending_deposits_without_private_key (const struct InstanceData *instance,
   5213                                            const char *exchange_url)
   5214 {
   5215   unsigned int count = 0;
   5216 
   5217   /* Before the deletion the deposits are pending. */
   5218   TEST_COND_RET_ON_FAIL (0 <
   5219                          TALER_MERCHANTDB_iterate_pending_deposits (
   5220                            pg,
   5221                            exchange_url,
   5222                            1024,
   5223                            true,
   5224                            &count_pending_deposits_cb,
   5225                            &count),
   5226                          "Iterating over pending deposits failed\n");
   5227   TEST_COND_RET_ON_FAIL (0 < count,
   5228                          "No pending deposits found\n");
   5229   TEST_RET_ON_FAIL (test_delete_instance_private_key (
   5230                       instance,
   5231                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   5232   /* Now they are unsettleable, but that must not be an error. */
   5233   count = 0;
   5234   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
   5235                          TALER_MERCHANTDB_iterate_pending_deposits (
   5236                            pg,
   5237                            exchange_url,
   5238                            1024,
   5239                            true,
   5240                            &count_pending_deposits_cb,
   5241                            &count),
   5242                          "Pending deposits of an instance without a private key"
   5243                          " were not skipped\n");
   5244   TEST_COND_RET_ON_FAIL (0 == count,
   5245                          "Deposit of an instance without a private key"
   5246                          " was returned\n");
   5247   return 0;
   5248 }
   5249 
   5250 
   5251 /**
   5252  * Runs tests for deposits.
   5253  *
   5254  * @param cls the closure containing test data.
   5255  * @return 0 on success, 1 otherwise.
   5256  */
   5257 static int
   5258 run_test_deposits (struct TestDeposits_Closure *cls)
   5259 {
   5260   /* Insert the instance */
   5261   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
   5262                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   5263   /* Insert an account */
   5264   TEST_RET_ON_FAIL (test_insert_account (&cls->instance,
   5265                                          &cls->account,
   5266                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   5267   /* Insert a signing key */
   5268   TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey,
   5269                                                   GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   5270   TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey,
   5271                                                   GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   5272   /* Insert an order */
   5273   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
   5274                                        &cls->orders[0],
   5275                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   5276   /* Insert contract terms */
   5277   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
   5278                                                 &cls->orders[0],
   5279                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   5280   /* Test inserting a deposit */
   5281   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
   5282                                          &cls->signkey,
   5283                                          &cls->deposits[0],
   5284                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   5285   /* Test double inserts are idempotent */
   5286   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
   5287                                          &cls->signkey,
   5288                                          &cls->deposits[0],
   5289                                          GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   5290   /* Each reason for not storing a deposit confirmation must be
   5291      reported distinctly, and none of them may look like success. */
   5292   {
   5293     struct TALER_PrivateContractHashP bogus_h_contract;
   5294     struct TALER_MerchantWireHashP bogus_h_wire;
   5295     struct TALER_ExchangePublicKeyP bogus_exchange_pub;
   5296     struct TALER_Amount other_wire_fee;
   5297 
   5298     GNUNET_CRYPTO_random_block (&bogus_h_contract,
   5299                                 sizeof (bogus_h_contract));
   5300     GNUNET_CRYPTO_random_block (&bogus_h_wire,
   5301                                 sizeof (bogus_h_wire));
   5302     GNUNET_CRYPTO_random_block (&bogus_exchange_pub,
   5303                                 sizeof (bogus_exchange_pub));
   5304     GNUNET_assert (GNUNET_OK ==
   5305                    TALER_string_to_amount ("EUR:3.00",
   5306                                            &other_wire_fee));
   5307     /* unknown merchant account */
   5308     TEST_RET_ON_FAIL (test_insert_deposit_confirmation_status (
   5309                         &cls->instance,
   5310                         &cls->deposits[0],
   5311                         &cls->deposits[0].h_contract_terms,
   5312                         &bogus_h_wire,
   5313                         &cls->signkey.exchange_pub,
   5314                         &cls->deposits[0].wire_fee,
   5315                         TALER_MERCHANTDB_DCS_NO_ACCOUNT));
   5316     /* unknown exchange signing key */
   5317     TEST_RET_ON_FAIL (test_insert_deposit_confirmation_status (
   5318                         &cls->instance,
   5319                         &cls->deposits[0],
   5320                         &cls->deposits[0].h_contract_terms,
   5321                         &cls->deposits[0].h_wire,
   5322                         &bogus_exchange_pub,
   5323                         &cls->deposits[0].wire_fee,
   5324                         TALER_MERCHANTDB_DCS_NO_SIGNKEY));
   5325     /* unknown order */
   5326     TEST_RET_ON_FAIL (test_insert_deposit_confirmation_status (
   5327                         &cls->instance,
   5328                         &cls->deposits[0],
   5329                         &bogus_h_contract,
   5330                         &cls->deposits[0].h_wire,
   5331                         &cls->signkey.exchange_pub,
   5332                         &cls->deposits[0].wire_fee,
   5333                         TALER_MERCHANTDB_DCS_NO_ORDER));
   5334     /* conflicting deposit confirmation (different wire fee) */
   5335     TEST_RET_ON_FAIL (test_insert_deposit_confirmation_status (
   5336                         &cls->instance,
   5337                         &cls->deposits[0],
   5338                         &cls->deposits[0].h_contract_terms,
   5339                         &cls->deposits[0].h_wire,
   5340                         &cls->signkey.exchange_pub,
   5341                         &other_wire_fee,
   5342                         TALER_MERCHANTDB_DCS_CONFLICT));
   5343   }
   5344   /* Test lookup deposits */
   5345   TEST_RET_ON_FAIL (test_lookup_deposits (&cls->instance,
   5346                                           &cls->deposits[0].h_contract_terms,
   5347                                           1,
   5348                                           cls->deposits));
   5349   TEST_RET_ON_FAIL (test_lookup_deposits (&cls->instance,
   5350                                           &cls->deposits[2].h_contract_terms,
   5351                                           0,
   5352                                           NULL));
   5353   /* Test lookup deposits by contract and coins */
   5354   TEST_RET_ON_FAIL (test_lookup_deposits_contract_and_coin (
   5355                       &cls->instance,
   5356                       &cls->deposits[0].h_contract_terms,
   5357                       &cls->deposits[0].coin_pub,
   5358                       1,
   5359                       cls->deposits));
   5360   /* Test multiple deposits */
   5361   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
   5362                                          &cls->signkey,
   5363                                          &cls->deposits[1],
   5364                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   5365   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
   5366                                        &cls->orders[1],
   5367                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   5368   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
   5369                                                 &cls->orders[1],
   5370                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   5371   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
   5372                                          &cls->signkey,
   5373                                          &cls->deposits[2],
   5374                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   5375   TEST_RET_ON_FAIL (test_lookup_deposits (&cls->instance,
   5376                                           &cls->deposits[0].h_contract_terms,
   5377                                           2,
   5378                                           cls->deposits));
   5379   TEST_RET_ON_FAIL (test_lookup_deposits (&cls->instance,
   5380                                           &cls->deposits[2].h_contract_terms,
   5381                                           1,
   5382                                           &cls->deposits[2]));
   5383   /* Test lookup deposits by order */
   5384   {
   5385     uint64_t order_serial = get_order_serial (&cls->instance,
   5386                                               &cls->orders[0]);
   5387     TEST_RET_ON_FAIL (test_lookup_deposits_by_order (order_serial,
   5388                                                      2,
   5389                                                      cls->deposits));
   5390     order_serial = get_order_serial (&cls->instance,
   5391                                      &cls->orders[1]);
   5392     TEST_RET_ON_FAIL (test_lookup_deposits_by_order (order_serial,
   5393                                                      1,
   5394                                                      &cls->deposits[2]));
   5395   }
   5396   /* Must be last: deletes the private key of the instance. */
   5397   TEST_RET_ON_FAIL (test_pending_deposits_without_private_key (
   5398                       &cls->instance,
   5399                       cls->deposits[0].exchange_url));
   5400   return 0;
   5401 }
   5402 
   5403 
   5404 /**
   5405  * Handles functionality for testing deposits.
   5406  *
   5407  * @return 0 on success, 1 otherwise.
   5408  */
   5409 static int
   5410 test_deposits (void)
   5411 {
   5412   struct TestDeposits_Closure test_cls;
   5413   int test_result;
   5414 
   5415   pre_test_deposits (&test_cls);
   5416   test_result = run_test_deposits (&test_cls);
   5417   post_test_deposits (&test_cls);
   5418   return test_result;
   5419 }
   5420 
   5421 
   5422 /* *********** Transfers ********** */
   5423 
   5424 
   5425 /**
   5426  * Container for wire fee data for an exchange.
   5427  */
   5428 struct WireFeeData
   5429 {
   5430   /**
   5431    * The method used.
   5432    */
   5433   const char *wire_method;
   5434 
   5435   /**
   5436    * Hash of the wire method.
   5437    */
   5438   struct GNUNET_HashCode h_wire_method;
   5439 
   5440   /**
   5441    * Wire fees charged.
   5442    */
   5443   struct TALER_WireFeeSet fees;
   5444 
   5445   /**
   5446    * Start date of the wire fee.
   5447    */
   5448   struct GNUNET_TIME_Timestamp wire_fee_start;
   5449 
   5450   /**
   5451    * End date of the wire fee.
   5452    */
   5453   struct GNUNET_TIME_Timestamp wire_fee_end;
   5454 
   5455   /**
   5456    * Signature on the wire fee.
   5457    */
   5458   struct TALER_MasterSignatureP fee_sig;
   5459 };
   5460 
   5461 
   5462 /**
   5463  * Creates data for an exchange wire fee.
   5464  *
   5465  * @param signkey the exchange signing key data.
   5466  * @param wire_fee where to store the wire fee data.
   5467  */
   5468 static void
   5469 make_wire_fee (const struct ExchangeSignkeyData *signkey,
   5470                struct WireFeeData *wire_fee)
   5471 {
   5472   wire_fee->wire_method = "wire-method";
   5473   GNUNET_CRYPTO_hash (wire_fee->wire_method,
   5474                       strlen (wire_fee->wire_method) + 1,
   5475                       &wire_fee->h_wire_method);
   5476   GNUNET_assert (GNUNET_OK ==
   5477                  TALER_string_to_amount ("EUR:0.49",
   5478                                          &wire_fee->fees.wire));
   5479   GNUNET_assert (GNUNET_OK ==
   5480                  TALER_string_to_amount ("EUR:0.49",
   5481                                          &wire_fee->fees.closing));
   5482   wire_fee->wire_fee_start = GNUNET_TIME_timestamp_get ();
   5483   wire_fee->wire_fee_end = GNUNET_TIME_relative_to_timestamp (
   5484     GNUNET_TIME_UNIT_MONTHS);
   5485   TALER_exchange_offline_wire_fee_sign (
   5486     wire_fee->wire_method,
   5487     wire_fee->wire_fee_start,
   5488     wire_fee->wire_fee_end,
   5489     &wire_fee->fees,
   5490     &signkey->master_priv,
   5491     &wire_fee->fee_sig);
   5492 }
   5493 
   5494 
   5495 /**
   5496  * Container for wire transfer data.
   5497  */
   5498 struct TransferData
   5499 {
   5500   /**
   5501    * Id of the transfer.
   5502    */
   5503   struct TALER_WireTransferIdentifierRawP wtid;
   5504 
   5505   /**
   5506    * The main data for the transfer.
   5507    */
   5508   struct TALER_EXCHANGE_TransferData data;
   5509 
   5510   /**
   5511    * URL to the exchange the transfer was made through.
   5512    */
   5513   const char *exchange_url;
   5514 
   5515   /**
   5516    * How much the fee for the deposit was.
   5517    */
   5518   struct TALER_Amount deposit_fee;
   5519 
   5520   /**
   5521    * Whether the transfer has been confirmed.
   5522    */
   5523   bool confirmed;
   5524 
   5525   /**
   5526    * Whether the transfer has been verified.
   5527    */
   5528   bool verified;
   5529 };
   5530 
   5531 
   5532 /**
   5533  * Creates a transfer for use with testing.
   5534  *
   5535  * @param deposits_length length of @e deposits.
   5536  * @param deposits list of deposits to combine into one transfer.
   5537  * @param transfer where to write the transfer data.
   5538  */
   5539 static void
   5540 make_transfer (const struct ExchangeSignkeyData *signkey,
   5541                unsigned int deposits_length,
   5542                const struct DepositData deposits[static deposits_length],
   5543                struct TransferData *transfer)
   5544 {
   5545   struct TALER_TrackTransferDetails *details = NULL;
   5546 
   5547   GNUNET_CRYPTO_random_block (&transfer->wtid,
   5548                               sizeof (struct TALER_WireTransferIdentifierRawP));
   5549   transfer->exchange_url = deposits[0].exchange_url;
   5550   transfer->verified = false;
   5551   transfer->confirmed = false;
   5552   transfer->data.details_length = 0;
   5553   GNUNET_assert (GNUNET_OK ==
   5554                  TALER_amount_set_zero (deposits[0].amount_with_fee.currency,
   5555                                         &transfer->data.total_amount));
   5556   GNUNET_assert (GNUNET_OK ==
   5557                  TALER_amount_set_zero (deposits[0].amount_with_fee.currency,
   5558                                         &transfer->deposit_fee));
   5559   for (unsigned int i = 0; i < deposits_length; ++i)
   5560   {
   5561     GNUNET_array_grow (details,
   5562                        transfer->data.details_length,
   5563                        i + 1);
   5564     details[i].h_contract_terms = deposits[i].h_contract_terms;
   5565     details[i].coin_pub = deposits[i].coin_pub;
   5566     details[i].coin_value = deposits[i].amount_with_fee;
   5567     details[i].coin_fee = deposits[i].deposit_fee;
   5568     GNUNET_assert (0 <=
   5569                    TALER_amount_add (&transfer->data.total_amount,
   5570                                      &transfer->data.total_amount,
   5571                                      &deposits[i].amount_with_fee));
   5572     GNUNET_assert (0 <=
   5573                    TALER_amount_add (&transfer->deposit_fee,
   5574                                      &transfer->deposit_fee,
   5575                                      &deposits[i].deposit_fee));
   5576   }
   5577   transfer->data.exchange_pub = signkey->exchange_pub;
   5578   transfer->data.execution_time = GNUNET_TIME_timestamp_get ();
   5579   transfer->data.details = details;
   5580   GNUNET_assert (GNUNET_OK ==
   5581                  TALER_string_to_amount ("EUR:0.50",
   5582                                          &transfer->data.wire_fee));
   5583 }
   5584 
   5585 
   5586 /**
   5587  * Closure for testing 'iterate_transfer_summaries'
   5588  */
   5589 struct TestLookupTransferSummary_Closure
   5590 {
   5591   /**
   5592    * Id of the order the transfer was made for.
   5593    */
   5594   const char *order_id;
   5595 
   5596   /**
   5597    * The value of the deposit made.
   5598    */
   5599   const struct TALER_Amount *deposit_value;
   5600 
   5601   /**
   5602    * The fee on the deposit made.
   5603    */
   5604   const struct TALER_Amount *deposit_fee;
   5605 
   5606   /**
   5607    * 0 if the comparison is true, 1 if false.
   5608    */
   5609   int result;
   5610 };
   5611 
   5612 
   5613 /**
   5614  * Called after 'test_lookup_transfer_summary'.
   5615  *
   5616  * @param cls pointer to 'TestLookupTransferSummary_Closure'.
   5617  * @param order_id id of the order the transfer was made for.
   5618  * @param deposit_value the value of the deposit made.
   5619  * @param deposit_fee the fee on the deposit made.
   5620  */
   5621 static void
   5622 lookup_transfer_summary_cb (void *cls,
   5623                             const char *order_id,
   5624                             const struct TALER_Amount *deposit_value,
   5625                             const struct TALER_Amount *deposit_fee)
   5626 {
   5627   struct TestLookupTransferSummary_Closure *cmp = cls;
   5628   if (NULL == cmp)
   5629     return;
   5630   if ((0 == strcmp (cmp->order_id,
   5631                     order_id)) &&
   5632       (GNUNET_OK == TALER_amount_cmp_currency (cmp->deposit_value,
   5633                                                deposit_value)) &&
   5634       (0 == TALER_amount_cmp (cmp->deposit_value,
   5635                               deposit_value)) &&
   5636       (GNUNET_OK == TALER_amount_cmp_currency (cmp->deposit_fee,
   5637                                                deposit_fee)) &&
   5638       (0 == TALER_amount_cmp (cmp->deposit_fee,
   5639                               deposit_fee)))
   5640     cmp->result = 0;
   5641   else
   5642     cmp->result = 1;
   5643 }
   5644 
   5645 
   5646 /**
   5647  * Tests looking up a transfer's summary.
   5648  *
   5649  * @param exchange_url url to the exchange for the transfer.
   5650  * @param wtid identifier of the transfer.
   5651  * @param expected_order_id the id of the order associated with the transfer.
   5652  * @param expected_deposit_value the amount of the deposit made for the transfer.
   5653  * @param expected_deposit_fee the fee on the deposit made for the transfer.
   5654  * @return 1 on success, 0 otherwise.
   5655  */
   5656 static int
   5657 test_lookup_transfer_summary (
   5658   const char *exchange_url,
   5659   const struct TALER_WireTransferIdentifierRawP *wtid,
   5660   const char *expected_order_id,
   5661   const struct TALER_Amount *expected_deposit_value,
   5662   const struct TALER_Amount *expected_deposit_fee)
   5663 {
   5664   struct TestLookupTransferSummary_Closure cmp = {
   5665     .order_id = expected_order_id,
   5666     .deposit_value = expected_deposit_value,
   5667     .deposit_fee = expected_deposit_fee,
   5668     .result = 0
   5669   };
   5670   if (1 != TALER_MERCHANTDB_iterate_transfer_summaries (pg,
   5671                                                         exchange_url,
   5672                                                         wtid,
   5673                                                         &lookup_transfer_summary_cb,
   5674                                                         &cmp))
   5675   {
   5676     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   5677                 "Lookup transfer summary failed\n");
   5678     return 1;
   5679   }
   5680   if (0 != cmp.result)
   5681   {
   5682     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   5683                 "Lookup transfer summary failed: mismatched data\n");
   5684     return 1;
   5685   }
   5686   return 0;
   5687 }
   5688 
   5689 
   5690 /**
   5691  * Closure for testing 'iterate_transfer_details'.
   5692  */
   5693 struct TestLookupTransferDetails_Closure
   5694 {
   5695   /**
   5696    * Length of @e details_to_cmp.
   5697    */
   5698   unsigned int details_to_cmp_length;
   5699 
   5700   /**
   5701    * The details we expect to find.
   5702    */
   5703   const struct TALER_TrackTransferDetails *details_to_cmp;
   5704 
   5705   /**
   5706    * Number of results matching each detail in @e details_to_cmp.
   5707    */
   5708   unsigned int *results_matching;
   5709 
   5710   /**
   5711    * Total number of results found.
   5712    */
   5713   unsigned int results_length;
   5714 };
   5715 
   5716 
   5717 /**
   5718  * Called after 'test_lookup_transfer_details'.
   5719  *
   5720  * @param cls pointer to 'TestLookupTransferDetails_Closure'.
   5721  * @param current_offset offset within transfer details.
   5722  * @param details the details that were found.
   5723  */
   5724 static void
   5725 lookup_transfer_details_cb (void *cls,
   5726                             unsigned int current_offset,
   5727                             const struct TALER_TrackTransferDetails *details)
   5728 {
   5729   struct TestLookupTransferDetails_Closure *cmp = cls;
   5730   if (NULL == cmp)
   5731     return;
   5732   for (unsigned int i = 0; cmp->details_to_cmp_length > i; ++i)
   5733   {
   5734     if ((0 == GNUNET_memcmp (&cmp->details_to_cmp[i].h_contract_terms,
   5735                              &details->h_contract_terms)) &&
   5736         (0 == GNUNET_memcmp (&cmp->details_to_cmp[i].coin_pub,
   5737                              &details->coin_pub)) &&
   5738         (GNUNET_OK == TALER_amount_cmp_currency (
   5739            &cmp->details_to_cmp[i].coin_value,
   5740            &details->coin_value)) &&
   5741         (0 == TALER_amount_cmp (&cmp->details_to_cmp[i].coin_value,
   5742                                 &details->coin_value)) &&
   5743         (GNUNET_OK == TALER_amount_cmp_currency (
   5744            &cmp->details_to_cmp[i].coin_fee,
   5745            &details->coin_fee)) &&
   5746         (0 == TALER_amount_cmp (&cmp->details_to_cmp[i].coin_fee,
   5747                                 &details->coin_fee)))
   5748     {
   5749       cmp->results_matching[i] += 1;
   5750     }
   5751   }
   5752   cmp->results_length += 1;
   5753 }
   5754 
   5755 
   5756 /**
   5757  * Tests looking up details for a wire transfer.
   5758  *
   5759  * @param exchange_url url to the exchange.
   5760  * @param wtid id of the transfer.
   5761  * @param details_length the length of @e details.
   5762  * @param details the details we expect to be returned.
   5763  * @return 1 on success, 0 otherwise.
   5764  */
   5765 static int
   5766 test_lookup_transfer_details (
   5767   const char *exchange_url,
   5768   const struct TALER_WireTransferIdentifierRawP *wtid,
   5769   unsigned int details_length,
   5770   const struct TALER_TrackTransferDetails *details)
   5771 {
   5772   unsigned int results_matching[details_length];
   5773   struct TestLookupTransferDetails_Closure cmp = {
   5774     .details_to_cmp_length = details_length,
   5775     .details_to_cmp = details,
   5776     .results_matching = results_matching,
   5777     .results_length = 0
   5778   };
   5779   memset (results_matching,
   5780           0,
   5781           sizeof (unsigned int) * details_length);
   5782   if (1 != TALER_MERCHANTDB_iterate_transfer_details (pg,
   5783                                                       exchange_url,
   5784                                                       wtid,
   5785                                                       &lookup_transfer_details_cb,
   5786                                                       &cmp))
   5787   {
   5788     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   5789                 "Lookup transfer details failed\n");
   5790     return 1;
   5791   }
   5792   if (details_length != cmp.results_length)
   5793   {
   5794     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   5795                 "Lookup transfer details failed: incorrect number of results (%d)\n",
   5796                 cmp.results_length);
   5797     return 1;
   5798   }
   5799   for (unsigned int i = 0; details_length > i; ++i)
   5800   {
   5801     if (1 != cmp.results_matching[i])
   5802     {
   5803       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   5804                   "Lookup transfer details failed: mismatched data\n");
   5805       return 1;
   5806     }
   5807   }
   5808   return 0;
   5809 }
   5810 
   5811 
   5812 /**
   5813  * Closure for 'iterate_transfer_details_by_order'.
   5814  */
   5815 struct TestLookupTransferDetailsByOrder_Closure
   5816 {
   5817   /**
   5818    * Length of @e transfers_to_cmp.
   5819    */
   5820   unsigned int transfers_to_cmp_length;
   5821 
   5822   /**
   5823    * List of transfers that we expect to find.
   5824    */
   5825   const struct TransferData *transfers_to_cmp;
   5826 
   5827   /**
   5828    * How many results match the corresponding element of @e transfers_to_cmp.
   5829    */
   5830   unsigned int *results_matching;
   5831 
   5832   /**
   5833    * Total number of results found.
   5834    */
   5835   unsigned int results_length;
   5836 };
   5837 
   5838 
   5839 /**
   5840  * Called after 'test_lookup_transfer_details_by_order'.
   5841  *
   5842  * @param cls pointer to 'TestLookupTransferDetailsByOrder_Closure'.
   5843  * @param wtid identifier of the transfer found.
   5844  * @param exchange_url exchange url of the transfer found.
   5845  * @param execution_time when the transfer found occurred.
   5846  * @param deposit_value amount of the deposit for the transfer found.
   5847  * @param deposit_fee amount of the fee for the deposit of the transfer.
   5848  * @param transfer_confirmed did the merchant confirm that a wire transfer with
   5849  *        @a wtid over the total amount happened?
   5850  */
   5851 static void
   5852 lookup_transfer_details_order_cb (
   5853   void *cls,
   5854   const struct TALER_WireTransferIdentifierRawP *wtid,
   5855   const char *exchange_url,
   5856   struct GNUNET_TIME_Timestamp execution_time,
   5857   const struct TALER_Amount *deposit_value,
   5858   const struct TALER_Amount *deposit_fee,
   5859   bool transfer_confirmed,
   5860   uint64_t expected_transfer_serial_id)
   5861 {
   5862   struct TestLookupTransferDetailsByOrder_Closure *cmp = cls;
   5863 
   5864   if (NULL == cmp)
   5865     return;
   5866   cmp->results_length += 1;
   5867   for (unsigned int i = 0; i < cmp->transfers_to_cmp_length; ++i)
   5868   {
   5869     /* Right now iterate_transfer_details_by_order leaves execution_time
   5870        uninitialized */
   5871     if ((0 == GNUNET_memcmp (&cmp->transfers_to_cmp[i].wtid,
   5872                              wtid)) &&
   5873         (0 == strcmp (cmp->transfers_to_cmp[i].exchange_url,
   5874                       exchange_url)) &&
   5875         (GNUNET_OK ==
   5876          TALER_amount_cmp_currency (
   5877            &cmp->transfers_to_cmp[i].data.total_amount,
   5878            deposit_value)) &&
   5879         (0 ==
   5880          TALER_amount_cmp (&cmp->transfers_to_cmp[i].data.total_amount,
   5881                            deposit_value)) &&
   5882         (GNUNET_OK ==
   5883          TALER_amount_cmp_currency (
   5884            &cmp->transfers_to_cmp[i].deposit_fee,
   5885            deposit_fee)) &&
   5886         (0 ==
   5887          TALER_amount_cmp (&cmp->transfers_to_cmp[i].deposit_fee,
   5888                            deposit_fee)) )
   5889       cmp->results_matching[i] += 1;
   5890   }
   5891 }
   5892 
   5893 
   5894 /**
   5895  * Tests looking up wire transfers associated with an order.
   5896  *
   5897  * @param order_serial the order to be queried.
   5898  * @param transfers_length length of @e transfers.
   5899  * @param transfers the transfers we expect to be found.
   5900  * @return 0 on success, 1 otherwise.
   5901  */
   5902 static int
   5903 test_lookup_transfer_details_by_order (
   5904   uint64_t order_serial,
   5905   unsigned int transfers_length,
   5906   const struct TransferData *transfers)
   5907 {
   5908   unsigned int results_matching[transfers_length];
   5909   struct TestLookupTransferDetailsByOrder_Closure cmp = {
   5910     .transfers_to_cmp_length = transfers_length,
   5911     .transfers_to_cmp = transfers,
   5912     .results_matching = results_matching,
   5913     .results_length = 0
   5914   };
   5915   memset (results_matching,
   5916           0,
   5917           sizeof (unsigned int) * transfers_length);
   5918   if (transfers_length !=
   5919       TALER_MERCHANTDB_iterate_transfer_details_by_order (
   5920         pg,
   5921         order_serial,
   5922         &lookup_transfer_details_order_cb,
   5923         &cmp))
   5924   {
   5925     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   5926                 "Lookup transfer details by order failed\n");
   5927     return 1;
   5928   }
   5929   if (transfers_length != cmp.results_length)
   5930   {
   5931     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   5932                 "Lookup transfer details by order failed: incorrect number of results\n");
   5933     return 1;
   5934   }
   5935   for (unsigned int i = 0; i < transfers_length; ++i)
   5936   {
   5937     if (1 != cmp.results_matching[i])
   5938     {
   5939       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   5940                   "Lookup transfer details by order failed: mismatched data\n");
   5941       return 1;
   5942     }
   5943   }
   5944   return 0;
   5945 }
   5946 
   5947 
   5948 /**
   5949  * Tests inserting wire fee data for an exchange.
   5950  *
   5951  * @param signkey the signing key for the exchange.
   5952  * @param wire_fee the wire fee data.
   5953  * @param expected_result what the database should return.
   5954  * @return 0 on success, 1 otherwise.
   5955  */
   5956 static int
   5957 test_insert_wire_fee (const struct ExchangeSignkeyData *signkey,
   5958                       const struct WireFeeData *wire_fee,
   5959                       enum GNUNET_DB_QueryStatus expected_result)
   5960 {
   5961   TEST_COND_RET_ON_FAIL (expected_result ==
   5962                          TALER_MERCHANTDB_insert_exchange_wire_fee (
   5963                            pg,
   5964                            &signkey->master_pub,
   5965                            &wire_fee->h_wire_method,
   5966                            &wire_fee->fees,
   5967                            wire_fee->wire_fee_start,
   5968                            wire_fee->wire_fee_end,
   5969                            &wire_fee->fee_sig),
   5970                          "Store wire fee by exchange failed\n");
   5971   return 0;
   5972 }
   5973 
   5974 
   5975 /**
   5976  * Tests looking up wire fee data for an exchange.
   5977  *
   5978  * @param signkey the signing key to use for lookup.
   5979  * @param wire_fee_data the wire fee data we expect to find.
   5980  * @return 0 on success, 1 otherwise.
   5981  */
   5982 static int
   5983 test_lookup_wire_fee (const struct ExchangeSignkeyData *signkey,
   5984                       const struct WireFeeData *wire_fee_data)
   5985 {
   5986   struct TALER_WireFeeSet fees;
   5987   struct GNUNET_TIME_Timestamp start_date;
   5988   struct GNUNET_TIME_Timestamp end_date;
   5989   struct TALER_MasterSignatureP master_sig;
   5990   if (1 != TALER_MERCHANTDB_get_exchange_wire_fee (pg,
   5991                                                    &signkey->master_pub,
   5992                                                    wire_fee_data->wire_method,
   5993                                                    GNUNET_TIME_timestamp_get (),
   5994                                                    &fees,
   5995                                                    &start_date,
   5996                                                    &end_date,
   5997                                                    &master_sig))
   5998   {
   5999     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   6000                 "Lookup wire fee failed\n");
   6001     return 1;
   6002   }
   6003   if ((0 !=
   6004        TALER_wire_fee_set_cmp (&wire_fee_data->fees,
   6005                                &fees)) ||
   6006       (GNUNET_TIME_timestamp_cmp (wire_fee_data->wire_fee_start,
   6007                                   !=,
   6008                                   start_date)) ||
   6009       (GNUNET_TIME_timestamp_cmp (wire_fee_data->wire_fee_end,
   6010                                   !=,
   6011                                   end_date)) ||
   6012       (0 != GNUNET_memcmp (&wire_fee_data->fee_sig,
   6013                            &master_sig)))
   6014   {
   6015     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   6016                 "Lookup wire fee failed: mismatched data\n");
   6017     return 1;
   6018   }
   6019   return 0;
   6020 }
   6021 
   6022 
   6023 /**
   6024  * Closure for 'iterate_transfers'.
   6025  */
   6026 struct TestLookupTransfers_Closure
   6027 {
   6028   /**
   6029    * Length of @e transfers_to_cmp.
   6030    */
   6031   unsigned int transfers_to_cmp_length;
   6032 
   6033   /**
   6034    * The transfers we expect to find.
   6035    */
   6036   const struct TransferData *transfers_to_cmp;
   6037 
   6038   /**
   6039    * Number of results matching each transfer.
   6040    */
   6041   unsigned int *results_matching;
   6042 
   6043   /**
   6044    * Total number of results found.
   6045    */
   6046   unsigned int results_length;
   6047 };
   6048 
   6049 
   6050 /**
   6051  * Function called after 'test_lookup_transfers'.
   6052  *
   6053  * @param cls pointer to 'TestLookupTransfers_Closure'.
   6054  * @param credit_amount how much was wired to the merchant (minus fees)
   6055  * @param wtid wire transfer identifier
   6056  * @param payto_uri target account that received the wire transfer
   6057  * @param exchange_payto_uri account of the exchange that was debited,
   6058  *        `full_payto` NULL if unknown; unused here
   6059  * @param exchange_url base URL of the exchange that made the wire transfer
   6060  * @param transfer_serial_id serial number identifying the transfer in the backend
   6061  * @param expected_transfer_serial_id serial number identifying the expected transfer in the backend, 0 if not @a expected
   6062  * @param execution_time when did the exchange make the transfer, #GNUNET_TIME_UNIT_FOREVER_TS
   6063  *           if it did not yet happen
   6064  * @param expected true if the merchant acknowledged the wire transfer reception
   6065  */
   6066 static void
   6067 lookup_transfers_cb (void *cls,
   6068                      const struct TALER_Amount *credit_amount,
   6069                      const struct TALER_WireTransferIdentifierRawP *wtid,
   6070                      struct TALER_FullPayto payto_uri,
   6071                      struct TALER_FullPayto exchange_payto_uri,
   6072                      const char *exchange_url,
   6073                      uint64_t transfer_serial_id,
   6074                      uint64_t expected_transfer_serial_id,
   6075                      struct GNUNET_TIME_Absolute execution_time,
   6076                      bool expected)
   6077 {
   6078   struct TestLookupTransfers_Closure *cmp = cls;
   6079   if (NULL == cmp)
   6080     return;
   6081   for (unsigned int i = 0; cmp->transfers_to_cmp_length > i; ++i)
   6082   {
   6083     if ( (GNUNET_OK ==
   6084           TALER_amount_cmp_currency (
   6085             &cmp->transfers_to_cmp[i].data.total_amount,
   6086             credit_amount)) &&
   6087          (0 == TALER_amount_cmp (&cmp->transfers_to_cmp[i].data.total_amount,
   6088                                  credit_amount)) )
   6089     {
   6090       cmp->results_matching[i]++;
   6091     }
   6092   }
   6093   cmp->results_length++;
   6094 }
   6095 
   6096 
   6097 /**
   6098  * Tests looking up transfers from the database.
   6099  *
   6100  * @param instance the instance to lookup from.
   6101  * @param account the account the transfer was made to.
   6102  * @param before do not return transfers before this time.
   6103  * @param after do not return transfers after this time.
   6104  * @param limit maximum number of transfers to return.
   6105  * @param offset row in the database to start with.
   6106  * @param filter_verified how to filter verified transfers.
   6107  * @param transfers_length length of @e transfers.
   6108  * @param transfers the transfers we expect to find.
   6109  * @return 0 on success, 1 otherwise.
   6110  */
   6111 static int
   6112 test_lookup_transfers (const struct InstanceData *instance,
   6113                        const struct TALER_MERCHANTDB_AccountDetails *account,
   6114                        struct GNUNET_TIME_Timestamp before,
   6115                        struct GNUNET_TIME_Timestamp after,
   6116                        int64_t limit,
   6117                        uint64_t offset,
   6118                        unsigned int transfers_length,
   6119                        const struct TransferData *transfers)
   6120 {
   6121   unsigned int results_matching[transfers_length];
   6122   struct TestLookupTransfers_Closure cmp = {
   6123     .transfers_to_cmp_length = transfers_length,
   6124     .transfers_to_cmp = transfers,
   6125     .results_matching = results_matching,
   6126     .results_length = 0
   6127   };
   6128   memset (results_matching,
   6129           0,
   6130           sizeof (unsigned int) * transfers_length);
   6131   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   6132   if (1 != TALER_MERCHANTDB_iterate_transfers (pg,
   6133                                                instance->instance.id,
   6134                                                account->payto_uri,
   6135                                                before,
   6136                                                after,
   6137                                                limit,
   6138                                                offset,
   6139                                                TALER_EXCHANGE_YNA_ALL,
   6140                                                &lookup_transfers_cb,
   6141                                                &cmp))
   6142   {
   6143     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   6144                 "Lookup transfers failed\n");
   6145     return 1;
   6146   }
   6147   if (transfers_length != cmp.results_length)
   6148   {
   6149     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   6150                 "Lookup transfers failed: incorrect number of results\n");
   6151     return 1;
   6152   }
   6153   for (unsigned int i = 0; transfers_length > i; ++i)
   6154   {
   6155     if (1 != cmp.results_matching[i])
   6156     {
   6157       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   6158                   "Lookup transfers failed: mismatched data\n");
   6159       return 1;
   6160     }
   6161   }
   6162   return 0;
   6163 }
   6164 
   6165 
   6166 /**
   6167  * Tests inserting a transfer into the database.
   6168  *
   6169  * @param instance the instance to use.
   6170  * @param account the account to transfer to.
   6171  * @param transfer the transfer to insert.
   6172  * @param expected_result the result we expect the db to return.
   6173  * @return 0 on success, 1 otherwise.
   6174  */
   6175 static int
   6176 test_insert_transfer (const struct InstanceData *instance,
   6177                       const struct TALER_MERCHANTDB_AccountDetails *account,
   6178                       const struct TransferData *transfer,
   6179                       enum GNUNET_DB_QueryStatus expected_result)
   6180 {
   6181   bool no_account;
   6182   bool conflict;
   6183 
   6184   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   6185   TEST_COND_RET_ON_FAIL (expected_result ==
   6186                          TALER_MERCHANTDB_insert_transfer (
   6187                            pg,
   6188                            instance->instance.id,
   6189                            transfer->exchange_url,
   6190                            &transfer->wtid,
   6191                            &transfer->data.total_amount,
   6192                            account->payto_uri,
   6193                            transfer->confirmed,
   6194                            &no_account,
   6195                            &conflict),
   6196                          "Insert transfer failed\n");
   6197   return 0;
   6198 }
   6199 
   6200 
   6201 /**
   6202  * Tests that inserting a transfer crediting a payto URI which belongs to
   6203  * no account of the instance is reported via the @e no_account flag,
   6204  * instead of being silently attributed to an arbitrary account.
   6205  *
   6206  * Regression test: the account lookup in insert_transfer.sql compared
   6207  * REGEXP_REPLACE(payto_uri,'\\?.*',''), a pattern which matches an
   6208  * *optional* backslash followed by anything.  It therefore matched at
   6209  * offset 0, reduced every URI to the empty string, and made the
   6210  * comparison unconditionally true.
   6211  *
   6212  * @param instance the instance to use.
   6213  * @param transfer the transfer to attempt to insert.
   6214  * @return 0 on success, 1 otherwise.
   6215  */
   6216 static int
   6217 test_insert_transfer_unknown_account (
   6218   const struct InstanceData *instance,
   6219   const struct TransferData *transfer)
   6220 {
   6221   bool no_account = false;
   6222   bool conflict = false;
   6223   struct TALER_FullPayto unknown = {
   6224     .full_payto = (char *) "payto://iban/DE/NO-SUCH-ACCOUNT"
   6225                   "?receiver-name=Not+My+Account"
   6226   };
   6227 
   6228   TEST_SET_INSTANCE (instance->instance.id,
   6229                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   6230   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   6231                          TALER_MERCHANTDB_insert_transfer (
   6232                            pg,
   6233                            instance->instance.id,
   6234                            transfer->exchange_url,
   6235                            &transfer->wtid,
   6236                            &transfer->data.total_amount,
   6237                            unknown,
   6238                            transfer->confirmed,
   6239                            &no_account,
   6240                            &conflict),
   6241                          "Insert transfer for unknown account failed\n");
   6242   TEST_COND_RET_ON_FAIL (no_account,
   6243                          "Insert transfer accepted a payto URI belonging to "
   6244                          "no account of the instance\n");
   6245   return 0;
   6246 }
   6247 
   6248 
   6249 /**
   6250  * Tests linking a deposit to a transfer.
   6251  *
   6252  * @param instance the instance that the deposit and transfer are for.
   6253  * @param signkey the signing used on the deposit.
   6254  * @param order the order the deposit and transfer were made for.
   6255  * @param transfer the transfer.
   6256  * @param expected_result the result the database should return.
   6257  * @param expected_cleared clearance status the database should return.
   6258  * @return 0 on success, 1 otherwise.
   6259  */
   6260 static int
   6261 test_insert_deposit_to_transfer (const struct InstanceData *instance,
   6262                                  const struct ExchangeSignkeyData *signkey,
   6263                                  const struct OrderData *order,
   6264                                  const struct DepositData *deposit,
   6265                                  const struct TransferData *transfer,
   6266                                  enum GNUNET_DB_QueryStatus expected_result,
   6267                                  bool expect_cleared)
   6268 {
   6269   const struct TALER_EXCHANGE_DepositData deposit_data = {
   6270     .exchange_pub = signkey->exchange_pub,
   6271     .exchange_sig = deposit->exchange_sig,
   6272     .wtid = transfer->wtid,
   6273     .execution_time = transfer->data.execution_time,
   6274     .coin_contribution = deposit->amount_with_fee
   6275   };
   6276   uint64_t deposit_serial;
   6277 
   6278   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   6279   deposit_serial = get_deposit_serial (instance,
   6280                                        order,
   6281                                        deposit);
   6282 
   6283   TEST_COND_RET_ON_FAIL (TALER_MERCHANTDB_DTTS_SETTLED ==
   6284                          TALER_MERCHANTDB_insert_deposit_to_transfer (
   6285                            pg,
   6286                            deposit_serial,
   6287                            &deposit->h_wire,
   6288                            deposit->exchange_url,
   6289                            &deposit_data),
   6290                          "insert deposit to transfer failed\n");
   6291   return 0;
   6292 }
   6293 
   6294 
   6295 /**
   6296  * Tests linking a deposit to a transfer that the exchange claims to
   6297  * have wired to an account we do not know.  This is a *permanent*
   6298  * failure: the caller must be able to tell it apart from a successful
   6299  * settlement, and the deposit must not make its order count as wired.
   6300  *
   6301  * @param instance the instance that the deposit and transfer are for.
   6302  * @param signkey the signing key used on the deposit.
   6303  * @param order the order the deposit was made for.
   6304  * @param deposit the deposit.
   6305  * @param transfer the transfer the exchange claims to have made.
   6306  * @return 0 on success, 1 otherwise.
   6307  */
   6308 static int
   6309 test_insert_deposit_to_unknown_account (
   6310   const struct InstanceData *instance,
   6311   const struct ExchangeSignkeyData *signkey,
   6312   const struct OrderData *order,
   6313   const struct DepositData *deposit,
   6314   const struct TransferData *transfer)
   6315 {
   6316   const struct TALER_EXCHANGE_DepositData deposit_data = {
   6317     .exchange_pub = signkey->exchange_pub,
   6318     .exchange_sig = deposit->exchange_sig,
   6319     .wtid = transfer->wtid,
   6320     .execution_time = transfer->data.execution_time,
   6321     .coin_contribution = deposit->amount_with_fee
   6322   };
   6323   struct TALER_MerchantWireHashP bogus_h_wire;
   6324   uint64_t deposit_serial;
   6325 
   6326   GNUNET_CRYPTO_random_block (&bogus_h_wire,
   6327                               sizeof (bogus_h_wire));
   6328   TEST_SET_INSTANCE (instance->instance.id,
   6329                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   6330   deposit_serial = get_deposit_serial (instance,
   6331                                        order,
   6332                                        deposit);
   6333   TEST_COND_RET_ON_FAIL (TALER_MERCHANTDB_DTTS_ACCOUNT_UNKNOWN ==
   6334                          TALER_MERCHANTDB_insert_deposit_to_transfer (
   6335                            pg,
   6336                            deposit_serial,
   6337                            &bogus_h_wire,
   6338                            deposit->exchange_url,
   6339                            &deposit_data),
   6340                          "insert deposit to transfer with an unknown target account must report ACCOUNT_UNKNOWN\n");
   6341   return 0;
   6342 }
   6343 
   6344 
   6345 /**
   6346  * Inserts details for a transfer into the database.
   6347  *
   6348  * @param instance the instance the transfer is in.
   6349  * @param account the destination account for the transfer.
   6350  * @param transfer the transfer we are adding details to.
   6351  * @param expected_result the result expected from the db.
   6352  * @return 0 on success, 1 otherwise.
   6353  */
   6354 static int
   6355 test_insert_transfer_details (
   6356   const struct InstanceData *instance,
   6357   const struct TALER_MERCHANTDB_AccountDetails *account,
   6358   const struct TransferData *transfer,
   6359   enum GNUNET_DB_QueryStatus expected_result)
   6360 {
   6361   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   6362   TEST_COND_RET_ON_FAIL (expected_result ==
   6363                          TALER_MERCHANTDB_insert_transfer_details (
   6364                            pg,
   6365                            instance->instance.id,
   6366                            transfer->exchange_url,
   6367                            account->payto_uri,
   6368                            &transfer->wtid,
   6369                            &transfer->data,
   6370                            NULL),
   6371                          "Insert transfer details failed\n");
   6372   return 0;
   6373 }
   6374 
   6375 
   6376 /**
   6377  * Inserts details for a transfer into the database and checks how
   6378  * many orders were reported as fully settled by it.  An aggregated
   6379  * transfer settles several orders at once and every one of them must
   6380  * be reported, otherwise clients long-polling on the others never
   6381  * learn that the order was wired.
   6382  *
   6383  * @param instance the instance the transfer is in.
   6384  * @param account the destination account for the transfer.
   6385  * @param transfer the transfer we are adding details to.
   6386  * @param expected_result the result expected from the db.
   6387  * @param expected_settled number of orders that must be reported.
   6388  * @return 0 on success, 1 otherwise.
   6389  */
   6390 static int
   6391 test_insert_transfer_details_settling (
   6392   const struct InstanceData *instance,
   6393   const struct TALER_MERCHANTDB_AccountDetails *account,
   6394   const struct TransferData *transfer,
   6395   enum GNUNET_DB_QueryStatus expected_result,
   6396   unsigned int expected_settled)
   6397 {
   6398   unsigned int num_settled = 0;
   6399 
   6400   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   6401   TEST_COND_RET_ON_FAIL (expected_result ==
   6402                          TALER_MERCHANTDB_insert_transfer_details (
   6403                            pg,
   6404                            instance->instance.id,
   6405                            transfer->exchange_url,
   6406                            account->payto_uri,
   6407                            &transfer->wtid,
   6408                            &transfer->data,
   6409                            &num_settled),
   6410                          "Insert transfer details failed\n");
   6411   TEST_COND_RET_ON_FAIL (expected_settled == num_settled,
   6412                          "Wrong number of settled orders reported\n");
   6413   return 0;
   6414 }
   6415 
   6416 
   6417 /**
   6418  * Container for data used when testing transfers.
   6419  */
   6420 struct TestTransfers_Closure
   6421 {
   6422   /**
   6423    * The instance.
   6424    */
   6425   struct InstanceData instance;
   6426 
   6427   /**
   6428    * The account.
   6429    */
   6430   struct TALER_MERCHANTDB_AccountDetails account;
   6431 
   6432   /**
   6433    * The exchange signing key.
   6434    */
   6435   struct ExchangeSignkeyData signkey;
   6436 
   6437   /**
   6438    * The order data.
   6439    */
   6440   struct OrderData order;
   6441 
   6442   /**
   6443    * Two more orders, both settled by one aggregated transfer.
   6444    */
   6445   struct OrderData orders_agg[2];
   6446 
   6447   /**
   6448    * Order with one deposit that settles and one that fails
   6449    * permanently.
   6450    */
   6451   struct OrderData order_perm;
   6452 
   6453   /**
   6454    * The deposit data.
   6455    */
   6456   struct DepositData deposit;
   6457 
   6458   /**
   6459    * A second deposit for the same order, used to test an
   6460    * exchange response that lists the same coin twice.
   6461    */
   6462   struct DepositData deposit2;
   6463 
   6464   /**
   6465    * One deposit for each of @e orders_agg.
   6466    */
   6467   struct DepositData deposits_agg[2];
   6468 
   6469   /**
   6470    * Two deposits for @e order_perm: the first one settles, the
   6471    * second one is wired to an account we do not know.
   6472    */
   6473   struct DepositData deposits_perm[2];
   6474 
   6475   /**
   6476    * Wire fee data.
   6477    */
   6478   struct WireFeeData wire_fee[2];
   6479 
   6480   /**
   6481    * The transfers.
   6482    */
   6483   struct TransferData transfers[5];
   6484 };
   6485 
   6486 
   6487 /**
   6488  * Prepares for testing transfers.
   6489  *
   6490  * @param cls the test data.
   6491  */
   6492 static void
   6493 pre_test_transfers (struct TestTransfers_Closure *cls)
   6494 {
   6495   /* Instance */
   6496   make_instance ("test_inst_transfers",
   6497                  &cls->instance);
   6498 
   6499   /* Account */
   6500   make_account (&cls->account);
   6501   cls->account.instance_id = cls->instance.instance.id;
   6502   /* Order */
   6503   make_order ("test_transfers_od_1",
   6504               &cls->order);
   6505 
   6506   /* Signing key */
   6507   make_exchange_signkey (&cls->signkey);
   6508 
   6509   /* Deposit */
   6510   make_deposit (&cls->instance,
   6511                 &cls->account,
   6512                 &cls->order,
   6513                 &cls->signkey,
   6514                 &cls->deposit);
   6515 
   6516   /* Wire fee */
   6517   make_wire_fee (&cls->signkey,
   6518                  &cls->wire_fee[0]);
   6519   make_wire_fee (&cls->signkey,
   6520                  &cls->wire_fee[1]);
   6521   cls->wire_fee[1].wire_method = "wire-method-2";
   6522   GNUNET_CRYPTO_hash (cls->wire_fee[1].wire_method,
   6523                       strlen (cls->wire_fee[1].wire_method) + 1,
   6524                       &cls->wire_fee[1].h_wire_method);
   6525 
   6526   /* Transfers */
   6527   make_transfer (&cls->signkey,
   6528                  1,
   6529                  &cls->deposit,
   6530                  &cls->transfers[0]);
   6531   cls->transfers[0].confirmed = true;
   6532   /* A transfer the exchange reports without any deposits. */
   6533   make_transfer (&cls->signkey,
   6534                  0,
   6535                  &cls->deposit,
   6536                  &cls->transfers[1]);
   6537   cls->transfers[1].confirmed = true;
   6538   /* A transfer where the exchange lists the same coin twice. */
   6539   make_deposit (&cls->instance,
   6540                 &cls->account,
   6541                 &cls->order,
   6542                 &cls->signkey,
   6543                 &cls->deposit2);
   6544   {
   6545     struct DepositData dup[2];
   6546 
   6547     dup[0] = cls->deposit2;
   6548     dup[1] = cls->deposit2;
   6549     make_transfer (&cls->signkey,
   6550                    2,
   6551                    dup,
   6552                    &cls->transfers[2]);
   6553   }
   6554   cls->transfers[2].confirmed = true;
   6555   /* Two orders settled by one aggregated wire transfer. */
   6556   make_order ("test_transfers_od_agg_1",
   6557               &cls->orders_agg[0]);
   6558   make_order ("test_transfers_od_agg_2",
   6559               &cls->orders_agg[1]);
   6560   for (unsigned int i = 0; i < 2; i++)
   6561     make_deposit (&cls->instance,
   6562                   &cls->account,
   6563                   &cls->orders_agg[i],
   6564                   &cls->signkey,
   6565                   &cls->deposits_agg[i]);
   6566   make_transfer (&cls->signkey,
   6567                  2,
   6568                  cls->deposits_agg,
   6569                  &cls->transfers[3]);
   6570   cls->transfers[3].confirmed = true;
   6571   /* An order with two deposits, one of which the exchange wires to an
   6572      account we do not know (permanent failure). */
   6573   make_order ("test_transfers_od_perm",
   6574               &cls->order_perm);
   6575   for (unsigned int i = 0; i < 2; i++)
   6576     make_deposit (&cls->instance,
   6577                   &cls->account,
   6578                   &cls->order_perm,
   6579                   &cls->signkey,
   6580                   &cls->deposits_perm[i]);
   6581   make_transfer (&cls->signkey,
   6582                  1,
   6583                  cls->deposits_perm,
   6584                  &cls->transfers[4]);
   6585   cls->transfers[4].confirmed = true;
   6586 }
   6587 
   6588 
   6589 /**
   6590  * Cleans up after testing transfers.
   6591  *
   6592  * @param cls the test data.
   6593  */
   6594 static void
   6595 post_test_transfers (struct TestTransfers_Closure *cls)
   6596 {
   6597   GNUNET_array_grow (cls->transfers->data.details,
   6598                      cls->transfers->data.details_length,
   6599                      0);
   6600   GNUNET_array_grow (cls->transfers[2].data.details,
   6601                      cls->transfers[2].data.details_length,
   6602                      0);
   6603   GNUNET_array_grow (cls->transfers[3].data.details,
   6604                      cls->transfers[3].data.details_length,
   6605                      0);
   6606   GNUNET_array_grow (cls->transfers[4].data.details,
   6607                      cls->transfers[4].data.details_length,
   6608                      0);
   6609   free_instance_data (&cls->instance);
   6610   free_order_data (&cls->order);
   6611   free_order_data (&cls->orders_agg[0]);
   6612   free_order_data (&cls->orders_agg[1]);
   6613   free_order_data (&cls->order_perm);
   6614 }
   6615 
   6616 
   6617 /**
   6618  * Runs the tests for transfers.
   6619  *
   6620  * @param cls the test data.
   6621  * @return 0 on success, 1 otherwise.
   6622  */
   6623 static int
   6624 run_test_transfers (struct TestTransfers_Closure *cls)
   6625 {
   6626   uint64_t order_serial;
   6627   struct TALER_WireFeeSet fees;
   6628 
   6629   /* Test lookup wire fee fails when it isn't in the db */
   6630   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
   6631                          TALER_MERCHANTDB_get_exchange_wire_fee (pg,
   6632                                                                  &cls->signkey.master_pub,
   6633                                                                  cls->wire_fee[0].wire_method,
   6634                                                                  GNUNET_TIME_timestamp_get (),
   6635                                                                  &fees,
   6636                                                                  NULL,
   6637                                                                  NULL,
   6638                                                                  NULL),
   6639                          "Lookup wire fee failed\n");
   6640   /* Test store wire fee by exchange */
   6641   TEST_RET_ON_FAIL (test_insert_wire_fee (&cls->signkey,
   6642                                           &cls->wire_fee[0],
   6643                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6644   /* Test double insertion fails */
   6645   TEST_RET_ON_FAIL (test_insert_wire_fee (&cls->signkey,
   6646                                           &cls->wire_fee[0],
   6647                                           GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   6648   /* Test lookup wire fee by exchange */
   6649   TEST_RET_ON_FAIL (test_lookup_wire_fee (&cls->signkey,
   6650                                           &cls->wire_fee[0]));
   6651   /* A *different* fee for the same (master_pub, h_wire_method,
   6652      start_date) must be reported, not silently dropped: the stored fee
   6653      is the one the exchange signed, and continuing to compute with a
   6654      stale fee is an accounting error.  Regression test: the INSERT used
   6655      a bare 'ON CONFLICT DO NOTHING' and returned NO_RESULTS here. */
   6656   {
   6657     struct WireFeeData changed = cls->wire_fee[0];
   6658 
   6659     GNUNET_assert (GNUNET_OK ==
   6660                    TALER_string_to_amount ("EUR:0.99",
   6661                                            &changed.fees.wire));
   6662     TALER_exchange_offline_wire_fee_sign (changed.wire_method,
   6663                                           changed.wire_fee_start,
   6664                                           changed.wire_fee_end,
   6665                                           &changed.fees,
   6666                                           &cls->signkey.master_priv,
   6667                                           &changed.fee_sig);
   6668     TEST_RET_ON_FAIL (test_insert_wire_fee (&cls->signkey,
   6669                                             &changed,
   6670                                             GNUNET_DB_STATUS_HARD_ERROR));
   6671     /* ... and the fee on file must be untouched. */
   6672     TEST_RET_ON_FAIL (test_lookup_wire_fee (&cls->signkey,
   6673                                             &cls->wire_fee[0]));
   6674   }
   6675   /* Test different wire fees for different methods. */
   6676   TEST_RET_ON_FAIL (test_insert_wire_fee (&cls->signkey,
   6677                                           &cls->wire_fee[1],
   6678                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6679   TEST_RET_ON_FAIL (test_lookup_wire_fee (&cls->signkey,
   6680                                           &cls->wire_fee[1]));
   6681   /* Insert the instance */
   6682   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
   6683                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6684   /* Insert the account */
   6685   TEST_RET_ON_FAIL (test_insert_account (&cls->instance,
   6686                                          &cls->account,
   6687                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6688   /* Insert a signing key */
   6689   TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey,
   6690                                                   GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6691   /* Insert an order */
   6692   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
   6693                                        &cls->order,
   6694                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6695   /* Insert contract terms */
   6696   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
   6697                                                 &cls->order,
   6698                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6699   order_serial = get_order_serial (&cls->instance,
   6700                                    &cls->order);
   6701   /* Insert the deposit */
   6702   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
   6703                                          &cls->signkey,
   6704                                          &cls->deposit,
   6705                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6706   /* Mark as paid, otherwise wiring doesn't make sense. */
   6707   TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
   6708                                              &cls->order,
   6709                                              GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6710   /* A payto URI that matches no account must be rejected, not silently
   6711      attributed to some arbitrary account of the instance. */
   6712   TEST_RET_ON_FAIL (test_insert_transfer_unknown_account (&cls->instance,
   6713                                                           &cls->transfers[0]));
   6714   /* Insert the transfer */
   6715   TEST_RET_ON_FAIL (test_insert_transfer (&cls->instance,
   6716                                           &cls->account,
   6717                                           &cls->transfers[0],
   6718                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6719   TEST_RET_ON_FAIL (test_insert_transfer (&cls->instance,
   6720                                           &cls->account,
   6721                                           &cls->transfers[0],
   6722                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6723   TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance,
   6724                                                      &cls->signkey,
   6725                                                      &cls->order,
   6726                                                      &cls->deposit,
   6727                                                      &cls->transfers[0],
   6728                                                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   6729                                                      false));
   6730   TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance,
   6731                                                      &cls->signkey,
   6732                                                      &cls->order,
   6733                                                      &cls->deposit,
   6734                                                      &cls->transfers[0],
   6735                                                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   6736                                                      false));
   6737   TEST_RET_ON_FAIL (test_insert_transfer_details (&cls->instance,
   6738                                                   &cls->account,
   6739                                                   &cls->transfers[0],
   6740                                                   GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6741   /* Inserting deposits/transfers will automatically mark as wired. */
   6742   TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
   6743                                                 cls->order.id,
   6744                                                 NULL,
   6745                                                 true,
   6746                                                 true));
   6747   TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance,
   6748                                                      &cls->signkey,
   6749                                                      &cls->order,
   6750                                                      &cls->deposit,
   6751                                                      &cls->transfers[0],
   6752                                                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   6753                                                      true));
   6754 
   6755   TEST_RET_ON_FAIL (test_lookup_transfer_summary (cls->deposit.exchange_url,
   6756                                                   &cls->transfers[0].wtid,
   6757                                                   cls->order.id,
   6758                                                   &cls->deposit.amount_with_fee,
   6759                                                   &cls->deposit.deposit_fee));
   6760   TEST_RET_ON_FAIL (test_lookup_transfer_details (cls->deposit.exchange_url,
   6761                                                   &cls->transfers[0].wtid,
   6762                                                   1,
   6763                                                   &cls->transfers[0].data.
   6764                                                   details[0]));
   6765   TEST_RET_ON_FAIL (test_lookup_transfer_details_by_order (order_serial,
   6766                                                            1,
   6767                                                            &cls->transfers[0]));
   6768   TEST_RET_ON_FAIL (test_lookup_transfers (&cls->instance,
   6769                                            &cls->account,
   6770                                            GNUNET_TIME_UNIT_FOREVER_TS,
   6771                                            GNUNET_TIME_UNIT_ZERO_TS,
   6772                                            8,
   6773                                            0,
   6774                                            1,
   6775                                            &cls->transfers[0]));
   6776   /* The exchange is allowed to report a wire transfer with an empty
   6777      list of deposits; that must not fail the transaction (which used
   6778      to abort taler-merchant-reconciliation for all instances).
   6779      The deposit-to-transfer insert is what makes us expect the
   6780      transfer in the first place, so that we get past the account
   6781      lookup and actually reach the per-coin loop. */
   6782   TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance,
   6783                                                      &cls->signkey,
   6784                                                      &cls->order,
   6785                                                      &cls->deposit,
   6786                                                      &cls->transfers[1],
   6787                                                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   6788                                                      false));
   6789   TEST_RET_ON_FAIL (test_insert_transfer_details (&cls->instance,
   6790                                                   &cls->account,
   6791                                                   &cls->transfers[1],
   6792                                                   GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6793   /* The exchange may list the same coin twice in one response;
   6794      merchant_expected_transfer_to_coin is UNIQUE on deposit_serial,
   6795      so this used to be a hard error killing the reconciliation
   6796      daemon for all instances. */
   6797   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
   6798                                          &cls->signkey,
   6799                                          &cls->deposit2,
   6800                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6801   TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance,
   6802                                                      &cls->signkey,
   6803                                                      &cls->order,
   6804                                                      &cls->deposit2,
   6805                                                      &cls->transfers[2],
   6806                                                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   6807                                                      false));
   6808   TEST_RET_ON_FAIL (test_insert_transfer_details (&cls->instance,
   6809                                                   &cls->account,
   6810                                                   &cls->transfers[2],
   6811                                                   GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6812   /* One aggregated transfer settles two orders: BOTH of them must be
   6813      reported, otherwise clients long-polling on the order that is not
   6814      reported never learn that it was wired. */
   6815   for (unsigned int i = 0; i < 2; i++)
   6816   {
   6817     TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
   6818                                          &cls->orders_agg[i],
   6819                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6820     TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
   6821                                                   &cls->orders_agg[i],
   6822                                                   GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6823     TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
   6824                                            &cls->signkey,
   6825                                            &cls->deposits_agg[i],
   6826                                            GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6827     TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
   6828                                                &cls->orders_agg[i],
   6829                                                GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6830     TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance,
   6831                                                        &cls->signkey,
   6832                                                        &cls->orders_agg[i],
   6833                                                        &cls->deposits_agg[i],
   6834                                                        &cls->transfers[3],
   6835                                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   6836                                                        false));
   6837   }
   6838   TEST_RET_ON_FAIL (test_insert_transfer_details_settling (
   6839                       &cls->instance,
   6840                       &cls->account,
   6841                       &cls->transfers[3],
   6842                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   6843                       2));
   6844   /* A deposit that failed permanently (exchange wired the money to an
   6845      account we do not know) must NOT make the order count as wired,
   6846      and no order_settled notification may be generated for it. */
   6847   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
   6848                                        &cls->order_perm,
   6849                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6850   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
   6851                                                 &cls->order_perm,
   6852                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6853   for (unsigned int i = 0; i < 2; i++)
   6854     TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
   6855                                            &cls->signkey,
   6856                                            &cls->deposits_perm[i],
   6857                                            GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6858   TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
   6859                                              &cls->order_perm,
   6860                                              GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   6861   TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance,
   6862                                                      &cls->signkey,
   6863                                                      &cls->order_perm,
   6864                                                      &cls->deposits_perm[0],
   6865                                                      &cls->transfers[4],
   6866                                                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   6867                                                      false));
   6868   TEST_RET_ON_FAIL (test_insert_deposit_to_unknown_account (
   6869                       &cls->instance,
   6870                       &cls->signkey,
   6871                       &cls->order_perm,
   6872                       &cls->deposits_perm[1],
   6873                       &cls->transfers[4]));
   6874   TEST_RET_ON_FAIL (test_insert_transfer_details_settling (
   6875                       &cls->instance,
   6876                       &cls->account,
   6877                       &cls->transfers[4],
   6878                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   6879                       0));
   6880   TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
   6881                                                 cls->order_perm.id,
   6882                                                 NULL,
   6883                                                 true,
   6884                                                 false));
   6885   return 0;
   6886 }
   6887 
   6888 
   6889 /**
   6890  * Takes care of all work for testing transfers.
   6891  *
   6892  * @return 0 on success, 1 otherwise.
   6893  */
   6894 static int
   6895 test_transfers (void)
   6896 {
   6897   struct TestTransfers_Closure test_cls;
   6898   int test_result;
   6899 
   6900   pre_test_transfers (&test_cls);
   6901   test_result = run_test_transfers (&test_cls);
   6902   post_test_transfers (&test_cls);
   6903   return test_result;
   6904 }
   6905 
   6906 
   6907 /**
   6908  * Closure for testing iterate_refunds.
   6909  */
   6910 struct TestLookupRefunds_Closure
   6911 {
   6912   /**
   6913    * Length of @e coin_pub_to_cmp and @e refund_amount_to_cmp.
   6914    */
   6915   unsigned int refunds_to_cmp_length;
   6916 
   6917   /**
   6918    * Public keys of the refunded coins.
   6919    */
   6920   const struct TALER_CoinSpendPublicKeyP *coin_pub_to_cmp;
   6921 
   6922   /**
   6923    * Amount of each refund.
   6924    */
   6925   const struct TALER_Amount *refund_amount_to_cmp;
   6926 
   6927   /**
   6928    * Number of results matching each refund provided.
   6929    */
   6930   unsigned int *results_matching;
   6931 
   6932   /**
   6933    * Total number of results returned;
   6934    */
   6935   unsigned int results_length;
   6936 };
   6937 
   6938 
   6939 /**
   6940  * Called after test_lookup_refunds.
   6941  * @param cls pointer to a TestLookupRefunds_Closure.
   6942  * @param coin_pub the public key of the coin for the refund found.
   6943  * @param refund_amount the amount of the refund found.
   6944  */
   6945 static void
   6946 lookup_refunds_cb (void *cls,
   6947                    const struct TALER_CoinSpendPublicKeyP *coin_pub,
   6948                    const struct TALER_Amount *refund_amount)
   6949 {
   6950   struct TestLookupRefunds_Closure *cmp = cls;
   6951   if (NULL == cmp)
   6952     return;
   6953   cmp->results_length += 1;
   6954   for (unsigned int i = 0; cmp->refunds_to_cmp_length > i; ++i)
   6955   {
   6956     if ((0 == GNUNET_memcmp (&cmp->coin_pub_to_cmp[i],
   6957                              coin_pub)) &&
   6958         (GNUNET_OK == TALER_amount_cmp_currency (&cmp->refund_amount_to_cmp[i],
   6959                                                  refund_amount)) &&
   6960         (0 == TALER_amount_cmp (&cmp->refund_amount_to_cmp[i],
   6961                                 refund_amount)))
   6962     {
   6963       cmp->results_matching[i] += 1;
   6964     }
   6965   }
   6966 }
   6967 
   6968 
   6969 /**
   6970  * Tests looking up refunds from the database.
   6971  * @param instance the instance to look up refunds for.
   6972  * @param h_contract_terms hash of the contract terms the refunds are for.
   6973  * @param refunds_length length of @e coin_pubs and @e refund_amounts.
   6974  * @param coin_pubs the public keys of the coins that were refunded.
   6975  * @param refund_amounts the amounts of the coins that were refunded.
   6976  *
   6977  * @return 0 on success, 1 otherwise.
   6978  */
   6979 static int
   6980 test_lookup_refunds (const struct InstanceData *instance,
   6981                      const struct TALER_PrivateContractHashP *h_contract_terms,
   6982                      unsigned int refunds_length,
   6983                      const struct TALER_CoinSpendPublicKeyP *coin_pubs,
   6984                      const struct TALER_Amount *refund_amounts)
   6985 {
   6986   unsigned int results_matching[refunds_length];
   6987   struct TestLookupRefunds_Closure cmp = {
   6988     .refunds_to_cmp_length = refunds_length,
   6989     .coin_pub_to_cmp = coin_pubs,
   6990     .refund_amount_to_cmp = refund_amounts,
   6991     .results_matching = results_matching,
   6992     .results_length = 0
   6993   };
   6994   memset (results_matching, 0, sizeof (unsigned int) * refunds_length);
   6995   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   6996   if (1 != TALER_MERCHANTDB_iterate_refunds (pg,
   6997                                              instance->instance.id,
   6998                                              h_contract_terms,
   6999                                              &lookup_refunds_cb,
   7000                                              &cmp))
   7001   {
   7002     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   7003                 "Lookup refunds failed\n");
   7004     return 1;
   7005   }
   7006   if (refunds_length != cmp.results_length)
   7007   {
   7008     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   7009                 "Lookup refunds failed: incorrect number of results returned\n")
   7010     ;
   7011     return 1;
   7012   }
   7013   for (unsigned int i = 0; refunds_length > i; ++i)
   7014   {
   7015     if (1 != cmp.results_matching[i])
   7016     {
   7017       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   7018                   "Lookup refunds failed: mismatched data\n");
   7019       return 1;
   7020     }
   7021   }
   7022   return 0;
   7023 }
   7024 
   7025 
   7026 /**
   7027  * Container for refund data.
   7028  */
   7029 struct RefundData
   7030 {
   7031   /**
   7032    * When the refund occurred.
   7033    */
   7034   struct GNUNET_TIME_Timestamp timestamp;
   7035 
   7036   /**
   7037    * Reason for the refund.
   7038    */
   7039   const char *reason;
   7040 
   7041   /**
   7042    * Amount of the refund.
   7043    */
   7044   struct TALER_Amount refund_amount;
   7045 
   7046   /**
   7047    * Public key of the coin that was refunded.
   7048    */
   7049   const struct TALER_CoinSpendPublicKeyP *coin_pub;
   7050 
   7051   /**
   7052    * URL to the exchange that did the refund.
   7053    */
   7054   const char *exchange_url;
   7055 };
   7056 
   7057 
   7058 /**
   7059  * Creates a refund for testing with.
   7060  * @param deposit the deposit being refunded.
   7061  * @param refund the data to set.
   7062  */
   7063 static void
   7064 make_refund (const struct DepositData *deposit,
   7065              struct RefundData *refund)
   7066 {
   7067   refund->timestamp = GNUNET_TIME_timestamp_get ();
   7068   refund->reason = "some reason";
   7069   refund->refund_amount = deposit->amount_with_fee;
   7070   refund->coin_pub = &deposit->coin_pub;
   7071   refund->exchange_url = deposit->exchange_url;
   7072 }
   7073 
   7074 
   7075 /**
   7076  * Container for proof of a refund.
   7077  */
   7078 struct RefundProofData
   7079 {
   7080   /**
   7081    * Fee charged for the refund.
   7082    */
   7083   struct TALER_Amount refund_fee;
   7084 
   7085   /**
   7086    * The exchange's signature on the refund.
   7087    */
   7088   struct TALER_ExchangeSignatureP exchange_sig;
   7089 };
   7090 
   7091 
   7092 /**
   7093  * Closure for testing iterate_refunds_detailed.
   7094  */
   7095 struct TestLookupRefundsDetailed_Closure
   7096 {
   7097   /**
   7098    * Length of @e refunds_to_cmp.
   7099    */
   7100   unsigned int refunds_to_cmp_length;
   7101 
   7102   /**
   7103    * The refunds we expect to find.
   7104    */
   7105   const struct RefundData *refunds_to_cmp;
   7106 
   7107   /**
   7108    * Whether to compare the timestamps or not (if we don't have direct control
   7109    * of the timestamps, there will be differences on the order of microseconds
   7110    * that can be ignored).
   7111    */
   7112   bool cmp_timestamps;
   7113 
   7114   /**
   7115    * The number of results matching each refund.
   7116    */
   7117   unsigned int *results_matching;
   7118 
   7119   /**
   7120    * The total number of results from the db.
   7121    */
   7122   unsigned int results_length;
   7123 };
   7124 
   7125 
   7126 /**
   7127  * Called after test_lookup_refunds_detailed.
   7128  * @param cls pointer to a TestLookupRefundsDetailed_Closure.
   7129  * @param refund_serial unique serial number of the refund
   7130  * @param timestamp time of the refund (for grouping of refunds in the wallet UI)
   7131  * @param coin_pub public coin from which the refund comes from
   7132  * @param exchange_url URL of the exchange that issued @a coin_pub
   7133  * @param rtransaction_id identificator of the refund
   7134  * @param reason human-readable explanation of the refund
   7135  * @param refund_amount refund amount which is being taken from @a coin_pub
   7136  * @param pending true if this refund has not been processed by the wallet/exchange
   7137  */
   7138 static void
   7139 lookup_refunds_detailed_cb (void *cls,
   7140                             uint64_t refund_serial,
   7141                             struct GNUNET_TIME_Timestamp timestamp,
   7142                             const struct TALER_CoinSpendPublicKeyP *coin_pub,
   7143                             const char *exchange_url,
   7144                             uint64_t rtransaction_id,
   7145                             const char *reason,
   7146                             const struct TALER_Amount *refund_amount,
   7147                             bool pending)
   7148 {
   7149   struct TestLookupRefundsDetailed_Closure *cmp = cls;
   7150   if (NULL == cmp)
   7151     return;
   7152   cmp->results_length += 1;
   7153   for (unsigned int i = 0; cmp->refunds_to_cmp_length > i; ++i)
   7154   {
   7155     if (((GNUNET_TIME_timestamp_cmp (cmp->refunds_to_cmp[i].timestamp,
   7156                                      ==,
   7157                                      timestamp)) ||
   7158          ! cmp->cmp_timestamps) &&
   7159         (0 == GNUNET_memcmp (cmp->refunds_to_cmp[i].coin_pub,
   7160                              coin_pub)) &&
   7161         (0 == strcmp (cmp->refunds_to_cmp[i].exchange_url,
   7162                       exchange_url)) &&
   7163         (0 == strcmp (cmp->refunds_to_cmp[i].reason,
   7164                       reason)) &&
   7165         (GNUNET_OK ==
   7166          TALER_amount_cmp_currency (
   7167            &cmp->refunds_to_cmp[i].refund_amount,
   7168            refund_amount)) &&
   7169         (0 == TALER_amount_cmp (&cmp->refunds_to_cmp[i].refund_amount,
   7170                                 refund_amount)))
   7171     {
   7172       cmp->results_matching[i] += 1;
   7173     }
   7174   }
   7175 }
   7176 
   7177 
   7178 /**
   7179  * Tests looking up refunds with details from the database.
   7180  * @param instance the instance to lookup from.
   7181  * @param h_contract_terms the contract terms the refunds were made for.
   7182  * @param cmp_timestamps whether to compare timestamps or not.
   7183  * @param refunds_length length of @e refunds.
   7184  * @param refunds the refunds we expect to be returned.
   7185  *
   7186  * @return 0 on success, 1 otherwise.
   7187  */
   7188 static int
   7189 test_lookup_refunds_detailed (
   7190   const struct InstanceData *instance,
   7191   const struct TALER_PrivateContractHashP *h_contract_terms,
   7192   bool cmp_timestamps,
   7193   unsigned int refunds_length,
   7194   const struct RefundData *refunds)
   7195 {
   7196   unsigned int results_matching[refunds_length];
   7197   struct TestLookupRefundsDetailed_Closure cmp = {
   7198     .refunds_to_cmp_length = refunds_length,
   7199     .refunds_to_cmp = refunds,
   7200     .cmp_timestamps = cmp_timestamps,
   7201     .results_matching = results_matching,
   7202     .results_length = 0
   7203   };
   7204   memset (results_matching, 0, sizeof (unsigned int) * refunds_length);
   7205   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   7206   if (0 > TALER_MERCHANTDB_iterate_refunds_detailed (pg,
   7207                                                      instance->instance.id,
   7208                                                      h_contract_terms,
   7209                                                      &lookup_refunds_detailed_cb,
   7210                                                      &cmp))
   7211   {
   7212     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   7213                 "Lookup refunds detailed failed\n");
   7214     return 1;
   7215   }
   7216   if (refunds_length != cmp.results_length)
   7217   {
   7218     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   7219                 "Lookup refunds detailed failed: incorrect number of results\n")
   7220     ;
   7221     return 1;
   7222   }
   7223   for (unsigned int i = 0; refunds_length > i; ++i)
   7224   {
   7225     if (1 != cmp.results_matching[i])
   7226     {
   7227       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   7228                   "Lookup refunds detailed failed: mismatched data\n");
   7229       return 1;
   7230     }
   7231   }
   7232   return 0;
   7233 }
   7234 
   7235 
   7236 /**
   7237  * Closure for get_refund_serial.
   7238  */
   7239 struct LookupRefundSerial_Closure
   7240 {
   7241   /**
   7242    * The refund we are looking up the id for.
   7243    */
   7244   const struct RefundData *refund;
   7245 
   7246   /**
   7247    * The row number found.
   7248    */
   7249   uint64_t serial;
   7250 };
   7251 
   7252 
   7253 /**
   7254  * Called after get_refund_serial.
   7255  * @param cls pointer to a LookupRefundSerial_Closure.
   7256  * @param refund_serial unique serial number of the refund
   7257  * @param timestamp time of the refund (for grouping of refunds in the wallet UI)
   7258  * @param coin_pub public coin from which the refund comes from
   7259  * @param exchange_url URL of the exchange that issued @a coin_pub
   7260  * @param rtransaction_id identificator of the refund
   7261  * @param reason human-readable explanation of the refund
   7262  * @param refund_amount refund amount which is being taken from @a coin_pub
   7263  * @param pending true if this refund has not been processed by the wallet/exchange
   7264  */
   7265 static void
   7266 get_refund_serial_cb (void *cls,
   7267                       uint64_t refund_serial,
   7268                       struct GNUNET_TIME_Timestamp timestamp,
   7269                       const struct TALER_CoinSpendPublicKeyP *coin_pub,
   7270                       const char *exchange_url,
   7271                       uint64_t rtransaction_id,
   7272                       const char *reason,
   7273                       const struct TALER_Amount *refund_amount,
   7274                       bool pending)
   7275 {
   7276   struct LookupRefundSerial_Closure *lookup_cls = cls;
   7277   if (NULL == lookup_cls)
   7278     return;
   7279   if ((GNUNET_TIME_timestamp_cmp (lookup_cls->refund->timestamp,
   7280                                   ==,
   7281                                   timestamp)) &&
   7282       (0 == GNUNET_memcmp (lookup_cls->refund->coin_pub,
   7283                            coin_pub)) &&
   7284       (0 == strcmp (lookup_cls->refund->exchange_url,
   7285                     exchange_url)) &&
   7286       (0 == strcmp (lookup_cls->refund->reason,
   7287                     reason)) &&
   7288       (GNUNET_OK ==
   7289        TALER_amount_cmp_currency (
   7290          &lookup_cls->refund->refund_amount,
   7291          refund_amount)) &&
   7292       (0 == TALER_amount_cmp (&lookup_cls->refund->refund_amount,
   7293                               refund_amount)))
   7294     lookup_cls->serial = refund_serial;
   7295 }
   7296 
   7297 
   7298 /**
   7299  * Utility function for getting the database row number of a refund.
   7300  * @param instance the instance associated with the refund.
   7301  * @param h_contract_terms the contract terms the refund was made with.
   7302  * @param refund the refund we are querying the row number of.
   7303  *
   7304  * @return the row number of the refund.
   7305  */
   7306 static uint64_t
   7307 get_refund_serial (const struct InstanceData *instance,
   7308                    const struct TALER_PrivateContractHashP *h_contract_terms,
   7309                    const struct RefundData *refund)
   7310 {
   7311   struct LookupRefundSerial_Closure lookup_cls = {
   7312     .refund = refund,
   7313     .serial = 0
   7314   };
   7315 
   7316   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   7317                  TALER_MERCHANTDB_set_instance (pg,
   7318                                                 instance->instance.id));
   7319   GNUNET_assert (0 < TALER_MERCHANTDB_iterate_refunds_detailed (pg,
   7320                                                                 instance->instance.id,
   7321                                                                 h_contract_terms,
   7322                                                                 &get_refund_serial_cb,
   7323                                                                 &lookup_cls));
   7324   GNUNET_assert (0 != lookup_cls.serial);
   7325 
   7326   return lookup_cls.serial;
   7327 }
   7328 
   7329 
   7330 /**
   7331  * Tests looking up proof of a refund.
   7332  * @param refund_serial the row number of the refund.
   7333  * @param expected_exchange_sig the exchange signature we are expecting.
   7334  * @param expected_exchange_pub the exchange public key we are expecting.
   7335  *
   7336  * @return 0 on success, 1 otherwise.
   7337  */
   7338 static int
   7339 test_lookup_refund_proof (uint64_t refund_serial,
   7340                           const struct
   7341                           TALER_ExchangeSignatureP *expected_exchange_sig,
   7342                           const struct
   7343                           TALER_ExchangePublicKeyP *expected_exchange_pub)
   7344 {
   7345   struct TALER_ExchangeSignatureP exchange_sig;
   7346   struct TALER_ExchangePublicKeyP exchange_pub;
   7347   if (1 != TALER_MERCHANTDB_get_refund_proof (pg,
   7348                                               refund_serial,
   7349                                               &exchange_sig,
   7350                                               &exchange_pub))
   7351   {
   7352     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   7353                 "Lookup refund proof failed\n");
   7354     return 1;
   7355   }
   7356   if ((0 != GNUNET_memcmp (expected_exchange_sig,
   7357                            &exchange_sig)) ||
   7358       (0 != GNUNET_memcmp (expected_exchange_pub,
   7359                            &exchange_pub)))
   7360   {
   7361     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   7362                 "Lookup refund proof failed: mismatched data\n");
   7363     return 1;
   7364   }
   7365   return 0;
   7366 }
   7367 
   7368 
   7369 /**
   7370  * Closure for testing refund functionality.
   7371  */
   7372 struct TestRefunds_Closure
   7373 {
   7374   /**
   7375    * The instance.
   7376    */
   7377   struct InstanceData instance;
   7378 
   7379   /**
   7380    * The merchant account.
   7381    */
   7382   struct TALER_MERCHANTDB_AccountDetails account;
   7383 
   7384   /**
   7385    * The exchange signing key.
   7386    */
   7387   struct ExchangeSignkeyData signkey;
   7388 
   7389   /**
   7390    * The order data.
   7391    */
   7392   struct OrderData orders[3];
   7393 
   7394   /**
   7395    * The deposit data.
   7396    */
   7397   struct DepositData deposits[5];
   7398 
   7399   /**
   7400    * The refund data.
   7401    */
   7402   struct RefundData refunds[3];
   7403 
   7404   /**
   7405    * The refund proof data.
   7406    */
   7407   struct RefundProofData refund_proof;
   7408 };
   7409 
   7410 
   7411 /**
   7412  * Prepares for testing refunds.
   7413  * @param cls the closure to initialize.
   7414  */
   7415 static void
   7416 pre_test_refunds (struct TestRefunds_Closure *cls)
   7417 {
   7418   /* Instance */
   7419   make_instance ("test_inst_refunds",
   7420                  &cls->instance);
   7421 
   7422   /* Account */
   7423   make_account (&cls->account);
   7424   cls->account.instance_id = cls->instance.instance.id;
   7425   /* Signing key */
   7426   make_exchange_signkey (&cls->signkey);
   7427 
   7428   /* Order */
   7429   make_order ("test_refunds_od_0",
   7430               &cls->orders[0]);
   7431   make_order ("test_refunds_od_1",
   7432               &cls->orders[1]);
   7433   make_order ("test_refunds_od_2",
   7434               &cls->orders[2]);
   7435 
   7436   /* Deposit */
   7437   make_deposit (&cls->instance,
   7438                 &cls->account,
   7439                 &cls->orders[0],
   7440                 &cls->signkey,
   7441                 &cls->deposits[0]);
   7442   make_deposit (&cls->instance,
   7443                 &cls->account,
   7444                 &cls->orders[0],
   7445                 &cls->signkey,
   7446                 &cls->deposits[1]);
   7447   make_deposit (&cls->instance,
   7448                 &cls->account,
   7449                 &cls->orders[1],
   7450                 &cls->signkey,
   7451                 &cls->deposits[2]);
   7452   /* Two deposits of the *same* coin on order 2, via two different
   7453      exchanges (and thus two deposit confirmations).  The schema allows
   7454      this: merchant_deposits is unique only on
   7455      (deposit_confirmation_serial, coin_pub).  It makes
   7456      do_increase_refund derive the same rtransaction_id twice, which is
   7457      exactly the collision a concurrent refund on another connection
   7458      produces. */
   7459   make_deposit (&cls->instance,
   7460                 &cls->account,
   7461                 &cls->orders[2],
   7462                 &cls->signkey,
   7463                 &cls->deposits[3]);
   7464   make_deposit (&cls->instance,
   7465                 &cls->account,
   7466                 &cls->orders[2],
   7467                 &cls->signkey,
   7468                 &cls->deposits[4]);
   7469   cls->deposits[4].exchange_url = "https://test-exchange-two/";
   7470   cls->deposits[4].coin_pub = cls->deposits[3].coin_pub;
   7471   cls->deposits[4].coin_sig = cls->deposits[3].coin_sig;
   7472 
   7473   /* Refund */
   7474   make_refund (&cls->deposits[0],
   7475                &cls->refunds[0]);
   7476   make_refund (&cls->deposits[2],
   7477                &cls->refunds[1]);
   7478   make_refund (&cls->deposits[2],
   7479                &cls->refunds[2]);
   7480   GNUNET_assert (GNUNET_OK ==
   7481                  TALER_string_to_amount ("EUR:10.00",
   7482                                          &cls->refunds[1].refund_amount));
   7483   cls->refunds[1].reason = "refund 1";
   7484   GNUNET_assert (GNUNET_OK ==
   7485                  TALER_string_to_amount ("EUR:10.00",
   7486                                          &cls->refunds[2].refund_amount));
   7487   cls->refunds[2].reason = "refund 2";
   7488 
   7489   /* Refund proof */
   7490   GNUNET_assert (GNUNET_OK ==
   7491                  TALER_string_to_amount ("EUR:0.02",
   7492                                          &cls->refund_proof.refund_fee));
   7493   memset (&cls->refund_proof.exchange_sig,
   7494           42,
   7495           sizeof (cls->refund_proof.exchange_sig));
   7496 }
   7497 
   7498 
   7499 /**
   7500  * Cleans up after testing refunds.
   7501  * @param cls the closure.
   7502  */
   7503 static void
   7504 post_test_refunds (struct TestRefunds_Closure *cls)
   7505 {
   7506   free_instance_data (&cls->instance);
   7507   free_order_data (&cls->orders[0]);
   7508   free_order_data (&cls->orders[1]);
   7509   free_order_data (&cls->orders[2]);
   7510 }
   7511 
   7512 
   7513 /**
   7514  * Runs the refund tests.
   7515  * @param cls the closure.
   7516  *
   7517  * @return 0 on success, 1 otherwise.
   7518  */
   7519 static int
   7520 run_test_refunds (struct TestRefunds_Closure *cls)
   7521 {
   7522   struct TALER_Amount inc;
   7523   uint64_t refund_serial;
   7524 
   7525   /* Insert an instance */
   7526   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
   7527                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7528   /* Insert an account */
   7529   TEST_RET_ON_FAIL (test_insert_account (&cls->instance,
   7530                                          &cls->account,
   7531                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7532   /* Insert an order */
   7533   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
   7534                                        &cls->orders[0],
   7535                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7536   /* Insert contract terms */
   7537   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
   7538                                                 &cls->orders[0],
   7539                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7540   /* Insert a signing key */
   7541   TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey,
   7542                                                   GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7543   /* Insert a deposit */
   7544   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
   7545                                          &cls->signkey,
   7546                                          &cls->deposits[0],
   7547                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7548   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
   7549                                          &cls->signkey,
   7550                                          &cls->deposits[1],
   7551                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7552   /* Mark as paid */
   7553   TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
   7554                                              &cls->orders[0],
   7555                                              GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7556   /* Test refund coin */
   7557   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   7558                          TALER_MERCHANTDB_insert_refund_by_coin (pg,
   7559                                                                  cls->instance.instance.id,
   7560                                                                  &cls->deposits[0].h_contract_terms
   7561                                                                  ,
   7562                                                                  cls->refunds[0].timestamp,
   7563                                                                  cls->refunds[0].coin_pub,
   7564                                                                  cls->refunds[0].reason),
   7565                          "Refund coin failed\n");
   7566   refund_serial = get_refund_serial (&cls->instance,
   7567                                      &cls->deposits[0].h_contract_terms,
   7568                                      &cls->refunds[0]);
   7569   /* Test double refund fails */
   7570   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
   7571                          TALER_MERCHANTDB_insert_refund_by_coin (pg,
   7572                                                                  cls->instance.instance.id,
   7573                                                                  &cls->deposits[0].h_contract_terms
   7574                                                                  ,
   7575                                                                  cls->refunds[0].timestamp,
   7576                                                                  cls->refunds[0].coin_pub,
   7577                                                                  cls->refunds[0].reason),
   7578                          "Refund coin failed\n");
   7579   /* Test lookup refunds */
   7580   TEST_RET_ON_FAIL (test_lookup_refunds (&cls->instance,
   7581                                          &cls->deposits[0].h_contract_terms,
   7582                                          1,
   7583                                          cls->refunds[0].coin_pub,
   7584                                          &cls->refunds[0].refund_amount));
   7585   /* Test lookup refunds detailed */
   7586   TEST_RET_ON_FAIL (test_lookup_refunds_detailed (&cls->instance,
   7587                                                   &cls->deposits[0].
   7588                                                   h_contract_terms,
   7589                                                   true,
   7590                                                   1,
   7591                                                   &cls->refunds[0]));
   7592   /* Test insert refund proof */
   7593   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   7594                          TALER_MERCHANTDB_insert_refund_proof (pg,
   7595                                                                refund_serial,
   7596                                                                &cls->refund_proof.
   7597                                                                exchange_sig,
   7598                                                                &cls->signkey.exchange_pub
   7599                                                                ),
   7600                          "Insert refund proof failed\n");
   7601   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
   7602                          TALER_MERCHANTDB_insert_refund_proof (pg,
   7603                                                                refund_serial,
   7604                                                                &cls->refund_proof.
   7605                                                                exchange_sig,
   7606                                                                &cls->signkey.exchange_pub
   7607                                                                ),
   7608                          "Insert refund proof failed\n");
   7609   /* Test that we can't give too much in refunds */
   7610   GNUNET_assert (GNUNET_OK ==
   7611                  TALER_string_to_amount ("EUR:1000.00",
   7612                                          &inc));
   7613   TEST_COND_RET_ON_FAIL (TALER_MERCHANTDB_RS_TOO_HIGH ==
   7614                          TALER_MERCHANTDB_do_increase_refund (pg,
   7615                                                               cls->instance.instance.id,
   7616                                                               cls->orders[0].id,
   7617                                                               &inc,
   7618                                                               NULL,
   7619                                                               NULL,
   7620                                                               "more"),
   7621                          "Increase refund failed\n");
   7622   /* Test increase refund */
   7623   GNUNET_assert (GNUNET_OK ==
   7624                  TALER_string_to_amount ("EUR:1.00",
   7625                                          &inc));
   7626   TEST_COND_RET_ON_FAIL (TALER_MERCHANTDB_RS_SUCCESS ==
   7627                          TALER_MERCHANTDB_do_increase_refund (pg,
   7628                                                               cls->instance.instance.id,
   7629                                                               cls->orders[0].id,
   7630                                                               &inc,
   7631                                                               NULL,
   7632                                                               NULL,
   7633                                                               "more"),
   7634                          "Increase refund failed\n");
   7635   /* Test lookup refund proof */
   7636   TEST_RET_ON_FAIL (test_lookup_refund_proof (1,
   7637                                               &cls->refund_proof.exchange_sig,
   7638                                               &cls->signkey.exchange_pub));
   7639   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
   7640                                        &cls->orders[1],
   7641                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7642   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
   7643                                                 &cls->orders[1],
   7644                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7645   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
   7646                                          &cls->signkey,
   7647                                          &cls->deposits[2],
   7648                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7649   TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
   7650                                              &cls->orders[1],
   7651                                              GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7652   /* Test refunding a small amount of the coin, then increasing it */
   7653   GNUNET_assert (GNUNET_OK ==
   7654                  TALER_string_to_amount ("EUR:10.00",
   7655                                          &inc));
   7656   TEST_COND_RET_ON_FAIL (TALER_MERCHANTDB_RS_SUCCESS ==
   7657                          TALER_MERCHANTDB_do_increase_refund (pg,
   7658                                                               cls->instance.instance.id,
   7659                                                               cls->orders[1].id,
   7660                                                               &inc,
   7661                                                               NULL,
   7662                                                               NULL,
   7663                                                               cls->refunds[1].reason),
   7664                          "Increase refund failed\n");
   7665   GNUNET_assert (GNUNET_OK ==
   7666                  TALER_string_to_amount ("EUR:20.00",
   7667                                          &inc));
   7668   TEST_COND_RET_ON_FAIL (TALER_MERCHANTDB_RS_SUCCESS ==
   7669                          TALER_MERCHANTDB_do_increase_refund (pg,
   7670                                                               cls->instance.instance.id,
   7671                                                               cls->orders[1].id,
   7672                                                               &inc,
   7673                                                               NULL,
   7674                                                               NULL,
   7675                                                               cls->refunds[2].reason),
   7676                          "Increase refund failed\n");
   7677   TEST_RET_ON_FAIL (test_lookup_refunds_detailed (&cls->instance,
   7678                                                   &cls->deposits[2].
   7679                                                   h_contract_terms,
   7680                                                   false,
   7681                                                   2,
   7682                                                   &cls->refunds[1]));
   7683   /* Two deposits of the same coin on the same order make
   7684      do_increase_refund pick the same rtransaction_id twice, which is
   7685      exactly what a concurrent refund on another connection does.  The
   7686      resulting unique violation must NOT be reported as
   7687      #TALER_MERCHANTDB_RS_NO_SUCH_ORDER (a bogus 404 for an order that
   7688      obviously exists), but as a serialization failure the caller can
   7689      retry. */
   7690   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
   7691                                        &cls->orders[2],
   7692                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7693   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
   7694                                                 &cls->orders[2],
   7695                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7696   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
   7697                                          &cls->signkey,
   7698                                          &cls->deposits[3],
   7699                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7700   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
   7701                                          &cls->signkey,
   7702                                          &cls->deposits[4],
   7703                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7704   TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
   7705                                              &cls->orders[2],
   7706                                              GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7707   /* More than a single deposit is worth, so that the second (duplicate)
   7708      deposit has to be refunded as well. */
   7709   GNUNET_assert (GNUNET_OK ==
   7710                  TALER_string_to_amount ("EUR:60.00",
   7711                                          &inc));
   7712   TEST_COND_RET_ON_FAIL (TALER_MERCHANTDB_RS_SOFT_ERROR ==
   7713                          TALER_MERCHANTDB_do_increase_refund (pg,
   7714                                                               cls->instance.instance.id,
   7715                                                               cls->orders[2].id,
   7716                                                               &inc,
   7717                                                               NULL,
   7718                                                               NULL,
   7719                                                               "duplicate coin"),
   7720                          "Duplicate rtransaction_id not reported as serialization failure\n");
   7721   return 0;
   7722 }
   7723 
   7724 
   7725 /**
   7726  * All logic for testing refunds.
   7727  *
   7728  * @return 0 on success, 1 otherwise.
   7729  */
   7730 static int
   7731 test_refunds (void)
   7732 {
   7733   struct TestRefunds_Closure test_cls;
   7734   int test_result;
   7735 
   7736   pre_test_refunds (&test_cls);
   7737   test_result = run_test_refunds (&test_cls);
   7738   post_test_refunds (&test_cls);
   7739   return test_result;
   7740 }
   7741 
   7742 
   7743 /**
   7744  * Convenience function that reverses an array of orders.
   7745  * @param orders_length length of @e orders.
   7746  * @param orders the array to reverse.
   7747  */
   7748 static void
   7749 reverse_order_data_array (unsigned int orders_length,
   7750                           struct OrderData *orders)
   7751 {
   7752   struct OrderData tmp[orders_length];
   7753   for (unsigned int i = 0; i < orders_length; ++i)
   7754     tmp[i] = orders[orders_length - 1 - i];
   7755   for (unsigned int i = 0; i < orders_length; ++i)
   7756     orders[i] = tmp[i];
   7757 }
   7758 
   7759 
   7760 /**
   7761  * Closure for testing all the filters for looking up orders.
   7762  */
   7763 struct TestLookupOrdersAllFilters_Closure
   7764 {
   7765   /**
   7766    * The instance.
   7767    */
   7768   struct InstanceData instance;
   7769 
   7770   /**
   7771    * The merchant account.
   7772    */
   7773   struct TALER_MERCHANTDB_AccountDetails account;
   7774 
   7775   /**
   7776    * The exchange signing key.
   7777    */
   7778   struct ExchangeSignkeyData signkey;
   7779 
   7780   /**
   7781    * The array of order ids.
   7782    */
   7783   char *order_ids[64];
   7784 
   7785   /**
   7786    * The array of orders.
   7787    */
   7788   struct OrderData orders[64];
   7789 
   7790   /**
   7791    * The array of deposits.
   7792    */
   7793   struct DepositData deposits[64];
   7794 
   7795   /**
   7796    * The array of refunds.
   7797    */
   7798   struct RefundData refunds[64];
   7799 };
   7800 
   7801 
   7802 /**
   7803  * Sets up for testing lookup order filters.
   7804  * @param cls the closure.
   7805  */
   7806 static void
   7807 pre_test_lookup_orders_all_filters (
   7808   struct TestLookupOrdersAllFilters_Closure *cls)
   7809 {
   7810   make_instance ("test_inst_lookup_orders_all_filters",
   7811                  &cls->instance);
   7812   make_account (&cls->account);
   7813   cls->account.instance_id = cls->instance.instance.id;
   7814   make_exchange_signkey (&cls->signkey);
   7815   for (unsigned int i = 0; i < 64; ++i)
   7816   {
   7817     (void) GNUNET_asprintf (&cls->order_ids[i],
   7818                             "test_orders_filter_od_%u",
   7819                             i);
   7820     make_order (cls->order_ids[i],
   7821                 &cls->orders[i]);
   7822     GNUNET_assert (0 ==
   7823                    json_object_set_new (cls->orders[i].contract,
   7824                                         "order_id",
   7825                                         json_string (cls->order_ids[i])));
   7826     make_deposit (&cls->instance,
   7827                   &cls->account,
   7828                   &cls->orders[i],
   7829                   &cls->signkey,
   7830                   &cls->deposits[i]);
   7831     make_refund (&cls->deposits[i],
   7832                  &cls->refunds[i]);
   7833   }
   7834 }
   7835 
   7836 
   7837 /**
   7838  * Cleans up after testing lookup order filters.
   7839  * @param cls the closure.
   7840  */
   7841 static void
   7842 post_test_lookup_orders_all_filters (
   7843   struct TestLookupOrdersAllFilters_Closure *cls)
   7844 {
   7845   free_instance_data (&cls->instance);
   7846   for (unsigned int i = 0; i < 64; ++i)
   7847   {
   7848     free_order_data (&cls->orders[i]);
   7849     GNUNET_free (cls->order_ids[i]);
   7850   }
   7851 }
   7852 
   7853 
   7854 /**
   7855  * Runs the tests for lookup order filters.
   7856  * @param cls the closure.
   7857  *
   7858  * @return 0 on success, 1 otherwise.
   7859  */
   7860 static int
   7861 run_test_lookup_orders_all_filters (
   7862   struct TestLookupOrdersAllFilters_Closure *cls)
   7863 {
   7864   /* Order filter extravaganza */
   7865   struct
   7866   {
   7867     bool claimed;
   7868     bool paid;
   7869     bool refunded;
   7870     bool wired;
   7871   } order_status[64];
   7872   unsigned int *permutation;
   7873 
   7874   /* Pseudorandomly generate variations for the filter to differentiate */
   7875   permutation = GNUNET_CRYPTO_random_permute (64);
   7876   for (unsigned int i = 0; i < 64; ++i)
   7877   {
   7878     unsigned int dest = permutation[i];
   7879     order_status[dest].claimed = (i & 1) ? true : false;
   7880     order_status[dest].paid = (3 == (i & 3)) ? true : false;
   7881     order_status[dest].refunded = (5 == (i & 5)) ? true : false;
   7882     order_status[dest].wired = (9 == (i & 9)) ? true : false;
   7883   }
   7884   GNUNET_free (permutation);
   7885 
   7886 
   7887   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
   7888                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7889   TEST_RET_ON_FAIL (test_insert_account (&cls->instance,
   7890                                          &cls->account,
   7891                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7892   TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey,
   7893                                                   GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7894   for (unsigned int i = 0; i < 64; ++i)
   7895   {
   7896     uint64_t order_serial;
   7897 
   7898     TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
   7899                                          &cls->orders[i],
   7900                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7901     order_serial = get_order_serial (&cls->instance,
   7902                                      &cls->orders[i]);
   7903 
   7904     if (order_status[i].claimed)
   7905     {
   7906       TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
   7907                                                     &cls->orders[i],
   7908                                                     GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7909     }
   7910     else
   7911     {
   7912       continue;
   7913     }
   7914 
   7915     if (order_status[i].paid)
   7916       TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
   7917                                                  &cls->orders[i],
   7918                                                  GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7919     if (order_status[i].refunded)
   7920     {
   7921       TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
   7922                                              &cls->signkey,
   7923                                              &cls->deposits[i],
   7924                                              GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7925       TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   7926                              TALER_MERCHANTDB_insert_refund_by_coin (pg,
   7927                                                                      cls->instance.instance.id,
   7928                                                                      &cls->deposits[i].
   7929                                                                      h_contract_terms,
   7930                                                                      cls->refunds[i].timestamp,
   7931                                                                      cls->refunds[i].coin_pub,
   7932                                                                      cls->refunds[i].reason),
   7933                              "Refund coin failed\n");
   7934     }
   7935 
   7936     if (order_status[i].wired)
   7937       TEST_RET_ON_FAIL (test_mark_order_wired (order_serial,
   7938                                                GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   7939   }
   7940 
   7941   /* There are 3^3 = 27 possibilities here, not counting inc/dec and start row */
   7942   for (unsigned int i = 0; i < 27; ++i)
   7943   {
   7944     struct TALER_MERCHANTDB_OrderFilter filter = {
   7945       .paid = (i % 3) + 1,
   7946       .refunded = ((i / 3) % 3) + 1,
   7947       .wired = ((i / 9) % 3) + 1,
   7948       .expired = TALER_EXCHANGE_YNA_ALL,
   7949       .date = GNUNET_TIME_UNIT_ZERO_TS,
   7950       .start_row = 0,
   7951       .delta = 64
   7952     };
   7953     unsigned int orders_length = 0;
   7954     struct OrderData orders[64];
   7955 
   7956     /* Now figure out which orders should make it through the filter */
   7957     for (unsigned int j = 0; j < 64; ++j)
   7958     {
   7959       if (((TALER_EXCHANGE_YNA_YES == filter.paid) &&
   7960            (true != order_status[j].paid)) ||
   7961           ((TALER_EXCHANGE_YNA_NO == filter.paid) &&
   7962            (false != order_status[j].paid)) ||
   7963           ((TALER_EXCHANGE_YNA_YES == filter.refunded) &&
   7964            (true != order_status[j].refunded)) ||
   7965           ((TALER_EXCHANGE_YNA_NO == filter.refunded) &&
   7966            (false != order_status[j].refunded)) ||
   7967           ((TALER_EXCHANGE_YNA_YES == filter.wired) &&
   7968            (true != order_status[j].wired)) ||
   7969           ((TALER_EXCHANGE_YNA_NO == filter.wired) &&
   7970            (false != order_status[j].wired)))
   7971         continue;
   7972       orders[orders_length] = cls->orders[j];
   7973       orders_length += 1;
   7974     }
   7975 
   7976     /* Test the lookup */
   7977     TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
   7978                                           &filter,
   7979                                           orders_length,
   7980                                           orders));
   7981 
   7982     /* Now test decreasing */
   7983     filter.start_row = 256;
   7984     filter.date = GNUNET_TIME_UNIT_FOREVER_TS;
   7985     filter.delta = -64;
   7986 
   7987     reverse_order_data_array (orders_length,
   7988                               orders);
   7989 
   7990     TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
   7991                                           &filter,
   7992                                           orders_length,
   7993                                           orders));
   7994   }
   7995 
   7996   return 0;
   7997 }
   7998 
   7999 
   8000 /**
   8001  * Handles all logic for testing lookup order filters.
   8002  *
   8003  * @return 0 on success, 1 otherwise.
   8004  */
   8005 static int
   8006 test_lookup_orders_all_filters (void)
   8007 {
   8008   struct TestLookupOrdersAllFilters_Closure test_cls;
   8009   int test_result;
   8010 
   8011   memset (&test_cls,
   8012           0,
   8013           sizeof (test_cls));
   8014   pre_test_lookup_orders_all_filters (&test_cls);
   8015   test_result = run_test_lookup_orders_all_filters (&test_cls);
   8016   post_test_lookup_orders_all_filters (&test_cls);
   8017   return test_result;
   8018 }
   8019 
   8020 
   8021 static void
   8022 kyc_status_ok (
   8023   void *cls,
   8024   const struct TALER_MerchantWireHashP *h_wire,
   8025   struct TALER_FullPayto payto_uri,
   8026   const char *exchange_url,
   8027   struct GNUNET_TIME_Timestamp last_check,
   8028   bool kyc_ok,
   8029   const struct TALER_AccountAccessTokenP *access_token,
   8030   unsigned int last_http_status,
   8031   enum TALER_ErrorCode last_ec,
   8032   bool in_aml_review,
   8033   const json_t *jlimits)
   8034 {
   8035   bool *fail = cls;
   8036 
   8037   if (kyc_ok)
   8038     *fail = false;
   8039 }
   8040 
   8041 
   8042 static void
   8043 kyc_status_fail (
   8044   void *cls,
   8045   const struct TALER_MerchantWireHashP *h_wire,
   8046   struct TALER_FullPayto payto_uri,
   8047   const char *exchange_url,
   8048   struct GNUNET_TIME_Timestamp last_check,
   8049   bool kyc_ok,
   8050   const struct TALER_AccountAccessTokenP *access_token,
   8051   unsigned int last_http_status,
   8052   enum TALER_ErrorCode last_ec,
   8053   bool in_aml_review,
   8054   const json_t *jlimits)
   8055 {
   8056   bool *fail = cls;
   8057 
   8058   if (! kyc_ok)
   8059     *fail = false;
   8060 }
   8061 
   8062 
   8063 /**
   8064  * Function that tests the KYC table.
   8065  *
   8066  * @return 0 on success, 1 otherwise.
   8067  */
   8068 static int
   8069 test_kyc (void)
   8070 {
   8071   struct InstanceData instance;
   8072   struct TALER_MERCHANTDB_AccountDetails account;
   8073   bool fail;
   8074   struct GNUNET_TIME_Timestamp now;
   8075   uint64_t alerts;
   8076 
   8077   make_instance ("test_kyc",
   8078                  &instance);
   8079   make_account (&account);
   8080   account.instance_id = instance.instance.id;
   8081   TEST_RET_ON_FAIL (test_insert_instance (&instance,
   8082                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   8083   TEST_RET_ON_FAIL (test_insert_account (&instance,
   8084                                          &account,
   8085                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   8086   /* Arm the KYC alert path: merchant_send_kyc_notification() returns
   8087      early unless the instance has BOTH an e-mail address and a
   8088      notification language.  No C entry point writes
   8089      'notification_language', hence the direct UPDATE. */
   8090   TEST_RET_ON_FAIL (exec_sql ("UPDATE merchant.merchant_instances"
   8091                               "   SET email='kyc-alerts@example.com'"
   8092                               "      ,notification_language='en'"
   8093                               " WHERE merchant_id='test_kyc'"));
   8094   now = GNUNET_TIME_timestamp_get ();
   8095   TEST_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   8096                     TALER_MERCHANTDB_insert_kyc_status (pg,
   8097                                                         instance.instance.id,
   8098                                                         &account.h_wire,
   8099                                                         "https://exchange.net/",
   8100                                                         now,
   8101                                                         GNUNET_TIME_UNIT_FOREVER_ABS,
   8102                                                         GNUNET_TIME_UNIT_HOURS,
   8103                                                         MHD_HTTP_OK,
   8104                                                         TALER_EC_NONE,
   8105                                                         42,
   8106                                                         NULL,
   8107                                                         NULL,
   8108                                                         false,
   8109                                                         false));
   8110   /* The AFTER INSERT trigger must have queued exactly one alert.
   8111      Regression test: merchant_send_kyc_notification() used to call
   8112      random_bytea()/uri_escape()/base32_crockford() unqualified -- they
   8113      live in schema 'merchant' only, while the search_path holds just the
   8114      per-instance schema -- and to insert a 'merchant_serial' column that
   8115      merchant-0036 dropped.  Either turned this call into a HARD_ERROR,
   8116      which taler-merchant-kyccheck treats as fatal. */
   8117   TEST_RET_ON_FAIL (query_sql_num ("SELECT COUNT(*) AS num"
   8118                                    "  FROM merchant_reports"
   8119                                    " WHERE one_shot_hidden",
   8120                                    &alerts));
   8121   TEST_COND_RET_ON_FAIL (1 == alerts,
   8122                          "KYC status change did not queue an alert report\n");
   8123   TEST_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   8124                     TALER_MERCHANTDB_insert_kyc_status (pg,
   8125                                                         instance.instance.id,
   8126                                                         &account.h_wire,
   8127                                                         "https://exchange2.com/",
   8128                                                         now,
   8129                                                         GNUNET_TIME_UNIT_FOREVER_ABS,
   8130                                                         GNUNET_TIME_UNIT_HOURS,
   8131                                                         MHD_HTTP_OK,
   8132                                                         TALER_EC_NONE,
   8133                                                         42,
   8134                                                         NULL,
   8135                                                         NULL,
   8136                                                         false,
   8137                                                         false));
   8138   TEST_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   8139                     TALER_MERCHANTDB_insert_kyc_status (pg,
   8140                                                         instance.instance.id,
   8141                                                         &account.h_wire,
   8142                                                         "https://exchange.net/",
   8143                                                         now,
   8144                                                         GNUNET_TIME_UNIT_FOREVER_ABS,
   8145                                                         GNUNET_TIME_UNIT_HOURS,
   8146                                                         MHD_HTTP_OK,
   8147                                                         TALER_EC_NONE,
   8148                                                         42,
   8149                                                         NULL,
   8150                                                         NULL,
   8151                                                         false,
   8152                                                         true));
   8153   fail = true;
   8154   TEST_RET_ON_FAIL (1 !=
   8155                     TALER_MERCHANTDB_iterate_kyc_statuses (pg,
   8156                                                            instance.instance.id,
   8157                                                            &account.h_wire,
   8158                                                            "https://exchange.net/",
   8159                                                            &kyc_status_ok,
   8160                                                            &fail));
   8161   TEST_RET_ON_FAIL (fail);
   8162   fail = true;
   8163   TEST_RET_ON_FAIL (1 !=
   8164                     TALER_MERCHANTDB_iterate_kyc_statuses (pg,
   8165                                                            instance.instance.id,
   8166                                                            NULL,
   8167                                                            "https://exchange2.com/",
   8168                                                            &kyc_status_fail,
   8169                                                            &fail));
   8170   TEST_RET_ON_FAIL (fail);
   8171   fail = true;
   8172   TEST_RET_ON_FAIL (2 !=
   8173                     TALER_MERCHANTDB_iterate_kyc_statuses (pg,
   8174                                                            instance.instance.id,
   8175                                                            NULL,
   8176                                                            NULL,
   8177                                                            &kyc_status_fail,
   8178                                                            &fail));
   8179   TEST_RET_ON_FAIL (fail);
   8180   fail = true;
   8181   TEST_RET_ON_FAIL (2 !=
   8182                     TALER_MERCHANTDB_iterate_kyc_statuses (pg,
   8183                                                            instance.instance.id,
   8184                                                            NULL,
   8185                                                            NULL,
   8186                                                            &kyc_status_ok,
   8187                                                            &fail));
   8188   TEST_RET_ON_FAIL (fail);
   8189   /* A bookkeeping-only re-poll -- kyc_ok, last_rule_gen, aml_review and
   8190      jaccount_limits are unchanged, only the timestamps move -- must NOT
   8191      queue another alert.  Regression test: pg_triggers.sql used to
   8192      install a second, unguarded merchant_kyc_update_trigger() on top of
   8193      the guarded one in pg_merchant_kyc_trigger.sql, and being loaded
   8194      last it won, so every next_kyc_poll update alerted the merchant. */
   8195   TEST_RET_ON_FAIL (query_sql_num ("SELECT COUNT(*) AS num"
   8196                                    "  FROM merchant_reports"
   8197                                    " WHERE report_description"
   8198                                    "       ='automatically triggered KYC alert'",
   8199                                    &alerts));
   8200   TEST_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
   8201                     TALER_MERCHANTDB_insert_kyc_status (pg,
   8202                                                         instance.instance.id,
   8203                                                         &account.h_wire,
   8204                                                         "https://exchange.net/",
   8205                                                         GNUNET_TIME_timestamp_get (),
   8206                                                         GNUNET_TIME_UNIT_FOREVER_ABS,
   8207                                                         GNUNET_TIME_UNIT_MINUTES,
   8208                                                         MHD_HTTP_OK,
   8209                                                         TALER_EC_NONE,
   8210                                                         42,
   8211                                                         NULL,
   8212                                                         NULL,
   8213                                                         false,
   8214                                                         true));
   8215   {
   8216     uint64_t alerts2;
   8217 
   8218     TEST_RET_ON_FAIL (query_sql_num ("SELECT COUNT(*) AS num"
   8219                                      "  FROM merchant_reports"
   8220                                      " WHERE report_description"
   8221                                      "       ='automatically triggered KYC alert'",
   8222                                      &alerts2));
   8223     TEST_COND_RET_ON_FAIL (alerts == alerts2,
   8224                            "Bookkeeping-only KYC update queued an alert\n");
   8225   }
   8226   /* Adding an account must queue an account-change alert.  Regression
   8227      test: pg_merchant_account_trigger.sql used to (re)define
   8228      'merchant_kyc_update_trigger' rather than
   8229      'merchant_account_change_trigger', so merchant_send_account_notification()
   8230      had no callers at all and the trigger installed by merchant-0041 was
   8231      still its 'RETURN NULL' stub. */
   8232   {
   8233     struct TALER_MERCHANTDB_AccountDetails account2;
   8234     uint64_t acc_alerts;
   8235 
   8236     make_account (&account2);
   8237     account2.instance_id = instance.instance.id;
   8238     /* make_account() hands out a fixed payto_uri, which would collide. */
   8239     account2.payto_uri.full_payto
   8240       = (char *) "payto://x-taler-bank/bank.demo.taler.net/5";
   8241     TEST_RET_ON_FAIL (test_insert_account (&instance,
   8242                                            &account2,
   8243                                            GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   8244     TEST_RET_ON_FAIL (query_sql_num ("SELECT COUNT(*) AS num"
   8245                                      "  FROM merchant_reports"
   8246                                      " WHERE report_description"
   8247                                      "       ='automatically triggered account"
   8248                                      " change alert'",
   8249                                      &acc_alerts));
   8250     TEST_COND_RET_ON_FAIL (1 == acc_alerts,
   8251                            "Adding an account did not queue an alert\n");
   8252   }
   8253   json_decref (instance.instance.address);
   8254   json_decref (instance.instance.jurisdiction);
   8255   return 0;
   8256 }
   8257 
   8258 
   8259 /* *********** Templates ********** */
   8260 
   8261 /**
   8262  * A container for data relevant to a template.
   8263  */
   8264 struct TemplateData
   8265 {
   8266   /**
   8267    * The identifier of the template.
   8268    */
   8269   const char *id;
   8270 
   8271   /**
   8272    * The details of the template.
   8273    */
   8274   struct TALER_MERCHANTDB_TemplateDetails template;
   8275 };
   8276 
   8277 
   8278 /**
   8279  * Creates a template for testing with.
   8280  *
   8281  * @param id the id of the template.
   8282  * @param template the template data to fill.
   8283  */
   8284 static void
   8285 make_template (const char *id,
   8286                struct TemplateData *template)
   8287 {
   8288   template->id = id;
   8289   template->template.template_description = "This is a test template";
   8290   template->template.otp_id = NULL;
   8291   template->template.template_contract = json_array ();
   8292   GNUNET_assert (NULL != template->template.template_contract);
   8293 }
   8294 
   8295 
   8296 /**
   8297  * Frees memory associated with @e TemplateData.
   8298  *
   8299  * @param template the container to free.
   8300  */
   8301 static void
   8302 free_template_data (struct TemplateData *template)
   8303 {
   8304   GNUNET_free (template->template.otp_id);
   8305   json_decref (template->template.template_contract);
   8306 }
   8307 
   8308 
   8309 /**
   8310  * Compare two templates for equality.
   8311  *
   8312  * @param a the first template.
   8313  * @param b the second template.
   8314  * @return 0 on equality, 1 otherwise.
   8315  */
   8316 static int
   8317 check_templates_equal (const struct TALER_MERCHANTDB_TemplateDetails *a,
   8318                        const struct TALER_MERCHANTDB_TemplateDetails *b)
   8319 {
   8320   if ((0 != strcmp (a->template_description,
   8321                     b->template_description)) ||
   8322       ( (NULL == a->otp_id) && (NULL != b->otp_id)) ||
   8323       ( (NULL != a->otp_id) && (NULL == b->otp_id)) ||
   8324       ( (NULL != a->otp_id) && (0 != strcmp (a->otp_id,
   8325                                              b->otp_id))) ||
   8326       (1 != json_equal (a->template_contract,
   8327                         b->template_contract)))
   8328     return 1;
   8329   return 0;
   8330 }
   8331 
   8332 
   8333 /**
   8334  * Tests inserting template data into the database.
   8335  *
   8336  * @param instance the instance to insert the template for.
   8337  * @param template the template data to insert.
   8338  * @param expected_result the result we expect the db to return.
   8339  * @return 0 when successful, 1 otherwise.
   8340  */
   8341 static int
   8342 test_insert_template (const struct InstanceData *instance,
   8343                       const struct TemplateData *template,
   8344                       enum GNUNET_DB_QueryStatus expected_result)
   8345 {
   8346   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   8347   TEST_COND_RET_ON_FAIL (expected_result ==
   8348                          TALER_MERCHANTDB_insert_template (pg,
   8349                                                            instance->instance.id,
   8350                                                            template->id,
   8351                                                            0,
   8352                                                            &template->template),
   8353                          "Insert template failed\n");
   8354   return 0;
   8355 }
   8356 
   8357 
   8358 /**
   8359  * Tests updating template data in the database.
   8360  *
   8361  * @param instance the instance to update the template for.
   8362  * @param template the template data to update.
   8363  * @param expected_result the result we expect the db to return.
   8364  * @return 0 when successful, 1 otherwise.
   8365  */
   8366 static int
   8367 test_update_template (const struct InstanceData *instance,
   8368                       const struct TemplateData *template,
   8369                       enum GNUNET_DB_QueryStatus expected_result)
   8370 {
   8371   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   8372   TEST_COND_RET_ON_FAIL (expected_result ==
   8373                          TALER_MERCHANTDB_update_template (pg,
   8374                                                            instance->instance.id,
   8375                                                            template->id,
   8376                                                            &template->template),
   8377                          "Update template failed\n");
   8378   return 0;
   8379 }
   8380 
   8381 
   8382 /**
   8383  * Tests looking up a template from the db.
   8384  *
   8385  * @param instance the instance to query from.
   8386  * @param template the template to query and compare to.
   8387  * @return 0 when successful, 1 otherwise.
   8388  */
   8389 static int
   8390 test_lookup_template (const struct InstanceData *instance,
   8391                       const struct TemplateData *template)
   8392 {
   8393   const struct TALER_MERCHANTDB_TemplateDetails *to_cmp
   8394     = &template->template;
   8395   struct TALER_MERCHANTDB_TemplateDetails lookup_result;
   8396 
   8397   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   8398   if (0 > TALER_MERCHANTDB_get_template (pg,
   8399                                          instance->instance.id,
   8400                                          template->id,
   8401                                          &lookup_result))
   8402   {
   8403     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   8404                 "Lookup template failed\n");
   8405     TALER_MERCHANTDB_template_details_free (&lookup_result);
   8406     return 1;
   8407   }
   8408   if (0 != check_templates_equal (&lookup_result,
   8409                                   to_cmp))
   8410   {
   8411     GNUNET_break (0);
   8412     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   8413                 "Lookup template failed: incorrect template returned\n");
   8414     TALER_MERCHANTDB_template_details_free (&lookup_result);
   8415     return 1;
   8416   }
   8417   TALER_MERCHANTDB_template_details_free (&lookup_result);
   8418   return 0;
   8419 }
   8420 
   8421 
   8422 /**
   8423  * Closure for testing template lookup
   8424  */
   8425 struct TestLookupTemplates_Closure
   8426 {
   8427   /**
   8428    * Number of template ids to compare to
   8429    */
   8430   unsigned int templates_to_cmp_length;
   8431 
   8432   /**
   8433    * Pointer to array of template ids
   8434    */
   8435   const struct TemplateData *templates_to_cmp;
   8436 
   8437   /**
   8438    * Pointer to array of number of matches for each template
   8439    */
   8440   unsigned int *results_matching;
   8441 
   8442   /**
   8443    * Total number of results returned
   8444    */
   8445   unsigned int results_length;
   8446 };
   8447 
   8448 
   8449 /**
   8450  * Function called after calling @e test_lookup_templates
   8451  *
   8452  * @param cls a pointer to the lookup closure.
   8453  * @param template_id the identifier of the template found.
   8454  */
   8455 static void
   8456 lookup_templates_cb (void *cls,
   8457                      const char *template_id,
   8458                      const char *template_description)
   8459 {
   8460   struct TestLookupTemplates_Closure *cmp = cls;
   8461 
   8462   if (NULL == cmp)
   8463     return;
   8464   cmp->results_length += 1;
   8465   for (unsigned int i = 0; cmp->templates_to_cmp_length > i; ++i)
   8466   {
   8467     if ( (0 == strcmp (cmp->templates_to_cmp[i].id,
   8468                        template_id)) &&
   8469          (0 == strcmp (cmp->templates_to_cmp[i].template.template_description,
   8470                        template_description)) )
   8471       cmp->results_matching[i] += 1;
   8472   }
   8473 }
   8474 
   8475 
   8476 /**
   8477  * Tests looking up all templates for an instance.
   8478  *
   8479  * @param instance the instance to query from.
   8480  * @param templates_length the number of templates we are expecting.
   8481  * @param templates the list of templates that we expect to be found.
   8482  * @return 0 when successful, 1 otherwise.
   8483  */
   8484 static int
   8485 test_lookup_templates (const struct InstanceData *instance,
   8486                        unsigned int templates_length,
   8487                        const struct TemplateData *templates)
   8488 {
   8489   unsigned int results_matching[templates_length];
   8490   struct TestLookupTemplates_Closure cls = {
   8491     .templates_to_cmp_length = templates_length,
   8492     .templates_to_cmp = templates,
   8493     .results_matching = results_matching,
   8494     .results_length = 0
   8495   };
   8496 
   8497   memset (results_matching,
   8498           0,
   8499           sizeof (unsigned int) * templates_length);
   8500   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   8501   if (0 > TALER_MERCHANTDB_iterate_templates (pg,
   8502                                               instance->instance.id,
   8503                                               &lookup_templates_cb,
   8504                                               &cls))
   8505   {
   8506     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   8507                 "Lookup templates failed\n");
   8508     return 1;
   8509   }
   8510   if (templates_length != cls.results_length)
   8511   {
   8512     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   8513                 "Lookup templates failed: incorrect number of results\n");
   8514     return 1;
   8515   }
   8516   for (unsigned int i = 0; templates_length > i; ++i)
   8517   {
   8518     if (1 != cls.results_matching[i])
   8519     {
   8520       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   8521                   "Lookup templates failed: mismatched data\n");
   8522       return 1;
   8523     }
   8524   }
   8525   return 0;
   8526 }
   8527 
   8528 
   8529 /**
   8530  * Tests deleting a template.
   8531  *
   8532  * @param instance the instance to delete the template from.
   8533  * @param template the template that should be deleted.
   8534  * @param expected_result the result that we expect the DB to return.
   8535  * @return 0 when successful, 1 otherwise.
   8536  */
   8537 static int
   8538 test_delete_template (const struct InstanceData *instance,
   8539                       const struct TemplateData *template,
   8540                       enum GNUNET_DB_QueryStatus expected_result)
   8541 {
   8542   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   8543   TEST_COND_RET_ON_FAIL (expected_result ==
   8544                          TALER_MERCHANTDB_delete_template (pg,
   8545                                                            instance->instance.id,
   8546                                                            template->id),
   8547                          "Delete template failed\n");
   8548   return 0;
   8549 }
   8550 
   8551 
   8552 /**
   8553  * Closure for template tests.
   8554  */
   8555 struct TestTemplates_Closure
   8556 {
   8557   /**
   8558    * The instance to use for this test.
   8559    */
   8560   struct InstanceData instance;
   8561 
   8562   /**
   8563    * The array of templates.
   8564    */
   8565   struct TemplateData templates[2];
   8566 };
   8567 
   8568 
   8569 /**
   8570  * Sets up the data structures used in the template tests.
   8571  *
   8572  * @param cls the closure to fill with test data.
   8573  */
   8574 static void
   8575 pre_test_templates (struct TestTemplates_Closure *cls)
   8576 {
   8577   /* Instance */
   8578   make_instance ("test_inst_templates",
   8579                  &cls->instance);
   8580 
   8581   /* Templates */
   8582   make_template ("test_templates_pd_0",
   8583                  &cls->templates[0]);
   8584 
   8585   make_template ("test_templates_pd_1",
   8586                  &cls->templates[1]);
   8587   cls->templates[1].template.template_description =
   8588     "This is a another test template";
   8589 }
   8590 
   8591 
   8592 /**
   8593  * Handles all teardown after testing.
   8594  *
   8595  * @param cls the closure containing memory to be freed.
   8596  */
   8597 static void
   8598 post_test_templates (struct TestTemplates_Closure *cls)
   8599 {
   8600   free_instance_data (&cls->instance);
   8601   free_template_data (&cls->templates[0]);
   8602   free_template_data (&cls->templates[1]);
   8603 }
   8604 
   8605 
   8606 /**
   8607  * Runs the tests for templates.
   8608  *
   8609  * @param cls the container of the test data.
   8610  * @return 0 on success, 1 otherwise.
   8611  */
   8612 static int
   8613 run_test_templates (struct TestTemplates_Closure *cls)
   8614 {
   8615 
   8616   /* Test that insert without an instance fails */
   8617   TEST_RET_ON_FAIL (test_insert_template (&cls->instance,
   8618                                           &cls->templates[0],
   8619                                           GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   8620   /* Insert the instance */
   8621   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
   8622                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   8623   /* Test inserting a template */
   8624   TEST_RET_ON_FAIL (test_insert_template (&cls->instance,
   8625                                           &cls->templates[0],
   8626                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   8627   /* Test that double insert fails */
   8628   TEST_RET_ON_FAIL (test_insert_template (&cls->instance,
   8629                                           &cls->templates[0],
   8630                                           GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   8631   /* Test lookup of individual templates */
   8632   TEST_RET_ON_FAIL (test_lookup_template (&cls->instance,
   8633                                           &cls->templates[0]));
   8634   /* Make sure it fails correctly for templates that don't exist */
   8635   {
   8636     struct TALER_MERCHANTDB_TemplateDetails tx;
   8637 
   8638     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
   8639         TALER_MERCHANTDB_get_template (pg,
   8640                                        cls->instance.instance.id,
   8641                                        "nonexistent_template",
   8642                                        &tx))
   8643     {
   8644       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   8645                   "Lookup template failed\n");
   8646       return 1;
   8647     }
   8648   }
   8649   /* Test template update */
   8650   cls->templates[0].template.template_description =
   8651     "This is a test template that has been updated!";
   8652   GNUNET_free (cls->templates[0].template.otp_id);
   8653   cls->templates[0].template.otp_id = GNUNET_strdup ("otp_id");
   8654   {
   8655     /* ensure OTP device exists */
   8656     struct TALER_MERCHANTDB_OtpDeviceDetails td = {
   8657       .otp_description = "my otp",
   8658       .otp_key = "my key",
   8659       .otp_algorithm = 1,
   8660       .otp_ctr = 42
   8661     };
   8662     GNUNET_assert (0 <=
   8663                    TALER_MERCHANTDB_insert_otp_device (pg,
   8664                                                        cls->instance.instance.id,
   8665                                                        "otp_id",
   8666                                                        &td));
   8667   }
   8668   GNUNET_assert (0 ==
   8669                  json_array_append_new (
   8670                    cls->templates[0].template.template_contract,
   8671                    json_string ("This is a test. 3CH.")));
   8672   TEST_RET_ON_FAIL (test_update_template (&cls->instance,
   8673                                           &cls->templates[0],
   8674                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   8675   TEST_RET_ON_FAIL (test_lookup_template (&cls->instance,
   8676                                           &cls->templates[0]));
   8677   TEST_RET_ON_FAIL (test_update_template (&cls->instance,
   8678                                           &cls->templates[1],
   8679                                           GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   8680   /* Test collective template lookup */
   8681   TEST_RET_ON_FAIL (test_insert_template (&cls->instance,
   8682                                           &cls->templates[1],
   8683                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   8684   TEST_RET_ON_FAIL (test_lookup_templates (&cls->instance,
   8685                                            2,
   8686                                            cls->templates));
   8687 
   8688   /* Test template deletion */
   8689   TEST_RET_ON_FAIL (test_delete_template (&cls->instance,
   8690                                           &cls->templates[1],
   8691                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   8692   /* Test double deletion fails */
   8693   TEST_RET_ON_FAIL (test_delete_template (&cls->instance,
   8694                                           &cls->templates[1],
   8695                                           GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   8696   TEST_RET_ON_FAIL (test_lookup_templates (&cls->instance,
   8697                                            1,
   8698                                            cls->templates));
   8699   return 0;
   8700 }
   8701 
   8702 
   8703 /**
   8704  * Takes care of template testing.
   8705  *
   8706  * @return 0 on success, 1 otherwise.
   8707  */
   8708 static int
   8709 test_templates (void)
   8710 {
   8711   struct TestTemplates_Closure test_cls;
   8712   int test_result;
   8713 
   8714   memset (&test_cls,
   8715           0,
   8716           sizeof (test_cls));
   8717   pre_test_templates (&test_cls);
   8718   test_result = run_test_templates (&test_cls);
   8719   post_test_templates (&test_cls);
   8720   return test_result;
   8721 }
   8722 
   8723 
   8724 /* *********** Webhooks ********** */
   8725 
   8726 /**
   8727  * A container for data relevant to a webhook.
   8728  */
   8729 struct WebhookData
   8730 {
   8731 
   8732   /**
   8733    * The identifier of the webhook.
   8734    */
   8735   const char *id;
   8736 
   8737   /**
   8738    * The details of the webhook.
   8739    */
   8740   struct TALER_MERCHANTDB_WebhookDetails webhook;
   8741 };
   8742 
   8743 
   8744 /**
   8745  * Creates a webhook for testing with.
   8746  *
   8747  * @param id the id of the webhook.
   8748  * @param webhook the webhook data to fill.
   8749  */
   8750 static void
   8751 make_webhook (const char *id,
   8752               struct WebhookData *webhook)
   8753 {
   8754   webhook->id = id;
   8755   webhook->webhook.event_type = "Paid";
   8756   webhook->webhook.url = "https://exampletest.com";
   8757   webhook->webhook.http_method = "POST";
   8758   webhook->webhook.header_template = "Authorization:XYJAORKJEO";
   8759   webhook->webhook.body_template = "$Amount";
   8760 }
   8761 
   8762 
   8763 /**
   8764  * Compare two webhooks for equality.
   8765  *
   8766  * @param a the first webhook.
   8767  * @param b the second webhook.
   8768  * @return 0 on equality, 1 otherwise.
   8769  */
   8770 static int
   8771 check_webhooks_equal (const struct TALER_MERCHANTDB_WebhookDetails *a,
   8772                       const struct TALER_MERCHANTDB_WebhookDetails *b)
   8773 {
   8774   if ((0 != strcmp (a->event_type,
   8775                     b->event_type)) ||
   8776       (0 != strcmp (a->url,
   8777                     b->url)) ||
   8778       (0 != strcmp (a->http_method,
   8779                     b->http_method)) ||
   8780       (0 != strcmp (a->header_template,
   8781                     b->header_template)) ||
   8782       (0 != strcmp (a->body_template,
   8783                     b->body_template)))
   8784     return 1;
   8785   return 0;
   8786 }
   8787 
   8788 
   8789 /**
   8790  * Tests inserting webhook data into the database.
   8791  *
   8792  * @param instance the instance to insert the webhook for.
   8793  * @param webhook the webhook data to insert.
   8794  * @param expected_result the result we expect the db to return.
   8795  * @return 0 when successful, 1 otherwise.
   8796  */
   8797 static int
   8798 test_insert_webhook (const struct InstanceData *instance,
   8799                      const struct WebhookData *webhook,
   8800                      enum GNUNET_DB_QueryStatus expected_result)
   8801 {
   8802   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   8803   TEST_COND_RET_ON_FAIL (expected_result ==
   8804                          TALER_MERCHANTDB_insert_webhook (pg,
   8805                                                           instance->instance.id,
   8806                                                           webhook->id,
   8807                                                           &webhook->webhook),
   8808                          "Insert webhook failed\n");
   8809   return 0;
   8810 }
   8811 
   8812 
   8813 /**
   8814  * Tests updating webhook data in the database.
   8815  *
   8816  * @param instance the instance to update the webhook for.
   8817  * @param webhook the webhook data to update.
   8818  * @param expected_result the result we expect the db to return.
   8819  * @return 0 when successful, 1 otherwise.
   8820  */
   8821 static int
   8822 test_update_webhook (const struct InstanceData *instance,
   8823                      const struct WebhookData *webhook,
   8824                      enum GNUNET_DB_QueryStatus expected_result)
   8825 {
   8826   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   8827   TEST_COND_RET_ON_FAIL (expected_result ==
   8828                          TALER_MERCHANTDB_update_webhook (pg,
   8829                                                           instance->instance.id,
   8830                                                           webhook->id,
   8831                                                           &webhook->webhook),
   8832                          "Update webhook failed\n");
   8833   return 0;
   8834 }
   8835 
   8836 
   8837 /**
   8838  * Tests looking up a webhook from the db.
   8839  *
   8840  * @param instance the instance to query from.
   8841  * @param webhook the webhook to query and compare to.
   8842  * @return 0 when successful, 1 otherwise.
   8843  */
   8844 static int
   8845 test_lookup_webhook (const struct InstanceData *instance,
   8846                      const struct WebhookData *webhook)
   8847 {
   8848   const struct TALER_MERCHANTDB_WebhookDetails *to_cmp = &webhook->webhook;
   8849   struct TALER_MERCHANTDB_WebhookDetails lookup_result;
   8850 
   8851   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   8852   if (0 > TALER_MERCHANTDB_get_webhook (pg,
   8853                                         instance->instance.id,
   8854                                         webhook->id,
   8855                                         &lookup_result))
   8856   {
   8857     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   8858                 "Lookup webhook failed\n");
   8859     TALER_MERCHANTDB_webhook_details_free (&lookup_result);
   8860     return 1;
   8861   }
   8862   if (0 != check_webhooks_equal (&lookup_result,
   8863                                  to_cmp))
   8864   {
   8865     GNUNET_break (0);
   8866     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   8867                 "Lookup webhook failed: incorrect webhook returned\n");
   8868     TALER_MERCHANTDB_webhook_details_free (&lookup_result);
   8869     return 1;
   8870   }
   8871   TALER_MERCHANTDB_webhook_details_free (&lookup_result);
   8872   return 0;
   8873 }
   8874 
   8875 
   8876 /**
   8877  * Closure for testing webhook lookup
   8878  */
   8879 struct TestLookupWebhooks_Closure
   8880 {
   8881   /**
   8882    * Number of webhook ids to compare to
   8883    */
   8884   unsigned int webhooks_to_cmp_length;
   8885 
   8886   /**
   8887    * Pointer to array of webhook ids
   8888    */
   8889   const struct WebhookData *webhooks_to_cmp;
   8890 
   8891   /**
   8892    * Pointer to array of number of matches for each webhook
   8893    */
   8894   unsigned int *results_matching;
   8895 
   8896   /**
   8897    * Total number of results returned
   8898    */
   8899   unsigned int results_length;
   8900 };
   8901 
   8902 
   8903 /**
   8904  * Function called after calling @e test_lookup_webhooks
   8905  *
   8906  * @param cls a pointer to the lookup closure.
   8907  * @param webhook_id the identifier of the webhook found.
   8908  */
   8909 static void
   8910 lookup_webhooks_cb (void *cls,
   8911                     const char *webhook_id,
   8912                     const char *event_type)
   8913 {
   8914   struct TestLookupWebhooks_Closure *cmp = cls;
   8915   if (NULL == cmp)
   8916     return;
   8917   cmp->results_length += 1;
   8918   for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i)
   8919   {
   8920     if ((0 == strcmp (cmp->webhooks_to_cmp[i].id,
   8921                       webhook_id)) &&
   8922         (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.event_type,
   8923                       event_type)) )
   8924       cmp->results_matching[i] += 1;
   8925   }
   8926 }
   8927 
   8928 
   8929 /**
   8930  * Tests looking up all webhooks for an instance.
   8931  *
   8932  * @param instance the instance to query from.
   8933  * @param webhooks_length the number of webhooks we are expecting.
   8934  * @param webhooks the list of webhooks that we expect to be found.
   8935  * @return 0 when successful, 1 otherwise.
   8936  */
   8937 static int
   8938 test_lookup_webhooks (const struct InstanceData *instance,
   8939                       unsigned int webhooks_length,
   8940                       const struct WebhookData *webhooks)
   8941 {
   8942   unsigned int results_matching[webhooks_length];
   8943   struct TestLookupWebhooks_Closure cls = {
   8944     .webhooks_to_cmp_length = webhooks_length,
   8945     .webhooks_to_cmp = webhooks,
   8946     .results_matching = results_matching,
   8947     .results_length = 0
   8948   };
   8949   memset (results_matching, 0, sizeof (unsigned int) * webhooks_length);
   8950   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   8951   if (0 > TALER_MERCHANTDB_iterate_webhooks (pg,
   8952                                              instance->instance.id,
   8953                                              &lookup_webhooks_cb,
   8954                                              &cls))
   8955   {
   8956     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   8957                 "Lookup webhooks failed\n");
   8958     return 1;
   8959   }
   8960   if (webhooks_length != cls.results_length)
   8961   {
   8962     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   8963                 "Lookup webhooks failed: incorrect number of results\n");
   8964     return 1;
   8965   }
   8966   for (unsigned int i = 0; webhooks_length > i; ++i)
   8967   {
   8968     if (1 != cls.results_matching[i])
   8969     {
   8970       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   8971                   "Lookup webhooks failed: mismatched data\n");
   8972       return 1;
   8973     }
   8974   }
   8975   return 0;
   8976 }
   8977 
   8978 
   8979 /**
   8980  * Function called after calling @e test_lookup_webhooks
   8981  *
   8982  * @param cls a pointer to the lookup closure.
   8983  * @param webhook_id the identifier of the webhook found.
   8984  */
   8985 static void
   8986 lookup_webhook_by_event_cb (void *cls,
   8987                             uint64_t webhook_serial,
   8988                             const char *event_type,
   8989                             const char *url,
   8990                             const char *http_method,
   8991                             const char *header_template,
   8992                             const char *body_template)
   8993 {
   8994   struct TestLookupWebhooks_Closure *cmp = cls;
   8995   if (NULL == cmp)
   8996     return;
   8997   cmp->results_length += 1;
   8998   for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i)
   8999   {
   9000     if ((0 == strcmp (cmp->webhooks_to_cmp[i].webhook.event_type,
   9001                       event_type)) &&
   9002         (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.url,
   9003                       url)) &&
   9004         (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.http_method,
   9005                       http_method)) &&
   9006         (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.header_template,
   9007                       header_template)) &&
   9008         (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.body_template,
   9009                       body_template)) )
   9010       cmp->results_matching[i] += 1;
   9011   }
   9012 }
   9013 
   9014 
   9015 /**
   9016  * Tests looking up webhooks by event for an instance.
   9017  *
   9018  * @param instance the instance to query from.
   9019  * @param webhooks_length the number of webhooks we are expecting.
   9020  * @param webhooks the list of webhooks that we expect to be found.
   9021  * @return 0 when successful, 1 otherwise.
   9022  */
   9023 static int
   9024 test_lookup_webhook_by_event (const struct InstanceData *instance,
   9025                               unsigned int webhooks_length,
   9026                               const struct WebhookData *webhooks)
   9027 {
   9028   unsigned int results_matching[webhooks_length];
   9029   struct TestLookupWebhooks_Closure cls = {
   9030     .webhooks_to_cmp_length = webhooks_length,
   9031     .webhooks_to_cmp = webhooks,
   9032     .results_matching = results_matching,
   9033     .results_length = 0
   9034   };
   9035   memset (results_matching, 0, sizeof (unsigned int) * webhooks_length);
   9036   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   9037   if (0 > TALER_MERCHANTDB_iterate_webhooks_by_event (pg,
   9038                                                       instance->instance.id,
   9039                                                       webhooks->webhook.event_type,
   9040                                                       &lookup_webhook_by_event_cb,
   9041                                                       &cls))
   9042   {
   9043     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   9044                 "Lookup webhooks by event failed\n");
   9045     return 1;
   9046   }
   9047 
   9048   if (webhooks_length != cls.results_length)
   9049   {
   9050     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   9051                 "Lookup webhooks by event failed: incorrect number of results\n");
   9052     return 1;
   9053   }
   9054   for (unsigned int i = 0; webhooks_length > i; ++i)
   9055   {
   9056     if (1 != cls.results_matching[i])
   9057     {
   9058       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   9059                   "Lookup webhooks by event failed: mismatched data\n");
   9060       return 1;
   9061     }
   9062   }
   9063   return 0;
   9064 }
   9065 
   9066 
   9067 /**
   9068  * Tests deleting a webhook.
   9069  *
   9070  * @param instance the instance to delete the webhook from.
   9071  * @param webhook the webhook that should be deleted.
   9072  * @param expected_result the result that we expect the DB to return.
   9073  * @return 0 when successful, 1 otherwise.
   9074  */
   9075 static int
   9076 test_delete_webhook (const struct InstanceData *instance,
   9077                      const struct WebhookData *webhook,
   9078                      enum GNUNET_DB_QueryStatus expected_result)
   9079 {
   9080   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   9081   TEST_COND_RET_ON_FAIL (expected_result ==
   9082                          TALER_MERCHANTDB_delete_webhook (pg,
   9083                                                           instance->instance.id,
   9084                                                           webhook->id),
   9085                          "Delete webhook failed\n");
   9086   return 0;
   9087 }
   9088 
   9089 
   9090 /**
   9091  * Tests that deleting a product queues an 'inventory_deleted' webhook
   9092  * whose placeholders were resolved from the row that went away.
   9093  *
   9094  * Regression test: handle_inventory_changes() set both substitution
   9095  * flags to FALSE for TG_OP='DELETE', so the pending webhook shipped
   9096  * body_template verbatim -- not even {{webhook_type}} was replaced.
   9097  *
   9098  * Note: this runs on its own instance and *after* the pending webhook
   9099  * tests, which rely on the global merchant_pending_webhooks identity
   9100  * sequence not having been advanced by anybody else.
   9101  *
   9102  * @return 0 when successful, 1 otherwise.
   9103  */
   9104 static int
   9105 test_inventory_deleted_webhook (void)
   9106 {
   9107   struct InstanceData instance;
   9108   struct WebhookData webhook;
   9109   struct ProductData product;
   9110   char *body = NULL;
   9111   int ret = 1;
   9112 
   9113   make_instance ("test_inst_inventory_webhooks",
   9114                  &instance);
   9115   TEST_WITH_FAIL_CLAUSE (test_insert_instance (&instance,
   9116                                                GNUNET_DB_STATUS_SUCCESS_ONE_RESULT),
   9117                          free_instance_data (&instance);
   9118                          return 1;
   9119                          );
   9120   make_webhook ("test_webhooks_wb_inventory_deleted",
   9121                 &webhook);
   9122   webhook.webhook.event_type = "inventory_deleted";
   9123   webhook.webhook.url = "https://example.com/inventory-deleted";
   9124   webhook.webhook.body_template =
   9125     "{\"type\":\"{{webhook_type}}\""
   9126     ",\"product_id\":\"{{product_id}}\""
   9127     ",\"description\":\"{{description}}\"}";
   9128   TEST_WITH_FAIL_CLAUSE (test_insert_webhook (&instance,
   9129                                               &webhook,
   9130                                               GNUNET_DB_STATUS_SUCCESS_ONE_RESULT),
   9131                          free_instance_data (&instance);
   9132                          return 1;
   9133                          );
   9134   make_product ("test_webhooks_deleted_product",
   9135                 &product);
   9136   TEST_WITH_FAIL_CLAUSE (test_insert_product (&instance,
   9137                                               &product,
   9138                                               0,
   9139                                               NULL,
   9140                                               GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
   9141                                               false,
   9142                                               false,
   9143                                               -1),
   9144                          goto cleanup;
   9145                          );
   9146   TEST_WITH_FAIL_CLAUSE (test_delete_product (&instance,
   9147                                               &product,
   9148                                               false,
   9149                                               false,
   9150                                               false),
   9151                          goto cleanup;
   9152                          );
   9153   TEST_WITH_FAIL_CLAUSE (
   9154     query_sql_text ("SELECT body AS txt"
   9155                     "  FROM merchant.merchant_pending_webhooks"
   9156                     " WHERE url='https://example.com/inventory-deleted'",
   9157                     &body),
   9158     goto cleanup;
   9159     );
   9160   if (0 != strcmp ("{\"type\":\"inventory_deleted\""
   9161                    ",\"product_id\":\"test_webhooks_deleted_product\""
   9162                    ",\"description\":\"This is a test product\"}",
   9163                    body))
   9164   {
   9165     GNUNET_break (0);
   9166     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   9167                 "inventory_deleted webhook body not resolved: `%s'\n",
   9168                 body);
   9169     goto cleanup;
   9170   }
   9171   ret = 0;
   9172 cleanup:
   9173   GNUNET_free (body);
   9174   free_product_data (&product);
   9175   free_instance_data (&instance);
   9176   return ret;
   9177 }
   9178 
   9179 
   9180 /**
   9181  * Closure for webhook tests.
   9182  */
   9183 struct TestWebhooks_Closure
   9184 {
   9185   /**
   9186    * The instance to use for this test.
   9187    */
   9188   struct InstanceData instance;
   9189 
   9190   /**
   9191    * The array of webhooks.
   9192    */
   9193   struct WebhookData webhooks[3];
   9194 };
   9195 
   9196 
   9197 /**
   9198  * Sets up the data structures used in the webhook tests.
   9199  *
   9200  * @param cls the closure to fill with test data.
   9201  */
   9202 static void
   9203 pre_test_webhooks (struct TestWebhooks_Closure *cls)
   9204 {
   9205   /* Instance */
   9206   make_instance ("test_inst_webhooks",
   9207                  &cls->instance);
   9208 
   9209   /* Webhooks */
   9210   make_webhook ("test_webhooks_wb_0",
   9211                 &cls->webhooks[0]);
   9212 
   9213   make_webhook ("test_webhooks_wb_1",
   9214                 &cls->webhooks[1]);
   9215   cls->webhooks[1].webhook.event_type = "Test paid";
   9216   cls->webhooks[1].webhook.url = "https://example.com";
   9217   cls->webhooks[1].webhook.http_method = "POST";
   9218   cls->webhooks[1].webhook.header_template = "Authorization:1XYJAOR493O";
   9219   cls->webhooks[1].webhook.body_template = "$Amount";
   9220 
   9221   make_webhook ("test_webhooks_wb_2",
   9222                 &cls->webhooks[2]);
   9223   cls->webhooks[2].webhook.event_type = "Test paid";
   9224   cls->webhooks[2].webhook.url = "https://examplerefund.com";
   9225   cls->webhooks[2].webhook.http_method = "POST";
   9226   cls->webhooks[2].webhook.header_template = "Authorization:XY6ORK52JEO";
   9227   cls->webhooks[2].webhook.body_template = "$Amount";
   9228 }
   9229 
   9230 
   9231 /**
   9232  * Handles all teardown after testing.
   9233  *
   9234  * @param cls the closure containing memory to be freed.
   9235  */
   9236 static void
   9237 post_test_webhooks (struct TestWebhooks_Closure *cls)
   9238 {
   9239   free_instance_data (&cls->instance);
   9240 }
   9241 
   9242 
   9243 /**
   9244  * Runs the tests for webhooks.
   9245  *
   9246  * @param cls the container of the test data.
   9247  * @return 0 on success, 1 otherwise.
   9248  */
   9249 static int
   9250 run_test_webhooks (struct TestWebhooks_Closure *cls)
   9251 {
   9252 
   9253   /* Test that insert without an instance fails */
   9254   TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
   9255                                          &cls->webhooks[0],
   9256                                          GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   9257   /* Insert the instance */
   9258   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
   9259                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   9260   /* Test inserting a webhook */
   9261   TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
   9262                                          &cls->webhooks[0],
   9263                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   9264   /* Test that double insert fails */
   9265   TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
   9266                                          &cls->webhooks[0],
   9267                                          GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   9268   /* Test lookup of individual webhooks */
   9269   TEST_RET_ON_FAIL (test_lookup_webhook (&cls->instance,
   9270                                          &cls->webhooks[0]));
   9271   /* Make sure it fails correctly for webhooks that don't exist */
   9272   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
   9273       TALER_MERCHANTDB_get_webhook (pg,
   9274                                     cls->instance.instance.id,
   9275                                     "nonexistent_webhook",
   9276                                     NULL))
   9277   {
   9278     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   9279                 "Lookup webhook failed\n");
   9280     return 1;
   9281   }
   9282   /* Test webhook update */
   9283   cls->webhooks[0].webhook.event_type =
   9284     "Test paid";
   9285   cls->webhooks[0].webhook.url =
   9286     "example.com";
   9287   cls->webhooks[0].webhook.http_method =
   9288     "POST";
   9289   cls->webhooks[0].webhook.header_template =
   9290     "Authorization:WEKFOEKEXZ";
   9291   cls->webhooks[0].webhook.body_template =
   9292     "$Amount";
   9293   TEST_RET_ON_FAIL (test_update_webhook (&cls->instance,
   9294                                          &cls->webhooks[0],
   9295                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   9296 
   9297   TEST_RET_ON_FAIL (test_lookup_webhook (&cls->instance,
   9298                                          &cls->webhooks[0]));
   9299   TEST_RET_ON_FAIL (test_update_webhook (&cls->instance,
   9300                                          &cls->webhooks[1],
   9301                                          GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   9302   /* Test collective webhook lookup */
   9303   TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
   9304                                          &cls->webhooks[1],
   9305                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   9306   TEST_RET_ON_FAIL (test_lookup_webhooks (&cls->instance,
   9307                                           2,
   9308                                           cls->webhooks));
   9309   TEST_RET_ON_FAIL (test_lookup_webhook_by_event (&cls->instance,
   9310                                                   2,
   9311                                                   cls->webhooks));
   9312   TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
   9313                                          &cls->webhooks[2],
   9314                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   9315 
   9316   /* Test webhook deletion */
   9317   TEST_RET_ON_FAIL (test_delete_webhook (&cls->instance,
   9318                                          &cls->webhooks[1],
   9319                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   9320   /* Test double deletion fails */
   9321   TEST_RET_ON_FAIL (test_delete_webhook (&cls->instance,
   9322                                          &cls->webhooks[1],
   9323                                          GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   9324   cls->webhooks[1] = cls->webhooks[2];
   9325   TEST_RET_ON_FAIL (test_lookup_webhooks (&cls->instance,
   9326                                           2,
   9327                                           cls->webhooks));
   9328   TEST_RET_ON_FAIL (test_lookup_webhook_by_event (&cls->instance,
   9329                                                   2,
   9330                                                   cls->webhooks));
   9331   return 0;
   9332 }
   9333 
   9334 
   9335 /**
   9336  * Takes care of webhook testing.
   9337  *
   9338  * @return 0 on success, 1 otherwise.
   9339  */
   9340 static int
   9341 test_webhooks (void)
   9342 {
   9343   struct TestWebhooks_Closure test_cls;
   9344   int test_result;
   9345 
   9346   pre_test_webhooks (&test_cls);
   9347   test_result = run_test_webhooks (&test_cls);
   9348   post_test_webhooks (&test_cls);
   9349   return test_result;
   9350 }
   9351 
   9352 
   9353 /* *********** Pending Webhooks ********** */
   9354 
   9355 /**
   9356  * A container for data relevant to a pending webhook.
   9357  */
   9358 struct PendingWebhookData
   9359 {
   9360   /**
   9361    * Reference to the configured webhook template.
   9362    */
   9363   uint64_t webhook_serial;
   9364 
   9365   /**
   9366    * The details of the pending webhook.
   9367    */
   9368   struct TALER_MERCHANTDB_PendingWebhookDetails pwebhook;
   9369 };
   9370 
   9371 
   9372 /**
   9373  * Creates a pending webhook for testing with.
   9374  *
   9375  * @param serial reference to the configured webhook template.
   9376  * @param pwebhook the pending webhook data to fill.
   9377  */
   9378 static void
   9379 make_pending_webhook (uint64_t webhook_serial,
   9380                       struct PendingWebhookData *pwebhook)
   9381 {
   9382   pwebhook->webhook_serial = webhook_serial;
   9383   pwebhook->pwebhook.next_attempt = GNUNET_TIME_UNIT_ZERO_ABS;
   9384   pwebhook->pwebhook.retries = 0;
   9385   pwebhook->pwebhook.url = "https://exampletest.com";
   9386   pwebhook->pwebhook.http_method = "POST";
   9387   pwebhook->pwebhook.header = "Authorization:XYJAORKJEO";
   9388   pwebhook->pwebhook.body = "$Amount";
   9389 }
   9390 
   9391 
   9392 /**
   9393  * Tests inserting pending webhook data into the database.
   9394  *
   9395  * @param instance the instance to insert the pending webhook for.
   9396  * @param pending webhook the pending webhook data to insert.
   9397  * @param expected_result the result we expect the db to return.
   9398  * @return 0 when successful, 1 otherwise.
   9399  */
   9400 static int
   9401 test_insert_pending_webhook (const struct InstanceData *instance,
   9402                              struct PendingWebhookData *pwebhook,
   9403                              enum GNUNET_DB_QueryStatus expected_result)
   9404 {
   9405 
   9406   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   9407   TEST_COND_RET_ON_FAIL (expected_result ==
   9408                          TALER_MERCHANTDB_insert_pending_webhook (pg,
   9409                                                                   instance->instance.id,
   9410                                                                   pwebhook->
   9411                                                                   webhook_serial,
   9412                                                                   pwebhook->pwebhook.url,
   9413                                                                   pwebhook->pwebhook.
   9414                                                                   http_method,
   9415                                                                   pwebhook->pwebhook.
   9416                                                                   header,
   9417                                                                   pwebhook->pwebhook.body
   9418                                                                   ),
   9419                          "Insert pending webhook failed\n");
   9420   return 0;
   9421 }
   9422 
   9423 
   9424 /**
   9425  * Tests updating pending webhook data in the database.
   9426  *
   9427  * @param instance the instance to update the pending webhook for.
   9428  * @param pending webhook the pending webhook data to update.
   9429  * @param expected_result the result we expect the db to return.
   9430  * @return 0 when successful, 1 otherwise.
   9431  */
   9432 static int
   9433 test_update_pending_webhook (const struct InstanceData *instance,
   9434                              struct PendingWebhookData *pwebhook,
   9435                              enum GNUNET_DB_QueryStatus expected_result)
   9436 {
   9437   pwebhook->pwebhook.next_attempt = GNUNET_TIME_relative_to_absolute (
   9438     GNUNET_TIME_UNIT_HOURS);
   9439   pwebhook->pwebhook.retries++;
   9440   TEST_SET_INSTANCE (instance->instance.id, expected_result);
   9441   TEST_COND_RET_ON_FAIL (expected_result ==
   9442                          TALER_MERCHANTDB_update_pending_webhook (pg,
   9443                                                                   pwebhook->
   9444                                                                   webhook_serial,
   9445                                                                   pwebhook->pwebhook.
   9446                                                                   next_attempt),
   9447                          "Update pending webhook failed\n");
   9448   return 0;
   9449 }
   9450 
   9451 
   9452 /**
   9453  * Container for information for looking up the row number of a deposit.
   9454  */
   9455 struct LookupPendingWebhookSerial_Closure
   9456 {
   9457   /**
   9458    * The pending webhook we're looking for.
   9459    */
   9460   const struct PendingWebhookData *pwebhook;
   9461 
   9462   /**
   9463    * The serial found.
   9464    */
   9465   uint64_t webhook_pending_serial;
   9466 };
   9467 
   9468 
   9469 /**
   9470  * Function called after calling @e test_lookup_all_webhook,
   9471  * test_iterate_pending_webhooks_next and test_lookup_pending_webhook
   9472  *
   9473  * @param cls a pointer to the lookup closure.
   9474  * @param webhook_serial reference to the configured webhook template.
   9475  */
   9476 static void
   9477 get_pending_serial_cb (void *cls,
   9478                        uint64_t webhook_pending_serial,
   9479                        struct GNUNET_TIME_Absolute next_attempt,
   9480                        uint32_t retries,
   9481                        const char *url,
   9482                        const char *http_method,
   9483                        const char *header,
   9484                        const char *body)
   9485 {
   9486   struct LookupPendingWebhookSerial_Closure *lpw = cls;
   9487 
   9488   if ((0 == strcmp (lpw->pwebhook->pwebhook.url,
   9489                     url)) &&
   9490       (0 == strcmp (lpw->pwebhook->pwebhook.http_method,
   9491                     http_method)) &&
   9492       (0 == strcmp (lpw->pwebhook->pwebhook.header,
   9493                     header)) &&
   9494       (0 == strcmp (lpw->pwebhook->pwebhook.body,
   9495                     body)) )
   9496   {
   9497     lpw->webhook_pending_serial = webhook_pending_serial;
   9498   }
   9499   /* else
   9500     {
   9501       fprintf(stdout, "next_attempt: %lu vs %lu\n", lpw->pwebhook->pwebhook.next_attempt.abs_value_us, next_attempt.abs_value_us);
   9502       fprintf(stdout, "retries: %d vs %d\n", lpw->pwebhook->pwebhook.retries, retries);
   9503       fprintf(stdout, "url: %s vs %s\n", lpw->pwebhook->pwebhook.url, url);
   9504       fprintf(stdout, "http_method: %s vs %s\n", lpw->pwebhook->pwebhook.http_method, http_method);
   9505       fprintf(stdout, "header: %s vs %s\n", lpw->pwebhook->pwebhook.header, header);
   9506       fprintf(stdout, "body: %s vs %s\n", lpw->pwebhook->pwebhook.body, body);
   9507       }*/
   9508 }
   9509 
   9510 
   9511 /**
   9512  * Convenience function to retrieve the row number of a webhook pending in the database.
   9513  *
   9514  * @param instance the instance to get webhook pending(wp) from.
   9515  * @param webhook pending the wp to lookup the serial for.
   9516  * @return the row number of the deposit.
   9517  */
   9518 static uint64_t
   9519 get_pending_serial (const struct InstanceData *instance,
   9520                     const struct PendingWebhookData *pwebhook)
   9521 {
   9522   struct LookupPendingWebhookSerial_Closure lpw = {
   9523     .pwebhook = pwebhook,
   9524     .webhook_pending_serial = 0
   9525   };
   9526 
   9527   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   9528                  TALER_MERCHANTDB_set_instance (pg,
   9529                                                 instance->instance.id));
   9530   GNUNET_assert (0 <
   9531                  TALER_MERCHANTDB_iterate_pending_webhooks_above_serial_id (pg,
   9532                                                                             instance->instance.id,
   9533                                                                             0,
   9534                                                                             INT_MAX,
   9535                                                                             &get_pending_serial_cb,
   9536                                                                             &lpw));
   9537   GNUNET_assert (0 != lpw.webhook_pending_serial);
   9538 
   9539   return lpw.webhook_pending_serial;
   9540 }
   9541 
   9542 
   9543 /**
   9544  * Closure for testing pending webhook lookup
   9545  */
   9546 struct TestLookupPendingWebhooks_Closure
   9547 {
   9548   /**
   9549    * Number of webhook serial to compare to
   9550    */
   9551   unsigned int webhooks_to_cmp_length;
   9552 
   9553   /**
   9554    * Pointer to array of webhook serials
   9555    */
   9556   const struct PendingWebhookData *webhooks_to_cmp;
   9557 
   9558   /**
   9559    * Pointer to array of number of matches for each pending webhook
   9560    */
   9561   unsigned int *results_matching;
   9562 
   9563   /**
   9564    * Total number of results returned
   9565    */
   9566   unsigned int results_length;
   9567 };
   9568 
   9569 
   9570 /**
   9571  * Function called after calling @e test_lookup_all_webhook,
   9572  * test_iterate_pending_webhooks_next and test_lookup_pending_webhook
   9573  *
   9574  * @param cls a pointer to the lookup closure.
   9575  * @param webhook_serial reference to the configured webhook template.
   9576  */
   9577 static void
   9578 lookup_pending_webhooks_cb (void *cls,
   9579                             uint64_t webhook_pending_serial,
   9580                             struct GNUNET_TIME_Absolute next_attempt,
   9581                             uint32_t retries,
   9582                             const char *url,
   9583                             const char *http_method,
   9584                             const char *header,
   9585                             const char *body)
   9586 {
   9587   struct TestLookupPendingWebhooks_Closure *cmp = cls;
   9588 
   9589   cmp->results_length++;
   9590   for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i)
   9591   {
   9592     if ((0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.url,
   9593                       url)) &&
   9594         (0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.http_method,
   9595                       http_method)) &&
   9596         (0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.header,
   9597                       header)) &&
   9598         (0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.body,
   9599                       body)) )
   9600     {
   9601       cmp->results_matching[i]++;
   9602     }
   9603   }
   9604 }
   9605 
   9606 
   9607 /**
   9608  * Tests looking up the pending webhook for an instance.
   9609  *
   9610  * @param instance the instance to query from.
   9611  * @param pwebhooks_length the number of pending webhook we are expecting.
   9612  * @param pwebhooks the list of pending webhooks that we expect to be found.
   9613  * @return 0 when successful, 1 otherwise.
   9614  */
   9615 static int
   9616 test_lookup_pending_webhooks (const struct InstanceData *instance,
   9617                               unsigned int pwebhooks_length,
   9618                               const struct PendingWebhookData *pwebhooks)
   9619 {
   9620   unsigned int results_matching[pwebhooks_length];
   9621   struct TestLookupPendingWebhooks_Closure cls = {
   9622     .webhooks_to_cmp_length = pwebhooks_length,
   9623     .webhooks_to_cmp = pwebhooks,
   9624     .results_matching = results_matching,
   9625     .results_length = 0
   9626   };
   9627 
   9628   memset (results_matching, 0, sizeof (results_matching));
   9629   if (0 > TALER_MERCHANTDB_iterate_pending_webhooks (pg,
   9630                                                      1024, /* limit */
   9631                                                      &lookup_pending_webhooks_cb,
   9632                                                      &cls))
   9633   {
   9634     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   9635                 "Lookup pending webhook failed\n");
   9636     return 1;
   9637   }
   9638   if (pwebhooks_length != cls.results_length)
   9639   {
   9640     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   9641                 "Lookup pending webhook failed: incorrect number of results\n");
   9642     return 1;
   9643   }
   9644   for (unsigned int i = 0; i < pwebhooks_length; i++)
   9645   {
   9646     if (1 != cls.results_matching[i])
   9647     {
   9648       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   9649                   "Lookup pending webhook failed: mismatched data\n");
   9650       return 1;
   9651     }
   9652   }
   9653   return 0;
   9654 }
   9655 
   9656 
   9657 /**
   9658  * Tests looking up the future webhook to send for an instance.
   9659  *
   9660  * @param instance the instance to query from.
   9661  * @param pwebhooks_length the number of pending webhook we are expecting.
   9662  * @param pwebhooks the list of pending webhooks that we expect to be found.
   9663  * @return 0 when successful, 1 otherwise.
   9664  */
   9665 static int
   9666 test_iterate_pending_webhooks_next (const struct InstanceData *instance,
   9667                                     unsigned int pwebhooks_length,
   9668                                     const struct PendingWebhookData *pwebhooks)
   9669 {
   9670   unsigned int results_matching[pwebhooks_length];
   9671   struct TestLookupPendingWebhooks_Closure cls = {
   9672     .webhooks_to_cmp_length = pwebhooks_length,
   9673     .webhooks_to_cmp = pwebhooks,
   9674     .results_matching = results_matching,
   9675     .results_length = 0
   9676   };
   9677 
   9678   memset (results_matching, 0, sizeof (results_matching));
   9679   if (0 > TALER_MERCHANTDB_iterate_pending_webhooks_next (pg,
   9680                                                           &lookup_pending_webhooks_cb,
   9681                                                           &cls))
   9682   {
   9683     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   9684                 "Lookup future webhook failed\n");
   9685     return 1;
   9686   }
   9687   if (pwebhooks_length != cls.results_length)
   9688   {
   9689     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   9690                 "Lookup future webhook failed: incorrect number of results\n");
   9691     return 1;
   9692   }
   9693   for (unsigned int i = 0; pwebhooks_length > i; ++i)
   9694   {
   9695     if (1 != cls.results_matching[i])
   9696     {
   9697       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   9698                   "Lookup future webhook failed: mismatched data\n");
   9699       return 1;
   9700     }
   9701   }
   9702   return 0;
   9703 }
   9704 
   9705 
   9706 /**
   9707  * Tests looking up all the pending webhook for an instance.
   9708  *
   9709  * @param instance the instance to query from.
   9710  * @param pwebhooks_length the number of pending webhook we are expecting.
   9711  * @param pwebhooks the list of pending webhooks that we expect to be found.
   9712  * @return 0 when successful, 1 otherwise.
   9713  */
   9714 static int
   9715 test_lookup_all_webhooks (const struct InstanceData *instance,
   9716                           unsigned int pwebhooks_length,
   9717                           const struct PendingWebhookData *pwebhooks)
   9718 {
   9719   uint64_t max_results = 2;
   9720   uint64_t min_row = 0;
   9721   unsigned int results_matching[GNUNET_NZL (pwebhooks_length)];
   9722   struct TestLookupPendingWebhooks_Closure cls = {
   9723     .webhooks_to_cmp_length = pwebhooks_length,
   9724     .webhooks_to_cmp = pwebhooks,
   9725     .results_matching = results_matching,
   9726     .results_length = 0
   9727   };
   9728 
   9729   memset (results_matching,
   9730           0,
   9731           sizeof (results_matching));
   9732   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   9733   if (0 > TALER_MERCHANTDB_iterate_pending_webhooks_above_serial_id (pg,
   9734                                                                      instance->instance.id,
   9735                                                                      min_row,
   9736                                                                      max_results,
   9737                                                                      &lookup_pending_webhooks_cb,
   9738                                                                      &cls))
   9739   {
   9740     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   9741                 "Lookup all webhooks failed\n");
   9742     return 1;
   9743   }
   9744   if (pwebhooks_length != cls.results_length)
   9745   {
   9746     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   9747                 "Lookup all webhooks failed: incorrect number of results\n");
   9748     return 1;
   9749   }
   9750   for (unsigned int i = 0; pwebhooks_length > i; ++i)
   9751   {
   9752     if (1 != cls.results_matching[i])
   9753     {
   9754       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   9755                   "Lookup all webhooks failed: mismatched data\n");
   9756       return 1;
   9757     }
   9758   }
   9759   return 0;
   9760 }
   9761 
   9762 
   9763 /**
   9764  * Tests deleting a pending webhook.
   9765  *
   9766  * @param instance the instance to delete the pending webhook from.
   9767  * @param pwebhook the pending webhook that should be deleted.
   9768  * @param expected_result the result that we expect the DB to return.
   9769  * @return 0 when successful, 1 otherwise.
   9770  */
   9771 static int
   9772 test_delete_pending_webhook (uint64_t webhooks_pending_serial,
   9773                              enum GNUNET_DB_QueryStatus expected_result)
   9774 {
   9775 
   9776   TEST_COND_RET_ON_FAIL (expected_result ==
   9777                          TALER_MERCHANTDB_delete_pending_webhook (pg,
   9778                                                                   webhooks_pending_serial),
   9779                          "Delete webhook failed\n");
   9780   return 0;
   9781 }
   9782 
   9783 
   9784 /**
   9785  * Closure for pending webhook tests.
   9786  */
   9787 struct TestPendingWebhooks_Closure
   9788 {
   9789   /**
   9790    * The instance to use for this test.
   9791    */
   9792   struct InstanceData instance;
   9793 
   9794   /**
   9795    * Webhooks that the pending webhooks reference via foreign key.
   9796    * Each per-instance schema has its own webhook_serial sequence
   9797    * starting at 1, so we must populate webhooks before inserting
   9798    * pending webhooks.
   9799    */
   9800   struct WebhookData webhooks[4];
   9801 
   9802   /**
   9803    * The array of pending webhooks.
   9804    */
   9805   struct PendingWebhookData pwebhooks[2];
   9806 };
   9807 
   9808 
   9809 /**
   9810  * Sets up the data structures used in the pending webhook tests.
   9811  *
   9812  * @param cls the closure to fill with test data.
   9813  */
   9814 static void
   9815 pre_test_pending_webhooks (struct TestPendingWebhooks_Closure *cls)
   9816 {
   9817   /* Instance */
   9818   make_instance ("test_inst_pending_webhooks",
   9819                  &cls->instance);
   9820 
   9821   /* Webhooks the pending webhooks reference (serials 1..4) */
   9822   make_webhook ("test_pwh_wb_0", &cls->webhooks[0]);
   9823   make_webhook ("test_pwh_wb_1", &cls->webhooks[1]);
   9824   make_webhook ("test_pwh_wb_2", &cls->webhooks[2]);
   9825   make_webhook ("test_pwh_wb_3", &cls->webhooks[3]);
   9826 
   9827   /* Webhooks */
   9828   make_pending_webhook (1,
   9829                         &cls->pwebhooks[0]);
   9830 
   9831   make_pending_webhook (4,
   9832                         &cls->pwebhooks[1]);
   9833   cls->pwebhooks[1].pwebhook.url = "https://test.com";
   9834   cls->pwebhooks[1].pwebhook.http_method = "POST";
   9835   cls->pwebhooks[1].pwebhook.header = "Authorization:XYJAO5R06EO";
   9836   cls->pwebhooks[1].pwebhook.body = "$Amount";
   9837 }
   9838 
   9839 
   9840 /**
   9841  * Handles all teardown after testing.
   9842  *
   9843  * @param cls the closure containing memory to be freed.
   9844  */
   9845 static void
   9846 post_test_pending_webhooks (struct TestPendingWebhooks_Closure *cls)
   9847 {
   9848   free_instance_data (&cls->instance);
   9849 }
   9850 
   9851 
   9852 /**
   9853  * Runs the tests for pending webhooks.
   9854  *
   9855  * @param cls the container of the test data.
   9856  * @return 0 on success, 1 otherwise.
   9857  */
   9858 static int
   9859 run_test_pending_webhooks (struct TestPendingWebhooks_Closure *cls)
   9860 {
   9861   uint64_t webhook_pending_serial0;
   9862   uint64_t webhook_pending_serial1;
   9863 
   9864   /* Test that insert without an instance fails */
   9865   TEST_RET_ON_FAIL (test_insert_pending_webhook (&cls->instance,
   9866                                                  &cls->pwebhooks[0],
   9867                                                  GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   9868 
   9869   /* Insert the instance */
   9870   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
   9871                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   9872 
   9873   /* Insert the referenced webhooks so pending webhook FKs resolve. */
   9874   for (unsigned int i = 0; i < 4; i++)
   9875     TEST_RET_ON_FAIL (
   9876       test_insert_webhook (&cls->instance,
   9877                            &cls->webhooks[i],
   9878                            GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   9879 
   9880   /* Test inserting a pending webhook */
   9881   TEST_RET_ON_FAIL (test_insert_pending_webhook (&cls->instance,
   9882                                                  &cls->pwebhooks[0],
   9883                                                  GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   9884   TEST_RET_ON_FAIL (test_insert_pending_webhook (&cls->instance,
   9885                                                  &cls->pwebhooks[1],
   9886                                                  GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   9887   /* Test collective pending webhook lookup */
   9888   TEST_RET_ON_FAIL (test_lookup_pending_webhooks (&cls->instance,
   9889                                                   2,
   9890                                                   cls->pwebhooks));
   9891   /* Test pending webhook update */
   9892   TEST_RET_ON_FAIL (test_update_pending_webhook (&cls->instance,
   9893                                                  &cls->pwebhooks[0],
   9894                                                  GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   9895   TEST_RET_ON_FAIL (test_iterate_pending_webhooks_next (&cls->instance,
   9896                                                         1,
   9897                                                         &cls->pwebhooks[1]));
   9898   TEST_RET_ON_FAIL (test_update_pending_webhook (&cls->instance,
   9899                                                  &cls->pwebhooks[1],
   9900                                                  GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   9901   // ???
   9902   TEST_RET_ON_FAIL (test_lookup_all_webhooks (&cls->instance,
   9903                                               2,
   9904                                               cls->pwebhooks));
   9905 
   9906   webhook_pending_serial0 = get_pending_serial (&cls->instance,
   9907                                                 &cls->pwebhooks[0]);
   9908   webhook_pending_serial1 = get_pending_serial (&cls->instance,
   9909                                                 &cls->pwebhooks[1]);
   9910 
   9911   /* Test webhook deletion */
   9912   TEST_RET_ON_FAIL (test_delete_pending_webhook (webhook_pending_serial1,
   9913                                                  GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   9914   /* Test double deletion fails */
   9915   TEST_RET_ON_FAIL (test_delete_pending_webhook (webhook_pending_serial1,
   9916                                                  GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   9917   TEST_RET_ON_FAIL (test_delete_pending_webhook (webhook_pending_serial0,
   9918                                                  GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   9919   TEST_RET_ON_FAIL (test_lookup_all_webhooks (&cls->instance,
   9920                                               0,
   9921                                               NULL));
   9922   return 0;
   9923 }
   9924 
   9925 
   9926 /**
   9927  * Takes care of pending webhook testing.
   9928  *
   9929  * @return 0 on success, 1 otherwise.
   9930  */
   9931 static int
   9932 test_pending_webhooks (void)
   9933 {
   9934   struct TestPendingWebhooks_Closure test_cls;
   9935   int test_result;
   9936 
   9937   pre_test_pending_webhooks (&test_cls);
   9938   test_result = run_test_pending_webhooks (&test_cls);
   9939   post_test_pending_webhooks (&test_cls);
   9940   return test_result;
   9941 }
   9942 
   9943 
   9944 /* ********** Statistics ********** */
   9945 
   9946 
   9947 /**
   9948  * Closure for the statistics tests.
   9949  */
   9950 struct TestStatistics_Closure
   9951 {
   9952   /**
   9953    * Instance the statistics are collected for.
   9954    */
   9955   struct InstanceData instance;
   9956 };
   9957 
   9958 
   9959 /**
   9960  * Closure for #count_bucket_counter_cb().
   9961  */
   9962 struct CountBuckets_Closure
   9963 {
   9964   /**
   9965    * Number of bucket rows we were called with.
   9966    */
   9967   unsigned int count;
   9968 };
   9969 
   9970 
   9971 /**
   9972  * Runs @a sql directly on the database connection of the test.  Used to
   9973  * set up statistics state that has no dedicated MERCHANTDB entry point
   9974  * (the statistics tables are only ever written by triggers).
   9975  *
   9976  * @param sql the SQL statement(s) to execute
   9977  * @return 0 on success, 1 otherwise.
   9978  */
   9979 static int
   9980 statistics_exec_sql (const char *sql)
   9981 {
   9982   struct GNUNET_PQ_ExecuteStatement es[] = {
   9983     GNUNET_PQ_make_execute (sql),
   9984     GNUNET_PQ_EXECUTE_STATEMENT_END
   9985   };
   9986 
   9987   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
   9988                          GNUNET_PQ_exec_statements (pg->conn,
   9989                                                     es),
   9990                          "Direct SQL execution failed\n");
   9991   return 0;
   9992 }
   9993 
   9994 
   9995 /**
   9996  * Counts the bucket rows we are called for.
   9997  *
   9998  * @param cls a `struct CountBuckets_Closure *`
   9999  * @param description description of the statistic
  10000  * @param bucket_start start time of the bucket
  10001  * @param bucket_end end time of the bucket
  10002  * @param bucket_range range of the bucket
  10003  * @param cumulative_counter counter value
  10004  */
  10005 static void
  10006 count_bucket_counter_cb (void *cls,
  10007                          const char *description,
  10008                          struct GNUNET_TIME_Timestamp bucket_start,
  10009                          struct GNUNET_TIME_Timestamp bucket_end,
  10010                          const char *bucket_range,
  10011                          uint64_t cumulative_counter)
  10012 {
  10013   struct CountBuckets_Closure *cbc = cls;
  10014 
  10015   (void) description;
  10016   (void) bucket_start;
  10017   (void) bucket_end;
  10018   (void) bucket_range;
  10019   (void) cumulative_counter;
  10020   cbc->count++;
  10021 }
  10022 
  10023 
  10024 /**
  10025  * Determines how many bucket rows exist for @a slug.
  10026  *
  10027  * @param instance the instance to look at.
  10028  * @param slug the statistic to count the buckets of.
  10029  * @param[out] count set to the number of bucket rows.
  10030  * @return 0 on success, 1 otherwise.
  10031  */
  10032 static int
  10033 count_bucket_counters (const struct InstanceData *instance,
  10034                        const char *slug,
  10035                        unsigned int *count)
  10036 {
  10037   struct CountBuckets_Closure cbc = {
  10038     .count = 0
  10039   };
  10040 
  10041   TEST_SET_INSTANCE (instance->instance.id,
  10042                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
  10043   TEST_COND_RET_ON_FAIL (0 <=
  10044                          TALER_MERCHANTDB_iterate_statistic_bucket_counters (
  10045                            pg,
  10046                            instance->instance.id,
  10047                            slug,
  10048                            &count_bucket_counter_cb,
  10049                            &cbc),
  10050                          "Lookup of bucket statistics failed\n");
  10051   *count = cbc.count;
  10052   return 0;
  10053 }
  10054 
  10055 
  10056 /**
  10057  * Tests that garbage collection does not throw away bucket statistics
  10058  * that are well within their retention age.  Regression test: the
  10059  * retention cut-off in merchant_statistic_bucket_gc() used to be
  10060  * computed with EXTRACT(SECONDS FROM ...), which is the seconds *field*
  10061  * of the interval and thus zero for every range we use, so the cut-off
  10062  * degenerated to "now" and every bucket row was deleted on every run.
  10063  *
  10064  * @param instance the instance to collect statistics for.
  10065  * @return 0 on success, 1 otherwise.
  10066  */
  10067 static int
  10068 test_statistics_bucket_gc (const struct InstanceData *instance)
  10069 {
  10070   unsigned int before;
  10071   unsigned int after;
  10072 
  10073   TEST_SET_INSTANCE (instance->instance.id,
  10074                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
  10075   TEST_RET_ON_FAIL (statistics_exec_sql (
  10076                       "CALL merchant_do_bump_number_stat"
  10077                       " ('tokens-issued'"
  10078                       " ,CURRENT_TIMESTAMP(0)::TIMESTAMP"
  10079                       " ,1)"));
  10080   TEST_RET_ON_FAIL (count_bucket_counters (instance,
  10081                                            "tokens-issued",
  10082                                            &before));
  10083   TEST_COND_RET_ON_FAIL (0 < before,
  10084                          "Bumping a bucket statistic recorded nothing\n");
  10085   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
  10086                          TALER_MERCHANTDB_gc (pg),
  10087                          "Garbage collection failed\n");
  10088   TEST_RET_ON_FAIL (count_bucket_counters (instance,
  10089                                            "tokens-issued",
  10090                                            &after));
  10091   TEST_COND_RET_ON_FAIL (before == after,
  10092                          "Garbage collection deleted bucket statistics\n");
  10093   return 0;
  10094 }
  10095 
  10096 
  10097 /**
  10098  * Counts the amount buckets we are called for.
  10099  *
  10100  * @param cls a `struct CountBuckets_Closure *`
  10101  * @param description description of the statistic
  10102  * @param bucket_start start time of the bucket
  10103  * @param bucket_end end time of the bucket
  10104  * @param bucket_range range of the bucket
  10105  * @param cumulative_amounts_len length of @a cumulative_amounts
  10106  * @param cumulative_amounts the cumulative amounts
  10107  */
  10108 static void
  10109 count_bucket_amount_cb (void *cls,
  10110                         const char *description,
  10111                         struct GNUNET_TIME_Timestamp bucket_start,
  10112                         struct GNUNET_TIME_Timestamp bucket_end,
  10113                         const char *bucket_range,
  10114                         unsigned int cumulative_amounts_len,
  10115                         const struct TALER_Amount cumulative_amounts[static
  10116                                                                      cumulative_amounts_len])
  10117 {
  10118   struct CountBuckets_Closure *cbc = cls;
  10119 
  10120   (void) description;
  10121   (void) bucket_start;
  10122   (void) bucket_end;
  10123   (void) bucket_range;
  10124   (void) cumulative_amounts_len;
  10125   (void) cumulative_amounts;
  10126   cbc->count++;
  10127 }
  10128 
  10129 
  10130 /**
  10131  * Bumps the amount statistic @a slug and checks that the bump was
  10132  * actually recorded in the bucket statistics.  Fails if @a slug is not
  10133  * registered in @a instance, as the bump procedures silently ignore
  10134  * statistics nobody registered.
  10135  *
  10136  * @param instance the instance to collect statistics for.
  10137  * @param slug the statistic to bump.
  10138  * @return 0 on success, 1 otherwise.
  10139  */
  10140 static int
  10141 test_statistics_amount_slug_collected (const struct InstanceData *instance,
  10142                                        const char *slug)
  10143 {
  10144   struct CountBuckets_Closure cbc = {
  10145     .count = 0
  10146   };
  10147   char *sql;
  10148 
  10149   TEST_SET_INSTANCE (instance->instance.id,
  10150                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
  10151   GNUNET_asprintf (&sql,
  10152                    "CALL merchant_do_bump_amount_stat"
  10153                    " ('%s'"
  10154                    " ,CURRENT_TIMESTAMP(0)::TIMESTAMP"
  10155                    " ,ROW(5,0,'EUR')::merchant.taler_amount_currency)",
  10156                    slug);
  10157   {
  10158     int ret;
  10159 
  10160     ret = statistics_exec_sql (sql);
  10161     GNUNET_free (sql);
  10162     TEST_RET_ON_FAIL (ret);
  10163   }
  10164   TEST_COND_RET_ON_FAIL (0 <=
  10165                          TALER_MERCHANTDB_iterate_statistic_bucket_amounts (
  10166                            pg,
  10167                            instance->instance.id,
  10168                            slug,
  10169                            &count_bucket_amount_cb,
  10170                            &cbc),
  10171                          "Lookup of bucket statistics failed\n");
  10172   TEST_COND_RET_ON_FAIL (0 < cbc.count,
  10173                          "Statistic bumped by a trigger is not registered\n");
  10174   return 0;
  10175 }
  10176 
  10177 
  10178 /**
  10179  * Tests that bumping a statistic only uses the bucket ranges of the
  10180  * registration with the matching type.  Regression test: the cursor
  10181  * over the ranges did not filter on the type, so a slug registered
  10182  * both as a number and as an amount statistic got buckets for the
  10183  * union of both range sets.
  10184  *
  10185  * @param instance the instance to collect statistics for.
  10186  * @return 0 on success, 1 otherwise.
  10187  */
  10188 static int
  10189 test_statistics_dual_type_slug (const struct InstanceData *instance)
  10190 {
  10191   unsigned int count;
  10192 
  10193   TEST_SET_INSTANCE (instance->instance.id,
  10194                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
  10195   TEST_RET_ON_FAIL (statistics_exec_sql (
  10196                       "INSERT INTO merchant_statistic_bucket_meta"
  10197                       " (slug"
  10198                       " ,description"
  10199                       " ,stype"
  10200                       " ,ranges"
  10201                       " ,ages)"
  10202                       " VALUES"
  10203                       " ('test-dual-type'"
  10204                       " ,'registered as a number statistic'"
  10205                       " ,'number'"
  10206                       " ,ARRAY['hour'::merchant.statistic_range]"
  10207                       " ,ARRAY[72])"
  10208                       " ,('test-dual-type'"
  10209                       " ,'registered as an amount statistic'"
  10210                       " ,'amount'"
  10211                       " ,ARRAY['day'::merchant.statistic_range,'week','month']"
  10212                       " ,ARRAY[14,12,24])"));
  10213   TEST_RET_ON_FAIL (statistics_exec_sql (
  10214                       "CALL merchant_do_bump_number_stat"
  10215                       " ('test-dual-type'"
  10216                       " ,CURRENT_TIMESTAMP(0)::TIMESTAMP"
  10217                       " ,1)"));
  10218   TEST_RET_ON_FAIL (count_bucket_counters (instance,
  10219                                            "test-dual-type",
  10220                                            &count));
  10221   TEST_COND_RET_ON_FAIL (1 == count,
  10222                          "Bump used the ranges of the wrong registration\n");
  10223   return 0;
  10224 }
  10225 
  10226 
  10227 /**
  10228  * Determines how many interval events are stored for @a slug.
  10229  *
  10230  * @param instance the instance to look at.
  10231  * @param slug the statistic to count the events of.
  10232  * @param[out] count set to the number of events.
  10233  * @return 0 on success, 1 otherwise.
  10234  */
  10235 static int
  10236 statistics_count_counter_events (const struct InstanceData *instance,
  10237                                  const char *slug,
  10238                                  uint64_t *count)
  10239 {
  10240   struct GNUNET_PQ_QueryParam params[] = {
  10241     GNUNET_PQ_query_param_string (slug),
  10242     GNUNET_PQ_query_param_end
  10243   };
  10244   struct GNUNET_PQ_ResultSpec rs[] = {
  10245     GNUNET_PQ_result_spec_uint64 ("num",
  10246                                   count),
  10247     GNUNET_PQ_result_spec_end
  10248   };
  10249 
  10250   TEST_SET_INSTANCE (instance->instance.id,
  10251                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
  10252   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
  10253                          GNUNET_PQ_prepare_anon (
  10254                            pg->conn,
  10255                            "SELECT COUNT(*) AS num"
  10256                            " FROM merchant_statistic_counter_event"
  10257                            " WHERE imeta_serial_id ="
  10258                            "  (SELECT imeta_serial_id"
  10259                            "     FROM merchant_statistic_interval_meta"
  10260                            "    WHERE slug=$1"
  10261                            "      AND stype='number')"),
  10262                          "Preparing event count failed\n");
  10263   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
  10264                          GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
  10265                                                                    "",
  10266                                                                    params,
  10267                                                                    rs),
  10268                          "Counting interval events failed\n");
  10269   return 0;
  10270 }
  10271 
  10272 
  10273 /**
  10274  * Tests that garbage collection survives compacting an interval event
  10275  * that an interval counter still uses as its watermark.  Regression
  10276  * test: merchant_statistic_counter_gc() deletes all but the lowest
  10277  * serial of a window, which trips the ON DELETE RESTRICT foreign key
  10278  * of merchant_statistic_interval_counter.event_delimiter as soon as
  10279  * the watermark is not that lowest serial.  merchant_do_gc() does not
  10280  * trap that error, so garbage collection was abandoned for all
  10281  * remaining instances.
  10282  *
  10283  * @param instance the instance to collect statistics for.
  10284  * @return 0 on success, 1 otherwise.
  10285  */
  10286 static int
  10287 test_statistics_counter_gc_delimiter (const struct InstanceData *instance)
  10288 {
  10289   TEST_SET_INSTANCE (instance->instance.id,
  10290                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
  10291   /* Two events in the same coarsening window, with the watermark of the
  10292      interval counter on the younger (higher serial) one. */
  10293   TEST_RET_ON_FAIL (statistics_exec_sql (
  10294                       "DO $$\n"
  10295                       "DECLARE\n"
  10296                       "  my_time INT8 := ROUND(EXTRACT(epoch FROM"
  10297                       " CURRENT_TIMESTAMP(0)::TIMESTAMP))::INT8;\n"
  10298                       "  my_meta INT8;\n"
  10299                       "  my_first INT8;\n"
  10300                       "  my_second INT8;\n"
  10301                       "BEGIN\n"
  10302                       "  SELECT imeta_serial_id\n"
  10303                       "    INTO my_meta\n"
  10304                       "    FROM merchant_statistic_interval_meta\n"
  10305                       "   WHERE slug='orders-created'\n"
  10306                       "     AND stype='number';\n"
  10307                       "  INSERT INTO merchant_statistic_counter_event\n"
  10308                       "    (imeta_serial_id, slot, delta)\n"
  10309                       "    VALUES (my_meta, my_time - 7000, 1)\n"
  10310                       "    RETURNING nevent_serial_id INTO my_first;\n"
  10311                       "  INSERT INTO merchant_statistic_counter_event\n"
  10312                       "    (imeta_serial_id, slot, delta)\n"
  10313                       "    VALUES (my_meta, my_time - 6900, 1)\n"
  10314                       "    RETURNING nevent_serial_id INTO my_second;\n"
  10315                       "  INSERT INTO merchant_statistic_interval_counter\n"
  10316                       "    (imeta_serial_id, range, event_delimiter,"
  10317                       " cumulative_number)\n"
  10318                       "    VALUES (my_meta, 7200, my_second, 1);\n"
  10319                       "END $$;"));
  10320   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
  10321                          TALER_MERCHANTDB_gc (pg),
  10322                          "Garbage collection failed\n");
  10323   {
  10324     uint64_t num_events;
  10325 
  10326     TEST_RET_ON_FAIL (statistics_count_counter_events (instance,
  10327                                                        "orders-created",
  10328                                                        &num_events));
  10329     TEST_COND_RET_ON_FAIL (1 == num_events,
  10330                            "Garbage collection did not compact the events\n");
  10331   }
  10332   return 0;
  10333 }
  10334 
  10335 
  10336 /**
  10337  * SQL bumping the 'tokens-used' statistic by one.
  10338  */
  10339 #define BUMP_TOKENS_USED \
  10340         "CALL merchant_do_bump_number_stat" \
  10341         " ('tokens-used'" \
  10342         " ,CURRENT_TIMESTAMP(0)::TIMESTAMP" \
  10343         " ,1)"
  10344 
  10345 
  10346 /**
  10347  * Determines the largest counter value stored for @a slug.
  10348  *
  10349  * @param instance the instance to look at.
  10350  * @param slug the statistic to look at.
  10351  * @param[out] value set to the largest bucket counter.
  10352  * @return 0 on success, 1 otherwise.
  10353  */
  10354 static int
  10355 statistics_max_bucket_counter (const struct InstanceData *instance,
  10356                                const char *slug,
  10357                                uint64_t *value)
  10358 {
  10359   struct GNUNET_PQ_QueryParam params[] = {
  10360     GNUNET_PQ_query_param_string (slug),
  10361     GNUNET_PQ_query_param_end
  10362   };
  10363   struct GNUNET_PQ_ResultSpec rs[] = {
  10364     GNUNET_PQ_result_spec_uint64 ("num",
  10365                                   value),
  10366     GNUNET_PQ_result_spec_end
  10367   };
  10368 
  10369   TEST_SET_INSTANCE (instance->instance.id,
  10370                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
  10371   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
  10372                          GNUNET_PQ_prepare_anon (
  10373                            pg->conn,
  10374                            "SELECT COALESCE(MAX(cumulative_number),0) AS num"
  10375                            " FROM merchant_statistic_bucket_counter"
  10376                            " WHERE bmeta_serial_id ="
  10377                            "  (SELECT bmeta_serial_id"
  10378                            "     FROM merchant_statistic_bucket_meta"
  10379                            "    WHERE slug=$1"
  10380                            "      AND stype='number')"),
  10381                          "Preparing counter lookup failed\n");
  10382   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
  10383                          GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
  10384                                                                    "",
  10385                                                                    params,
  10386                                                                    rs),
  10387                          "Looking up bucket counter failed\n");
  10388   return 0;
  10389 }
  10390 
  10391 
  10392 /**
  10393  * Bumps the 'tokens-used' statistic from a second process while this
  10394  * process still holds the very first (uncommitted) bump of the same
  10395  * bucket.  Regression test: the bucket statistics used to be written
  10396  * as UPDATE-then-INSERT, so the second writer got a unique violation
  10397  * (which GNUnet maps to SUCCESS_NO_RESULTS) and its event was lost;
  10398  * in the pay path the very same pattern surfaces to the wallet as a
  10399  * bogus 409 on a perfectly valid payment.
  10400  *
  10401  * @param instance the instance to collect statistics for.
  10402  * @return 0 on success, 1 otherwise.
  10403  */
  10404 static int
  10405 test_statistics_bucket_upsert_race (const struct InstanceData *instance)
  10406 {
  10407   pid_t child;
  10408   int status;
  10409   uint64_t value;
  10410 
  10411   TEST_SET_INSTANCE (instance->instance.id,
  10412                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
  10413   /* Make sure the bucket does not exist yet, only then do both writers
  10414      race to create it. */
  10415   TEST_RET_ON_FAIL (statistics_exec_sql (
  10416                       "DELETE FROM merchant_statistic_bucket_counter"
  10417                       " WHERE bmeta_serial_id ="
  10418                       "  (SELECT bmeta_serial_id"
  10419                       "     FROM merchant_statistic_bucket_meta"
  10420                       "    WHERE slug='tokens-used'"
  10421                       "      AND stype='number')"));
  10422   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
  10423                          TALER_MERCHANTDB_start_read_committed (
  10424                            pg,
  10425                            "bucket statistics upsert race"),
  10426                          "Failed to start transaction\n");
  10427   TEST_RET_ON_FAIL (statistics_exec_sql (BUMP_TOKENS_USED));
  10428   child = fork ();
  10429   if (-1 == child)
  10430   {
  10431     TALER_MERCHANTDB_rollback (pg);
  10432     GNUNET_break (0);
  10433     return 1;
  10434   }
  10435   if (0 == child)
  10436   {
  10437     struct TALER_MERCHANTDB_PostgresContext *cpg;
  10438     int ret = 1;
  10439 
  10440     /* Second writer: blocks on the uncommitted row of the parent until
  10441        the parent commits below. */
  10442     cpg = TALER_MERCHANTDB_connect (test_cfg);
  10443     if ( (NULL != cpg) &&
  10444          (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
  10445           TALER_MERCHANTDB_set_instance (cpg,
  10446                                          instance->instance.id)) )
  10447     {
  10448       struct GNUNET_PQ_ExecuteStatement es[] = {
  10449         GNUNET_PQ_make_execute (BUMP_TOKENS_USED),
  10450         GNUNET_PQ_EXECUTE_STATEMENT_END
  10451       };
  10452 
  10453       if (GNUNET_OK ==
  10454           GNUNET_PQ_exec_statements (cpg->conn,
  10455                                      es))
  10456         ret = 0;
  10457     }
  10458     _exit (ret);
  10459   }
  10460   sleep (1); /* give the child time to reach the conflicting INSERT */
  10461   TEST_COND_RET_ON_FAIL (0 <=
  10462                          TALER_MERCHANTDB_commit (pg),
  10463                          "Failed to commit transaction\n");
  10464   if (0 >= waitpid (child,
  10465                     &status,
  10466                     0))
  10467     sleep (2); /* cannot reap the child, just give it time to finish */
  10468   TEST_RET_ON_FAIL (statistics_max_bucket_counter (instance,
  10469                                                    "tokens-used",
  10470                                                    &value));
  10471   TEST_COND_RET_ON_FAIL (2 == value,
  10472                          "Concurrent statistics bump was lost\n");
  10473   return 0;
  10474 }
  10475 
  10476 
  10477 /**
  10478  * Prepares for the statistics tests.
  10479  *
  10480  * @param[out] cls the closure to initialize.
  10481  */
  10482 static void
  10483 pre_test_statistics (struct TestStatistics_Closure *cls)
  10484 {
  10485   make_instance ("test_inst_statistics",
  10486                  &cls->instance);
  10487 }
  10488 
  10489 
  10490 /**
  10491  * Cleans up after the statistics tests.
  10492  *
  10493  * @param[in,out] cls the closure to clean up.
  10494  */
  10495 static void
  10496 post_test_statistics (struct TestStatistics_Closure *cls)
  10497 {
  10498   free_instance_data (&cls->instance);
  10499 }
  10500 
  10501 
  10502 /**
  10503  * Runs the statistics tests.
  10504  *
  10505  * @param cls the closure to use.
  10506  * @return 0 on success, 1 otherwise.
  10507  */
  10508 static int
  10509 run_test_statistics (struct TestStatistics_Closure *cls)
  10510 {
  10511   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
  10512                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
  10513   TEST_RET_ON_FAIL (test_statistics_bucket_gc (&cls->instance));
  10514   /* Every amount statistic bumped by a trigger in pg_triggers.sql must
  10515      be registered in the per-instance schema. */
  10516   TEST_RET_ON_FAIL (test_statistics_amount_slug_collected (
  10517                       &cls->instance,
  10518                       "payments-received-after-deposit-fee"));
  10519   TEST_RET_ON_FAIL (test_statistics_amount_slug_collected (
  10520                       &cls->instance,
  10521                       "total-deposit-fees-paid"));
  10522   TEST_RET_ON_FAIL (test_statistics_amount_slug_collected (
  10523                       &cls->instance,
  10524                       "total-wire-fees-paid"));
  10525   TEST_RET_ON_FAIL (test_statistics_amount_slug_collected (
  10526                       &cls->instance,
  10527                       "refunds-granted"));
  10528   TEST_RET_ON_FAIL (test_statistics_amount_slug_collected (
  10529                       &cls->instance,
  10530                       "deposits-received"));
  10531   TEST_RET_ON_FAIL (test_statistics_amount_slug_collected (
  10532                       &cls->instance,
  10533                       "deposits-fees-paid"));
  10534   TEST_RET_ON_FAIL (test_statistics_counter_gc_delimiter (&cls->instance));
  10535   TEST_RET_ON_FAIL (test_statistics_dual_type_slug (&cls->instance));
  10536   TEST_RET_ON_FAIL (test_statistics_bucket_upsert_race (&cls->instance));
  10537   return 0;
  10538 }
  10539 
  10540 
  10541 /**
  10542  * Takes care of statistics testing.
  10543  *
  10544  * @return 0 on success, 1 otherwise.
  10545  */
  10546 static int
  10547 test_statistics (void)
  10548 {
  10549   struct TestStatistics_Closure test_cls;
  10550   int test_result;
  10551 
  10552   pre_test_statistics (&test_cls);
  10553   test_result = run_test_statistics (&test_cls);
  10554   post_test_statistics (&test_cls);
  10555   return test_result;
  10556 }
  10557 
  10558 
  10559 /* ********** MFA challenges ********** */
  10560 
  10561 
  10562 /**
  10563  * Tests solving multi-factor authentication (MFA) challenges.
  10564  *
  10565  * Besides the obvious cases, this pins down the *idempotency* of
  10566  * confirming a challenge that has already been confirmed: any TAN is
  10567  * then accepted and the retry counter is left alone.  That is
  10568  * intentional (see the comment in do_solve_mfa_challenge.sql): the
  10569  * authorization is carried by the confirmation already stored in the
  10570  * database, which this path does not change, and reaching it already
  10571  * requires knowing the challenge ID together with the matching body
  10572  * hash.  The assertions are here so that the contract cannot silently
  10573  * drift.
  10574  *
  10575  * @return 0 on success, 1 otherwise.
  10576  */
  10577 static int
  10578 test_mfa_challenges (void)
  10579 {
  10580   struct TALER_MERCHANT_MFA_BodyHash h_body;
  10581   struct TALER_MERCHANT_MFA_BodySalt salt;
  10582   struct TALER_MERCHANT_MFA_BodyHash other_h_body;
  10583   uint64_t challenge_id;
  10584   bool solved;
  10585   uint32_t retry_counter;
  10586 
  10587   GNUNET_CRYPTO_random_block (&h_body,
  10588                               sizeof (h_body));
  10589   GNUNET_CRYPTO_random_block (&other_h_body,
  10590                               sizeof (other_h_body));
  10591   GNUNET_CRYPTO_random_block (&salt,
  10592                               sizeof (salt));
  10593   TEST_COND_RET_ON_FAIL (
  10594     GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
  10595     TALER_MERCHANTDB_insert_mfa_challenge (
  10596       pg,
  10597       "test_inst_mfa",
  10598       TALER_MERCHANT_MFA_CO_AUTH_CONFIGURATION,
  10599       &h_body,
  10600       &salt,
  10601       "1234-5678",
  10602       GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS),
  10603       GNUNET_TIME_UNIT_ZERO_ABS,
  10604       TALER_MERCHANT_MFA_CHANNEL_EMAIL,
  10605       "test@example.com",
  10606       &challenge_id),
  10607     "Insert MFA challenge failed\n");
  10608   /* A challenge is only addressable with the matching body hash. */
  10609   TEST_COND_RET_ON_FAIL (
  10610     GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
  10611     TALER_MERCHANTDB_do_solve_mfa_challenge (pg,
  10612                                              challenge_id,
  10613                                              &other_h_body,
  10614                                              "1234-5678",
  10615                                              &solved,
  10616                                              &retry_counter),
  10617     "Challenge was solvable with a different body hash\n");
  10618   TEST_COND_RET_ON_FAIL (! solved,
  10619                          "Challenge solved with a different body hash\n");
  10620   /* A wrong TAN is rejected and costs one of the three attempts. */
  10621   TEST_COND_RET_ON_FAIL (
  10622     GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
  10623     TALER_MERCHANTDB_do_solve_mfa_challenge (pg,
  10624                                              challenge_id,
  10625                                              &h_body,
  10626                                              "0000-0000",
  10627                                              &solved,
  10628                                              &retry_counter),
  10629     "Solving MFA challenge failed\n");
  10630   TEST_COND_RET_ON_FAIL (! solved,
  10631                          "A wrong TAN solved the challenge\n");
  10632   TEST_COND_RET_ON_FAIL (2 == retry_counter,
  10633                          "A wrong TAN did not cost an attempt\n");
  10634   /* The correct TAN confirms the challenge. */
  10635   TEST_COND_RET_ON_FAIL (
  10636     GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
  10637     TALER_MERCHANTDB_do_solve_mfa_challenge (pg,
  10638                                              challenge_id,
  10639                                              &h_body,
  10640                                              "1234-5678",
  10641                                              &solved,
  10642                                              &retry_counter),
  10643     "Solving MFA challenge failed\n");
  10644   TEST_COND_RET_ON_FAIL (solved,
  10645                          "The correct TAN did not solve the challenge\n");
  10646   TEST_COND_RET_ON_FAIL (2 == retry_counter,
  10647                          "The correct TAN cost an attempt\n");
  10648   /* Re-confirming an already confirmed challenge is idempotent. */
  10649   TEST_COND_RET_ON_FAIL (
  10650     GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
  10651     TALER_MERCHANTDB_do_solve_mfa_challenge (pg,
  10652                                              challenge_id,
  10653                                              &h_body,
  10654                                              "0000-0000",
  10655                                              &solved,
  10656                                              &retry_counter),
  10657     "Solving MFA challenge failed\n");
  10658   TEST_COND_RET_ON_FAIL (solved,
  10659                          "Re-confirming a confirmed challenge is not"
  10660                          " idempotent\n");
  10661   TEST_COND_RET_ON_FAIL (2 == retry_counter,
  10662                          "Re-confirming a confirmed challenge cost an"
  10663                          " attempt\n");
  10664   /* ... but still only for the challenge's own body hash. */
  10665   TEST_COND_RET_ON_FAIL (
  10666     GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
  10667     TALER_MERCHANTDB_do_solve_mfa_challenge (pg,
  10668                                              challenge_id,
  10669                                              &other_h_body,
  10670                                              "1234-5678",
  10671                                              &solved,
  10672                                              &retry_counter),
  10673     "Confirmed challenge was addressable with a different body hash\n");
  10674   TEST_COND_RET_ON_FAIL (! solved,
  10675                          "Confirmed challenge solved with a different body"
  10676                          " hash\n");
  10677   return 0;
  10678 }
  10679 
  10680 
  10681 /**
  10682  * Test that COMMIT on a transaction PostgreSQL has already aborted is not
  10683  * reported as a success -- and that a clean COMMIT still returns exactly
  10684  * GNUNET_DB_STATUS_SUCCESS_NO_RESULTS, which is what every caller that
  10685  * compares the result against 0 relies on.
  10686  *
  10687  * @return 0 on success
  10688  */
  10689 static int
  10690 test_commit_on_aborted_transaction (void)
  10691 {
  10692   struct GNUNET_PQ_ExecuteStatement mk[] = {
  10693     GNUNET_PQ_make_execute ("CREATE TABLE test_commit_rollback (x INT)"),
  10694     GNUNET_PQ_EXECUTE_STATEMENT_END
  10695   };
  10696   struct GNUNET_PQ_ExecuteStatement bad[] = {
  10697     GNUNET_PQ_make_execute ("SELECT 1/0"),
  10698     GNUNET_PQ_EXECUTE_STATEMENT_END
  10699   };
  10700   struct GNUNET_PQ_ExecuteStatement chk[] = {
  10701     GNUNET_PQ_make_execute (
  10702       "DO $$ BEGIN"
  10703       " IF EXISTS (SELECT FROM pg_class"
  10704       "             WHERE relname='test_commit_rollback')"
  10705       " THEN RAISE EXCEPTION 'table survived a rollback';"
  10706       " END IF; END $$"),
  10707     GNUNET_PQ_EXECUTE_STATEMENT_END
  10708   };
  10709 
  10710   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
  10711                          TALER_MERCHANTDB_start (pg,
  10712                                                  "test-commit-aborted"),
  10713                          "Failed to start transaction\n");
  10714   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
  10715                          GNUNET_PQ_exec_statements (pg->conn,
  10716                                                     mk),
  10717                          "Failed to create the scratch table\n");
  10718   /* provoke an error; from here on the transaction is doomed */
  10719   TEST_COND_RET_ON_FAIL (GNUNET_OK !=
  10720                          GNUNET_PQ_exec_statements (pg->conn,
  10721                                                     bad),
  10722                          "Division by zero unexpectedly succeeded\n");
  10723   /* before the fix this returned GNUNET_DB_STATUS_SUCCESS_NO_RESULTS (0) */
  10724   TEST_COND_RET_ON_FAIL (0 >
  10725                          TALER_MERCHANTDB_commit (pg),
  10726                          "COMMIT of an aborted transaction reported success\n");
  10727   /* ...and the table is indeed gone, so 'success' would have been a lie */
  10728   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
  10729                          GNUNET_PQ_exec_statements (pg->conn,
  10730                                                     chk),
  10731                          "Scratch table survived the rollback\n");
  10732   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
  10733                          TALER_MERCHANTDB_start (pg,
  10734                                                  "test-commit-clean"),
  10735                          "Failed to start transaction\n");
  10736   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
  10737                          TALER_MERCHANTDB_commit (pg),
  10738                          "COMMIT changed its return value on success\n");
  10739   return 0;
  10740 }
  10741 
  10742 
  10743 /**
  10744  * Function that runs all tests.
  10745  *
  10746  * @return 0 on success, 1 otherwise.
  10747  */
  10748 static int
  10749 run_tests (void)
  10750 {
  10751   TEST_RET_ON_FAIL (test_instances ());
  10752   TEST_RET_ON_FAIL (test_products ());
  10753   TEST_RET_ON_FAIL (test_inventory_locks ());
  10754   TEST_RET_ON_FAIL (test_tokens ());
  10755   TEST_RET_ON_FAIL (test_orders ());
  10756   TEST_RET_ON_FAIL (test_deposits ());
  10757   TEST_RET_ON_FAIL (test_transfers ());
  10758   TEST_RET_ON_FAIL (test_refunds ());
  10759   TEST_RET_ON_FAIL (test_lookup_orders_all_filters ());
  10760   TEST_RET_ON_FAIL (test_kyc ());
  10761   TEST_RET_ON_FAIL (test_templates ());
  10762   TEST_RET_ON_FAIL (test_webhooks ());
  10763   TEST_RET_ON_FAIL (test_pending_webhooks ());
  10764   TEST_RET_ON_FAIL (test_inventory_deleted_webhook ());
  10765   TEST_RET_ON_FAIL (test_statistics ());
  10766   TEST_RET_ON_FAIL (test_mfa_challenges ());
  10767   TEST_RET_ON_FAIL (test_commit_on_aborted_transaction ());
  10768   return 0;
  10769 }
  10770 
  10771 
  10772 /**
  10773  * Main function that will be run by the scheduler.
  10774  *
  10775  * @param cls closure with config
  10776  */
  10777 static void
  10778 run (void *cls)
  10779 {
  10780   struct GNUNET_CONFIGURATION_Handle *cfg = cls;
  10781 
  10782   test_cfg = cfg;
  10783   if (NULL == (pg = TALER_MERCHANTDB_connect (cfg)))
  10784   {
  10785     result = 77;
  10786     return;
  10787   }
  10788   /* Drop the tables to cleanup anything that might cause issues */
  10789   (void) TALER_MERCHANTDB_drop_tables (pg);
  10790   if (GNUNET_OK !=
  10791       TALER_MERCHANTDB_create_tables (pg))
  10792   {
  10793     result = 77;
  10794     return;
  10795   }
  10796   /* Run the preflight */
  10797   TALER_MERCHANTDB_preflight (pg);
  10798 
  10799   result = run_tests ();
  10800   /* if (0 == result) result = run_test_templates (); */
  10801   if (0 == result)
  10802   {
  10803     if (GNUNET_OK !=
  10804         TALER_MERCHANTDB_drop_tables (pg))
  10805     {
  10806       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  10807                   "Dropping tables failed\n");
  10808       result = 1;
  10809     }
  10810   }
  10811   TALER_MERCHANTDB_disconnect (pg);
  10812   pg = NULL;
  10813 }
  10814 
  10815 
  10816 /**
  10817  * Entry point for the tests.
  10818  */
  10819 int
  10820 main (int argc,
  10821       char *const argv[])
  10822 {
  10823   struct GNUNET_CONFIGURATION_Handle *cfg;
  10824 
  10825   GNUNET_log_setup (argv[0],
  10826                     "DEBUG",
  10827                     NULL);
  10828   cfg = GNUNET_CONFIGURATION_create (TALER_MERCHANT_project_data ());
  10829   if (GNUNET_OK !=
  10830       GNUNET_CONFIGURATION_parse (cfg,
  10831                                   "test-merchantdb-postgres.conf"))
  10832   {
  10833     GNUNET_break (0);
  10834     return 2;
  10835   }
  10836   GNUNET_SCHEDULER_run (&run,
  10837                         cfg);
  10838   GNUNET_CONFIGURATION_destroy (cfg);
  10839   return result;
  10840 }
  10841 
  10842 
  10843 /* end of test_merchantdb.c */