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