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_revolving_work_shards.c
18 : * @brief tests for the exchangedb functions whose primary table is
19 : * `revolving_work_shards`
20 : * @author Christian Grothoff
21 : *
22 : * Covers #TALER_EXCHANGEDB_begin_revolving_shard(),
23 : * #TALER_EXCHANGEDB_release_revolving_shard() and, for its
24 : * `revolving_work_shards` half, #TALER_EXCHANGEDB_delete_shard_locks().
25 : *
26 : * Unlike `work_shards`, this table is finite: once the configured shard
27 : * limit is covered, the same shards are handed out over and over, which is
28 : * what the checks below pin down. No foreign keys, so no fixtures.
29 : */
30 : #include "test_common.h"
31 : #include "exchange-database/begin_revolving_shard.h"
32 : #include "exchange-database/delete_shard_locks.h"
33 : #include "exchange-database/release_revolving_shard.h"
34 :
35 :
36 : /**
37 : * Releasing a shard that was never claimed does nothing.
38 : *
39 : * @param pg the database context
40 : * @return 0 on success
41 : */
42 : static int
43 1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
44 : {
45 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
46 : TALER_EXCHANGEDB_release_revolving_shard (pg,
47 : "rws-nothing",
48 : 0,
49 : 9));
50 1 : FAILIF (0 != TDB_count (pg,
51 : "FROM revolving_work_shards"));
52 1 : return 0;
53 : }
54 :
55 :
56 : /**
57 : * Fresh shards tile the range [0,shard_limit) and are handed out active.
58 : *
59 : * @param pg the database context
60 : * @return 0 on success
61 : */
62 : static int
63 1 : check_claim_fresh (struct TALER_EXCHANGEDB_PostgresContext *pg)
64 : {
65 : uint32_t start;
66 : uint32_t end;
67 :
68 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
69 : TALER_EXCHANGEDB_begin_revolving_shard (pg,
70 : "rws-claim",
71 : 10,
72 : 25,
73 : &start,
74 : &end));
75 1 : FAILIF (0 != start);
76 1 : FAILIF (9 != end);
77 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
78 : TALER_EXCHANGEDB_begin_revolving_shard (pg,
79 : "rws-claim",
80 : 10,
81 : 25,
82 : &start,
83 : &end));
84 1 : FAILIF (10 != start);
85 1 : FAILIF (19 != end);
86 : /* the last shard is clipped to the limit */
87 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
88 : TALER_EXCHANGEDB_begin_revolving_shard (pg,
89 : "rws-claim",
90 : 10,
91 : 25,
92 : &start,
93 : &end));
94 1 : FAILIF (20 != start);
95 1 : FAILIF (25 != end);
96 1 : FAILIF (3 != TDB_count (pg,
97 : "FROM revolving_work_shards"
98 : " WHERE job_name='rws-claim'"
99 : " AND active"));
100 1 : return 0;
101 : }
102 :
103 :
104 : /**
105 : * Once the range is covered, no further shard can be claimed until one is
106 : * released, and then it is that one that comes back.
107 : *
108 : * @param pg the database context
109 : * @return 0 on success
110 : */
111 : static int
112 1 : check_exhausted_and_release (struct TALER_EXCHANGEDB_PostgresContext *pg)
113 : {
114 : uint32_t start;
115 : uint32_t end;
116 :
117 : /* everything from check_claim_fresh() is still active */
118 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
119 : TALER_EXCHANGEDB_begin_revolving_shard (pg,
120 : "rws-claim",
121 : 10,
122 : 25,
123 : &start,
124 : &end));
125 :
126 : /* releasing a range that was never claimed changes nothing */
127 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
128 : TALER_EXCHANGEDB_release_revolving_shard (pg,
129 : "rws-claim",
130 : 0,
131 : 8));
132 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
133 : TALER_EXCHANGEDB_release_revolving_shard (pg,
134 : "rws-claim-elsewhere",
135 : 10,
136 : 19));
137 :
138 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
139 : TALER_EXCHANGEDB_release_revolving_shard (pg,
140 : "rws-claim",
141 : 10,
142 : 19));
143 1 : FAILIF (1 != TDB_count (pg,
144 : "FROM revolving_work_shards"
145 : " WHERE job_name='rws-claim'"
146 : " AND NOT active"));
147 : /* the released shard is the only one available, so it comes back */
148 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
149 : TALER_EXCHANGEDB_begin_revolving_shard (pg,
150 : "rws-claim",
151 : 10,
152 : 25,
153 : &start,
154 : &end));
155 1 : FAILIF (10 != start);
156 1 : FAILIF (19 != end);
157 1 : FAILIF (3 != TDB_count (pg,
158 : "FROM revolving_work_shards"
159 : " WHERE job_name='rws-claim'"
160 : " AND active"));
161 : /* releasing twice is idempotent from the caller's point of view */
162 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
163 : TALER_EXCHANGEDB_release_revolving_shard (pg,
164 : "rws-claim",
165 : 10,
166 : 19));
167 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
168 : TALER_EXCHANGEDB_release_revolving_shard (pg,
169 : "rws-claim",
170 : 10,
171 : 19));
172 1 : return 0;
173 : }
174 :
175 :
176 : /**
177 : * A shard size at or above the limit yields a single shard covering
178 : * everything.
179 : *
180 : * @param pg the database context
181 : * @return 0 on success
182 : */
183 : static int
184 1 : check_single_shard (struct TALER_EXCHANGEDB_PostgresContext *pg)
185 : {
186 : uint32_t start;
187 : uint32_t end;
188 :
189 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
190 : TALER_EXCHANGEDB_begin_revolving_shard (pg,
191 : "rws-single",
192 : 1000,
193 : 4,
194 : &start,
195 : &end));
196 1 : FAILIF (0 != start);
197 1 : FAILIF (4 != end);
198 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
199 : TALER_EXCHANGEDB_begin_revolving_shard (pg,
200 : "rws-single",
201 : 1000,
202 : 4,
203 : &start,
204 : &end));
205 1 : return 0;
206 : }
207 :
208 :
209 : /**
210 : * Deleting the shard locks empties the table. Runs last.
211 : *
212 : * @param pg the database context
213 : * @return 0 on success
214 : */
215 : static int
216 1 : check_delete_locks (struct TALER_EXCHANGEDB_PostgresContext *pg)
217 : {
218 : uint32_t start;
219 : uint32_t end;
220 :
221 1 : FAILIF (0 == TDB_count (pg,
222 : "FROM revolving_work_shards"));
223 1 : FAILIF (GNUNET_OK !=
224 : TALER_EXCHANGEDB_delete_shard_locks (pg));
225 1 : FAILIF (0 != TDB_count (pg,
226 : "FROM revolving_work_shards"));
227 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
228 : TALER_EXCHANGEDB_begin_revolving_shard (pg,
229 : "rws-claim",
230 : 10,
231 : 25,
232 : &start,
233 : &end));
234 1 : FAILIF (0 != start);
235 1 : return 0;
236 : }
237 :
238 :
239 : /**
240 : * The checks to run, in order.
241 : */
242 : static const struct TDB_Test tests[] = {
243 : { "revolving-work-shards-empty",
244 : &check_empty },
245 : { "revolving-work-shards-claim-fresh",
246 : &check_claim_fresh },
247 : { "revolving-work-shards-exhausted-and-release",
248 : &check_exhausted_and_release },
249 : { "revolving-work-shards-single-shard",
250 : &check_single_shard },
251 : { "revolving-work-shards-delete-locks",
252 : &check_delete_locks },
253 : { NULL, NULL }
254 : };
255 :
256 :
257 : int
258 1 : main (int argc,
259 : char *const *argv)
260 : {
261 1 : return TDB_main (argc,
262 : argv,
263 : "test-revolving-work-shards",
264 : "Tests for the exchangedb `revolving_work_shards' table",
265 : tests);
266 : }
267 :
268 :
269 : /* end of test_revolving_work_shards.c */
|