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 "platform.h"
25 : #include "taler_json_lib.h"
26 : #include <gnunet/gnunet_curl_lib.h>
27 : #include "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_PurseGetHandle *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 0 : purse_status_cb (void *cls,
123 : const struct TALER_EXCHANGE_PurseGetResponse *rs)
124 : {
125 0 : struct StatusState *ss = cls;
126 0 : struct TALER_TESTING_Interpreter *is = ss->is;
127 :
128 0 : ss->pgh = NULL;
129 0 : if (ss->expected_response_code != rs->hr.http_status)
130 : {
131 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
132 : "Unexpected HTTP response code: %d in %s:%u\n",
133 : rs->hr.http_status,
134 : __FILE__,
135 : __LINE__);
136 0 : json_dumpf (rs->hr.reply,
137 : stderr,
138 : 0);
139 0 : TALER_TESTING_interpreter_fail (ss->is);
140 0 : return;
141 : }
142 0 : if (MHD_HTTP_OK == ss->expected_response_code)
143 : {
144 : struct TALER_Amount eb;
145 :
146 0 : GNUNET_assert (GNUNET_OK ==
147 : TALER_string_to_amount (ss->expected_balance,
148 : &eb));
149 0 : if (0 != TALER_amount_cmp (&eb,
150 : &rs->details.success.balance))
151 : {
152 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
153 : "Unexpected amount in purse: %s\n",
154 : TALER_amount_to_string (&rs->details.success.balance));
155 0 : TALER_TESTING_interpreter_fail (ss->is);
156 0 : return;
157 : }
158 : }
159 0 : if (NULL != ss->ps)
160 : {
161 : /* force continuation on long poller */
162 0 : GNUNET_SCHEDULER_cancel (ss->ps->tt);
163 0 : ss->ps->tt = NULL;
164 0 : TALER_TESTING_interpreter_next (is);
165 0 : return;
166 : }
167 0 : if (GNUNET_TIME_relative_is_zero (ss->timeout))
168 0 : TALER_TESTING_interpreter_next (is);
169 : }
170 :
171 :
172 : /**
173 : * Run the command.
174 : *
175 : * @param cls closure.
176 : * @param cmd the command being executed.
177 : * @param is the interpreter state.
178 : */
179 : static void
180 0 : status_run (void *cls,
181 : const struct TALER_TESTING_Command *cmd,
182 : struct TALER_TESTING_Interpreter *is)
183 : {
184 0 : struct StatusState *ss = cls;
185 : const struct TALER_TESTING_Command *create_purse;
186 :
187 0 : ss->is = is;
188 : create_purse
189 0 : = TALER_TESTING_interpreter_lookup_command (is,
190 : ss->purse_reference);
191 :
192 0 : if (NULL == create_purse)
193 : {
194 0 : GNUNET_break (0);
195 0 : TALER_TESTING_interpreter_fail (is);
196 0 : return;
197 : }
198 0 : if (GNUNET_OK !=
199 0 : TALER_TESTING_get_trait_purse_pub (create_purse,
200 : &ss->purse_pub))
201 : {
202 0 : GNUNET_break (0);
203 0 : TALER_LOG_ERROR ("Failed to find purse_pub for status query\n");
204 0 : TALER_TESTING_interpreter_fail (is);
205 0 : return;
206 : }
207 0 : ss->pgh = TALER_EXCHANGE_purse_get (is->exchange,
208 : ss->purse_pub,
209 : ss->timeout,
210 0 : ss->wait_for_merge,
211 : &purse_status_cb,
212 : ss);
213 0 : if (! GNUNET_TIME_relative_is_zero (ss->timeout))
214 : {
215 0 : TALER_TESTING_interpreter_next (is);
216 0 : return;
217 : }
218 : }
219 :
220 :
221 : /**
222 : * Cleanup the state from a "purse status" CMD, and possibly
223 : * cancel a pending operation thereof.
224 : *
225 : * @param cls closure.
226 : * @param cmd the command which is being cleaned up.
227 : */
228 : static void
229 0 : status_cleanup (void *cls,
230 : const struct TALER_TESTING_Command *cmd)
231 : {
232 0 : struct StatusState *ss = cls;
233 :
234 0 : if (NULL != ss->pgh)
235 : {
236 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
237 : "Command %u (%s) did not complete\n",
238 : ss->is->ip,
239 : cmd->label);
240 0 : TALER_EXCHANGE_purse_get_cancel (ss->pgh);
241 0 : ss->pgh = NULL;
242 : }
243 0 : GNUNET_free (ss);
244 0 : }
245 :
246 :
247 : struct TALER_TESTING_Command
248 0 : TALER_TESTING_cmd_purse_poll (
249 : const char *label,
250 : unsigned int expected_http_status,
251 : const char *purse_ref,
252 : const char *expected_balance,
253 : bool wait_for_merge,
254 : struct GNUNET_TIME_Relative timeout)
255 : {
256 : struct StatusState *ss;
257 :
258 0 : GNUNET_assert (NULL != purse_ref);
259 0 : ss = GNUNET_new (struct StatusState);
260 0 : ss->purse_reference = purse_ref;
261 0 : ss->expected_balance = expected_balance;
262 0 : ss->expected_response_code = expected_http_status;
263 0 : ss->timeout = timeout;
264 0 : ss->wait_for_merge = wait_for_merge;
265 : {
266 0 : struct TALER_TESTING_Command cmd = {
267 : .cls = ss,
268 : .label = label,
269 : .run = &status_run,
270 : .cleanup = &status_cleanup
271 : };
272 :
273 0 : return cmd;
274 : }
275 : }
276 :
277 :
278 : /**
279 : * Long poller timed out. Fail the test.
280 : *
281 : * @param cls a `struct PollState`
282 : */
283 : static void
284 0 : finish_timeout (void *cls)
285 : {
286 0 : struct PollState *ps = cls;
287 :
288 0 : ps->tt = NULL;
289 0 : GNUNET_break (0);
290 0 : TALER_TESTING_interpreter_fail (ps->is);
291 0 : }
292 :
293 :
294 : /**
295 : * Run the command.
296 : *
297 : * @param cls closure.
298 : * @param cmd the command being executed.
299 : * @param is the interpreter state.
300 : */
301 : static void
302 0 : finish_run (void *cls,
303 : const struct TALER_TESTING_Command *cmd,
304 : struct TALER_TESTING_Interpreter *is)
305 : {
306 0 : struct PollState *ps = cls;
307 : const struct TALER_TESTING_Command *poll_purse;
308 : struct StatusState *ss;
309 :
310 0 : ps->is = is;
311 : poll_purse
312 0 : = TALER_TESTING_interpreter_lookup_command (is,
313 : ps->poll_reference);
314 0 : GNUNET_assert (poll_purse->run == &status_run);
315 0 : ss = poll_purse->cls;
316 0 : if (NULL == ss->pgh)
317 : {
318 0 : TALER_TESTING_interpreter_next (is);
319 0 : return;
320 : }
321 0 : GNUNET_assert (NULL == ss->ps);
322 0 : ss->ps = ps;
323 0 : ps->tt = GNUNET_SCHEDULER_add_delayed (ps->timeout,
324 : &finish_timeout,
325 : ps);
326 : }
327 :
328 :
329 : /**
330 : * Cleanup the state from a "purse finish" CMD.
331 : *
332 : * @param cls closure.
333 : * @param cmd the command which is being cleaned up.
334 : */
335 : static void
336 0 : finish_cleanup (void *cls,
337 : const struct TALER_TESTING_Command *cmd)
338 : {
339 0 : struct PollState *ps = cls;
340 :
341 0 : if (NULL != ps->tt)
342 : {
343 0 : GNUNET_SCHEDULER_cancel (ps->tt);
344 0 : ps->tt = NULL;
345 : }
346 0 : GNUNET_free (ps);
347 0 : }
348 :
349 :
350 : struct TALER_TESTING_Command
351 0 : TALER_TESTING_cmd_purse_poll_finish (const char *label,
352 : struct GNUNET_TIME_Relative timeout,
353 : const char *poll_reference)
354 : {
355 : struct PollState *ps;
356 :
357 0 : GNUNET_assert (NULL != poll_reference);
358 0 : ps = GNUNET_new (struct PollState);
359 0 : ps->timeout = timeout;
360 0 : ps->poll_reference = poll_reference;
361 : {
362 0 : struct TALER_TESTING_Command cmd = {
363 : .cls = ps,
364 : .label = label,
365 : .run = &finish_run,
366 : .cleanup = &finish_cleanup
367 : };
368 :
369 0 : return cmd;
370 : }
371 : }
|