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_purse_get.c
21 : * @brief Implement the GET /purse/$RID test command.
22 : * @author Marcello Stanisci
23 : */
24 : #include "taler/taler_json_lib.h"
25 : #include <gnunet/gnunet_curl_lib.h>
26 : #include "taler/taler_testing_lib.h"
27 :
28 :
29 : /**
30 : * State for a "poll" CMD.
31 : */
32 : struct PollState
33 : {
34 :
35 : /**
36 : * How long do we give the exchange to respond?
37 : */
38 : struct GNUNET_TIME_Relative timeout;
39 :
40 : /**
41 : * Label to the command which created the purse to check,
42 : * needed to resort the purse key.
43 : */
44 : const char *poll_reference;
45 :
46 : /**
47 : * Timeout to wait for at most.
48 : */
49 : struct GNUNET_SCHEDULER_Task *tt;
50 :
51 : /**
52 : * The interpreter we are using.
53 : */
54 : struct TALER_TESTING_Interpreter *is;
55 : };
56 :
57 :
58 : /**
59 : * State for a "status" CMD.
60 : */
61 : struct StatusState
62 : {
63 :
64 : /**
65 : * How long do we give the exchange to respond?
66 : */
67 : struct GNUNET_TIME_Relative timeout;
68 :
69 : /**
70 : * Poller waiting for us.
71 : */
72 : struct PollState *ps;
73 :
74 : /**
75 : * Label to the command which created the purse to check,
76 : * needed to resort the purse key.
77 : */
78 : const char *purse_reference;
79 :
80 : /**
81 : * Handle to the "purse status" operation.
82 : */
83 : struct TALER_EXCHANGE_GetPursesHandle *pgh;
84 :
85 : /**
86 : * Expected purse balance.
87 : */
88 : const char *expected_balance;
89 :
90 : /**
91 : * Public key of the purse being analyzed.
92 : */
93 : const struct TALER_PurseContractPublicKeyP *purse_pub;
94 :
95 : /**
96 : * Interpreter state.
97 : */
98 : struct TALER_TESTING_Interpreter *is;
99 :
100 : /**
101 : * Expected HTTP response code.
102 : */
103 : unsigned int expected_response_code;
104 :
105 : /**
106 : * Are we waiting for a merge or a deposit?
107 : */
108 : bool wait_for_merge;
109 :
110 : };
111 :
112 :
113 : /**
114 : * Check that the purse balance and HTTP response code are
115 : * both acceptable.
116 : *
117 : * @param cls closure.
118 : * @param rs HTTP response details
119 : */
120 : static void
121 10 : purse_status_cb (void *cls,
122 : const struct TALER_EXCHANGE_GetPursesResponse *rs)
123 : {
124 10 : struct StatusState *ss = cls;
125 10 : struct TALER_TESTING_Interpreter *is = ss->is;
126 :
127 10 : ss->pgh = NULL;
128 10 : if (ss->expected_response_code != rs->hr.http_status)
129 : {
130 0 : TALER_TESTING_unexpected_status (is,
131 : rs->hr.http_status,
132 : ss->expected_response_code);
133 0 : return;
134 : }
135 10 : if (MHD_HTTP_OK == ss->expected_response_code)
136 : {
137 : struct TALER_Amount eb;
138 :
139 6 : GNUNET_assert (GNUNET_OK ==
140 : TALER_string_to_amount (ss->expected_balance,
141 : &eb));
142 6 : if (0 != TALER_amount_cmp (&eb,
143 : &rs->details.ok.balance))
144 : {
145 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
146 : "Unexpected amount in purse: %s\n",
147 : TALER_amount_to_string (&rs->details.ok.balance));
148 0 : TALER_TESTING_interpreter_fail (ss->is);
149 0 : return;
150 : }
151 : }
152 10 : if (NULL != ss->ps)
153 : {
154 : /* force continuation on long poller */
155 8 : GNUNET_SCHEDULER_cancel (ss->ps->tt);
156 8 : ss->ps->tt = NULL;
157 8 : TALER_TESTING_interpreter_next (is);
158 8 : return;
159 : }
160 2 : if (GNUNET_TIME_relative_is_zero (ss->timeout))
161 0 : TALER_TESTING_interpreter_next (is);
162 : }
163 :
164 :
165 : /**
166 : * Run the command.
167 : *
168 : * @param cls closure.
169 : * @param cmd the command being executed.
170 : * @param is the interpreter state.
171 : */
172 : static void
173 10 : status_run (void *cls,
174 : const struct TALER_TESTING_Command *cmd,
175 : struct TALER_TESTING_Interpreter *is)
176 : {
177 10 : struct StatusState *ss = cls;
178 : const struct TALER_TESTING_Command *create_purse;
179 :
180 10 : ss->is = is;
181 : create_purse
182 10 : = TALER_TESTING_interpreter_lookup_command (is,
183 : ss->purse_reference);
184 10 : GNUNET_assert (NULL != create_purse);
185 10 : if (GNUNET_OK !=
186 10 : TALER_TESTING_get_trait_purse_pub (create_purse,
187 : &ss->purse_pub))
188 : {
189 0 : GNUNET_break (0);
190 0 : TALER_LOG_ERROR ("Failed to find purse_pub for status query\n");
191 0 : TALER_TESTING_interpreter_fail (is);
192 10 : return;
193 : }
194 10 : ss->pgh = TALER_EXCHANGE_get_purses_create (
195 : TALER_TESTING_interpreter_get_context (is),
196 : TALER_TESTING_get_exchange_url (is),
197 : TALER_TESTING_get_keys (is),
198 : ss->purse_pub);
199 10 : if (NULL == ss->pgh)
200 : {
201 0 : GNUNET_break (0);
202 0 : TALER_TESTING_interpreter_fail (is);
203 0 : return;
204 : }
205 10 : TALER_EXCHANGE_get_purses_set_options (
206 : ss->pgh,
207 : TALER_EXCHANGE_get_purses_option_timeout (ss->timeout),
208 : TALER_EXCHANGE_get_purses_option_wait_for_merge (ss->wait_for_merge));
209 10 : if (TALER_EC_NONE !=
210 10 : TALER_EXCHANGE_get_purses_start (ss->pgh,
211 : &purse_status_cb,
212 : ss))
213 : {
214 0 : GNUNET_break (0);
215 0 : TALER_EXCHANGE_get_purses_cancel (ss->pgh);
216 0 : ss->pgh = NULL;
217 0 : TALER_TESTING_interpreter_fail (is);
218 0 : return;
219 : }
220 10 : if (! GNUNET_TIME_relative_is_zero (ss->timeout))
221 : {
222 10 : TALER_TESTING_interpreter_next (is);
223 10 : return;
224 : }
225 : }
226 :
227 :
228 : /**
229 : * Cleanup the state from a "purse status" CMD, and possibly
230 : * cancel a pending operation thereof.
231 : *
232 : * @param cls closure.
233 : * @param cmd the command which is being cleaned up.
234 : */
235 : static void
236 10 : status_cleanup (void *cls,
237 : const struct TALER_TESTING_Command *cmd)
238 : {
239 10 : struct StatusState *ss = cls;
240 :
241 10 : if (NULL != ss->pgh)
242 : {
243 0 : TALER_TESTING_command_incomplete (ss->is,
244 : cmd->label);
245 0 : TALER_EXCHANGE_get_purses_cancel (ss->pgh);
246 0 : ss->pgh = NULL;
247 : }
248 10 : GNUNET_free (ss);
249 10 : }
250 :
251 :
252 : struct TALER_TESTING_Command
253 10 : TALER_TESTING_cmd_purse_poll (
254 : const char *label,
255 : unsigned int expected_http_status,
256 : const char *purse_ref,
257 : const char *expected_balance,
258 : bool wait_for_merge,
259 : struct GNUNET_TIME_Relative timeout)
260 : {
261 : struct StatusState *ss;
262 :
263 10 : GNUNET_assert (NULL != purse_ref);
264 10 : ss = GNUNET_new (struct StatusState);
265 10 : ss->purse_reference = purse_ref;
266 10 : ss->expected_balance = expected_balance;
267 10 : ss->expected_response_code = expected_http_status;
268 10 : ss->timeout = timeout;
269 10 : ss->wait_for_merge = wait_for_merge;
270 : {
271 10 : struct TALER_TESTING_Command cmd = {
272 : .cls = ss,
273 : .label = label,
274 : .run = &status_run,
275 : .cleanup = &status_cleanup
276 : };
277 :
278 10 : return cmd;
279 : }
280 : }
281 :
282 :
283 : /**
284 : * Long poller timed out. Fail the test.
285 : *
286 : * @param cls a `struct PollState`
287 : */
288 : static void
289 0 : finish_timeout (void *cls)
290 : {
291 0 : struct PollState *ps = cls;
292 :
293 0 : ps->tt = NULL;
294 0 : GNUNET_break (0);
295 0 : TALER_TESTING_interpreter_fail (ps->is);
296 0 : }
297 :
298 :
299 : /**
300 : * Run the command.
301 : *
302 : * @param cls closure.
303 : * @param cmd the command being executed.
304 : * @param is the interpreter state.
305 : */
306 : static void
307 10 : finish_run (void *cls,
308 : const struct TALER_TESTING_Command *cmd,
309 : struct TALER_TESTING_Interpreter *is)
310 : {
311 10 : struct PollState *ps = cls;
312 : const struct TALER_TESTING_Command *poll_purse;
313 : struct StatusState *ss;
314 :
315 10 : ps->is = is;
316 : poll_purse
317 10 : = TALER_TESTING_interpreter_lookup_command (is,
318 : ps->poll_reference);
319 10 : GNUNET_assert (NULL != poll_purse);
320 10 : GNUNET_assert (poll_purse->run == &status_run);
321 10 : ss = poll_purse->cls;
322 10 : if (NULL == ss->pgh)
323 : {
324 2 : TALER_TESTING_interpreter_next (is);
325 2 : return;
326 : }
327 8 : GNUNET_assert (NULL == ss->ps);
328 8 : ss->ps = ps;
329 8 : ps->tt = GNUNET_SCHEDULER_add_delayed (ps->timeout,
330 : &finish_timeout,
331 : ps);
332 : }
333 :
334 :
335 : /**
336 : * Cleanup the state from a "purse finish" CMD.
337 : *
338 : * @param cls closure.
339 : * @param cmd the command which is being cleaned up.
340 : */
341 : static void
342 10 : finish_cleanup (void *cls,
343 : const struct TALER_TESTING_Command *cmd)
344 : {
345 10 : struct PollState *ps = cls;
346 :
347 10 : if (NULL != ps->tt)
348 : {
349 0 : GNUNET_SCHEDULER_cancel (ps->tt);
350 0 : ps->tt = NULL;
351 : }
352 10 : GNUNET_free (ps);
353 10 : }
354 :
355 :
356 : struct TALER_TESTING_Command
357 10 : TALER_TESTING_cmd_purse_poll_finish (const char *label,
358 : struct GNUNET_TIME_Relative timeout,
359 : const char *poll_reference)
360 : {
361 : struct PollState *ps;
362 :
363 10 : GNUNET_assert (NULL != poll_reference);
364 10 : ps = GNUNET_new (struct PollState);
365 10 : ps->timeout = timeout;
366 10 : ps->poll_reference = poll_reference;
367 : {
368 10 : struct TALER_TESTING_Command cmd = {
369 : .cls = ps,
370 : .label = label,
371 : .run = &finish_run,
372 : .cleanup = &finish_cleanup
373 : };
374 :
375 10 : return cmd;
376 : }
377 : }
|