Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2020 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 :
69 :
70 : /**
71 : * Callback for a /get/product/$ID operation.
72 : *
73 : * @param cls closure for this function
74 : * @param hr HTTP response details
75 : * @param description description of the product
76 : * @param description_i18n Map from IETF BCP 47 language tags to localized descriptions
77 : * @param unit unit in which the product is measured (liters, kilograms, packages, etc.)
78 : * @param price the price for one @a unit of the product, zero is used to imply that
79 : * this product is not sold separately or that the price is not fixed and
80 : * must be supplied by the front-end. If non-zero, price must include
81 : * applicable taxes.
82 : * @param image base64-encoded product image
83 : * @param taxes list of taxes paid by the merchant
84 : * @param total_stock in @a units, -1 to indicate "infinite" (i.e. electronic books),
85 : * does NOT indicate remaining stocks, to get remaining stocks,
86 : * subtract @a total_sold and @a total_lost. Note that this still
87 : * does not then say how many of the remaining inventory are locked.
88 : * @param total_sold in @a units, total number of @a unit of product sold
89 : * @param total_lost in @a units, total number of @a unit of product lost from inventory
90 : * @param location where the product is in stock
91 : * @param next_restock when the next restocking is expected to happen, 0 for unknown,
92 : * #GNUNET_TIME_UNIT_FOREVER_ABS for 'never'.
93 : */
94 : static void
95 0 : get_product_cb (void *cls,
96 : const struct TALER_MERCHANT_HttpResponse *hr,
97 : const char *description,
98 : const json_t *description_i18n,
99 : const char *unit,
100 : const struct TALER_Amount *price,
101 : const char *image,
102 : const json_t *taxes,
103 : int64_t total_stock,
104 : uint64_t total_sold,
105 : uint64_t total_lost,
106 : const json_t *location,
107 : struct GNUNET_TIME_Timestamp next_restock)
108 : {
109 0 : struct GetProductState *gis = cls;
110 : const struct TALER_TESTING_Command *product_cmd;
111 :
112 0 : gis->igh = NULL;
113 0 : if (gis->http_status != hr->http_status)
114 : {
115 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
116 : "Unexpected response code %u (%d) to command %s\n",
117 : hr->http_status,
118 : (int) hr->ec,
119 : TALER_TESTING_interpreter_get_current_label (gis->is));
120 0 : TALER_TESTING_interpreter_fail (gis->is);
121 0 : return;
122 : }
123 0 : switch (hr->http_status)
124 : {
125 0 : case MHD_HTTP_OK:
126 : {
127 : const char **expected_description;
128 :
129 0 : product_cmd = TALER_TESTING_interpreter_lookup_command (
130 : gis->is,
131 : gis->product_reference);
132 0 : if (GNUNET_OK !=
133 0 : TALER_TESTING_get_trait_product_description (product_cmd,
134 : &expected_description))
135 0 : TALER_TESTING_interpreter_fail (gis->is);
136 0 : if (0 != strcmp (description,
137 : *expected_description))
138 : {
139 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
140 : "Product description does not match\n");
141 0 : TALER_TESTING_interpreter_fail (gis->is);
142 0 : return;
143 : }
144 : }
145 : {
146 : const json_t *expected_description_i18n;
147 :
148 0 : if (GNUNET_OK !=
149 0 : TALER_TESTING_get_trait_i18n_description (product_cmd,
150 : &expected_description_i18n))
151 0 : TALER_TESTING_interpreter_fail (gis->is);
152 0 : if (1 != json_equal (description_i18n,
153 : expected_description_i18n))
154 : {
155 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
156 : "Product description i18n does not match\n");
157 0 : TALER_TESTING_interpreter_fail (gis->is);
158 0 : return;
159 : }
160 : }
161 : {
162 : const struct TALER_Amount *expected_price;
163 :
164 0 : if (GNUNET_OK !=
165 0 : TALER_TESTING_get_trait_amount (product_cmd,
166 : &expected_price))
167 0 : TALER_TESTING_interpreter_fail (gis->is);
168 0 : if ((GNUNET_OK != TALER_amount_cmp_currency (price,
169 0 : expected_price)) ||
170 0 : (0 != TALER_amount_cmp (price,
171 : expected_price)))
172 : {
173 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
174 : "Product price does not match\n");
175 0 : TALER_TESTING_interpreter_fail (gis->is);
176 0 : return;
177 : }
178 : }
179 : {
180 : const char **expected_image;
181 :
182 0 : if (GNUNET_OK !=
183 0 : TALER_TESTING_get_trait_product_image (product_cmd,
184 : &expected_image))
185 0 : TALER_TESTING_interpreter_fail (gis->is);
186 0 : if (0 != strcmp (image,
187 : *expected_image))
188 : {
189 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
190 : "Product image does not match\n");
191 0 : TALER_TESTING_interpreter_fail (gis->is);
192 0 : return;
193 : }
194 : }
195 : {
196 : const json_t *expected_taxes;
197 :
198 0 : if (GNUNET_OK !=
199 0 : TALER_TESTING_get_trait_taxes (product_cmd,
200 : &expected_taxes))
201 0 : TALER_TESTING_interpreter_fail (gis->is);
202 0 : if (1 != json_equal (taxes,
203 : expected_taxes))
204 : {
205 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
206 : "Product taxes do not match\n");
207 0 : TALER_TESTING_interpreter_fail (gis->is);
208 0 : return;
209 : }
210 : }
211 : {
212 : const char **expected_unit;
213 :
214 0 : if (GNUNET_OK !=
215 0 : TALER_TESTING_get_trait_product_unit (product_cmd,
216 : &expected_unit))
217 0 : TALER_TESTING_interpreter_fail (gis->is);
218 0 : if (0 != strcmp (unit,
219 : *expected_unit))
220 : {
221 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
222 : "Product unit does not match\n");
223 0 : TALER_TESTING_interpreter_fail (gis->is);
224 0 : return;
225 : }
226 : }
227 : {
228 : const json_t *expected_location;
229 :
230 0 : if (GNUNET_OK !=
231 0 : TALER_TESTING_get_trait_address (product_cmd,
232 : &expected_location))
233 0 : TALER_TESTING_interpreter_fail (gis->is);
234 0 : if (1 != json_equal (location,
235 : expected_location))
236 : {
237 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
238 : "Product location does not match\n");
239 0 : TALER_TESTING_interpreter_fail (gis->is);
240 0 : return;
241 : }
242 : }
243 : {
244 : const int64_t *expected_total_stock;
245 :
246 0 : if (GNUNET_OK !=
247 0 : TALER_TESTING_get_trait_product_stock (product_cmd,
248 : &expected_total_stock))
249 0 : TALER_TESTING_interpreter_fail (gis->is);
250 0 : if (total_stock != *expected_total_stock)
251 : {
252 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
253 : "Product total stock does not match\n");
254 0 : TALER_TESTING_interpreter_fail (gis->is);
255 0 : return;
256 : }
257 : }
258 : {
259 : const struct GNUNET_TIME_Timestamp *expected_next_restock;
260 :
261 0 : if (GNUNET_OK !=
262 0 : TALER_TESTING_get_trait_timestamp (product_cmd,
263 : 0,
264 : &expected_next_restock))
265 0 : TALER_TESTING_interpreter_fail (gis->is);
266 0 : if (GNUNET_TIME_timestamp_cmp (next_restock,
267 : !=,
268 : *expected_next_restock))
269 : {
270 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
271 : "Product next restock does not match\n");
272 0 : TALER_TESTING_interpreter_fail (gis->is);
273 0 : return;
274 : }
275 : }
276 0 : break;
277 0 : case MHD_HTTP_UNAUTHORIZED:
278 0 : break;
279 0 : case MHD_HTTP_NOT_FOUND:
280 0 : break;
281 0 : default:
282 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
283 : "Unhandled HTTP status.\n");
284 : }
285 0 : TALER_TESTING_interpreter_next (gis->is);
286 : }
287 :
288 :
289 : /**
290 : * Run the "GET product" CMD.
291 : *
292 : *
293 : * @param cls closure.
294 : * @param cmd command being run now.
295 : * @param is interpreter state.
296 : */
297 : static void
298 0 : get_product_run (void *cls,
299 : const struct TALER_TESTING_Command *cmd,
300 : struct TALER_TESTING_Interpreter *is)
301 : {
302 0 : struct GetProductState *gis = cls;
303 :
304 0 : gis->is = is;
305 0 : gis->igh = TALER_MERCHANT_product_get (is->ctx,
306 : gis->merchant_url,
307 : gis->product_id,
308 : &get_product_cb,
309 : gis);
310 0 : GNUNET_assert (NULL != gis->igh);
311 0 : }
312 :
313 :
314 : /**
315 : * Free the state of a "GET product" CMD, and possibly
316 : * cancel a pending operation thereof.
317 : *
318 : * @param cls closure.
319 : * @param cmd command being run.
320 : */
321 : static void
322 0 : get_product_cleanup (void *cls,
323 : const struct TALER_TESTING_Command *cmd)
324 : {
325 0 : struct GetProductState *gis = cls;
326 :
327 0 : if (NULL != gis->igh)
328 : {
329 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
330 : "GET /products/$ID operation did not complete\n");
331 0 : TALER_MERCHANT_product_get_cancel (gis->igh);
332 : }
333 0 : GNUNET_free (gis);
334 0 : }
335 :
336 :
337 : struct TALER_TESTING_Command
338 0 : TALER_TESTING_cmd_merchant_get_product (const char *label,
339 : const char *merchant_url,
340 : const char *product_id,
341 : unsigned int http_status,
342 : const char *product_reference)
343 : {
344 : struct GetProductState *gis;
345 :
346 0 : gis = GNUNET_new (struct GetProductState);
347 0 : gis->merchant_url = merchant_url;
348 0 : gis->product_id = product_id;
349 0 : gis->http_status = http_status;
350 0 : gis->product_reference = product_reference;
351 : {
352 0 : struct TALER_TESTING_Command cmd = {
353 : .cls = gis,
354 : .label = label,
355 : .run = &get_product_run,
356 : .cleanup = &get_product_cleanup
357 : };
358 :
359 0 : return cmd;
360 : }
361 : }
362 :
363 :
364 : /* end of testing_api_cmd_get_product.c */
|