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