testing_api_cmd_get_product.c (14572B)
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 TALER_TESTING_interpreter_fail (gis->is); 116 if (0 != strcmp (pgr->details.ok.description, 117 expected_description)) 118 { 119 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 120 "Product description does not match\n"); 121 TALER_TESTING_interpreter_fail (gis->is); 122 return; 123 } 124 } 125 { 126 const json_t *expected_description_i18n; 127 128 if (GNUNET_OK != 129 TALER_TESTING_get_trait_i18n_description (product_cmd, 130 &expected_description_i18n)) 131 TALER_TESTING_interpreter_fail (gis->is); 132 if (1 != json_equal (pgr->details.ok.description_i18n, 133 expected_description_i18n)) 134 { 135 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 136 "Product description i18n does not match\n"); 137 TALER_TESTING_interpreter_fail (gis->is); 138 return; 139 } 140 } 141 { 142 const struct TALER_Amount *expected_price; 143 144 if (GNUNET_OK != 145 TALER_TESTING_get_trait_amount (product_cmd, 146 &expected_price)) 147 TALER_TESTING_interpreter_fail (gis->is); 148 if ((GNUNET_OK != 149 TALER_amount_cmp_currency (&pgr->details.ok.price, 150 expected_price)) || 151 (0 != TALER_amount_cmp (&pgr->details.ok.price, 152 expected_price))) 153 { 154 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 155 "Product price does not match\n"); 156 TALER_TESTING_interpreter_fail (gis->is); 157 return; 158 } 159 } 160 { 161 const bool *expected_allow; 162 bool have_allow = GNUNET_OK == 163 TALER_TESTING_get_trait_product_unit_allow_fraction ( 164 product_cmd, 165 &expected_allow); 166 bool override_allow = (NULL != ue) && 167 ue->have_unit_allow_fraction; 168 169 if (override_allow) 170 { 171 if (pgr->details.ok.unit_allow_fraction != 172 ue->unit_allow_fraction) 173 { 174 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 175 "Product fractional flag does not match expectation\n"); 176 TALER_TESTING_interpreter_fail (gis->is); 177 return; 178 } 179 } 180 else if (! have_allow) 181 { 182 if (pgr->details.ok.unit_allow_fraction) 183 { 184 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 185 "Product fractional flag unexpected\n"); 186 TALER_TESTING_interpreter_fail (gis->is); 187 return; 188 } 189 } 190 else 191 { 192 if (pgr->details.ok.unit_allow_fraction != *expected_allow) 193 { 194 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 195 "Product fractional flag does not match\n"); 196 TALER_TESTING_interpreter_fail (gis->is); 197 return; 198 } 199 { 200 const char *expected_unit_total_stock; 201 202 if (GNUNET_OK != 203 TALER_TESTING_get_trait_product_unit_total_stock ( 204 product_cmd, 205 &expected_unit_total_stock)) 206 TALER_TESTING_interpreter_fail (gis->is); 207 else if (0 != strcmp (pgr->details.ok.unit_total_stock, 208 expected_unit_total_stock)) 209 { 210 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 211 "Product stock string does not match\n"); 212 TALER_TESTING_interpreter_fail (gis->is); 213 return; 214 } 215 } 216 } 217 } 218 { 219 const uint32_t *expected_precision; 220 bool have_precision = GNUNET_OK == 221 TALER_TESTING_get_trait_product_unit_precision_level 222 ( 223 product_cmd, 224 &expected_precision); 225 bool override_precision = (NULL != ue) && 226 ue->have_unit_precision_level; 227 228 if (override_precision) 229 { 230 if (pgr->details.ok.unit_precision_level != 231 ue->unit_precision_level) 232 { 233 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 234 "Product fractional precision does not match expectation\n"); 235 TALER_TESTING_interpreter_fail (gis->is); 236 return; 237 } 238 } 239 else if (have_precision) 240 { 241 if (pgr->details.ok.unit_precision_level != *expected_precision) 242 { 243 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 244 "Product fractional precision does not match\n"); 245 TALER_TESTING_interpreter_fail (gis->is); 246 return; 247 } 248 } 249 else if (! pgr->details.ok.unit_allow_fraction) 250 { 251 if (0 != pgr->details.ok.unit_precision_level) 252 { 253 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 254 "Product fractional precision should be zero when disallowed\n"); 255 TALER_TESTING_interpreter_fail (gis->is); 256 return; 257 } 258 } 259 else if (pgr->details.ok.unit_precision_level > 8) 260 { 261 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 262 "Product fractional precision exceeds supported range\n"); 263 TALER_TESTING_interpreter_fail (gis->is); 264 return; 265 } 266 } 267 { 268 const char *expected_image; 269 270 if (GNUNET_OK != 271 TALER_TESTING_get_trait_product_image (product_cmd, 272 &expected_image)) 273 TALER_TESTING_interpreter_fail (gis->is); 274 if (0 != strcmp (pgr->details.ok.image, 275 expected_image)) 276 { 277 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 278 "Product image does not match\n"); 279 TALER_TESTING_interpreter_fail (gis->is); 280 return; 281 } 282 } 283 { 284 const json_t *expected_taxes; 285 286 if (GNUNET_OK != 287 TALER_TESTING_get_trait_taxes (product_cmd, 288 &expected_taxes)) 289 TALER_TESTING_interpreter_fail (gis->is); 290 if (1 != json_equal (pgr->details.ok.taxes, 291 expected_taxes)) 292 { 293 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 294 "Product taxes do not match\n"); 295 TALER_TESTING_interpreter_fail (gis->is); 296 return; 297 } 298 } 299 { 300 const char *expected_unit; 301 302 if (GNUNET_OK != 303 TALER_TESTING_get_trait_product_unit (product_cmd, 304 &expected_unit)) 305 TALER_TESTING_interpreter_fail (gis->is); 306 if (0 != strcmp (pgr->details.ok.unit, 307 expected_unit)) 308 { 309 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 310 "Product unit does not match\n"); 311 TALER_TESTING_interpreter_fail (gis->is); 312 return; 313 } 314 } 315 { 316 const json_t *expected_location; 317 318 if (GNUNET_OK != 319 TALER_TESTING_get_trait_address (product_cmd, 320 &expected_location)) 321 TALER_TESTING_interpreter_fail (gis->is); 322 if (NULL != expected_location) 323 { 324 if (1 != json_equal (pgr->details.ok.location, 325 expected_location)) 326 { 327 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 328 "Product location does not match\n"); 329 TALER_TESTING_interpreter_fail (gis->is); 330 return; 331 } 332 } 333 } 334 { 335 const int64_t *expected_total_stock; 336 337 if (GNUNET_OK != 338 TALER_TESTING_get_trait_product_stock (product_cmd, 339 &expected_total_stock)) 340 TALER_TESTING_interpreter_fail (gis->is); 341 if (pgr->details.ok.total_stock != *expected_total_stock) 342 { 343 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 344 "Product total stock does not match\n"); 345 TALER_TESTING_interpreter_fail (gis->is); 346 return; 347 } 348 } 349 { 350 const struct GNUNET_TIME_Timestamp *expected_next_restock; 351 352 if (GNUNET_OK != 353 TALER_TESTING_get_trait_timestamp (product_cmd, 354 0, 355 &expected_next_restock)) 356 TALER_TESTING_interpreter_fail (gis->is); 357 if (GNUNET_TIME_timestamp_cmp (pgr->details.ok.next_restock, 358 !=, 359 *expected_next_restock)) 360 { 361 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 362 "Product next restock does not match\n"); 363 TALER_TESTING_interpreter_fail (gis->is); 364 return; 365 } 366 } 367 break; 368 case MHD_HTTP_UNAUTHORIZED: 369 break; 370 case MHD_HTTP_NOT_FOUND: 371 break; 372 default: 373 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 374 "Unhandled HTTP status.\n"); 375 } 376 TALER_TESTING_interpreter_next (gis->is); 377 } 378 379 380 /** 381 * Run the "GET product" CMD. 382 * 383 * 384 * @param cls closure. 385 * @param cmd command being run now. 386 * @param is interpreter state. 387 */ 388 static void 389 get_product_run (void *cls, 390 const struct TALER_TESTING_Command *cmd, 391 struct TALER_TESTING_Interpreter *is) 392 { 393 struct GetProductState *gis = cls; 394 395 gis->is = is; 396 gis->igh = TALER_MERCHANT_get_private_product_create ( 397 TALER_TESTING_interpreter_get_context (is), 398 gis->merchant_url, 399 gis->product_id); 400 { 401 enum TALER_ErrorCode ec; 402 403 ec = TALER_MERCHANT_get_private_product_start ( 404 gis->igh, 405 &get_product_cb, 406 gis); 407 GNUNET_assert (TALER_EC_NONE == ec); 408 } 409 } 410 411 412 /** 413 * Free the state of a "GET product" CMD, and possibly 414 * cancel a pending operation thereof. 415 * 416 * @param cls closure. 417 * @param cmd command being run. 418 */ 419 static void 420 get_product_cleanup (void *cls, 421 const struct TALER_TESTING_Command *cmd) 422 { 423 struct GetProductState *gis = cls; 424 425 if (NULL != gis->igh) 426 { 427 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 428 "GET /products/$ID operation did not complete\n"); 429 TALER_MERCHANT_get_private_product_cancel (gis->igh); 430 } 431 GNUNET_free (gis); 432 } 433 434 435 struct TALER_TESTING_Command 436 TALER_TESTING_cmd_merchant_get_product (const char *label, 437 const char *merchant_url, 438 const char *product_id, 439 unsigned int http_status, 440 const char *product_reference) 441 { 442 return TALER_TESTING_cmd_merchant_get_product2 (label, 443 merchant_url, 444 product_id, 445 http_status, 446 product_reference, 447 NULL); 448 } 449 450 451 struct TALER_TESTING_Command 452 TALER_TESTING_cmd_merchant_get_product2 ( 453 const char *label, 454 const char *merchant_url, 455 const char *product_id, 456 unsigned int http_status, 457 const char *product_reference, 458 const struct TALER_TESTING_ProductUnitExpectations *unit_expectations) 459 { 460 struct GetProductState *gis; 461 462 gis = GNUNET_new (struct GetProductState); 463 gis->merchant_url = merchant_url; 464 gis->product_id = product_id; 465 gis->http_status = http_status; 466 gis->product_reference = product_reference; 467 gis->unit_expectations = unit_expectations; 468 { 469 struct TALER_TESTING_Command cmd = { 470 .cls = gis, 471 .label = label, 472 .run = &get_product_run, 473 .cleanup = &get_product_cleanup 474 }; 475 476 return cmd; 477 } 478 } 479 480 481 /* end of testing_api_cmd_get_product.c */