test_auditors.c (11542B)
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_auditors.c 18 * @brief tests for the exchangedb functions whose primary table is `auditors` 19 * @author Christian Grothoff 20 * 21 * Covers #TALER_EXCHANGEDB_insert_auditor(), 22 * #TALER_EXCHANGEDB_update_auditor(), 23 * #TALER_EXCHANGEDB_get_auditor_status(), 24 * #TALER_EXCHANGEDB_get_auditor_timestamp() and 25 * #TALER_EXCHANGEDB_iterate_active_auditors(). 26 * 27 * `auditors` has no foreign keys, so the checks need no fixtures. 28 */ 29 #include "test_common.h" 30 #include "exchange-database/insert_auditor.h" 31 #include "exchange-database/update_auditor.h" 32 #include "exchange-database/get_auditor_status.h" 33 #include "exchange-database/get_auditor_timestamp.h" 34 #include "exchange-database/iterate_active_auditors.h" 35 36 37 /** 38 * Nothing is known about an auditor that was never inserted. 39 * 40 * @param pg the database context 41 * @return 0 on success 42 */ 43 static int 44 check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg) 45 { 46 struct TALER_AuditorPublicKeyP auditor_pub; 47 struct GNUNET_TIME_Timestamp last_date; 48 char *url = NULL; 49 bool enabled = true; 50 51 TDB_FILL (auditor_pub, 52 1); 53 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 54 TALER_EXCHANGEDB_get_auditor_status (pg, 55 &auditor_pub, 56 &url, 57 &enabled)); 58 FAILIF (NULL != url); 59 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 60 TALER_EXCHANGEDB_get_auditor_timestamp (pg, 61 &auditor_pub, 62 &last_date)); 63 /* updating an auditor that is not on file does not create one */ 64 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 65 TALER_EXCHANGEDB_update_auditor (pg, 66 &auditor_pub, 67 "https://auditor.example/", 68 "Nobody", 69 GNUNET_TIME_timestamp_get (), 70 true)); 71 FAILIF (0 != TDB_count (pg, 72 "FROM auditors")); 73 return 0; 74 } 75 76 77 /** 78 * Closure for #count_auditors_cb(). 79 */ 80 struct CountContext 81 { 82 /** 83 * Auditor we are looking for. 84 */ 85 const struct TALER_AuditorPublicKeyP *auditor_pub; 86 87 /** 88 * How many auditors did the callback see in total? 89 */ 90 unsigned int total; 91 92 /** 93 * How many times did we see @e auditor_pub? 94 */ 95 unsigned int matched; 96 97 /** 98 * URL reported for @e auditor_pub, owned by this struct. 99 */ 100 char *url; 101 102 /** 103 * Name reported for @e auditor_pub, owned by this struct. 104 */ 105 char *name; 106 }; 107 108 109 /** 110 * Callback for #TALER_EXCHANGEDB_iterate_active_auditors(). 111 * 112 * @param cls a `struct CountContext *` 113 * @param auditor_pub public key of the auditor 114 * @param auditor_url base URL of the auditor 115 * @param auditor_name human-readable name of the auditor 116 */ 117 static void 118 count_auditors_cb (void *cls, 119 const struct TALER_AuditorPublicKeyP *auditor_pub, 120 const char *auditor_url, 121 const char *auditor_name) 122 { 123 struct CountContext *ctx = cls; 124 125 ctx->total++; 126 if (0 != GNUNET_memcmp (auditor_pub, 127 ctx->auditor_pub)) 128 return; 129 ctx->matched++; 130 GNUNET_free (ctx->url); 131 GNUNET_free (ctx->name); 132 ctx->url = GNUNET_strdup (auditor_url); 133 ctx->name = GNUNET_strdup (auditor_name); 134 } 135 136 137 /** 138 * Insert an auditor, read it back, then disable and re-enable it. 139 * 140 * @param pg the database context 141 * @return 0 on success 142 */ 143 static int 144 check_insert_update (struct TALER_EXCHANGEDB_PostgresContext *pg) 145 { 146 struct TALER_AuditorPublicKeyP auditor_pub; 147 struct GNUNET_TIME_Timestamp start; 148 struct GNUNET_TIME_Timestamp later; 149 struct GNUNET_TIME_Timestamp last_date; 150 struct CountContext ctx; 151 char *url = NULL; 152 bool enabled = false; 153 154 TDB_FILL (auditor_pub, 155 2); 156 start = GNUNET_TIME_timestamp_get (); 157 later = GNUNET_TIME_absolute_to_timestamp ( 158 GNUNET_TIME_absolute_add (start.abs_time, 159 GNUNET_TIME_UNIT_HOURS)); 160 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 161 TALER_EXCHANGEDB_insert_auditor (pg, 162 &auditor_pub, 163 "https://auditor.example/", 164 "The Auditor", 165 start)); 166 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 167 TALER_EXCHANGEDB_get_auditor_status (pg, 168 &auditor_pub, 169 &url, 170 &enabled)); 171 FAILIF (NULL == url); 172 FAILIF_C (0 != strcmp (url, 173 "https://auditor.example/"), 174 GNUNET_free (url)); 175 GNUNET_free (url); 176 /* a fresh auditor is active */ 177 FAILIF (! enabled); 178 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 179 TALER_EXCHANGEDB_get_auditor_timestamp (pg, 180 &auditor_pub, 181 &last_date)); 182 FAILIF (GNUNET_TIME_timestamp_cmp (last_date, 183 !=, 184 start)); 185 186 memset (&ctx, 187 0, 188 sizeof (ctx)); 189 ctx.auditor_pub = &auditor_pub; 190 FAILIF (0 >= 191 TALER_EXCHANGEDB_iterate_active_auditors (pg, 192 &count_auditors_cb, 193 &ctx)); 194 FAILIF_C (1 != ctx.matched, 195 GNUNET_free (ctx.url); GNUNET_free (ctx.name)); 196 FAILIF_C (0 != strcmp (ctx.name, 197 "The Auditor"), 198 GNUNET_free (ctx.url); GNUNET_free (ctx.name)); 199 GNUNET_free (ctx.url); 200 GNUNET_free (ctx.name); 201 202 /* disabling drops the auditor from the active iterator */ 203 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 204 TALER_EXCHANGEDB_update_auditor (pg, 205 &auditor_pub, 206 "https://auditor2.example/", 207 "The Other Auditor", 208 later, 209 false)); 210 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 211 TALER_EXCHANGEDB_get_auditor_status (pg, 212 &auditor_pub, 213 &url, 214 &enabled)); 215 FAILIF_C (0 != strcmp (url, 216 "https://auditor2.example/"), 217 GNUNET_free (url)); 218 GNUNET_free (url); 219 FAILIF (enabled); 220 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 221 TALER_EXCHANGEDB_get_auditor_timestamp (pg, 222 &auditor_pub, 223 &last_date)); 224 FAILIF (GNUNET_TIME_timestamp_cmp (last_date, 225 !=, 226 later)); 227 memset (&ctx, 228 0, 229 sizeof (ctx)); 230 ctx.auditor_pub = &auditor_pub; 231 FAILIF (0 > 232 TALER_EXCHANGEDB_iterate_active_auditors (pg, 233 &count_auditors_cb, 234 &ctx)); 235 FAILIF (0 != ctx.matched); 236 237 /* ...and re-enabling brings it back */ 238 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 239 TALER_EXCHANGEDB_update_auditor (pg, 240 &auditor_pub, 241 "https://auditor2.example/", 242 "The Other Auditor", 243 later, 244 true)); 245 memset (&ctx, 246 0, 247 sizeof (ctx)); 248 ctx.auditor_pub = &auditor_pub; 249 FAILIF (0 >= 250 TALER_EXCHANGEDB_iterate_active_auditors (pg, 251 &count_auditors_cb, 252 &ctx)); 253 FAILIF_C (1 != ctx.matched, 254 GNUNET_free (ctx.url); GNUNET_free (ctx.name)); 255 GNUNET_free (ctx.url); 256 GNUNET_free (ctx.name); 257 return 0; 258 } 259 260 261 /** 262 * A second auditor is independent of the first, and only the active ones 263 * are iterated. 264 * 265 * @param pg the database context 266 * @return 0 on success 267 */ 268 static int 269 check_two_auditors (struct TALER_EXCHANGEDB_PostgresContext *pg) 270 { 271 struct TALER_AuditorPublicKeyP a2; 272 struct TALER_AuditorPublicKeyP a3; 273 struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); 274 struct CountContext ctx; 275 276 TDB_FILL (a2, 277 3); 278 TDB_FILL (a3, 279 4); 280 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 281 TALER_EXCHANGEDB_insert_auditor (pg, 282 &a2, 283 "https://a2.example/", 284 "A2", 285 now)); 286 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 287 TALER_EXCHANGEDB_insert_auditor (pg, 288 &a3, 289 "https://a3.example/", 290 "A3", 291 now)); 292 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 293 TALER_EXCHANGEDB_update_auditor (pg, 294 &a3, 295 "https://a3.example/", 296 "A3", 297 now, 298 false)); 299 memset (&ctx, 300 0, 301 sizeof (ctx)); 302 ctx.auditor_pub = &a3; 303 FAILIF (0 >= 304 TALER_EXCHANGEDB_iterate_active_auditors (pg, 305 &count_auditors_cb, 306 &ctx)); 307 FAILIF (0 != ctx.matched); 308 FAILIF (ctx.total != 309 TDB_count (pg, 310 "FROM auditors WHERE is_active")); 311 FAILIF (ctx.total == 312 TDB_count (pg, 313 "FROM auditors")); 314 return 0; 315 } 316 317 318 /** 319 * The checks to run, in order. 320 */ 321 static const struct TDB_Test tests[] = { 322 { "auditors-empty", 323 &check_empty }, 324 { "auditors-insert-update", 325 &check_insert_update }, 326 { "auditors-two-auditors", 327 &check_two_auditors }, 328 { NULL, NULL } 329 }; 330 331 332 int 333 main (int argc, 334 char *const *argv) 335 { 336 return TDB_main (argc, 337 argv, 338 "test-auditors", 339 "Tests for the exchangedb `auditors' table", 340 tests); 341 } 342 343 344 /* end of test_auditors.c */