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_transient.c
18 : * @brief tests for the exchangedb functions whose primary table is
19 : * `aggregation_transient`
20 : * @author Christian Grothoff
21 : *
22 : * Covers #TALER_EXCHANGEDB_insert_aggregation_transient(),
23 : * #TALER_EXCHANGEDB_update_aggregation_transient(),
24 : * #TALER_EXCHANGEDB_delete_aggregation_transient(),
25 : * #TALER_EXCHANGEDB_get_aggregation_transient(),
26 : * #TALER_EXCHANGEDB_get_aggregation_transient_by_wtid() and
27 : * #TALER_EXCHANGEDB_get_aggregation_transient_by_normalized_payto().
28 : *
29 : * The table holds what the aggregator decided to hold back for later; it
30 : * is updated in place and deleted on payout, so it has no serial ID. It
31 : * references `wire_targets`, which TDB_account() creates. The three
32 : * lookups reach the row by three different keys, which is what the checks
33 : * exercise.
34 : */
35 : #include "test_common.h"
36 : #include "exchange-database/delete_aggregation_transient.h"
37 : #include "exchange-database/get_aggregation_transient.h"
38 : #include "exchange-database/get_aggregation_transient_by_normalized_payto.h"
39 : #include "exchange-database/get_aggregation_transient_by_wtid.h"
40 : #include "exchange-database/insert_aggregation_transient.h"
41 : #include "exchange-database/update_aggregation_transient.h"
42 :
43 :
44 : /**
45 : * Exchange bank account the checks aggregate for.
46 : */
47 : #define SECTION "exchange-account-1"
48 :
49 :
50 : /**
51 : * Account the merchants of the checks are paid at.
52 : */
53 : static struct TDB_Account account;
54 :
55 :
56 : /**
57 : * Nothing is found while the table is empty, and updating or deleting a
58 : * row that is not there does nothing.
59 : *
60 : * @param pg the database context
61 : * @return 0 on success
62 : */
63 : static int
64 1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
65 : {
66 : struct TALER_MerchantPublicKeyP merchant_pub;
67 : struct TALER_WireTransferIdentifierRawP wtid;
68 : struct TALER_Amount total;
69 1 : struct TALER_Amount one = TDB_amount ("1");
70 1 : struct TALER_FullPayto payto_uri = { NULL };
71 : uint64_t requirement_row;
72 :
73 1 : TDB_account (pg,
74 : 10,
75 : &account);
76 1 : TDB_FILL (merchant_pub,
77 : 1);
78 1 : TDB_FILL (wtid,
79 : 1);
80 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
81 : TALER_EXCHANGEDB_get_aggregation_transient (pg,
82 : &account.h_full,
83 : &merchant_pub,
84 : SECTION,
85 : &wtid,
86 : &requirement_row,
87 : &total));
88 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
89 : TALER_EXCHANGEDB_get_aggregation_transient_by_wtid (
90 : pg,
91 : &account.h_full,
92 : &wtid,
93 : &total,
94 : &requirement_row));
95 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
96 : TALER_EXCHANGEDB_get_aggregation_transient_by_normalized_payto (
97 : pg,
98 : &account.h_normalized,
99 : &payto_uri,
100 : &wtid,
101 : &merchant_pub,
102 : &total));
103 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
104 : TALER_EXCHANGEDB_update_aggregation_transient (pg,
105 : &account.h_full,
106 : &wtid,
107 : 0,
108 : &one));
109 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
110 : TALER_EXCHANGEDB_delete_aggregation_transient (pg,
111 : &account.h_full,
112 : &wtid));
113 1 : FAILIF (0 != TDB_count (pg,
114 : "FROM aggregation_transient"));
115 1 : return 0;
116 : }
117 :
118 :
119 : /**
120 : * An inserted row is found by all three lookups.
121 : *
122 : * @param pg the database context
123 : * @return 0 on success
124 : */
125 : static int
126 1 : check_insert_and_lookup (struct TALER_EXCHANGEDB_PostgresContext *pg)
127 : {
128 : struct TALER_MerchantPublicKeyP merchant_pub;
129 : struct TALER_MerchantPublicKeyP got_merchant;
130 : struct TALER_WireTransferIdentifierRawP wtid;
131 : struct TALER_WireTransferIdentifierRawP got_wtid;
132 1 : struct TALER_Amount total = TDB_amount ("3");
133 : struct TALER_Amount got;
134 1 : struct TALER_FullPayto payto_uri = { NULL };
135 1 : uint64_t requirement_row = 42;
136 :
137 1 : TDB_FILL (merchant_pub,
138 : 10);
139 1 : TDB_FILL (wtid,
140 : 10);
141 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
142 : TALER_EXCHANGEDB_insert_aggregation_transient (pg,
143 : &account.h_full,
144 : SECTION,
145 : &merchant_pub,
146 : &wtid,
147 : 0,
148 : &total));
149 1 : FAILIF (1 != TDB_count (pg,
150 : "FROM aggregation_transient"));
151 :
152 : /* by (account, merchant, exchange account) */
153 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
154 : TALER_EXCHANGEDB_get_aggregation_transient (pg,
155 : &account.h_full,
156 : &merchant_pub,
157 : SECTION,
158 : &got_wtid,
159 : &requirement_row,
160 : &got));
161 1 : FAILIF (0 != GNUNET_memcmp (&got_wtid,
162 : &wtid));
163 1 : FAILIF (0 != TALER_amount_cmp (&got,
164 : &total));
165 : /* no legitimization reason was recorded */
166 1 : FAILIF (0 != requirement_row);
167 :
168 : /* by (account, wtid) */
169 1 : requirement_row = 42;
170 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
171 : TALER_EXCHANGEDB_get_aggregation_transient_by_wtid (
172 : pg,
173 : &account.h_full,
174 : &wtid,
175 : &got,
176 : &requirement_row));
177 1 : FAILIF (0 != TALER_amount_cmp (&got,
178 : &total));
179 : /* no legitimization reason was recorded */
180 1 : FAILIF (0 != requirement_row);
181 :
182 : /* by normalized account */
183 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
184 : TALER_EXCHANGEDB_get_aggregation_transient_by_normalized_payto (
185 : pg,
186 : &account.h_normalized,
187 : &payto_uri,
188 : &got_wtid,
189 : &got_merchant,
190 : &got));
191 1 : FAILIF_C (0 != strcmp (payto_uri.full_payto,
192 : account.payto.full_payto),
193 : GNUNET_free (payto_uri.full_payto));
194 1 : GNUNET_free (payto_uri.full_payto);
195 1 : FAILIF (0 != GNUNET_memcmp (&got_merchant,
196 : &merchant_pub));
197 1 : FAILIF (0 != GNUNET_memcmp (&got_wtid,
198 : &wtid));
199 :
200 : /* a different merchant on the same account has nothing */
201 : {
202 : struct TALER_MerchantPublicKeyP other;
203 :
204 1 : TDB_FILL (other,
205 : 99);
206 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
207 : TALER_EXCHANGEDB_get_aggregation_transient (pg,
208 : &account.h_full,
209 : &other,
210 : SECTION,
211 : &got_wtid,
212 : &requirement_row,
213 : &got));
214 : }
215 : /* and neither does a different exchange bank account */
216 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
217 : TALER_EXCHANGEDB_get_aggregation_transient (pg,
218 : &account.h_full,
219 : &merchant_pub,
220 : "exchange-account-2",
221 : &got_wtid,
222 : &requirement_row,
223 : &got));
224 1 : return 0;
225 : }
226 :
227 :
228 : /**
229 : * Updating a row changes the amount and the legitimization reason.
230 : *
231 : * @param pg the database context
232 : * @return 0 on success
233 : */
234 : static int
235 1 : check_update (struct TALER_EXCHANGEDB_PostgresContext *pg)
236 : {
237 : struct TALER_MerchantPublicKeyP merchant_pub;
238 : struct TALER_WireTransferIdentifierRawP wtid;
239 : struct TALER_WireTransferIdentifierRawP got_wtid;
240 1 : struct TALER_Amount total = TDB_amount ("7");
241 : struct TALER_Amount got;
242 1 : uint64_t requirement_row = 0;
243 :
244 1 : TDB_FILL (merchant_pub,
245 : 10);
246 1 : TDB_FILL (wtid,
247 : 10);
248 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
249 : TALER_EXCHANGEDB_update_aggregation_transient (pg,
250 : &account.h_full,
251 : &wtid,
252 : 5,
253 : &total));
254 1 : FAILIF (1 != TDB_count (pg,
255 : "FROM aggregation_transient"));
256 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
257 : TALER_EXCHANGEDB_get_aggregation_transient (pg,
258 : &account.h_full,
259 : &merchant_pub,
260 : SECTION,
261 : &got_wtid,
262 : &requirement_row,
263 : &got));
264 1 : FAILIF (0 != TALER_amount_cmp (&got,
265 : &total));
266 : /* the legitimization reason is read back, so re-parking keeps it */
267 1 : FAILIF (5 != requirement_row);
268 1 : requirement_row = 0;
269 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
270 : TALER_EXCHANGEDB_get_aggregation_transient_by_wtid (
271 : pg,
272 : &account.h_full,
273 : &wtid,
274 : &got,
275 : &requirement_row));
276 1 : FAILIF (5 != requirement_row);
277 :
278 : /* a wire transfer identifier that is not on file is not created */
279 : {
280 : struct TALER_WireTransferIdentifierRawP other;
281 :
282 1 : TDB_FILL (other,
283 : 98);
284 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
285 : TALER_EXCHANGEDB_update_aggregation_transient (pg,
286 : &account.h_full,
287 : &other,
288 : 0,
289 : &total));
290 1 : FAILIF (1 != TDB_count (pg,
291 : "FROM aggregation_transient"));
292 : }
293 1 : return 0;
294 : }
295 :
296 :
297 : /**
298 : * Deleting a row removes it, and a second delete does nothing.
299 : *
300 : * @param pg the database context
301 : * @return 0 on success
302 : */
303 : static int
304 1 : check_delete (struct TALER_EXCHANGEDB_PostgresContext *pg)
305 : {
306 : struct TALER_MerchantPublicKeyP merchant_pub;
307 : struct TALER_WireTransferIdentifierRawP wtid;
308 : struct TALER_Amount got;
309 : uint64_t requirement_row;
310 :
311 1 : TDB_FILL (merchant_pub,
312 : 10);
313 1 : TDB_FILL (wtid,
314 : 10);
315 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
316 : TALER_EXCHANGEDB_delete_aggregation_transient (pg,
317 : &account.h_full,
318 : &wtid));
319 1 : FAILIF (0 != TDB_count (pg,
320 : "FROM aggregation_transient"));
321 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
322 : TALER_EXCHANGEDB_delete_aggregation_transient (pg,
323 : &account.h_full,
324 : &wtid));
325 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
326 : TALER_EXCHANGEDB_get_aggregation_transient (pg,
327 : &account.h_full,
328 : &merchant_pub,
329 : SECTION,
330 : &wtid,
331 : &requirement_row,
332 : &got));
333 1 : return 0;
334 : }
335 :
336 :
337 : /**
338 : * The checks to run, in order.
339 : */
340 : static const struct TDB_Test tests[] = {
341 : { "aggregation-transient-empty",
342 : &check_empty },
343 : { "aggregation-transient-insert-and-lookup",
344 : &check_insert_and_lookup },
345 : { "aggregation-transient-update",
346 : &check_update },
347 : { "aggregation-transient-delete",
348 : &check_delete },
349 : { NULL, NULL }
350 : };
351 :
352 :
353 : int
354 1 : main (int argc,
355 : char *const *argv)
356 : {
357 : int ret;
358 :
359 1 : ret = TDB_main (argc,
360 : argv,
361 : "test-aggregation-transient",
362 : "Tests for the exchangedb `aggregation_transient' table",
363 : tests);
364 1 : TDB_account_free (&account);
365 1 : return ret;
366 : }
367 :
368 :
369 : /* end of test_aggregation_transient.c */
|