Line data Source code
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 24 : get_product_cb (void *cls,
83 : const struct TALER_MERCHANT_ProductGetResponse *pgr)
84 : {
85 24 : struct GetProductState *gis = cls;
86 : const struct TALER_TESTING_Command *product_cmd;
87 24 : const struct TALER_TESTING_ProductUnitExpectations *ue =
88 : gis->unit_expectations;
89 :
90 24 : gis->igh = NULL;
91 24 : if (gis->http_status != pgr->hr.http_status)
92 : {
93 0 : 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 0 : TALER_TESTING_interpreter_fail (gis->is);
99 0 : return;
100 : }
101 24 : switch (pgr->hr.http_status)
102 : {
103 12 : case MHD_HTTP_OK:
104 : {
105 : const char *expected_description;
106 :
107 12 : product_cmd = TALER_TESTING_interpreter_lookup_command (
108 : gis->is,
109 : gis->product_reference);
110 12 : if (GNUNET_OK !=
111 12 : TALER_TESTING_get_trait_product_description (product_cmd,
112 : &expected_description))
113 0 : TALER_TESTING_interpreter_fail (gis->is);
114 12 : if (0 != strcmp (pgr->details.ok.description,
115 : expected_description))
116 : {
117 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
118 : "Product description does not match\n");
119 0 : TALER_TESTING_interpreter_fail (gis->is);
120 0 : return;
121 : }
122 : }
123 : {
124 : const json_t *expected_description_i18n;
125 :
126 12 : if (GNUNET_OK !=
127 12 : TALER_TESTING_get_trait_i18n_description (product_cmd,
128 : &expected_description_i18n))
129 0 : TALER_TESTING_interpreter_fail (gis->is);
130 12 : if (1 != json_equal (pgr->details.ok.description_i18n,
131 : expected_description_i18n))
132 : {
133 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
134 : "Product description i18n does not match\n");
135 0 : TALER_TESTING_interpreter_fail (gis->is);
136 0 : return;
137 : }
138 : }
139 : {
140 : const struct TALER_Amount *expected_price;
141 :
142 12 : if (GNUNET_OK !=
143 12 : TALER_TESTING_get_trait_amount (product_cmd,
144 : &expected_price))
145 0 : TALER_TESTING_interpreter_fail (gis->is);
146 12 : if ((GNUNET_OK !=
147 12 : TALER_amount_cmp_currency (&pgr->details.ok.price,
148 12 : expected_price)) ||
149 12 : (0 != TALER_amount_cmp (&pgr->details.ok.price,
150 : expected_price)))
151 : {
152 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
153 : "Product price does not match\n");
154 0 : TALER_TESTING_interpreter_fail (gis->is);
155 0 : return;
156 : }
157 : }
158 : {
159 : const bool *expected_allow;
160 12 : bool have_allow = GNUNET_OK ==
161 12 : TALER_TESTING_get_trait_product_unit_allow_fraction (
162 : product_cmd,
163 : &expected_allow);
164 16 : bool override_allow = (NULL != ue) &&
165 4 : ue->have_unit_allow_fraction;
166 :
167 12 : if (override_allow)
168 : {
169 4 : if (pgr->details.ok.unit_allow_fraction !=
170 4 : ue->unit_allow_fraction)
171 : {
172 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
173 : "Product fractional flag does not match expectation\n");
174 0 : TALER_TESTING_interpreter_fail (gis->is);
175 0 : return;
176 : }
177 : }
178 8 : else if (! have_allow)
179 : {
180 0 : if (pgr->details.ok.unit_allow_fraction)
181 : {
182 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
183 : "Product fractional flag unexpected\n");
184 0 : TALER_TESTING_interpreter_fail (gis->is);
185 0 : return;
186 : }
187 : }
188 : else
189 : {
190 8 : if (pgr->details.ok.unit_allow_fraction != *expected_allow)
191 : {
192 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
193 : "Product fractional flag does not match\n");
194 0 : TALER_TESTING_interpreter_fail (gis->is);
195 0 : return;
196 : }
197 : {
198 : const char *expected_unit_total_stock;
199 :
200 8 : if (GNUNET_OK !=
201 8 : TALER_TESTING_get_trait_product_unit_total_stock (
202 : product_cmd,
203 : &expected_unit_total_stock))
204 0 : TALER_TESTING_interpreter_fail (gis->is);
205 8 : else if (0 != strcmp (pgr->details.ok.unit_total_stock,
206 : expected_unit_total_stock))
207 : {
208 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
209 : "Product stock string does not match\n");
210 0 : TALER_TESTING_interpreter_fail (gis->is);
211 0 : return;
212 : }
213 : }
214 : }
215 : }
216 : {
217 : const uint32_t *expected_precision;
218 12 : bool have_precision = GNUNET_OK ==
219 12 : TALER_TESTING_get_trait_product_unit_precision_level (
220 : product_cmd,
221 : &expected_precision);
222 16 : bool override_precision = (NULL != ue) &&
223 4 : ue->have_unit_precision_level;
224 :
225 12 : if (override_precision)
226 : {
227 4 : if (pgr->details.ok.unit_precision_level !=
228 4 : ue->unit_precision_level)
229 : {
230 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
231 : "Product fractional precision does not match expectation\n");
232 0 : TALER_TESTING_interpreter_fail (gis->is);
233 0 : return;
234 : }
235 : }
236 8 : else if (have_precision)
237 : {
238 8 : if (pgr->details.ok.unit_precision_level != *expected_precision)
239 : {
240 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
241 : "Product fractional precision does not match\n");
242 0 : TALER_TESTING_interpreter_fail (gis->is);
243 0 : return;
244 : }
245 : }
246 0 : else if (! pgr->details.ok.unit_allow_fraction)
247 : {
248 0 : if (0 != pgr->details.ok.unit_precision_level)
249 : {
250 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
251 : "Product fractional precision should be zero when disallowed\n");
252 0 : TALER_TESTING_interpreter_fail (gis->is);
253 0 : return;
254 : }
255 : }
256 0 : else if (pgr->details.ok.unit_precision_level > 8)
257 : {
258 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
259 : "Product fractional precision exceeds supported range\n");
260 0 : TALER_TESTING_interpreter_fail (gis->is);
261 0 : return;
262 : }
263 : }
264 : {
265 : const char *expected_image;
266 :
267 12 : if (GNUNET_OK !=
268 12 : TALER_TESTING_get_trait_product_image (product_cmd,
269 : &expected_image))
270 0 : TALER_TESTING_interpreter_fail (gis->is);
271 12 : if (0 != strcmp (pgr->details.ok.image,
272 : expected_image))
273 : {
274 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
275 : "Product image does not match\n");
276 0 : TALER_TESTING_interpreter_fail (gis->is);
277 0 : return;
278 : }
279 : }
280 : {
281 : const json_t *expected_taxes;
282 :
283 12 : if (GNUNET_OK !=
284 12 : TALER_TESTING_get_trait_taxes (product_cmd,
285 : &expected_taxes))
286 0 : TALER_TESTING_interpreter_fail (gis->is);
287 12 : if (1 != json_equal (pgr->details.ok.taxes,
288 : expected_taxes))
289 : {
290 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
291 : "Product taxes do not match\n");
292 0 : TALER_TESTING_interpreter_fail (gis->is);
293 0 : return;
294 : }
295 : }
296 : {
297 : const char *expected_unit;
298 :
299 12 : if (GNUNET_OK !=
300 12 : TALER_TESTING_get_trait_product_unit (product_cmd,
301 : &expected_unit))
302 0 : TALER_TESTING_interpreter_fail (gis->is);
303 12 : if (0 != strcmp (pgr->details.ok.unit,
304 : expected_unit))
305 : {
306 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
307 : "Product unit does not match\n");
308 0 : TALER_TESTING_interpreter_fail (gis->is);
309 0 : return;
310 : }
311 : }
312 : {
313 : const json_t *expected_location;
314 :
315 12 : if (GNUNET_OK !=
316 12 : TALER_TESTING_get_trait_address (product_cmd,
317 : &expected_location))
318 0 : TALER_TESTING_interpreter_fail (gis->is);
319 12 : if (NULL != expected_location)
320 : {
321 10 : if (1 != json_equal (pgr->details.ok.location,
322 : expected_location))
323 : {
324 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
325 : "Product location does not match\n");
326 0 : TALER_TESTING_interpreter_fail (gis->is);
327 0 : return;
328 : }
329 : }
330 : }
331 : {
332 : const int64_t *expected_total_stock;
333 :
334 12 : if (GNUNET_OK !=
335 12 : TALER_TESTING_get_trait_product_stock (product_cmd,
336 : &expected_total_stock))
337 0 : TALER_TESTING_interpreter_fail (gis->is);
338 12 : if (pgr->details.ok.total_stock != *expected_total_stock)
339 : {
340 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
341 : "Product total stock does not match\n");
342 0 : TALER_TESTING_interpreter_fail (gis->is);
343 0 : return;
344 : }
345 : }
346 : {
347 : const struct GNUNET_TIME_Timestamp *expected_next_restock;
348 :
349 12 : if (GNUNET_OK !=
350 12 : TALER_TESTING_get_trait_timestamp (product_cmd,
351 : 0,
352 : &expected_next_restock))
353 0 : TALER_TESTING_interpreter_fail (gis->is);
354 12 : if (GNUNET_TIME_timestamp_cmp (pgr->details.ok.next_restock,
355 : !=,
356 : *expected_next_restock))
357 : {
358 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
359 : "Product next restock does not match\n");
360 0 : TALER_TESTING_interpreter_fail (gis->is);
361 0 : return;
362 : }
363 : }
364 12 : break;
365 4 : case MHD_HTTP_UNAUTHORIZED:
366 4 : break;
367 8 : case MHD_HTTP_NOT_FOUND:
368 8 : break;
369 0 : default:
370 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
371 : "Unhandled HTTP status.\n");
372 : }
373 24 : 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 24 : get_product_run (void *cls,
387 : const struct TALER_TESTING_Command *cmd,
388 : struct TALER_TESTING_Interpreter *is)
389 : {
390 24 : struct GetProductState *gis = cls;
391 :
392 24 : gis->is = is;
393 24 : 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 24 : GNUNET_assert (NULL != gis->igh);
400 24 : }
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 24 : get_product_cleanup (void *cls,
412 : const struct TALER_TESTING_Command *cmd)
413 : {
414 24 : struct GetProductState *gis = cls;
415 :
416 24 : if (NULL != gis->igh)
417 : {
418 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
419 : "GET /products/$ID operation did not complete\n");
420 0 : TALER_MERCHANT_product_get_cancel (gis->igh);
421 : }
422 24 : GNUNET_free (gis);
423 24 : }
424 :
425 :
426 : struct TALER_TESTING_Command
427 20 : 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 20 : 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 24 : 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 24 : gis = GNUNET_new (struct GetProductState);
454 24 : gis->merchant_url = merchant_url;
455 24 : gis->product_id = product_id;
456 24 : gis->http_status = http_status;
457 24 : gis->product_reference = product_reference;
458 24 : gis->unit_expectations = unit_expectations;
459 : {
460 24 : struct TALER_TESTING_Command cmd = {
461 : .cls = gis,
462 : .label = label,
463 : .run = &get_product_run,
464 : .cleanup = &get_product_cleanup
465 : };
466 :
467 24 : return cmd;
468 : }
469 : }
470 :
471 :
472 : /* end of testing_api_cmd_get_product.c */
|