merchant

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

test_merchantdb.c (432287B)


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