exchange

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

test_kyc_alerts.c (9695B)


      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_kyc_alerts.c
     18  * @brief tests for the exchangedb functions whose primary table is
     19  *        `kyc_alerts`
     20  * @author Christian Grothoff
     21  *
     22  * Covers #TALER_EXCHANGEDB_do_drain_kyc_alert(), and that the table keeps
     23  * one alert per account and trigger type.
     24  *
     25  * `kyc_alerts` is a work queue: the stored procedures that change the KYC
     26  * state of an account append a row, and taler-exchange-aggregator drains
     27  * it one row at a time.  Nothing exported writes to it directly, so the
     28  * rows here are produced through
     29  * #TALER_EXCHANGEDB_insert_aml_program_failure(), the cheapest of the
     30  * three producers.
     31  */
     32 #include "test_common.h"
     33 #include "exchange-database/do_drain_kyc_alert.h"
     34 #include "exchange-database/insert_aml_program_failure.h"
     35 
     36 
     37 /**
     38  * Trigger type the aggregator drains, and the only one any stored
     39  * procedure ever writes.
     40  */
     41 #define AGGREGATOR_TRIGGER 1
     42 
     43 /**
     44  * Trigger type of a (hypothetical) second receiver of alerts.
     45  */
     46 #define OTHER_TRIGGER 2
     47 
     48 
     49 /**
     50  * The two accounts we raise alerts for.
     51  */
     52 static struct TDB_Account account[2];
     53 
     54 
     55 /**
     56  * Nothing to drain while the queue is empty.
     57  *
     58  * @param pg the database context
     59  * @return 0 on success
     60  */
     61 static int
     62 check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
     63 {
     64   struct TALER_NormalizedPaytoHashP h_payto;
     65 
     66   for (unsigned int i = 0; i < 2; i++)
     67     TDB_account (pg,
     68                  10 + i,
     69                  &account[i]);
     70   memset (&h_payto,
     71           0,
     72           sizeof (h_payto));
     73   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     74           TALER_EXCHANGEDB_do_drain_kyc_alert (pg,
     75                                                AGGREGATOR_TRIGGER,
     76                                                &h_payto));
     77   return 0;
     78 }
     79 
     80 
     81 /**
     82  * A KYC state change queues exactly one alert per account.
     83  *
     84  * @param pg the database context
     85  * @return 0 on success
     86  */
     87 static int
     88 check_raise (struct TALER_EXCHANGEDB_PostgresContext *pg)
     89 {
     90   for (unsigned int i = 0; i < 2; i++)
     91     FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     92             TALER_EXCHANGEDB_insert_aml_program_failure (
     93               pg,
     94               0,
     95               &account[i].h_normalized,
     96               "no such AML program",
     97               TALER_EC_EXCHANGE_GENERIC_KYC_FAILED));
     98   FAILIF (2 != TDB_count (pg,
     99                           "FROM kyc_alerts"
    100                           " WHERE trigger_type=%d",
    101                           AGGREGATOR_TRIGGER));
    102 
    103   /* a second state change on an account that is already queued does not
    104      queue it twice */
    105   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    106           TALER_EXCHANGEDB_insert_aml_program_failure (
    107             pg,
    108             0,
    109             &account[0].h_normalized,
    110             "still no such AML program",
    111             TALER_EC_EXCHANGE_GENERIC_KYC_FAILED));
    112   FAILIF (2 != TDB_count (pg,
    113                           "FROM kyc_alerts"));
    114   return 0;
    115 }
    116 
    117 
    118 /**
    119  * Draining returns the queued accounts one by one and removes them.
    120  *
    121  * @param pg the database context
    122  * @return 0 on success
    123  */
    124 static int
    125 check_drain (struct TALER_EXCHANGEDB_PostgresContext *pg)
    126 {
    127   struct TALER_NormalizedPaytoHashP first;
    128   struct TALER_NormalizedPaytoHashP second;
    129 
    130   /* nobody listens for this trigger type, so the queue looks empty */
    131   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    132           TALER_EXCHANGEDB_do_drain_kyc_alert (pg,
    133                                                AGGREGATOR_TRIGGER + 1,
    134                                                &first));
    135   FAILIF (2 != TDB_count (pg,
    136                           "FROM kyc_alerts"));
    137 
    138   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    139           TALER_EXCHANGEDB_do_drain_kyc_alert (pg,
    140                                                AGGREGATOR_TRIGGER,
    141                                                &first));
    142   FAILIF (1 != TDB_count (pg,
    143                           "FROM kyc_alerts"));
    144   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    145           TALER_EXCHANGEDB_do_drain_kyc_alert (pg,
    146                                                AGGREGATOR_TRIGGER,
    147                                                &second));
    148   FAILIF (0 != TDB_count (pg,
    149                           "FROM kyc_alerts"));
    150 
    151   /* both of our accounts came back, each exactly once */
    152   FAILIF (0 == GNUNET_memcmp (&first,
    153                               &second));
    154   for (unsigned int i = 0; i < 2; i++)
    155   {
    156     unsigned int seen = 0;
    157 
    158     if (0 == GNUNET_memcmp (&first,
    159                             &account[i].h_normalized))
    160       seen++;
    161     if (0 == GNUNET_memcmp (&second,
    162                             &account[i].h_normalized))
    163       seen++;
    164     FAILIF (1 != seen);
    165   }
    166 
    167   /* and the queue is empty again */
    168   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    169           TALER_EXCHANGEDB_do_drain_kyc_alert (pg,
    170                                                AGGREGATOR_TRIGGER,
    171                                                &first));
    172   return 0;
    173 }
    174 
    175 
    176 /**
    177  * An account that was drained can be queued again.
    178  *
    179  * @param pg the database context
    180  * @return 0 on success
    181  */
    182 static int
    183 check_requeue (struct TALER_EXCHANGEDB_PostgresContext *pg)
    184 {
    185   struct TALER_NormalizedPaytoHashP h_payto;
    186 
    187   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    188           TALER_EXCHANGEDB_insert_aml_program_failure (
    189             pg,
    190             0,
    191             &account[1].h_normalized,
    192             "and again",
    193             TALER_EC_EXCHANGE_GENERIC_KYC_FAILED));
    194   FAILIF (1 != TDB_count (pg,
    195                           "FROM kyc_alerts"));
    196   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    197           TALER_EXCHANGEDB_do_drain_kyc_alert (pg,
    198                                                AGGREGATOR_TRIGGER,
    199                                                &h_payto));
    200   FAILIF (0 != GNUNET_memcmp (&h_payto,
    201                               &account[1].h_normalized));
    202   FAILIF (0 != TDB_count (pg,
    203                           "FROM kyc_alerts"));
    204   return 0;
    205 }
    206 
    207 
    208 /**
    209  * Each receiver has its own queue: an account can be queued for two
    210  * receivers at once, and draining it for one leaves it queued for the
    211  * other.
    212  *
    213  * @param pg the database context
    214  * @return 0 on success
    215  */
    216 static int
    217 check_per_trigger (struct TALER_EXCHANGEDB_PostgresContext *pg)
    218 {
    219   struct TALER_NormalizedPaytoHashP h_payto;
    220   char *hex;
    221 
    222   /* no producer writes another trigger type yet, so do it by hand */
    223   hex = TDB_hex (&account[0].h_normalized,
    224                  sizeof (account[0].h_normalized));
    225   FAILIF_C (GNUNET_OK !=
    226             TDB_exec (pg,
    227                       "INSERT INTO kyc_alerts"
    228                       " (h_payto,trigger_type)"
    229                       " VALUES (decode('%s','hex'),%d);",
    230                       hex,
    231                       OTHER_TRIGGER),
    232             GNUNET_free (hex));
    233   GNUNET_free (hex);
    234   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    235           TALER_EXCHANGEDB_insert_aml_program_failure (
    236             pg,
    237             0,
    238             &account[0].h_normalized,
    239             "for the aggregator",
    240             TALER_EC_EXCHANGE_GENERIC_KYC_FAILED));
    241   /* both alerts for the same account are queued */
    242   FAILIF (2 != TDB_count (pg,
    243                           "FROM kyc_alerts"));
    244   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    245           TALER_EXCHANGEDB_do_drain_kyc_alert (pg,
    246                                                AGGREGATOR_TRIGGER,
    247                                                &h_payto));
    248   FAILIF (0 != GNUNET_memcmp (&h_payto,
    249                               &account[0].h_normalized));
    250   /* draining it for the aggregator leaves the other receiver's alert */
    251   FAILIF (1 != TDB_count (pg,
    252                           "FROM kyc_alerts"
    253                           " WHERE trigger_type=%d",
    254                           OTHER_TRIGGER));
    255   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    256           TALER_EXCHANGEDB_do_drain_kyc_alert (pg,
    257                                                AGGREGATOR_TRIGGER,
    258                                                &h_payto));
    259   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    260           TALER_EXCHANGEDB_do_drain_kyc_alert (pg,
    261                                                OTHER_TRIGGER,
    262                                                &h_payto));
    263   FAILIF (0 != GNUNET_memcmp (&h_payto,
    264                               &account[0].h_normalized));
    265   FAILIF (0 != TDB_count (pg,
    266                           "FROM kyc_alerts"));
    267   return 0;
    268 }
    269 
    270 
    271 /**
    272  * The checks to run, in order.
    273  */
    274 static const struct TDB_Test tests[] = {
    275   { "kyc-alerts-empty",
    276     &check_empty },
    277   { "kyc-alerts-raise",
    278     &check_raise },
    279   { "kyc-alerts-drain",
    280     &check_drain },
    281   { "kyc-alerts-requeue",
    282     &check_requeue },
    283   { "kyc-alerts-per-trigger",
    284     &check_per_trigger },
    285   { NULL, NULL }
    286 };
    287 
    288 
    289 int
    290 main (int argc,
    291       char *const *argv)
    292 {
    293   int ret;
    294 
    295   ret = TDB_main (argc,
    296                   argv,
    297                   "test-kyc-alerts",
    298                   "Tests for the exchangedb `kyc_alerts' table",
    299                   tests);
    300   for (unsigned int i = 0; i < 2; i++)
    301     TDB_account_free (&account[i]);
    302   return ret;
    303 }
    304 
    305 
    306 /* end of test_kyc_alerts.c */