LCOV - code coverage report
Current view: top level - backenddb - test_merchantdb.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 87.4 % 2827 2470
Test Date: 2026-09-04 23:42:01 Functions: 100.0 % 203 203

            Line data    Source code
       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            2 : exec_sql (const char *sql)
     223              : {
     224            2 :   struct GNUNET_PQ_ExecuteStatement es[] = {
     225            2 :     GNUNET_PQ_make_execute (sql),
     226              :     GNUNET_PQ_EXECUTE_STATEMENT_END
     227              :   };
     228              : 
     229            2 :   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
     230              :                          GNUNET_PQ_exec_statements (pg->conn,
     231              :                                                     es),
     232              :                          "Direct SQL execution failed\n");
     233            2 :   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            6 : query_sql_num (const char *sql,
     247              :                uint64_t *num)
     248              : {
     249            6 :   struct GNUNET_PQ_QueryParam params[] = {
     250              :     GNUNET_PQ_query_param_end
     251              :   };
     252            6 :   struct GNUNET_PQ_ResultSpec rs[] = {
     253            6 :     GNUNET_PQ_result_spec_uint64 ("num",
     254              :                                   num),
     255              :     GNUNET_PQ_result_spec_end
     256              :   };
     257              : 
     258            6 :   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
     259              :                          GNUNET_PQ_prepare_anon (pg->conn,
     260              :                                                  sql),
     261              :                          "Preparing direct query failed\n");
     262            6 :   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            6 :   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            1 : query_sql_text (const char *sql,
     282              :                 char **txt)
     283              : {
     284            1 :   struct GNUNET_PQ_QueryParam params[] = {
     285              :     GNUNET_PQ_query_param_end
     286              :   };
     287            1 :   struct GNUNET_PQ_ResultSpec rs[] = {
     288            1 :     GNUNET_PQ_result_spec_string ("txt",
     289              :                                   txt),
     290              :     GNUNET_PQ_result_spec_end
     291              :   };
     292              : 
     293            1 :   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
     294              :                          GNUNET_PQ_prepare_anon (pg->conn,
     295              :                                                  sql),
     296              :                          "Preparing direct query failed\n");
     297            1 :   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            1 :   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           16 : make_instance (const char *instance_id,
     340              :                struct InstanceData *instance)
     341              : {
     342           16 :   memset (instance,
     343              :           0,
     344              :           sizeof (*instance));
     345           16 :   GNUNET_CRYPTO_eddsa_key_create (&instance->merchant_priv.eddsa_priv);
     346           16 :   GNUNET_CRYPTO_eddsa_key_get_public (&instance->merchant_priv.eddsa_priv,
     347              :                                       &instance->merchant_pub.eddsa_pub);
     348           16 :   instance->instance.id = (char *) instance_id;
     349           16 :   instance->instance.name = (char *) "Test";
     350           16 :   instance->instance.address = json_array ();
     351           16 :   GNUNET_assert (NULL != instance->instance.address);
     352           16 :   GNUNET_assert (0 == json_array_append_new (instance->instance.address,
     353              :                                              json_string ("123 Example St")));
     354           16 :   instance->instance.jurisdiction = json_array ();
     355           16 :   GNUNET_assert (NULL != instance->instance.jurisdiction);
     356           16 :   GNUNET_assert (0 == json_array_append_new (instance->instance.jurisdiction,
     357              :                                              json_string ("Ohio")));
     358           16 :   instance->instance.use_stefan = true;
     359              :   instance->instance.default_wire_transfer_delay =
     360           16 :     GNUNET_TIME_relative_get_minute_ ();
     361           16 :   instance->instance.default_pay_delay = GNUNET_TIME_UNIT_SECONDS;
     362           16 :   instance->instance.default_refund_delay = GNUNET_TIME_UNIT_MINUTES;
     363           16 : }
     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           15 : free_instance_data (struct InstanceData *instance)
     373              : {
     374           15 :   json_decref (instance->instance.address);
     375           15 :   json_decref (instance->instance.jurisdiction);
     376           15 : }
     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            8 : make_account (struct TALER_MERCHANTDB_AccountDetails *account)
     386              : {
     387            8 :   memset (account,
     388              :           0,
     389              :           sizeof (*account));
     390            8 :   GNUNET_CRYPTO_random_block (&account->h_wire.hash,
     391              :                               sizeof account->h_wire.hash);
     392            8 :   GNUNET_CRYPTO_random_block (&account->salt,
     393              :                               sizeof (account->salt));
     394              :   account->payto_uri.full_payto
     395            8 :     = (char *) "payto://x-taler-bank/bank.demo.taler.net/4";
     396            8 :   account->active = true;
     397            8 : }
     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           11 : check_instances_equal (const struct TALER_MERCHANTDB_InstanceSettings *a,
     459              :                        const struct TALER_MERCHANTDB_InstanceSettings *b)
     460              : {
     461           11 :   if ((0 != strcmp (a->id,
     462           11 :                     b->id)) ||
     463            9 :       (0 != strcmp (a->name,
     464           18 :                     b->name)) ||
     465            9 :       (1 != json_equal (a->address,
     466           18 :                         b->address)) ||
     467            9 :       (1 != json_equal (a->jurisdiction,
     468            9 :                         b->jurisdiction)) ||
     469            9 :       (a->use_stefan != b->use_stefan) ||
     470            9 :       (a->default_wire_transfer_delay.rel_value_us !=
     471            9 :        b->default_wire_transfer_delay.rel_value_us) ||
     472            9 :       (a->default_pay_delay.rel_value_us != b->default_pay_delay.rel_value_us))
     473            2 :     return 1;
     474            9 :   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            9 : 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            9 :   struct TestLookupInstances_Closure *cmp = cls;
     522              : 
     523            9 :   if (NULL == cmp)
     524            0 :     return;
     525            9 :   cmp->results_length += 1;
     526              :   /* Look through the closure and test each instance for equality */
     527           20 :   for (unsigned int i = 0; cmp->instances_to_cmp_length > i; ++i)
     528              :   {
     529           11 :     if (0 != check_instances_equal (cmp->instances_to_cmp[i].instance,
     530              :                                     is))
     531            2 :       continue;
     532            9 :     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           17 : test_insert_instance (const struct InstanceData *instance,
     546              :                       enum GNUNET_DB_QueryStatus expected_result)
     547              : {
     548           17 :   struct TALER_MERCHANTDB_InstanceAuthSettings ias = { 0 };
     549              : 
     550           17 :   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           17 :   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            2 : test_update_instance (const struct InstanceData *updated_data,
     570              :                       enum GNUNET_DB_QueryStatus expected_result)
     571              : {
     572            2 :   TEST_COND_RET_ON_FAIL (expected_result ==
     573              :                          TALER_MERCHANTDB_update_instance (pg,
     574              :                                                            &updated_data->instance),
     575              :                          "Update instance failed\n");
     576            2 :   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            9 : test_lookup_instances (bool active_only,
     591              :                        unsigned int instances_length,
     592              :                        struct InstanceWithAccounts instances[])
     593            9 : {
     594            9 :   unsigned int results_matching[GNUNET_NZL (instances_length)];
     595            9 :   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            9 :   memset (results_matching, 0, sizeof (unsigned int) * instances_length);
     602            9 :   if (0 > TALER_MERCHANTDB_iterate_instances (pg,
     603              :                                               active_only,
     604              :                                               &lookup_instances_cb,
     605              :                                               &cmp))
     606              :   {
     607            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     608              :                 "Lookup instances failed\n");
     609            0 :     return 1;
     610              :   }
     611            9 :   if (instances_length != cmp.results_length)
     612              :   {
     613            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     614              :                 "Lookup instances failed: incorrect number of results\n");
     615            0 :     return 1;
     616              :   }
     617           18 :   for (unsigned int i = 0; instances_length > i; ++i)
     618              :   {
     619            9 :     if (1 != cmp.results_matching[i])
     620              :     {
     621            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     622              :                   "Lookup instances failed: mismatched data\n");
     623            0 :       return 1;
     624              :     }
     625              :   }
     626            9 :   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            3 : test_delete_instance_private_key (const struct InstanceData *instance,
     639              :                                   enum GNUNET_DB_QueryStatus expected_result)
     640              : {
     641            3 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
     642            2 :   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            2 :   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            2 : test_purge_instance (const struct InstanceData *instance,
     660              :                      enum GNUNET_DB_QueryStatus expected_result)
     661              : {
     662            2 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
     663            2 :   TEST_COND_RET_ON_FAIL (expected_result ==
     664              :                          TALER_MERCHANTDB_delete_instance (pg,
     665              :                                                            instance->instance.id),
     666              :                          "Purge instance failed\n");
     667            2 :   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            9 : test_insert_account (const struct InstanceData *instance,
     681              :                      const struct TALER_MERCHANTDB_AccountDetails *account,
     682              :                      enum GNUNET_DB_QueryStatus expected_result)
     683              : {
     684            9 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
     685            8 :   TEST_COND_RET_ON_FAIL (expected_result ==
     686              :                          TALER_MERCHANTDB_insert_account (pg,
     687              :                                                           account),
     688              :                          "Insert account failed\n");
     689            8 :   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            2 : test_inactivate_account (const struct InstanceData *instance,
     702              :                          const struct TALER_MERCHANTDB_AccountDetails *account,
     703              :                          enum GNUNET_DB_QueryStatus expected_result)
     704              : {
     705            2 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
     706            1 :   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            1 :   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            1 : pre_test_instances (struct TestInstances_Closure *cls)
     740              : {
     741              :   /* Instance */
     742            1 :   make_instance ("test_instances_inst0",
     743              :                  &cls->instances[0]);
     744            1 :   make_instance ("test_instances_inst1",
     745              :                  &cls->instances[1]);
     746              : 
     747              :   /* Accounts */
     748            1 :   make_account (&cls->accounts[0]);
     749              :   cls->accounts[0].instance_id
     750            1 :     = cls->instances[0].instance.id;
     751            1 :   make_account (&cls->accounts[1]);
     752              :   cls->accounts[1].instance_id
     753            1 :     = cls->instances[1].instance.id;
     754            1 : }
     755              : 
     756              : 
     757              : /**
     758              :  * Handles all teardown after testing
     759              :  *
     760              :  * @cls the closure to free data from
     761              :  */
     762              : static void
     763            1 : post_test_instances (struct TestInstances_Closure *cls)
     764              : {
     765            1 :   free_instance_data (&cls->instances[0]);
     766            1 :   free_instance_data (&cls->instances[1]);
     767            1 : }
     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            1 : test_reconnect_search_path (const struct InstanceData *instance)
     788              : {
     789            1 :   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            1 :   int ret = 1;
     796              : 
     797            1 :   GNUNET_assert (NULL != i18n);
     798            1 :   TEST_SET_INSTANCE (instance->instance.id,
     799              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
     800            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     801            1 :       TALER_MERCHANTDB_insert_category (pg,
     802            1 :                                         instance->instance.id,
     803              :                                         "reconnect-before",
     804              :                                         i18n,
     805              :                                         &cat))
     806              :   {
     807            0 :     GNUNET_break (0);
     808            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     809              :                 "Insert category before reconnect failed\n");
     810            0 :     goto cleanup;
     811              :   }
     812              :   /* Find out which backend serves our connection. */
     813              :   {
     814            1 :     struct GNUNET_PQ_QueryParam params[] = {
     815              :       GNUNET_PQ_query_param_end
     816              :     };
     817            1 :     struct GNUNET_PQ_ResultSpec rs[] = {
     818            1 :       GNUNET_PQ_result_spec_uint64 ("pid",
     819              :                                     &backend_pid),
     820              :       GNUNET_PQ_result_spec_end
     821              :     };
     822              : 
     823            1 :     if ( (GNUNET_OK !=
     824            1 :           GNUNET_PQ_prepare_anon (pg->conn,
     825            1 :                                   "SELECT pg_backend_pid()::INT8 AS pid")) ||
     826              :          (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     827            1 :           GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
     828              :                                                     "",
     829              :                                                     params,
     830              :                                                     rs)) )
     831              :     {
     832            0 :       GNUNET_break (0);
     833            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     834              :                   "Failed to determine backend PID\n");
     835            0 :       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            1 :   GNUNET_snprintf (sql,
     841              :                    sizeof (sql),
     842              :                    "DO $$ BEGIN"
     843              :                    " PERFORM pg_terminate_backend(%llu);"
     844              :                    " END $$",
     845              :                    (unsigned long long) backend_pid);
     846            1 :   cpg = TALER_MERCHANTDB_connect (test_cfg);
     847            1 :   if (NULL == cpg)
     848              :   {
     849            0 :     GNUNET_break (0);
     850            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     851              :                 "Failed to open second database connection\n");
     852            0 :     goto cleanup;
     853              :   }
     854              :   {
     855            1 :     struct GNUNET_PQ_ExecuteStatement es[] = {
     856            1 :       GNUNET_PQ_make_execute (sql),
     857              :       GNUNET_PQ_EXECUTE_STATEMENT_END
     858              :     };
     859              :     enum GNUNET_GenericReturnValue res;
     860              : 
     861            1 :     res = GNUNET_PQ_exec_statements (cpg->conn,
     862              :                                      es);
     863            1 :     TALER_MERCHANTDB_disconnect (cpg);
     864            1 :     if (GNUNET_OK != res)
     865              :     {
     866            0 :       GNUNET_break (0);
     867            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     868              :                   "Failed to terminate our own backend\n");
     869            0 :       goto cleanup;
     870              :     }
     871              :   }
     872            1 :   gen = TMH_PG_prep_gen_;
     873            2 :   for (unsigned int i = 0; i<10; i++)
     874              :   {
     875            2 :     GNUNET_PQ_reconnect_if_down (pg->conn);
     876            2 :     if (gen != TMH_PG_prep_gen_)
     877            1 :       break;
     878            1 :     sleep (1);
     879              :   }
     880            1 :   if (gen == TMH_PG_prep_gen_)
     881              :   {
     882            0 :     GNUNET_break (0);
     883            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     884              :                 "Database did not reconnect\n");
     885            0 :     goto cleanup;
     886              :   }
     887              :   /* This is what the dispatcher does for the next request; it must
     888              :      restore the per-instance search_path. */
     889            1 :   TEST_SET_INSTANCE (instance->instance.id,
     890              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
     891            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     892            1 :       TALER_MERCHANTDB_insert_category (pg,
     893            1 :                                         instance->instance.id,
     894              :                                         "reconnect-after",
     895              :                                         i18n,
     896              :                                         &cat))
     897              :   {
     898            0 :     GNUNET_break (0);
     899            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     900              :                 "Insert category after reconnect failed\n");
     901            0 :     goto cleanup;
     902              :   }
     903            1 :   ret = 0;
     904            1 : cleanup:
     905            1 :   json_decref (i18n);
     906            1 :   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            1 : test_get_instance_auth_null (const struct InstanceData *instance)
     926              : {
     927              :   struct TALER_MERCHANTDB_InstanceAuthSettings ias;
     928              :   char sql[256];
     929              : 
     930            1 :   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            1 :                    instance->instance.id);
     937              :   {
     938            1 :     struct GNUNET_PQ_ExecuteStatement es[] = {
     939            1 :       GNUNET_PQ_make_execute (sql),
     940              :       GNUNET_PQ_EXECUTE_STATEMENT_END
     941              :     };
     942              : 
     943            1 :     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            1 :   memset (&ias,
     949              :           42,
     950              :           sizeof (ias));
     951            1 :   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            1 :   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            1 :   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            1 : test_insert_report_serial (const struct InstanceData *instance)
     980              : {
     981              :   uint64_t report_ids[3];
     982              : 
     983            1 :   TEST_SET_INSTANCE (instance->instance.id,
     984              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
     985            4 :   for (unsigned int i = 0; i<3; i++)
     986              :   {
     987            3 :     report_ids[i] = 0;
     988            3 :     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            3 :     TEST_COND_RET_ON_FAIL (0 != report_ids[i],
    1002              :                            "Insert report did not return a serial\n");
    1003            6 :     for (unsigned int j = 0; j<i; j++)
    1004            3 :       TEST_COND_RET_ON_FAIL (report_ids[i] != report_ids[j],
    1005              :                              "Insert report returned a duplicate serial\n");
    1006              :   }
    1007            1 :   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            1 : run_test_instances (struct TestInstances_Closure *cls)
    1019              : {
    1020            1 :   struct InstanceWithAccounts instances[2] = {
    1021              :     {
    1022              :       .accounts_length = 0,
    1023            1 :       .accounts = cls->accounts,
    1024            1 :       .instance = &cls->instances[0].instance
    1025              :     },
    1026              :     {
    1027              :       .accounts_length = 0,
    1028            1 :       .accounts = cls->accounts,
    1029            1 :       .instance = &cls->instances[1].instance
    1030              :     }
    1031              :   };
    1032              :   uint64_t account_serial;
    1033              : 
    1034              :   /* Test inserting an instance */
    1035            1 :   TEST_RET_ON_FAIL (test_insert_instance (&cls->instances[0],
    1036              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    1037              :   /* Test double insertion fails */
    1038            1 :   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            1 :   TEST_RET_ON_FAIL (test_lookup_instances (false,
    1042              :                                            1,
    1043              :                                            instances));
    1044              :   /* Test update instance */
    1045            1 :   cls->instances[0].instance.name = "Test - updated";
    1046            1 :   json_array_append_new (cls->instances[0].instance.address,
    1047              :                          json_pack ("{s:s, s:I}",
    1048              :                                     "this", "is",
    1049              :                                     "more data", 47));
    1050            1 :   json_array_append_new (cls->instances[0].instance.jurisdiction,
    1051              :                          json_pack ("{s:s}",
    1052              :                                     "vegetables", "bad"));
    1053            1 :   cls->instances[0].instance.use_stefan = false;
    1054              :   cls->instances[0].instance.default_wire_transfer_delay =
    1055            1 :     GNUNET_TIME_UNIT_HOURS;
    1056            1 :   cls->instances[0].instance.default_pay_delay = GNUNET_TIME_UNIT_MINUTES;
    1057              : 
    1058            1 :   TEST_RET_ON_FAIL (test_update_instance (&cls->instances[0],
    1059              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    1060            1 :   TEST_RET_ON_FAIL (test_lookup_instances (false,
    1061              :                                            1,
    1062              :                                            instances));
    1063            1 :   TEST_RET_ON_FAIL (test_update_instance (&cls->instances[1],
    1064              :                                           GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
    1065              :   /* Test account creation */
    1066            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_account (&cls->instances[1],
    1071              :                                          &cls->accounts[1],
    1072              :                                          GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
    1073            1 :   TEST_RET_ON_FAIL (test_insert_account (&cls->instances[0],
    1074              :                                          &cls->accounts[0],
    1075              :                                          GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
    1076            1 :   instances[0].accounts_length = 1;
    1077            1 :   TEST_RET_ON_FAIL (test_lookup_instances (false,
    1078              :                                            1,
    1079              :                                            instances));
    1080              :   /* Test deactivate account */
    1081            1 :   TEST_RET_ON_FAIL (test_inactivate_account (&cls->instances[0],
    1082              :                                              &cls->accounts[0],
    1083              :                                              GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    1084            1 :   TEST_RET_ON_FAIL (test_inactivate_account (&cls->instances[1],
    1085              :                                              &cls->accounts[1],
    1086              :                                              GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
    1087            1 :   cls->accounts[0].active = false;
    1088            1 :   TEST_RET_ON_FAIL (test_lookup_instances (false,
    1089              :                                            1,
    1090              :                                            instances));
    1091              :   /* Test lookup account */
    1092            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    1093            1 :       TALER_MERCHANTDB_get_account_serial (pg,
    1094            1 :                                            cls->instances[0].instance.id,
    1095              :                                            cls->accounts[0].payto_uri,
    1096              :                                            &account_serial))
    1097              :   {
    1098            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1099              :                 "Lookup account failed\n");
    1100            0 :     return 1;
    1101              :   }
    1102            1 :   if (1 != account_serial)
    1103              :   {
    1104            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1105              :                 "Lookup account failed: incorrect serial number found\n");
    1106            0 :     return 1;
    1107              :   }
    1108            1 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    1109            1 :       TALER_MERCHANTDB_get_account_serial (pg,
    1110            1 :                                            cls->instances[0].instance.id,
    1111            1 :                                            (struct TALER_FullPayto) {
    1112              :     (char *) "payto://other-uri"
    1113              :   },
    1114              :                                            &account_serial))
    1115              :   {
    1116            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1117              :                 "Lookup account failed: account found where there is none\n");
    1118            0 :     return 1;
    1119              :   }
    1120              :   /* Test that a reconnect does not lose the per-instance search_path */
    1121            1 :   TEST_RET_ON_FAIL (test_reconnect_search_path (&cls->instances[0]));
    1122              :   /* Test that every stored report gets its own serial */
    1123            1 :   TEST_RET_ON_FAIL (test_insert_report_serial (&cls->instances[0]));
    1124              :   /* Test that a NULL authentication hash/salt is not an error */
    1125            1 :   TEST_RET_ON_FAIL (test_get_instance_auth_null (&cls->instances[0]));
    1126              :   /* Test instance private key deletion */
    1127            1 :   TEST_RET_ON_FAIL (test_delete_instance_private_key (&cls->instances[0],
    1128              :                                                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    1129            1 :   TEST_RET_ON_FAIL (test_delete_instance_private_key (&cls->instances[1],
    1130              :                                                       GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
    1131            1 :   TEST_RET_ON_FAIL (test_lookup_instances (true,
    1132              :                                            0,
    1133              :                                            NULL));
    1134            1 :   TEST_RET_ON_FAIL (test_lookup_instances (false,
    1135              :                                            1,
    1136              :                                            instances));
    1137            1 :   TEST_RET_ON_FAIL (test_insert_instance (&cls->instances[1],
    1138              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    1139            1 :   TEST_RET_ON_FAIL (test_lookup_instances (false,
    1140              :                                            2,
    1141              :                                            instances));
    1142            1 :   TEST_RET_ON_FAIL (test_lookup_instances (true,
    1143              :                                            1,
    1144              :                                            &instances[1]));
    1145            1 :   TEST_RET_ON_FAIL (test_purge_instance (&cls->instances[1],
    1146              :                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    1147            1 :   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            1 :   TEST_RET_ON_FAIL (test_lookup_instances (false,
    1151              :                                            1,
    1152              :                                            instances));
    1153            1 :   return 0;
    1154              : }
    1155              : 
    1156              : 
    1157              : /**
    1158              :  * Function that tests instances.
    1159              :  *
    1160              :  * @return 0 on success, 1 otherwise.
    1161              :  */
    1162              : static int
    1163            1 : test_instances (void)
    1164              : {
    1165              :   struct TestInstances_Closure test_cls;
    1166              :   int test_result;
    1167              : 
    1168            1 :   pre_test_instances (&test_cls);
    1169            1 :   test_result = run_test_instances (&test_cls);
    1170            1 :   post_test_instances (&test_cls);
    1171            1 :   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            6 : make_product (const char *id,
    1203              :               struct ProductData *product)
    1204              : {
    1205              :   static struct TALER_Amount htwenty40;
    1206              : 
    1207            6 :   GNUNET_assert (GNUNET_OK ==
    1208              :                  TALER_string_to_amount ("EUR:120.40",
    1209              :                                          &htwenty40));
    1210              : 
    1211            6 :   memset (product,
    1212              :           0,
    1213              :           sizeof (*product));
    1214            6 :   product->id = id;
    1215            6 :   product->product.product_name = "Test product";
    1216            6 :   product->product.description = "This is a test product";
    1217            6 :   product->product.description_i18n = json_array ();
    1218            6 :   GNUNET_assert (NULL != product->product.description_i18n);
    1219            6 :   product->product.unit = "boxes";
    1220            6 :   product->product.minimum_age = 0;
    1221            6 :   product->product.price_array = &htwenty40;
    1222            6 :   product->product.price_array_length = 1;
    1223            6 :   product->product.taxes = json_array ();
    1224            6 :   GNUNET_assert (NULL != product->product.taxes);
    1225            6 :   product->product.total_stock = 55;
    1226            6 :   product->product.total_sold = 0;
    1227            6 :   product->product.total_lost = 0;
    1228            6 :   product->product.image = GNUNET_strdup ("");
    1229            6 :   GNUNET_assert (NULL != product->product.image);
    1230            6 :   product->product.address = json_array ();
    1231            6 :   GNUNET_assert (NULL != product->product.address);
    1232            6 :   product->product.next_restock = GNUNET_TIME_UNIT_ZERO_TS;
    1233            6 : }
    1234              : 
    1235              : 
    1236              : /**
    1237              :  * Frees memory associated with @e ProductData.
    1238              :  *
    1239              :  * @param product the container to free.
    1240              :  */
    1241              : static void
    1242            6 : free_product_data (struct ProductData *product)
    1243              : {
    1244            6 :   json_decref (product->product.description_i18n);
    1245            6 :   json_decref (product->product.taxes);
    1246            6 :   GNUNET_free (product->product.image);
    1247            6 :   json_decref (product->product.address);
    1248            6 : }
    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            2 : check_products_equal (const struct TALER_MERCHANTDB_ProductDetails *a,
    1260              :                       const struct TALER_MERCHANTDB_ProductDetails *b)
    1261              : {
    1262            2 :   if ((0 != strcmp (a->description,
    1263            4 :                     b->description)) ||
    1264            2 :       (1 != json_equal (a->description_i18n,
    1265            2 :                         b->description_i18n)) ||
    1266            2 :       (0 != strcmp (a->unit,
    1267            2 :                     b->unit)) ||
    1268            4 :       (a->price_array_length != b->price_array_length) ||
    1269            2 :       (1 != json_equal (a->taxes,
    1270            2 :                         b->taxes)) ||
    1271            2 :       (a->total_stock != b->total_stock) ||
    1272            2 :       (a->total_sold != b->total_sold) ||
    1273            2 :       (a->total_lost != b->total_lost) ||
    1274            2 :       (0 != strcmp (a->image,
    1275            4 :                     b->image)) ||
    1276            2 :       (1 != json_equal (a->address,
    1277            2 :                         b->address)) ||
    1278            2 :       (GNUNET_TIME_timestamp_cmp (a->next_restock,
    1279              :                                   !=,
    1280              :                                   b->next_restock)))
    1281              : 
    1282            0 :     return 1;
    1283            4 :   for (size_t i = 0; i<a->price_array_length; i++)
    1284            2 :     if ( (GNUNET_OK !=
    1285            2 :           TALER_amount_cmp_currency (&a->price_array[i],
    1286            4 :                                      &b->price_array[i])) ||
    1287            2 :          (0 != TALER_amount_cmp (&a->price_array[i],
    1288            2 :                                  &b->price_array[i])) )
    1289            0 :       return 1;
    1290            2 :   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           10 : 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           10 :   if (expect_no_instance)
    1323              :   {
    1324            1 :     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            1 :     return 0;
    1329              :   }
    1330            9 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    1331            9 :   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            9 :   if (expected_result > 0)
    1344              :   {
    1345            9 :     TEST_COND_RET_ON_FAIL (conflict == expect_conflict,
    1346              :                            "Conflict wrong");
    1347            9 :     TEST_COND_RET_ON_FAIL (no_cat == expected_no_cat,
    1348              :                            "Wrong category missing returned");
    1349              :   }
    1350            9 :   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            7 : 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            7 :   if (expect_no_instance)
    1385              :   {
    1386            0 :     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            0 :     return 0;
    1391              :   }
    1392            7 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    1393            7 :   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            7 :   if (expected_result > 0)
    1410              :   {
    1411            7 :     TEST_COND_RET_ON_FAIL (no_product == expect_no_product,
    1412              :                            "No product wrong");
    1413            7 :     TEST_COND_RET_ON_FAIL (lost_reduced == expect_lost_reduced,
    1414              :                            "No product wrong");
    1415            7 :     TEST_COND_RET_ON_FAIL (stocked_reduced == expect_stocked_reduced,
    1416              :                            "Stocked reduced wrong");
    1417            7 :     TEST_COND_RET_ON_FAIL (sold_reduced == expect_sold_reduced,
    1418              :                            "Sold reduced wrong");
    1419            7 :     TEST_COND_RET_ON_FAIL (no_cat == expected_no_cat,
    1420              :                            "Wrong category missing returned");
    1421              :   }
    1422            7 :   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            2 : test_lookup_product (const struct InstanceData *instance,
    1435              :                      const struct ProductData *product)
    1436              : {
    1437              :   struct TALER_MERCHANTDB_ProductDetails lookup_result;
    1438            2 :   size_t num_categories = 0;
    1439            2 :   uint64_t *categories = NULL;
    1440              : 
    1441            2 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    1442            2 :   if (0 > TALER_MERCHANTDB_get_product (pg,
    1443            2 :                                         instance->instance.id,
    1444            2 :                                         product->id,
    1445              :                                         &lookup_result,
    1446              :                                         &num_categories,
    1447              :                                         &categories))
    1448              :   {
    1449            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1450              :                 "Lookup product failed\n");
    1451            0 :     TALER_MERCHANTDB_product_details_free (&lookup_result);
    1452            0 :     return 1;
    1453              :   }
    1454            2 :   GNUNET_free (categories);
    1455              :   {
    1456            2 :     const struct TALER_MERCHANTDB_ProductDetails *to_cmp = &product->product;
    1457              : 
    1458            2 :     if (0 != check_products_equal (&lookup_result,
    1459              :                                    to_cmp))
    1460              :     {
    1461            0 :       GNUNET_break (0);
    1462            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1463              :                   "Lookup product failed: incorrect product returned\n");
    1464            0 :       TALER_MERCHANTDB_product_details_free (&lookup_result);
    1465            0 :       return 1;
    1466              :     }
    1467              :   }
    1468            2 :   TALER_MERCHANTDB_product_details_free (&lookup_result);
    1469            2 :   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            4 : lookup_products_cb (void *cls,
    1509              :                     uint64_t product_serial,
    1510              :                     const char *product_id)
    1511              : {
    1512            4 :   struct TestLookupProducts_Closure *cmp = cls;
    1513              : 
    1514            4 :   GNUNET_assert (product_serial > 0);
    1515            4 :   if (NULL == cmp)
    1516            0 :     return;
    1517            4 :   cmp->results_length += 1;
    1518           10 :   for (unsigned int i = 0; cmp->products_to_cmp_length > i; ++i)
    1519              :   {
    1520            6 :     if (0 == strcmp (cmp->products_to_cmp[i].id,
    1521              :                      product_id))
    1522            4 :       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            4 : test_lookup_products (const struct InstanceData *instance,
    1537              :                       unsigned int products_length,
    1538              :                       const struct ProductData *products)
    1539            4 : {
    1540            4 :   unsigned int results_matching[GNUNET_NZL (products_length)];
    1541            4 :   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            4 :   memset (results_matching, 0, sizeof (unsigned int) * products_length);
    1548            4 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    1549            4 :   if (0 > TALER_MERCHANTDB_iterate_products (pg,
    1550            4 :                                              instance->instance.id,
    1551              :                                              0,
    1552              :                                              20,
    1553              :                                              NULL,
    1554              :                                              NULL,
    1555              :                                              NULL,
    1556              :                                              0,
    1557              :                                              &lookup_products_cb,
    1558              :                                              &cls))
    1559              :   {
    1560            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1561              :                 "Lookup products failed\n");
    1562            0 :     return 1;
    1563              :   }
    1564            4 :   if (products_length != cls.results_length)
    1565              :   {
    1566            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1567              :                 "Lookup products failed: incorrect number of results\n");
    1568            0 :     return 1;
    1569              :   }
    1570            8 :   for (unsigned int i = 0; products_length > i; ++i)
    1571              :   {
    1572            4 :     if (1 != cls.results_matching[i])
    1573              :     {
    1574            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1575              :                   "Lookup products failed: mismatched data\n");
    1576            0 :       return 1;
    1577              :     }
    1578              :   }
    1579            4 :   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            5 : 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            5 :   bool not_found = false;
    1603            5 :   bool locked = false;
    1604              :   enum GNUNET_DB_QueryStatus qs;
    1605              : 
    1606            5 :   TEST_SET_INSTANCE (instance->instance.id,
    1607              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    1608            5 :   qs = TALER_MERCHANTDB_delete_product (pg,
    1609            5 :                                         instance->instance.id,
    1610            5 :                                         product->id,
    1611              :                                         force,
    1612              :                                         &not_found,
    1613              :                                         &locked);
    1614            5 :   TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs,
    1615              :                          "Delete product failed\n");
    1616            5 :   TEST_COND_RET_ON_FAIL (expect_not_found == not_found,
    1617              :                          "Delete product 'not_found' mismatch\n");
    1618            5 :   TEST_COND_RET_ON_FAIL (expect_locked == locked,
    1619              :                          "Delete product 'locked' mismatch\n");
    1620            5 :   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            1 : pre_test_products (struct TestProducts_Closure *cls)
    1648              : {
    1649              :   static struct TALER_Amount four95;
    1650              : 
    1651              :   /* Instance */
    1652            1 :   make_instance ("test_inst_products",
    1653              :                  &cls->instance);
    1654              : 
    1655              :   /* Products */
    1656            1 :   make_product ("test_products_pd_0",
    1657              :                 &cls->products[0]);
    1658              : 
    1659            1 :   make_product ("test_products_pd_1",
    1660              :                 &cls->products[1]);
    1661            1 :   cls->products[1].product.description = "This is a another test product";
    1662            1 :   cls->products[1].product.unit = "cans";
    1663            1 :   cls->products[1].product.minimum_age = 0;
    1664              : 
    1665            1 :   GNUNET_assert (GNUNET_OK ==
    1666              :                  TALER_string_to_amount ("EUR:4.95",
    1667              :                                          &four95));
    1668            1 :   cls->products[1].product.price_array = &four95;
    1669            1 :   cls->products[1].product.price_array_length = 1;
    1670            1 :   cls->products[1].product.total_stock = 5001;
    1671            1 : }
    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            1 : post_test_products (struct TestProducts_Closure *cls)
    1681              : {
    1682            1 :   free_instance_data (&cls->instance);
    1683            1 :   free_product_data (&cls->products[0]);
    1684            1 :   free_product_data (&cls->products[1]);
    1685            1 : }
    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            1 : test_insert_duplicate_conflicts (const struct InstanceData *instance)
    1707              : {
    1708            1 :   json_t *i18n = json_object ();
    1709            1 :   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            1 :   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            1 :   int ret = 1;
    1724              : 
    1725            1 :   GNUNET_assert (NULL != i18n);
    1726            1 :   TEST_SET_INSTANCE (instance->instance.id,
    1727              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    1728            1 :   if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    1729            1 :         TALER_MERCHANTDB_insert_category (pg,
    1730            1 :                                           instance->instance.id,
    1731              :                                           "duplicate-conflict",
    1732              :                                           i18n,
    1733            1 :                                           &cat)) ||
    1734              :        (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    1735            1 :         TALER_MERCHANTDB_insert_otp_device (pg,
    1736            1 :                                             instance->instance.id,
    1737              :                                             "duplicate-conflict",
    1738            1 :                                             &td)) ||
    1739              :        (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    1740            1 :         TALER_MERCHANTDB_insert_webhook (pg,
    1741            1 :                                          instance->instance.id,
    1742              :                                          "duplicate-conflict",
    1743              :                                          &wb)) )
    1744              :   {
    1745            0 :     GNUNET_break (0);
    1746            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1747              :                 "Initial insert for the conflict test failed\n");
    1748            0 :     goto cleanup;
    1749              :   }
    1750            1 :   if (GNUNET_OK !=
    1751            1 :       TALER_MERCHANTDB_start (pg,
    1752              :                               "insert duplicate conflicts"))
    1753              :   {
    1754            0 :     GNUNET_break (0);
    1755            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1756              :                 "Failed to start transaction\n");
    1757            0 :     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            1 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    1763            1 :       TALER_MERCHANTDB_insert_category (pg,
    1764            1 :                                         instance->instance.id,
    1765              :                                         "duplicate-conflict",
    1766              :                                         i18n,
    1767              :                                         &cat))
    1768              :   {
    1769            0 :     GNUNET_break (0);
    1770            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1771              :                 "Duplicate category insert not reported as 'no results'\n");
    1772            0 :     goto rollback;
    1773              :   }
    1774            1 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    1775            1 :       TALER_MERCHANTDB_insert_otp_device (pg,
    1776            1 :                                           instance->instance.id,
    1777              :                                           "duplicate-conflict",
    1778              :                                           &td))
    1779              :   {
    1780            0 :     GNUNET_break (0);
    1781            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1782              :                 "Duplicate OTP device insert not reported as 'no results'\n");
    1783            0 :     goto rollback;
    1784              :   }
    1785            1 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    1786            1 :       TALER_MERCHANTDB_insert_webhook (pg,
    1787            1 :                                        instance->instance.id,
    1788              :                                        "duplicate-conflict",
    1789              :                                        &wb))
    1790              :   {
    1791            0 :     GNUNET_break (0);
    1792            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1793              :                 "Duplicate webhook insert not reported as 'no results'\n");
    1794            0 :     goto rollback;
    1795              :   }
    1796              :   /* ... and the transaction must still be able to do useful work. */
    1797            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    1798            1 :       TALER_MERCHANTDB_insert_category (pg,
    1799            1 :                                         instance->instance.id,
    1800              :                                         "duplicate-conflict-other",
    1801              :                                         i18n,
    1802              :                                         &cat))
    1803              :   {
    1804            0 :     GNUNET_break (0);
    1805            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1806              :                 "Insert after duplicate conflict failed\n");
    1807            0 :     goto rollback;
    1808              :   }
    1809            1 :   if (0 >
    1810            1 :       TALER_MERCHANTDB_commit (pg))
    1811              :   {
    1812            0 :     GNUNET_break (0);
    1813            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1814              :                 "Commit after duplicate conflict failed\n");
    1815            0 :     goto cleanup;
    1816              :   }
    1817            1 :   ret = 0;
    1818            1 :   goto cleanup;
    1819            0 : rollback:
    1820            0 :   TALER_MERCHANTDB_rollback (pg);
    1821            1 : cleanup:
    1822            1 :   json_decref (i18n);
    1823            1 :   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            1 : test_update_category_conflict (const struct InstanceData *instance)
    1839              : {
    1840            1 :   json_t *i18n = json_object ();
    1841              :   uint64_t cat1;
    1842              :   uint64_t cat2;
    1843            1 :   bool conflict = true;
    1844            1 :   int ret = 1;
    1845              : 
    1846            1 :   GNUNET_assert (NULL != i18n);
    1847            1 :   TEST_SET_INSTANCE (instance->instance.id,
    1848              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    1849            1 :   if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    1850            1 :         TALER_MERCHANTDB_insert_category (pg,
    1851            1 :                                           instance->instance.id,
    1852              :                                           "category-conflict-a",
    1853              :                                           i18n,
    1854            1 :                                           &cat1)) ||
    1855              :        (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    1856            1 :         TALER_MERCHANTDB_insert_category (pg,
    1857            1 :                                           instance->instance.id,
    1858              :                                           "category-conflict-b",
    1859              :                                           i18n,
    1860              :                                           &cat2)) )
    1861              :   {
    1862            0 :     GNUNET_break (0);
    1863            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1864              :                 "Insert category failed\n");
    1865            0 :     goto cleanup;
    1866              :   }
    1867              :   /* Renaming 'a' to 'b' must be a conflict, not 'unknown category'. */
    1868            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    1869            1 :       TALER_MERCHANTDB_update_category (pg,
    1870            1 :                                         instance->instance.id,
    1871              :                                         cat1,
    1872              :                                         "category-conflict-b",
    1873              :                                         i18n,
    1874              :                                         &conflict))
    1875              :   {
    1876            0 :     GNUNET_break (0);
    1877            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1878              :                 "Conflicting category rename not reported as conflict\n");
    1879            0 :     goto cleanup;
    1880              :   }
    1881            1 :   if (! conflict)
    1882              :   {
    1883            0 :     GNUNET_break (0);
    1884            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1885              :                 "Conflicting category rename did not set 'conflict'\n");
    1886            0 :     goto cleanup;
    1887              :   }
    1888              :   /* ... and the connection must still be usable afterwards. */
    1889            1 :   conflict = true;
    1890            1 :   if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    1891            1 :         TALER_MERCHANTDB_update_category (pg,
    1892            1 :                                           instance->instance.id,
    1893              :                                           cat1,
    1894              :                                           "category-conflict-c",
    1895              :                                           i18n,
    1896            1 :                                           &conflict)) ||
    1897              :        conflict)
    1898              :   {
    1899            0 :     GNUNET_break (0);
    1900            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1901              :                 "Category rename after conflict failed\n");
    1902            0 :     goto cleanup;
    1903              :   }
    1904              :   /* Unknown category must still be reported as 'no results'. */
    1905            1 :   conflict = true;
    1906            1 :   if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    1907            1 :         TALER_MERCHANTDB_update_category (pg,
    1908            1 :                                           instance->instance.id,
    1909            1 :                                           cat1 + cat2 + 424242,
    1910              :                                           "category-conflict-d",
    1911              :                                           i18n,
    1912            1 :                                           &conflict)) ||
    1913              :        conflict)
    1914              :   {
    1915            0 :     GNUNET_break (0);
    1916            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1917              :                 "Rename of unknown category not reported as 'no results'\n");
    1918            0 :     goto cleanup;
    1919              :   }
    1920            1 :   ret = 0;
    1921            1 : cleanup:
    1922            1 :   json_decref (i18n);
    1923            1 :   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            1 : run_test_products (struct TestProducts_Closure *cls)
    1935              : {
    1936              :   struct GNUNET_Uuid uuid;
    1937              :   struct GNUNET_TIME_Timestamp refund_deadline =
    1938            1 :     GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_WEEKS);
    1939              : 
    1940              :   /* Test that insert without an instance fails */
    1941            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (test_update_category_conflict (&cls->instance));
    1954              :   /* Test that duplicate inserts do not poison the transaction */
    1955            1 :   TEST_RET_ON_FAIL (test_insert_duplicate_conflicts (&cls->instance));
    1956              :   /* Test inserting a product */
    1957            1 :   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            1 :   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            1 :     uint64_t cat = 42;
    1985              : 
    1986            1 :     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            1 :     struct ProductData conflicting = cls->products[1];
    2005              : 
    2006            1 :     conflicting.id = cls->products[0].id;
    2007            1 :     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            1 :   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            1 :     size_t num_categories = 0;
    2022            1 :     uint64_t *categories = NULL;
    2023              : 
    2024            1 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    2025            1 :         TALER_MERCHANTDB_get_product (pg,
    2026            1 :                                       cls->instance.instance.id,
    2027              :                                       "nonexistent_product",
    2028              :                                       NULL,
    2029              :                                       &num_categories,
    2030              :                                       &categories))
    2031              :     {
    2032            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2033              :                   "Lookup product failed\n");
    2034            0 :       return 1;
    2035              :     }
    2036            1 :     GNUNET_free (categories);
    2037              :   }
    2038              :   /* Test product update */
    2039            1 :   cls->products[0].product.description =
    2040              :     "This is a test product that has been updated!";
    2041            1 :   GNUNET_assert (0 ==
    2042              :                  json_array_append_new (
    2043              :                    cls->products[0].product.description_i18n,
    2044              :                    json_string (
    2045              :                      "description in another language")));
    2046            1 :   cls->products[0].product.unit = "barrels";
    2047              :   {
    2048              :     static struct TALER_Amount seven68;
    2049              : 
    2050            1 :     GNUNET_assert (GNUNET_OK ==
    2051              :                    TALER_string_to_amount ("EUR:7.68",
    2052              :                                            &seven68));
    2053            1 :     cls->products[0].product.price_array = &seven68;
    2054            1 :     cls->products[0].product.price_array_length = 1;
    2055              :   }
    2056            1 :   GNUNET_assert (0 ==
    2057              :                  json_array_append_new (cls->products[0].product.taxes,
    2058              :                                         json_string ("2% sales tax")));
    2059            1 :   cls->products[0].product.total_stock = 100;
    2060            1 :   cls->products[0].product.total_sold = 0; /* will be ignored! */
    2061            1 :   cls->products[0].product.total_lost = 7;
    2062            1 :   GNUNET_free (cls->products[0].product.image);
    2063            1 :   cls->products[0].product.image = GNUNET_strdup ("image");
    2064            1 :   GNUNET_assert (0 ==
    2065              :                  json_array_append_new (cls->products[0].product.address,
    2066              :                                         json_string ("444 Some Street")));
    2067            1 :   cls->products[0].product.next_restock = GNUNET_TIME_timestamp_get ();
    2068            1 :   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            1 :   cls->products[0].product.total_stock_frac = 500000;
    2085            1 :   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            1 :     struct ProductData frac_dec = cls->products[0];
    2099              : 
    2100            1 :     frac_dec.product.total_stock_frac = 400000;
    2101            1 :     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            1 :     struct ProductData stock_dec = cls->products[0];
    2116              : 
    2117            1 :     stock_dec.product.total_stock = 40;
    2118            1 :     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            1 :     struct ProductData lost_dec = cls->products[0];
    2133              : 
    2134            1 :     lost_dec.product.total_lost = 1;
    2135            1 :     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            1 :   TEST_RET_ON_FAIL (test_lookup_product (&cls->instance,
    2149              :                                          &cls->products[0]));
    2150            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (test_lookup_products (&cls->instance,
    2172              :                                           2,
    2173              :                                           cls->products));
    2174              :   /* Test locking */
    2175            1 :   uuid.value[0] = 0x1287346a;
    2176            1 :   if (0 != TALER_MERCHANTDB_insert_inventory_lock (pg,
    2177            1 :                                                    cls->instance.instance.id,
    2178              :                                                    cls->products[0].id,
    2179              :                                                    &uuid,
    2180              :                                                    256,
    2181              :                                                    0,
    2182              :                                                    refund_deadline))
    2183              :   {
    2184            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2185              :                 "Lock product failed\n");
    2186            0 :     return 1;
    2187              :   }
    2188            1 :   if (1 != TALER_MERCHANTDB_insert_inventory_lock (pg,
    2189            1 :                                                    cls->instance.instance.id,
    2190              :                                                    cls->products[0].id,
    2191              :                                                    &uuid,
    2192              :                                                    1,
    2193              :                                                    0,
    2194              :                                                    refund_deadline))
    2195              :   {
    2196            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2197              :                 "Lock product failed\n");
    2198            0 :     return 1;
    2199              :   }
    2200              :   /* Test product deletion of an unlocked product */
    2201            1 :   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            1 :   TEST_RET_ON_FAIL (test_delete_product (&cls->instance,
    2208              :                                          &cls->products[1],
    2209              :                                          false,
    2210              :                                          true,
    2211              :                                          false));
    2212            1 :   TEST_RET_ON_FAIL (test_lookup_products (&cls->instance,
    2213              :                                           1,
    2214              :                                           cls->products));
    2215              :   /* Test unlocking */
    2216            1 :   if (1 != TALER_MERCHANTDB_delete_inventory_lock (pg,
    2217              :                                                    &uuid))
    2218              :   {
    2219            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2220              :                 "Unlock inventory failed\n");
    2221            0 :     return 1;
    2222              :   }
    2223            1 :   if (0 != TALER_MERCHANTDB_delete_inventory_lock (pg,
    2224              :                                                    &uuid))
    2225              :   {
    2226            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2227              :                 "Unlock inventory failed\n");
    2228            0 :     return 1;
    2229              :   }
    2230              :   /* Re-lock products[0] to exercise deletion of a locked product */
    2231            1 :   if (1 != TALER_MERCHANTDB_insert_inventory_lock (pg,
    2232            1 :                                                    cls->instance.instance.id,
    2233              :                                                    cls->products[0].id,
    2234              :                                                    &uuid,
    2235              :                                                    1,
    2236              :                                                    0,
    2237              :                                                    refund_deadline))
    2238              :   {
    2239            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2240              :                 "Lock product failed\n");
    2241            0 :     return 1;
    2242              :   }
    2243              :   /* Deletion without force must be refused while the product is locked */
    2244            1 :   TEST_RET_ON_FAIL (test_delete_product (&cls->instance,
    2245              :                                          &cls->products[0],
    2246              :                                          false,
    2247              :                                          false,
    2248              :                                          true));
    2249            1 :   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            1 :   TEST_RET_ON_FAIL (test_delete_product (&cls->instance,
    2254              :                                          &cls->products[0],
    2255              :                                          true,
    2256              :                                          false,
    2257              :                                          false));
    2258            1 :   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            1 :   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            1 :   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            1 : test_products (void)
    2294              : {
    2295              :   struct TestProducts_Closure test_cls;
    2296              :   int test_result;
    2297              : 
    2298            1 :   pre_test_products (&test_cls);
    2299            1 :   test_result = run_test_products (&test_cls);
    2300            1 :   post_test_products (&test_cls);
    2301            1 :   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            9 : 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            9 :   size_t num_categories = 0;
    2343            9 :   uint64_t *categories = NULL;
    2344            9 :   int ret = 0;
    2345              : 
    2346            9 :   memset (&pd,
    2347              :           0,
    2348              :           sizeof (pd));
    2349            9 :   TEST_SET_INSTANCE (instance->instance.id,
    2350              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    2351            9 :   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            9 :   GNUNET_free (categories);
    2360            9 :   if ( (expected_locked != pd.total_locked) ||
    2361            9 :        (expected_locked_frac != pd.total_locked_frac) )
    2362              :   {
    2363            0 :     GNUNET_break (0);
    2364            0 :     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            0 :     ret = 1;
    2373              :   }
    2374            9 :   TALER_MERCHANTDB_product_details_free (&pd);
    2375            9 :   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            9 : 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            9 :     = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_WEEKS);
    2398              : 
    2399            9 :   TEST_SET_INSTANCE (instance->instance.id,
    2400              :                      expected_result);
    2401            9 :   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            9 :   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            1 : pre_test_locks (struct TestLocks_Closure *cls)
    2422              : {
    2423            1 :   make_instance ("test_inst_locks",
    2424              :                  &cls->instance);
    2425            1 :   make_product ("test_locks_pd_0",
    2426              :                 &cls->products[0]);
    2427            1 :   cls->products[0].product.total_stock = 100;
    2428            1 :   make_product ("test_locks_pd_1",
    2429              :                 &cls->products[1]);
    2430            1 :   cls->products[1].product.total_stock = INT64_MAX;
    2431            1 : }
    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            1 : post_test_locks (struct TestLocks_Closure *cls)
    2441              : {
    2442            1 :   free_instance_data (&cls->instance);
    2443            1 :   free_product_data (&cls->products[0]);
    2444            1 :   free_product_data (&cls->products[1]);
    2445            1 : }
    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            1 : run_test_locks (struct TestLocks_Closure *cls)
    2456              : {
    2457              :   struct GNUNET_Uuid uuid1;
    2458              :   struct GNUNET_Uuid uuid2;
    2459              : 
    2460            1 :   memset (&uuid1,
    2461              :           0,
    2462              :           sizeof (uuid1));
    2463            1 :   memset (&uuid2,
    2464              :           0,
    2465              :           sizeof (uuid2));
    2466            1 :   uuid1.value[0] = 0x11111111;
    2467            1 :   uuid2.value[0] = 0x22222222;
    2468            1 :   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
    2469              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    2470            1 :   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            1 :   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            1 :   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            1 :   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            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (test_product_locked (&cls->instance,
    2509              :                                          &cls->products[0],
    2510              :                                          10,
    2511              :                                          0));
    2512            1 :   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            1 :   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            1 :   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            1 :   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            1 :   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            1 :   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            1 :   TEST_SET_INSTANCE (cls->instance.instance.id,
    2546              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    2547            1 :   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            1 :   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            1 :     struct ProductData unknown = cls->products[0];
    2558              : 
    2559            1 :     unknown.id = "test_locks_pd_unknown";
    2560            1 :     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            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (test_product_locked (&cls->instance,
    2586              :                                          &cls->products[1],
    2587              :                                          INT64_MAX,
    2588              :                                          0));
    2589            1 :   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            1 :   TEST_RET_ON_FAIL (test_product_locked (&cls->instance,
    2596              :                                          &cls->products[1],
    2597              :                                          INT64_MAX,
    2598              :                                          999999));
    2599            1 :   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            1 : test_inventory_locks (void)
    2610              : {
    2611              :   struct TestLocks_Closure test_cls;
    2612              :   int test_result;
    2613              : 
    2614            1 :   pre_test_locks (&test_cls);
    2615            1 :   test_result = run_test_locks (&test_cls);
    2616            1 :   post_test_locks (&test_cls);
    2617            1 :   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            2 : make_blinded_token_sig (struct GNUNET_CRYPTO_BlindedSignature *bs,
    2671              :                         struct TALER_BlindedTokenIssueSignature *sig)
    2672              : {
    2673            2 :   memset (bs,
    2674              :           0,
    2675              :           sizeof (*bs));
    2676            2 :   bs->cipher = GNUNET_CRYPTO_BSA_CS;
    2677            2 :   bs->rc = 1;
    2678            2 :   GNUNET_CRYPTO_random_block (&bs->details.blinded_cs_answer,
    2679              :                               sizeof (bs->details.blinded_cs_answer));
    2680            2 :   sig->signature = bs;
    2681            2 : }
    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            1 : make_token_issue_sig (struct GNUNET_CRYPTO_UnblindedSignature *ub,
    2692              :                       struct TALER_TokenIssueSignature *sig)
    2693              : {
    2694            1 :   memset (ub,
    2695              :           0,
    2696              :           sizeof (*ub));
    2697            1 :   ub->cipher = GNUNET_CRYPTO_BSA_CS;
    2698            1 :   ub->rc = 1;
    2699            1 :   GNUNET_CRYPTO_random_block (&ub->details.cs_signature,
    2700              :                               sizeof (ub->details.cs_signature));
    2701            1 :   sig->signature = ub;
    2702            1 : }
    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            7 : 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            7 :   int ret = 0;
    2720              : 
    2721            7 :   memset (&details,
    2722              :           0,
    2723              :           sizeof (details));
    2724            7 :   TEST_SET_INSTANCE (cls->instance.instance.id,
    2725              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    2726            7 :   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            7 :   if ( (expected_issued != details.issued) ||
    2734            7 :        (expected_used != details.used) )
    2735              :   {
    2736            0 :     GNUNET_break (0);
    2737            0 :     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            0 :     ret = 1;
    2745              :   }
    2746            7 :   TALER_MERCHANTDB_token_family_details_free (&details);
    2747            7 :   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            4 : 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            4 :   bool no_family = true;
    2773              : 
    2774            4 :   TEST_SET_INSTANCE (cls->instance.instance.id,
    2775              :                      expected_result);
    2776            4 :   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            4 :   TEST_COND_RET_ON_FAIL (expect_no_family == no_family,
    2785              :                          "Insert issued token 'no_family' mismatch\n");
    2786            4 :   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            4 : 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            4 :   bool no_family = true;
    2812              : 
    2813            4 :   TEST_SET_INSTANCE (cls->instance.instance.id,
    2814              :                      expected_result);
    2815            4 :   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            4 :   TEST_COND_RET_ON_FAIL (expect_no_family == no_family,
    2826              :                          "Insert used token 'no_family' mismatch\n");
    2827            4 :   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            1 : pre_test_tokens (struct TestTokens_Closure *cls)
    2838              : {
    2839            1 :   make_instance ("test_inst_tokens",
    2840              :                  &cls->instance);
    2841            1 :   memset (&cls->family,
    2842              :           0,
    2843              :           sizeof (cls->family));
    2844            1 :   cls->family.slug = (char *) "test_tokens_family";
    2845            1 :   cls->family.name = (char *) "Test token family";
    2846            1 :   cls->family.description = (char *) "This is a test token family";
    2847            1 :   cls->family.description_i18n = json_object ();
    2848            1 :   GNUNET_assert (NULL != cls->family.description_i18n);
    2849            1 :   cls->family.valid_after = GNUNET_TIME_timestamp_get ();
    2850              :   cls->family.valid_before
    2851            1 :     = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_YEARS);
    2852            1 :   cls->family.duration = GNUNET_TIME_UNIT_DAYS;
    2853            1 :   cls->family.validity_granularity = GNUNET_TIME_UNIT_DAYS;
    2854            1 :   cls->family.start_offset = GNUNET_TIME_UNIT_ZERO;
    2855            1 :   cls->family.kind = TALER_MERCHANTDB_TFK_Discount;
    2856            1 :   GNUNET_CRYPTO_blind_sign_keys_create (&cls->priv.private_key,
    2857              :                                         &cls->pub.public_key,
    2858              :                                         GNUNET_CRYPTO_BSA_CS);
    2859            1 :   cls->h_pub.hash = cls->pub.public_key->pub_key_hash;
    2860            1 :   GNUNET_CRYPTO_random_block (&cls->h_contract_terms,
    2861              :                               sizeof (cls->h_contract_terms));
    2862            1 : }
    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            1 : post_test_tokens (struct TestTokens_Closure *cls)
    2872              : {
    2873            1 :   GNUNET_CRYPTO_blind_sign_pub_decref (cls->pub.public_key);
    2874            1 :   GNUNET_CRYPTO_blind_sign_priv_decref (cls->priv.private_key);
    2875            1 :   json_decref (cls->family.description_i18n);
    2876            1 :   free_instance_data (&cls->instance);
    2877            1 : }
    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            1 : 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            1 :   make_blinded_token_sig (&bs1,
    2900              :                           &blind_sig1);
    2901            1 :   make_blinded_token_sig (&bs2,
    2902              :                           &blind_sig2);
    2903            1 :   make_token_issue_sig (&ub1,
    2904              :                         &issue_sig);
    2905            1 :   GNUNET_CRYPTO_random_block (&use_pub,
    2906              :                               sizeof (use_pub));
    2907            1 :   GNUNET_CRYPTO_random_block (&use_sig,
    2908              :                               sizeof (use_sig));
    2909            1 :   GNUNET_CRYPTO_random_block (&other_sig,
    2910              :                               sizeof (other_sig));
    2911              :   /* Set up instance, token family and token family key */
    2912            1 :   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
    2913              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    2914            1 :   TEST_SET_INSTANCE (cls->instance.instance.id,
    2915              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    2916            1 :   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            1 :   TEST_SET_INSTANCE (cls->instance.instance.id,
    2924              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    2925            1 :   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            1 :   TEST_RET_ON_FAIL (test_token_family_counters (cls,
    2937              :                                                 0,
    2938              :                                                 0));
    2939              :   /* Issuing a token bumps the 'issued' counter */
    2940            1 :   TEST_RET_ON_FAIL (test_insert_issued_token (
    2941              :                       cls,
    2942              :                       &blind_sig1,
    2943              :                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
    2944              :                       false));
    2945            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_issued_token (
    2951              :                       cls,
    2952              :                       &blind_sig1,
    2953              :                       GNUNET_DB_STATUS_SUCCESS_NO_RESULTS,
    2954              :                       false));
    2955            1 :   TEST_RET_ON_FAIL (test_token_family_counters (cls,
    2956              :                                                 1,
    2957              :                                                 0));
    2958              :   /* A different blind signature is a different token */
    2959            1 :   TEST_RET_ON_FAIL (test_insert_issued_token (
    2960              :                       cls,
    2961              :                       &blind_sig2,
    2962              :                       GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
    2963              :                       false));
    2964            1 :   TEST_RET_ON_FAIL (test_token_family_counters (cls,
    2965              :                                                 2,
    2966              :                                                 0));
    2967              :   /* Spending a token bumps the 'used' counter */
    2968            1 :   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            1 :   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            1 :   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            1 :   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            1 :   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            1 :   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            1 :   TEST_SET_INSTANCE (cls->instance.instance.id,
    3007              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    3008            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_issued_token (
    3015              :                       cls,
    3016              :                       &blind_sig1,
    3017              :                       GNUNET_DB_STATUS_SUCCESS_NO_RESULTS,
    3018              :                       true));
    3019            1 :   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            1 :   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            1 : test_tokens (void)
    3037              : {
    3038              :   struct TestTokens_Closure test_cls;
    3039              :   int test_result;
    3040              : 
    3041            1 :   pre_test_tokens (&test_cls);
    3042            1 :   test_result = run_test_tokens (&test_cls);
    3043            1 :   post_test_tokens (&test_cls);
    3044            1 :   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           76 : make_order (const char *order_id,
    3086              :             struct OrderData *order)
    3087              : {
    3088              :   struct GNUNET_TIME_Timestamp refund_deadline;
    3089              : 
    3090           76 :   order->id = order_id;
    3091           76 :   order->contract = json_object ();
    3092           76 :   GNUNET_assert (NULL != order->contract);
    3093           76 :   order->pay_deadline = GNUNET_TIME_relative_to_timestamp (
    3094              :     GNUNET_TIME_UNIT_DAYS);
    3095           76 :   GNUNET_CRYPTO_random_block (&order->claim_token,
    3096              :                               sizeof (order->claim_token));
    3097           76 :   refund_deadline = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_WEEKS);
    3098           76 :   GNUNET_assert (0 ==
    3099              :                  json_object_set_new (order->contract,
    3100              :                                       "fulfillment_url",
    3101              :                                       json_string ("a")));
    3102           76 :   GNUNET_assert (0 ==
    3103              :                  json_object_set_new (order->contract,
    3104              :                                       "summary",
    3105              :                                       json_string ("Test order")));
    3106           76 :   GNUNET_assert (0 ==
    3107              :                  json_object_set_new (order->contract,
    3108              :                                       "order_id",
    3109              :                                       json_string (order_id)));
    3110           76 :   GNUNET_assert (0 ==
    3111              :                  json_object_set_new (
    3112              :                    order->contract,
    3113              :                    "pay_deadline",
    3114              :                    GNUNET_JSON_from_timestamp (order->pay_deadline))
    3115              :                  );
    3116           76 :   GNUNET_assert (0 ==
    3117              :                  json_object_set_new (order->contract,
    3118              :                                       "refund_deadline",
    3119              :                                       GNUNET_JSON_from_timestamp (
    3120              :                                         refund_deadline)));
    3121           76 : }
    3122              : 
    3123              : 
    3124              : /**
    3125              :  * Frees memory associated with an order.
    3126              :  *
    3127              :  * @param the order to free.
    3128              :  */
    3129              : static void
    3130           74 : free_order_data (struct OrderData *order)
    3131              : {
    3132           74 :   json_decref (order->contract);
    3133           74 : }
    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           78 : 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           78 :   memset (&h_post,
    3152              :           42,
    3153              :           sizeof (h_post));
    3154           78 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    3155           78 :   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           78 :   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            1 : test_lookup_order (const struct InstanceData *instance,
    3180              :                    const struct OrderData *order)
    3181              : {
    3182              :   struct TALER_ClaimTokenP ct;
    3183            1 :   json_t *lookup_terms = NULL;
    3184              :   struct TALER_MerchantPostDataHashP oh;
    3185              :   struct TALER_MerchantPostDataHashP wh;
    3186              : 
    3187            1 :   memset (&wh,
    3188              :           42,
    3189              :           sizeof (wh));
    3190            1 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    3191            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    3192            1 :       TALER_MERCHANTDB_get_order (pg,
    3193            1 :                                   instance->instance.id,
    3194            1 :                                   order->id,
    3195              :                                   &ct,
    3196              :                                   &oh,
    3197              :                                   &lookup_terms))
    3198              :   {
    3199            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3200              :                 "Lookup order failed\n");
    3201            0 :     if (NULL != lookup_terms)
    3202            0 :       json_decref (lookup_terms);
    3203            0 :     return 1;
    3204              :   }
    3205            1 :   if ( (1 != json_equal (order->contract,
    3206            1 :                          lookup_terms)) ||
    3207            1 :        (0 != GNUNET_memcmp (&order->claim_token,
    3208            1 :                             &ct)) ||
    3209            1 :        (0 != GNUNET_memcmp (&oh,
    3210              :                             &wh)) )
    3211              :   {
    3212            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3213              :                 "Lookup order failed: incorrect order returned\n");
    3214            0 :     if (NULL != lookup_terms)
    3215            0 :       json_decref (lookup_terms);
    3216            0 :     return 1;
    3217              :   }
    3218            1 :   json_decref (lookup_terms);
    3219            1 :   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         1031 : lookup_orders_cb (void *cls,
    3260              :                   const char *order_id,
    3261              :                   uint64_t order_serial,
    3262              :                   struct GNUNET_TIME_Timestamp timestamp)
    3263              : {
    3264         1031 :   struct TestLookupOrders_Closure *cmp = cls;
    3265              :   unsigned int i;
    3266              : 
    3267         1031 :   if (NULL == cmp)
    3268            0 :     return;
    3269         1031 :   i = cmp->results_length;
    3270         1031 :   cmp->results_length += 1;
    3271         1031 :   if (cmp->orders_to_cmp_length > i)
    3272              :   {
    3273              :     /* Compare the orders */
    3274         1031 :     if (0 == strcmp (cmp->orders_to_cmp[i].id,
    3275              :                      order_id))
    3276         1031 :       cmp->results_match[i] = true;
    3277              :     else
    3278            0 :       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           63 : 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           63 : {
    3298           63 :   bool results_match[GNUNET_NZL (orders_length)];
    3299           63 :   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           63 :   memset (results_match,
    3306              :           0,
    3307              :           sizeof (bool) * orders_length);
    3308           63 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    3309           63 :   if (0 > TALER_MERCHANTDB_iterate_orders (pg,
    3310           63 :                                            instance->instance.id,
    3311              :                                            filter,
    3312              :                                            &lookup_orders_cb,
    3313              :                                            &cls))
    3314              :   {
    3315            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3316              :                 "Lookup orders failed\n");
    3317            0 :     return 1;
    3318              :   }
    3319           63 :   if (orders_length != cls.results_length)
    3320              :   {
    3321            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3322              :                 "Lookup orders failed: incorrect number of results (%d)\n",
    3323              :                 cls.results_length);
    3324            0 :     return 1;
    3325              :   }
    3326         1094 :   for (unsigned int i = 0; orders_length > i; ++i)
    3327              :   {
    3328         1031 :     if (false == cls.results_match[i])
    3329              :     {
    3330            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3331              :                   "Lookup orders failed: mismatched data (index %d)\n",
    3332              :                   i);
    3333            0 :       return 1;
    3334              :     }
    3335              :   }
    3336           63 :   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         2105 : get_order_serial_cb (void *cls,
    3367              :                      const char *order_id,
    3368              :                      uint64_t order_serial,
    3369              :                      struct GNUNET_TIME_Timestamp timestamp)
    3370              : {
    3371         2105 :   struct LookupOrderSerial_Closure *lookup_cls = cls;
    3372         2105 :   if (NULL == lookup_cls)
    3373            0 :     return;
    3374         2105 :   if (0 == strcmp (lookup_cls->order->id,
    3375              :                    order_id))
    3376           77 :     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           77 : get_order_serial (const struct InstanceData *instance,
    3389              :                   const struct OrderData *order)
    3390              : {
    3391           77 :   struct LookupOrderSerial_Closure lookup_cls = {
    3392              :     .order = order,
    3393              :     .serial = 0
    3394              :   };
    3395           77 :   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           77 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    3406              :                  TALER_MERCHANTDB_set_instance (pg,
    3407              :                                                 instance->instance.id));
    3408           77 :   GNUNET_assert (0 < TALER_MERCHANTDB_iterate_orders (pg,
    3409              :                                                       instance->instance.id,
    3410              :                                                       &filter,
    3411              :                                                       &get_order_serial_cb,
    3412              :                                                       &lookup_cls));
    3413           77 :   GNUNET_assert (0 != lookup_cls.serial);
    3414              : 
    3415           77 :   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            4 : test_delete_order (const struct InstanceData *instance,
    3429              :                    const struct OrderData *order,
    3430              :                    enum GNUNET_DB_QueryStatus expected_result)
    3431              : {
    3432            4 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    3433            4 :   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            4 :   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            2 : orders_exec_sql (const char *sql)
    3452              : {
    3453            2 :   struct GNUNET_PQ_ExecuteStatement es[] = {
    3454            2 :     GNUNET_PQ_make_execute (sql),
    3455              :     GNUNET_PQ_EXECUTE_STATEMENT_END
    3456              :   };
    3457              : 
    3458            2 :   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
    3459              :                          GNUNET_PQ_exec_statements (pg->conn,
    3460              :                                                     es),
    3461              :                          "Direct SQL execution failed\n");
    3462            2 :   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            1 : test_delete_order_atomic (const struct InstanceData *instance,
    3484              :                           const struct OrderData *order)
    3485              : {
    3486              :   struct TALER_MerchantPostDataHashP h_post_data;
    3487              : 
    3488            1 :   TEST_SET_INSTANCE (instance->instance.id,
    3489              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    3490            1 :   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            1 :   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            1 :   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            1 :   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            1 :   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            1 :   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            1 :   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            1 :   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           45 : 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           45 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    3566           45 :   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           45 :   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            1 : test_update_contract_terms (const struct InstanceData *instance,
    3587              :                             const struct OrderData *order,
    3588              :                             enum GNUNET_DB_QueryStatus expected_result)
    3589              : {
    3590            1 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    3591            1 :   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            1 :   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            2 : test_lookup_contract_terms (const struct InstanceData *instance,
    3610              :                             const struct OrderData *order)
    3611              : {
    3612            2 :   json_t *contract = NULL;
    3613              :   uint64_t order_serial;
    3614              : 
    3615            2 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    3616            2 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    3617            2 :       TALER_MERCHANTDB_get_contract_terms (pg,
    3618            2 :                                            instance->instance.id,
    3619            2 :                                            order->id,
    3620              :                                            &contract,
    3621              :                                            &order_serial,
    3622              :                                            NULL))
    3623              :   {
    3624            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3625              :                 "Lookup contract terms failed\n");
    3626            0 :     GNUNET_assert (NULL == contract);
    3627            0 :     return 1;
    3628              :   }
    3629            2 :   if (1 != json_equal (order->contract,
    3630              :                        contract))
    3631              :   {
    3632            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3633              :                 "Lookup contract terms failed: mismatched data\n");
    3634            0 :     json_decref (contract);
    3635            0 :     return 1;
    3636              :   }
    3637            2 :   json_decref (contract);
    3638            2 :   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            3 : 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            3 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    3658            3 :   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            3 :   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           25 : 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           25 :   GNUNET_assert (GNUNET_OK ==
    3684              :                  TALER_JSON_contract_hash (order->contract,
    3685              :                                            &h_contract_terms));
    3686           25 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    3687           25 :   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           25 :   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            2 : 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            2 :   bool order_paid = false;
    3714              : 
    3715            2 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    3716            2 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    3717            2 :       TALER_MERCHANTDB_get_order_status (pg,
    3718            2 :                                          instance->instance.id,
    3719            2 :                                          order->id,
    3720              :                                          &h_contract_terms,
    3721              :                                          &order_paid))
    3722              :   {
    3723            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3724              :                 "Lookup order status failed\n");
    3725            0 :     return 1;
    3726              :   }
    3727            2 :   GNUNET_assert (GNUNET_OK ==
    3728              :                  TALER_JSON_contract_hash (order->contract,
    3729              :                                            &h_contract_terms_expected));
    3730            2 :   if ((expected_paid != order_paid) ||
    3731            2 :       (0 != GNUNET_memcmp (&h_contract_terms,
    3732              :                            &h_contract_terms_expected)))
    3733              :   {
    3734            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3735              :                 "Lookup order status/deposit failed: mismatched data\n");
    3736            0 :     return 1;
    3737              :   }
    3738            2 :   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            1 : 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            1 :     json_string_value (json_object_get (order->contract,
    3758              :                                         "fulfillment_url"));
    3759            1 :   GNUNET_assert (NULL != fulfillment_url);
    3760            1 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    3761            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    3762            1 :       TALER_MERCHANTDB_get_order_by_fulfillment (pg,
    3763            1 :                                                  instance->instance.id,
    3764              :                                                  fulfillment_url,
    3765              :                                                  session_id,
    3766              :                                                  false,
    3767              :                                                  &order_id))
    3768              :   {
    3769            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3770              :                 "Lookup order by fulfillment failed\n");
    3771            0 :     GNUNET_free (order_id);
    3772            0 :     return 1;
    3773              :   }
    3774            1 :   if (0 != strcmp (order->id,
    3775              :                    order_id))
    3776              :   {
    3777            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3778              :                 "Lookup order by fulfillment failed\n");
    3779            0 :     GNUNET_free (order_id);
    3780            0 :     return 1;
    3781              :   }
    3782            1 :   GNUNET_free (order_id);
    3783            1 :   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            7 : 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            7 :   TEST_SET_INSTANCE (instance_id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    3810            7 :   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            7 :   if ( (NULL != session_id) && (! matches) )
    3824              :   {
    3825            1 :     paid = false;
    3826            1 :     wired = false;
    3827              :   }
    3828            7 :   if (expected_wired != wired)
    3829              :   {
    3830            0 :     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            0 :     return 1;
    3837              :   }
    3838            7 :   if (expected_paid != paid)
    3839              :   {
    3840            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3841              :                 "Lookup payment status failed: paid status is wrong\n");
    3842            0 :     return 1;
    3843              :   }
    3844            7 :   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           18 : test_mark_order_wired (uint64_t order_id,
    3857              :                        enum GNUNET_DB_QueryStatus expected_result)
    3858              : {
    3859           18 :   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           18 :   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            1 : pre_test_orders (struct TestOrders_Closure *cls)
    3896              : {
    3897              :   /* Instance */
    3898            1 :   make_instance ("test_inst_orders",
    3899              :                  &cls->instance);
    3900              : 
    3901              :   /* Product */
    3902            1 :   make_product ("test_orders_pd_0",
    3903              :                 &cls->product);
    3904              : 
    3905              :   /* Orders */
    3906            1 :   make_order ("test_orders_od_0",
    3907              :               &cls->orders[0]);
    3908            1 :   make_order ("test_orders_od_1",
    3909              :               &cls->orders[1]);
    3910            1 :   make_order ("test_orders_od_2",
    3911              :               &cls->orders[2]);
    3912              : 
    3913            1 :   GNUNET_assert (0 ==
    3914              :                  json_object_set_new (cls->orders[1].contract,
    3915              :                                       "other_field",
    3916              :                                       json_string ("Second contract")));
    3917              : 
    3918            1 :   cls->orders[2].pay_deadline = GNUNET_TIME_UNIT_ZERO_TS;
    3919            1 :   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            1 : }
    3925              : 
    3926              : 
    3927              : /**
    3928              :  * Frees memory after order tests.
    3929              :  *
    3930              :  * @param cls the order test closure.
    3931              :  */
    3932              : static void
    3933            1 : post_test_orders (struct TestOrders_Closure *cls)
    3934              : {
    3935            1 :   free_instance_data (&cls->instance);
    3936            1 :   free_product_data (&cls->product);
    3937            1 :   free_order_data (&cls->orders[0]);
    3938            1 :   free_order_data (&cls->orders[1]);
    3939            1 :   free_order_data (&cls->orders[2]);
    3940            1 : }
    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            1 : run_test_orders (struct TestOrders_Closure *cls)
    3951              : {
    3952            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
    3965              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    3966              :   /* Test inserting an order */
    3967            1 :   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            1 :   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            1 :   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            1 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    3982            1 :         TALER_MERCHANTDB_get_order (pg,
    3983            1 :                                     cls->instance.instance.id,
    3984              :                                     cls->orders[1].id,
    3985              :                                     NULL,
    3986              :                                     &unused,
    3987              :                                     NULL))
    3988              :     {
    3989            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3990              :                   "Lookup order failed\n");
    3991            0 :       return 1;
    3992              :     }
    3993              :   }
    3994              :   /* Test lookups on multiple orders */
    3995            1 :   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
    3996              :                                        &cls->orders[1],
    3997              :                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    3998            1 :   filter.expired = TALER_EXCHANGE_YNA_NO;
    3999            1 :   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
    4000              :                                         &filter,
    4001              :                                         2,
    4002              :                                         cls->orders));
    4003            1 :   filter.expired = TALER_EXCHANGE_YNA_YES;
    4004            1 :   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
    4005              :                                         &filter,
    4006              :                                         0,
    4007              :                                         NULL));
    4008            1 :   filter.expired = TALER_EXCHANGE_YNA_ALL;
    4009            1 :   serial = get_order_serial (&cls->instance,
    4010            1 :                              &cls->orders[0]);
    4011            1 :   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
    4012              :                                         &filter,
    4013              :                                         2,
    4014              :                                         cls->orders));
    4015              :   /* Test inserting contract terms */
    4016            1 :   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            1 :   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            1 :   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            1 :   if (1 != TALER_MERCHANTDB_insert_order_lock (pg,
    4033            1 :                                                cls->instance.instance.id,
    4034              :                                                cls->orders[0].id,
    4035              :                                                cls->product.id,
    4036              :                                                5,
    4037              :                                                0))
    4038              :   {
    4039            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    4040              :                 "Insert order lock failed\n");
    4041            0 :     return 1;
    4042              :   }
    4043              :   /* Test lookup contract terms */
    4044            1 :   TEST_RET_ON_FAIL (test_lookup_contract_terms (&cls->instance,
    4045              :                                                 &cls->orders[0]));
    4046              :   /* Test lookup fails for nonexistent contract terms */
    4047              :   {
    4048            1 :     json_t *lookup_contract = NULL;
    4049              :     uint64_t lookup_order_serial;
    4050              : 
    4051            1 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    4052            1 :         TALER_MERCHANTDB_get_contract_terms (pg,
    4053            1 :                                              cls->instance.instance.id,
    4054              :                                              cls->orders[1].id,
    4055              :                                              &lookup_contract,
    4056              :                                              &lookup_order_serial,
    4057              :                                              NULL))
    4058              :     {
    4059            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    4060              :                   "Lookup contract terms failed\n");
    4061            0 :       GNUNET_assert (NULL == lookup_contract);
    4062            0 :       return 1;
    4063              :     }
    4064              :   }
    4065              :   /* Test update contract terms */
    4066            1 :   GNUNET_assert (0 ==
    4067              :                  json_object_set_new (cls->orders[0].contract,
    4068              :                                       "some_new_field",
    4069              :                                       json_string ("another value")));
    4070            1 :   TEST_RET_ON_FAIL (test_update_contract_terms (&cls->instance,
    4071              :                                                 &cls->orders[0],
    4072              :                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    4073            1 :   TEST_RET_ON_FAIL (test_lookup_contract_terms (&cls->instance,
    4074              :                                                 &cls->orders[0]));
    4075              :   /* Test lookup order status */
    4076            1 :   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            1 :     bool order_paid = false;
    4082              : 
    4083            1 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    4084            1 :         TALER_MERCHANTDB_get_order_status (pg,
    4085            1 :                                            cls->instance.instance.id,
    4086              :                                            cls->orders[1].id,
    4087              :                                            &h_contract_terms,
    4088              :                                            &order_paid))
    4089              :     {
    4090            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    4091              :                   "Lookup order status failed\n");
    4092            0 :       return 1;
    4093              :     }
    4094              :   }
    4095              :   /* Test lookup payment status */
    4096            1 :   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            1 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    4107            1 :         TALER_MERCHANTDB_get_order_status (pg,
    4108            1 :                                            cls->instance.instance.id,
    4109              :                                            cls->orders[1].id,
    4110              :                                            &h_contract_terms,
    4111              :                                            &order_paid))
    4112              :     {
    4113            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    4114              :                   "Lookup order status failed\n");
    4115            0 :       return 1;
    4116              :     }
    4117              :   }
    4118              :   /* Test marking contracts as paid */
    4119            1 :   TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
    4120              :                                              &cls->orders[0],
    4121              :                                              GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    4122            1 :   TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
    4123              :                                                 cls->orders[0].id,
    4124              :                                                 NULL,
    4125              :                                                 true,
    4126              :                                                 false));
    4127            1 :   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            1 :   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            1 :   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            1 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    4144            1 :         TALER_MERCHANTDB_get_order_by_fulfillment (pg,
    4145            1 :                                                    cls->instance.instance.id,
    4146              :                                                    "fulfillment_url",
    4147              :                                                    "test_orders_session",
    4148              :                                                    false,
    4149              :                                                    &order_id))
    4150              :     {
    4151            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    4152              :                   "Lookup order by fulfillment failed\n");
    4153            0 :       GNUNET_free (order_id);
    4154            0 :       return 1;
    4155              :     }
    4156              :   }
    4157              :   /* Test mark as paid fails for nonexistent order */
    4158            1 :   TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
    4159              :                                              &cls->orders[1],
    4160              :                                              GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
    4161            1 :   TEST_RET_ON_FAIL (test_lookup_order_status (&cls->instance,
    4162              :                                               &cls->orders[0],
    4163              :                                               true));
    4164            1 :   filter.paid = TALER_EXCHANGE_YNA_ALL;
    4165            1 :   filter.expired = TALER_EXCHANGE_YNA_YES;
    4166            1 :   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
    4167              :                                         &filter,
    4168              :                                         0,
    4169              :                                         NULL));
    4170            1 :   filter.expired = TALER_EXCHANGE_YNA_ALL;
    4171            1 :   filter.paid = TALER_EXCHANGE_YNA_YES;
    4172            1 :   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
    4173              :                                         &filter,
    4174              :                                         1,
    4175              :                                         cls->orders));
    4176              :   /* Test marking orders as wired */
    4177            1 :   TEST_RET_ON_FAIL (test_mark_order_wired (serial,
    4178              :                                            GNUNET_DB_STATUS_SUCCESS_ONE_RESULT))
    4179              :   ;
    4180            1 :   TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
    4181              :                                                 cls->orders[0].id,
    4182              :                                                 NULL,
    4183              :                                                 true,
    4184              :                                                 true));
    4185            1 :   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            1 :   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            1 :   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            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (test_delete_order (&cls->instance,
    4211              :                                        &cls->orders[1],
    4212              :                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    4213            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
    4224              :                                        &cls->orders[2],
    4225              :                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    4226            1 :   filter.paid = TALER_EXCHANGE_YNA_ALL;
    4227            1 :   filter.expired = TALER_EXCHANGE_YNA_YES;
    4228            1 :   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
    4229              :                                         &filter,
    4230              :                                         1,
    4231              :                                         &cls->orders[2]));
    4232            1 :   filter.expired = TALER_EXCHANGE_YNA_NO;
    4233            1 :   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
    4234              :                                         &filter,
    4235              :                                         0,
    4236              :                                         NULL));
    4237            1 :   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
    4238              :                                                 &cls->orders[2],
    4239              :                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    4240            1 :   filter.expired = TALER_EXCHANGE_YNA_YES;
    4241            1 :   TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
    4242              :                                         &filter,
    4243              :                                         1,
    4244              :                                         &cls->orders[2]));
    4245            1 :   filter.expired = TALER_EXCHANGE_YNA_ALL;
    4246            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
    4254              :                                        &cls->orders[1],
    4255              :                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    4256            1 :   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
    4257              :                                                 &cls->orders[1],
    4258              :                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    4259            1 :   TEST_RET_ON_FAIL (test_delete_order_atomic (&cls->instance,
    4260              :                                               &cls->orders[1]));
    4261            1 :   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            1 : test_orders (void)
    4272              : {
    4273              :   struct TestOrders_Closure test_cls;
    4274              :   int test_result;
    4275              : 
    4276            1 :   pre_test_orders (&test_cls);
    4277            1 :   test_result = run_test_orders (&test_cls);
    4278            1 :   post_test_orders (&test_cls);
    4279            1 :   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            4 : make_exchange_signkey (struct ExchangeSignkeyData *signkey)
    4340              : {
    4341            4 :   struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
    4342              : 
    4343            4 :   GNUNET_CRYPTO_eddsa_key_create (&signkey->exchange_priv.eddsa_priv);
    4344            4 :   GNUNET_CRYPTO_eddsa_key_get_public (&signkey->exchange_priv.eddsa_priv,
    4345              :                                       &signkey->exchange_pub.eddsa_pub);
    4346            4 :   GNUNET_CRYPTO_eddsa_key_create (&signkey->master_priv.eddsa_priv);
    4347            4 :   GNUNET_CRYPTO_eddsa_key_get_public (&signkey->master_priv.eddsa_priv,
    4348              :                                       &signkey->master_pub.eddsa_pub);
    4349            4 :   signkey->start_date = now;
    4350            4 :   signkey->expire_date = now;
    4351            4 :   signkey->end_date = now;
    4352            4 :   TALER_exchange_offline_signkey_validity_sign (
    4353            4 :     &signkey->exchange_pub,
    4354              :     signkey->start_date,
    4355              :     signkey->expire_date,
    4356              :     signkey->end_date,
    4357            4 :     &signkey->master_priv,
    4358              :     &signkey->master_sig);
    4359            4 : }
    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           78 : 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           78 :   now = GNUNET_TIME_timestamp_get ();
    4446           78 :   deposit->timestamp = now;
    4447           78 :   GNUNET_assert (GNUNET_OK ==
    4448              :                  TALER_JSON_contract_hash (order->contract,
    4449              :                                            &deposit->h_contract_terms));
    4450           78 :   GNUNET_CRYPTO_eddsa_key_create (&coin_priv.eddsa_priv);
    4451           78 :   GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv,
    4452              :                                       &deposit->coin_pub.eddsa_pub);
    4453           78 :   deposit->exchange_url = "https://test-exchange/";
    4454           78 :   GNUNET_assert (GNUNET_OK ==
    4455              :                  TALER_string_to_amount ("EUR:50.00",
    4456              :                                          &deposit->amount_with_fee));
    4457           78 :   GNUNET_assert (GNUNET_OK ==
    4458              :                  TALER_string_to_amount ("EUR:1.00",
    4459              :                                          &deposit->deposit_fee));
    4460           78 :   GNUNET_assert (GNUNET_OK ==
    4461              :                  TALER_string_to_amount ("EUR:1.50",
    4462              :                                          &deposit->refund_fee));
    4463           78 :   GNUNET_assert (GNUNET_OK ==
    4464              :                  TALER_string_to_amount ("EUR:2.00",
    4465              :                                          &deposit->wire_fee));
    4466           78 :   GNUNET_assert (0 <=
    4467              :                  TALER_amount_subtract (&amount_without_fee,
    4468              :                                         &deposit->amount_with_fee,
    4469              :                                         &deposit->deposit_fee));
    4470           78 :   deposit->h_wire = account->h_wire;
    4471           78 :   GNUNET_CRYPTO_random_block (&deposit->exchange_sig,
    4472              :                               sizeof (deposit->exchange_sig));
    4473           78 :   GNUNET_CRYPTO_random_block (&deposit->coin_sig,
    4474              :                               sizeof (deposit->coin_sig));
    4475           78 : }
    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            5 : test_insert_exchange_signkey (const struct ExchangeSignkeyData *signkey,
    4487              :                               enum GNUNET_DB_QueryStatus expected_result)
    4488              : {
    4489            5 :   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            5 :   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           31 : 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           31 :   GNUNET_assert (0 <=
    4522              :                  TALER_amount_subtract (&awf,
    4523              :                                         &deposit->amount_with_fee,
    4524              :                                         &deposit->deposit_fee));
    4525           31 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    4526           31 :   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           31 :   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           31 :   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            4 : 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            4 :   uint64_t row = 0;
    4584              :   struct TALER_Amount awf;
    4585              : 
    4586            4 :   GNUNET_assert (0 <=
    4587              :                  TALER_amount_subtract (&awf,
    4588              :                                         &deposit->amount_with_fee,
    4589              :                                         &deposit->deposit_fee));
    4590            4 :   TEST_SET_INSTANCE (instance->instance.id,
    4591              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    4592            4 :   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            4 :   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            4 : 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            4 :   struct TestLookupDeposits_Closure *cmp = cls;
    4658              : 
    4659            4 :   if (NULL == cmp)
    4660            0 :     return;
    4661            4 :   cmp->results_length += 1;
    4662           10 :   for (unsigned int i = 0; cmp->deposits_to_cmp_length > i; ++i)
    4663              :   {
    4664            6 :     if ((GNUNET_OK ==
    4665            6 :          TALER_amount_cmp_currency (
    4666            6 :            &cmp->deposits_to_cmp[i].amount_with_fee,
    4667            6 :            amount_with_fee)) &&
    4668              :         (0 ==
    4669            6 :          TALER_amount_cmp (&cmp->deposits_to_cmp[i].amount_with_fee,
    4670            4 :                            amount_with_fee)) &&
    4671              :         (GNUNET_OK ==
    4672            4 :          TALER_amount_cmp_currency (
    4673            4 :            &cmp->deposits_to_cmp[i].deposit_fee,
    4674            4 :            deposit_fee)) &&
    4675              :         (0 ==
    4676            4 :          TALER_amount_cmp (&cmp->deposits_to_cmp[i].deposit_fee,
    4677            4 :                            deposit_fee)) &&
    4678              :         (GNUNET_OK ==
    4679            4 :          TALER_amount_cmp_currency (
    4680            4 :            &cmp->deposits_to_cmp[i].refund_fee,
    4681            4 :            refund_fee)) &&
    4682              :         (0 ==
    4683            4 :          TALER_amount_cmp (&cmp->deposits_to_cmp[i].refund_fee,
    4684              :                            refund_fee)))
    4685              :     {
    4686            4 :       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            4 : 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            4 : {
    4708            4 :   unsigned int results_matching[GNUNET_NZL (deposits_length)];
    4709            4 :   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            4 :   memset (results_matching,
    4716              :           0,
    4717              :           sizeof (unsigned int) * deposits_length);
    4718            4 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    4719            4 :   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            4 :   if (deposits_length != cmp.results_length)
    4727              :   {
    4728            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    4729              :                 "Lookup deposits failed: incorrect number of results returned (%d)\n",
    4730              :                 cmp.results_length);
    4731            0 :     return 1;
    4732              :   }
    4733            8 :   for (unsigned int i = 0; deposits_length > i; ++i)
    4734              :   {
    4735            4 :     if (cmp.results_matching[i] != 1)
    4736              :     {
    4737            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    4738              :                   "Lookup deposits failed: mismatched data\n");
    4739            0 :       return 1;
    4740              :     }
    4741              :   }
    4742            4 :   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            1 : 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            1 :   struct TestLookupDeposits_Closure *cmp = cls;
    4776              : 
    4777            1 :   if (NULL == cmp)
    4778            0 :     return;
    4779            1 :   cmp->results_length++;
    4780            2 :   for (unsigned int i = 0; cmp->deposits_to_cmp_length > i; ++i)
    4781              :   {
    4782            1 :     if ((GNUNET_TIME_timestamp_cmp (cmp->deposits_to_cmp[i].timestamp,
    4783              :                                     ==,
    4784            1 :                                     deposit_timestamp)) &&
    4785            1 :         (0 == strcmp (cmp->deposits_to_cmp[i].exchange_url,
    4786            1 :                       exchange_url)) &&
    4787            1 :         (GNUNET_OK == TALER_amount_cmp_currency (
    4788            1 :            &cmp->deposits_to_cmp[i].amount_with_fee,
    4789            1 :            amount_with_fee)) &&
    4790            1 :         (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].amount_with_fee,
    4791            1 :                                 amount_with_fee)) &&
    4792            1 :         (GNUNET_OK == TALER_amount_cmp_currency (
    4793            1 :            &cmp->deposits_to_cmp[i].deposit_fee,
    4794            1 :            deposit_fee)) &&
    4795            1 :         (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].deposit_fee,
    4796            1 :                                 deposit_fee)) &&
    4797            1 :         (GNUNET_OK == TALER_amount_cmp_currency (
    4798            1 :            &cmp->deposits_to_cmp[i].refund_fee,
    4799            1 :            refund_fee)) &&
    4800            1 :         (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].refund_fee,
    4801            1 :                                 refund_fee)) &&
    4802            1 :         (GNUNET_OK == TALER_amount_cmp_currency (
    4803            1 :            &cmp->deposits_to_cmp[i].wire_fee,
    4804            1 :            wire_fee)) &&
    4805            1 :         (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].wire_fee,
    4806            1 :                                 wire_fee)) &&
    4807            1 :         (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].h_wire,
    4808            1 :                              h_wire)) &&
    4809            1 :         (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].exchange_sig,
    4810              :                              exchange_sig)))
    4811              :     {
    4812            1 :       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            1 : 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            1 : {
    4836            1 :   unsigned int results_matching[deposits_length];
    4837            1 :   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            1 :   memset (results_matching,
    4844              :           0,
    4845              :           sizeof (unsigned int) * deposits_length);
    4846            1 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    4847            1 :   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            1 :   if (deposits_length != cmp.results_length)
    4858              :   {
    4859            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    4860              :                 "Lookup deposits failed: incorrect number of results returned\n");
    4861            0 :     return 1;
    4862              :   }
    4863            2 :   for (unsigned int i = 0; deposits_length > i; ++i)
    4864              :   {
    4865            1 :     if (cmp.results_matching[i] != 1)
    4866              :     {
    4867            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    4868              :                   "Lookup deposits failed: mismatched data\n");
    4869            0 :       return 1;
    4870              :     }
    4871              :   }
    4872            1 :   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            3 : 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            3 :   struct TestLookupDeposits_Closure *cmp = cls;
    4899              : 
    4900            3 :   if (NULL == cmp)
    4901            0 :     return;
    4902            3 :   cmp->results_length += 1;
    4903            8 :   for (unsigned int i = 0; i < cmp->deposits_to_cmp_length; ++i)
    4904              :   {
    4905            5 :     if ((0 == strcmp (cmp->deposits_to_cmp[i].exchange_url,
    4906            5 :                       exchange_url)) &&
    4907            5 :         (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].h_wire,
    4908            5 :                              h_wire)) &&
    4909            5 :         (GNUNET_OK == TALER_amount_cmp_currency (
    4910            5 :            &cmp->deposits_to_cmp[i].amount_with_fee,
    4911            5 :            amount_with_fee)) &&
    4912            5 :         (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].amount_with_fee,
    4913            3 :                                 amount_with_fee)) &&
    4914            3 :         (GNUNET_OK == TALER_amount_cmp_currency (
    4915            3 :            &cmp->deposits_to_cmp[i].deposit_fee,
    4916            3 :            deposit_fee)) &&
    4917            3 :         (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].deposit_fee,
    4918            3 :                                 deposit_fee)) &&
    4919            3 :         (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].coin_pub,
    4920              :                              coin_pub)))
    4921            3 :       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            2 : test_lookup_deposits_by_order (uint64_t order_serial,
    4936              :                                unsigned int deposits_length,
    4937              :                                const struct DepositData *deposits)
    4938            2 : {
    4939            2 :   unsigned int results_matching[deposits_length];
    4940            2 :   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            2 :   memset (results_matching,
    4947              :           0,
    4948              :           sizeof (unsigned int) * deposits_length);
    4949            2 :   if (deposits_length !=
    4950            2 :       TALER_MERCHANTDB_iterate_deposits_by_order (pg,
    4951              :                                                   order_serial,
    4952              :                                                   &lookup_deposits_order_cb,
    4953              :                                                   &cmp))
    4954              :   {
    4955            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    4956              :                 "Lookup deposits by order failed\n");
    4957            0 :     return 1;
    4958              :   }
    4959            2 :   if (deposits_length != cmp.results_length)
    4960              :   {
    4961            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    4962              :                 "Lookup deposits by order failed: incorrect number of results\n");
    4963            0 :     return 1;
    4964              :   }
    4965            5 :   for (unsigned int i = 0; i < deposits_length; ++i)
    4966              :   {
    4967            3 :     if (1 != cmp.results_matching[i])
    4968              :     {
    4969            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    4970              :                   "Lookup deposits by order failed: mismatched data\n");
    4971            0 :       return 1;
    4972              :     }
    4973              :   }
    4974            2 :   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           12 : 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           12 :   struct LookupDepositSerial_Closure *lookup_cls = cls;
    5018              : 
    5019              :   (void) deposit_timestamp;
    5020           12 :   if (NULL == lookup_cls)
    5021            0 :     return;
    5022           12 :   if ((0 == strcmp (lookup_cls->deposit->exchange_url,
    5023           12 :                     exchange_url)) &&
    5024           12 :       (0 == GNUNET_memcmp (&lookup_cls->deposit->h_wire,
    5025           12 :                            h_wire)) &&
    5026           12 :       (GNUNET_OK == TALER_amount_cmp_currency (
    5027           12 :          &lookup_cls->deposit->amount_with_fee,
    5028           12 :          amount_with_fee)) &&
    5029           12 :       (0 == TALER_amount_cmp (&lookup_cls->deposit->amount_with_fee,
    5030           12 :                               amount_with_fee)) &&
    5031           12 :       (GNUNET_OK == TALER_amount_cmp_currency (
    5032           12 :          &lookup_cls->deposit->deposit_fee,
    5033           12 :          deposit_fee)) &&
    5034           12 :       (0 == TALER_amount_cmp (&lookup_cls->deposit->deposit_fee,
    5035           12 :                               deposit_fee)) &&
    5036           12 :       (0 == GNUNET_memcmp (&lookup_cls->deposit->coin_pub,
    5037              :                            coin_pub)))
    5038            9 :     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            9 : get_deposit_serial (const struct InstanceData *instance,
    5052              :                     const struct OrderData *order,
    5053              :                     const struct DepositData *deposit)
    5054              : {
    5055            9 :   uint64_t order_serial = get_order_serial (instance,
    5056              :                                             order);
    5057            9 :   struct LookupDepositSerial_Closure lookup_cls = {
    5058              :     .deposit = deposit,
    5059              :     .serial = 0
    5060              :   };
    5061              : 
    5062            9 :   GNUNET_assert (0 <
    5063              :                  TALER_MERCHANTDB_iterate_deposits_by_order (pg,
    5064              :                                                              order_serial,
    5065              :                                                              &get_deposit_serial_cb,
    5066              :                                                              &lookup_cls));
    5067            9 :   GNUNET_assert (0 != lookup_cls.serial);
    5068              : 
    5069            9 :   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            1 : pre_test_deposits (struct TestDeposits_Closure *cls)
    5112              : {
    5113              :   /* Instance */
    5114            1 :   make_instance ("test_inst_deposits",
    5115              :                  &cls->instance);
    5116              : 
    5117              :   /* Account */
    5118            1 :   make_account (&cls->account);
    5119            1 :   cls->account.instance_id = cls->instance.instance.id;
    5120              :   /* Signing key */
    5121            1 :   make_exchange_signkey (&cls->signkey);
    5122              : 
    5123              :   /* Order */
    5124            1 :   make_order ("test_deposits_od_1",
    5125              :               &cls->orders[0]);
    5126            1 :   make_order ("test_deposits_od_2",
    5127              :               &cls->orders[1]);
    5128              : 
    5129              :   /* Deposit */
    5130            1 :   make_deposit (&cls->instance,
    5131            1 :                 &cls->account,
    5132            1 :                 &cls->orders[0],
    5133            1 :                 &cls->signkey,
    5134              :                 &cls->deposits[0]);
    5135            1 :   make_deposit (&cls->instance,
    5136            1 :                 &cls->account,
    5137            1 :                 &cls->orders[0],
    5138            1 :                 &cls->signkey,
    5139              :                 &cls->deposits[1]);
    5140            1 :   GNUNET_assert (GNUNET_OK ==
    5141              :                  TALER_string_to_amount ("EUR:29.00",
    5142              :                                          &cls->deposits[1].amount_with_fee));
    5143            1 :   make_deposit (&cls->instance,
    5144            1 :                 &cls->account,
    5145            1 :                 &cls->orders[1],
    5146            1 :                 &cls->signkey,
    5147              :                 &cls->deposits[2]);
    5148            1 : }
    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            1 : post_test_deposits (struct TestDeposits_Closure *cls)
    5158              : {
    5159            1 :   free_instance_data (&cls->instance);
    5160            1 :   json_decref (cls->orders[0].contract);
    5161            1 :   json_decref (cls->orders[1].contract);
    5162            1 : }
    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            3 : 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            3 :   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            3 :   (*count)++;
    5197            3 : }
    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            1 : test_pending_deposits_without_private_key (const struct InstanceData *instance,
    5216              :                                            const char *exchange_url)
    5217              : {
    5218            1 :   unsigned int count = 0;
    5219              : 
    5220              :   /* Before the deletion the deposits are pending. */
    5221            1 :   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            1 :   TEST_COND_RET_ON_FAIL (0 < count,
    5231              :                          "No pending deposits found\n");
    5232            1 :   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            1 :   count = 0;
    5237            1 :   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            1 :   TEST_COND_RET_ON_FAIL (0 == count,
    5248              :                          "Deposit of an instance without a private key"
    5249              :                          " was returned\n");
    5250            1 :   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            1 : run_test_deposits (struct TestDeposits_Closure *cls)
    5262              : {
    5263              :   /* Insert the instance */
    5264            1 :   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
    5265              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    5266              :   /* Insert an account */
    5267            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey,
    5272              :                                                   GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    5273            1 :   TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey,
    5274              :                                                   GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
    5275              :   /* Insert an order */
    5276            1 :   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            1 :   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            1 :   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            1 :   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            1 :     GNUNET_CRYPTO_random_block (&bogus_h_contract,
    5302              :                                 sizeof (bogus_h_contract));
    5303            1 :     GNUNET_CRYPTO_random_block (&bogus_h_wire,
    5304              :                                 sizeof (bogus_h_wire));
    5305            1 :     GNUNET_CRYPTO_random_block (&bogus_exchange_pub,
    5306              :                                 sizeof (bogus_exchange_pub));
    5307            1 :     GNUNET_assert (GNUNET_OK ==
    5308              :                    TALER_string_to_amount ("EUR:3.00",
    5309              :                                            &other_wire_fee));
    5310              :     /* unknown merchant account */
    5311            1 :     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            1 :     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            1 :     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            1 :     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            1 :   TEST_RET_ON_FAIL (test_lookup_deposits (&cls->instance,
    5349              :                                           &cls->deposits[0].h_contract_terms,
    5350              :                                           1,
    5351              :                                           cls->deposits));
    5352            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
    5365              :                                          &cls->signkey,
    5366              :                                          &cls->deposits[1],
    5367              :                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    5368            1 :   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
    5369              :                                        &cls->orders[1],
    5370              :                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    5371            1 :   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
    5372              :                                                 &cls->orders[1],
    5373              :                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    5374            1 :   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
    5375              :                                          &cls->signkey,
    5376              :                                          &cls->deposits[2],
    5377              :                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    5378            1 :   TEST_RET_ON_FAIL (test_lookup_deposits (&cls->instance,
    5379              :                                           &cls->deposits[0].h_contract_terms,
    5380              :                                           2,
    5381              :                                           cls->deposits));
    5382            1 :   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            1 :     uint64_t order_serial = get_order_serial (&cls->instance,
    5389            1 :                                               &cls->orders[0]);
    5390            1 :     TEST_RET_ON_FAIL (test_lookup_deposits_by_order (order_serial,
    5391              :                                                      2,
    5392              :                                                      cls->deposits));
    5393            1 :     order_serial = get_order_serial (&cls->instance,
    5394            1 :                                      &cls->orders[1]);
    5395            1 :     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            1 :   TEST_RET_ON_FAIL (test_pending_deposits_without_private_key (
    5401              :                       &cls->instance,
    5402              :                       cls->deposits[0].exchange_url));
    5403            1 :   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            1 : test_deposits (void)
    5414              : {
    5415              :   struct TestDeposits_Closure test_cls;
    5416              :   int test_result;
    5417              : 
    5418            1 :   pre_test_deposits (&test_cls);
    5419            1 :   test_result = run_test_deposits (&test_cls);
    5420            1 :   post_test_deposits (&test_cls);
    5421            1 :   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            2 : make_wire_fee (const struct ExchangeSignkeyData *signkey,
    5473              :                struct WireFeeData *wire_fee)
    5474              : {
    5475            2 :   wire_fee->wire_method = "wire-method";
    5476            2 :   GNUNET_CRYPTO_hash (wire_fee->wire_method,
    5477            2 :                       strlen (wire_fee->wire_method) + 1,
    5478              :                       &wire_fee->h_wire_method);
    5479            2 :   GNUNET_assert (GNUNET_OK ==
    5480              :                  TALER_string_to_amount ("EUR:0.49",
    5481              :                                          &wire_fee->fees.wire));
    5482            2 :   GNUNET_assert (GNUNET_OK ==
    5483              :                  TALER_string_to_amount ("EUR:0.49",
    5484              :                                          &wire_fee->fees.closing));
    5485            2 :   wire_fee->wire_fee_start = GNUNET_TIME_timestamp_get ();
    5486            2 :   wire_fee->wire_fee_end = GNUNET_TIME_relative_to_timestamp (
    5487              :     GNUNET_TIME_UNIT_MONTHS);
    5488            2 :   TALER_exchange_offline_wire_fee_sign (
    5489              :     wire_fee->wire_method,
    5490              :     wire_fee->wire_fee_start,
    5491              :     wire_fee->wire_fee_end,
    5492            2 :     &wire_fee->fees,
    5493              :     &signkey->master_priv,
    5494              :     &wire_fee->fee_sig);
    5495            2 : }
    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            5 : make_transfer (const struct ExchangeSignkeyData *signkey,
    5544              :                unsigned int deposits_length,
    5545              :                const struct DepositData deposits[static deposits_length],
    5546              :                struct TransferData *transfer)
    5547            5 : {
    5548            5 :   struct TALER_TrackTransferDetails *details = NULL;
    5549              : 
    5550            5 :   GNUNET_CRYPTO_random_block (&transfer->wtid,
    5551              :                               sizeof (struct TALER_WireTransferIdentifierRawP));
    5552            5 :   transfer->exchange_url = deposits[0].exchange_url;
    5553            5 :   transfer->verified = false;
    5554            5 :   transfer->confirmed = false;
    5555            5 :   transfer->data.details_length = 0;
    5556            5 :   GNUNET_assert (GNUNET_OK ==
    5557              :                  TALER_amount_set_zero (deposits[0].amount_with_fee.currency,
    5558              :                                         &transfer->data.total_amount));
    5559            5 :   GNUNET_assert (GNUNET_OK ==
    5560              :                  TALER_amount_set_zero (deposits[0].amount_with_fee.currency,
    5561              :                                         &transfer->deposit_fee));
    5562           11 :   for (unsigned int i = 0; i < deposits_length; ++i)
    5563              :   {
    5564            6 :     GNUNET_array_grow (details,
    5565              :                        transfer->data.details_length,
    5566              :                        i + 1);
    5567            6 :     details[i].h_contract_terms = deposits[i].h_contract_terms;
    5568            6 :     details[i].coin_pub = deposits[i].coin_pub;
    5569            6 :     details[i].coin_value = deposits[i].amount_with_fee;
    5570            6 :     details[i].coin_fee = deposits[i].deposit_fee;
    5571            6 :     GNUNET_assert (0 <=
    5572              :                    TALER_amount_add (&transfer->data.total_amount,
    5573              :                                      &transfer->data.total_amount,
    5574              :                                      &deposits[i].amount_with_fee));
    5575            6 :     GNUNET_assert (0 <=
    5576              :                    TALER_amount_add (&transfer->deposit_fee,
    5577              :                                      &transfer->deposit_fee,
    5578              :                                      &deposits[i].deposit_fee));
    5579              :   }
    5580            5 :   transfer->data.exchange_pub = signkey->exchange_pub;
    5581            5 :   transfer->data.execution_time = GNUNET_TIME_timestamp_get ();
    5582            5 :   transfer->data.details = details;
    5583            5 :   GNUNET_assert (GNUNET_OK ==
    5584              :                  TALER_string_to_amount ("EUR:0.50",
    5585              :                                          &transfer->data.wire_fee));
    5586            5 : }
    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            1 : 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            1 :   struct TestLookupTransferSummary_Closure *cmp = cls;
    5631            1 :   if (NULL == cmp)
    5632            0 :     return;
    5633            1 :   if ((0 == strcmp (cmp->order_id,
    5634            1 :                     order_id)) &&
    5635            1 :       (GNUNET_OK == TALER_amount_cmp_currency (cmp->deposit_value,
    5636            1 :                                                deposit_value)) &&
    5637            1 :       (0 == TALER_amount_cmp (cmp->deposit_value,
    5638            1 :                               deposit_value)) &&
    5639            1 :       (GNUNET_OK == TALER_amount_cmp_currency (cmp->deposit_fee,
    5640            1 :                                                deposit_fee)) &&
    5641            1 :       (0 == TALER_amount_cmp (cmp->deposit_fee,
    5642              :                               deposit_fee)))
    5643            1 :     cmp->result = 0;
    5644              :   else
    5645            0 :     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            1 : 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            1 :   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            1 :   if (1 != TALER_MERCHANTDB_iterate_transfer_summaries (pg,
    5674              :                                                         exchange_url,
    5675              :                                                         wtid,
    5676              :                                                         &lookup_transfer_summary_cb,
    5677              :                                                         &cmp))
    5678              :   {
    5679            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    5680              :                 "Lookup transfer summary failed\n");
    5681            0 :     return 1;
    5682              :   }
    5683            1 :   if (0 != cmp.result)
    5684              :   {
    5685            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    5686              :                 "Lookup transfer summary failed: mismatched data\n");
    5687            0 :     return 1;
    5688              :   }
    5689            1 :   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            1 : lookup_transfer_details_cb (void *cls,
    5729              :                             unsigned int current_offset,
    5730              :                             const struct TALER_TrackTransferDetails *details)
    5731              : {
    5732            1 :   struct TestLookupTransferDetails_Closure *cmp = cls;
    5733            1 :   if (NULL == cmp)
    5734            0 :     return;
    5735            2 :   for (unsigned int i = 0; cmp->details_to_cmp_length > i; ++i)
    5736              :   {
    5737            1 :     if ((0 == GNUNET_memcmp (&cmp->details_to_cmp[i].h_contract_terms,
    5738            1 :                              &details->h_contract_terms)) &&
    5739            1 :         (0 == GNUNET_memcmp (&cmp->details_to_cmp[i].coin_pub,
    5740            1 :                              &details->coin_pub)) &&
    5741            1 :         (GNUNET_OK == TALER_amount_cmp_currency (
    5742            1 :            &cmp->details_to_cmp[i].coin_value,
    5743            1 :            &details->coin_value)) &&
    5744            1 :         (0 == TALER_amount_cmp (&cmp->details_to_cmp[i].coin_value,
    5745            1 :                                 &details->coin_value)) &&
    5746            1 :         (GNUNET_OK == TALER_amount_cmp_currency (
    5747            1 :            &cmp->details_to_cmp[i].coin_fee,
    5748            1 :            &details->coin_fee)) &&
    5749            1 :         (0 == TALER_amount_cmp (&cmp->details_to_cmp[i].coin_fee,
    5750              :                                 &details->coin_fee)))
    5751              :     {
    5752            1 :       cmp->results_matching[i] += 1;
    5753              :     }
    5754              :   }
    5755            1 :   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            1 : 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            1 : {
    5775            1 :   unsigned int results_matching[details_length];
    5776            1 :   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            1 :   memset (results_matching,
    5783              :           0,
    5784              :           sizeof (unsigned int) * details_length);
    5785            1 :   if (1 != TALER_MERCHANTDB_iterate_transfer_details (pg,
    5786              :                                                       exchange_url,
    5787              :                                                       wtid,
    5788              :                                                       &lookup_transfer_details_cb,
    5789              :                                                       &cmp))
    5790              :   {
    5791            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    5792              :                 "Lookup transfer details failed\n");
    5793            0 :     return 1;
    5794              :   }
    5795            1 :   if (details_length != cmp.results_length)
    5796              :   {
    5797            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    5798              :                 "Lookup transfer details failed: incorrect number of results (%d)\n",
    5799              :                 cmp.results_length);
    5800            0 :     return 1;
    5801              :   }
    5802            2 :   for (unsigned int i = 0; details_length > i; ++i)
    5803              :   {
    5804            1 :     if (1 != cmp.results_matching[i])
    5805              :     {
    5806            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    5807              :                   "Lookup transfer details failed: mismatched data\n");
    5808            0 :       return 1;
    5809              :     }
    5810              :   }
    5811            1 :   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            1 : 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            1 :   struct TestLookupTransferDetailsByOrder_Closure *cmp = cls;
    5866              : 
    5867            1 :   if (NULL == cmp)
    5868            0 :     return;
    5869            1 :   cmp->results_length += 1;
    5870            2 :   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            1 :     if ((0 == GNUNET_memcmp (&cmp->transfers_to_cmp[i].wtid,
    5875            1 :                              wtid)) &&
    5876            1 :         (0 == strcmp (cmp->transfers_to_cmp[i].exchange_url,
    5877            1 :                       exchange_url)) &&
    5878              :         (GNUNET_OK ==
    5879            1 :          TALER_amount_cmp_currency (
    5880            1 :            &cmp->transfers_to_cmp[i].data.total_amount,
    5881            1 :            deposit_value)) &&
    5882              :         (0 ==
    5883            1 :          TALER_amount_cmp (&cmp->transfers_to_cmp[i].data.total_amount,
    5884            1 :                            deposit_value)) &&
    5885              :         (GNUNET_OK ==
    5886            1 :          TALER_amount_cmp_currency (
    5887            1 :            &cmp->transfers_to_cmp[i].deposit_fee,
    5888            1 :            deposit_fee)) &&
    5889              :         (0 ==
    5890            1 :          TALER_amount_cmp (&cmp->transfers_to_cmp[i].deposit_fee,
    5891              :                            deposit_fee)) )
    5892            1 :       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            1 : test_lookup_transfer_details_by_order (
    5907              :   uint64_t order_serial,
    5908              :   unsigned int transfers_length,
    5909              :   const struct TransferData *transfers)
    5910            1 : {
    5911            1 :   unsigned int results_matching[transfers_length];
    5912            1 :   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            1 :   memset (results_matching,
    5919              :           0,
    5920              :           sizeof (unsigned int) * transfers_length);
    5921            1 :   if (transfers_length !=
    5922            1 :       TALER_MERCHANTDB_iterate_transfer_details_by_order (
    5923              :         pg,
    5924              :         order_serial,
    5925              :         &lookup_transfer_details_order_cb,
    5926              :         &cmp))
    5927              :   {
    5928            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    5929              :                 "Lookup transfer details by order failed\n");
    5930            0 :     return 1;
    5931              :   }
    5932            1 :   if (transfers_length != cmp.results_length)
    5933              :   {
    5934            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    5935              :                 "Lookup transfer details by order failed: incorrect number of results\n");
    5936            0 :     return 1;
    5937              :   }
    5938            2 :   for (unsigned int i = 0; i < transfers_length; ++i)
    5939              :   {
    5940            1 :     if (1 != cmp.results_matching[i])
    5941              :     {
    5942            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    5943              :                   "Lookup transfer details by order failed: mismatched data\n");
    5944            0 :       return 1;
    5945              :     }
    5946              :   }
    5947            1 :   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            4 : test_insert_wire_fee (const struct ExchangeSignkeyData *signkey,
    5961              :                       const struct WireFeeData *wire_fee,
    5962              :                       enum GNUNET_DB_QueryStatus expected_result)
    5963              : {
    5964            4 :   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            4 :   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            3 : 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            6 :   if (1 != TALER_MERCHANTDB_get_exchange_wire_fee (pg,
    5994              :                                                    &signkey->master_pub,
    5995            3 :                                                    wire_fee_data->wire_method,
    5996              :                                                    GNUNET_TIME_timestamp_get (),
    5997              :                                                    &fees,
    5998              :                                                    &start_date,
    5999              :                                                    &end_date,
    6000              :                                                    &master_sig))
    6001              :   {
    6002            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    6003              :                 "Lookup wire fee failed\n");
    6004            0 :     return 1;
    6005              :   }
    6006            3 :   if ((0 !=
    6007            3 :        TALER_wire_fee_set_cmp (&wire_fee_data->fees,
    6008            3 :                                &fees)) ||
    6009            3 :       (GNUNET_TIME_timestamp_cmp (wire_fee_data->wire_fee_start,
    6010              :                                   !=,
    6011            3 :                                   start_date)) ||
    6012            3 :       (GNUNET_TIME_timestamp_cmp (wire_fee_data->wire_fee_end,
    6013              :                                   !=,
    6014            3 :                                   end_date)) ||
    6015            3 :       (0 != GNUNET_memcmp (&wire_fee_data->fee_sig,
    6016              :                            &master_sig)))
    6017              :   {
    6018            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    6019              :                 "Lookup wire fee failed: mismatched data\n");
    6020            0 :     return 1;
    6021              :   }
    6022            3 :   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            1 : 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            1 :   struct TestLookupTransfers_Closure *cmp = cls;
    6082            1 :   if (NULL == cmp)
    6083            0 :     return;
    6084            2 :   for (unsigned int i = 0; cmp->transfers_to_cmp_length > i; ++i)
    6085              :   {
    6086            1 :     if ( (GNUNET_OK ==
    6087            1 :           TALER_amount_cmp_currency (
    6088            1 :             &cmp->transfers_to_cmp[i].data.total_amount,
    6089            1 :             credit_amount)) &&
    6090            1 :          (0 == TALER_amount_cmp (&cmp->transfers_to_cmp[i].data.total_amount,
    6091              :                                  credit_amount)) )
    6092              :     {
    6093            1 :       cmp->results_matching[i]++;
    6094              :     }
    6095              :   }
    6096            1 :   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            1 : 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            1 : {
    6124            1 :   unsigned int results_matching[transfers_length];
    6125            1 :   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            1 :   memset (results_matching,
    6132              :           0,
    6133              :           sizeof (unsigned int) * transfers_length);
    6134            1 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    6135            1 :   if (1 != TALER_MERCHANTDB_iterate_transfers (pg,
    6136            1 :                                                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            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    6147              :                 "Lookup transfers failed\n");
    6148            0 :     return 1;
    6149              :   }
    6150            1 :   if (transfers_length != cmp.results_length)
    6151              :   {
    6152            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    6153              :                 "Lookup transfers failed: incorrect number of results\n");
    6154            0 :     return 1;
    6155              :   }
    6156            2 :   for (unsigned int i = 0; transfers_length > i; ++i)
    6157              :   {
    6158            1 :     if (1 != cmp.results_matching[i])
    6159              :     {
    6160            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    6161              :                   "Lookup transfers failed: mismatched data\n");
    6162            0 :       return 1;
    6163              :     }
    6164              :   }
    6165            1 :   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            2 : 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            2 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    6188            2 :   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            2 :   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            1 : test_insert_transfer_unknown_account (
    6221              :   const struct InstanceData *instance,
    6222              :   const struct TransferData *transfer)
    6223              : {
    6224            1 :   bool no_account = false;
    6225            1 :   bool conflict = false;
    6226            1 :   struct TALER_FullPayto unknown = {
    6227              :     .full_payto = (char *) "payto://iban/DE/NO-SUCH-ACCOUNT"
    6228              :                   "?receiver-name=Not+My+Account"
    6229              :   };
    6230              : 
    6231            1 :   TEST_SET_INSTANCE (instance->instance.id,
    6232              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    6233            1 :   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            1 :   TEST_COND_RET_ON_FAIL (no_account,
    6246              :                          "Insert transfer accepted a payto URI belonging to "
    6247              :                          "no account of the instance\n");
    6248            1 :   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            8 : 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            8 :   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            8 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    6282            8 :   deposit_serial = get_deposit_serial (instance,
    6283              :                                        order,
    6284              :                                        deposit);
    6285              : 
    6286            8 :   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            8 :   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            1 : 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            1 :   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            1 :   GNUNET_CRYPTO_random_block (&bogus_h_wire,
    6330              :                               sizeof (bogus_h_wire));
    6331            1 :   TEST_SET_INSTANCE (instance->instance.id,
    6332              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    6333            1 :   deposit_serial = get_deposit_serial (instance,
    6334              :                                        order,
    6335              :                                        deposit);
    6336            1 :   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            1 :   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            3 : 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            3 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    6365            3 :   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            3 :   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            2 : 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            2 :   unsigned int num_settled = 0;
    6402              : 
    6403            2 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    6404            2 :   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            2 :   TEST_COND_RET_ON_FAIL (expected_settled == num_settled,
    6415              :                          "Wrong number of settled orders reported\n");
    6416            2 :   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            1 : webhook_pending_event_cb (void *cls,
    6441              :                           const void *extra,
    6442              :                           size_t extra_size)
    6443              : {
    6444            1 :   struct WebhookPendingEventState *state = cls;
    6445              : 
    6446              :   (void) extra;
    6447              :   (void) extra_size;
    6448            1 :   state->fired++;
    6449            1 : }
    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            1 : pre_test_transfers (struct TestTransfers_Closure *cls)
    6529              : {
    6530              :   /* Instance */
    6531            1 :   make_instance ("test_inst_transfers",
    6532              :                  &cls->instance);
    6533              : 
    6534              :   /* Account */
    6535            1 :   make_account (&cls->account);
    6536            1 :   cls->account.instance_id = cls->instance.instance.id;
    6537              :   /* Order */
    6538            1 :   make_order ("test_transfers_od_1",
    6539              :               &cls->order);
    6540              : 
    6541              :   /* Signing key */
    6542            1 :   make_exchange_signkey (&cls->signkey);
    6543              : 
    6544              :   /* Deposit */
    6545            1 :   make_deposit (&cls->instance,
    6546            1 :                 &cls->account,
    6547            1 :                 &cls->order,
    6548            1 :                 &cls->signkey,
    6549              :                 &cls->deposit);
    6550              : 
    6551              :   /* Wire fee */
    6552            1 :   make_wire_fee (&cls->signkey,
    6553              :                  &cls->wire_fee[0]);
    6554            1 :   make_wire_fee (&cls->signkey,
    6555              :                  &cls->wire_fee[1]);
    6556            1 :   cls->wire_fee[1].wire_method = "wire-method-2";
    6557            1 :   GNUNET_CRYPTO_hash (cls->wire_fee[1].wire_method,
    6558            1 :                       strlen (cls->wire_fee[1].wire_method) + 1,
    6559              :                       &cls->wire_fee[1].h_wire_method);
    6560              : 
    6561              :   /* Transfers */
    6562            1 :   make_transfer (&cls->signkey,
    6563              :                  1,
    6564            1 :                  &cls->deposit,
    6565              :                  &cls->transfers[0]);
    6566            1 :   cls->transfers[0].confirmed = true;
    6567              :   /* A transfer the exchange reports without any deposits. */
    6568            1 :   make_transfer (&cls->signkey,
    6569              :                  0,
    6570            1 :                  &cls->deposit,
    6571              :                  &cls->transfers[1]);
    6572            1 :   cls->transfers[1].confirmed = true;
    6573              :   /* A transfer where the exchange lists the same coin twice. */
    6574            1 :   make_deposit (&cls->instance,
    6575            1 :                 &cls->account,
    6576            1 :                 &cls->order,
    6577            1 :                 &cls->signkey,
    6578              :                 &cls->deposit2);
    6579              :   {
    6580              :     struct DepositData dup[2];
    6581              : 
    6582            1 :     dup[0] = cls->deposit2;
    6583            1 :     dup[1] = cls->deposit2;
    6584            1 :     make_transfer (&cls->signkey,
    6585              :                    2,
    6586              :                    dup,
    6587              :                    &cls->transfers[2]);
    6588              :   }
    6589            1 :   cls->transfers[2].confirmed = true;
    6590              :   /* Two orders settled by one aggregated wire transfer. */
    6591            1 :   make_order ("test_transfers_od_agg_1",
    6592              :               &cls->orders_agg[0]);
    6593            1 :   make_order ("test_transfers_od_agg_2",
    6594              :               &cls->orders_agg[1]);
    6595            3 :   for (unsigned int i = 0; i < 2; i++)
    6596            2 :     make_deposit (&cls->instance,
    6597            2 :                   &cls->account,
    6598            2 :                   &cls->orders_agg[i],
    6599            2 :                   &cls->signkey,
    6600              :                   &cls->deposits_agg[i]);
    6601            1 :   make_transfer (&cls->signkey,
    6602              :                  2,
    6603            1 :                  cls->deposits_agg,
    6604              :                  &cls->transfers[3]);
    6605            1 :   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            1 :   make_order ("test_transfers_od_perm",
    6609              :               &cls->order_perm);
    6610            3 :   for (unsigned int i = 0; i < 2; i++)
    6611            2 :     make_deposit (&cls->instance,
    6612            2 :                   &cls->account,
    6613            2 :                   &cls->order_perm,
    6614            2 :                   &cls->signkey,
    6615              :                   &cls->deposits_perm[i]);
    6616            1 :   make_transfer (&cls->signkey,
    6617              :                  1,
    6618            1 :                  cls->deposits_perm,
    6619              :                  &cls->transfers[4]);
    6620            1 :   cls->transfers[4].confirmed = true;
    6621            1 : }
    6622              : 
    6623              : 
    6624              : /**
    6625              :  * Cleans up after testing transfers.
    6626              :  *
    6627              :  * @param cls the test data.
    6628              :  */
    6629              : static void
    6630            1 : post_test_transfers (struct TestTransfers_Closure *cls)
    6631              : {
    6632            1 :   GNUNET_array_grow (cls->transfers->data.details,
    6633              :                      cls->transfers->data.details_length,
    6634              :                      0);
    6635            1 :   GNUNET_array_grow (cls->transfers[2].data.details,
    6636              :                      cls->transfers[2].data.details_length,
    6637              :                      0);
    6638            1 :   GNUNET_array_grow (cls->transfers[3].data.details,
    6639              :                      cls->transfers[3].data.details_length,
    6640              :                      0);
    6641            1 :   GNUNET_array_grow (cls->transfers[4].data.details,
    6642              :                      cls->transfers[4].data.details_length,
    6643              :                      0);
    6644            1 :   free_instance_data (&cls->instance);
    6645            1 :   free_order_data (&cls->order);
    6646            1 :   free_order_data (&cls->orders_agg[0]);
    6647            1 :   free_order_data (&cls->orders_agg[1]);
    6648            1 :   free_order_data (&cls->order_perm);
    6649            1 : }
    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            1 : 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            1 :   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            1 :   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            1 :   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            1 :   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            1 :     struct WireFeeData changed = cls->wire_fee[0];
    6693              : 
    6694            1 :     GNUNET_assert (GNUNET_OK ==
    6695              :                    TALER_string_to_amount ("EUR:0.99",
    6696              :                                            &changed.fees.wire));
    6697            1 :     TALER_exchange_offline_wire_fee_sign (changed.wire_method,
    6698              :                                           changed.wire_fee_start,
    6699              :                                           changed.wire_fee_end,
    6700              :                                           &changed.fees,
    6701            1 :                                           &cls->signkey.master_priv,
    6702              :                                           &changed.fee_sig);
    6703            1 :     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            1 :     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            1 :   TEST_RET_ON_FAIL (test_insert_wire_fee (&cls->signkey,
    6712              :                                           &cls->wire_fee[1],
    6713              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    6714            1 :   TEST_RET_ON_FAIL (test_lookup_wire_fee (&cls->signkey,
    6715              :                                           &cls->wire_fee[1]));
    6716              :   /* Insert the instance */
    6717            1 :   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
    6718              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    6719              :   /* Insert the account */
    6720            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey,
    6725              :                                                   GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    6726              :   /* Insert an order */
    6727            1 :   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
    6728              :                                        &cls->order,
    6729              :                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    6730              :   /* Insert contract terms */
    6731            1 :   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
    6732              :                                                 &cls->order,
    6733              :                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    6734            1 :   order_serial = get_order_serial (&cls->instance,
    6735            1 :                                    &cls->order);
    6736              :   /* Insert the deposit */
    6737            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_transfer_unknown_account (&cls->instance,
    6748              :                                                           &cls->transfers[0]));
    6749              :   /* Insert the transfer */
    6750            1 :   TEST_RET_ON_FAIL (test_insert_transfer (&cls->instance,
    6751              :                                           &cls->account,
    6752              :                                           &cls->transfers[0],
    6753              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    6754            1 :   TEST_RET_ON_FAIL (test_insert_transfer (&cls->instance,
    6755              :                                           &cls->account,
    6756              :                                           &cls->transfers[0],
    6757              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    6758            1 :   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            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
    6778              :                                                 cls->order.id,
    6779              :                                                 NULL,
    6780              :                                                 true,
    6781              :                                                 true));
    6782            1 :   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            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (test_lookup_transfer_details_by_order (order_serial,
    6801              :                                                            1,
    6802              :                                                            &cls->transfers[0]));
    6803            1 :   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            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
    6833              :                                          &cls->signkey,
    6834              :                                          &cls->deposit2,
    6835              :                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    6836            1 :   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            1 :   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            3 :   for (unsigned int i = 0; i < 2; i++)
    6851              :   {
    6852            2 :     TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
    6853              :                                          &cls->orders_agg[i],
    6854              :                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    6855            2 :     TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
    6856              :                                                   &cls->orders_agg[i],
    6857              :                                                   GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    6858            2 :     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            2 :     TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
    6863              :                                                &cls->orders_agg[i],
    6864              :                                                GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    6865            2 :     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            1 :     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            1 :     struct GNUNET_DB_EventHeaderP es = {
    6885            1 :       .size = htons (sizeof (es)),
    6886            1 :       .type = htons (TALER_DBEVENT_MERCHANT_WEBHOOK_PENDING)
    6887              :     };
    6888            1 :     struct WebhookPendingEventState event_state = { 0 };
    6889              :     struct GNUNET_DB_EventHandler *eh;
    6890              :     uint64_t pending_count;
    6891            1 :     int webhook_test_result = 1;
    6892              : 
    6893            1 :     TEST_SET_INSTANCE (cls->instance.instance.id,
    6894              :                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    6895            1 :     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            1 :     eh = TALER_MERCHANTDB_event_listen (pg,
    6903              :                                         &es,
    6904            1 :                                         GNUNET_TIME_UNIT_FOREVER_REL,
    6905              :                                         &webhook_pending_event_cb,
    6906              :                                         &event_state);
    6907            1 :     if (NULL == eh)
    6908              :     {
    6909            0 :       GNUNET_break (0);
    6910            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    6911              :                   "Failed to listen for pending webhook events\n");
    6912            0 :       goto webhook_cleanup;
    6913              :     }
    6914              : 
    6915            1 :     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            1 :     GNUNET_PQ_event_do_poll (pg->conn);
    6925            1 :     if (1 != event_state.fired)
    6926              :     {
    6927            0 :       GNUNET_break (0);
    6928            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    6929              :                   "Settlement generated %u pending webhook events, expected 1\n",
    6930              :                   event_state.fired);
    6931            0 :       goto webhook_listener_cleanup;
    6932              :     }
    6933            1 :     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            1 :     if (2 != pending_count)
    6942              :     {
    6943            0 :       GNUNET_break (0);
    6944            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    6945              :                   "Settlement queued %llu webhooks, expected 2\n",
    6946              :                   (unsigned long long) pending_count);
    6947            0 :       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            1 :     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            1 :     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            3 :     for (unsigned int i = 0; i < 2; i++)
    6966            2 :       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            1 :     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            1 :     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            1 :     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            1 :     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            1 :     GNUNET_PQ_event_do_poll (pg->conn);
    7008            1 :     if (1 != event_state.fired)
    7009              :     {
    7010            0 :       GNUNET_break (0);
    7011            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    7012              :                   "Unsettled order generated a pending webhook event\n");
    7013            0 :       goto webhook_listener_cleanup;
    7014              :     }
    7015            1 :     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            1 :     if (2 != pending_count)
    7024              :     {
    7025            0 :       GNUNET_break (0);
    7026            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    7027              :                   "Unsettled order changed the pending webhook count\n");
    7028            0 :       goto webhook_listener_cleanup;
    7029              :     }
    7030            1 :     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            1 :     webhook_test_result = 0;
    7039              : 
    7040            1 : webhook_listener_cleanup:
    7041            1 :     TALER_MERCHANTDB_event_listen_cancel (eh);
    7042            1 : webhook_cleanup:
    7043            1 :     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            1 :     TEST_SET_INSTANCE (cls->instance.instance.id,
    7049              :                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    7050            1 :     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            1 :     if (0 != webhook_test_result)
    7057            0 :       return 1;
    7058              :   }
    7059            1 :   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            1 : test_transfers (void)
    7070              : {
    7071              :   struct TestTransfers_Closure test_cls;
    7072              :   int test_result;
    7073              : 
    7074            1 :   pre_test_transfers (&test_cls);
    7075            1 :   test_result = run_test_transfers (&test_cls);
    7076            1 :   post_test_transfers (&test_cls);
    7077            1 :   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            1 : lookup_refunds_cb (void *cls,
    7121              :                    const struct TALER_CoinSpendPublicKeyP *coin_pub,
    7122              :                    const struct TALER_Amount *refund_amount)
    7123              : {
    7124            1 :   struct TestLookupRefunds_Closure *cmp = cls;
    7125            1 :   if (NULL == cmp)
    7126            0 :     return;
    7127            1 :   cmp->results_length += 1;
    7128            2 :   for (unsigned int i = 0; cmp->refunds_to_cmp_length > i; ++i)
    7129              :   {
    7130            1 :     if ((0 == GNUNET_memcmp (&cmp->coin_pub_to_cmp[i],
    7131            1 :                              coin_pub)) &&
    7132            1 :         (GNUNET_OK == TALER_amount_cmp_currency (&cmp->refund_amount_to_cmp[i],
    7133            1 :                                                  refund_amount)) &&
    7134            1 :         (0 == TALER_amount_cmp (&cmp->refund_amount_to_cmp[i],
    7135              :                                 refund_amount)))
    7136              :     {
    7137            1 :       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            1 : 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            1 : {
    7160            1 :   unsigned int results_matching[refunds_length];
    7161            1 :   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            1 :   memset (results_matching, 0, sizeof (unsigned int) * refunds_length);
    7169            1 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    7170            1 :   if (1 != TALER_MERCHANTDB_iterate_refunds (pg,
    7171            1 :                                              instance->instance.id,
    7172              :                                              h_contract_terms,
    7173              :                                              &lookup_refunds_cb,
    7174              :                                              &cmp))
    7175              :   {
    7176            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    7177              :                 "Lookup refunds failed\n");
    7178            0 :     return 1;
    7179              :   }
    7180            1 :   if (refunds_length != cmp.results_length)
    7181              :   {
    7182            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    7183              :                 "Lookup refunds failed: incorrect number of results returned\n")
    7184              :     ;
    7185            0 :     return 1;
    7186              :   }
    7187            2 :   for (unsigned int i = 0; refunds_length > i; ++i)
    7188              :   {
    7189            1 :     if (1 != cmp.results_matching[i])
    7190              :     {
    7191            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    7192              :                   "Lookup refunds failed: mismatched data\n");
    7193            0 :       return 1;
    7194              :     }
    7195              :   }
    7196            1 :   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           67 : make_refund (const struct DepositData *deposit,
    7239              :              struct RefundData *refund)
    7240              : {
    7241           67 :   refund->timestamp = GNUNET_TIME_timestamp_get ();
    7242           67 :   refund->reason = "some reason";
    7243           67 :   refund->refund_amount = deposit->amount_with_fee;
    7244           67 :   refund->coin_pub = &deposit->coin_pub;
    7245           67 :   refund->exchange_url = deposit->exchange_url;
    7246           67 : }
    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            3 : 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            3 :   struct TestLookupRefundsDetailed_Closure *cmp = cls;
    7324            3 :   if (NULL == cmp)
    7325            0 :     return;
    7326            3 :   cmp->results_length += 1;
    7327            8 :   for (unsigned int i = 0; cmp->refunds_to_cmp_length > i; ++i)
    7328              :   {
    7329            5 :     if (((GNUNET_TIME_timestamp_cmp (cmp->refunds_to_cmp[i].timestamp,
    7330              :                                      ==,
    7331            0 :                                      timestamp)) ||
    7332            0 :          ! cmp->cmp_timestamps) &&
    7333            5 :         (0 == GNUNET_memcmp (cmp->refunds_to_cmp[i].coin_pub,
    7334            5 :                              coin_pub)) &&
    7335            5 :         (0 == strcmp (cmp->refunds_to_cmp[i].exchange_url,
    7336            5 :                       exchange_url)) &&
    7337            5 :         (0 == strcmp (cmp->refunds_to_cmp[i].reason,
    7338            3 :                       reason)) &&
    7339              :         (GNUNET_OK ==
    7340            3 :          TALER_amount_cmp_currency (
    7341            3 :            &cmp->refunds_to_cmp[i].refund_amount,
    7342            3 :            refund_amount)) &&
    7343            3 :         (0 == TALER_amount_cmp (&cmp->refunds_to_cmp[i].refund_amount,
    7344              :                                 refund_amount)))
    7345              :     {
    7346            3 :       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            2 : 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            2 : {
    7370            2 :   unsigned int results_matching[refunds_length];
    7371            2 :   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            2 :   memset (results_matching, 0, sizeof (unsigned int) * refunds_length);
    7379            2 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    7380            2 :   if (0 > TALER_MERCHANTDB_iterate_refunds_detailed (pg,
    7381            2 :                                                      instance->instance.id,
    7382              :                                                      h_contract_terms,
    7383              :                                                      &lookup_refunds_detailed_cb,
    7384              :                                                      &cmp))
    7385              :   {
    7386            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    7387              :                 "Lookup refunds detailed failed\n");
    7388            0 :     return 1;
    7389              :   }
    7390            2 :   if (refunds_length != cmp.results_length)
    7391              :   {
    7392            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    7393              :                 "Lookup refunds detailed failed: incorrect number of results\n")
    7394              :     ;
    7395            0 :     return 1;
    7396              :   }
    7397            5 :   for (unsigned int i = 0; refunds_length > i; ++i)
    7398              :   {
    7399            3 :     if (1 != cmp.results_matching[i])
    7400              :     {
    7401            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    7402              :                   "Lookup refunds detailed failed: mismatched data\n");
    7403            0 :       return 1;
    7404              :     }
    7405              :   }
    7406            2 :   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            1 : 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            1 :   struct LookupRefundSerial_Closure *lookup_cls = cls;
    7451            1 :   if (NULL == lookup_cls)
    7452            0 :     return;
    7453            1 :   if ((GNUNET_TIME_timestamp_cmp (lookup_cls->refund->timestamp,
    7454              :                                   ==,
    7455            1 :                                   timestamp)) &&
    7456            1 :       (0 == GNUNET_memcmp (lookup_cls->refund->coin_pub,
    7457            1 :                            coin_pub)) &&
    7458            1 :       (0 == strcmp (lookup_cls->refund->exchange_url,
    7459            1 :                     exchange_url)) &&
    7460            1 :       (0 == strcmp (lookup_cls->refund->reason,
    7461            1 :                     reason)) &&
    7462              :       (GNUNET_OK ==
    7463            1 :        TALER_amount_cmp_currency (
    7464            1 :          &lookup_cls->refund->refund_amount,
    7465            1 :          refund_amount)) &&
    7466            1 :       (0 == TALER_amount_cmp (&lookup_cls->refund->refund_amount,
    7467              :                               refund_amount)))
    7468            1 :     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            1 : get_refund_serial (const struct InstanceData *instance,
    7482              :                    const struct TALER_PrivateContractHashP *h_contract_terms,
    7483              :                    const struct RefundData *refund)
    7484              : {
    7485            1 :   struct LookupRefundSerial_Closure lookup_cls = {
    7486              :     .refund = refund,
    7487              :     .serial = 0
    7488              :   };
    7489              : 
    7490            1 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    7491              :                  TALER_MERCHANTDB_set_instance (pg,
    7492              :                                                 instance->instance.id));
    7493            1 :   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            1 :   GNUNET_assert (0 != lookup_cls.serial);
    7499              : 
    7500            1 :   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            1 : 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            1 :   if (1 != TALER_MERCHANTDB_get_refund_proof (pg,
    7522              :                                               refund_serial,
    7523              :                                               &exchange_sig,
    7524              :                                               &exchange_pub))
    7525              :   {
    7526            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    7527              :                 "Lookup refund proof failed\n");
    7528            0 :     return 1;
    7529              :   }
    7530            1 :   if ((0 != GNUNET_memcmp (expected_exchange_sig,
    7531            1 :                            &exchange_sig)) ||
    7532            1 :       (0 != GNUNET_memcmp (expected_exchange_pub,
    7533              :                            &exchange_pub)))
    7534              :   {
    7535            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    7536              :                 "Lookup refund proof failed: mismatched data\n");
    7537            0 :     return 1;
    7538              :   }
    7539            1 :   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            1 : pre_test_refunds (struct TestRefunds_Closure *cls)
    7591              : {
    7592              :   /* Instance */
    7593            1 :   make_instance ("test_inst_refunds",
    7594              :                  &cls->instance);
    7595              : 
    7596              :   /* Account */
    7597            1 :   make_account (&cls->account);
    7598            1 :   cls->account.instance_id = cls->instance.instance.id;
    7599              :   /* Signing key */
    7600            1 :   make_exchange_signkey (&cls->signkey);
    7601              : 
    7602              :   /* Order */
    7603            1 :   make_order ("test_refunds_od_0",
    7604              :               &cls->orders[0]);
    7605            1 :   make_order ("test_refunds_od_1",
    7606              :               &cls->orders[1]);
    7607            1 :   make_order ("test_refunds_od_2",
    7608              :               &cls->orders[2]);
    7609              : 
    7610              :   /* Deposit */
    7611            1 :   make_deposit (&cls->instance,
    7612            1 :                 &cls->account,
    7613            1 :                 &cls->orders[0],
    7614            1 :                 &cls->signkey,
    7615              :                 &cls->deposits[0]);
    7616            1 :   make_deposit (&cls->instance,
    7617            1 :                 &cls->account,
    7618            1 :                 &cls->orders[0],
    7619            1 :                 &cls->signkey,
    7620              :                 &cls->deposits[1]);
    7621            1 :   make_deposit (&cls->instance,
    7622            1 :                 &cls->account,
    7623            1 :                 &cls->orders[1],
    7624            1 :                 &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            1 :   make_deposit (&cls->instance,
    7634            1 :                 &cls->account,
    7635            1 :                 &cls->orders[2],
    7636            1 :                 &cls->signkey,
    7637              :                 &cls->deposits[3]);
    7638            1 :   make_deposit (&cls->instance,
    7639            1 :                 &cls->account,
    7640            1 :                 &cls->orders[2],
    7641            1 :                 &cls->signkey,
    7642              :                 &cls->deposits[4]);
    7643            1 :   cls->deposits[4].exchange_url = "https://test-exchange-two/";
    7644            1 :   cls->deposits[4].coin_pub = cls->deposits[3].coin_pub;
    7645            1 :   cls->deposits[4].coin_sig = cls->deposits[3].coin_sig;
    7646              : 
    7647              :   /* Refund */
    7648            1 :   make_refund (&cls->deposits[0],
    7649              :                &cls->refunds[0]);
    7650            1 :   make_refund (&cls->deposits[2],
    7651              :                &cls->refunds[1]);
    7652            1 :   make_refund (&cls->deposits[2],
    7653              :                &cls->refunds[2]);
    7654            1 :   GNUNET_assert (GNUNET_OK ==
    7655              :                  TALER_string_to_amount ("EUR:10.00",
    7656              :                                          &cls->refunds[1].refund_amount));
    7657            1 :   cls->refunds[1].reason = "refund 1";
    7658            1 :   GNUNET_assert (GNUNET_OK ==
    7659              :                  TALER_string_to_amount ("EUR:10.00",
    7660              :                                          &cls->refunds[2].refund_amount));
    7661            1 :   cls->refunds[2].reason = "refund 2";
    7662              : 
    7663              :   /* Refund proof */
    7664            1 :   GNUNET_assert (GNUNET_OK ==
    7665              :                  TALER_string_to_amount ("EUR:0.02",
    7666              :                                          &cls->refund_proof.refund_fee));
    7667            1 :   memset (&cls->refund_proof.exchange_sig,
    7668              :           42,
    7669              :           sizeof (cls->refund_proof.exchange_sig));
    7670            1 : }
    7671              : 
    7672              : 
    7673              : /**
    7674              :  * Cleans up after testing refunds.
    7675              :  * @param cls the closure.
    7676              :  */
    7677              : static void
    7678            1 : post_test_refunds (struct TestRefunds_Closure *cls)
    7679              : {
    7680            1 :   free_instance_data (&cls->instance);
    7681            1 :   free_order_data (&cls->orders[0]);
    7682            1 :   free_order_data (&cls->orders[1]);
    7683            1 :   free_order_data (&cls->orders[2]);
    7684            1 : }
    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            1 : run_test_refunds (struct TestRefunds_Closure *cls)
    7695              : {
    7696              :   struct TALER_Amount inc;
    7697              :   uint64_t refund_serial;
    7698              : 
    7699              :   /* Insert an instance */
    7700            1 :   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
    7701              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    7702              :   /* Insert an account */
    7703            1 :   TEST_RET_ON_FAIL (test_insert_account (&cls->instance,
    7704              :                                          &cls->account,
    7705              :                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    7706              :   /* Insert an order */
    7707            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey,
    7716              :                                                   GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    7717              :   /* Insert a deposit */
    7718            1 :   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
    7719              :                                          &cls->signkey,
    7720              :                                          &cls->deposits[0],
    7721              :                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    7722            1 :   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            1 :   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            1 :   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            1 :   refund_serial = get_refund_serial (&cls->instance,
    7741            1 :                                      &cls->deposits[0].h_contract_terms,
    7742            1 :                                      &cls->refunds[0]);
    7743              :   /* Test double refund fails */
    7744            1 :   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            1 :   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            1 :   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            1 :   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            1 :   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            1 :   GNUNET_assert (GNUNET_OK ==
    7785              :                  TALER_string_to_amount ("EUR:1000.00",
    7786              :                                          &inc));
    7787            1 :   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            1 :   GNUNET_assert (GNUNET_OK ==
    7798              :                  TALER_string_to_amount ("EUR:1.00",
    7799              :                                          &inc));
    7800            1 :   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            1 :   TEST_RET_ON_FAIL (test_lookup_refund_proof (1,
    7811              :                                               &cls->refund_proof.exchange_sig,
    7812              :                                               &cls->signkey.exchange_pub));
    7813            1 :   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
    7814              :                                        &cls->orders[1],
    7815              :                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    7816            1 :   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
    7817              :                                                 &cls->orders[1],
    7818              :                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    7819            1 :   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
    7820              :                                          &cls->signkey,
    7821              :                                          &cls->deposits[2],
    7822              :                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    7823            1 :   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            1 :   GNUNET_assert (GNUNET_OK ==
    7828              :                  TALER_string_to_amount ("EUR:10.00",
    7829              :                                          &inc));
    7830            1 :   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            1 :   GNUNET_assert (GNUNET_OK ==
    7840              :                  TALER_string_to_amount ("EUR:20.00",
    7841              :                                          &inc));
    7842            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
    7865              :                                        &cls->orders[2],
    7866              :                                        GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    7867            1 :   TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
    7868              :                                                 &cls->orders[2],
    7869              :                                                 GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    7870            1 :   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
    7871              :                                          &cls->signkey,
    7872              :                                          &cls->deposits[3],
    7873              :                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    7874            1 :   TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
    7875              :                                          &cls->signkey,
    7876              :                                          &cls->deposits[4],
    7877              :                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    7878            1 :   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            1 :   GNUNET_assert (GNUNET_OK ==
    7884              :                  TALER_string_to_amount ("EUR:60.00",
    7885              :                                          &inc));
    7886            1 :   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            1 :   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            1 : test_refunds (void)
    7906              : {
    7907              :   struct TestRefunds_Closure test_cls;
    7908              :   int test_result;
    7909              : 
    7910            1 :   pre_test_refunds (&test_cls);
    7911            1 :   test_result = run_test_refunds (&test_cls);
    7912            1 :   post_test_refunds (&test_cls);
    7913            1 :   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           27 : reverse_order_data_array (unsigned int orders_length,
    7924              :                           struct OrderData *orders)
    7925           27 : {
    7926           27 :   struct OrderData tmp[orders_length];
    7927          539 :   for (unsigned int i = 0; i < orders_length; ++i)
    7928          512 :     tmp[i] = orders[orders_length - 1 - i];
    7929          539 :   for (unsigned int i = 0; i < orders_length; ++i)
    7930          512 :     orders[i] = tmp[i];
    7931           27 : }
    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            1 : pre_test_lookup_orders_all_filters (
    7982              :   struct TestLookupOrdersAllFilters_Closure *cls)
    7983              : {
    7984            1 :   make_instance ("test_inst_lookup_orders_all_filters",
    7985              :                  &cls->instance);
    7986            1 :   make_account (&cls->account);
    7987            1 :   cls->account.instance_id = cls->instance.instance.id;
    7988            1 :   make_exchange_signkey (&cls->signkey);
    7989           65 :   for (unsigned int i = 0; i < 64; ++i)
    7990              :   {
    7991           64 :     (void) GNUNET_asprintf (&cls->order_ids[i],
    7992              :                             "test_orders_filter_od_%u",
    7993              :                             i);
    7994           64 :     make_order (cls->order_ids[i],
    7995              :                 &cls->orders[i]);
    7996           64 :     GNUNET_assert (0 ==
    7997              :                    json_object_set_new (cls->orders[i].contract,
    7998              :                                         "order_id",
    7999              :                                         json_string (cls->order_ids[i])));
    8000           64 :     make_deposit (&cls->instance,
    8001           64 :                   &cls->account,
    8002           64 :                   &cls->orders[i],
    8003           64 :                   &cls->signkey,
    8004              :                   &cls->deposits[i]);
    8005           64 :     make_refund (&cls->deposits[i],
    8006              :                  &cls->refunds[i]);
    8007              :   }
    8008            1 : }
    8009              : 
    8010              : 
    8011              : /**
    8012              :  * Cleans up after testing lookup order filters.
    8013              :  * @param cls the closure.
    8014              :  */
    8015              : static void
    8016            1 : post_test_lookup_orders_all_filters (
    8017              :   struct TestLookupOrdersAllFilters_Closure *cls)
    8018              : {
    8019            1 :   free_instance_data (&cls->instance);
    8020           65 :   for (unsigned int i = 0; i < 64; ++i)
    8021              :   {
    8022           64 :     free_order_data (&cls->orders[i]);
    8023           64 :     GNUNET_free (cls->order_ids[i]);
    8024              :   }
    8025            1 : }
    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            1 : 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            1 :   permutation = GNUNET_CRYPTO_random_permute (64);
    8050           65 :   for (unsigned int i = 0; i < 64; ++i)
    8051              :   {
    8052           64 :     unsigned int dest = permutation[i];
    8053           64 :     order_status[dest].claimed = (i & 1) ? true : false;
    8054           64 :     order_status[dest].paid = (3 == (i & 3)) ? true : false;
    8055           64 :     order_status[dest].refunded = (5 == (i & 5)) ? true : false;
    8056           64 :     order_status[dest].wired = (9 == (i & 9)) ? true : false;
    8057              :   }
    8058            1 :   GNUNET_free (permutation);
    8059              : 
    8060              : 
    8061            1 :   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
    8062              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    8063            1 :   TEST_RET_ON_FAIL (test_insert_account (&cls->instance,
    8064              :                                          &cls->account,
    8065              :                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    8066            1 :   TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey,
    8067              :                                                   GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    8068           65 :   for (unsigned int i = 0; i < 64; ++i)
    8069              :   {
    8070              :     uint64_t order_serial;
    8071              : 
    8072           64 :     TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
    8073              :                                          &cls->orders[i],
    8074              :                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    8075           64 :     order_serial = get_order_serial (&cls->instance,
    8076           64 :                                      &cls->orders[i]);
    8077              : 
    8078           64 :     if (order_status[i].claimed)
    8079              :     {
    8080           32 :       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           32 :       continue;
    8087              :     }
    8088              : 
    8089           32 :     if (order_status[i].paid)
    8090           16 :       TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
    8091              :                                                  &cls->orders[i],
    8092              :                                                  GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    8093           32 :     if (order_status[i].refunded)
    8094              :     {
    8095           16 :       TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
    8096              :                                              &cls->signkey,
    8097              :                                              &cls->deposits[i],
    8098              :                                              GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    8099           16 :       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           32 :     if (order_status[i].wired)
    8111           16 :       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           28 :   for (unsigned int i = 0; i < 27; ++i)
    8117              :   {
    8118           27 :     struct TALER_MERCHANTDB_OrderFilter filter = {
    8119           27 :       .paid = (i % 3) + 1,
    8120           27 :       .refunded = ((i / 3) % 3) + 1,
    8121           27 :       .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           27 :     unsigned int orders_length = 0;
    8128              :     struct OrderData orders[64];
    8129              : 
    8130              :     /* Now figure out which orders should make it through the filter */
    8131         1755 :     for (unsigned int j = 0; j < 64; ++j)
    8132              :     {
    8133         1728 :       if (((TALER_EXCHANGE_YNA_YES == filter.paid) &&
    8134          576 :            (true != order_status[j].paid)) ||
    8135         1296 :           ((TALER_EXCHANGE_YNA_NO == filter.paid) &&
    8136          576 :            (false != order_status[j].paid)) ||
    8137         1152 :           ((TALER_EXCHANGE_YNA_YES == filter.refunded) &&
    8138          384 :            (true != order_status[j].refunded)) ||
    8139          864 :           ((TALER_EXCHANGE_YNA_NO == filter.refunded) &&
    8140          384 :            (false != order_status[j].refunded)) ||
    8141          768 :           ((TALER_EXCHANGE_YNA_YES == filter.wired) &&
    8142          256 :            (true != order_status[j].wired)) ||
    8143          576 :           ((TALER_EXCHANGE_YNA_NO == filter.wired) &&
    8144          256 :            (false != order_status[j].wired)))
    8145         1216 :         continue;
    8146          512 :       orders[orders_length] = cls->orders[j];
    8147          512 :       orders_length += 1;
    8148              :     }
    8149              : 
    8150              :     /* Test the lookup */
    8151           27 :     TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
    8152              :                                           &filter,
    8153              :                                           orders_length,
    8154              :                                           orders));
    8155              : 
    8156              :     /* Now test decreasing */
    8157           27 :     filter.start_row = 256;
    8158           27 :     filter.date = GNUNET_TIME_UNIT_FOREVER_TS;
    8159           27 :     filter.delta = -64;
    8160              : 
    8161           27 :     reverse_order_data_array (orders_length,
    8162              :                               orders);
    8163              : 
    8164           27 :     TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
    8165              :                                           &filter,
    8166              :                                           orders_length,
    8167              :                                           orders));
    8168              :   }
    8169              : 
    8170            1 :   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            1 : test_lookup_orders_all_filters (void)
    8181              : {
    8182              :   struct TestLookupOrdersAllFilters_Closure test_cls;
    8183              :   int test_result;
    8184              : 
    8185            1 :   memset (&test_cls,
    8186              :           0,
    8187              :           sizeof (test_cls));
    8188            1 :   pre_test_lookup_orders_all_filters (&test_cls);
    8189            1 :   test_result = run_test_lookup_orders_all_filters (&test_cls);
    8190            1 :   post_test_lookup_orders_all_filters (&test_cls);
    8191            1 :   return test_result;
    8192              : }
    8193              : 
    8194              : 
    8195              : static void
    8196            3 : 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            3 :   bool *fail = cls;
    8210              : 
    8211            3 :   if (kyc_ok)
    8212            2 :     *fail = false;
    8213            3 : }
    8214              : 
    8215              : 
    8216              : static void
    8217            3 : 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            3 :   bool *fail = cls;
    8231              : 
    8232            3 :   if (! kyc_ok)
    8233            2 :     *fail = false;
    8234            3 : }
    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            2 : 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            2 :   struct KycFailureExpected *expected = cls;
    8304              : 
    8305            2 :   expected->called++;
    8306            2 :   if ( (0 != GNUNET_memcmp (&expected->account->h_wire,
    8307            2 :                             h_wire)) ||
    8308            2 :        (0 != strcmp (expected->account->payto_uri.full_payto,
    8309            2 :                      payto_uri.full_payto)) ||
    8310            2 :        (0 != strcmp (expected->exchange_url,
    8311            2 :                      exchange_url)) ||
    8312            2 :        (GNUNET_TIME_timestamp_cmp (expected->timestamp,
    8313              :                                    !=,
    8314            2 :                                    last_check)) ||
    8315            2 :        kyc_ok ||
    8316            2 :        (NULL != access_token) ||
    8317            2 :        (expected->exchange_http_status != last_http_status) ||
    8318            2 :        (TALER_EC_NONE != last_ec) ||
    8319            2 :        in_aml_review ||
    8320              :        (NULL != jlimits) )
    8321              :   {
    8322            0 :     GNUNET_break (0);
    8323            0 :     expected->failed = true;
    8324              :   }
    8325            2 : }
    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            4 : kyc_event_cb (void *cls,
    8337              :               const void *extra,
    8338              :               size_t extra_size)
    8339              : {
    8340            4 :   unsigned int *fired = cls;
    8341              : 
    8342              :   (void) extra;
    8343              :   (void) extra_size;
    8344            4 :   (*fired)++;
    8345            4 : }
    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            1 : 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            1 :   struct TALER_MERCHANTDB_MerchantKycStatusChangeEventP account_event = {
    8364            1 :     .header.size = htons (sizeof (account_event)),
    8365            1 :     .header.type = htons (
    8366              :       TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_STATUS_CHANGED),
    8367              :     .h_wire = account->h_wire
    8368              :   };
    8369            1 :   struct GNUNET_DB_EventHeaderP general_event = {
    8370            1 :     .size = htons (sizeof (general_event)),
    8371            1 :     .type = htons (TALER_DBEVENT_MERCHANT_KYC_STATUS_CHANGED)
    8372              :   };
    8373            1 :   struct GNUNET_DB_EventHandler *account_eh = NULL;
    8374            1 :   struct GNUNET_DB_EventHandler *general_eh = NULL;
    8375            1 :   unsigned int account_events = 0;
    8376            1 :   unsigned int general_events = 0;
    8377            1 :   struct KycFailureExpected expected = {
    8378              :     .account = account,
    8379              :     .exchange_url = exchange_url,
    8380              :     .exchange_http_status = MHD_HTTP_BAD_GATEWAY
    8381              :   };
    8382            1 :   int ret = 1;
    8383              : 
    8384            1 :   TEST_SET_INSTANCE (instance->instance.id,
    8385              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    8386            1 :   account_eh = TALER_MERCHANTDB_event_listen (
    8387              :     pg,
    8388              :     &account_event.header,
    8389            1 :     GNUNET_TIME_UNIT_FOREVER_REL,
    8390              :     &kyc_event_cb,
    8391              :     &account_events);
    8392            1 :   if (NULL == account_eh)
    8393              :   {
    8394            0 :     GNUNET_break (0);
    8395            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    8396              :                 "Failed to listen for account KYC events\n");
    8397            0 :     goto cleanup;
    8398              :   }
    8399            1 :   general_eh = TALER_MERCHANTDB_event_listen (
    8400              :     pg,
    8401              :     &general_event,
    8402            1 :     GNUNET_TIME_UNIT_FOREVER_REL,
    8403              :     &kyc_event_cb,
    8404              :     &general_events);
    8405            1 :   if (NULL == general_eh)
    8406              :   {
    8407            0 :     GNUNET_break (0);
    8408            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    8409              :                 "Failed to listen for general KYC events\n");
    8410            0 :     goto cleanup;
    8411              :   }
    8412              : 
    8413            1 :   expected.timestamp = GNUNET_TIME_timestamp_get ();
    8414            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    8415            1 :       TALER_MERCHANTDB_insert_kyc_failure (
    8416              :         pg,
    8417            1 :         instance->instance.id,
    8418              :         &account->h_wire,
    8419              :         exchange_url,
    8420              :         expected.timestamp,
    8421              :         expected.exchange_http_status,
    8422              :         false))
    8423              :   {
    8424            0 :     GNUNET_break (0);
    8425            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    8426              :                 "Inserting KYC failure failed\n");
    8427            0 :     goto cleanup;
    8428              :   }
    8429            1 :   GNUNET_PQ_event_do_poll (pg->conn);
    8430            1 :   if ( (1 != account_events) ||
    8431            1 :        (1 != general_events) )
    8432              :   {
    8433            0 :     GNUNET_break (0);
    8434            0 :     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            0 :     goto cleanup;
    8440              :   }
    8441            1 :   if (1 != TALER_MERCHANTDB_iterate_kyc_statuses (
    8442              :         pg,
    8443            1 :         instance->instance.id,
    8444              :         &account->h_wire,
    8445              :         exchange_url,
    8446              :         &kyc_failure_check,
    8447              :         &expected))
    8448              :   {
    8449            0 :     GNUNET_break (0);
    8450            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    8451              :                 "Looking up inserted KYC failure failed\n");
    8452            0 :     goto cleanup;
    8453              :   }
    8454            1 :   if (expected.failed ||
    8455            1 :       (1 != expected.called))
    8456              :   {
    8457            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    8458              :                 "Inserted KYC failure did not match expectations\n");
    8459            0 :     goto cleanup;
    8460              :   }
    8461              : 
    8462            1 :   expected.timestamp = GNUNET_TIME_timestamp_get ();
    8463            1 :   expected.exchange_http_status = MHD_HTTP_ACCEPTED;
    8464            1 :   expected.called = 0;
    8465            1 :   expected.failed = false;
    8466            1 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    8467            1 :       TALER_MERCHANTDB_insert_kyc_failure (
    8468              :         pg,
    8469            1 :         instance->instance.id,
    8470              :         &account->h_wire,
    8471              :         exchange_url,
    8472              :         expected.timestamp,
    8473              :         expected.exchange_http_status,
    8474              :         false))
    8475              :   {
    8476            0 :     GNUNET_break (0);
    8477            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    8478              :                 "Updating KYC failure failed\n");
    8479            0 :     goto cleanup;
    8480              :   }
    8481            1 :   GNUNET_PQ_event_do_poll (pg->conn);
    8482            1 :   if ( (2 != account_events) ||
    8483            1 :        (2 != general_events) )
    8484              :   {
    8485            0 :     GNUNET_break (0);
    8486            0 :     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            0 :     goto cleanup;
    8492              :   }
    8493            1 :   if (1 != TALER_MERCHANTDB_iterate_kyc_statuses (
    8494              :         pg,
    8495            1 :         instance->instance.id,
    8496              :         &account->h_wire,
    8497              :         exchange_url,
    8498              :         &kyc_failure_check,
    8499              :         &expected))
    8500              :   {
    8501            0 :     GNUNET_break (0);
    8502            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    8503              :                 "Looking up updated KYC failure failed\n");
    8504            0 :     goto cleanup;
    8505              :   }
    8506            1 :   if (expected.failed ||
    8507            1 :       (1 != expected.called))
    8508              :   {
    8509            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    8510              :                 "Updated KYC failure did not match expectations\n");
    8511            0 :     goto cleanup;
    8512              :   }
    8513            1 :   ret = 0;
    8514              : 
    8515            1 : cleanup:
    8516            1 :   if (NULL != general_eh)
    8517            1 :     TALER_MERCHANTDB_event_listen_cancel (general_eh);
    8518            1 :   if (NULL != account_eh)
    8519            1 :     TALER_MERCHANTDB_event_listen_cancel (account_eh);
    8520            1 :   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            1 : 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            1 :   make_instance ("test_kyc",
    8539              :                  &instance);
    8540            1 :   make_account (&account);
    8541            1 :   account.instance_id = instance.instance.id;
    8542            1 :   TEST_RET_ON_FAIL (test_insert_instance (&instance,
    8543              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    8544            1 :   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            1 :   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            1 :   now = GNUNET_TIME_timestamp_get ();
    8556            1 :   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            1 :   TEST_RET_ON_FAIL (query_sql_num ("SELECT COUNT(*) AS num"
    8579              :                                    "  FROM merchant_reports"
    8580              :                                    " WHERE one_shot_hidden",
    8581              :                                    &alerts));
    8582            1 :   TEST_COND_RET_ON_FAIL (1 == alerts,
    8583              :                          "KYC status change did not queue an alert report\n");
    8584            1 :   TEST_RET_ON_FAIL (test_insert_kyc_failure (&instance,
    8585              :                                              &account));
    8586            1 :   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            1 :   fail = true;
    8602            1 :   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            1 :   TEST_RET_ON_FAIL (fail);
    8610            1 :   fail = true;
    8611            1 :   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            1 :   TEST_RET_ON_FAIL (fail);
    8619            1 :   fail = true;
    8620            1 :   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            1 :   TEST_RET_ON_FAIL (fail);
    8628            1 :   fail = true;
    8629            1 :   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            1 :   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            1 :   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            1 :   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            1 :     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            1 :     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            1 :     make_account (&account2);
    8685            1 :     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            1 :       = (char *) "payto://x-taler-bank/bank.demo.taler.net/5";
    8689            1 :     TEST_RET_ON_FAIL (test_insert_account (&instance,
    8690              :                                            &account2,
    8691              :                                            GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    8692            1 :     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            1 :     TEST_COND_RET_ON_FAIL (1 == acc_alerts,
    8699              :                            "Adding an account did not queue an alert\n");
    8700              :   }
    8701            1 :   json_decref (instance.instance.address);
    8702            1 :   json_decref (instance.instance.jurisdiction);
    8703            1 :   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            2 : make_template (const char *id,
    8734              :                struct TemplateData *template)
    8735              : {
    8736            2 :   template->id = id;
    8737            2 :   template->template.template_description = "This is a test template";
    8738            2 :   template->template.otp_id = NULL;
    8739            2 :   template->template.template_contract = json_array ();
    8740            2 :   GNUNET_assert (NULL != template->template.template_contract);
    8741            2 : }
    8742              : 
    8743              : 
    8744              : /**
    8745              :  * Frees memory associated with @e TemplateData.
    8746              :  *
    8747              :  * @param template the container to free.
    8748              :  */
    8749              : static void
    8750            2 : free_template_data (struct TemplateData *template)
    8751              : {
    8752            2 :   GNUNET_free (template->template.otp_id);
    8753            2 :   json_decref (template->template.template_contract);
    8754            2 : }
    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            2 : check_templates_equal (const struct TALER_MERCHANTDB_TemplateDetails *a,
    8766              :                        const struct TALER_MERCHANTDB_TemplateDetails *b)
    8767              : {
    8768            2 :   if ((0 != strcmp (a->template_description,
    8769            2 :                     b->template_description)) ||
    8770            2 :       ( (NULL == a->otp_id) && (NULL != b->otp_id)) ||
    8771            2 :       ( (NULL != a->otp_id) && (NULL == b->otp_id)) ||
    8772            2 :       ( (NULL != a->otp_id) && (0 != strcmp (a->otp_id,
    8773            3 :                                              b->otp_id))) ||
    8774            2 :       (1 != json_equal (a->template_contract,
    8775            2 :                         b->template_contract)))
    8776            0 :     return 1;
    8777            2 :   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            4 : test_insert_template (const struct InstanceData *instance,
    8791              :                       const struct TemplateData *template,
    8792              :                       enum GNUNET_DB_QueryStatus expected_result)
    8793              : {
    8794            4 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    8795            3 :   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            3 :   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            2 : test_update_template (const struct InstanceData *instance,
    8816              :                       const struct TemplateData *template,
    8817              :                       enum GNUNET_DB_QueryStatus expected_result)
    8818              : {
    8819            2 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    8820            2 :   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            2 :   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            2 : test_lookup_template (const struct InstanceData *instance,
    8839              :                       const struct TemplateData *template)
    8840              : {
    8841            2 :   const struct TALER_MERCHANTDB_TemplateDetails *to_cmp
    8842              :     = &template->template;
    8843              :   struct TALER_MERCHANTDB_TemplateDetails lookup_result;
    8844              : 
    8845            2 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    8846            2 :   if (0 > TALER_MERCHANTDB_get_template (pg,
    8847            2 :                                          instance->instance.id,
    8848            2 :                                          template->id,
    8849              :                                          &lookup_result))
    8850              :   {
    8851            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    8852              :                 "Lookup template failed\n");
    8853            0 :     TALER_MERCHANTDB_template_details_free (&lookup_result);
    8854            0 :     return 1;
    8855              :   }
    8856            2 :   if (0 != check_templates_equal (&lookup_result,
    8857              :                                   to_cmp))
    8858              :   {
    8859            0 :     GNUNET_break (0);
    8860            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    8861              :                 "Lookup template failed: incorrect template returned\n");
    8862            0 :     TALER_MERCHANTDB_template_details_free (&lookup_result);
    8863            0 :     return 1;
    8864              :   }
    8865            2 :   TALER_MERCHANTDB_template_details_free (&lookup_result);
    8866            2 :   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            3 : lookup_templates_cb (void *cls,
    8905              :                      const char *template_id,
    8906              :                      const char *template_description)
    8907              : {
    8908            3 :   struct TestLookupTemplates_Closure *cmp = cls;
    8909              : 
    8910            3 :   if (NULL == cmp)
    8911            0 :     return;
    8912            3 :   cmp->results_length += 1;
    8913            8 :   for (unsigned int i = 0; cmp->templates_to_cmp_length > i; ++i)
    8914              :   {
    8915            5 :     if ( (0 == strcmp (cmp->templates_to_cmp[i].id,
    8916            3 :                        template_id)) &&
    8917            3 :          (0 == strcmp (cmp->templates_to_cmp[i].template.template_description,
    8918              :                        template_description)) )
    8919            3 :       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            2 : test_lookup_templates (const struct InstanceData *instance,
    8934              :                        unsigned int templates_length,
    8935              :                        const struct TemplateData *templates)
    8936            2 : {
    8937            2 :   unsigned int results_matching[templates_length];
    8938            2 :   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            2 :   memset (results_matching,
    8946              :           0,
    8947              :           sizeof (unsigned int) * templates_length);
    8948            2 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    8949            2 :   if (0 > TALER_MERCHANTDB_iterate_templates (pg,
    8950            2 :                                               instance->instance.id,
    8951              :                                               &lookup_templates_cb,
    8952              :                                               &cls))
    8953              :   {
    8954            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    8955              :                 "Lookup templates failed\n");
    8956            0 :     return 1;
    8957              :   }
    8958            2 :   if (templates_length != cls.results_length)
    8959              :   {
    8960            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    8961              :                 "Lookup templates failed: incorrect number of results\n");
    8962            0 :     return 1;
    8963              :   }
    8964            5 :   for (unsigned int i = 0; templates_length > i; ++i)
    8965              :   {
    8966            3 :     if (1 != cls.results_matching[i])
    8967              :     {
    8968            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    8969              :                   "Lookup templates failed: mismatched data\n");
    8970            0 :       return 1;
    8971              :     }
    8972              :   }
    8973            2 :   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            2 : test_delete_template (const struct InstanceData *instance,
    8987              :                       const struct TemplateData *template,
    8988              :                       enum GNUNET_DB_QueryStatus expected_result)
    8989              : {
    8990            2 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    8991            2 :   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            2 :   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            1 : pre_test_templates (struct TestTemplates_Closure *cls)
    9024              : {
    9025              :   /* Instance */
    9026            1 :   make_instance ("test_inst_templates",
    9027              :                  &cls->instance);
    9028              : 
    9029              :   /* Templates */
    9030            1 :   make_template ("test_templates_pd_0",
    9031              :                  &cls->templates[0]);
    9032              : 
    9033            1 :   make_template ("test_templates_pd_1",
    9034              :                  &cls->templates[1]);
    9035            1 :   cls->templates[1].template.template_description =
    9036              :     "This is a another test template";
    9037            1 : }
    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            1 : post_test_templates (struct TestTemplates_Closure *cls)
    9047              : {
    9048            1 :   free_instance_data (&cls->instance);
    9049            1 :   free_template_data (&cls->templates[0]);
    9050            1 :   free_template_data (&cls->templates[1]);
    9051            1 : }
    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            1 : run_test_templates (struct TestTemplates_Closure *cls)
    9062              : {
    9063              : 
    9064              :   /* Test that insert without an instance fails */
    9065            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
    9070              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    9071              :   /* Test inserting a template */
    9072            1 :   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            1 :   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            1 :   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            1 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    9087            1 :         TALER_MERCHANTDB_get_template (pg,
    9088            1 :                                        cls->instance.instance.id,
    9089              :                                        "nonexistent_template",
    9090              :                                        &tx))
    9091              :     {
    9092            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    9093              :                   "Lookup template failed\n");
    9094            0 :       return 1;
    9095              :     }
    9096              :   }
    9097              :   /* Test template update */
    9098            1 :   cls->templates[0].template.template_description =
    9099              :     "This is a test template that has been updated!";
    9100            1 :   GNUNET_free (cls->templates[0].template.otp_id);
    9101            1 :   cls->templates[0].template.otp_id = GNUNET_strdup ("otp_id");
    9102              :   {
    9103              :     /* ensure OTP device exists */
    9104            1 :     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            1 :     GNUNET_assert (0 <=
    9111              :                    TALER_MERCHANTDB_insert_otp_device (pg,
    9112              :                                                        cls->instance.instance.id,
    9113              :                                                        "otp_id",
    9114              :                                                        &td));
    9115              :   }
    9116            1 :   GNUNET_assert (0 ==
    9117              :                  json_array_append_new (
    9118              :                    cls->templates[0].template.template_contract,
    9119              :                    json_string ("This is a test. 3CH.")));
    9120            1 :   TEST_RET_ON_FAIL (test_update_template (&cls->instance,
    9121              :                                           &cls->templates[0],
    9122              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    9123            1 :   TEST_RET_ON_FAIL (test_lookup_template (&cls->instance,
    9124              :                                           &cls->templates[0]));
    9125            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_template (&cls->instance,
    9130              :                                           &cls->templates[1],
    9131              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    9132            1 :   TEST_RET_ON_FAIL (test_lookup_templates (&cls->instance,
    9133              :                                            2,
    9134              :                                            cls->templates));
    9135              : 
    9136              :   /* Test template deletion */
    9137            1 :   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            1 :   TEST_RET_ON_FAIL (test_delete_template (&cls->instance,
    9142              :                                           &cls->templates[1],
    9143              :                                           GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
    9144            1 :   TEST_RET_ON_FAIL (test_lookup_templates (&cls->instance,
    9145              :                                            1,
    9146              :                                            cls->templates));
    9147            1 :   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            1 : test_templates (void)
    9158              : {
    9159              :   struct TestTemplates_Closure test_cls;
    9160              :   int test_result;
    9161              : 
    9162            1 :   memset (&test_cls,
    9163              :           0,
    9164              :           sizeof (test_cls));
    9165            1 :   pre_test_templates (&test_cls);
    9166            1 :   test_result = run_test_templates (&test_cls);
    9167            1 :   post_test_templates (&test_cls);
    9168            1 :   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            8 : make_webhook (const char *id,
    9200              :               struct WebhookData *webhook)
    9201              : {
    9202            8 :   webhook->id = id;
    9203            8 :   webhook->webhook.event_type = "Paid";
    9204            8 :   webhook->webhook.url = "https://exampletest.com";
    9205            8 :   webhook->webhook.http_method = "POST";
    9206            8 :   webhook->webhook.header_template = "Authorization:XYJAORKJEO";
    9207            8 :   webhook->webhook.body_template = "$Amount";
    9208            8 : }
    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            2 : check_webhooks_equal (const struct TALER_MERCHANTDB_WebhookDetails *a,
    9220              :                       const struct TALER_MERCHANTDB_WebhookDetails *b)
    9221              : {
    9222            2 :   if ((0 != strcmp (a->event_type,
    9223            2 :                     b->event_type)) ||
    9224            2 :       (0 != strcmp (a->url,
    9225            2 :                     b->url)) ||
    9226            2 :       (0 != strcmp (a->http_method,
    9227            2 :                     b->http_method)) ||
    9228            2 :       (0 != strcmp (a->header_template,
    9229            2 :                     b->header_template)) ||
    9230            2 :       (0 != strcmp (a->body_template,
    9231            2 :                     b->body_template)))
    9232            0 :     return 1;
    9233            2 :   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           10 : test_insert_webhook (const struct InstanceData *instance,
    9247              :                      const struct WebhookData *webhook,
    9248              :                      enum GNUNET_DB_QueryStatus expected_result)
    9249              : {
    9250           10 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    9251            9 :   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            9 :   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            2 : test_update_webhook (const struct InstanceData *instance,
    9271              :                      const struct WebhookData *webhook,
    9272              :                      enum GNUNET_DB_QueryStatus expected_result)
    9273              : {
    9274            2 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    9275            2 :   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            2 :   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            2 : test_lookup_webhook (const struct InstanceData *instance,
    9294              :                      const struct WebhookData *webhook)
    9295              : {
    9296            2 :   const struct TALER_MERCHANTDB_WebhookDetails *to_cmp = &webhook->webhook;
    9297              :   struct TALER_MERCHANTDB_WebhookDetails lookup_result;
    9298              : 
    9299            2 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    9300            2 :   if (0 > TALER_MERCHANTDB_get_webhook (pg,
    9301            2 :                                         instance->instance.id,
    9302            2 :                                         webhook->id,
    9303              :                                         &lookup_result))
    9304              :   {
    9305            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    9306              :                 "Lookup webhook failed\n");
    9307            0 :     TALER_MERCHANTDB_webhook_details_free (&lookup_result);
    9308            0 :     return 1;
    9309              :   }
    9310            2 :   if (0 != check_webhooks_equal (&lookup_result,
    9311              :                                  to_cmp))
    9312              :   {
    9313            0 :     GNUNET_break (0);
    9314            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    9315              :                 "Lookup webhook failed: incorrect webhook returned\n");
    9316            0 :     TALER_MERCHANTDB_webhook_details_free (&lookup_result);
    9317            0 :     return 1;
    9318              :   }
    9319            2 :   TALER_MERCHANTDB_webhook_details_free (&lookup_result);
    9320            2 :   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            4 : lookup_webhooks_cb (void *cls,
    9359              :                     const char *webhook_id,
    9360              :                     const char *event_type)
    9361              : {
    9362            4 :   struct TestLookupWebhooks_Closure *cmp = cls;
    9363            4 :   if (NULL == cmp)
    9364            0 :     return;
    9365            4 :   cmp->results_length += 1;
    9366           12 :   for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i)
    9367              :   {
    9368            8 :     if ((0 == strcmp (cmp->webhooks_to_cmp[i].id,
    9369            4 :                       webhook_id)) &&
    9370            4 :         (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.event_type,
    9371              :                       event_type)) )
    9372            4 :       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            2 : test_lookup_webhooks (const struct InstanceData *instance,
    9387              :                       unsigned int webhooks_length,
    9388              :                       const struct WebhookData *webhooks)
    9389            2 : {
    9390            2 :   unsigned int results_matching[webhooks_length];
    9391            2 :   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            2 :   memset (results_matching, 0, sizeof (unsigned int) * webhooks_length);
    9398            2 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    9399            2 :   if (0 > TALER_MERCHANTDB_iterate_webhooks (pg,
    9400            2 :                                              instance->instance.id,
    9401              :                                              &lookup_webhooks_cb,
    9402              :                                              &cls))
    9403              :   {
    9404            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    9405              :                 "Lookup webhooks failed\n");
    9406            0 :     return 1;
    9407              :   }
    9408            2 :   if (webhooks_length != cls.results_length)
    9409              :   {
    9410            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    9411              :                 "Lookup webhooks failed: incorrect number of results\n");
    9412            0 :     return 1;
    9413              :   }
    9414            6 :   for (unsigned int i = 0; webhooks_length > i; ++i)
    9415              :   {
    9416            4 :     if (1 != cls.results_matching[i])
    9417              :     {
    9418            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    9419              :                   "Lookup webhooks failed: mismatched data\n");
    9420            0 :       return 1;
    9421              :     }
    9422              :   }
    9423            2 :   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            4 : 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            4 :   struct TestLookupWebhooks_Closure *cmp = cls;
    9443            4 :   if (NULL == cmp)
    9444            0 :     return;
    9445            4 :   cmp->results_length += 1;
    9446           12 :   for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i)
    9447              :   {
    9448            8 :     if ((0 == strcmp (cmp->webhooks_to_cmp[i].webhook.event_type,
    9449            8 :                       event_type)) &&
    9450            8 :         (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.url,
    9451            4 :                       url)) &&
    9452            4 :         (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.http_method,
    9453            4 :                       http_method)) &&
    9454            4 :         (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.header_template,
    9455            4 :                       header_template)) &&
    9456            4 :         (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.body_template,
    9457              :                       body_template)) )
    9458            4 :       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            2 : test_lookup_webhook_by_event (const struct InstanceData *instance,
    9473              :                               unsigned int webhooks_length,
    9474              :                               const struct WebhookData *webhooks)
    9475            2 : {
    9476            2 :   unsigned int results_matching[webhooks_length];
    9477            2 :   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            2 :   memset (results_matching, 0, sizeof (unsigned int) * webhooks_length);
    9484            2 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    9485            2 :   if (0 > TALER_MERCHANTDB_iterate_webhooks_by_event (pg,
    9486            2 :                                                       instance->instance.id,
    9487            2 :                                                       webhooks->webhook.event_type,
    9488              :                                                       &lookup_webhook_by_event_cb,
    9489              :                                                       &cls))
    9490              :   {
    9491            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    9492              :                 "Lookup webhooks by event failed\n");
    9493            0 :     return 1;
    9494              :   }
    9495              : 
    9496            2 :   if (webhooks_length != cls.results_length)
    9497              :   {
    9498            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    9499              :                 "Lookup webhooks by event failed: incorrect number of results\n");
    9500            0 :     return 1;
    9501              :   }
    9502            6 :   for (unsigned int i = 0; webhooks_length > i; ++i)
    9503              :   {
    9504            4 :     if (1 != cls.results_matching[i])
    9505              :     {
    9506            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    9507              :                   "Lookup webhooks by event failed: mismatched data\n");
    9508            0 :       return 1;
    9509              :     }
    9510              :   }
    9511            2 :   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            2 : test_delete_webhook (const struct InstanceData *instance,
    9525              :                      const struct WebhookData *webhook,
    9526              :                      enum GNUNET_DB_QueryStatus expected_result)
    9527              : {
    9528            2 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    9529            2 :   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            2 :   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            1 : test_inventory_deleted_webhook (void)
    9554              : {
    9555              :   struct InstanceData instance;
    9556              :   struct WebhookData webhook;
    9557              :   struct ProductData product;
    9558            1 :   char *body = NULL;
    9559            1 :   int ret = 1;
    9560              : 
    9561            1 :   make_instance ("test_inst_inventory_webhooks",
    9562              :                  &instance);
    9563            1 :   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            1 :   make_webhook ("test_webhooks_wb_inventory_deleted",
    9569              :                 &webhook);
    9570            1 :   webhook.webhook.event_type = "inventory_deleted";
    9571            1 :   webhook.webhook.url = "https://example.com/inventory-deleted";
    9572            1 :   webhook.webhook.body_template =
    9573              :     "{\"type\":\"{{webhook_type}}\""
    9574              :     ",\"product_id\":\"{{product_id}}\""
    9575              :     ",\"description\":\"{{description}}\"}";
    9576            1 :   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            1 :   make_product ("test_webhooks_deleted_product",
    9583              :                 &product);
    9584            1 :   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            1 :   TEST_WITH_FAIL_CLAUSE (test_delete_product (&instance,
    9595              :                                               &product,
    9596              :                                               false,
    9597              :                                               false,
    9598              :                                               false),
    9599              :                          goto cleanup;
    9600              :                          );
    9601            1 :   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            1 :   if (0 != strcmp ("{\"type\":\"inventory_deleted\""
    9609              :                    ",\"product_id\":\"test_webhooks_deleted_product\""
    9610              :                    ",\"description\":\"This is a test product\"}",
    9611              :                    body))
    9612              :   {
    9613            0 :     GNUNET_break (0);
    9614            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    9615              :                 "inventory_deleted webhook body not resolved: `%s'\n",
    9616              :                 body);
    9617            0 :     goto cleanup;
    9618              :   }
    9619            1 :   ret = 0;
    9620            1 : cleanup:
    9621            1 :   GNUNET_free (body);
    9622            1 :   free_product_data (&product);
    9623            1 :   free_instance_data (&instance);
    9624            1 :   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            1 : pre_test_webhooks (struct TestWebhooks_Closure *cls)
    9652              : {
    9653              :   /* Instance */
    9654            1 :   make_instance ("test_inst_webhooks",
    9655              :                  &cls->instance);
    9656              : 
    9657              :   /* Webhooks */
    9658            1 :   make_webhook ("test_webhooks_wb_0",
    9659              :                 &cls->webhooks[0]);
    9660              : 
    9661            1 :   make_webhook ("test_webhooks_wb_1",
    9662              :                 &cls->webhooks[1]);
    9663            1 :   cls->webhooks[1].webhook.event_type = "Test paid";
    9664            1 :   cls->webhooks[1].webhook.url = "https://example.com";
    9665            1 :   cls->webhooks[1].webhook.http_method = "POST";
    9666            1 :   cls->webhooks[1].webhook.header_template = "Authorization:1XYJAOR493O";
    9667            1 :   cls->webhooks[1].webhook.body_template = "$Amount";
    9668              : 
    9669            1 :   make_webhook ("test_webhooks_wb_2",
    9670              :                 &cls->webhooks[2]);
    9671            1 :   cls->webhooks[2].webhook.event_type = "Test paid";
    9672            1 :   cls->webhooks[2].webhook.url = "https://examplerefund.com";
    9673            1 :   cls->webhooks[2].webhook.http_method = "POST";
    9674            1 :   cls->webhooks[2].webhook.header_template = "Authorization:XY6ORK52JEO";
    9675            1 :   cls->webhooks[2].webhook.body_template = "$Amount";
    9676            1 : }
    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            1 : post_test_webhooks (struct TestWebhooks_Closure *cls)
    9686              : {
    9687            1 :   free_instance_data (&cls->instance);
    9688            1 : }
    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            1 : run_test_webhooks (struct TestWebhooks_Closure *cls)
    9699              : {
    9700              : 
    9701              :   /* Test that insert without an instance fails */
    9702            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
    9707              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    9708              :   /* Test inserting a webhook */
    9709            1 :   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            1 :   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            1 :   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            1 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    9721            1 :       TALER_MERCHANTDB_get_webhook (pg,
    9722            1 :                                     cls->instance.instance.id,
    9723              :                                     "nonexistent_webhook",
    9724              :                                     NULL))
    9725              :   {
    9726            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    9727              :                 "Lookup webhook failed\n");
    9728            0 :     return 1;
    9729              :   }
    9730              :   /* Test webhook update */
    9731            1 :   cls->webhooks[0].webhook.event_type =
    9732              :     "Test paid";
    9733            1 :   cls->webhooks[0].webhook.url =
    9734              :     "example.com";
    9735            1 :   cls->webhooks[0].webhook.http_method =
    9736              :     "POST";
    9737            1 :   cls->webhooks[0].webhook.header_template =
    9738              :     "Authorization:WEKFOEKEXZ";
    9739            1 :   cls->webhooks[0].webhook.body_template =
    9740              :     "$Amount";
    9741            1 :   TEST_RET_ON_FAIL (test_update_webhook (&cls->instance,
    9742              :                                          &cls->webhooks[0],
    9743              :                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    9744              : 
    9745            1 :   TEST_RET_ON_FAIL (test_lookup_webhook (&cls->instance,
    9746              :                                          &cls->webhooks[0]));
    9747            1 :   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            1 :   TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
    9752              :                                          &cls->webhooks[1],
    9753              :                                          GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
    9754            1 :   TEST_RET_ON_FAIL (test_lookup_webhooks (&cls->instance,
    9755              :                                           2,
    9756              :                                           cls->webhooks));
    9757            1 :   TEST_RET_ON_FAIL (test_lookup_webhook_by_event (&cls->instance,
    9758              :                                                   2,
    9759              :                                                   cls->webhooks));
    9760            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (test_delete_webhook (&cls->instance,
    9770              :                                          &cls->webhooks[1],
    9771              :                                          GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
    9772            1 :   cls->webhooks[1] = cls->webhooks[2];
    9773            1 :   TEST_RET_ON_FAIL (test_lookup_webhooks (&cls->instance,
    9774              :                                           2,
    9775              :                                           cls->webhooks));
    9776            1 :   TEST_RET_ON_FAIL (test_lookup_webhook_by_event (&cls->instance,
    9777              :                                                   2,
    9778              :                                                   cls->webhooks));
    9779            1 :   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            1 : test_webhooks (void)
    9790              : {
    9791              :   struct TestWebhooks_Closure test_cls;
    9792              :   int test_result;
    9793              : 
    9794            1 :   pre_test_webhooks (&test_cls);
    9795            1 :   test_result = run_test_webhooks (&test_cls);
    9796            1 :   post_test_webhooks (&test_cls);
    9797            1 :   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            2 : make_pending_webhook (uint64_t webhook_serial,
    9828              :                       struct PendingWebhookData *pwebhook)
    9829              : {
    9830            2 :   pwebhook->webhook_serial = webhook_serial;
    9831            2 :   pwebhook->pwebhook.next_attempt = GNUNET_TIME_UNIT_ZERO_ABS;
    9832            2 :   pwebhook->pwebhook.retries = 0;
    9833            2 :   pwebhook->pwebhook.url = "https://exampletest.com";
    9834            2 :   pwebhook->pwebhook.http_method = "POST";
    9835            2 :   pwebhook->pwebhook.header = "Authorization:XYJAORKJEO";
    9836            2 :   pwebhook->pwebhook.body = "$Amount";
    9837            2 : }
    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            3 : test_insert_pending_webhook (const struct InstanceData *instance,
    9850              :                              struct PendingWebhookData *pwebhook,
    9851              :                              enum GNUNET_DB_QueryStatus expected_result)
    9852              : {
    9853              : 
    9854            3 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    9855            2 :   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            2 :   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            2 : test_update_pending_webhook (const struct InstanceData *instance,
    9882              :                              struct PendingWebhookData *pwebhook,
    9883              :                              enum GNUNET_DB_QueryStatus expected_result)
    9884              : {
    9885            2 :   pwebhook->pwebhook.next_attempt = GNUNET_TIME_relative_to_absolute (
    9886              :     GNUNET_TIME_UNIT_HOURS);
    9887            2 :   pwebhook->pwebhook.retries++;
    9888            2 :   TEST_SET_INSTANCE (instance->instance.id, expected_result);
    9889            2 :   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            2 :   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            4 : 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            4 :   struct LookupPendingWebhookSerial_Closure *lpw = cls;
    9935              : 
    9936            4 :   if ((0 == strcmp (lpw->pwebhook->pwebhook.url,
    9937            2 :                     url)) &&
    9938            2 :       (0 == strcmp (lpw->pwebhook->pwebhook.http_method,
    9939            2 :                     http_method)) &&
    9940            2 :       (0 == strcmp (lpw->pwebhook->pwebhook.header,
    9941            2 :                     header)) &&
    9942            2 :       (0 == strcmp (lpw->pwebhook->pwebhook.body,
    9943              :                     body)) )
    9944              :   {
    9945            2 :     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            4 : }
    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            2 : get_pending_serial (const struct InstanceData *instance,
    9968              :                     const struct PendingWebhookData *pwebhook)
    9969              : {
    9970            2 :   struct LookupPendingWebhookSerial_Closure lpw = {
    9971              :     .pwebhook = pwebhook,
    9972              :     .webhook_pending_serial = 0
    9973              :   };
    9974              : 
    9975            2 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    9976              :                  TALER_MERCHANTDB_set_instance (pg,
    9977              :                                                 instance->instance.id));
    9978            2 :   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            2 :   GNUNET_assert (0 != lpw.webhook_pending_serial);
    9986              : 
    9987            2 :   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            5 : 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            5 :   struct TestLookupPendingWebhooks_Closure *cmp = cls;
   10036              : 
   10037            5 :   cmp->results_length++;
   10038           14 :   for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i)
   10039              :   {
   10040            9 :     if ((0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.url,
   10041            5 :                       url)) &&
   10042            5 :         (0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.http_method,
   10043            5 :                       http_method)) &&
   10044            5 :         (0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.header,
   10045            5 :                       header)) &&
   10046            5 :         (0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.body,
   10047              :                       body)) )
   10048              :     {
   10049            5 :       cmp->results_matching[i]++;
   10050              :     }
   10051              :   }
   10052            5 : }
   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            1 : test_lookup_pending_webhooks (const struct InstanceData *instance,
   10065              :                               unsigned int pwebhooks_length,
   10066              :                               const struct PendingWebhookData *pwebhooks)
   10067            1 : {
   10068            1 :   unsigned int results_matching[pwebhooks_length];
   10069            1 :   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            1 :   memset (results_matching, 0, sizeof (results_matching));
   10077            1 :   if (0 > TALER_MERCHANTDB_iterate_pending_webhooks (pg,
   10078              :                                                      1024, /* limit */
   10079              :                                                      &lookup_pending_webhooks_cb,
   10080              :                                                      &cls))
   10081              :   {
   10082            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   10083              :                 "Lookup pending webhook failed\n");
   10084            0 :     return 1;
   10085              :   }
   10086            1 :   if (pwebhooks_length != cls.results_length)
   10087              :   {
   10088            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   10089              :                 "Lookup pending webhook failed: incorrect number of results\n");
   10090            0 :     return 1;
   10091              :   }
   10092            3 :   for (unsigned int i = 0; i < pwebhooks_length; i++)
   10093              :   {
   10094            2 :     if (1 != cls.results_matching[i])
   10095              :     {
   10096            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   10097              :                   "Lookup pending webhook failed: mismatched data\n");
   10098            0 :       return 1;
   10099              :     }
   10100              :   }
   10101            1 :   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            1 : test_iterate_pending_webhooks_next (const struct InstanceData *instance,
   10115              :                                     unsigned int pwebhooks_length,
   10116              :                                     const struct PendingWebhookData *pwebhooks)
   10117            1 : {
   10118            1 :   unsigned int results_matching[pwebhooks_length];
   10119            1 :   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            1 :   memset (results_matching, 0, sizeof (results_matching));
   10127            1 :   if (0 > TALER_MERCHANTDB_iterate_pending_webhooks_next (pg,
   10128              :                                                           &lookup_pending_webhooks_cb,
   10129              :                                                           &cls))
   10130              :   {
   10131            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   10132              :                 "Lookup future webhook failed\n");
   10133            0 :     return 1;
   10134              :   }
   10135            1 :   if (pwebhooks_length != cls.results_length)
   10136              :   {
   10137            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   10138              :                 "Lookup future webhook failed: incorrect number of results\n");
   10139            0 :     return 1;
   10140              :   }
   10141            2 :   for (unsigned int i = 0; pwebhooks_length > i; ++i)
   10142              :   {
   10143            1 :     if (1 != cls.results_matching[i])
   10144              :     {
   10145            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   10146              :                   "Lookup future webhook failed: mismatched data\n");
   10147            0 :       return 1;
   10148              :     }
   10149              :   }
   10150            1 :   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            2 : test_lookup_all_webhooks (const struct InstanceData *instance,
   10164              :                           unsigned int pwebhooks_length,
   10165              :                           const struct PendingWebhookData *pwebhooks)
   10166            2 : {
   10167            2 :   uint64_t max_results = 2;
   10168            2 :   uint64_t min_row = 0;
   10169            2 :   unsigned int results_matching[GNUNET_NZL (pwebhooks_length)];
   10170            2 :   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            2 :   memset (results_matching,
   10178              :           0,
   10179              :           sizeof (results_matching));
   10180            2 :   TEST_SET_INSTANCE (instance->instance.id, GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   10181            2 :   if (0 > TALER_MERCHANTDB_iterate_pending_webhooks_above_serial_id (pg,
   10182            2 :                                                                      instance->instance.id,
   10183              :                                                                      min_row,
   10184              :                                                                      max_results,
   10185              :                                                                      &lookup_pending_webhooks_cb,
   10186              :                                                                      &cls))
   10187              :   {
   10188            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   10189              :                 "Lookup all webhooks failed\n");
   10190            0 :     return 1;
   10191              :   }
   10192            2 :   if (pwebhooks_length != cls.results_length)
   10193              :   {
   10194            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   10195              :                 "Lookup all webhooks failed: incorrect number of results\n");
   10196            0 :     return 1;
   10197              :   }
   10198            4 :   for (unsigned int i = 0; pwebhooks_length > i; ++i)
   10199              :   {
   10200            2 :     if (1 != cls.results_matching[i])
   10201              :     {
   10202            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   10203              :                   "Lookup all webhooks failed: mismatched data\n");
   10204            0 :       return 1;
   10205              :     }
   10206              :   }
   10207            2 :   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            3 : test_delete_pending_webhook (uint64_t webhooks_pending_serial,
   10221              :                              enum GNUNET_DB_QueryStatus expected_result)
   10222              : {
   10223              : 
   10224            3 :   TEST_COND_RET_ON_FAIL (expected_result ==
   10225              :                          TALER_MERCHANTDB_delete_pending_webhook (pg,
   10226              :                                                                   webhooks_pending_serial),
   10227              :                          "Delete webhook failed\n");
   10228            3 :   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            1 : pre_test_pending_webhooks (struct TestPendingWebhooks_Closure *cls)
   10264              : {
   10265              :   /* Instance */
   10266            1 :   make_instance ("test_inst_pending_webhooks",
   10267              :                  &cls->instance);
   10268              : 
   10269              :   /* Webhooks the pending webhooks reference (serials 1..4) */
   10270            1 :   make_webhook ("test_pwh_wb_0", &cls->webhooks[0]);
   10271            1 :   make_webhook ("test_pwh_wb_1", &cls->webhooks[1]);
   10272            1 :   make_webhook ("test_pwh_wb_2", &cls->webhooks[2]);
   10273            1 :   make_webhook ("test_pwh_wb_3", &cls->webhooks[3]);
   10274              : 
   10275              :   /* Webhooks */
   10276            1 :   make_pending_webhook (1,
   10277              :                         &cls->pwebhooks[0]);
   10278              : 
   10279            1 :   make_pending_webhook (4,
   10280              :                         &cls->pwebhooks[1]);
   10281            1 :   cls->pwebhooks[1].pwebhook.url = "https://test.com";
   10282            1 :   cls->pwebhooks[1].pwebhook.http_method = "POST";
   10283            1 :   cls->pwebhooks[1].pwebhook.header = "Authorization:XYJAO5R06EO";
   10284            1 :   cls->pwebhooks[1].pwebhook.body = "$Amount";
   10285            1 : }
   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            1 : post_test_pending_webhooks (struct TestPendingWebhooks_Closure *cls)
   10295              : {
   10296            1 :   free_instance_data (&cls->instance);
   10297            1 : }
   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            1 : 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            1 :   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            1 :   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            5 :   for (unsigned int i = 0; i < 4; i++)
   10323            4 :     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            1 :   TEST_RET_ON_FAIL (test_insert_pending_webhook (&cls->instance,
   10330              :                                                  &cls->pwebhooks[0],
   10331              :                                                  GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   10332            1 :   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            1 :   TEST_RET_ON_FAIL (test_lookup_pending_webhooks (&cls->instance,
   10337              :                                                   2,
   10338              :                                                   cls->pwebhooks));
   10339              :   /* Test pending webhook update */
   10340            1 :   TEST_RET_ON_FAIL (test_update_pending_webhook (&cls->instance,
   10341              :                                                  &cls->pwebhooks[0],
   10342              :                                                  GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   10343            1 :   TEST_RET_ON_FAIL (test_iterate_pending_webhooks_next (&cls->instance,
   10344              :                                                         1,
   10345              :                                                         &cls->pwebhooks[1]));
   10346            1 :   TEST_RET_ON_FAIL (test_update_pending_webhook (&cls->instance,
   10347              :                                                  &cls->pwebhooks[1],
   10348              :                                                  GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   10349              :   // ???
   10350            1 :   TEST_RET_ON_FAIL (test_lookup_all_webhooks (&cls->instance,
   10351              :                                               2,
   10352              :                                               cls->pwebhooks));
   10353              : 
   10354            1 :   webhook_pending_serial0 = get_pending_serial (&cls->instance,
   10355            1 :                                                 &cls->pwebhooks[0]);
   10356            1 :   webhook_pending_serial1 = get_pending_serial (&cls->instance,
   10357            1 :                                                 &cls->pwebhooks[1]);
   10358              : 
   10359              :   /* Test webhook deletion */
   10360            1 :   TEST_RET_ON_FAIL (test_delete_pending_webhook (webhook_pending_serial1,
   10361              :                                                  GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   10362              :   /* Test double deletion fails */
   10363            1 :   TEST_RET_ON_FAIL (test_delete_pending_webhook (webhook_pending_serial1,
   10364              :                                                  GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
   10365            1 :   TEST_RET_ON_FAIL (test_delete_pending_webhook (webhook_pending_serial0,
   10366              :                                                  GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   10367            1 :   TEST_RET_ON_FAIL (test_lookup_all_webhooks (&cls->instance,
   10368              :                                               0,
   10369              :                                               NULL));
   10370            1 :   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            1 : test_pending_webhooks (void)
   10381              : {
   10382              :   struct TestPendingWebhooks_Closure test_cls;
   10383              :   int test_result;
   10384              : 
   10385            1 :   pre_test_pending_webhooks (&test_cls);
   10386            1 :   test_result = run_test_pending_webhooks (&test_cls);
   10387            1 :   post_test_pending_webhooks (&test_cls);
   10388            1 :   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           12 : statistics_exec_sql (const char *sql)
   10429              : {
   10430           12 :   struct GNUNET_PQ_ExecuteStatement es[] = {
   10431           12 :     GNUNET_PQ_make_execute (sql),
   10432              :     GNUNET_PQ_EXECUTE_STATEMENT_END
   10433              :   };
   10434              : 
   10435           12 :   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
   10436              :                          GNUNET_PQ_exec_statements (pg->conn,
   10437              :                                                     es),
   10438              :                          "Direct SQL execution failed\n");
   10439           12 :   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           13 : 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           13 :   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           13 :   cbc->count++;
   10469           13 : }
   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            3 : count_bucket_counters (const struct InstanceData *instance,
   10482              :                        const char *slug,
   10483              :                        unsigned int *count)
   10484              : {
   10485            3 :   struct CountBuckets_Closure cbc = {
   10486              :     .count = 0
   10487              :   };
   10488              : 
   10489            3 :   TEST_SET_INSTANCE (instance->instance.id,
   10490              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   10491            3 :   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            3 :   *count = cbc.count;
   10500            3 :   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            1 : test_statistics_bucket_gc (const struct InstanceData *instance)
   10517              : {
   10518              :   unsigned int before;
   10519              :   unsigned int after;
   10520              : 
   10521            1 :   TEST_SET_INSTANCE (instance->instance.id,
   10522              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   10523            1 :   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            1 :   TEST_RET_ON_FAIL (count_bucket_counters (instance,
   10529              :                                            "tokens-issued",
   10530              :                                            &before));
   10531            1 :   TEST_COND_RET_ON_FAIL (0 < before,
   10532              :                          "Bumping a bucket statistic recorded nothing\n");
   10533            1 :   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
   10534              :                          TALER_MERCHANTDB_gc (pg),
   10535              :                          "Garbage collection failed\n");
   10536            1 :   TEST_RET_ON_FAIL (count_bucket_counters (instance,
   10537              :                                            "tokens-issued",
   10538              :                                            &after));
   10539            1 :   TEST_COND_RET_ON_FAIL (before == after,
   10540              :                          "Garbage collection deleted bucket statistics\n");
   10541            1 :   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           36 : 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           36 : {
   10566           36 :   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           36 :   cbc->count++;
   10575           36 : }
   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            6 : test_statistics_amount_slug_collected (const struct InstanceData *instance,
   10590              :                                        const char *slug)
   10591              : {
   10592            6 :   struct CountBuckets_Closure cbc = {
   10593              :     .count = 0
   10594              :   };
   10595              :   char *sql;
   10596              : 
   10597            6 :   TEST_SET_INSTANCE (instance->instance.id,
   10598              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   10599            6 :   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            6 :     ret = statistics_exec_sql (sql);
   10609            6 :     GNUNET_free (sql);
   10610            6 :     TEST_RET_ON_FAIL (ret);
   10611              :   }
   10612            6 :   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            6 :   TEST_COND_RET_ON_FAIL (0 < cbc.count,
   10621              :                          "Statistic bumped by a trigger is not registered\n");
   10622            6 :   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            1 : test_statistics_dual_type_slug (const struct InstanceData *instance)
   10638              : {
   10639              :   unsigned int count;
   10640              : 
   10641            1 :   TEST_SET_INSTANCE (instance->instance.id,
   10642              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   10643            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (count_bucket_counters (instance,
   10667              :                                            "test-dual-type",
   10668              :                                            &count));
   10669            1 :   TEST_COND_RET_ON_FAIL (1 == count,
   10670              :                          "Bump used the ranges of the wrong registration\n");
   10671            1 :   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            1 : statistics_count_counter_events (const struct InstanceData *instance,
   10685              :                                  const char *slug,
   10686              :                                  uint64_t *count)
   10687              : {
   10688            1 :   struct GNUNET_PQ_QueryParam params[] = {
   10689            1 :     GNUNET_PQ_query_param_string (slug),
   10690              :     GNUNET_PQ_query_param_end
   10691              :   };
   10692            1 :   struct GNUNET_PQ_ResultSpec rs[] = {
   10693            1 :     GNUNET_PQ_result_spec_uint64 ("num",
   10694              :                                   count),
   10695              :     GNUNET_PQ_result_spec_end
   10696              :   };
   10697              : 
   10698            1 :   TEST_SET_INSTANCE (instance->instance.id,
   10699              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   10700            1 :   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            1 :   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            1 :   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            1 : test_statistics_counter_gc_delimiter (const struct InstanceData *instance)
   10736              : {
   10737            1 :   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            1 :   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            1 :   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            1 :     TEST_RET_ON_FAIL (statistics_count_counter_events (instance,
   10775              :                                                        "orders-created",
   10776              :                                                        &num_events));
   10777            1 :     TEST_COND_RET_ON_FAIL (1 == num_events,
   10778              :                            "Garbage collection did not compact the events\n");
   10779              :   }
   10780            1 :   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            1 : statistics_max_bucket_counter (const struct InstanceData *instance,
   10804              :                                const char *slug,
   10805              :                                uint64_t *value)
   10806              : {
   10807            1 :   struct GNUNET_PQ_QueryParam params[] = {
   10808            1 :     GNUNET_PQ_query_param_string (slug),
   10809              :     GNUNET_PQ_query_param_end
   10810              :   };
   10811            1 :   struct GNUNET_PQ_ResultSpec rs[] = {
   10812            1 :     GNUNET_PQ_result_spec_uint64 ("num",
   10813              :                                   value),
   10814              :     GNUNET_PQ_result_spec_end
   10815              :   };
   10816              : 
   10817            1 :   TEST_SET_INSTANCE (instance->instance.id,
   10818              :                      GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
   10819            1 :   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            1 :   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            1 :   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            1 : test_statistics_bucket_upsert_race (const struct InstanceData *instance)
   10854              : {
   10855              :   pid_t child;
   10856              :   int status;
   10857              :   uint64_t value;
   10858              : 
   10859            1 :   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            1 :   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            1 :   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            1 :   TEST_RET_ON_FAIL (statistics_exec_sql (BUMP_TOKENS_USED));
   10876            1 :   child = fork ();
   10877            1 :   if (-1 == child)
   10878              :   {
   10879            0 :     TALER_MERCHANTDB_rollback (pg);
   10880            0 :     GNUNET_break (0);
   10881            0 :     return 1;
   10882              :   }
   10883            1 :   if (0 == child)
   10884              :   {
   10885              :     struct TALER_MERCHANTDB_PostgresContext *cpg;
   10886            0 :     int ret = 1;
   10887              : 
   10888              :     /* Second writer: blocks on the uncommitted row of the parent until
   10889              :        the parent commits below. */
   10890            0 :     cpg = TALER_MERCHANTDB_connect (test_cfg);
   10891            0 :     if ( (NULL != cpg) &&
   10892              :          (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
   10893            0 :           TALER_MERCHANTDB_set_instance (cpg,
   10894            0 :                                          instance->instance.id)) )
   10895              :     {
   10896            0 :       struct GNUNET_PQ_ExecuteStatement es[] = {
   10897            0 :         GNUNET_PQ_make_execute (BUMP_TOKENS_USED),
   10898              :         GNUNET_PQ_EXECUTE_STATEMENT_END
   10899              :       };
   10900              : 
   10901            0 :       if (GNUNET_OK ==
   10902            0 :           GNUNET_PQ_exec_statements (cpg->conn,
   10903              :                                      es))
   10904            0 :         ret = 0;
   10905              :     }
   10906            0 :     _exit (ret);
   10907              :   }
   10908            1 :   sleep (1); /* give the child time to reach the conflicting INSERT */
   10909            1 :   TEST_COND_RET_ON_FAIL (0 <=
   10910              :                          TALER_MERCHANTDB_commit (pg),
   10911              :                          "Failed to commit transaction\n");
   10912            1 :   if (0 >= waitpid (child,
   10913              :                     &status,
   10914              :                     0))
   10915            0 :     sleep (2); /* cannot reap the child, just give it time to finish */
   10916            1 :   TEST_RET_ON_FAIL (statistics_max_bucket_counter (instance,
   10917              :                                                    "tokens-used",
   10918              :                                                    &value));
   10919            1 :   TEST_COND_RET_ON_FAIL (2 == value,
   10920              :                          "Concurrent statistics bump was lost\n");
   10921            1 :   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            1 : pre_test_statistics (struct TestStatistics_Closure *cls)
   10932              : {
   10933            1 :   make_instance ("test_inst_statistics",
   10934              :                  &cls->instance);
   10935            1 : }
   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            1 : post_test_statistics (struct TestStatistics_Closure *cls)
   10945              : {
   10946            1 :   free_instance_data (&cls->instance);
   10947            1 : }
   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            1 : run_test_statistics (struct TestStatistics_Closure *cls)
   10958              : {
   10959            1 :   TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
   10960              :                                           GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
   10961            1 :   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            1 :   TEST_RET_ON_FAIL (test_statistics_amount_slug_collected (
   10965              :                       &cls->instance,
   10966              :                       "payments-received-after-deposit-fee"));
   10967            1 :   TEST_RET_ON_FAIL (test_statistics_amount_slug_collected (
   10968              :                       &cls->instance,
   10969              :                       "total-deposit-fees-paid"));
   10970            1 :   TEST_RET_ON_FAIL (test_statistics_amount_slug_collected (
   10971              :                       &cls->instance,
   10972              :                       "total-wire-fees-paid"));
   10973            1 :   TEST_RET_ON_FAIL (test_statistics_amount_slug_collected (
   10974              :                       &cls->instance,
   10975              :                       "refunds-granted"));
   10976            1 :   TEST_RET_ON_FAIL (test_statistics_amount_slug_collected (
   10977              :                       &cls->instance,
   10978              :                       "deposits-received"));
   10979            1 :   TEST_RET_ON_FAIL (test_statistics_amount_slug_collected (
   10980              :                       &cls->instance,
   10981              :                       "deposits-fees-paid"));
   10982            1 :   TEST_RET_ON_FAIL (test_statistics_counter_gc_delimiter (&cls->instance));
   10983            1 :   TEST_RET_ON_FAIL (test_statistics_dual_type_slug (&cls->instance));
   10984            1 :   TEST_RET_ON_FAIL (test_statistics_bucket_upsert_race (&cls->instance));
   10985            1 :   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            1 : test_statistics (void)
   10996              : {
   10997              :   struct TestStatistics_Closure test_cls;
   10998              :   int test_result;
   10999              : 
   11000            1 :   pre_test_statistics (&test_cls);
   11001            1 :   test_result = run_test_statistics (&test_cls);
   11002            1 :   post_test_statistics (&test_cls);
   11003            1 :   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            1 : 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            1 :   GNUNET_CRYPTO_random_block (&h_body,
   11036              :                               sizeof (h_body));
   11037            1 :   GNUNET_CRYPTO_random_block (&other_h_body,
   11038              :                               sizeof (other_h_body));
   11039            1 :   GNUNET_CRYPTO_random_block (&salt,
   11040              :                               sizeof (salt));
   11041            1 :   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            1 :   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            1 :   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            1 :   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            1 :   TEST_COND_RET_ON_FAIL (! solved,
   11079              :                          "A wrong TAN solved the challenge\n");
   11080            1 :   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            1 :   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            1 :   TEST_COND_RET_ON_FAIL (solved,
   11093              :                          "The correct TAN did not solve the challenge\n");
   11094            1 :   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            1 :   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            1 :   TEST_COND_RET_ON_FAIL (solved,
   11107              :                          "Re-confirming a confirmed challenge is not"
   11108              :                          " idempotent\n");
   11109            1 :   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            1 :   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            1 :   TEST_COND_RET_ON_FAIL (! solved,
   11123              :                          "Confirmed challenge solved with a different body"
   11124              :                          " hash\n");
   11125            1 :   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            1 : test_commit_on_aborted_transaction (void)
   11139              : {
   11140            1 :   struct GNUNET_PQ_ExecuteStatement mk[] = {
   11141            1 :     GNUNET_PQ_make_execute ("CREATE TABLE test_commit_rollback (x INT)"),
   11142              :     GNUNET_PQ_EXECUTE_STATEMENT_END
   11143              :   };
   11144            1 :   struct GNUNET_PQ_ExecuteStatement bad[] = {
   11145            1 :     GNUNET_PQ_make_execute ("SELECT 1/0"),
   11146              :     GNUNET_PQ_EXECUTE_STATEMENT_END
   11147              :   };
   11148            1 :   struct GNUNET_PQ_ExecuteStatement chk[] = {
   11149            1 :     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            1 :   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
   11159              :                          TALER_MERCHANTDB_start (pg,
   11160              :                                                  "test-commit-aborted"),
   11161              :                          "Failed to start transaction\n");
   11162            1 :   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            1 :   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            1 :   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            1 :   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
   11177              :                          GNUNET_PQ_exec_statements (pg->conn,
   11178              :                                                     chk),
   11179              :                          "Scratch table survived the rollback\n");
   11180            1 :   TEST_COND_RET_ON_FAIL (GNUNET_OK ==
   11181              :                          TALER_MERCHANTDB_start (pg,
   11182              :                                                  "test-commit-clean"),
   11183              :                          "Failed to start transaction\n");
   11184            1 :   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            1 :   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            1 : run_tests (void)
   11198              : {
   11199            1 :   TEST_RET_ON_FAIL (test_instances ());
   11200            1 :   TEST_RET_ON_FAIL (test_products ());
   11201            1 :   TEST_RET_ON_FAIL (test_inventory_locks ());
   11202            1 :   TEST_RET_ON_FAIL (test_tokens ());
   11203            1 :   TEST_RET_ON_FAIL (test_orders ());
   11204            1 :   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            1 :   TEST_RET_ON_FAIL (test_pending_webhooks ());
   11208            1 :   TEST_RET_ON_FAIL (test_transfers ());
   11209            1 :   TEST_RET_ON_FAIL (test_refunds ());
   11210            1 :   TEST_RET_ON_FAIL (test_lookup_orders_all_filters ());
   11211            1 :   TEST_RET_ON_FAIL (test_kyc ());
   11212            1 :   TEST_RET_ON_FAIL (test_templates ());
   11213            1 :   TEST_RET_ON_FAIL (test_webhooks ());
   11214            1 :   TEST_RET_ON_FAIL (test_inventory_deleted_webhook ());
   11215            1 :   TEST_RET_ON_FAIL (test_statistics ());
   11216            1 :   TEST_RET_ON_FAIL (test_mfa_challenges ());
   11217            1 :   TEST_RET_ON_FAIL (test_commit_on_aborted_transaction ());
   11218            1 :   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            1 : run (void *cls)
   11229              : {
   11230            1 :   struct GNUNET_CONFIGURATION_Handle *cfg = cls;
   11231              : 
   11232            1 :   test_cfg = cfg;
   11233            1 :   if (NULL == (pg = TALER_MERCHANTDB_connect (cfg)))
   11234              :   {
   11235            0 :     result = 77;
   11236            0 :     return;
   11237              :   }
   11238              :   /* Drop the tables to cleanup anything that might cause issues */
   11239            1 :   (void) TALER_MERCHANTDB_drop_tables (pg);
   11240            1 :   if (GNUNET_OK !=
   11241            1 :       TALER_MERCHANTDB_create_tables (pg))
   11242              :   {
   11243            0 :     result = 77;
   11244            0 :     return;
   11245              :   }
   11246              :   /* Run the preflight */
   11247            1 :   TALER_MERCHANTDB_preflight (pg);
   11248              : 
   11249            1 :   result = run_tests ();
   11250              :   /* if (0 == result) result = run_test_templates (); */
   11251            1 :   if (0 == result)
   11252              :   {
   11253            1 :     if (GNUNET_OK !=
   11254            1 :         TALER_MERCHANTDB_drop_tables (pg))
   11255              :     {
   11256            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   11257              :                   "Dropping tables failed\n");
   11258            0 :       result = 1;
   11259              :     }
   11260              :   }
   11261            1 :   TALER_MERCHANTDB_disconnect (pg);
   11262            1 :   pg = NULL;
   11263              : }
   11264              : 
   11265              : 
   11266              : /**
   11267              :  * Entry point for the tests.
   11268              :  */
   11269              : int
   11270            1 : main (int argc,
   11271              :       char *const argv[])
   11272              : {
   11273              :   struct GNUNET_CONFIGURATION_Handle *cfg;
   11274              : 
   11275            1 :   GNUNET_log_setup (argv[0],
   11276              :                     "DEBUG",
   11277              :                     NULL);
   11278            1 :   cfg = GNUNET_CONFIGURATION_create (TALER_MERCHANT_project_data ());
   11279            1 :   if (GNUNET_OK !=
   11280            1 :       GNUNET_CONFIGURATION_parse (cfg,
   11281              :                                   "test-merchantdb-postgres.conf"))
   11282              :   {
   11283            0 :     GNUNET_break (0);
   11284            0 :     return 2;
   11285              :   }
   11286            1 :   GNUNET_SCHEDULER_run (&run,
   11287              :                         cfg);
   11288            1 :   GNUNET_CONFIGURATION_destroy (cfg);
   11289            1 :   return result;
   11290              : }
   11291              : 
   11292              : 
   11293              : /* end of test_merchantdb.c */
        

Generated by: LCOV version 2.0-1