test_denominations.c (12756B)
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_denominations.c 18 * @brief tests for the exchangedb functions whose primary table is 19 * `denominations` 20 * @author Christian Grothoff 21 * 22 * Covers #TALER_EXCHANGEDB_insert_denomination_info(), 23 * #TALER_EXCHANGEDB_get_denomination_info(), 24 * #TALER_EXCHANGEDB_get_denomination_by_serial(), 25 * #TALER_EXCHANGEDB_get_denomination_meta(), 26 * #TALER_EXCHANGEDB_iterate_denomination_info() and 27 * #TALER_EXCHANGEDB_iterate_denominations(). 28 * 29 * `denominations` has no foreign keys, so the checks need no fixtures. The 30 * empty-table checks run first, before any denomination exists. 31 */ 32 #include "test_common.h" 33 #include "exchange-database/insert_denomination_info.h" 34 #include "exchange-database/get_denomination_info.h" 35 #include "exchange-database/get_denomination_by_serial.h" 36 #include "exchange-database/get_denomination_meta.h" 37 #include "exchange-database/iterate_denomination_info.h" 38 #include "exchange-database/iterate_denominations.h" 39 40 41 /** 42 * Check that nothing is found while the table is still empty. 43 * 44 * @param pg the database context 45 * @return 0 on success 46 */ 47 static int 48 check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg) 49 { 50 struct TALER_DenominationHashP h_denom_pub; 51 struct TALER_EXCHANGEDB_DenominationKeyInformation issue; 52 struct TALER_EXCHANGEDB_DenominationKeyMetaData meta; 53 uint64_t serial; 54 55 TDB_FILL (h_denom_pub, 56 1); 57 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 58 TALER_EXCHANGEDB_get_denomination_info (pg, 59 &h_denom_pub, 60 &serial, 61 &issue)); 62 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 63 TALER_EXCHANGEDB_get_denomination_by_serial (pg, 64 42, 65 &issue)); 66 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 67 TALER_EXCHANGEDB_get_denomination_meta (pg, 68 &h_denom_pub, 69 &meta)); 70 return 0; 71 } 72 73 74 /** 75 * Closure for #count_info_cb() and #count_denoms_cb(). 76 */ 77 struct CountContext 78 { 79 /** 80 * Denomination we are looking for. 81 */ 82 const struct TDB_Denom *denom; 83 84 /** 85 * How many rows did the callback see in total? 86 */ 87 unsigned int total; 88 89 /** 90 * How many times did we see @e denom? 91 */ 92 unsigned int matched; 93 94 /** 95 * Set to true if @e denom was reported with data that does not match 96 * what we inserted. 97 */ 98 bool mismatch; 99 100 /** 101 * Recoup flag reported for @e denom. 102 */ 103 bool recoup_possible; 104 105 /** 106 * Row reported for @e denom. 107 */ 108 uint64_t serial; 109 }; 110 111 112 /** 113 * Callback for #TALER_EXCHANGEDB_iterate_denomination_info(). 114 * 115 * @param cls a `struct CountContext *` 116 * @param denom_serial table row of the denomination 117 * @param denom_pub public key of the denomination 118 * @param issue information about the denomination 119 */ 120 static void 121 count_info_cb (void *cls, 122 uint64_t denom_serial, 123 const struct TALER_DenominationPublicKey *denom_pub, 124 const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue) 125 { 126 struct CountContext *ctx = cls; 127 128 ctx->total++; 129 if (0 != GNUNET_memcmp (&issue->denom_hash, 130 &ctx->denom->h_denom_pub)) 131 return; 132 ctx->matched++; 133 ctx->serial = denom_serial; 134 if ( (0 != TALER_denom_pub_cmp (denom_pub, 135 &ctx->denom->pub)) || 136 (0 != TALER_amount_cmp (&issue->value, 137 &ctx->denom->issue.value)) || 138 (0 != TALER_amount_cmp (&issue->fees.deposit, 139 &ctx->denom->issue.fees.deposit)) ) 140 ctx->mismatch = true; 141 } 142 143 144 /** 145 * Callback for #TALER_EXCHANGEDB_iterate_denominations(). 146 * 147 * @param cls a `struct CountContext *` 148 * @param denom_pub public key of the denomination 149 * @param h_denom_pub hash of @a denom_pub 150 * @param meta meta data of the denomination 151 * @param master_sig master signature over the denomination 152 * @param recoup_possible true if the denomination was revoked 153 */ 154 static void 155 count_denoms_cb (void *cls, 156 const struct TALER_DenominationPublicKey *denom_pub, 157 const struct TALER_DenominationHashP *h_denom_pub, 158 const struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta, 159 const struct TALER_MasterSignatureP *master_sig, 160 bool recoup_possible) 161 { 162 struct CountContext *ctx = cls; 163 164 ctx->total++; 165 if (0 != GNUNET_memcmp (h_denom_pub, 166 &ctx->denom->h_denom_pub)) 167 return; 168 ctx->matched++; 169 ctx->serial = meta->serial; 170 ctx->recoup_possible = recoup_possible; 171 if ( (0 != TALER_denom_pub_cmp (denom_pub, 172 &ctx->denom->pub)) || 173 (0 != GNUNET_memcmp (master_sig, 174 &ctx->denom->issue.signature)) || 175 (0 != TALER_amount_cmp (&meta->value, 176 &ctx->denom->issue.value)) || 177 (GNUNET_TIME_timestamp_cmp (meta->start, 178 !=, 179 ctx->denom->issue.start)) ) 180 ctx->mismatch = true; 181 } 182 183 184 /** 185 * Insert a denomination and read it back through every lookup the 186 * table offers. 187 * 188 * @param pg the database context 189 * @return 0 on success 190 */ 191 static int 192 check_insert_and_lookup (struct TALER_EXCHANGEDB_PostgresContext *pg) 193 { 194 struct TDB_Denom denom; 195 struct TALER_EXCHANGEDB_DenominationKeyInformation issue; 196 struct TALER_EXCHANGEDB_DenominationKeyMetaData meta; 197 struct TALER_DenominationHashP other; 198 uint64_t serial; 199 200 /* TDB_denom() inserts and looks up the row it created. */ 201 TDB_denom (pg, 202 10, 203 "5", 204 "0.1", 205 &denom); 206 memset (&issue, 207 0, 208 sizeof (issue)); 209 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 210 TALER_EXCHANGEDB_get_denomination_info (pg, 211 &denom.h_denom_pub, 212 &serial, 213 &issue), 214 TDB_denom_free (&denom)); 215 FAILIF_C (0 != GNUNET_memcmp (&issue, 216 &denom.issue), 217 TDB_denom_free (&denom)); 218 FAILIF_C (serial != denom.serial, 219 TDB_denom_free (&denom)); 220 221 /* the denom_serial output is optional */ 222 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 223 TALER_EXCHANGEDB_get_denomination_info (pg, 224 &denom.h_denom_pub, 225 NULL, 226 &issue), 227 TDB_denom_free (&denom)); 228 229 memset (&issue, 230 0, 231 sizeof (issue)); 232 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 233 TALER_EXCHANGEDB_get_denomination_by_serial (pg, 234 serial, 235 &issue), 236 TDB_denom_free (&denom)); 237 FAILIF_C (0 != GNUNET_memcmp (&issue, 238 &denom.issue), 239 TDB_denom_free (&denom)); 240 241 memset (&meta, 242 0, 243 sizeof (meta)); 244 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 245 TALER_EXCHANGEDB_get_denomination_meta (pg, 246 &denom.h_denom_pub, 247 &meta), 248 TDB_denom_free (&denom)); 249 FAILIF_C (meta.serial != serial, 250 TDB_denom_free (&denom)); 251 FAILIF_C (0 != TALER_amount_cmp (&meta.value, 252 &denom.issue.value), 253 TDB_denom_free (&denom)); 254 FAILIF_C (0 != TALER_amount_cmp (&meta.fees.refund, 255 &denom.issue.fees.refund), 256 TDB_denom_free (&denom)); 257 FAILIF_C (GNUNET_TIME_timestamp_cmp (meta.expire_legal, 258 !=, 259 denom.issue.expire_legal), 260 TDB_denom_free (&denom)); 261 FAILIF_C (meta.age_mask.bits != denom.issue.age_mask.bits, 262 TDB_denom_free (&denom)); 263 264 /* ...and a denomination that was never inserted is still not found. */ 265 TDB_FILL (other, 266 99); 267 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 268 TALER_EXCHANGEDB_get_denomination_info (pg, 269 &other, 270 &serial, 271 &issue), 272 TDB_denom_free (&denom)); 273 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 274 TALER_EXCHANGEDB_get_denomination_meta (pg, 275 &other, 276 &meta), 277 TDB_denom_free (&denom)); 278 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 279 TALER_EXCHANGEDB_get_denomination_by_serial (pg, 280 denom.serial + 1000, 281 &issue), 282 TDB_denom_free (&denom)); 283 TDB_denom_free (&denom); 284 return 0; 285 } 286 287 288 /** 289 * Both iterators must report every denomination in the table. 290 * 291 * @param pg the database context 292 * @return 0 on success 293 */ 294 static int 295 check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg) 296 { 297 struct TDB_Denom d1; 298 struct TDB_Denom d2; 299 struct CountContext ctx; 300 unsigned int seen; 301 302 TDB_denom (pg, 303 20, 304 "1", 305 "0.01", 306 &d1); 307 TDB_denom (pg, 308 21, 309 "2", 310 "0.02", 311 &d2); 312 memset (&ctx, 313 0, 314 sizeof (ctx)); 315 ctx.denom = &d2; 316 FAILIF_C (0 >= 317 TALER_EXCHANGEDB_iterate_denomination_info (pg, 318 &count_info_cb, 319 &ctx), 320 TDB_denom_free (&d1); TDB_denom_free (&d2)); 321 FAILIF_C (1 != ctx.matched, 322 TDB_denom_free (&d1); TDB_denom_free (&d2)); 323 FAILIF_C (ctx.mismatch, 324 TDB_denom_free (&d1); TDB_denom_free (&d2)); 325 FAILIF_C (ctx.serial != d2.serial, 326 TDB_denom_free (&d1); TDB_denom_free (&d2)); 327 seen = ctx.total; 328 FAILIF_C (seen < 2, 329 TDB_denom_free (&d1); TDB_denom_free (&d2)); 330 331 memset (&ctx, 332 0, 333 sizeof (ctx)); 334 ctx.denom = &d1; 335 FAILIF_C (0 >= 336 TALER_EXCHANGEDB_iterate_denominations (pg, 337 &count_denoms_cb, 338 &ctx), 339 TDB_denom_free (&d1); TDB_denom_free (&d2)); 340 FAILIF_C (1 != ctx.matched, 341 TDB_denom_free (&d1); TDB_denom_free (&d2)); 342 FAILIF_C (ctx.mismatch, 343 TDB_denom_free (&d1); TDB_denom_free (&d2)); 344 FAILIF_C (ctx.serial != d1.serial, 345 TDB_denom_free (&d1); TDB_denom_free (&d2)); 346 /* nothing was revoked in this binary */ 347 FAILIF_C (ctx.recoup_possible, 348 TDB_denom_free (&d1); TDB_denom_free (&d2)); 349 /* both iterators walk the same table, so they must agree on its size */ 350 FAILIF_C (ctx.total != seen, 351 TDB_denom_free (&d1); TDB_denom_free (&d2)); 352 TDB_denom_free (&d1); 353 TDB_denom_free (&d2); 354 return 0; 355 } 356 357 358 /** 359 * The checks to run, in order. 360 */ 361 static const struct TDB_Test tests[] = { 362 { "denominations-empty", 363 &check_empty }, 364 { "denominations-insert-and-lookup", 365 &check_insert_and_lookup }, 366 { "denominations-iterate", 367 &check_iterate }, 368 { NULL, NULL } 369 }; 370 371 372 int 373 main (int argc, 374 char *const *argv) 375 { 376 return TDB_main (argc, 377 argv, 378 "test-denominations", 379 "Tests for the exchangedb `denominations' table", 380 tests); 381 } 382 383 384 /* end of test_denominations.c */