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 */