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