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