testing_api_cmd_get_product.c (14486B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2020-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 testing_api_cmd_get_product.c 21 * @brief command to test GET /product/$ID 22 * @author Christian Grothoff 23 */ 24 #include "taler/platform.h" 25 #include <taler/taler_exchange_service.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/taler-merchant/get-private-products-PRODUCT_ID.h> 30 31 32 /** 33 * State of a "GET product" CMD. 34 */ 35 struct GetProductState 36 { 37 38 /** 39 * Handle for a "GET product" request. 40 */ 41 struct TALER_MERCHANT_GetPrivateProductHandle *igh; 42 43 /** 44 * The interpreter state. 45 */ 46 struct TALER_TESTING_Interpreter *is; 47 48 /** 49 * Base URL of the merchant serving the request. 50 */ 51 const char *merchant_url; 52 53 /** 54 * ID of the product to run GET for. 55 */ 56 const char *product_id; 57 58 /** 59 * Reference for a POST or PATCH /products CMD (optional). 60 */ 61 const char *product_reference; 62 63 /** 64 * Expected HTTP response code. 65 */ 66 unsigned int http_status; 67 68 /** 69 * Optional overrides for fractional fields. 70 */ 71 const struct TALER_TESTING_ProductUnitExpectations *unit_expectations; 72 73 }; 74 75 76 /** 77 * Callback for a /get/product/$ID operation. 78 * 79 * @param cls closure for this function 80 * @param pgr response details 81 */ 82 static void 83 get_product_cb (void *cls, 84 const struct TALER_MERCHANT_GetPrivateProductResponse *pgr) 85 { 86 struct GetProductState *gis = cls; 87 const struct TALER_TESTING_Command *product_cmd; 88 const struct TALER_TESTING_ProductUnitExpectations *ue = 89 gis->unit_expectations; 90 91 gis->igh = NULL; 92 if (gis->http_status != pgr->hr.http_status) 93 { 94 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 95 "Unexpected response code %u (%d) to command %s\n", 96 pgr->hr.http_status, 97 (int) pgr->hr.ec, 98 TALER_TESTING_interpreter_get_current_label (gis->is)); 99 TALER_TESTING_interpreter_fail (gis->is); 100 return; 101 } 102 switch (pgr->hr.http_status) 103 { 104 case MHD_HTTP_OK: 105 { 106 const char *expected_description; 107 108 product_cmd = TALER_TESTING_interpreter_lookup_command ( 109 gis->is, 110 gis->product_reference); 111 if (GNUNET_OK != 112 TALER_TESTING_get_trait_product_description (product_cmd, 113 &expected_description)) 114 TALER_TESTING_interpreter_fail (gis->is); 115 if (0 != strcmp (pgr->details.ok.description, 116 expected_description)) 117 { 118 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 119 "Product description does not match\n"); 120 TALER_TESTING_interpreter_fail (gis->is); 121 return; 122 } 123 } 124 { 125 const json_t *expected_description_i18n; 126 127 if (GNUNET_OK != 128 TALER_TESTING_get_trait_i18n_description (product_cmd, 129 &expected_description_i18n)) 130 TALER_TESTING_interpreter_fail (gis->is); 131 if (1 != json_equal (pgr->details.ok.description_i18n, 132 expected_description_i18n)) 133 { 134 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 135 "Product description i18n does not match\n"); 136 TALER_TESTING_interpreter_fail (gis->is); 137 return; 138 } 139 } 140 { 141 const struct TALER_Amount *expected_price; 142 143 if (GNUNET_OK != 144 TALER_TESTING_get_trait_amount (product_cmd, 145 &expected_price)) 146 TALER_TESTING_interpreter_fail (gis->is); 147 if ((GNUNET_OK != 148 TALER_amount_cmp_currency (&pgr->details.ok.price, 149 expected_price)) || 150 (0 != TALER_amount_cmp (&pgr->details.ok.price, 151 expected_price))) 152 { 153 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 154 "Product price does not match\n"); 155 TALER_TESTING_interpreter_fail (gis->is); 156 return; 157 } 158 } 159 { 160 const bool *expected_allow; 161 bool have_allow = GNUNET_OK == 162 TALER_TESTING_get_trait_product_unit_allow_fraction ( 163 product_cmd, 164 &expected_allow); 165 bool override_allow = (NULL != ue) && 166 ue->have_unit_allow_fraction; 167 168 if (override_allow) 169 { 170 if (pgr->details.ok.unit_allow_fraction != 171 ue->unit_allow_fraction) 172 { 173 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 174 "Product fractional flag does not match expectation\n"); 175 TALER_TESTING_interpreter_fail (gis->is); 176 return; 177 } 178 } 179 else if (! have_allow) 180 { 181 if (pgr->details.ok.unit_allow_fraction) 182 { 183 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 184 "Product fractional flag unexpected\n"); 185 TALER_TESTING_interpreter_fail (gis->is); 186 return; 187 } 188 } 189 else 190 { 191 if (pgr->details.ok.unit_allow_fraction != *expected_allow) 192 { 193 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 194 "Product fractional flag does not match\n"); 195 TALER_TESTING_interpreter_fail (gis->is); 196 return; 197 } 198 { 199 const char *expected_unit_total_stock; 200 201 if (GNUNET_OK != 202 TALER_TESTING_get_trait_product_unit_total_stock ( 203 product_cmd, 204 &expected_unit_total_stock)) 205 TALER_TESTING_interpreter_fail (gis->is); 206 else if (0 != strcmp (pgr->details.ok.unit_total_stock, 207 expected_unit_total_stock)) 208 { 209 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 210 "Product stock string does not match\n"); 211 TALER_TESTING_interpreter_fail (gis->is); 212 return; 213 } 214 } 215 } 216 } 217 { 218 const uint32_t *expected_precision; 219 bool have_precision = GNUNET_OK == 220 TALER_TESTING_get_trait_product_unit_precision_level 221 ( 222 product_cmd, 223 &expected_precision); 224 bool override_precision = (NULL != ue) && 225 ue->have_unit_precision_level; 226 227 if (override_precision) 228 { 229 if (pgr->details.ok.unit_precision_level != 230 ue->unit_precision_level) 231 { 232 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 233 "Product fractional precision does not match expectation\n"); 234 TALER_TESTING_interpreter_fail (gis->is); 235 return; 236 } 237 } 238 else if (have_precision) 239 { 240 if (pgr->details.ok.unit_precision_level != *expected_precision) 241 { 242 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 243 "Product fractional precision does not match\n"); 244 TALER_TESTING_interpreter_fail (gis->is); 245 return; 246 } 247 } 248 else if (! pgr->details.ok.unit_allow_fraction) 249 { 250 if (0 != pgr->details.ok.unit_precision_level) 251 { 252 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 253 "Product fractional precision should be zero when disallowed\n"); 254 TALER_TESTING_interpreter_fail (gis->is); 255 return; 256 } 257 } 258 else if (pgr->details.ok.unit_precision_level > 8) 259 { 260 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 261 "Product fractional precision exceeds supported range\n"); 262 TALER_TESTING_interpreter_fail (gis->is); 263 return; 264 } 265 } 266 { 267 const char *expected_image; 268 269 if (GNUNET_OK != 270 TALER_TESTING_get_trait_product_image (product_cmd, 271 &expected_image)) 272 TALER_TESTING_interpreter_fail (gis->is); 273 if (0 != strcmp (pgr->details.ok.image, 274 expected_image)) 275 { 276 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 277 "Product image does not match\n"); 278 TALER_TESTING_interpreter_fail (gis->is); 279 return; 280 } 281 } 282 { 283 const json_t *expected_taxes; 284 285 if (GNUNET_OK != 286 TALER_TESTING_get_trait_taxes (product_cmd, 287 &expected_taxes)) 288 TALER_TESTING_interpreter_fail (gis->is); 289 if (1 != json_equal (pgr->details.ok.taxes, 290 expected_taxes)) 291 { 292 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 293 "Product taxes do not match\n"); 294 TALER_TESTING_interpreter_fail (gis->is); 295 return; 296 } 297 } 298 { 299 const char *expected_unit; 300 301 if (GNUNET_OK != 302 TALER_TESTING_get_trait_product_unit (product_cmd, 303 &expected_unit)) 304 TALER_TESTING_interpreter_fail (gis->is); 305 if (0 != strcmp (pgr->details.ok.unit, 306 expected_unit)) 307 { 308 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 309 "Product unit does not match\n"); 310 TALER_TESTING_interpreter_fail (gis->is); 311 return; 312 } 313 } 314 { 315 const json_t *expected_location; 316 317 if (GNUNET_OK != 318 TALER_TESTING_get_trait_address (product_cmd, 319 &expected_location)) 320 TALER_TESTING_interpreter_fail (gis->is); 321 if (NULL != expected_location) 322 { 323 if (1 != json_equal (pgr->details.ok.location, 324 expected_location)) 325 { 326 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 327 "Product location does not match\n"); 328 TALER_TESTING_interpreter_fail (gis->is); 329 return; 330 } 331 } 332 } 333 { 334 const int64_t *expected_total_stock; 335 336 if (GNUNET_OK != 337 TALER_TESTING_get_trait_product_stock (product_cmd, 338 &expected_total_stock)) 339 TALER_TESTING_interpreter_fail (gis->is); 340 if (pgr->details.ok.total_stock != *expected_total_stock) 341 { 342 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 343 "Product total stock does not match\n"); 344 TALER_TESTING_interpreter_fail (gis->is); 345 return; 346 } 347 } 348 { 349 const struct GNUNET_TIME_Timestamp *expected_next_restock; 350 351 if (GNUNET_OK != 352 TALER_TESTING_get_trait_timestamp (product_cmd, 353 0, 354 &expected_next_restock)) 355 TALER_TESTING_interpreter_fail (gis->is); 356 if (GNUNET_TIME_timestamp_cmp (pgr->details.ok.next_restock, 357 !=, 358 *expected_next_restock)) 359 { 360 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 361 "Product next restock does not match\n"); 362 TALER_TESTING_interpreter_fail (gis->is); 363 return; 364 } 365 } 366 break; 367 case MHD_HTTP_UNAUTHORIZED: 368 break; 369 case MHD_HTTP_NOT_FOUND: 370 break; 371 default: 372 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 373 "Unhandled HTTP status.\n"); 374 } 375 TALER_TESTING_interpreter_next (gis->is); 376 } 377 378 379 /** 380 * Run the "GET product" CMD. 381 * 382 * 383 * @param cls closure. 384 * @param cmd command being run now. 385 * @param is interpreter state. 386 */ 387 static void 388 get_product_run (void *cls, 389 const struct TALER_TESTING_Command *cmd, 390 struct TALER_TESTING_Interpreter *is) 391 { 392 struct GetProductState *gis = cls; 393 394 gis->is = is; 395 gis->igh = TALER_MERCHANT_get_private_product_create ( 396 TALER_TESTING_interpreter_get_context (is), 397 gis->merchant_url, 398 gis->product_id); 399 { 400 enum TALER_ErrorCode ec; 401 402 ec = TALER_MERCHANT_get_private_product_start ( 403 gis->igh, 404 &get_product_cb, 405 gis); 406 GNUNET_assert (TALER_EC_NONE == ec); 407 } 408 } 409 410 411 /** 412 * Free the state of a "GET product" CMD, and possibly 413 * cancel a pending operation thereof. 414 * 415 * @param cls closure. 416 * @param cmd command being run. 417 */ 418 static void 419 get_product_cleanup (void *cls, 420 const struct TALER_TESTING_Command *cmd) 421 { 422 struct GetProductState *gis = cls; 423 424 if (NULL != gis->igh) 425 { 426 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 427 "GET /products/$ID operation did not complete\n"); 428 TALER_MERCHANT_get_private_product_cancel (gis->igh); 429 } 430 GNUNET_free (gis); 431 } 432 433 434 struct TALER_TESTING_Command 435 TALER_TESTING_cmd_merchant_get_product (const char *label, 436 const char *merchant_url, 437 const char *product_id, 438 unsigned int http_status, 439 const char *product_reference) 440 { 441 return TALER_TESTING_cmd_merchant_get_product2 (label, 442 merchant_url, 443 product_id, 444 http_status, 445 product_reference, 446 NULL); 447 } 448 449 450 struct TALER_TESTING_Command 451 TALER_TESTING_cmd_merchant_get_product2 ( 452 const char *label, 453 const char *merchant_url, 454 const char *product_id, 455 unsigned int http_status, 456 const char *product_reference, 457 const struct TALER_TESTING_ProductUnitExpectations *unit_expectations) 458 { 459 struct GetProductState *gis; 460 461 gis = GNUNET_new (struct GetProductState); 462 gis->merchant_url = merchant_url; 463 gis->product_id = product_id; 464 gis->http_status = http_status; 465 gis->product_reference = product_reference; 466 gis->unit_expectations = unit_expectations; 467 { 468 struct TALER_TESTING_Command cmd = { 469 .cls = gis, 470 .label = label, 471 .run = &get_product_run, 472 .cleanup = &get_product_cleanup 473 }; 474 475 return cmd; 476 } 477 } 478 479 480 /* end of testing_api_cmd_get_product.c */