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_auditors.c
18 : * @brief tests for the exchangedb functions whose primary table is `auditors`
19 : * @author Christian Grothoff
20 : *
21 : * Covers #TALER_EXCHANGEDB_insert_auditor(),
22 : * #TALER_EXCHANGEDB_update_auditor(),
23 : * #TALER_EXCHANGEDB_get_auditor_status(),
24 : * #TALER_EXCHANGEDB_get_auditor_timestamp() and
25 : * #TALER_EXCHANGEDB_iterate_active_auditors().
26 : *
27 : * `auditors` has no foreign keys, so the checks need no fixtures.
28 : */
29 : #include "test_common.h"
30 : #include "exchange-database/insert_auditor.h"
31 : #include "exchange-database/update_auditor.h"
32 : #include "exchange-database/get_auditor_status.h"
33 : #include "exchange-database/get_auditor_timestamp.h"
34 : #include "exchange-database/iterate_active_auditors.h"
35 :
36 :
37 : /**
38 : * Nothing is known about an auditor that was never inserted.
39 : *
40 : * @param pg the database context
41 : * @return 0 on success
42 : */
43 : static int
44 1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
45 : {
46 : struct TALER_AuditorPublicKeyP auditor_pub;
47 : struct GNUNET_TIME_Timestamp last_date;
48 1 : char *url = NULL;
49 1 : bool enabled = true;
50 :
51 1 : TDB_FILL (auditor_pub,
52 : 1);
53 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
54 : TALER_EXCHANGEDB_get_auditor_status (pg,
55 : &auditor_pub,
56 : &url,
57 : &enabled));
58 1 : FAILIF (NULL != url);
59 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
60 : TALER_EXCHANGEDB_get_auditor_timestamp (pg,
61 : &auditor_pub,
62 : &last_date));
63 : /* updating an auditor that is not on file does not create one */
64 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
65 : TALER_EXCHANGEDB_update_auditor (pg,
66 : &auditor_pub,
67 : "https://auditor.example/",
68 : "Nobody",
69 : GNUNET_TIME_timestamp_get (),
70 : true));
71 1 : FAILIF (0 != TDB_count (pg,
72 : "FROM auditors"));
73 1 : return 0;
74 : }
75 :
76 :
77 : /**
78 : * Closure for #count_auditors_cb().
79 : */
80 : struct CountContext
81 : {
82 : /**
83 : * Auditor we are looking for.
84 : */
85 : const struct TALER_AuditorPublicKeyP *auditor_pub;
86 :
87 : /**
88 : * How many auditors did the callback see in total?
89 : */
90 : unsigned int total;
91 :
92 : /**
93 : * How many times did we see @e auditor_pub?
94 : */
95 : unsigned int matched;
96 :
97 : /**
98 : * URL reported for @e auditor_pub, owned by this struct.
99 : */
100 : char *url;
101 :
102 : /**
103 : * Name reported for @e auditor_pub, owned by this struct.
104 : */
105 : char *name;
106 : };
107 :
108 :
109 : /**
110 : * Callback for #TALER_EXCHANGEDB_iterate_active_auditors().
111 : *
112 : * @param cls a `struct CountContext *`
113 : * @param auditor_pub public key of the auditor
114 : * @param auditor_url base URL of the auditor
115 : * @param auditor_name human-readable name of the auditor
116 : */
117 : static void
118 4 : count_auditors_cb (void *cls,
119 : const struct TALER_AuditorPublicKeyP *auditor_pub,
120 : const char *auditor_url,
121 : const char *auditor_name)
122 : {
123 4 : struct CountContext *ctx = cls;
124 :
125 4 : ctx->total++;
126 4 : if (0 != GNUNET_memcmp (auditor_pub,
127 : ctx->auditor_pub))
128 2 : return;
129 2 : ctx->matched++;
130 2 : GNUNET_free (ctx->url);
131 2 : GNUNET_free (ctx->name);
132 2 : ctx->url = GNUNET_strdup (auditor_url);
133 2 : ctx->name = GNUNET_strdup (auditor_name);
134 : }
135 :
136 :
137 : /**
138 : * Insert an auditor, read it back, then disable and re-enable it.
139 : *
140 : * @param pg the database context
141 : * @return 0 on success
142 : */
143 : static int
144 1 : check_insert_update (struct TALER_EXCHANGEDB_PostgresContext *pg)
145 : {
146 : struct TALER_AuditorPublicKeyP auditor_pub;
147 : struct GNUNET_TIME_Timestamp start;
148 : struct GNUNET_TIME_Timestamp later;
149 : struct GNUNET_TIME_Timestamp last_date;
150 : struct CountContext ctx;
151 1 : char *url = NULL;
152 1 : bool enabled = false;
153 :
154 1 : TDB_FILL (auditor_pub,
155 : 2);
156 1 : start = GNUNET_TIME_timestamp_get ();
157 1 : later = GNUNET_TIME_absolute_to_timestamp (
158 : GNUNET_TIME_absolute_add (start.abs_time,
159 : GNUNET_TIME_UNIT_HOURS));
160 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
161 : TALER_EXCHANGEDB_insert_auditor (pg,
162 : &auditor_pub,
163 : "https://auditor.example/",
164 : "The Auditor",
165 : start));
166 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
167 : TALER_EXCHANGEDB_get_auditor_status (pg,
168 : &auditor_pub,
169 : &url,
170 : &enabled));
171 1 : FAILIF (NULL == url);
172 1 : FAILIF_C (0 != strcmp (url,
173 : "https://auditor.example/"),
174 : GNUNET_free (url));
175 1 : GNUNET_free (url);
176 : /* a fresh auditor is active */
177 1 : FAILIF (! enabled);
178 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
179 : TALER_EXCHANGEDB_get_auditor_timestamp (pg,
180 : &auditor_pub,
181 : &last_date));
182 1 : FAILIF (GNUNET_TIME_timestamp_cmp (last_date,
183 : !=,
184 : start));
185 :
186 1 : memset (&ctx,
187 : 0,
188 : sizeof (ctx));
189 1 : ctx.auditor_pub = &auditor_pub;
190 1 : FAILIF (0 >=
191 : TALER_EXCHANGEDB_iterate_active_auditors (pg,
192 : &count_auditors_cb,
193 : &ctx));
194 1 : FAILIF_C (1 != ctx.matched,
195 : GNUNET_free (ctx.url); GNUNET_free (ctx.name));
196 1 : FAILIF_C (0 != strcmp (ctx.name,
197 : "The Auditor"),
198 : GNUNET_free (ctx.url); GNUNET_free (ctx.name));
199 1 : GNUNET_free (ctx.url);
200 1 : GNUNET_free (ctx.name);
201 :
202 : /* disabling drops the auditor from the active iterator */
203 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
204 : TALER_EXCHANGEDB_update_auditor (pg,
205 : &auditor_pub,
206 : "https://auditor2.example/",
207 : "The Other Auditor",
208 : later,
209 : false));
210 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
211 : TALER_EXCHANGEDB_get_auditor_status (pg,
212 : &auditor_pub,
213 : &url,
214 : &enabled));
215 1 : FAILIF_C (0 != strcmp (url,
216 : "https://auditor2.example/"),
217 : GNUNET_free (url));
218 1 : GNUNET_free (url);
219 1 : FAILIF (enabled);
220 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
221 : TALER_EXCHANGEDB_get_auditor_timestamp (pg,
222 : &auditor_pub,
223 : &last_date));
224 1 : FAILIF (GNUNET_TIME_timestamp_cmp (last_date,
225 : !=,
226 : later));
227 1 : memset (&ctx,
228 : 0,
229 : sizeof (ctx));
230 1 : ctx.auditor_pub = &auditor_pub;
231 1 : FAILIF (0 >
232 : TALER_EXCHANGEDB_iterate_active_auditors (pg,
233 : &count_auditors_cb,
234 : &ctx));
235 1 : FAILIF (0 != ctx.matched);
236 :
237 : /* ...and re-enabling brings it back */
238 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
239 : TALER_EXCHANGEDB_update_auditor (pg,
240 : &auditor_pub,
241 : "https://auditor2.example/",
242 : "The Other Auditor",
243 : later,
244 : true));
245 1 : memset (&ctx,
246 : 0,
247 : sizeof (ctx));
248 1 : ctx.auditor_pub = &auditor_pub;
249 1 : FAILIF (0 >=
250 : TALER_EXCHANGEDB_iterate_active_auditors (pg,
251 : &count_auditors_cb,
252 : &ctx));
253 1 : FAILIF_C (1 != ctx.matched,
254 : GNUNET_free (ctx.url); GNUNET_free (ctx.name));
255 1 : GNUNET_free (ctx.url);
256 1 : GNUNET_free (ctx.name);
257 1 : return 0;
258 : }
259 :
260 :
261 : /**
262 : * A second auditor is independent of the first, and only the active ones
263 : * are iterated.
264 : *
265 : * @param pg the database context
266 : * @return 0 on success
267 : */
268 : static int
269 1 : check_two_auditors (struct TALER_EXCHANGEDB_PostgresContext *pg)
270 : {
271 : struct TALER_AuditorPublicKeyP a2;
272 : struct TALER_AuditorPublicKeyP a3;
273 1 : struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
274 : struct CountContext ctx;
275 :
276 1 : TDB_FILL (a2,
277 : 3);
278 1 : TDB_FILL (a3,
279 : 4);
280 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
281 : TALER_EXCHANGEDB_insert_auditor (pg,
282 : &a2,
283 : "https://a2.example/",
284 : "A2",
285 : now));
286 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
287 : TALER_EXCHANGEDB_insert_auditor (pg,
288 : &a3,
289 : "https://a3.example/",
290 : "A3",
291 : now));
292 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
293 : TALER_EXCHANGEDB_update_auditor (pg,
294 : &a3,
295 : "https://a3.example/",
296 : "A3",
297 : now,
298 : false));
299 1 : memset (&ctx,
300 : 0,
301 : sizeof (ctx));
302 1 : ctx.auditor_pub = &a3;
303 1 : FAILIF (0 >=
304 : TALER_EXCHANGEDB_iterate_active_auditors (pg,
305 : &count_auditors_cb,
306 : &ctx));
307 1 : FAILIF (0 != ctx.matched);
308 1 : FAILIF (ctx.total !=
309 : TDB_count (pg,
310 : "FROM auditors WHERE is_active"));
311 1 : FAILIF (ctx.total ==
312 : TDB_count (pg,
313 : "FROM auditors"));
314 1 : return 0;
315 : }
316 :
317 :
318 : /**
319 : * The checks to run, in order.
320 : */
321 : static const struct TDB_Test tests[] = {
322 : { "auditors-empty",
323 : &check_empty },
324 : { "auditors-insert-update",
325 : &check_insert_update },
326 : { "auditors-two-auditors",
327 : &check_two_auditors },
328 : { NULL, NULL }
329 : };
330 :
331 :
332 : int
333 1 : main (int argc,
334 : char *const *argv)
335 : {
336 1 : return TDB_main (argc,
337 : argv,
338 : "test-auditors",
339 : "Tests for the exchangedb `auditors' table",
340 : tests);
341 : }
342 :
343 :
344 : /* end of test_auditors.c */
|