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 10 : purse_status_cb (void *cls,
123 : const struct TALER_EXCHANGE_PurseGetResponse *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 7 : GNUNET_SCHEDULER_cancel (ss->ps->tt);
157 7 : ss->ps->tt = NULL;
158 7 : TALER_TESTING_interpreter_next (is);
159 7 : return;
160 : }
161 3 : 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 0 : return;
194 : }
195 10 : ss->pgh = TALER_EXCHANGE_purse_get (
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 : ss->timeout,
201 10 : ss->wait_for_merge,
202 : &purse_status_cb,
203 : ss);
204 10 : if (! GNUNET_TIME_relative_is_zero (ss->timeout))
205 : {
206 10 : TALER_TESTING_interpreter_next (is);
207 10 : return;
208 : }
209 : }
210 :
211 :
212 : /**
213 : * Cleanup the state from a "purse status" CMD, and possibly
214 : * cancel a pending operation thereof.
215 : *
216 : * @param cls closure.
217 : * @param cmd the command which is being cleaned up.
218 : */
219 : static void
220 10 : status_cleanup (void *cls,
221 : const struct TALER_TESTING_Command *cmd)
222 : {
223 10 : struct StatusState *ss = cls;
224 :
225 10 : if (NULL != ss->pgh)
226 : {
227 0 : TALER_TESTING_command_incomplete (ss->is,
228 : cmd->label);
229 0 : TALER_EXCHANGE_purse_get_cancel (ss->pgh);
230 0 : ss->pgh = NULL;
231 : }
232 10 : GNUNET_free (ss);
233 10 : }
234 :
235 :
236 : struct TALER_TESTING_Command
237 10 : TALER_TESTING_cmd_purse_poll (
238 : const char *label,
239 : unsigned int expected_http_status,
240 : const char *purse_ref,
241 : const char *expected_balance,
242 : bool wait_for_merge,
243 : struct GNUNET_TIME_Relative timeout)
244 : {
245 : struct StatusState *ss;
246 :
247 10 : GNUNET_assert (NULL != purse_ref);
248 10 : ss = GNUNET_new (struct StatusState);
249 10 : ss->purse_reference = purse_ref;
250 10 : ss->expected_balance = expected_balance;
251 10 : ss->expected_response_code = expected_http_status;
252 10 : ss->timeout = timeout;
253 10 : ss->wait_for_merge = wait_for_merge;
254 : {
255 10 : struct TALER_TESTING_Command cmd = {
256 : .cls = ss,
257 : .label = label,
258 : .run = &status_run,
259 : .cleanup = &status_cleanup
260 : };
261 :
262 10 : return cmd;
263 : }
264 : }
265 :
266 :
267 : /**
268 : * Long poller timed out. Fail the test.
269 : *
270 : * @param cls a `struct PollState`
271 : */
272 : static void
273 0 : finish_timeout (void *cls)
274 : {
275 0 : struct PollState *ps = cls;
276 :
277 0 : ps->tt = NULL;
278 0 : GNUNET_break (0);
279 0 : TALER_TESTING_interpreter_fail (ps->is);
280 0 : }
281 :
282 :
283 : /**
284 : * Run the command.
285 : *
286 : * @param cls closure.
287 : * @param cmd the command being executed.
288 : * @param is the interpreter state.
289 : */
290 : static void
291 10 : finish_run (void *cls,
292 : const struct TALER_TESTING_Command *cmd,
293 : struct TALER_TESTING_Interpreter *is)
294 : {
295 10 : struct PollState *ps = cls;
296 : const struct TALER_TESTING_Command *poll_purse;
297 : struct StatusState *ss;
298 :
299 10 : ps->is = is;
300 : poll_purse
301 10 : = TALER_TESTING_interpreter_lookup_command (is,
302 : ps->poll_reference);
303 10 : GNUNET_assert (NULL != poll_purse);
304 10 : GNUNET_assert (poll_purse->run == &status_run);
305 10 : ss = poll_purse->cls;
306 10 : if (NULL == ss->pgh)
307 : {
308 3 : TALER_TESTING_interpreter_next (is);
309 3 : return;
310 : }
311 7 : GNUNET_assert (NULL == ss->ps);
312 7 : ss->ps = ps;
313 7 : ps->tt = GNUNET_SCHEDULER_add_delayed (ps->timeout,
314 : &finish_timeout,
315 : ps);
316 : }
317 :
318 :
319 : /**
320 : * Cleanup the state from a "purse finish" CMD.
321 : *
322 : * @param cls closure.
323 : * @param cmd the command which is being cleaned up.
324 : */
325 : static void
326 10 : finish_cleanup (void *cls,
327 : const struct TALER_TESTING_Command *cmd)
328 : {
329 10 : struct PollState *ps = cls;
330 :
331 10 : if (NULL != ps->tt)
332 : {
333 0 : GNUNET_SCHEDULER_cancel (ps->tt);
334 0 : ps->tt = NULL;
335 : }
336 10 : GNUNET_free (ps);
337 10 : }
338 :
339 :
340 : struct TALER_TESTING_Command
341 10 : TALER_TESTING_cmd_purse_poll_finish (const char *label,
342 : struct GNUNET_TIME_Relative timeout,
343 : const char *poll_reference)
344 : {
345 : struct PollState *ps;
346 :
347 10 : GNUNET_assert (NULL != poll_reference);
348 10 : ps = GNUNET_new (struct PollState);
349 10 : ps->timeout = timeout;
350 10 : ps->poll_reference = poll_reference;
351 : {
352 10 : struct TALER_TESTING_Command cmd = {
353 : .cls = ps,
354 : .label = label,
355 : .run = &finish_run,
356 : .cleanup = &finish_cleanup
357 : };
358 :
359 10 : return cmd;
360 : }
361 : }
|