test_aggregation_deferrals.c (8756B)
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_aggregation_deferrals.c 18 * @brief tests for the exchangedb functions whose primary table is 19 * `aggregation_deferrals` 20 * @author Christian Grothoff 21 * 22 * Covers #TALER_EXCHANGEDB_insert_aggregation_deferral() and 23 * #TALER_EXCHANGEDB_get_aggregation_deferral_by_wtid(). 24 * 25 * The table is the exchange's append-only account of why it did not make a 26 * wire transfer it had already decided on. Each row hangs off the deposit 27 * of the aggregate that is last to reach its wire deadline, so the insert 28 * finds nothing to attach to until `aggregation_tracking` has been written 29 * -- which is the first thing the checks pin down. The lookup returns the 30 * most recent claim, so the checks defer twice. 31 */ 32 #include "test_common.h" 33 #include "exchange-database/do_aggregate.h" 34 #include "exchange-database/get_aggregation_deferral_by_wtid.h" 35 #include "exchange-database/insert_aggregation_deferral.h" 36 37 38 /** 39 * Account the merchants of the checks are paid at. 40 */ 41 static struct TDB_Account account; 42 43 44 /** 45 * Denomination the checks deposit. 46 */ 47 static struct TDB_Denom denom; 48 49 50 /** 51 * The wire transfer the checks aggregate into. 52 */ 53 static struct TALER_WireTransferIdentifierRawP wtid; 54 55 56 /** 57 * Build a timestamp from a number of seconds since the epoch. 58 * 59 * @param secs seconds since the epoch 60 * @return the timestamp 61 */ 62 static struct GNUNET_TIME_Timestamp 63 ts (uint64_t secs) 64 { 65 struct GNUNET_TIME_Absolute abs = { 66 .abs_value_us = secs * 1000LLU * 1000LLU 67 }; 68 69 return GNUNET_TIME_absolute_to_timestamp (abs); 70 } 71 72 73 /** 74 * Without an aggregation to hang off, nothing is recorded and nothing is 75 * found. 76 * 77 * @param pg the database context 78 * @return 0 on success 79 */ 80 static int 81 check_no_aggregation (struct TALER_EXCHANGEDB_PostgresContext *pg) 82 { 83 struct TALER_Amount amount = TDB_amount ("1"); 84 struct TALER_Amount got; 85 struct GNUNET_TIME_Timestamp deferral_time; 86 enum TALER_EXCHANGEDB_DeferralReason reason; 87 uint64_t requirement_row; 88 89 TDB_denom (pg, 90 10, 91 "5", 92 "0.1", 93 &denom); 94 TDB_account (pg, 95 10, 96 &account); 97 TDB_FILL (wtid, 98 10); 99 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 100 TALER_EXCHANGEDB_get_aggregation_deferral_by_wtid (pg, 101 &wtid, 102 &got, 103 &reason, 104 &requirement_row, 105 &deferral_time)); 106 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 107 TALER_EXCHANGEDB_insert_aggregation_deferral ( 108 pg, 109 &wtid, 110 &account.h_full, 111 &amount, 112 TALER_EXCHANGEDB_DR_AMOUNT_TOO_SMALL, 113 0, 114 ts (1600000000))); 115 FAILIF (0 != TDB_count (pg, 116 "FROM aggregation_deferrals")); 117 return 0; 118 } 119 120 121 /** 122 * With an aggregation on file, the deferral is recorded and read back. 123 * 124 * @param pg the database context 125 * @return 0 on success 126 */ 127 static int 128 check_defer (struct TALER_EXCHANGEDB_PostgresContext *pg) 129 { 130 struct TALER_CoinPublicInfo coin; 131 struct TDB_Deposit dep; 132 struct TALER_Amount total; 133 struct TALER_Amount amount = TDB_amount ("0.9"); 134 struct TALER_Amount got; 135 struct GNUNET_TIME_Timestamp deferral_time; 136 enum TALER_EXCHANGEDB_DeferralReason reason; 137 uint64_t requirement_row = 42; 138 139 TDB_coin (pg, 140 &denom, 141 20, 142 &coin, 143 NULL); 144 TDB_deposit (pg, 145 &account, 146 &coin, 147 20, 148 "1", 149 "0.1", 150 ts (1600000000), 151 ts (1600000000), 152 &dep); 153 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 154 TALER_EXCHANGEDB_do_aggregate (pg, 155 &account.h_full, 156 &dep.merchant_pub, 157 &wtid, 158 &total), 159 TDB_coin_free (&coin)); 160 TDB_coin_free (&coin); 161 162 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 163 TALER_EXCHANGEDB_insert_aggregation_deferral ( 164 pg, 165 &wtid, 166 &account.h_full, 167 &amount, 168 TALER_EXCHANGEDB_DR_AMOUNT_TOO_SMALL, 169 0, 170 ts (1600000000))); 171 FAILIF (1 != TDB_count (pg, 172 "FROM aggregation_deferrals")); 173 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 174 TALER_EXCHANGEDB_get_aggregation_deferral_by_wtid (pg, 175 &wtid, 176 &got, 177 &reason, 178 &requirement_row, 179 &deferral_time)); 180 FAILIF (0 != TALER_amount_cmp (&got, 181 &amount)); 182 FAILIF (TALER_EXCHANGEDB_DR_AMOUNT_TOO_SMALL != reason); 183 FAILIF (0 != requirement_row); 184 FAILIF (GNUNET_TIME_timestamp_cmp (deferral_time, 185 !=, 186 ts (1600000000))); 187 188 /* a wire transfer nobody deferred is still unknown */ 189 { 190 struct TALER_WireTransferIdentifierRawP other; 191 192 TDB_FILL (other, 193 99); 194 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 195 TALER_EXCHANGEDB_get_aggregation_deferral_by_wtid ( 196 pg, 197 &other, 198 &got, 199 &reason, 200 &requirement_row, 201 &deferral_time)); 202 } 203 return 0; 204 } 205 206 207 /** 208 * The table is append-only: a second deferral is a second row, and the 209 * lookup returns the newer one. 210 * 211 * @param pg the database context 212 * @return 0 on success 213 */ 214 static int 215 check_append_only (struct TALER_EXCHANGEDB_PostgresContext *pg) 216 { 217 struct TALER_Amount amount = TDB_amount ("0.9"); 218 struct TALER_Amount got; 219 struct GNUNET_TIME_Timestamp deferral_time; 220 enum TALER_EXCHANGEDB_DeferralReason reason; 221 uint64_t requirement_row = 0; 222 223 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 224 TALER_EXCHANGEDB_insert_aggregation_deferral ( 225 pg, 226 &wtid, 227 &account.h_full, 228 &amount, 229 TALER_EXCHANGEDB_DR_KYC, 230 7, 231 ts (1600003600))); 232 FAILIF (2 != TDB_count (pg, 233 "FROM aggregation_deferrals")); 234 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 235 TALER_EXCHANGEDB_get_aggregation_deferral_by_wtid (pg, 236 &wtid, 237 &got, 238 &reason, 239 &requirement_row, 240 &deferral_time)); 241 FAILIF (TALER_EXCHANGEDB_DR_KYC != reason); 242 FAILIF (7 != requirement_row); 243 FAILIF (GNUNET_TIME_timestamp_cmp (deferral_time, 244 !=, 245 ts (1600003600))); 246 return 0; 247 } 248 249 250 /** 251 * The checks to run, in order. 252 */ 253 static const struct TDB_Test tests[] = { 254 { "aggregation-deferrals-no-aggregation", 255 &check_no_aggregation }, 256 { "aggregation-deferrals-defer", 257 &check_defer }, 258 { "aggregation-deferrals-append-only", 259 &check_append_only }, 260 { NULL, NULL } 261 }; 262 263 264 int 265 main (int argc, 266 char *const *argv) 267 { 268 int ret; 269 270 ret = TDB_main (argc, 271 argv, 272 "test-aggregation-deferrals", 273 "Tests for the exchangedb `aggregation_deferrals' table", 274 tests); 275 TDB_account_free (&account); 276 TDB_denom_free (&denom); 277 return ret; 278 } 279 280 281 /* end of test_aggregation_deferrals.c */