merchant

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

test_merchantdb.c (436880B)


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