merchant

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

test_merchantdb.c (394137B)


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