Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2022 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU General Public License as
7 : published by the Free Software Foundation; either version 3, or
8 : (at your option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not, see
17 : <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file testing/testing_api_cmd_reserve_get.c
21 : * @brief Implement the GET /reserve/$RID test command.
22 : * @author Marcello Stanisci
23 : */
24 : #include "taler/taler_json_lib.h"
25 : #include <gnunet/gnunet_curl_lib.h>
26 : struct StatusState;
27 : #define TALER_EXCHANGE_GET_RESERVES_RESULT_CLOSURE struct StatusState
28 : #include "taler/exchange/get-reserves-RESERVE_PUB.h"
29 : #include "taler/taler_testing_lib.h"
30 :
31 :
32 : /**
33 : * State for a "poll" CMD.
34 : */
35 : struct PollState
36 : {
37 :
38 : /**
39 : * How long do we give the exchange to respond?
40 : */
41 : struct GNUNET_TIME_Relative timeout;
42 :
43 : /**
44 : * Label to the command which created the reserve to check,
45 : * needed to resort the reserve key.
46 : */
47 : const char *poll_reference;
48 :
49 : /**
50 : * Timeout to wait for at most.
51 : */
52 : struct GNUNET_SCHEDULER_Task *tt;
53 :
54 : /**
55 : * The interpreter we are using.
56 : */
57 : struct TALER_TESTING_Interpreter *is;
58 : };
59 :
60 :
61 : /**
62 : * State for a "status" CMD.
63 : */
64 : struct StatusState
65 : {
66 :
67 : /**
68 : * How long do we give the exchange to respond?
69 : */
70 : struct GNUNET_TIME_Relative timeout;
71 :
72 : /**
73 : * Poller waiting for us.
74 : */
75 : struct PollState *ps;
76 :
77 : /**
78 : * Label to the command which created the reserve to check,
79 : * needed to resort the reserve key.
80 : */
81 : const char *reserve_reference;
82 :
83 : /**
84 : * Handle to the "reserve status" operation.
85 : */
86 : struct TALER_EXCHANGE_GetReservesHandle *rsh;
87 :
88 : /**
89 : * Expected reserve balance.
90 : */
91 : const char *expected_balance;
92 :
93 : /**
94 : * Public key of the reserve being analyzed.
95 : */
96 : const struct TALER_ReservePublicKeyP *reserve_pubp;
97 :
98 : /**
99 : * Expected HTTP response code.
100 : */
101 : unsigned int expected_response_code;
102 :
103 : /**
104 : * Interpreter state.
105 : */
106 : struct TALER_TESTING_Interpreter *is;
107 :
108 : };
109 :
110 :
111 : /**
112 : * Check that the reserve balance and HTTP response code are
113 : * both acceptable.
114 : *
115 : * @param ss closure.
116 : * @param rs HTTP response details
117 : */
118 : static void
119 30 : reserve_status_cb (struct StatusState *ss,
120 : const struct TALER_EXCHANGE_GetReservesResponse *rs)
121 : {
122 30 : struct TALER_TESTING_Interpreter *is = ss->is;
123 : struct TALER_Amount eb;
124 :
125 30 : ss->rsh = NULL;
126 30 : if (ss->expected_response_code != rs->hr.http_status)
127 : {
128 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
129 : "Unexpected HTTP response code: %d in %s:%u\n",
130 : rs->hr.http_status,
131 : __FILE__,
132 : __LINE__);
133 0 : json_dumpf (rs->hr.reply,
134 : stderr,
135 : 0);
136 0 : TALER_TESTING_interpreter_fail (ss->is);
137 0 : return;
138 : }
139 30 : if (MHD_HTTP_OK == ss->expected_response_code)
140 : {
141 30 : GNUNET_assert (GNUNET_OK ==
142 : TALER_string_to_amount (ss->expected_balance,
143 : &eb));
144 30 : if (0 != TALER_amount_cmp (&eb,
145 : &rs->details.ok.balance))
146 : {
147 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
148 : "Unexpected amount %s in reserve, wanted %s\n",
149 : TALER_amount_to_string (&rs->details.ok.balance),
150 : ss->expected_balance);
151 0 : TALER_TESTING_interpreter_fail (ss->is);
152 0 : return;
153 : }
154 : }
155 30 : if (NULL != ss->ps)
156 : {
157 : /* force continuation on long poller */
158 0 : GNUNET_SCHEDULER_cancel (ss->ps->tt);
159 0 : ss->ps->tt = NULL;
160 0 : TALER_TESTING_interpreter_next (is);
161 0 : return;
162 : }
163 30 : if (GNUNET_TIME_relative_is_zero (ss->timeout))
164 23 : TALER_TESTING_interpreter_next (is);
165 : }
166 :
167 :
168 : /**
169 : * Run the command.
170 : *
171 : * @param cls closure.
172 : * @param cmd the command being executed.
173 : * @param is the interpreter state.
174 : */
175 : static void
176 30 : status_run (void *cls,
177 : const struct TALER_TESTING_Command *cmd,
178 : struct TALER_TESTING_Interpreter *is)
179 : {
180 30 : struct StatusState *ss = cls;
181 : const struct TALER_TESTING_Command *create_reserve;
182 : const char *exchange_url;
183 :
184 30 : ss->is = is;
185 30 : exchange_url = TALER_TESTING_get_exchange_url (is);
186 30 : if (NULL == exchange_url)
187 : {
188 0 : GNUNET_break (0);
189 0 : return;
190 : }
191 : create_reserve
192 30 : = TALER_TESTING_interpreter_lookup_command (is,
193 : ss->reserve_reference);
194 30 : GNUNET_assert (NULL != create_reserve);
195 30 : if (GNUNET_OK !=
196 30 : TALER_TESTING_get_trait_reserve_pub (create_reserve,
197 : &ss->reserve_pubp))
198 : {
199 0 : GNUNET_break (0);
200 0 : TALER_LOG_ERROR ("Failed to find reserve_pub for status query\n");
201 0 : TALER_TESTING_interpreter_fail (is);
202 0 : return;
203 : }
204 30 : ss->rsh = TALER_EXCHANGE_get_reserves_create (
205 : TALER_TESTING_interpreter_get_context (is),
206 : exchange_url,
207 : ss->reserve_pubp);
208 30 : if (NULL == ss->rsh)
209 : {
210 0 : GNUNET_break (0);
211 0 : TALER_TESTING_interpreter_fail (is);
212 0 : return;
213 : }
214 30 : if (! GNUNET_TIME_relative_is_zero (ss->timeout))
215 7 : TALER_EXCHANGE_get_reserves_set_options (
216 : ss->rsh,
217 : TALER_EXCHANGE_get_reserves_option_timeout (ss->timeout));
218 30 : if (TALER_EC_NONE !=
219 30 : TALER_EXCHANGE_get_reserves_start (ss->rsh,
220 : &reserve_status_cb,
221 : ss))
222 : {
223 0 : GNUNET_break (0);
224 0 : TALER_EXCHANGE_get_reserves_cancel (ss->rsh);
225 0 : ss->rsh = NULL;
226 0 : TALER_TESTING_interpreter_fail (is);
227 0 : return;
228 : }
229 30 : if (! GNUNET_TIME_relative_is_zero (ss->timeout))
230 : {
231 7 : TALER_TESTING_interpreter_next (is);
232 7 : return;
233 : }
234 : }
235 :
236 :
237 : /**
238 : * Cleanup the state from a "reserve status" CMD, and possibly
239 : * cancel a pending operation thereof.
240 : *
241 : * @param cls closure.
242 : * @param cmd the command which is being cleaned up.
243 : */
244 : static void
245 30 : status_cleanup (void *cls,
246 : const struct TALER_TESTING_Command *cmd)
247 : {
248 30 : struct StatusState *ss = cls;
249 :
250 30 : if (NULL != ss->rsh)
251 : {
252 0 : TALER_TESTING_command_incomplete (ss->is,
253 : cmd->label);
254 0 : TALER_EXCHANGE_get_reserves_cancel (ss->rsh);
255 0 : ss->rsh = NULL;
256 : }
257 30 : GNUNET_free (ss);
258 30 : }
259 :
260 :
261 : struct TALER_TESTING_Command
262 23 : TALER_TESTING_cmd_status (const char *label,
263 : const char *reserve_reference,
264 : const char *expected_balance,
265 : unsigned int expected_response_code)
266 : {
267 : struct StatusState *ss;
268 :
269 23 : GNUNET_assert (NULL != reserve_reference);
270 23 : ss = GNUNET_new (struct StatusState);
271 23 : ss->reserve_reference = reserve_reference;
272 23 : ss->expected_balance = expected_balance;
273 23 : ss->expected_response_code = expected_response_code;
274 : {
275 23 : struct TALER_TESTING_Command cmd = {
276 : .cls = ss,
277 : .label = label,
278 : .run = &status_run,
279 : .cleanup = &status_cleanup
280 : };
281 :
282 23 : return cmd;
283 : }
284 : }
285 :
286 :
287 : struct TALER_TESTING_Command
288 7 : TALER_TESTING_cmd_reserve_poll (const char *label,
289 : const char *reserve_reference,
290 : const char *expected_balance,
291 : struct GNUNET_TIME_Relative timeout,
292 : unsigned int expected_response_code)
293 : {
294 : struct StatusState *ss;
295 :
296 7 : GNUNET_assert (NULL != reserve_reference);
297 7 : ss = GNUNET_new (struct StatusState);
298 7 : ss->reserve_reference = reserve_reference;
299 7 : ss->expected_balance = expected_balance;
300 7 : ss->expected_response_code = expected_response_code;
301 7 : ss->timeout = timeout;
302 : {
303 7 : struct TALER_TESTING_Command cmd = {
304 : .cls = ss,
305 : .label = label,
306 : .run = &status_run,
307 : .cleanup = &status_cleanup
308 : };
309 :
310 7 : return cmd;
311 : }
312 : }
313 :
314 :
315 : /**
316 : * Long poller timed out. Fail the test.
317 : *
318 : * @param cls a `struct PollState`
319 : */
320 : static void
321 0 : finish_timeout (void *cls)
322 : {
323 0 : struct PollState *ps = cls;
324 :
325 0 : ps->tt = NULL;
326 0 : GNUNET_break (0);
327 0 : TALER_TESTING_interpreter_fail (ps->is);
328 0 : }
329 :
330 :
331 : /**
332 : * Run the command.
333 : *
334 : * @param cls closure.
335 : * @param cmd the command being executed.
336 : * @param is the interpreter state.
337 : */
338 : static void
339 7 : finish_run (void *cls,
340 : const struct TALER_TESTING_Command *cmd,
341 : struct TALER_TESTING_Interpreter *is)
342 : {
343 7 : struct PollState *ps = cls;
344 : const struct TALER_TESTING_Command *poll_reserve;
345 : struct StatusState *ss;
346 :
347 7 : ps->is = is;
348 : poll_reserve
349 7 : = TALER_TESTING_interpreter_lookup_command (is,
350 : ps->poll_reference);
351 7 : GNUNET_assert (NULL != poll_reserve);
352 7 : GNUNET_assert (poll_reserve->run == &status_run);
353 7 : ss = poll_reserve->cls;
354 7 : if (NULL == ss->rsh)
355 : {
356 7 : TALER_TESTING_interpreter_next (is);
357 7 : return;
358 : }
359 0 : GNUNET_assert (NULL == ss->ps);
360 0 : ss->ps = ps;
361 0 : ps->tt = GNUNET_SCHEDULER_add_delayed (ps->timeout,
362 : &finish_timeout,
363 : ps);
364 : }
365 :
366 :
367 : /**
368 : * Cleanup the state from a "reserve finish" CMD.
369 : *
370 : * @param cls closure.
371 : * @param cmd the command which is being cleaned up.
372 : */
373 : static void
374 7 : finish_cleanup (void *cls,
375 : const struct TALER_TESTING_Command *cmd)
376 : {
377 7 : struct PollState *ps = cls;
378 :
379 7 : if (NULL != ps->tt)
380 : {
381 0 : GNUNET_SCHEDULER_cancel (ps->tt);
382 0 : ps->tt = NULL;
383 : }
384 7 : GNUNET_free (ps);
385 7 : }
386 :
387 :
388 : struct TALER_TESTING_Command
389 7 : TALER_TESTING_cmd_reserve_poll_finish (const char *label,
390 : struct GNUNET_TIME_Relative timeout,
391 : const char *poll_reference)
392 : {
393 : struct PollState *ps;
394 :
395 7 : GNUNET_assert (NULL != poll_reference);
396 7 : ps = GNUNET_new (struct PollState);
397 7 : ps->timeout = timeout;
398 7 : ps->poll_reference = poll_reference;
399 : {
400 7 : struct TALER_TESTING_Command cmd = {
401 : .cls = ps,
402 : .label = label,
403 : .run = &finish_run,
404 : .cleanup = &finish_cleanup
405 : };
406 :
407 7 : return cmd;
408 : }
409 : }
|