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 : &total));
87 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
88 : TALER_EXCHANGEDB_get_aggregation_transient_by_wtid (
89 : pg,
90 : &account.h_full,
91 : &wtid,
92 : &total,
93 : &requirement_row));
94 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
95 : TALER_EXCHANGEDB_get_aggregation_transient_by_normalized_payto (
96 : pg,
97 : &account.h_normalized,
98 : &payto_uri,
99 : &wtid,
100 : &merchant_pub,
101 : &total));
102 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
103 : TALER_EXCHANGEDB_update_aggregation_transient (pg,
104 : &account.h_full,
105 : &wtid,
106 : 0,
107 : &one));
108 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
109 : TALER_EXCHANGEDB_delete_aggregation_transient (pg,
110 : &account.h_full,
111 : &wtid));
112 1 : FAILIF (0 != TDB_count (pg,
113 : "FROM aggregation_transient"));
114 1 : return 0;
115 : }
116 :
117 :
118 : /**
119 : * An inserted row is found by all three lookups.
120 : *
121 : * @param pg the database context
122 : * @return 0 on success
123 : */
124 : static int
125 1 : check_insert_and_lookup (struct TALER_EXCHANGEDB_PostgresContext *pg)
126 : {
127 : struct TALER_MerchantPublicKeyP merchant_pub;
128 : struct TALER_MerchantPublicKeyP got_merchant;
129 : struct TALER_WireTransferIdentifierRawP wtid;
130 : struct TALER_WireTransferIdentifierRawP got_wtid;
131 1 : struct TALER_Amount total = TDB_amount ("3");
132 : struct TALER_Amount got;
133 1 : struct TALER_FullPayto payto_uri = { NULL };
134 1 : uint64_t requirement_row = 42;
135 :
136 1 : TDB_FILL (merchant_pub,
137 : 10);
138 1 : TDB_FILL (wtid,
139 : 10);
140 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
141 : TALER_EXCHANGEDB_insert_aggregation_transient (pg,
142 : &account.h_full,
143 : SECTION,
144 : &merchant_pub,
145 : &wtid,
146 : 0,
147 : &total));
148 1 : FAILIF (1 != TDB_count (pg,
149 : "FROM aggregation_transient"));
150 :
151 : /* by (account, merchant, exchange account) */
152 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
153 : TALER_EXCHANGEDB_get_aggregation_transient (pg,
154 : &account.h_full,
155 : &merchant_pub,
156 : SECTION,
157 : &got_wtid,
158 : &got));
159 1 : FAILIF (0 != GNUNET_memcmp (&got_wtid,
160 : &wtid));
161 1 : FAILIF (0 != TALER_amount_cmp (&got,
162 : &total));
163 :
164 : /* by (account, wtid) */
165 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
166 : TALER_EXCHANGEDB_get_aggregation_transient_by_wtid (
167 : pg,
168 : &account.h_full,
169 : &wtid,
170 : &got,
171 : &requirement_row));
172 1 : FAILIF (0 != TALER_amount_cmp (&got,
173 : &total));
174 : /* no legitimization reason was recorded */
175 1 : FAILIF (0 != requirement_row);
176 :
177 : /* by normalized account */
178 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
179 : TALER_EXCHANGEDB_get_aggregation_transient_by_normalized_payto (
180 : pg,
181 : &account.h_normalized,
182 : &payto_uri,
183 : &got_wtid,
184 : &got_merchant,
185 : &got));
186 1 : FAILIF_C (0 != strcmp (payto_uri.full_payto,
187 : account.payto.full_payto),
188 : GNUNET_free (payto_uri.full_payto));
189 1 : GNUNET_free (payto_uri.full_payto);
190 1 : FAILIF (0 != GNUNET_memcmp (&got_merchant,
191 : &merchant_pub));
192 1 : FAILIF (0 != GNUNET_memcmp (&got_wtid,
193 : &wtid));
194 :
195 : /* a different merchant on the same account has nothing */
196 : {
197 : struct TALER_MerchantPublicKeyP other;
198 :
199 1 : TDB_FILL (other,
200 : 99);
201 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
202 : TALER_EXCHANGEDB_get_aggregation_transient (pg,
203 : &account.h_full,
204 : &other,
205 : SECTION,
206 : &got_wtid,
207 : &got));
208 : }
209 : /* and neither does a different exchange bank account */
210 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
211 : TALER_EXCHANGEDB_get_aggregation_transient (pg,
212 : &account.h_full,
213 : &merchant_pub,
214 : "exchange-account-2",
215 : &got_wtid,
216 : &got));
217 1 : return 0;
218 : }
219 :
220 :
221 : /**
222 : * Updating a row changes the amount and the legitimization reason.
223 : *
224 : * @param pg the database context
225 : * @return 0 on success
226 : */
227 : static int
228 1 : check_update (struct TALER_EXCHANGEDB_PostgresContext *pg)
229 : {
230 : struct TALER_MerchantPublicKeyP merchant_pub;
231 : struct TALER_WireTransferIdentifierRawP wtid;
232 : struct TALER_WireTransferIdentifierRawP got_wtid;
233 1 : struct TALER_Amount total = TDB_amount ("7");
234 : struct TALER_Amount got;
235 1 : uint64_t requirement_row = 0;
236 :
237 1 : TDB_FILL (merchant_pub,
238 : 10);
239 1 : TDB_FILL (wtid,
240 : 10);
241 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
242 : TALER_EXCHANGEDB_update_aggregation_transient (pg,
243 : &account.h_full,
244 : &wtid,
245 : 5,
246 : &total));
247 1 : FAILIF (1 != TDB_count (pg,
248 : "FROM aggregation_transient"));
249 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
250 : TALER_EXCHANGEDB_get_aggregation_transient (pg,
251 : &account.h_full,
252 : &merchant_pub,
253 : SECTION,
254 : &got_wtid,
255 : &got));
256 1 : FAILIF (0 != TALER_amount_cmp (&got,
257 : &total));
258 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
259 : TALER_EXCHANGEDB_get_aggregation_transient_by_wtid (
260 : pg,
261 : &account.h_full,
262 : &wtid,
263 : &got,
264 : &requirement_row));
265 1 : FAILIF (5 != requirement_row);
266 :
267 : /* a wire transfer identifier that is not on file is not created */
268 : {
269 : struct TALER_WireTransferIdentifierRawP other;
270 :
271 1 : TDB_FILL (other,
272 : 98);
273 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
274 : TALER_EXCHANGEDB_update_aggregation_transient (pg,
275 : &account.h_full,
276 : &other,
277 : 0,
278 : &total));
279 1 : FAILIF (1 != TDB_count (pg,
280 : "FROM aggregation_transient"));
281 : }
282 1 : return 0;
283 : }
284 :
285 :
286 : /**
287 : * Deleting a row removes it, and a second delete does nothing.
288 : *
289 : * @param pg the database context
290 : * @return 0 on success
291 : */
292 : static int
293 1 : check_delete (struct TALER_EXCHANGEDB_PostgresContext *pg)
294 : {
295 : struct TALER_MerchantPublicKeyP merchant_pub;
296 : struct TALER_WireTransferIdentifierRawP wtid;
297 : struct TALER_Amount got;
298 :
299 1 : TDB_FILL (merchant_pub,
300 : 10);
301 1 : TDB_FILL (wtid,
302 : 10);
303 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
304 : TALER_EXCHANGEDB_delete_aggregation_transient (pg,
305 : &account.h_full,
306 : &wtid));
307 1 : FAILIF (0 != TDB_count (pg,
308 : "FROM aggregation_transient"));
309 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
310 : TALER_EXCHANGEDB_delete_aggregation_transient (pg,
311 : &account.h_full,
312 : &wtid));
313 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
314 : TALER_EXCHANGEDB_get_aggregation_transient (pg,
315 : &account.h_full,
316 : &merchant_pub,
317 : SECTION,
318 : &wtid,
319 : &got));
320 1 : return 0;
321 : }
322 :
323 :
324 : /**
325 : * The checks to run, in order.
326 : */
327 : static const struct TDB_Test tests[] = {
328 : { "aggregation-transient-empty",
329 : &check_empty },
330 : { "aggregation-transient-insert-and-lookup",
331 : &check_insert_and_lookup },
332 : { "aggregation-transient-update",
333 : &check_update },
334 : { "aggregation-transient-delete",
335 : &check_delete },
336 : { NULL, NULL }
337 : };
338 :
339 :
340 : int
341 1 : main (int argc,
342 : char *const *argv)
343 : {
344 : int ret;
345 :
346 1 : ret = TDB_main (argc,
347 : argv,
348 : "test-aggregation-transient",
349 : "Tests for the exchangedb `aggregation_transient' table",
350 : tests);
351 1 : TDB_account_free (&account);
352 1 : return ret;
353 : }
354 :
355 :
356 : /* end of test_aggregation_transient.c */
|