testing_api_cmd_wallet_get_template.c (13877B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2025 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file src/testing/testing_api_cmd_wallet_get_template.c 18 * @brief command to test GET /templates/$ID (wallet) 19 * @author Bohdan Potuzhnyi 20 */ 21 #include "platform.h" 22 struct WalletGetTemplateState; 23 #define TALER_MERCHANT_GET_TEMPLATES_RESULT_CLOSURE struct WalletGetTemplateState 24 #include <microhttpd.h> 25 #include <jansson.h> 26 #include <taler/taler_testing_lib.h> 27 #include "taler/taler_merchant_service.h" 28 #include "taler/taler_merchant_testing_lib.h" 29 #include <taler/merchant/get-templates-TEMPLATE_ID.h> 30 31 /** 32 * State of a "GET template" wallet CMD. 33 */ 34 struct WalletGetTemplateState 35 { 36 /** 37 * Handle for a "GET template" request. 38 */ 39 struct TALER_MERCHANT_GetTemplatesHandle *igh; 40 41 /** 42 * The interpreter state. 43 */ 44 struct TALER_TESTING_Interpreter *is; 45 46 /** 47 * Base URL of the merchant serving the request. 48 */ 49 const char *merchant_url; 50 51 /** 52 * ID of the template to run GET for. 53 */ 54 const char *template_id; 55 56 /** 57 * Expected product count (0 to ignore). 58 */ 59 size_t expected_products_len; 60 61 /** 62 * Product id to verify unit info for (optional). 63 */ 64 const char *expected_product_id; 65 66 /** 67 * Expected unit for @e expected_product_id. 68 */ 69 const char *expected_unit; 70 71 /** 72 * Expected allow_fraction for @e expected_product_id. 73 */ 74 bool expected_unit_allow_fraction; 75 76 /** 77 * Expected precision for @e expected_product_id. 78 */ 79 uint32_t expected_unit_precision_level; 80 81 /** 82 * Expected unit name short i18n for @e expected_unit. 83 */ 84 json_t *expected_unit_name_short_i18n; 85 86 /** 87 * Optional second product id expected to be present. 88 */ 89 const char *expected_product_id2; 90 91 /** 92 * Optional third product id expected to be present. 93 */ 94 const char *expected_product_id3; 95 96 /** 97 * Expected category id 1 (0 to ignore). 98 */ 99 uint64_t expected_category_id1; 100 101 /** 102 * Expected category id 2 (0 to ignore). 103 */ 104 uint64_t expected_category_id2; 105 106 /** 107 * Expected HTTP response code. 108 */ 109 unsigned int http_status; 110 }; 111 112 static bool 113 product_id_matches (const json_t *product, 114 const char *expected_id) 115 { 116 const json_t *id_val; 117 118 if (NULL == expected_id) 119 return false; 120 id_val = json_object_get (product, 121 "product_id"); 122 if (! json_is_string (id_val)) 123 return false; 124 return (0 == strcmp (json_string_value (id_val), 125 expected_id)); 126 } 127 128 129 /** 130 * Callback for a wallet /get/templates/$ID operation. 131 * 132 * @param cls closure for this function 133 * @param tgr HTTP response details 134 */ 135 static void 136 wallet_get_template_cb (struct WalletGetTemplateState *wgs, 137 const struct 138 TALER_MERCHANT_GetTemplatesResponse *tgr) 139 { 140 141 wgs->igh = NULL; 142 if (wgs->http_status != tgr->hr.http_status) 143 { 144 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 145 "Unexpected response code %u (%d) to command %s\n", 146 tgr->hr.http_status, 147 (int) tgr->hr.ec, 148 TALER_TESTING_interpreter_get_current_label (wgs->is)); 149 TALER_TESTING_interpreter_fail (wgs->is); 150 return; 151 } 152 if (MHD_HTTP_OK == tgr->hr.http_status) 153 { 154 const json_t *template_contract = tgr->details.ok.template_contract; 155 const json_t *inventory_payload; 156 const json_t *products; 157 158 inventory_payload = json_object_get (template_contract, 159 "inventory_payload"); 160 if (! json_is_object (inventory_payload)) 161 { 162 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 163 "Missing inventory_payload in wallet template\n"); 164 TALER_TESTING_interpreter_fail (wgs->is); 165 return; 166 } 167 products = json_object_get (inventory_payload, 168 "products"); 169 if (! json_is_array (products)) 170 { 171 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 172 "Missing products in wallet template\n"); 173 TALER_TESTING_interpreter_fail (wgs->is); 174 return; 175 } 176 if ( (0 < wgs->expected_products_len) && 177 (json_array_size (products) != wgs->expected_products_len) ) 178 { 179 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 180 "Unexpected products length\n"); 181 TALER_TESTING_interpreter_fail (wgs->is); 182 return; 183 } 184 185 { 186 bool found_unit = (NULL == wgs->expected_product_id); 187 bool found_extra = (NULL == wgs->expected_product_id2); 188 bool found_extra2 = (NULL == wgs->expected_product_id3); 189 190 for (size_t i = 0; i < json_array_size (products); i++) 191 { 192 const json_t *product = json_array_get (products, 193 i); 194 195 if (product_id_matches (product, 196 wgs->expected_product_id)) 197 { 198 const json_t *unit_val; 199 const json_t *allow_val; 200 const json_t *prec_val; 201 202 unit_val = json_object_get (product, 203 "unit"); 204 allow_val = json_object_get (product, 205 "unit_allow_fraction"); 206 prec_val = json_object_get (product, 207 "unit_precision_level"); 208 if ( (NULL == unit_val) || 209 (! json_is_string (unit_val)) || 210 (0 != strcmp (json_string_value (unit_val), 211 wgs->expected_unit)) ) 212 { 213 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 214 "Unexpected unit in wallet template\n"); 215 TALER_TESTING_interpreter_fail (wgs->is); 216 return; 217 } 218 if ( (! json_is_boolean (allow_val)) || 219 (json_boolean_value (allow_val) != 220 wgs->expected_unit_allow_fraction) ) 221 { 222 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 223 "Unexpected unit_allow_fraction in wallet template\n"); 224 TALER_TESTING_interpreter_fail (wgs->is); 225 return; 226 } 227 if ( (! json_is_integer (prec_val)) || 228 ((uint32_t) json_integer_value (prec_val) != 229 wgs->expected_unit_precision_level) ) 230 { 231 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 232 "Unexpected unit_precision_level in wallet template\n"); 233 TALER_TESTING_interpreter_fail (wgs->is); 234 return; 235 } 236 found_unit = true; 237 } 238 if (product_id_matches (product, 239 wgs->expected_product_id2)) 240 found_extra = true; 241 if (product_id_matches (product, 242 wgs->expected_product_id3)) 243 found_extra2 = true; 244 } 245 if (! found_unit || ! found_extra || ! found_extra2) 246 { 247 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 248 "Expected product ids missing in wallet template\n"); 249 TALER_TESTING_interpreter_fail (wgs->is); 250 return; 251 } 252 } 253 254 if (NULL != wgs->expected_unit_name_short_i18n) 255 { 256 const json_t *units; 257 bool found_unit_i18n = false; 258 259 units = json_object_get (inventory_payload, 260 "units"); 261 if (! json_is_array (units)) 262 { 263 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 264 "Missing units in wallet template\n"); 265 TALER_TESTING_interpreter_fail (wgs->is); 266 return; 267 } 268 for (size_t i = 0; i < json_array_size (units); i++) 269 { 270 const json_t *unit = json_array_get (units, 271 i); 272 const json_t *unit_id; 273 const json_t *unit_i18n; 274 275 unit_id = json_object_get (unit, 276 "unit"); 277 if (! json_is_string (unit_id)) 278 continue; 279 if (0 != strcmp (json_string_value (unit_id), 280 wgs->expected_unit)) 281 continue; 282 unit_i18n = json_object_get (unit, 283 "unit_name_short_i18n"); 284 if ( (NULL == unit_i18n) || 285 (! json_is_object (unit_i18n)) || 286 (1 != json_equal (unit_i18n, 287 wgs->expected_unit_name_short_i18n)) ) 288 { 289 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 290 "Unexpected unit_name_short_i18n in wallet template\n"); 291 TALER_TESTING_interpreter_fail (wgs->is); 292 return; 293 } 294 found_unit_i18n = true; 295 break; 296 } 297 if (! found_unit_i18n) 298 { 299 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 300 "Expected unit entry missing in wallet template\n"); 301 TALER_TESTING_interpreter_fail (wgs->is); 302 return; 303 } 304 } 305 306 if ( (0 != wgs->expected_category_id1) || 307 (0 != wgs->expected_category_id2) ) 308 { 309 const json_t *categories; 310 bool found_cat1 = (0 == wgs->expected_category_id1); 311 bool found_cat2 = (0 == wgs->expected_category_id2); 312 313 categories = json_object_get (inventory_payload, 314 "categories"); 315 if (! json_is_array (categories)) 316 { 317 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 318 "Missing categories in wallet template\n"); 319 TALER_TESTING_interpreter_fail (wgs->is); 320 return; 321 } 322 for (size_t i = 0; i < json_array_size (categories); i++) 323 { 324 const json_t *category = json_array_get (categories, 325 i); 326 const json_t *cid; 327 328 cid = json_object_get (category, 329 "category_id"); 330 if (! json_is_integer (cid)) 331 continue; 332 if ( (0 != wgs->expected_category_id1) && 333 ((uint64_t) json_integer_value (cid) 334 == wgs->expected_category_id1) ) 335 found_cat1 = true; 336 if ( (0 != wgs->expected_category_id2) && 337 ((uint64_t) json_integer_value (cid) 338 == wgs->expected_category_id2) ) 339 found_cat2 = true; 340 } 341 if (! found_cat1 || ! found_cat2) 342 { 343 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 344 "Expected category ids missing in wallet template\n"); 345 TALER_TESTING_interpreter_fail (wgs->is); 346 return; 347 } 348 } 349 } 350 TALER_TESTING_interpreter_next (wgs->is); 351 } 352 353 354 /** 355 * Run the "GET /templates/$ID" wallet CMD. 356 * 357 * @param cls closure. 358 * @param cmd command being run now. 359 * @param is interpreter state. 360 */ 361 static void 362 wallet_get_template_run (void *cls, 363 const struct TALER_TESTING_Command *cmd, 364 struct TALER_TESTING_Interpreter *is) 365 { 366 struct WalletGetTemplateState *wgs = cls; 367 368 wgs->is = is; 369 wgs->igh = TALER_MERCHANT_get_templates_create ( 370 TALER_TESTING_interpreter_get_context (is), 371 wgs->merchant_url, 372 wgs->template_id); 373 { 374 enum TALER_ErrorCode ec; 375 376 ec = TALER_MERCHANT_get_templates_start ( 377 wgs->igh, 378 &wallet_get_template_cb, 379 wgs); 380 GNUNET_assert (TALER_EC_NONE == ec); 381 } 382 } 383 384 385 /** 386 * Free the state of a "GET template" CMD, and possibly 387 * cancel a pending operation thereof. 388 * 389 * @param cls closure. 390 * @param cmd command being run. 391 */ 392 static void 393 wallet_get_template_cleanup (void *cls, 394 const struct TALER_TESTING_Command *cmd) 395 { 396 struct WalletGetTemplateState *wgs = cls; 397 398 if (NULL != wgs->igh) 399 { 400 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 401 "GET /templates/$ID operation did not complete\n"); 402 TALER_MERCHANT_get_templates_cancel (wgs->igh); 403 } 404 json_decref (wgs->expected_unit_name_short_i18n); 405 GNUNET_free (wgs); 406 } 407 408 409 struct TALER_TESTING_Command 410 TALER_TESTING_cmd_merchant_wallet_get_template ( 411 const char *label, 412 const char *merchant_url, 413 const char *template_id, 414 size_t expected_products_len, 415 const char *expected_product_id, 416 const char *expected_unit, 417 bool expected_unit_allow_fraction, 418 uint32_t expected_unit_precision_level, 419 json_t *expected_unit_name_short_i18n, 420 const char *expected_product_id2, 421 const char *expected_product_id3, 422 uint64_t expected_category_id1, 423 uint64_t expected_category_id2, 424 unsigned int http_status) 425 { 426 struct WalletGetTemplateState *wgs; 427 428 wgs = GNUNET_new (struct WalletGetTemplateState); 429 wgs->merchant_url = merchant_url; 430 wgs->template_id = template_id; 431 wgs->expected_products_len = expected_products_len; 432 wgs->expected_product_id = expected_product_id; 433 wgs->expected_unit = expected_unit; 434 wgs->expected_unit_allow_fraction = expected_unit_allow_fraction; 435 wgs->expected_unit_precision_level = expected_unit_precision_level; 436 if (NULL != expected_unit_name_short_i18n) 437 wgs->expected_unit_name_short_i18n = 438 json_incref (expected_unit_name_short_i18n); 439 wgs->expected_product_id2 = expected_product_id2; 440 wgs->expected_product_id3 = expected_product_id3; 441 wgs->expected_category_id1 = expected_category_id1; 442 wgs->expected_category_id2 = expected_category_id2; 443 wgs->http_status = http_status; 444 { 445 struct TALER_TESTING_Command cmd = { 446 .cls = wgs, 447 .label = label, 448 .run = &wallet_get_template_run, 449 .cleanup = &wallet_get_template_cleanup 450 }; 451 452 return cmd; 453 } 454 } 455 456 457 /* end of testing_api_cmd_wallet_get_template.c */