LCOV - code coverage report
Current view: top level - exchangedb - test_misc.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 99.3 % 141 140
Test Date: 2026-09-11 18:55:36 Functions: 100.0 % 16 16

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2026 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU 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 exchangedb/test_misc.c
      18              :  * @brief tests for the exchangedb functions that have no table of their own
      19              :  * @author Christian Grothoff
      20              :  *
      21              :  * Covers #TALER_EXCHANGEDB_start(), #TALER_EXCHANGEDB_start_read_only(),
      22              :  * #TALER_EXCHANGEDB_start_read_committed(),
      23              :  * #TALER_EXCHANGEDB_start_deferred_wire_out(), #TALER_EXCHANGEDB_commit(),
      24              :  * #TALER_EXCHANGEDB_rollback(), #TALER_EXCHANGEDB_preflight(),
      25              :  * #TALER_EXCHANGEDB_compute_shard(),
      26              :  * #TALER_EXCHANGEDB_calculate_transaction_list_totals(),
      27              :  * #TALER_EXCHANGEDB_free_coin_transaction_list(),
      28              :  * #TALER_EXCHANGEDB_free_reserve_history(),
      29              :  * #TALER_EXCHANGEDB_event_listen(),
      30              :  * #TALER_EXCHANGEDB_event_listen_cancel(),
      31              :  * #TALER_EXCHANGEDB_event_notify(), #TALER_EXCHANGEDB_enable_rules(),
      32              :  * #TALER_EXCHANGEDB_disable_rules(),
      33              :  * #TALER_EXCHANGEDB_inject_auditor_triggers(),
      34              :  * #TALER_EXCHANGEDB_drop_tables() and #TALER_EXCHANGEDB_create_tables().
      35              :  *
      36              :  * These are the connection-level functions: transactions, notifications,
      37              :  * schema management and the two list destructors.  The schema checks come
      38              :  * last on purpose -- they take the tables away and put them back, so
      39              :  * nothing may run after them.
      40              :  */
      41              : #include "test_common.h"
      42              : #include "taler/taler_dbevents.h"
      43              : #include "exchange-database/commit.h"
      44              : #include "exchange-database/compute_shard.h"
      45              : #include "exchange-database/create_tables.h"
      46              : #include "exchange-database/disable_rules.h"
      47              : #include "exchange-database/drop_tables.h"
      48              : #include "exchange-database/enable_rules.h"
      49              : #include "exchange-database/event_listen.h"
      50              : #include "exchange-database/event_listen_cancel.h"
      51              : #include "exchange-database/event_notify.h"
      52              : #include "exchange-database/free_coin_transaction_list.h"
      53              : #include "exchange-database/free_reserve_history.h"
      54              : #include "exchange-database/get_coin_transactions.h"
      55              : #include "exchange-database/get_reserve_history.h"
      56              : #include "exchange-database/inject_auditor_triggers.h"
      57              : #include "exchange-database/preflight.h"
      58              : #include "exchange-database/rollback.h"
      59              : #include "exchange-database/start.h"
      60              : #include "exchange-database/start_deferred_wire_out.h"
      61              : #include "exchange-database/start_read_committed.h"
      62              : #include "exchange-database/start_read_only.h"
      63              : 
      64              : 
      65              : /**
      66              :  * Account the checks fund their reserves from.
      67              :  */
      68              : static struct TDB_Account account;
      69              : 
      70              : 
      71              : /**
      72              :  * Denomination the checks use.
      73              :  */
      74              : static struct TDB_Denom denom;
      75              : 
      76              : 
      77              : /**
      78              :  * The four ways to start a transaction.
      79              :  *
      80              :  * @param pg the database context
      81              :  * @param name name of the transaction
      82              :  * @return #GNUNET_OK on success
      83              :  */
      84              : static enum GNUNET_GenericReturnValue
      85            2 : start_serializable (struct TALER_EXCHANGEDB_PostgresContext *pg,
      86              :                     const char *name)
      87              : {
      88            2 :   return TALER_EXCHANGEDB_start (pg,
      89              :                                  name);
      90              : }
      91              : 
      92              : 
      93              : /**
      94              :  * Start a read-only transaction.
      95              :  *
      96              :  * @param pg the database context
      97              :  * @param name name of the transaction
      98              :  * @return #GNUNET_OK on success
      99              :  */
     100              : static enum GNUNET_GenericReturnValue
     101            2 : start_read_only (struct TALER_EXCHANGEDB_PostgresContext *pg,
     102              :                  const char *name)
     103              : {
     104            2 :   return TALER_EXCHANGEDB_start_read_only (pg,
     105              :                                            name);
     106              : }
     107              : 
     108              : 
     109              : /**
     110              :  * Start a read-committed transaction.
     111              :  *
     112              :  * @param pg the database context
     113              :  * @param name name of the transaction
     114              :  * @return #GNUNET_OK on success
     115              :  */
     116              : static enum GNUNET_GenericReturnValue
     117            2 : start_read_committed (struct TALER_EXCHANGEDB_PostgresContext *pg,
     118              :                       const char *name)
     119              : {
     120            2 :   return TALER_EXCHANGEDB_start_read_committed (pg,
     121              :                                                 name);
     122              : }
     123              : 
     124              : 
     125              : /**
     126              :  * Start a transaction with deferred wire-out constraints.
     127              :  *
     128              :  * @param pg the database context
     129              :  * @param name name of the transaction
     130              :  * @return #GNUNET_OK on success
     131              :  */
     132              : static enum GNUNET_GenericReturnValue
     133            2 : start_deferred (struct TALER_EXCHANGEDB_PostgresContext *pg,
     134              :                 const char *name)
     135              : {
     136              :   (void) name;
     137            2 :   return TALER_EXCHANGEDB_start_deferred_wire_out (pg);
     138              : }
     139              : 
     140              : 
     141              : /**
     142              :  * All four transaction starters behave the same way as far as the
     143              :  * connection's bookkeeping is concerned.
     144              :  *
     145              :  * @param pg the database context
     146              :  * @return 0 on success
     147              :  */
     148              : static int
     149            1 : check_transactions (struct TALER_EXCHANGEDB_PostgresContext *pg)
     150              : {
     151              :   static enum GNUNET_GenericReturnValue (*const starters[])(
     152              :     struct TALER_EXCHANGEDB_PostgresContext *,
     153              :     const char *) = {
     154              :     &start_serializable,
     155              :     &start_read_only,
     156              :     &start_read_committed,
     157              :     &start_deferred
     158              :   };
     159              : 
     160            1 :   FAILIF (NULL != pg->transaction_name);
     161            5 :   for (unsigned int i = 0; i < 4; i++)
     162              :   {
     163              :     /* commit ends the transaction ... */
     164            4 :     FAILIF (GNUNET_OK !=
     165              :             starters[i](pg,
     166              :                         "misc-commit"));
     167            4 :     FAILIF (NULL == pg->transaction_name);
     168            4 :     FAILIF (0 > TALER_EXCHANGEDB_commit (pg));
     169            4 :     FAILIF (NULL != pg->transaction_name);
     170              : 
     171              :     /* ... and so does a rollback */
     172            4 :     FAILIF (GNUNET_OK !=
     173              :             starters[i](pg,
     174              :                         "misc-rollback"));
     175            4 :     FAILIF (NULL == pg->transaction_name);
     176            4 :     TALER_EXCHANGEDB_rollback (pg);
     177            4 :     FAILIF (NULL != pg->transaction_name);
     178              :   }
     179              : 
     180              :   /* rolling back when nothing is open is a no-op */
     181            1 :   TALER_EXCHANGEDB_rollback (pg);
     182            1 :   FAILIF (NULL != pg->transaction_name);
     183            1 :   return 0;
     184              : }
     185              : 
     186              : 
     187              : /**
     188              :  * A transaction really isolates: what it wrote is gone after a rollback
     189              :  * and there after a commit.
     190              :  *
     191              :  * @param pg the database context
     192              :  * @return 0 on success
     193              :  */
     194              : static int
     195            1 : check_isolation (struct TALER_EXCHANGEDB_PostgresContext *pg)
     196              : {
     197            1 :   FAILIF (GNUNET_OK !=
     198              :           TALER_EXCHANGEDB_start (pg,
     199              :                                   "misc-isolation"));
     200            1 :   TDB_account (pg,
     201              :                10,
     202              :                &account);
     203            1 :   FAILIF (1 != TDB_count (pg,
     204              :                           "FROM wire_targets"));
     205            1 :   TALER_EXCHANGEDB_rollback (pg);
     206            1 :   FAILIF (0 != TDB_count (pg,
     207              :                           "FROM wire_targets"));
     208              : 
     209            1 :   FAILIF (GNUNET_OK !=
     210              :           TALER_EXCHANGEDB_start (pg,
     211              :                                   "misc-isolation"));
     212            1 :   TDB_account (pg,
     213              :                10,
     214              :                &account);
     215            1 :   FAILIF (0 > TALER_EXCHANGEDB_commit (pg));
     216            1 :   FAILIF (1 != TDB_count (pg,
     217              :                           "FROM wire_targets"));
     218            1 :   return 0;
     219              : }
     220              : 
     221              : 
     222              : /**
     223              :  * Preflight is happy on an idle connection and cleans up after a caller
     224              :  * that forgot to end its transaction.
     225              :  *
     226              :  * @param pg the database context
     227              :  * @return 0 on success
     228              :  */
     229              : static int
     230            1 : check_preflight (struct TALER_EXCHANGEDB_PostgresContext *pg)
     231              : {
     232            1 :   FAILIF (GNUNET_OK !=
     233              :           TALER_EXCHANGEDB_preflight (pg));
     234              : 
     235              :   /* a forgotten transaction is rolled back, and preflight says so */
     236            1 :   FAILIF (GNUNET_OK !=
     237              :           TALER_EXCHANGEDB_start (pg,
     238              :                                   "misc-leak"));
     239            1 :   FAILIF (GNUNET_NO !=
     240              :           TALER_EXCHANGEDB_preflight (pg));
     241            1 :   FAILIF (NULL != pg->transaction_name);
     242            1 :   FAILIF (GNUNET_OK !=
     243              :           TALER_EXCHANGEDB_preflight (pg));
     244              : 
     245              :   /* which is also what start() does for us */
     246            1 :   FAILIF (GNUNET_OK !=
     247              :           TALER_EXCHANGEDB_start (pg,
     248              :                                   "misc-leak"));
     249            1 :   FAILIF (GNUNET_OK !=
     250              :           TALER_EXCHANGEDB_start (pg,
     251              :                                   "misc-after-leak"));
     252            1 :   TALER_EXCHANGEDB_rollback (pg);
     253            1 :   return 0;
     254              : }
     255              : 
     256              : 
     257              : /**
     258              :  * The shard of a merchant key is deterministic and stays in range.
     259              :  *
     260              :  * @param pg the database context
     261              :  * @return 0 on success
     262              :  */
     263              : static int
     264            1 : check_compute_shard (struct TALER_EXCHANGEDB_PostgresContext *pg)
     265              : {
     266              :   struct TALER_MerchantPublicKeyP m1;
     267              :   struct TALER_MerchantPublicKeyP m2;
     268              :   uint64_t s1;
     269              :   uint64_t s2;
     270              : 
     271              :   (void) pg;
     272            1 :   TDB_FILL (m1,
     273              :             1);
     274            1 :   TDB_FILL (m2,
     275              :             2);
     276            1 :   s1 = TALER_EXCHANGEDB_compute_shard (&m1);
     277            1 :   s2 = TALER_EXCHANGEDB_compute_shard (&m2);
     278            1 :   FAILIF (s1 > INT32_MAX);
     279            1 :   FAILIF (s2 > INT32_MAX);
     280            1 :   FAILIF (s1 == s2);
     281              :   /* the same key always maps to the same shard, otherwise the shard
     282              :      locks of the aggregator would not partition the work */
     283            1 :   FAILIF (s1 != TALER_EXCHANGEDB_compute_shard (&m1));
     284            1 :   FAILIF (s2 != TALER_EXCHANGEDB_compute_shard (&m2));
     285            1 :   return 0;
     286              : }
     287              : 
     288              : 
     289              : /**
     290              :  * The coin transaction list adds up and can be released.
     291              :  *
     292              :  * @param pg the database context
     293              :  * @return 0 on success
     294              :  */
     295              : static int
     296            1 : check_coin_list (struct TALER_EXCHANGEDB_PostgresContext *pg)
     297              : {
     298            1 :   struct TALER_EXCHANGEDB_TransactionList *tl = NULL;
     299              :   struct TALER_CoinPublicInfo coin;
     300              :   struct TDB_Deposit dep;
     301              :   struct TALER_Amount balance;
     302              :   struct TALER_Amount total;
     303            1 :   struct TALER_Amount zero = TDB_amount ("0");
     304            1 :   struct TALER_Amount one = TDB_amount ("1");
     305            1 :   struct TALER_Amount two = TDB_amount ("2");
     306              :   struct TALER_DenominationHashP h_denom_pub;
     307            1 :   uint64_t etag = 0;
     308              : 
     309              :   /* an empty list is worth the offset it started from */
     310            1 :   FAILIF (GNUNET_OK !=
     311              :           TALER_EXCHANGEDB_calculate_transaction_list_totals (NULL,
     312              :                                                               &one,
     313              :                                                               &total));
     314            1 :   FAILIF (0 != TALER_amount_cmp (&total,
     315              :                                  &one));
     316              :   /* and freeing it is a no-op */
     317            1 :   TALER_EXCHANGEDB_free_coin_transaction_list (NULL);
     318              : 
     319            1 :   TDB_denom (pg,
     320              :              10,
     321              :              "5",
     322              :              "0.1",
     323              :              &denom);
     324            1 :   TDB_coin (pg,
     325              :             &denom,
     326              :             20,
     327              :             &coin,
     328              :             NULL);
     329            1 :   TDB_deposit (pg,
     330              :                &account,
     331              :                &coin,
     332              :                30,
     333              :                "1",
     334              :                "0.1",
     335              :                GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_HOURS),
     336              :                GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_HOURS),
     337              :                &dep);
     338            1 :   FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     339              :             TALER_EXCHANGEDB_get_coin_transactions (pg,
     340              :                                                     true,
     341              :                                                     &coin.coin_pub,
     342              :                                                     0,
     343              :                                                     0,
     344              :                                                     &etag,
     345              :                                                     &balance,
     346              :                                                     &h_denom_pub,
     347              :                                                     &tl),
     348              :             TDB_coin_free (&coin));
     349            1 :   TDB_coin_free (&coin);
     350            1 :   FAILIF (NULL == tl);
     351            1 :   FAILIF_C (GNUNET_OK !=
     352              :             TALER_EXCHANGEDB_calculate_transaction_list_totals (tl,
     353              :                                                                 &zero,
     354              :                                                                 &total),
     355              :             TALER_EXCHANGEDB_free_coin_transaction_list (tl));
     356            1 :   FAILIF_C (0 != TALER_amount_cmp (&total,
     357              :                                    &one),
     358              :             TALER_EXCHANGEDB_free_coin_transaction_list (tl));
     359              :   /* the offset is added on top */
     360            1 :   FAILIF_C (GNUNET_OK !=
     361              :             TALER_EXCHANGEDB_calculate_transaction_list_totals (tl,
     362              :                                                                 &one,
     363              :                                                                 &total),
     364              :             TALER_EXCHANGEDB_free_coin_transaction_list (tl));
     365            1 :   FAILIF_C (0 != TALER_amount_cmp (&total,
     366              :                                    &two),
     367              :             TALER_EXCHANGEDB_free_coin_transaction_list (tl));
     368            1 :   TALER_EXCHANGEDB_free_coin_transaction_list (tl);
     369            1 :   return 0;
     370              : }
     371              : 
     372              : 
     373              : /**
     374              :  * The reserve history can be released.
     375              :  *
     376              :  * @param pg the database context
     377              :  * @return 0 on success
     378              :  */
     379              : static int
     380            1 : check_reserve_history (struct TALER_EXCHANGEDB_PostgresContext *pg)
     381              : {
     382            1 :   struct TALER_EXCHANGEDB_ReserveHistory *rh = NULL;
     383              :   struct TALER_ReservePublicKeyP reserve_pub;
     384              :   struct TALER_Amount balance;
     385            1 :   uint64_t etag = 0;
     386            1 :   unsigned int cnt = 0;
     387              : 
     388              :   /* freeing nothing is a no-op */
     389            1 :   TALER_EXCHANGEDB_free_reserve_history (NULL);
     390              : 
     391            1 :   TDB_reserve_in (pg,
     392              :                   &account,
     393              :                   11,
     394              :                   "10",
     395              :                   &reserve_pub);
     396            1 :   (void) TDB_withdraw (pg,
     397              :                        &denom,
     398              :                        &reserve_pub,
     399              :                        11,
     400              :                        "5");
     401            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     402              :           TALER_EXCHANGEDB_get_reserve_history (pg,
     403              :                                                 &reserve_pub,
     404              :                                                 0,
     405              :                                                 0,
     406              :                                                 &etag,
     407              :                                                 &balance,
     408              :                                                 &rh));
     409            1 :   FAILIF (NULL == rh);
     410            1 :   for (const struct TALER_EXCHANGEDB_ReserveHistory *pos = rh;
     411            3 :        NULL != pos;
     412            2 :        pos = pos->next)
     413            2 :     cnt++;
     414            1 :   FAILIF_C (2 != cnt,
     415              :             TALER_EXCHANGEDB_free_reserve_history (rh));
     416            1 :   TALER_EXCHANGEDB_free_reserve_history (rh);
     417            1 :   return 0;
     418              : }
     419              : 
     420              : 
     421              : /**
     422              :  * State of the event check.
     423              :  */
     424              : struct EventState
     425              : {
     426              :   /**
     427              :    * How often did the callback fire with a payload?
     428              :    */
     429              :   unsigned int fired;
     430              : 
     431              :   /**
     432              :    * Payload of the last notification.
     433              :    */
     434              :   char payload[16];
     435              : 
     436              :   /**
     437              :    * Number of bytes in @e payload.
     438              :    */
     439              :   size_t payload_len;
     440              : };
     441              : 
     442              : 
     443              : /**
     444              :  * Payload of the notification.
     445              :  */
     446              : #define PAYLOAD "hello"
     447              : 
     448              : 
     449              : /**
     450              :  * Called when the notification arrives (or the listener times out).
     451              :  *
     452              :  * @param cls a `struct EventState *`
     453              :  * @param extra the payload, NULL on timeout
     454              :  * @param extra_size number of bytes in @a extra
     455              :  */
     456              : static void
     457            1 : event_cb (void *cls,
     458              :           const void *extra,
     459              :           size_t extra_size)
     460              : {
     461            1 :   struct EventState *st = cls;
     462              : 
     463            1 :   if (NULL == extra)
     464            0 :     return; /* timeout event, not what we are after */
     465            1 :   st->fired++;
     466            1 :   st->payload_len = GNUNET_MIN (extra_size,
     467              :                                 sizeof (st->payload));
     468            1 :   memcpy (st->payload,
     469              :           extra,
     470              :           st->payload_len);
     471              : }
     472              : 
     473              : 
     474              : /**
     475              :  * A notification sent on the connection reaches a listener registered on
     476              :  * it, and stops reaching it once the listener is cancelled.
     477              :  *
     478              :  * @param pg the database context
     479              :  * @return 0 on success
     480              :  */
     481              : static int
     482            1 : check_events (struct TALER_EXCHANGEDB_PostgresContext *pg)
     483              : {
     484            1 :   struct GNUNET_DB_EventHeaderP es = {
     485            1 :     .size = htons (sizeof (es)),
     486            1 :     .type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED)
     487              :   };
     488              :   struct GNUNET_DB_EventHandler *eh;
     489            1 :   struct EventState st = { 0 };
     490              : 
     491            1 :   eh = TALER_EXCHANGEDB_event_listen (pg,
     492              :                                       GNUNET_TIME_UNIT_MINUTES,
     493              :                                       &es,
     494              :                                       &event_cb,
     495              :                                       &st);
     496            1 :   FAILIF (NULL == eh);
     497            1 :   TALER_EXCHANGEDB_event_notify (pg,
     498              :                                  &es,
     499              :                                  PAYLOAD,
     500              :                                  strlen (PAYLOAD));
     501              :   /* the scheduler task that would pick this up does not get to run while
     502              :      the checks are executing, so poll for it ourselves -- which is also
     503              :      what the exchange does after every notifying transaction */
     504            1 :   GNUNET_PQ_event_do_poll (pg->conn);
     505            1 :   FAILIF_C (1 != st.fired,
     506              :             TALER_EXCHANGEDB_event_listen_cancel (pg, eh));
     507            1 :   FAILIF_C (strlen (PAYLOAD) != st.payload_len,
     508              :             TALER_EXCHANGEDB_event_listen_cancel (pg, eh));
     509            1 :   FAILIF_C (0 != memcmp (st.payload,
     510              :                          PAYLOAD,
     511              :                          st.payload_len),
     512              :             TALER_EXCHANGEDB_event_listen_cancel (pg, eh));
     513              : 
     514              :   /* an unrelated event does not wake us */
     515              :   {
     516            1 :     struct GNUNET_DB_EventHeaderP other = {
     517            1 :       .size = htons (sizeof (other)),
     518            1 :       .type = htons (TALER_DBEVENT_EXCHANGE_NEW_KYC_ATTRIBUTES)
     519              :     };
     520              : 
     521            1 :     TALER_EXCHANGEDB_event_notify (pg,
     522              :                                    &other,
     523              :                                    PAYLOAD,
     524              :                                    strlen (PAYLOAD));
     525            1 :     GNUNET_PQ_event_do_poll (pg->conn);
     526            1 :     FAILIF_C (1 != st.fired,
     527              :               TALER_EXCHANGEDB_event_listen_cancel (pg, eh));
     528              :   }
     529              : 
     530              :   /* and after cancelling, neither does the one we listened for */
     531            1 :   TALER_EXCHANGEDB_event_listen_cancel (pg,
     532              :                                         eh);
     533            1 :   TALER_EXCHANGEDB_event_notify (pg,
     534              :                                  &es,
     535              :                                  PAYLOAD,
     536              :                                  strlen (PAYLOAD));
     537            1 :   GNUNET_PQ_event_do_poll (pg->conn);
     538            1 :   FAILIF (1 != st.fired);
     539            1 :   return 0;
     540              : }
     541              : 
     542              : 
     543              : /**
     544              :  * Customization rules that were never deployed cannot be dropped.
     545              :  *
     546              :  * @param pg the database context
     547              :  * @return 0 on success
     548              :  */
     549              : static int
     550            1 : check_rules (struct TALER_EXCHANGEDB_PostgresContext *pg)
     551              : {
     552            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     553              :           TALER_EXCHANGEDB_disable_rules (pg,
     554              :                                           "no-such-customization"));
     555              :   /* and a schema without SQL files on disk cannot be deployed; the
     556              :      search_path must be back to `exchange' afterwards either way */
     557            1 :   FAILIF (GNUNET_SYSERR ==
     558              :           TALER_EXCHANGEDB_enable_rules (pg,
     559              :                                          "no-such-customization"));
     560              :   /* the search_path is back to `exchange', so an unqualified query
     561              :      still finds the exchange's own tables */
     562            1 :   FAILIF (1 != TDB_count (pg,
     563              :                           "FROM wire_targets"));
     564            1 :   return 0;
     565              : }
     566              : 
     567              : 
     568              : /**
     569              :  * The auditor triggers can be injected into an existing schema.
     570              :  *
     571              :  * @param pg the database context
     572              :  * @return 0 on success
     573              :  */
     574              : static int
     575            1 : check_auditor_triggers (struct TALER_EXCHANGEDB_PostgresContext *pg)
     576              : {
     577            1 :   FAILIF (GNUNET_OK !=
     578              :           TALER_EXCHANGEDB_inject_auditor_triggers (pg));
     579            1 :   return 0;
     580              : }
     581              : 
     582              : 
     583              : /**
     584              :  * Dropping and re-creating the schema leaves an empty but usable
     585              :  * database.  Nothing may run after this check.
     586              :  *
     587              :  * @param pg the database context
     588              :  * @return 0 on success
     589              :  */
     590              : static int
     591            1 : check_schema (struct TALER_EXCHANGEDB_PostgresContext *pg)
     592              : {
     593            1 :   FAILIF (0 == TDB_count (pg,
     594              :                           "FROM wire_targets"));
     595            1 :   FAILIF (GNUNET_OK !=
     596              :           TALER_EXCHANGEDB_drop_tables (pg));
     597              :   /* dropping twice fails: drop.sql says `DROP SCHEMA exchange CASCADE'
     598              :      without IF EXISTS, so there is nothing left to drop the second time */
     599            1 :   FAILIF (GNUNET_OK ==
     600              :           TALER_EXCHANGEDB_drop_tables (pg));
     601            1 :   FAILIF (GNUNET_OK !=
     602              :           TALER_EXCHANGEDB_create_tables (pg,
     603              :                                           false,
     604              :                                           0));
     605            1 :   FAILIF (0 != TDB_count (pg,
     606              :                           "FROM wire_targets"));
     607            1 :   return 0;
     608              : }
     609              : 
     610              : 
     611              : /**
     612              :  * The checks to run, in order.
     613              :  */
     614              : static const struct TDB_Test tests[] = {
     615              :   { "misc-transactions",
     616              :     &check_transactions },
     617              :   { "misc-isolation",
     618              :     &check_isolation },
     619              :   { "misc-preflight",
     620              :     &check_preflight },
     621              :   { "misc-compute-shard",
     622              :     &check_compute_shard },
     623              :   { "misc-coin-list",
     624              :     &check_coin_list },
     625              :   { "misc-reserve-history",
     626              :     &check_reserve_history },
     627              :   { "misc-events",
     628              :     &check_events },
     629              :   { "misc-rules",
     630              :     &check_rules },
     631              :   { "misc-auditor-triggers",
     632              :     &check_auditor_triggers },
     633              :   { "misc-schema",
     634              :     &check_schema },
     635              :   { NULL, NULL }
     636              : };
     637              : 
     638              : 
     639              : int
     640            1 : main (int argc,
     641              :       char *const *argv)
     642              : {
     643              :   int ret;
     644              : 
     645            1 :   ret = TDB_main (argc,
     646              :                   argv,
     647              :                   "test-misc",
     648              :                   "Tests for the exchangedb functions without a table",
     649              :                   tests);
     650            1 :   TDB_denom_free (&denom);
     651            1 :   TDB_account_free (&account);
     652            1 :   return ret;
     653              : }
     654              : 
     655              : 
     656              : /* end of test_misc.c */
        

Generated by: LCOV version 2.0-1