Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2020 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_reserve_status.c
21 : * @brief Implement the /reserve/$RID/status 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 "status" CMD.
32 : */
33 : struct StatusState
34 : {
35 : /**
36 : * Label to the command which created the reserve to check,
37 : * needed to resort the reserve key.
38 : */
39 : const char *reserve_reference;
40 :
41 : /**
42 : * Handle to the "reserve status" operation.
43 : */
44 : struct TALER_EXCHANGE_ReservesStatusHandle *rsh;
45 :
46 : /**
47 : * Expected reserve balance.
48 : */
49 : const char *expected_balance;
50 :
51 : /**
52 : * Private key of the reserve being analyzed.
53 : */
54 : const struct TALER_ReservePrivateKeyP *reserve_priv;
55 :
56 : /**
57 : * Public key of the reserve being analyzed.
58 : */
59 : struct TALER_ReservePublicKeyP reserve_pub;
60 :
61 : /**
62 : * Expected HTTP response code.
63 : */
64 : unsigned int expected_response_code;
65 :
66 : /**
67 : * Interpreter state.
68 : */
69 : struct TALER_TESTING_Interpreter *is;
70 : };
71 :
72 :
73 : /**
74 : * Check if @a cmd changed the reserve, if so, find the
75 : * entry in @a history and set the respective index in @a found
76 : * to #GNUNET_YES. If the entry is not found, return #GNUNET_SYSERR.
77 : *
78 : * @param reserve_pub public key of the reserve for which we have the @a history
79 : * @param cmd command to analyze for impact on history
80 : * @param history_length number of entries in @a history and @a found
81 : * @param history history to check
82 : * @param[in,out] found array to update
83 : * @return #GNUNET_OK if @a cmd action on reserve was found in @a history
84 : */
85 : static enum GNUNET_GenericReturnValue
86 0 : analyze_command (const struct TALER_ReservePublicKeyP *reserve_pub,
87 : const struct TALER_TESTING_Command *cmd,
88 : unsigned int history_length,
89 : const struct TALER_EXCHANGE_ReserveHistoryEntry *history,
90 : bool *found)
91 : {
92 0 : if (TALER_TESTING_cmd_is_batch (cmd))
93 : {
94 : struct TALER_TESTING_Command *cur;
95 : struct TALER_TESTING_Command **bcmd;
96 :
97 0 : cur = TALER_TESTING_cmd_batch_get_current (cmd);
98 0 : if (GNUNET_OK !=
99 0 : TALER_TESTING_get_trait_batch_cmds (cmd,
100 : &bcmd))
101 : {
102 0 : GNUNET_break (0);
103 0 : return GNUNET_SYSERR;
104 : }
105 0 : for (unsigned int i = 0; NULL != (*bcmd)[i].label; i++)
106 : {
107 0 : struct TALER_TESTING_Command *step = &(*bcmd)[i];
108 :
109 0 : if (step == cur)
110 0 : break; /* if *we* are in a batch, make sure not to analyze commands past 'now' */
111 0 : if (GNUNET_OK !=
112 0 : analyze_command (reserve_pub,
113 : step,
114 : history_length,
115 : history,
116 : found))
117 : {
118 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
119 : "Entry for batch step `%s' missing in history\n",
120 : step->label);
121 0 : return GNUNET_SYSERR;
122 : }
123 : }
124 0 : return GNUNET_OK;
125 : }
126 : else
127 : {
128 : const struct TALER_ReservePublicKeyP *rp;
129 :
130 0 : if (GNUNET_OK !=
131 0 : TALER_TESTING_get_trait_reserve_pub (cmd,
132 : &rp))
133 0 : return GNUNET_OK; /* command does nothing for reserves */
134 0 : if (0 !=
135 0 : GNUNET_memcmp (rp,
136 : reserve_pub))
137 0 : return GNUNET_OK; /* command affects some _other_ reserve */
138 0 : for (unsigned int j = 0; true; j++)
139 0 : {
140 : const struct TALER_EXCHANGE_ReserveHistoryEntry *he;
141 0 : bool matched = false;
142 :
143 0 : if (GNUNET_OK !=
144 0 : TALER_TESTING_get_trait_reserve_history (cmd,
145 : j,
146 : &he))
147 : {
148 : /* NOTE: only for debugging... */
149 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
150 : "Command `%s' has the reserve_pub trait, but does not reserve history trait\n",
151 : cmd->label);
152 0 : return GNUNET_OK; /* command does nothing for reserves */
153 : }
154 0 : for (unsigned int i = 0; i<history_length; i++)
155 : {
156 0 : if (found[i])
157 0 : continue; /* already found, skip */
158 0 : if (0 ==
159 0 : TALER_TESTING_history_entry_cmp (he,
160 0 : &history[i]))
161 : {
162 0 : found[i] = true;
163 0 : matched = true;
164 0 : break;
165 : }
166 : }
167 0 : if (! matched)
168 : {
169 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
170 : "Command `%s' reserve history entry #%u not found\n",
171 : cmd->label,
172 : j);
173 0 : return GNUNET_SYSERR;
174 : }
175 : }
176 : }
177 : }
178 :
179 :
180 : /**
181 : * Check that the reserve balance and HTTP response code are
182 : * both acceptable.
183 : *
184 : * @param cls closure.
185 : * @param rs HTTP response details
186 : */
187 : static void
188 0 : reserve_status_cb (void *cls,
189 : const struct TALER_EXCHANGE_ReserveStatus *rs)
190 : {
191 0 : struct StatusState *ss = cls;
192 0 : struct TALER_TESTING_Interpreter *is = ss->is;
193 : struct TALER_Amount eb;
194 :
195 0 : ss->rsh = NULL;
196 0 : if (ss->expected_response_code != rs->hr.http_status)
197 : {
198 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
199 : "Unexpected HTTP response code: %d in %s:%u\n",
200 : rs->hr.http_status,
201 : __FILE__,
202 : __LINE__);
203 0 : json_dumpf (rs->hr.reply,
204 : stderr,
205 : JSON_INDENT (2));
206 0 : TALER_TESTING_interpreter_fail (ss->is);
207 0 : return;
208 : }
209 0 : if (MHD_HTTP_OK != rs->hr.http_status)
210 : {
211 0 : TALER_TESTING_interpreter_next (is);
212 0 : return;
213 : }
214 0 : GNUNET_assert (GNUNET_OK ==
215 : TALER_string_to_amount (ss->expected_balance,
216 : &eb));
217 :
218 0 : if (0 != TALER_amount_cmp (&eb,
219 : &rs->details.ok.balance))
220 : {
221 0 : GNUNET_break (0);
222 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
223 : "Unexpected amount in reserve: %s\n",
224 : TALER_amount_to_string (&rs->details.ok.balance));
225 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
226 : "Expected balance of: %s\n",
227 : TALER_amount_to_string (&eb));
228 0 : TALER_TESTING_interpreter_fail (ss->is);
229 0 : return;
230 : }
231 0 : {
232 0 : bool found[rs->details.ok.history_len];
233 :
234 0 : memset (found,
235 : 0,
236 0 : sizeof (found));
237 0 : for (unsigned int i = 0; i<= (unsigned int) is->ip; i++)
238 : {
239 0 : struct TALER_TESTING_Command *cmd = &is->commands[i];
240 :
241 0 : if (GNUNET_OK !=
242 0 : analyze_command (&ss->reserve_pub,
243 : cmd,
244 : rs->details.ok.history_len,
245 : rs->details.ok.history,
246 : found))
247 : {
248 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
249 : "Entry for command `%s' missing in history\n",
250 : cmd->label);
251 0 : json_dumpf (rs->hr.reply,
252 : stderr,
253 : JSON_INDENT (2));
254 0 : TALER_TESTING_interpreter_fail (ss->is);
255 0 : return;
256 : }
257 : }
258 0 : for (unsigned int i = 0; i<rs->details.ok.history_len; i++)
259 0 : if (! found[i])
260 : {
261 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
262 : "History entry at index %u of type %d not justified by command status\n",
263 : i,
264 : rs->details.ok.history[i].type);
265 0 : json_dumpf (rs->hr.reply,
266 : stderr,
267 : JSON_INDENT (2));
268 0 : TALER_TESTING_interpreter_fail (ss->is);
269 0 : return;
270 : }
271 : }
272 0 : TALER_TESTING_interpreter_next (is);
273 : }
274 :
275 :
276 : /**
277 : * Run the command.
278 : *
279 : * @param cls closure.
280 : * @param cmd the command being executed.
281 : * @param is the interpreter state.
282 : */
283 : static void
284 0 : status_run (void *cls,
285 : const struct TALER_TESTING_Command *cmd,
286 : struct TALER_TESTING_Interpreter *is)
287 : {
288 0 : struct StatusState *ss = cls;
289 : const struct TALER_TESTING_Command *create_reserve;
290 :
291 0 : ss->is = is;
292 : create_reserve
293 0 : = TALER_TESTING_interpreter_lookup_command (is,
294 : ss->reserve_reference);
295 :
296 0 : if (NULL == create_reserve)
297 : {
298 0 : GNUNET_break (0);
299 0 : TALER_TESTING_interpreter_fail (is);
300 0 : return;
301 : }
302 0 : if (GNUNET_OK !=
303 0 : TALER_TESTING_get_trait_reserve_priv (create_reserve,
304 : &ss->reserve_priv))
305 : {
306 0 : GNUNET_break (0);
307 0 : TALER_LOG_ERROR ("Failed to find reserve_priv for status query\n");
308 0 : TALER_TESTING_interpreter_fail (is);
309 0 : return;
310 : }
311 0 : GNUNET_CRYPTO_eddsa_key_get_public (&ss->reserve_priv->eddsa_priv,
312 : &ss->reserve_pub.eddsa_pub);
313 0 : ss->rsh = TALER_EXCHANGE_reserves_status (is->exchange,
314 : ss->reserve_priv,
315 : &reserve_status_cb,
316 : ss);
317 : }
318 :
319 :
320 : /**
321 : * Cleanup the state from a "reserve status" CMD, and possibly
322 : * cancel a pending operation thereof.
323 : *
324 : * @param cls closure.
325 : * @param cmd the command which is being cleaned up.
326 : */
327 : static void
328 0 : status_cleanup (void *cls,
329 : const struct TALER_TESTING_Command *cmd)
330 : {
331 0 : struct StatusState *ss = cls;
332 :
333 0 : if (NULL != ss->rsh)
334 : {
335 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
336 : "Command %u (%s) did not complete\n",
337 : ss->is->ip,
338 : cmd->label);
339 0 : TALER_EXCHANGE_reserves_status_cancel (ss->rsh);
340 0 : ss->rsh = NULL;
341 : }
342 0 : GNUNET_free (ss);
343 0 : }
344 :
345 :
346 : struct TALER_TESTING_Command
347 0 : TALER_TESTING_cmd_reserve_status (const char *label,
348 : const char *reserve_reference,
349 : const char *expected_balance,
350 : unsigned int expected_response_code)
351 : {
352 : struct StatusState *ss;
353 :
354 0 : GNUNET_assert (NULL != reserve_reference);
355 0 : ss = GNUNET_new (struct StatusState);
356 0 : ss->reserve_reference = reserve_reference;
357 0 : ss->expected_balance = expected_balance;
358 0 : ss->expected_response_code = expected_response_code;
359 : {
360 0 : struct TALER_TESTING_Command cmd = {
361 : .cls = ss,
362 : .label = label,
363 : .run = &status_run,
364 : .cleanup = &status_cleanup
365 : };
366 :
367 0 : return cmd;
368 : }
369 : }
|