merchant

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

test_merchantdb.c (439386B)


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