Line data Source code
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 7 : ts (uint64_t secs)
64 : {
65 7 : struct GNUNET_TIME_Absolute abs = {
66 7 : .abs_value_us = secs * 1000LLU * 1000LLU
67 : };
68 :
69 7 : 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 1 : check_no_aggregation (struct TALER_EXCHANGEDB_PostgresContext *pg)
82 : {
83 1 : 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 1 : TDB_denom (pg,
90 : 10,
91 : "5",
92 : "0.1",
93 : &denom);
94 1 : TDB_account (pg,
95 : 10,
96 : &account);
97 1 : TDB_FILL (wtid,
98 : 10);
99 1 : 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 1 : 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 1 : FAILIF (0 != TDB_count (pg,
116 : "FROM aggregation_deferrals"));
117 1 : 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 1 : check_defer (struct TALER_EXCHANGEDB_PostgresContext *pg)
129 : {
130 : struct TALER_CoinPublicInfo coin;
131 : struct TDB_Deposit dep;
132 : struct TALER_Amount total;
133 1 : 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 1 : uint64_t requirement_row = 42;
138 :
139 1 : TDB_coin (pg,
140 : &denom,
141 : 20,
142 : &coin,
143 : NULL);
144 1 : TDB_deposit (pg,
145 : &account,
146 : &coin,
147 : 20,
148 : "1",
149 : "0.1",
150 : ts (1600000000),
151 : ts (1600000000),
152 : &dep);
153 1 : 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 1 : TDB_coin_free (&coin);
161 :
162 1 : 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 1 : FAILIF (1 != TDB_count (pg,
172 : "FROM aggregation_deferrals"));
173 1 : 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 1 : FAILIF (0 != TALER_amount_cmp (&got,
181 : &amount));
182 1 : FAILIF (TALER_EXCHANGEDB_DR_AMOUNT_TOO_SMALL != reason);
183 1 : FAILIF (0 != requirement_row);
184 1 : 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 1 : TDB_FILL (other,
193 : 99);
194 1 : 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 1 : 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 1 : check_append_only (struct TALER_EXCHANGEDB_PostgresContext *pg)
216 : {
217 1 : 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 1 : uint64_t requirement_row = 0;
222 :
223 1 : 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 1 : FAILIF (2 != TDB_count (pg,
233 : "FROM aggregation_deferrals"));
234 1 : 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 1 : FAILIF (TALER_EXCHANGEDB_DR_KYC != reason);
242 1 : FAILIF (7 != requirement_row);
243 1 : FAILIF (GNUNET_TIME_timestamp_cmp (deferral_time,
244 : !=,
245 : ts (1600003600)));
246 1 : 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 1 : main (int argc,
266 : char *const *argv)
267 : {
268 : int ret;
269 :
270 1 : ret = TDB_main (argc,
271 : argv,
272 : "test-aggregation-deferrals",
273 : "Tests for the exchangedb `aggregation_deferrals' table",
274 : tests);
275 1 : TDB_account_free (&account);
276 1 : TDB_denom_free (&denom);
277 1 : return ret;
278 : }
279 :
280 :
281 : /* end of test_aggregation_deferrals.c */
|