exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

test_partners.c (6085B)


      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_partners.c
     18  * @brief tests for the exchangedb functions whose primary table is
     19  *        `partners`
     20  * @author Christian Grothoff
     21  *
     22  * Covers #TALER_EXCHANGEDB_insert_partner().  It is the only exported
     23  * function that writes the table; the rows are read back by the purse
     24  * machinery, which is covered in test_purse_merges.c and
     25  * test_purse_deposits.c.
     26  *
     27  * `partners` has no foreign keys.  The insert is ON CONFLICT DO NOTHING,
     28  * so re-inserting the same (key, validity period) is a no-op rather than a
     29  * failure.
     30  */
     31 #include "test_common.h"
     32 #include "exchange-database/insert_partner.h"
     33 
     34 
     35 /**
     36  * Build a timestamp from a number of seconds since the epoch.
     37  *
     38  * @param secs seconds since the epoch
     39  * @return the timestamp
     40  */
     41 static struct GNUNET_TIME_Timestamp
     42 ts (uint64_t secs)
     43 {
     44   struct GNUNET_TIME_Absolute abs = {
     45     .abs_value_us = secs * 1000LLU * 1000LLU
     46   };
     47 
     48   return GNUNET_TIME_absolute_to_timestamp (abs);
     49 }
     50 
     51 
     52 /**
     53  * Insert a partner exchange.
     54  *
     55  * @param pg the database context
     56  * @param seed seed for the partner's key and signature
     57  * @param from start of the validity period
     58  * @param until end of the validity period
     59  * @param url base URL of the partner
     60  * @return transaction status
     61  */
     62 static enum GNUNET_DB_QueryStatus
     63 add_partner (struct TALER_EXCHANGEDB_PostgresContext *pg,
     64              uint32_t seed,
     65              uint64_t from,
     66              uint64_t until,
     67              const char *url)
     68 {
     69   struct TALER_MasterPublicKeyP master_pub;
     70   struct TALER_MasterSignatureP master_sig;
     71   struct TALER_Amount wad_fee = TDB_amount ("0.05");
     72 
     73   TDB_FILL (master_pub,
     74             seed);
     75   TDB_FILL (master_sig,
     76             seed);
     77   return TALER_EXCHANGEDB_insert_partner (pg,
     78                                           &master_pub,
     79                                           ts (from),
     80                                           ts (until),
     81                                           GNUNET_TIME_UNIT_HOURS,
     82                                           &wad_fee,
     83                                           url,
     84                                           &master_sig);
     85 }
     86 
     87 
     88 /**
     89  * Inserting a partner stores exactly the row we asked for.
     90  *
     91  * @param pg the database context
     92  * @return 0 on success
     93  */
     94 static int
     95 check_insert (struct TALER_EXCHANGEDB_PostgresContext *pg)
     96 {
     97   struct TALER_MasterPublicKeyP master_pub;
     98   struct TALER_Amount wad_fee = TDB_amount ("0.05");
     99   char *hex;
    100 
    101   FAILIF (0 != TDB_count (pg,
    102                           "FROM partners"));
    103   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    104           add_partner (pg,
    105                        10,
    106                        1600000000,
    107                        1700000000,
    108                        "https://partner.example/"));
    109   TDB_FILL (master_pub,
    110             10);
    111   hex = TDB_hex (&master_pub,
    112                  sizeof (master_pub));
    113   FAILIF_C (1 != TDB_count (pg,
    114                             "FROM partners"
    115                             " WHERE partner_master_pub=decode('%s','hex')"
    116                             "   AND partner_base_url='https://partner.example/'"
    117                             "   AND wad_fee=ROW(%llu,%u)::taler_amount"
    118                             "   AND start_date=%llu"
    119                             "   AND end_date=%llu",
    120                             hex,
    121                             (unsigned long long) wad_fee.value,
    122                             (unsigned int) wad_fee.fraction,
    123                             (unsigned long long) 1600000000000000LLU,
    124                             (unsigned long long) 1700000000000000LLU),
    125             GNUNET_free (hex));
    126   GNUNET_free (hex);
    127   return 0;
    128 }
    129 
    130 
    131 /**
    132  * Re-inserting the identical row is a no-op, while a different validity
    133  * period for the same key is a new row.
    134  *
    135  * @param pg the database context
    136  * @return 0 on success
    137  */
    138 static int
    139 check_conflict (struct TALER_EXCHANGEDB_PostgresContext *pg)
    140 {
    141   /* identical to what check_insert() stored */
    142   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    143           add_partner (pg,
    144                        10,
    145                        1600000000,
    146                        1700000000,
    147                        "https://partner.example/"));
    148   FAILIF (1 != TDB_count (pg,
    149                           "FROM partners"));
    150   /* a later period for the same partner is a separate row */
    151   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    152           add_partner (pg,
    153                        10,
    154                        1700000000,
    155                        1800000000,
    156                        "https://partner.example/"));
    157   FAILIF (2 != TDB_count (pg,
    158                           "FROM partners"));
    159   /* and so is a different partner */
    160   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    161           add_partner (pg,
    162                        11,
    163                        1600000000,
    164                        1700000000,
    165                        "https://other.example/"));
    166   FAILIF (3 != TDB_count (pg,
    167                           "FROM partners"));
    168   return 0;
    169 }
    170 
    171 
    172 /**
    173  * The checks to run, in order.
    174  */
    175 static const struct TDB_Test tests[] = {
    176   { "partners-insert",
    177     &check_insert },
    178   { "partners-conflict",
    179     &check_conflict },
    180   { NULL, NULL }
    181 };
    182 
    183 
    184 int
    185 main (int argc,
    186       char *const *argv)
    187 {
    188   return TDB_main (argc,
    189                    argv,
    190                    "test-partners",
    191                    "Tests for the exchangedb `partners' table",
    192                    tests);
    193 }
    194 
    195 
    196 /* end of test_partners.c */