merchant

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

test_merchantdb.c (401094B)


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