test_prewire.c (9460B)
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_prewire.c 18 * @brief tests for the exchangedb functions whose primary table is `prewire` 19 * @author Christian Grothoff 20 * 21 * Covers #TALER_EXCHANGEDB_insert_prewire(), 22 * #TALER_EXCHANGEDB_iterate_prewires(), 23 * #TALER_EXCHANGEDB_update_to_prewire_finished() and 24 * #TALER_EXCHANGEDB_update_to_prewire_failed(). 25 * 26 * `prewire` has no foreign keys. The table is a work queue: the iterator 27 * only reports rows that are neither finished nor failed, which is what the 28 * checks below are about. 29 */ 30 #include "test_common.h" 31 #include "exchange-database/insert_prewire.h" 32 #include "exchange-database/iterate_prewires.h" 33 #include "exchange-database/update_to_prewire_finished.h" 34 #include "exchange-database/update_to_prewire_failed.h" 35 36 37 /** 38 * Closure for #prewire_cb(). 39 */ 40 struct PrewireContext 41 { 42 /** 43 * How many rows did the callback see? 44 */ 45 unsigned int total; 46 47 /** 48 * Row of the first entry seen. 49 */ 50 uint64_t first_row; 51 52 /** 53 * Payload of the first entry seen, owned by this struct. 54 */ 55 char *first_buf; 56 57 /** 58 * Number of bytes in @e first_buf. 59 */ 60 size_t first_buf_size; 61 62 /** 63 * Wire method of the first entry seen, owned by this struct. 64 */ 65 char *first_method; 66 }; 67 68 69 /** 70 * Callback for #TALER_EXCHANGEDB_iterate_prewires(). 71 * 72 * @param cls a `struct PrewireContext *` 73 * @param rowid row of the entry 74 * @param wire_method wire method of the entry 75 * @param buf payload of the entry 76 * @param buf_size number of bytes in @a buf 77 */ 78 static void 79 prewire_cb (void *cls, 80 uint64_t rowid, 81 const char *wire_method, 82 const char *buf, 83 size_t buf_size) 84 { 85 struct PrewireContext *ctx = cls; 86 87 if (0 == ctx->total++) 88 { 89 ctx->first_row = rowid; 90 ctx->first_buf = GNUNET_memdup (buf, 91 buf_size); 92 ctx->first_buf_size = buf_size; 93 ctx->first_method = GNUNET_strdup (wire_method); 94 } 95 } 96 97 98 /** 99 * Release what #prewire_cb() allocated. 100 * 101 * @param[in,out] ctx context to clean up 102 */ 103 static void 104 ctx_free (struct PrewireContext *ctx) 105 { 106 GNUNET_free (ctx->first_buf); 107 GNUNET_free (ctx->first_method); 108 } 109 110 111 /** 112 * An empty queue yields nothing, and marking rows that do not exist does 113 * nothing. 114 * 115 * @param pg the database context 116 * @return 0 on success 117 */ 118 static int 119 check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg) 120 { 121 struct PrewireContext ctx = { 0 }; 122 123 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 124 TALER_EXCHANGEDB_iterate_prewires (pg, 125 0, 126 10, 127 &prewire_cb, 128 &ctx)); 129 FAILIF (0 != ctx.total); 130 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 131 TALER_EXCHANGEDB_update_to_prewire_finished (pg, 132 1)); 133 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 134 TALER_EXCHANGEDB_update_to_prewire_failed (pg, 135 1)); 136 return 0; 137 } 138 139 140 /** 141 * Inserted rows come back in order, with their payload intact. 142 * 143 * @param pg the database context 144 * @return 0 on success 145 */ 146 static int 147 check_insert_and_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg) 148 { 149 static const char buf1[] = "first-transfer"; 150 static const char buf2[] = "second-transfer"; 151 struct PrewireContext ctx; 152 uint64_t row1; 153 154 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 155 TALER_EXCHANGEDB_insert_prewire (pg, 156 "x-taler-bank", 157 buf1, 158 sizeof (buf1))); 159 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 160 TALER_EXCHANGEDB_insert_prewire (pg, 161 "iban", 162 buf2, 163 sizeof (buf2))); 164 memset (&ctx, 165 0, 166 sizeof (ctx)); 167 FAILIF (0 >= 168 TALER_EXCHANGEDB_iterate_prewires (pg, 169 0, 170 10, 171 &prewire_cb, 172 &ctx)); 173 FAILIF_C (2 != ctx.total, 174 ctx_free (&ctx)); 175 FAILIF_C (sizeof (buf1) != ctx.first_buf_size, 176 ctx_free (&ctx)); 177 FAILIF_C (0 != memcmp (ctx.first_buf, 178 buf1, 179 sizeof (buf1)), 180 ctx_free (&ctx)); 181 FAILIF_C (0 != strcmp (ctx.first_method, 182 "x-taler-bank"), 183 ctx_free (&ctx)); 184 row1 = ctx.first_row; 185 ctx_free (&ctx); 186 187 /* the limit is honoured */ 188 memset (&ctx, 189 0, 190 sizeof (ctx)); 191 FAILIF (0 >= 192 TALER_EXCHANGEDB_iterate_prewires (pg, 193 0, 194 1, 195 &prewire_cb, 196 &ctx)); 197 FAILIF_C (1 != ctx.total, 198 ctx_free (&ctx)); 199 ctx_free (&ctx); 200 201 /* ...and so is the starting row */ 202 memset (&ctx, 203 0, 204 sizeof (ctx)); 205 FAILIF (0 >= 206 TALER_EXCHANGEDB_iterate_prewires (pg, 207 row1 + 1, 208 10, 209 &prewire_cb, 210 &ctx)); 211 FAILIF_C (1 != ctx.total, 212 ctx_free (&ctx)); 213 FAILIF_C (0 != strcmp (ctx.first_method, 214 "iban"), 215 ctx_free (&ctx)); 216 ctx_free (&ctx); 217 return 0; 218 } 219 220 221 /** 222 * Finished and failed rows drop out of the queue. 223 * 224 * @param pg the database context 225 * @return 0 on success 226 */ 227 static int 228 check_finished_and_failed (struct TALER_EXCHANGEDB_PostgresContext *pg) 229 { 230 struct PrewireContext ctx; 231 uint64_t row1; 232 uint64_t row2; 233 234 memset (&ctx, 235 0, 236 sizeof (ctx)); 237 FAILIF (0 >= 238 TALER_EXCHANGEDB_iterate_prewires (pg, 239 0, 240 10, 241 &prewire_cb, 242 &ctx)); 243 FAILIF_C (2 != ctx.total, 244 ctx_free (&ctx)); 245 row1 = ctx.first_row; 246 ctx_free (&ctx); 247 248 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 249 TALER_EXCHANGEDB_update_to_prewire_finished (pg, 250 row1)); 251 memset (&ctx, 252 0, 253 sizeof (ctx)); 254 FAILIF (0 >= 255 TALER_EXCHANGEDB_iterate_prewires (pg, 256 0, 257 10, 258 &prewire_cb, 259 &ctx)); 260 FAILIF_C (1 != ctx.total, 261 ctx_free (&ctx)); 262 row2 = ctx.first_row; 263 ctx_free (&ctx); 264 FAILIF (row2 == row1); 265 266 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 267 TALER_EXCHANGEDB_update_to_prewire_failed (pg, 268 row2)); 269 memset (&ctx, 270 0, 271 sizeof (ctx)); 272 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 273 TALER_EXCHANGEDB_iterate_prewires (pg, 274 0, 275 10, 276 &prewire_cb, 277 &ctx)); 278 FAILIF_C (0 != ctx.total, 279 ctx_free (&ctx)); 280 ctx_free (&ctx); 281 282 /* both rows are still on file, one finished and one failed */ 283 FAILIF (2 != TDB_count (pg, 284 "FROM prewire")); 285 FAILIF (1 != TDB_count (pg, 286 "FROM prewire WHERE finished")); 287 FAILIF (1 != TDB_count (pg, 288 "FROM prewire WHERE failed")); 289 /* marking again is still reported as a row touched */ 290 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 291 TALER_EXCHANGEDB_update_to_prewire_finished (pg, 292 row1)); 293 return 0; 294 } 295 296 297 /** 298 * The checks to run, in order. 299 */ 300 static const struct TDB_Test tests[] = { 301 { "prewire-empty", 302 &check_empty }, 303 { "prewire-insert-and-iterate", 304 &check_insert_and_iterate }, 305 { "prewire-finished-and-failed", 306 &check_finished_and_failed }, 307 { NULL, NULL } 308 }; 309 310 311 int 312 main (int argc, 313 char *const *argv) 314 { 315 return TDB_main (argc, 316 argv, 317 "test-prewire", 318 "Tests for the exchangedb `prewire' table", 319 tests); 320 } 321 322 323 /* end of test_prewire.c */