testing_api_cmd_abort_order.c (11815B)
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 build_coins (struct TALER_MERCHANT_PostOrdersAbortCoin **ac, 93 unsigned int *nac, 94 char *coins, 95 struct TALER_TESTING_Interpreter *is) 96 { 97 for (char *token = strtok (coins, ";"); 98 NULL != token; 99 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 ctok = strchr (token, '/'); 107 ci = 0; 108 if (NULL != ctok) 109 { 110 *ctok = '\0'; 111 ctok++; 112 if (1 != sscanf (ctok, 113 "%u", 114 &ci)) 115 { 116 GNUNET_break (0); 117 return GNUNET_SYSERR; 118 } 119 } 120 { 121 const struct TALER_TESTING_Command *coin_cmd; 122 123 coin_cmd = TALER_TESTING_interpreter_lookup_command (is, 124 token); 125 if (NULL == coin_cmd) 126 { 127 GNUNET_break (0); 128 return GNUNET_SYSERR; 129 } 130 GNUNET_array_grow (*ac, 131 *nac, 132 (*nac) + 1); 133 icoin = &((*ac)[(*nac) - 1]); 134 135 { 136 const struct TALER_CoinSpendPrivateKeyP *coin_priv; 137 138 GNUNET_assert (GNUNET_OK == 139 TALER_TESTING_get_trait_coin_priv (coin_cmd, 140 ci, 141 &coin_priv)); 142 GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, 143 &icoin->coin_pub.eddsa_pub); 144 } 145 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 GNUNET_assert (GNUNET_OK == 153 TALER_TESTING_get_trait_amount (coin_cmd, 154 &denom_value)); 155 icoin->amount_with_fee = *denom_value; 156 } 157 158 } 159 } 160 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 abort_cb (struct AbortState *as, 174 const struct TALER_MERCHANT_PostOrdersAbortResponse *ar) 175 { 176 177 as->oah = NULL; 178 if (as->http_status != ar->hr.http_status) 179 { 180 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 TALER_TESTING_FAIL (as->is); 187 } 188 if ( (MHD_HTTP_OK == ar->hr.http_status) && 189 (TALER_EC_NONE == ar->hr.ec) ) 190 { 191 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 192 "Received %u refunds\n", 193 ar->details.ok.num_aborts); 194 as->acs_length = ar->details.ok.num_aborts; 195 as->acs = GNUNET_new_array (as->acs_length, 196 struct TALER_MERCHANT_PostOrdersAbortedCoin); 197 GNUNET_memcpy (as->acs, 198 ar->details.ok.aborts, 199 as->acs_length 200 * sizeof (struct TALER_MERCHANT_PostOrdersAbortedCoin)); 201 } 202 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 203 "Successful pay-abort (HTTP status: %u)\n", 204 ar->hr.http_status); 205 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 abort_run (void *cls, 218 const struct TALER_TESTING_Command *cmd, 219 struct TALER_TESTING_Interpreter *is) 220 { 221 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 as->is = is; 237 pay_cmd = TALER_TESTING_interpreter_lookup_command (is, 238 as->pay_reference); 239 if (NULL == pay_cmd) 240 TALER_TESTING_FAIL (is); 241 if (GNUNET_OK != 242 TALER_TESTING_get_trait_proposal_reference (pay_cmd, 243 &proposal_reference)) 244 TALER_TESTING_FAIL (is); 245 if (GNUNET_OK != 246 TALER_TESTING_get_trait_coin_reference (pay_cmd, 247 0, 248 &coin_reference)) 249 TALER_TESTING_FAIL (is); 250 proposal_cmd = TALER_TESTING_interpreter_lookup_command (is, 251 proposal_reference); 252 253 if (NULL == proposal_cmd) 254 TALER_TESTING_FAIL (is); 255 256 { 257 const json_t *contract_terms; 258 259 if (GNUNET_OK != 260 TALER_TESTING_get_trait_contract_terms (proposal_cmd, 261 &contract_terms)) 262 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 GNUNET_JSON_spec_string ("order_id", 268 &order_id), 269 GNUNET_JSON_spec_fixed_auto ("merchant_pub", 270 &merchant_pub), 271 TALER_JSON_spec_amount_any ("amount", 272 &total_amount), 273 GNUNET_JSON_spec_end () 274 }; 275 276 if (GNUNET_OK != 277 GNUNET_JSON_parse (contract_terms, 278 spec, 279 &error_name, 280 &error_line)) 281 { 282 char *js; 283 284 js = json_dumps (contract_terms, 285 JSON_INDENT (1)); 286 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 free (js); 292 TALER_TESTING_FAIL (is); 293 } 294 } 295 } 296 297 cr = GNUNET_strdup (coin_reference); 298 abort_coins = NULL; 299 nabort_coins = 0; 300 if (GNUNET_OK != 301 build_coins (&abort_coins, 302 &nabort_coins, 303 cr, 304 is)) 305 { 306 GNUNET_array_grow (abort_coins, 307 nabort_coins, 308 0); 309 GNUNET_free (cr); 310 TALER_TESTING_FAIL (is); 311 } 312 GNUNET_free (cr); 313 314 if (GNUNET_OK != 315 TALER_TESTING_get_trait_h_contract_terms (proposal_cmd, 316 &h_proposal)) 317 TALER_TESTING_FAIL (is); 318 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 GNUNET_array_grow (abort_coins, 327 nabort_coins, 328 0); 329 { 330 enum TALER_ErrorCode ec; 331 332 ec = TALER_MERCHANT_post_orders_abort_start ( 333 as->oah, 334 &abort_cb, 335 as); 336 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 abort_cleanup (void *cls, 349 const struct TALER_TESTING_Command *cmd) 350 { 351 struct AbortState *as = cls; 352 353 if (NULL != as->oah) 354 { 355 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 356 "Command `%s' did not complete.\n", 357 TALER_TESTING_interpreter_get_current_label ( 358 as->is)); 359 TALER_MERCHANT_post_orders_abort_cancel (as->oah); 360 } 361 GNUNET_array_grow (as->acs, 362 as->acs_length, 363 0); 364 GNUNET_free (as); 365 } 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 abort_traits (void *cls, 379 const void **ret, 380 const char *trait, 381 unsigned int index) 382 { 383 struct AbortState *as = cls; 384 struct TALER_TESTING_Trait traits[] = { 385 TALER_TESTING_trait_end () 386 }; 387 388 (void) as; 389 return TALER_TESTING_get_trait (traits, 390 ret, 391 trait, 392 index); 393 } 394 395 396 struct TALER_TESTING_Command 397 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 as = GNUNET_new (struct AbortState); 405 as->http_status = http_status; 406 as->pay_reference = pay_reference; 407 as->merchant_url = merchant_url; 408 { 409 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 return cmd; 418 } 419 } 420 421 422 /* end of testing_api_cmd_abort_order.c */