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_orders.c
21 : * @brief command to test GET /orders
22 : * @author Jonathan Buchanan
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 orders" CMD.
33 : */
34 : struct GetOrdersState
35 : {
36 :
37 : /**
38 : * Handle for a "GET orders" request.
39 : */
40 : struct TALER_MERCHANT_OrdersGetHandle *ogh;
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 : * Expected HTTP response code.
54 : */
55 : unsigned int http_status;
56 :
57 : /**
58 : * A NULL-terminated array of CMD labels that created orders.
59 : */
60 : const char **orders;
61 :
62 : /**
63 : * The length of @e orders.
64 : */
65 : unsigned int orders_length;
66 :
67 : };
68 :
69 :
70 : /**
71 : * Callback for a GET /orders operation.
72 : *
73 : * @param cls closure for this function
74 : * @param hr HTTP response
75 : * @param orders_length how many orders are returned
76 : * @param orders all the orders' details
77 : */
78 : static void
79 0 : get_orders_cb (void *cls,
80 : const struct TALER_MERCHANT_HttpResponse *hr,
81 : unsigned int orders_length,
82 : const struct TALER_MERCHANT_OrderEntry orders[])
83 : {
84 0 : struct GetOrdersState *gos = cls;
85 :
86 0 : gos->ogh = NULL;
87 0 : if (gos->http_status != hr->http_status)
88 : {
89 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
90 : "Unexpected response code %u (%d) to command %s\n",
91 : hr->http_status,
92 : (int) hr->ec,
93 : TALER_TESTING_interpreter_get_current_label (gos->is));
94 0 : TALER_TESTING_interpreter_fail (gos->is);
95 0 : return;
96 : }
97 0 : switch (hr->http_status)
98 : {
99 0 : case MHD_HTTP_OK:
100 0 : if (orders_length != gos->orders_length)
101 : {
102 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
103 : "Number of orders found does not match\n");
104 0 : TALER_TESTING_interpreter_fail (gos->is);
105 0 : return;
106 : }
107 0 : for (unsigned int i = 0; i < orders_length; ++i)
108 : {
109 : const struct TALER_TESTING_Command *order_cmd;
110 :
111 0 : order_cmd = TALER_TESTING_interpreter_lookup_command (
112 : gos->is,
113 0 : gos->orders[i]);
114 :
115 : {
116 : const char **order_id;
117 :
118 0 : if (GNUNET_OK !=
119 0 : TALER_TESTING_get_trait_order_id (order_cmd,
120 : &order_id))
121 : {
122 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
123 : "Could not fetch order id\n");
124 0 : TALER_TESTING_interpreter_fail (gos->is);
125 0 : return;
126 : }
127 0 : if (0 != strcmp (orders[i].order_id,
128 : *order_id))
129 : {
130 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
131 : "Order id does not match\n");
132 0 : TALER_TESTING_interpreter_fail (gos->is);
133 0 : return;
134 : }
135 : }
136 : {
137 : const json_t *contract_terms;
138 : struct TALER_Amount amount;
139 : const char *summary;
140 : struct GNUNET_JSON_Specification spec[] = {
141 0 : GNUNET_JSON_spec_string ("summary",
142 : &summary),
143 0 : TALER_JSON_spec_amount_any ("amount",
144 : &amount),
145 0 : GNUNET_JSON_spec_end ()
146 : };
147 :
148 0 : if (GNUNET_OK !=
149 0 : TALER_TESTING_get_trait_contract_terms (order_cmd,
150 : &contract_terms))
151 : {
152 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
153 : "Could not fetch order contract terms\n");
154 0 : TALER_TESTING_interpreter_fail (gos->is);
155 0 : return;
156 : }
157 0 : if (GNUNET_OK !=
158 0 : GNUNET_JSON_parse (contract_terms,
159 : spec,
160 : NULL, NULL))
161 : {
162 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
163 : "Could not parse order contract terms\n");
164 0 : TALER_TESTING_interpreter_fail (gos->is);
165 0 : return;
166 : }
167 0 : if ((0 != strcmp (summary,
168 0 : orders[i].summary)) ||
169 0 : (GNUNET_OK != TALER_amount_cmp_currency (&amount,
170 0 : &orders[i].amount)) ||
171 0 : (0 != TALER_amount_cmp (&amount,
172 0 : &orders[i].amount)))
173 : {
174 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
175 : "Order summary and/or amount does not match\n");
176 0 : TALER_TESTING_interpreter_fail (gos->is);
177 0 : return;
178 : }
179 : }
180 : }
181 0 : break;
182 0 : case MHD_HTTP_ACCEPTED:
183 : /* FIXME: do more checks here (new KYC logic!) */
184 0 : break;
185 0 : default:
186 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
187 : "Unhandled HTTP status.\n");
188 : }
189 0 : TALER_TESTING_interpreter_next (gos->is);
190 : }
191 :
192 :
193 : /**
194 : * Run the "GET /orders" CMD.
195 : *
196 : * @param cls closure.
197 : * @param cmd command being run now.
198 : * @param is interpreter state.
199 : */
200 : static void
201 0 : get_orders_run (void *cls,
202 : const struct TALER_TESTING_Command *cmd,
203 : struct TALER_TESTING_Interpreter *is)
204 : {
205 0 : struct GetOrdersState *gos = cls;
206 :
207 0 : gos->is = is;
208 0 : gos->ogh = TALER_MERCHANT_orders_get (is->ctx,
209 : gos->merchant_url,
210 : &get_orders_cb,
211 : gos);
212 0 : GNUNET_assert (NULL != gos->ogh);
213 0 : }
214 :
215 :
216 : /**
217 : * Free the state of a "GET orders" CMD, and possibly
218 : * cancel a pending operation thereof.
219 : *
220 : * @param cls closure.
221 : * @param cmd command being run.
222 : */
223 : static void
224 0 : get_orders_cleanup (void *cls,
225 : const struct TALER_TESTING_Command *cmd)
226 : {
227 0 : struct GetOrdersState *gos = cls;
228 :
229 0 : if (NULL != gos->ogh)
230 : {
231 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
232 : "GET /orders operation did not complete\n");
233 0 : TALER_MERCHANT_orders_get_cancel (gos->ogh);
234 : }
235 0 : GNUNET_array_grow (gos->orders,
236 : gos->orders_length,
237 : 0);
238 0 : GNUNET_free (gos);
239 0 : }
240 :
241 :
242 : struct TALER_TESTING_Command
243 0 : TALER_TESTING_cmd_merchant_get_orders (const char *label,
244 : const char *merchant_url,
245 : unsigned int http_status,
246 : ...)
247 : {
248 : struct GetOrdersState *gos;
249 :
250 0 : gos = GNUNET_new (struct GetOrdersState);
251 0 : gos->merchant_url = merchant_url;
252 0 : gos->http_status = http_status;
253 : {
254 : const char *clabel;
255 : va_list ap;
256 :
257 0 : va_start (ap, http_status);
258 0 : while (NULL != (clabel = va_arg (ap, const char *)))
259 : {
260 0 : GNUNET_array_append (gos->orders,
261 : gos->orders_length,
262 : clabel);
263 : }
264 0 : va_end (ap);
265 : }
266 : {
267 0 : struct TALER_TESTING_Command cmd = {
268 : .cls = gos,
269 : .label = label,
270 : .run = &get_orders_run,
271 : .cleanup = &get_orders_cleanup
272 : };
273 :
274 0 : return cmd;
275 : }
276 : }
277 :
278 :
279 : struct MerchantPollOrdersConcludeState
280 : {
281 : /**
282 : * The interpreter state.
283 : */
284 : struct TALER_TESTING_Interpreter *is;
285 :
286 : /**
287 : * Reference to a command that can provide a poll orders start command.
288 : */
289 : const char *start_reference;
290 :
291 : /**
292 : * Task to wait for the deadline.
293 : */
294 : struct GNUNET_SCHEDULER_Task *task;
295 :
296 : /**
297 : * Expected HTTP response status code.
298 : */
299 : unsigned int expected_http_status;
300 : };
301 :
302 :
303 : struct MerchantPollOrdersStartState
304 : {
305 : /**
306 : * The merchant base URL.
307 : */
308 : const char *merchant_url;
309 :
310 : /**
311 : * The handle to the current GET /private/orders request.
312 : */
313 : struct TALER_MERCHANT_OrdersGetHandle *ogh;
314 :
315 : /**
316 : * The interpreter state.
317 : */
318 : struct TALER_TESTING_Interpreter *is;
319 :
320 : /**
321 : * How long to wait for server to return a response.
322 : */
323 : struct GNUNET_TIME_Relative timeout;
324 :
325 : /**
326 : * Conclude state waiting for completion (if any).
327 : */
328 : struct MerchantPollOrdersConcludeState *cs;
329 :
330 : /**
331 : * The HTTP status code returned by the backend.
332 : */
333 : unsigned int http_status;
334 :
335 : /**
336 : * When the request should be completed by.
337 : */
338 : struct GNUNET_TIME_Absolute deadline;
339 : };
340 :
341 :
342 : /**
343 : * Task called when either the timeout for the get orders
344 : * command expired or we got a response. Checks if the
345 : * result is what we expected.
346 : *
347 : * @param cls a `struct MerchantPollOrdersConcludeState`
348 : */
349 : static void
350 0 : conclude_task (void *cls)
351 : {
352 0 : struct MerchantPollOrdersConcludeState *poc = cls;
353 : const struct TALER_TESTING_Command *poll_cmd;
354 : struct MerchantPollOrdersStartState *pos;
355 : struct GNUNET_TIME_Absolute now;
356 :
357 0 : poc->task = NULL;
358 : poll_cmd =
359 0 : TALER_TESTING_interpreter_lookup_command (poc->is,
360 : poc->start_reference);
361 0 : if (NULL == poll_cmd)
362 0 : TALER_TESTING_FAIL (poc->is);
363 0 : pos = poll_cmd->cls;
364 0 : if (NULL != pos->ogh)
365 : {
366 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
367 : "Expected poll GET /private/orders to have completed, but it did not!\n");
368 0 : TALER_TESTING_FAIL (poc->is);
369 : }
370 0 : if (pos->http_status != poc->expected_http_status)
371 : {
372 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
373 : "Expected HTTP status %u, got %u\n",
374 : poc->expected_http_status,
375 : pos->http_status);
376 0 : TALER_TESTING_FAIL (poc->is);
377 : }
378 0 : now = GNUNET_TIME_absolute_get ();
379 0 : if (GNUNET_TIME_absolute_cmp (GNUNET_TIME_absolute_add (
380 : pos->deadline,
381 : GNUNET_TIME_UNIT_SECONDS),
382 : <,
383 : now))
384 : {
385 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
386 : "Expected answer to be delayed until %llu, but got response at %llu\n",
387 : (unsigned long long) pos->deadline.abs_value_us,
388 : (unsigned long long) now.abs_value_us);
389 0 : TALER_TESTING_FAIL (poc->is);
390 : }
391 0 : TALER_TESTING_interpreter_next (poc->is);
392 : }
393 :
394 :
395 : /**
396 : * Callback to process a GET /orders request
397 : *
398 : * @param cls closure
399 : * @param hr HTTP response details
400 : * @param orders_length how many orders are returned
401 : * @param orders the returned orders
402 : */
403 : static void
404 0 : merchant_poll_orders_cb (
405 : void *cls,
406 : const struct TALER_MERCHANT_HttpResponse *hr,
407 : unsigned int orders_length,
408 : const struct TALER_MERCHANT_OrderEntry orders[])
409 : {
410 0 : struct MerchantPollOrdersStartState *pos = cls;
411 :
412 0 : pos->ogh = NULL;
413 0 : if (MHD_HTTP_OK != hr->http_status)
414 : {
415 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
416 : "Unexpected response code %u (%d) to command %s\n",
417 : hr->http_status,
418 : (int) hr->ec,
419 : TALER_TESTING_interpreter_get_current_label (pos->is));
420 0 : TALER_TESTING_interpreter_fail (pos->is);
421 0 : return;
422 : }
423 0 : switch (hr->http_status)
424 : {
425 0 : case MHD_HTTP_OK:
426 : // FIXME: use order references to check if the data returned matches that from the POST / PATCH
427 0 : break;
428 0 : default:
429 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
430 : "Unhandled HTTP status.\n");
431 : }
432 0 : pos->http_status = hr->http_status;
433 0 : if (NULL != pos->cs)
434 : {
435 0 : GNUNET_SCHEDULER_cancel (pos->cs->task);
436 0 : pos->cs->task = GNUNET_SCHEDULER_add_now (&conclude_task,
437 0 : pos->cs);
438 : }
439 : }
440 :
441 :
442 : /**
443 : * Run the "GET orders" CMD.
444 : *
445 : * @param cls closure.
446 : * @param cmd command being run now.
447 : * @param is interpreter state.
448 : */
449 : static void
450 0 : merchant_poll_orders_start_run (void *cls,
451 : const struct TALER_TESTING_Command *cmd,
452 : struct TALER_TESTING_Interpreter *is)
453 : {
454 0 : struct MerchantPollOrdersStartState *pos = cls;
455 :
456 : /* add 1s grace time to timeout */
457 : pos->deadline
458 0 : = GNUNET_TIME_relative_to_absolute (
459 : GNUNET_TIME_relative_add (pos->timeout,
460 : GNUNET_TIME_UNIT_SECONDS));
461 0 : pos->is = is;
462 0 : pos->ogh = TALER_MERCHANT_orders_get2 (is->ctx,
463 : pos->merchant_url,
464 : TALER_EXCHANGE_YNA_ALL,
465 : TALER_EXCHANGE_YNA_ALL,
466 : TALER_EXCHANGE_YNA_ALL,
467 0 : GNUNET_TIME_UNIT_ZERO_TS,
468 : 1,
469 : 2,
470 : pos->timeout,
471 : &merchant_poll_orders_cb,
472 : pos);
473 0 : GNUNET_assert (NULL != pos->ogh);
474 : /* We CONTINUE to run the interpreter while the long-polled command
475 : completes asynchronously! */
476 0 : TALER_TESTING_interpreter_next (pos->is);
477 0 : }
478 :
479 :
480 : /**
481 : * Free the state of a "GET orders" CMD, and possibly
482 : * cancel a pending operation thereof.
483 : *
484 : * @param cls closure.
485 : * @param cmd command being run.
486 : */
487 : static void
488 0 : merchant_poll_orders_start_cleanup (void *cls,
489 : const struct TALER_TESTING_Command *cmd)
490 : {
491 0 : struct MerchantPollOrdersStartState *pos = cls;
492 :
493 0 : if (NULL != pos->ogh)
494 : {
495 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
496 : "Command `%s' was not terminated\n",
497 : TALER_TESTING_interpreter_get_current_label (
498 : pos->is));
499 0 : TALER_MERCHANT_orders_get_cancel (pos->ogh);
500 : }
501 0 : GNUNET_free (pos);
502 0 : }
503 :
504 :
505 : struct TALER_TESTING_Command
506 0 : TALER_TESTING_cmd_poll_orders_start (const char *label,
507 : const char *merchant_url,
508 : struct GNUNET_TIME_Relative timeout)
509 : {
510 : struct MerchantPollOrdersStartState *pos;
511 :
512 0 : pos = GNUNET_new (struct MerchantPollOrdersStartState);
513 0 : pos->merchant_url = merchant_url;
514 0 : pos->timeout = timeout;
515 : {
516 0 : struct TALER_TESTING_Command cmd = {
517 : .cls = pos,
518 : .label = label,
519 : .run = &merchant_poll_orders_start_run,
520 : .cleanup = &merchant_poll_orders_start_cleanup
521 : };
522 :
523 0 : return cmd;
524 : }
525 : }
526 :
527 :
528 : /**
529 : * Wait for the "GET orders" CMD to complete.
530 : *
531 : * @param cls closure.
532 : * @param cmd command being run now.
533 : * @param is interpreter state.
534 : */
535 : static void
536 0 : merchant_poll_orders_conclude_run (void *cls,
537 : const struct TALER_TESTING_Command *cmd,
538 : struct TALER_TESTING_Interpreter *is)
539 : {
540 0 : struct MerchantPollOrdersConcludeState *poc = cls;
541 : const struct TALER_TESTING_Command *poll_cmd;
542 : struct MerchantPollOrdersStartState *pos;
543 :
544 0 : poc->is = is;
545 : poll_cmd =
546 0 : TALER_TESTING_interpreter_lookup_command (is,
547 : poc->start_reference);
548 0 : if (NULL == poll_cmd)
549 0 : TALER_TESTING_FAIL (poc->is);
550 0 : GNUNET_assert (poll_cmd->run == &merchant_poll_orders_start_run);
551 0 : pos = poll_cmd->cls;
552 0 : pos->cs = poc;
553 0 : if (NULL == pos->ogh)
554 0 : poc->task = GNUNET_SCHEDULER_add_now (&conclude_task,
555 : poc);
556 : else
557 0 : poc->task = GNUNET_SCHEDULER_add_at (pos->deadline,
558 : &conclude_task,
559 : poc);
560 : }
561 :
562 :
563 : /**
564 : * Free the state of a "GET orders" CMD, and possibly
565 : * cancel a pending operation thereof.
566 : *
567 : * @param cls closure.
568 : * @param cmd command being run.
569 : */
570 : static void
571 0 : merchant_poll_orders_conclude_cleanup (void *cls,
572 : const struct TALER_TESTING_Command *cmd)
573 : {
574 0 : struct MerchantPollOrdersConcludeState *poc = cls;
575 :
576 0 : if (NULL != poc->task)
577 : {
578 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
579 : "Command `%s' was not terminated\n",
580 : TALER_TESTING_interpreter_get_current_label (
581 : poc->is));
582 0 : GNUNET_SCHEDULER_cancel (poc->task);
583 0 : poc->task = NULL;
584 : }
585 0 : GNUNET_free (poc);
586 0 : }
587 :
588 :
589 : struct TALER_TESTING_Command
590 0 : TALER_TESTING_cmd_poll_orders_conclude (const char *label,
591 : unsigned int http_status,
592 : const char *poll_start_reference)
593 : {
594 : struct MerchantPollOrdersConcludeState *poc;
595 :
596 0 : poc = GNUNET_new (struct MerchantPollOrdersConcludeState);
597 0 : poc->start_reference = poll_start_reference;
598 0 : poc->expected_http_status = http_status;
599 : {
600 0 : struct TALER_TESTING_Command cmd = {
601 : .cls = poc,
602 : .label = label,
603 : .run = &merchant_poll_orders_conclude_run,
604 : .cleanup = &merchant_poll_orders_conclude_cleanup
605 : };
606 :
607 0 : return cmd;
608 : }
609 : }
610 :
611 :
612 : /* end of testing_api_cmd_get_orders.c */
|