Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2023 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 src/testing/testing_api_cmd_abort_order.c
21 : * @brief command to test the abort feature.
22 : * @author Marcello Stanisci
23 : */
24 : #include "platform.h"
25 : struct AbortState;
26 : #define TALER_MERCHANT_POST_ORDERS_ABORT_RESULT_CLOSURE struct AbortState
27 : #include <taler/taler_exchange_service.h>
28 : #include <taler/taler_testing_lib.h>
29 : #include <taler/taler_signatures.h>
30 : #include "taler/taler_merchant_service.h"
31 : #include "taler/taler_merchant_testing_lib.h"
32 : #include <taler/merchant/post-orders-ORDER_ID-abort.h>
33 :
34 : /**
35 : * State for a " abort" CMD.
36 : */
37 : struct AbortState
38 : {
39 :
40 : /**
41 : * Reference to the "pay" command to abort.
42 : */
43 : const char *pay_reference;
44 :
45 : /**
46 : * Merchant URL.
47 : */
48 : const char *merchant_url;
49 :
50 : /**
51 : * Handle to a "abort" operation.
52 : */
53 : struct TALER_MERCHANT_PostOrdersAbortHandle *oah;
54 :
55 : /**
56 : * Interpreter state.
57 : */
58 : struct TALER_TESTING_Interpreter *is;
59 :
60 : /**
61 : * The actual abort/refund data.
62 : */
63 : struct TALER_MERCHANT_PostOrdersAbortedCoin *acs;
64 :
65 : /**
66 : * Expected HTTP response code.
67 : */
68 : unsigned int http_status;
69 :
70 : /**
71 : * How many refund permissions this CMD got
72 : * the right for. Roughly, there is one refund
73 : * permission for one coin.
74 : */
75 : unsigned int acs_length;
76 :
77 : };
78 :
79 :
80 : /**
81 : * Parse the @a coins specification and grow the @a ac
82 : * array with the coins found, updating @a nac.
83 : *
84 : * @param[in,out] ac pointer to array of coins found
85 : * @param[in,out] nac length of array at @a pc
86 : * @param[in] coins string specifying coins to add to @a pc,
87 : * clobbered in the process
88 : * @param is interpreter state
89 : * @return #GNUNET_OK on success
90 : */
91 : static enum GNUNET_GenericReturnValue
92 2 : build_coins (struct TALER_MERCHANT_PostOrdersAbortCoin **ac,
93 : unsigned int *nac,
94 : char *coins,
95 : struct TALER_TESTING_Interpreter *is)
96 : {
97 2 : for (char *token = strtok (coins, ";");
98 4 : NULL != token;
99 2 : token = strtok (NULL, ";"))
100 : {
101 : char *ctok;
102 : unsigned int ci;
103 : struct TALER_MERCHANT_PostOrdersAbortCoin *icoin;
104 :
105 : /* Token syntax is "LABEL[/NUMBER]" */
106 2 : ctok = strchr (token, '/');
107 2 : ci = 0;
108 2 : if (NULL != ctok)
109 : {
110 0 : *ctok = '\0';
111 0 : ctok++;
112 0 : if (1 != sscanf (ctok,
113 : "%u",
114 : &ci))
115 : {
116 0 : GNUNET_break (0);
117 0 : return GNUNET_SYSERR;
118 : }
119 : }
120 : {
121 : const struct TALER_TESTING_Command *coin_cmd;
122 :
123 2 : coin_cmd = TALER_TESTING_interpreter_lookup_command (is,
124 : token);
125 2 : if (NULL == coin_cmd)
126 : {
127 0 : GNUNET_break (0);
128 0 : return GNUNET_SYSERR;
129 : }
130 2 : GNUNET_array_grow (*ac,
131 : *nac,
132 : (*nac) + 1);
133 2 : icoin = &((*ac)[(*nac) - 1]);
134 :
135 : {
136 : const struct TALER_CoinSpendPrivateKeyP *coin_priv;
137 :
138 2 : GNUNET_assert (GNUNET_OK ==
139 : TALER_TESTING_get_trait_coin_priv (coin_cmd,
140 : ci,
141 : &coin_priv));
142 2 : GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
143 : &icoin->coin_pub.eddsa_pub);
144 : }
145 2 : GNUNET_assert (GNUNET_OK ==
146 : TALER_TESTING_get_trait_exchange_url (coin_cmd,
147 : &icoin->exchange_url)
148 : );
149 : {
150 : const struct TALER_Amount *denom_value;
151 :
152 2 : GNUNET_assert (GNUNET_OK ==
153 : TALER_TESTING_get_trait_amount (coin_cmd,
154 : &denom_value));
155 2 : icoin->amount_with_fee = *denom_value;
156 : }
157 :
158 : }
159 : }
160 2 : return GNUNET_OK;
161 : }
162 :
163 :
164 : /**
165 : * Callback for a "pay abort" operation. Mainly, check HTTP
166 : * response code was as expected and stores refund permissions
167 : * in the state.
168 : *
169 : * @param cls closure.
170 : * @param ar response
171 : */
172 : static void
173 2 : abort_cb (struct AbortState *as,
174 : const struct TALER_MERCHANT_PostOrdersAbortResponse *ar)
175 : {
176 :
177 2 : as->oah = NULL;
178 2 : if (as->http_status != ar->hr.http_status)
179 : {
180 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
181 : "Unexpected response code %u (%d) to command `%s' (expected %u)\n",
182 : ar->hr.http_status,
183 : (int) ar->hr.ec,
184 : TALER_TESTING_interpreter_get_current_label (as->is),
185 : as->http_status);
186 0 : TALER_TESTING_FAIL (as->is);
187 : }
188 2 : if ( (MHD_HTTP_OK == ar->hr.http_status) &&
189 2 : (TALER_EC_NONE == ar->hr.ec) )
190 : {
191 2 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
192 : "Received %u refunds\n",
193 : ar->details.ok.num_aborts);
194 2 : as->acs_length = ar->details.ok.num_aborts;
195 2 : as->acs = GNUNET_new_array (as->acs_length,
196 : struct TALER_MERCHANT_PostOrdersAbortedCoin);
197 2 : GNUNET_memcpy (as->acs,
198 : ar->details.ok.aborts,
199 : as->acs_length
200 : * sizeof (struct TALER_MERCHANT_PostOrdersAbortedCoin));
201 : }
202 2 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
203 : "Successful pay-abort (HTTP status: %u)\n",
204 : ar->hr.http_status);
205 2 : TALER_TESTING_interpreter_next (as->is);
206 : }
207 :
208 :
209 : /**
210 : * Run an "abort" CMD.
211 : *
212 : * @param cls closure
213 : * @param cmd command being run.
214 : * @param is interpreter state
215 : */
216 : static void
217 2 : abort_run (void *cls,
218 : const struct TALER_TESTING_Command *cmd,
219 : struct TALER_TESTING_Interpreter *is)
220 : {
221 2 : struct AbortState *as = cls;
222 : const struct TALER_TESTING_Command *pay_cmd;
223 : const char *proposal_reference;
224 : const char *coin_reference;
225 : const struct TALER_TESTING_Command *proposal_cmd;
226 : const char *order_id;
227 : const struct TALER_PrivateContractHashP *h_proposal;
228 : struct TALER_MerchantPublicKeyP merchant_pub;
229 : struct TALER_Amount total_amount;
230 : const char *error_name;
231 : unsigned int error_line;
232 : struct TALER_MERCHANT_PostOrdersAbortCoin *abort_coins;
233 : unsigned int nabort_coins;
234 : char *cr;
235 :
236 2 : as->is = is;
237 2 : pay_cmd = TALER_TESTING_interpreter_lookup_command (is,
238 : as->pay_reference);
239 2 : if (NULL == pay_cmd)
240 0 : TALER_TESTING_FAIL (is);
241 2 : if (GNUNET_OK !=
242 2 : TALER_TESTING_get_trait_proposal_reference (pay_cmd,
243 : &proposal_reference))
244 0 : TALER_TESTING_FAIL (is);
245 2 : if (GNUNET_OK !=
246 2 : TALER_TESTING_get_trait_coin_reference (pay_cmd,
247 : 0,
248 : &coin_reference))
249 0 : TALER_TESTING_FAIL (is);
250 2 : proposal_cmd = TALER_TESTING_interpreter_lookup_command (is,
251 : proposal_reference);
252 :
253 2 : if (NULL == proposal_cmd)
254 0 : TALER_TESTING_FAIL (is);
255 :
256 : {
257 : const json_t *contract_terms;
258 :
259 2 : if (GNUNET_OK !=
260 2 : TALER_TESTING_get_trait_contract_terms (proposal_cmd,
261 : &contract_terms))
262 0 : TALER_TESTING_FAIL (is);
263 : {
264 : /* Get information that needs to be put verbatim in the
265 : * deposit permission */
266 : struct GNUNET_JSON_Specification spec[] = {
267 2 : TALER_JSON_spec_slug ("order_id",
268 : &order_id),
269 2 : GNUNET_JSON_spec_fixed_auto ("merchant_pub",
270 : &merchant_pub),
271 2 : TALER_JSON_spec_amount_any ("amount",
272 : &total_amount),
273 2 : GNUNET_JSON_spec_end ()
274 : };
275 :
276 2 : if (GNUNET_OK !=
277 2 : GNUNET_JSON_parse (contract_terms,
278 : spec,
279 : &error_name,
280 : &error_line))
281 : {
282 : char *js;
283 :
284 0 : js = json_dumps (contract_terms,
285 : JSON_INDENT (1));
286 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
287 : "Parser failed on %s:%u for input `%s'\n",
288 : error_name,
289 : error_line,
290 : js);
291 0 : free (js);
292 0 : TALER_TESTING_FAIL (is);
293 : }
294 : }
295 : }
296 :
297 2 : cr = GNUNET_strdup (coin_reference);
298 2 : abort_coins = NULL;
299 2 : nabort_coins = 0;
300 2 : if (GNUNET_OK !=
301 2 : build_coins (&abort_coins,
302 : &nabort_coins,
303 : cr,
304 : is))
305 : {
306 0 : GNUNET_array_grow (abort_coins,
307 : nabort_coins,
308 : 0);
309 0 : GNUNET_free (cr);
310 0 : TALER_TESTING_FAIL (is);
311 : }
312 2 : GNUNET_free (cr);
313 :
314 2 : if (GNUNET_OK !=
315 2 : TALER_TESTING_get_trait_h_contract_terms (proposal_cmd,
316 : &h_proposal))
317 0 : TALER_TESTING_FAIL (is);
318 2 : as->oah = TALER_MERCHANT_post_orders_abort_create (
319 : TALER_TESTING_interpreter_get_context (is),
320 : as->merchant_url,
321 : order_id,
322 : &merchant_pub,
323 : h_proposal,
324 : nabort_coins,
325 : abort_coins);
326 2 : GNUNET_array_grow (abort_coins,
327 : nabort_coins,
328 : 0);
329 : {
330 : enum TALER_ErrorCode ec;
331 :
332 2 : ec = TALER_MERCHANT_post_orders_abort_start (
333 : as->oah,
334 : &abort_cb,
335 : as);
336 2 : GNUNET_assert (TALER_EC_NONE == ec);
337 : }
338 : }
339 :
340 :
341 : /**
342 : * Free a "pay abort" CMD, and cancel it if need be.
343 : *
344 : * @param cls closure.
345 : * @param cmd command currently being freed.
346 : */
347 : static void
348 2 : abort_cleanup (void *cls,
349 : const struct TALER_TESTING_Command *cmd)
350 : {
351 2 : struct AbortState *as = cls;
352 :
353 2 : if (NULL != as->oah)
354 : {
355 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
356 : "Command `%s' did not complete.\n",
357 : TALER_TESTING_interpreter_get_current_label (
358 : as->is));
359 0 : TALER_MERCHANT_post_orders_abort_cancel (as->oah);
360 : }
361 2 : GNUNET_array_grow (as->acs,
362 : as->acs_length,
363 : 0);
364 2 : GNUNET_free (as);
365 2 : }
366 :
367 :
368 : /**
369 : * Offer internal data useful to other commands.
370 : *
371 : * @param cls closure
372 : * @param[out] ret result (could be anything)
373 : * @param trait name of the trait
374 : * @param index index number of the object to extract.
375 : * @return #GNUNET_OK on success
376 : */
377 : static int
378 0 : abort_traits (void *cls,
379 : const void **ret,
380 : const char *trait,
381 : unsigned int index)
382 : {
383 0 : struct AbortState *as = cls;
384 : struct TALER_TESTING_Trait traits[] = {
385 0 : TALER_TESTING_trait_end ()
386 : };
387 :
388 : (void) as;
389 0 : return TALER_TESTING_get_trait (traits,
390 : ret,
391 : trait,
392 : index);
393 : }
394 :
395 :
396 : struct TALER_TESTING_Command
397 2 : TALER_TESTING_cmd_merchant_order_abort (const char *label,
398 : const char *merchant_url,
399 : const char *pay_reference,
400 : unsigned int http_status)
401 : {
402 : struct AbortState *as;
403 :
404 2 : as = GNUNET_new (struct AbortState);
405 2 : as->http_status = http_status;
406 2 : as->pay_reference = pay_reference;
407 2 : as->merchant_url = merchant_url;
408 : {
409 2 : struct TALER_TESTING_Command cmd = {
410 : .cls = as,
411 : .label = label,
412 : .run = &abort_run,
413 : .cleanup = &abort_cleanup,
414 : .traits = &abort_traits
415 : };
416 :
417 2 : return cmd;
418 : }
419 : }
420 :
421 :
422 : /* end of testing_api_cmd_abort_order.c */
|