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