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