Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2014-2024 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Lesser General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file test_merchantdb.c
18 : * @brief testcase for merchant's postgres db plugin
19 : * @author Marcello Stanisci
20 : * @author Christian Grothoff
21 : * @author Pricilla Huang
22 : */
23 : #include "platform.h"
24 : #include "microhttpd.h"
25 : #include <taler/taler_util.h>
26 : #include <taler/taler_json_lib.h>
27 : #include <taler/taler_signatures.h>
28 : #include "taler_merchant_util.h"
29 : #include "taler_merchantdb_lib.h"
30 :
31 :
32 : /**
33 : * Global return value for the test. Initially -1, set to 0 upon
34 : * completion. Other values indicate some kind of error.
35 : */
36 : static int result;
37 :
38 : /**
39 : * Handle to the plugin we are testing.
40 : */
41 : static struct TALER_MERCHANTDB_Plugin *plugin;
42 :
43 : /**
44 : * @param test 0 on success, non-zero on failure
45 : */
46 : #define TEST_WITH_FAIL_CLAUSE(test, on_fail) \
47 : if ((test)) \
48 : { \
49 : GNUNET_break (0); \
50 : on_fail \
51 : }
52 :
53 : #define TEST_COND_RET_ON_FAIL(cond, msg) \
54 : if (! (cond)) \
55 : { \
56 : GNUNET_break (0); \
57 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR, \
58 : msg); \
59 : return 1; \
60 : }
61 :
62 : /**
63 : * @param __test 0 on success, non-zero on failure
64 : */
65 : #define TEST_RET_ON_FAIL(__test) \
66 : TEST_WITH_FAIL_CLAUSE (__test, \
67 : return 1; \
68 : )
69 :
70 :
71 : /* ********** Instances ********** */
72 :
73 :
74 : /**
75 : * Container for instance settings along with keys.
76 : */
77 : struct InstanceData
78 : {
79 : /**
80 : * The instance settings.
81 : */
82 : struct TALER_MERCHANTDB_InstanceSettings instance;
83 :
84 : /**
85 : * The public key for the instance.
86 : */
87 : struct TALER_MerchantPublicKeyP merchant_pub;
88 :
89 : /**
90 : * The private key for the instance.
91 : */
92 : struct TALER_MerchantPrivateKeyP merchant_priv;
93 : };
94 :
95 :
96 : /**
97 : * Creates data for an instance to use with testing.
98 : *
99 : * @param instance_id the identifier for this instance.
100 : * @param instance the instance data to be filled.
101 : */
102 : static void
103 0 : make_instance (const char *instance_id,
104 : struct InstanceData *instance)
105 : {
106 0 : memset (instance,
107 : 0,
108 : sizeof (*instance));
109 0 : GNUNET_CRYPTO_eddsa_key_create (&instance->merchant_priv.eddsa_priv);
110 0 : GNUNET_CRYPTO_eddsa_key_get_public (&instance->merchant_priv.eddsa_priv,
111 : &instance->merchant_pub.eddsa_pub);
112 0 : instance->instance.id = (char *) instance_id;
113 0 : instance->instance.name = (char *) "Test";
114 0 : instance->instance.address = json_array ();
115 0 : GNUNET_assert (NULL != instance->instance.address);
116 0 : GNUNET_assert (0 == json_array_append_new (instance->instance.address,
117 : json_string ("123 Example St")));
118 0 : instance->instance.jurisdiction = json_array ();
119 0 : GNUNET_assert (NULL != instance->instance.jurisdiction);
120 0 : GNUNET_assert (0 == json_array_append_new (instance->instance.jurisdiction,
121 : json_string ("Ohio")));
122 0 : instance->instance.use_stefan = true;
123 : instance->instance.default_wire_transfer_delay =
124 0 : GNUNET_TIME_relative_get_minute_ ();
125 0 : instance->instance.default_pay_delay = GNUNET_TIME_UNIT_SECONDS;
126 0 : instance->instance.default_refund_delay = GNUNET_TIME_UNIT_MINUTES;
127 0 : }
128 :
129 :
130 : /**
131 : * Frees memory allocated when creating an instance for testing.
132 : *
133 : * @param instance the instance containing the memory to be freed.
134 : */
135 : static void
136 0 : free_instance_data (struct InstanceData *instance)
137 : {
138 0 : json_decref (instance->instance.address);
139 0 : json_decref (instance->instance.jurisdiction);
140 0 : }
141 :
142 :
143 : /**
144 : * Creates an account with test data for an instance.
145 : *
146 : * @param account the account to initialize.
147 : */
148 : static void
149 0 : make_account (struct TALER_MERCHANTDB_AccountDetails *account)
150 : {
151 0 : memset (account,
152 : 0,
153 : sizeof (*account));
154 0 : GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG,
155 : &account->h_wire.hash);
156 0 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
157 0 : &account->salt,
158 : sizeof (account->salt));
159 : account->payto_uri.full_payto
160 0 : = (char *) "payto://x-taler-bank/bank.demo.taler.net/4";
161 0 : account->active = true;
162 0 : }
163 :
164 :
165 : /**
166 : * Instance settings along with corresponding accounts.
167 : */
168 : struct InstanceWithAccounts
169 : {
170 : /**
171 : * Pointer to the instance settings.
172 : */
173 : const struct TALER_MERCHANTDB_InstanceSettings *instance;
174 :
175 : /**
176 : * Length of the array of accounts.
177 : */
178 : unsigned int accounts_length;
179 :
180 : /**
181 : * Pointer to the array of accounts.
182 : */
183 : const struct TALER_MERCHANTDB_AccountDetails *accounts;
184 :
185 : };
186 :
187 :
188 : /**
189 : * Closure for testing instance lookup.
190 : */
191 : struct TestLookupInstances_Closure
192 : {
193 : /**
194 : * Number of instances to compare to.
195 : */
196 : unsigned int instances_to_cmp_length;
197 :
198 : /**
199 : * Pointer to array of instances.
200 : */
201 : const struct InstanceWithAccounts *instances_to_cmp;
202 :
203 : /**
204 : * Pointer to array of number of matches for each instance.
205 : */
206 : unsigned int *results_matching;
207 :
208 : /**
209 : * Total number of results returned.
210 : */
211 : unsigned int results_length;
212 : };
213 :
214 :
215 : /**
216 : * Compares two instances for equality.
217 : *
218 : * @param a the first instance.
219 : * @param b the second instance.
220 : * @return 0 on equality, 1 otherwise.
221 : */
222 : static int
223 0 : check_instances_equal (const struct TALER_MERCHANTDB_InstanceSettings *a,
224 : const struct TALER_MERCHANTDB_InstanceSettings *b)
225 : {
226 0 : if ((0 != strcmp (a->id,
227 0 : b->id)) ||
228 0 : (0 != strcmp (a->name,
229 0 : b->name)) ||
230 0 : (1 != json_equal (a->address,
231 0 : b->address)) ||
232 0 : (1 != json_equal (a->jurisdiction,
233 0 : b->jurisdiction)) ||
234 0 : (a->use_stefan != b->use_stefan) ||
235 0 : (a->default_wire_transfer_delay.rel_value_us !=
236 0 : b->default_wire_transfer_delay.rel_value_us) ||
237 0 : (a->default_pay_delay.rel_value_us != b->default_pay_delay.rel_value_us))
238 0 : return 1;
239 0 : return 0;
240 : }
241 :
242 :
243 : #if 0
244 : /**
245 : * Compares two accounts for equality.
246 : *
247 : * @param a the first account.
248 : * @param b the second account.
249 : * @return 0 on equality, 1 otherwise.
250 : */
251 : static int
252 : check_accounts_equal (const struct TALER_MERCHANTDB_AccountDetails *a,
253 : const struct TALER_MERCHANTDB_AccountDetails *b)
254 : {
255 : if ((0 != GNUNET_memcmp (&a->h_wire,
256 : &b->h_wire)) ||
257 : (0 != GNUNET_memcmp (&a->salt,
258 : &b->salt)) ||
259 : (0 != TALER_full_payto_cmp (a->payto_uri,
260 : b->payto_uri)) ||
261 : (a->active != b->active))
262 : return 1;
263 : return 0;
264 : }
265 :
266 :
267 : #endif
268 :
269 :
270 : /**
271 : * Called after testing 'lookup_instances'.
272 : *
273 : * @param cls pointer to 'struct TestLookupInstances_Closure'.
274 : * @param merchant_pub public key of the instance
275 : * @param merchant_priv private key of the instance, NULL if not available
276 : * @param is general instance settings
277 : * @param ias instance authentication settings
278 : */
279 : static void
280 0 : lookup_instances_cb (void *cls,
281 : const struct TALER_MerchantPublicKeyP *merchant_pub,
282 : const struct TALER_MerchantPrivateKeyP *merchant_priv,
283 : const struct TALER_MERCHANTDB_InstanceSettings *is,
284 : const struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
285 : {
286 0 : struct TestLookupInstances_Closure *cmp = cls;
287 :
288 0 : if (NULL == cmp)
289 0 : return;
290 0 : cmp->results_length += 1;
291 : /* Look through the closure and test each instance for equality */
292 0 : for (unsigned int i = 0; cmp->instances_to_cmp_length > i; ++i)
293 : {
294 0 : if (0 != check_instances_equal (cmp->instances_to_cmp[i].instance,
295 : is))
296 0 : continue;
297 0 : cmp->results_matching[i] += 1;
298 : }
299 : }
300 :
301 :
302 : /**
303 : * Tests @e insert_instance.
304 : *
305 : * @param instance the instance data to insert.
306 : * @param expected_result the result that should be returned from the plugin.
307 : * @return 0 on success, 1 on failure.
308 : */
309 : static int
310 0 : test_insert_instance (const struct InstanceData *instance,
311 : enum GNUNET_DB_QueryStatus expected_result)
312 : {
313 0 : struct TALER_MERCHANTDB_InstanceAuthSettings ias = { 0 };
314 :
315 0 : TEST_COND_RET_ON_FAIL (expected_result ==
316 : plugin->insert_instance (plugin->cls,
317 : &instance->merchant_pub,
318 : &instance->merchant_priv,
319 : &instance->instance,
320 : &ias,
321 : false),
322 : "Insert instance failed\n");
323 0 : return 0;
324 : }
325 :
326 :
327 : /**
328 : * Tests @e update_instance.
329 : *
330 : * @param updated_data the instance data to update the row in the database to.
331 : * @param expected_result the result that should be returned from the plugin.
332 : * @return 0 on success, 1 on failure.
333 : */
334 : static int
335 0 : test_update_instance (const struct InstanceData *updated_data,
336 : enum GNUNET_DB_QueryStatus expected_result)
337 : {
338 0 : TEST_COND_RET_ON_FAIL (expected_result ==
339 : plugin->update_instance (plugin->cls,
340 : &updated_data->instance),
341 : "Update instance failed\n");
342 0 : return 0;
343 : }
344 :
345 :
346 : /**
347 : * Tests @e lookup_instances.
348 : *
349 : * @param active_only whether to lookup all instance, or only active ones.
350 : * @param instances_length number of instances to compare to in @e instances.
351 : * @param instances a list of instances that will be compared with the results
352 : * found.
353 : * @return 0 on success, 1 otherwise.
354 : */
355 : static int
356 0 : test_lookup_instances (bool active_only,
357 : unsigned int instances_length,
358 : struct InstanceWithAccounts instances[])
359 0 : {
360 0 : unsigned int results_matching[GNUNET_NZL (instances_length)];
361 0 : struct TestLookupInstances_Closure cmp = {
362 : .instances_to_cmp_length = instances_length,
363 : .instances_to_cmp = instances,
364 : .results_matching = results_matching,
365 : .results_length = 0
366 : };
367 0 : memset (results_matching, 0, sizeof (unsigned int) * instances_length);
368 0 : if (0 > plugin->lookup_instances (plugin->cls,
369 : active_only,
370 : &lookup_instances_cb,
371 : &cmp))
372 : {
373 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
374 : "Lookup instances failed\n");
375 0 : return 1;
376 : }
377 0 : if (instances_length != cmp.results_length)
378 : {
379 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
380 : "Lookup instances failed: incorrect number of results\n");
381 0 : return 1;
382 : }
383 0 : for (unsigned int i = 0; instances_length > i; ++i)
384 : {
385 0 : if (1 != cmp.results_matching[i])
386 : {
387 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
388 : "Lookup instances failed: mismatched data\n");
389 0 : return 1;
390 : }
391 : }
392 0 : return 0;
393 : }
394 :
395 :
396 : /**
397 : * Tests removing the private key of the given instance from the database.
398 : *
399 : * @param instance the instance whose private key to delete.
400 : * @param expected_result the result we expect the db to return.
401 : * @return 0 on success, 1 otherwise.
402 : */
403 : static int
404 0 : test_delete_instance_private_key (const struct InstanceData *instance,
405 : enum GNUNET_DB_QueryStatus expected_result)
406 : {
407 0 : TEST_COND_RET_ON_FAIL (expected_result ==
408 : plugin->delete_instance_private_key (
409 : plugin->cls,
410 : instance->instance.id),
411 : "Delete instance private key failed\n");
412 0 : return 0;
413 : }
414 :
415 :
416 : /**
417 : * Tests purging all data for an instance from the database.
418 : *
419 : * @param instance the instance to purge.
420 : * @param expected_result the result we expect the db to return.
421 : * @return 0 on success, 1 otherwise.
422 : */
423 : static int
424 0 : test_purge_instance (const struct InstanceData *instance,
425 : enum GNUNET_DB_QueryStatus expected_result)
426 : {
427 0 : TEST_COND_RET_ON_FAIL (expected_result ==
428 : plugin->purge_instance (plugin->cls,
429 : instance->instance.id),
430 : "Purge instance failed\n");
431 0 : return 0;
432 : }
433 :
434 :
435 : /**
436 : * Tests inserting an account for a merchant instance.
437 : *
438 : * @param instance the instance to associate the account with.
439 : * @param account the account to insert.
440 : * @param expected_result the result expected from the db.
441 : * @return 0 on success, 1 otherwise.
442 : */
443 : static int
444 0 : test_insert_account (const struct InstanceData *instance,
445 : const struct TALER_MERCHANTDB_AccountDetails *account,
446 : enum GNUNET_DB_QueryStatus expected_result)
447 : {
448 0 : TEST_COND_RET_ON_FAIL (expected_result ==
449 : plugin->insert_account (plugin->cls,
450 : account),
451 : "Insert account failed\n");
452 0 : return 0;
453 : }
454 :
455 :
456 : /**
457 : * Tests deactivating an account.
458 : *
459 : * @param account the account to deactivate.
460 : * @param expected_result the result expected from the plugin.
461 : * @return 0 on success, 1 otherwise.
462 : */
463 : static int
464 0 : test_inactivate_account (const struct InstanceData *instance,
465 : const struct TALER_MERCHANTDB_AccountDetails *account,
466 : enum GNUNET_DB_QueryStatus expected_result)
467 : {
468 0 : TEST_COND_RET_ON_FAIL (expected_result ==
469 : plugin->inactivate_account (plugin->cls,
470 : instance->instance.id,
471 : &account->h_wire),
472 : "Deactivate account failed\n");
473 0 : return 0;
474 : }
475 :
476 :
477 : /**
478 : * Closure for instance tests
479 : */
480 : struct TestInstances_Closure
481 : {
482 : /**
483 : * The list of instances that we use for testing instances.
484 : */
485 : struct InstanceData instances[2];
486 :
487 : /**
488 : * The list of accounts to use with the instances.
489 : */
490 : struct TALER_MERCHANTDB_AccountDetails accounts[2];
491 :
492 : };
493 :
494 :
495 : /**
496 : * Sets up the data structures used in the instance tests
497 : *
498 : * @cls the closure to initialize with test data.
499 : */
500 : static void
501 0 : pre_test_instances (struct TestInstances_Closure *cls)
502 : {
503 : /* Instance */
504 0 : make_instance ("test_instances_inst0",
505 : &cls->instances[0]);
506 0 : make_instance ("test_instances_inst1",
507 : &cls->instances[1]);
508 :
509 : /* Accounts */
510 0 : make_account (&cls->accounts[0]);
511 : cls->accounts[0].instance_id
512 0 : = cls->instances[0].instance.id;
513 0 : make_account (&cls->accounts[1]);
514 : cls->accounts[1].instance_id
515 0 : = cls->instances[1].instance.id;
516 0 : }
517 :
518 :
519 : /**
520 : * Handles all teardown after testing
521 : *
522 : * @cls the closure to free data from
523 : */
524 : static void
525 0 : post_test_instances (struct TestInstances_Closure *cls)
526 : {
527 0 : free_instance_data (&cls->instances[0]);
528 0 : free_instance_data (&cls->instances[1]);
529 0 : }
530 :
531 :
532 : /**
533 : * Function that tests instances.
534 : *
535 : * @param cls closure with config
536 : * @return 0 on success, 1 if failure.
537 : */
538 : static int
539 0 : run_test_instances (struct TestInstances_Closure *cls)
540 : {
541 0 : struct InstanceWithAccounts instances[2] = {
542 : {
543 : .accounts_length = 0,
544 0 : .accounts = cls->accounts,
545 0 : .instance = &cls->instances[0].instance
546 : },
547 : {
548 : .accounts_length = 0,
549 0 : .accounts = cls->accounts,
550 0 : .instance = &cls->instances[1].instance
551 : }
552 : };
553 : uint64_t account_serial;
554 :
555 : /* Test inserting an instance */
556 0 : TEST_RET_ON_FAIL (test_insert_instance (&cls->instances[0],
557 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
558 : /* Test double insertion fails */
559 0 : TEST_RET_ON_FAIL (test_insert_instance (&cls->instances[0],
560 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
561 : /* Test lookup instances- is our new instance there? */
562 0 : TEST_RET_ON_FAIL (test_lookup_instances (false,
563 : 1,
564 : instances));
565 : /* Test update instance */
566 0 : cls->instances[0].instance.name = "Test - updated";
567 0 : json_array_append_new (cls->instances[0].instance.address,
568 : json_pack ("{s:s, s:I}",
569 : "this", "is",
570 : "more data", 47));
571 0 : json_array_append_new (cls->instances[0].instance.jurisdiction,
572 : json_pack ("{s:s}",
573 : "vegetables", "bad"));
574 0 : cls->instances[0].instance.use_stefan = false;
575 : cls->instances[0].instance.default_wire_transfer_delay =
576 0 : GNUNET_TIME_UNIT_HOURS;
577 0 : cls->instances[0].instance.default_pay_delay = GNUNET_TIME_UNIT_MINUTES;
578 :
579 0 : TEST_RET_ON_FAIL (test_update_instance (&cls->instances[0],
580 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
581 0 : TEST_RET_ON_FAIL (test_lookup_instances (false,
582 : 1,
583 : instances));
584 0 : TEST_RET_ON_FAIL (test_update_instance (&cls->instances[1],
585 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
586 : /* Test account creation */
587 0 : TEST_RET_ON_FAIL (test_insert_account (&cls->instances[0],
588 : &cls->accounts[0],
589 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
590 : /* Test double account insertion fails */
591 0 : TEST_RET_ON_FAIL (test_insert_account (&cls->instances[1],
592 : &cls->accounts[1],
593 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
594 0 : TEST_RET_ON_FAIL (test_insert_account (&cls->instances[0],
595 : &cls->accounts[0],
596 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
597 0 : instances[0].accounts_length = 1;
598 0 : TEST_RET_ON_FAIL (test_lookup_instances (false,
599 : 1,
600 : instances));
601 : /* Test deactivate account */
602 0 : TEST_RET_ON_FAIL (test_inactivate_account (&cls->instances[0],
603 : &cls->accounts[0],
604 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
605 0 : TEST_RET_ON_FAIL (test_inactivate_account (&cls->instances[1],
606 : &cls->accounts[1],
607 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
608 0 : cls->accounts[0].active = false;
609 0 : TEST_RET_ON_FAIL (test_lookup_instances (false,
610 : 1,
611 : instances));
612 : /* Test lookup account */
613 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
614 0 : plugin->lookup_account (plugin->cls,
615 0 : cls->instances[0].instance.id,
616 : cls->accounts[0].payto_uri,
617 : &account_serial))
618 : {
619 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
620 : "Lookup account failed\n");
621 0 : return 1;
622 : }
623 0 : if (1 != account_serial)
624 : {
625 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
626 : "Lookup account failed: incorrect serial number found\n");
627 0 : return 1;
628 : }
629 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
630 0 : plugin->lookup_account (plugin->cls,
631 0 : cls->instances[0].instance.id,
632 0 : (struct TALER_FullPayto) {
633 : (char *) "payto://other-uri"
634 : },
635 : &account_serial))
636 : {
637 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
638 : "Lookup account failed: account found where there is none\n");
639 0 : return 1;
640 : }
641 : /* Test instance private key deletion */
642 0 : TEST_RET_ON_FAIL (test_delete_instance_private_key (&cls->instances[0],
643 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
644 0 : TEST_RET_ON_FAIL (test_delete_instance_private_key (&cls->instances[1],
645 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
646 0 : TEST_RET_ON_FAIL (test_lookup_instances (true,
647 : 0,
648 : NULL));
649 0 : TEST_RET_ON_FAIL (test_lookup_instances (false,
650 : 1,
651 : instances));
652 0 : TEST_RET_ON_FAIL (test_insert_instance (&cls->instances[1],
653 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
654 0 : TEST_RET_ON_FAIL (test_lookup_instances (false,
655 : 2,
656 : instances));
657 0 : TEST_RET_ON_FAIL (test_lookup_instances (true,
658 : 1,
659 : &instances[1]));
660 0 : TEST_RET_ON_FAIL (test_purge_instance (&cls->instances[1],
661 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
662 0 : TEST_RET_ON_FAIL (test_purge_instance (&cls->instances[1],
663 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
664 : /* Test that the instance is gone. */
665 0 : TEST_RET_ON_FAIL (test_lookup_instances (false,
666 : 1,
667 : instances));
668 0 : return 0;
669 : }
670 :
671 :
672 : /**
673 : * Function that tests instances.
674 : *
675 : * @return 0 on success, 1 otherwise.
676 : */
677 : static int
678 0 : test_instances (void)
679 : {
680 : struct TestInstances_Closure test_cls;
681 : int test_result;
682 :
683 0 : pre_test_instances (&test_cls);
684 0 : test_result = run_test_instances (&test_cls);
685 0 : post_test_instances (&test_cls);
686 0 : return test_result;
687 : }
688 :
689 :
690 : /* *********** Products ********** */
691 :
692 :
693 : /**
694 : * A container for data relevant to a product.
695 : */
696 : struct ProductData
697 : {
698 : /**
699 : * The identifier of the product.
700 : */
701 : const char *id;
702 :
703 : /**
704 : * The details of the product.
705 : */
706 : struct TALER_MERCHANTDB_ProductDetails product;
707 : };
708 :
709 :
710 : /**
711 : * Creates a product for testing with.
712 : *
713 : * @param id the id of the product.
714 : * @param product the product data to fill.
715 : */
716 : static void
717 0 : make_product (const char *id,
718 : struct ProductData *product)
719 : {
720 0 : product->id = id;
721 0 : product->product.product_name = "Test product";
722 0 : product->product.description = "This is a test product";
723 0 : product->product.description_i18n = json_array ();
724 0 : GNUNET_assert (NULL != product->product.description_i18n);
725 0 : product->product.unit = "boxes";
726 0 : product->product.minimum_age = 0;
727 0 : GNUNET_assert (GNUNET_OK ==
728 : TALER_string_to_amount ("EUR:120.40",
729 : &product->product.price));
730 0 : product->product.taxes = json_array ();
731 0 : GNUNET_assert (NULL != product->product.taxes);
732 0 : product->product.total_stock = 55;
733 0 : product->product.total_sold = 0;
734 0 : product->product.total_lost = 0;
735 0 : product->product.image = GNUNET_strdup ("");
736 0 : GNUNET_assert (NULL != product->product.image);
737 0 : product->product.address = json_array ();
738 0 : GNUNET_assert (NULL != product->product.address);
739 0 : product->product.next_restock = GNUNET_TIME_UNIT_ZERO_TS;
740 0 : }
741 :
742 :
743 : /**
744 : * Frees memory associated with @e ProductData.
745 : *
746 : * @param product the container to free.
747 : */
748 : static void
749 0 : free_product_data (struct ProductData *product)
750 : {
751 0 : json_decref (product->product.description_i18n);
752 0 : json_decref (product->product.taxes);
753 0 : GNUNET_free (product->product.image);
754 0 : json_decref (product->product.address);
755 0 : }
756 :
757 :
758 : /**
759 : * Compare two products for equality.
760 : *
761 : * @param a the first product.
762 : * @param b the second product.
763 : * @return 0 on equality, 1 otherwise.
764 : */
765 : static int
766 0 : check_products_equal (const struct TALER_MERCHANTDB_ProductDetails *a,
767 : const struct TALER_MERCHANTDB_ProductDetails *b)
768 : {
769 0 : if ((0 != strcmp (a->description,
770 0 : b->description)) ||
771 0 : (1 != json_equal (a->description_i18n,
772 0 : b->description_i18n)) ||
773 0 : (0 != strcmp (a->unit,
774 0 : b->unit)) ||
775 : (GNUNET_OK !=
776 0 : TALER_amount_cmp_currency (&a->price,
777 0 : &b->price)) ||
778 0 : (0 != TALER_amount_cmp (&a->price,
779 0 : &b->price)) ||
780 0 : (1 != json_equal (a->taxes,
781 0 : b->taxes)) ||
782 0 : (a->total_stock != b->total_stock) ||
783 0 : (a->total_sold != b->total_sold) ||
784 0 : (a->total_lost != b->total_lost) ||
785 0 : (0 != strcmp (a->image,
786 0 : b->image)) ||
787 0 : (1 != json_equal (a->address,
788 0 : b->address)) ||
789 0 : (GNUNET_TIME_timestamp_cmp (a->next_restock,
790 : !=,
791 : b->next_restock)))
792 :
793 0 : return 1;
794 0 : return 0;
795 : }
796 :
797 :
798 : /**
799 : * Tests inserting product data into the database.
800 : *
801 : * @param instance the instance to insert the product for.
802 : * @param product the product data to insert.
803 : * @param num_cats length of the @a cats array
804 : * @param cats array of categories for the product
805 : * @param expected_result the result we expect the db to return.
806 : * @param expect_conflict expected conflict status
807 : * @param expect_no_instance expected instance missing status
808 : * @param expected_no_cat expected category missing index
809 : * @return 0 when successful, 1 otherwise.
810 : */
811 : static int
812 0 : test_insert_product (const struct InstanceData *instance,
813 : const struct ProductData *product,
814 : unsigned int num_cats,
815 : const uint64_t *cats,
816 : enum GNUNET_DB_QueryStatus expected_result,
817 : bool expect_conflict,
818 : bool expect_no_instance,
819 : ssize_t expected_no_cat)
820 : {
821 : bool conflict;
822 : bool no_instance;
823 : ssize_t no_cat;
824 :
825 0 : TEST_COND_RET_ON_FAIL (expected_result ==
826 : plugin->insert_product (plugin->cls,
827 : instance->instance.id,
828 : product->id,
829 : &product->product,
830 : num_cats,
831 : cats,
832 : &no_instance,
833 : &conflict,
834 : &no_cat),
835 : "Insert product failed\n");
836 0 : if (expected_result > 0)
837 : {
838 0 : TEST_COND_RET_ON_FAIL (no_instance == expect_no_instance,
839 : "No instance wrong");
840 0 : TEST_COND_RET_ON_FAIL (conflict == expect_conflict,
841 : "Conflict wrong");
842 0 : TEST_COND_RET_ON_FAIL (no_cat == expected_no_cat,
843 : "Wrong category missing returned");
844 : }
845 0 : return 0;
846 : }
847 :
848 :
849 : /**
850 : * Tests updating product data in the database.
851 : *
852 : * @param instance the instance to update the product for.
853 : * @param product the product data to update.
854 : * @param expected_result the result we expect the db to return.
855 : * @return 0 when successful, 1 otherwise.
856 : */
857 : static int
858 0 : test_update_product (const struct InstanceData *instance,
859 : const struct ProductData *product,
860 : unsigned int num_cats,
861 : const uint64_t *cats,
862 : enum GNUNET_DB_QueryStatus expected_result,
863 : bool expect_no_instance,
864 : bool expect_no_product,
865 : bool expect_lost_reduced,
866 : bool expect_sold_reduced,
867 : bool expect_stocked_reduced,
868 : ssize_t expected_no_cat)
869 :
870 : {
871 : bool no_instance;
872 : ssize_t no_cat;
873 : bool no_product;
874 : bool lost_reduced;
875 : bool sold_reduced;
876 : bool stocked_reduced;
877 :
878 0 : TEST_COND_RET_ON_FAIL (
879 : expected_result ==
880 : plugin->update_product (plugin->cls,
881 : instance->instance.id,
882 : product->id,
883 : &product->product,
884 : num_cats,
885 : cats,
886 : &no_instance,
887 : &no_cat,
888 : &no_product,
889 : &lost_reduced,
890 : &sold_reduced,
891 : &stocked_reduced),
892 : "Update product failed\n");
893 0 : if (expected_result > 0)
894 : {
895 0 : TEST_COND_RET_ON_FAIL (no_instance == expect_no_instance,
896 : "No instance wrong");
897 0 : TEST_COND_RET_ON_FAIL (no_product == expect_no_product,
898 : "No product wrong");
899 0 : TEST_COND_RET_ON_FAIL (lost_reduced == expect_lost_reduced,
900 : "No product wrong");
901 0 : TEST_COND_RET_ON_FAIL (stocked_reduced == expect_stocked_reduced,
902 : "Stocked reduced wrong");
903 0 : TEST_COND_RET_ON_FAIL (sold_reduced == expect_sold_reduced,
904 : "Sold reduced wrong");
905 0 : TEST_COND_RET_ON_FAIL (no_cat == expected_no_cat,
906 : "Wrong category missing returned");
907 : }
908 0 : return 0;
909 : }
910 :
911 :
912 : /**
913 : * Tests looking up a product from the db.
914 : *
915 : * @param instance the instance to query from.
916 : * @param product the product to query and compare to.
917 : * @return 0 when successful, 1 otherwise.
918 : */
919 : static int
920 0 : test_lookup_product (const struct InstanceData *instance,
921 : const struct ProductData *product)
922 : {
923 : struct TALER_MERCHANTDB_ProductDetails lookup_result;
924 0 : size_t num_categories = 0;
925 0 : uint64_t *categories = NULL;
926 :
927 0 : if (0 > plugin->lookup_product (plugin->cls,
928 0 : instance->instance.id,
929 0 : product->id,
930 : &lookup_result,
931 : &num_categories,
932 : &categories))
933 : {
934 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
935 : "Lookup product failed\n");
936 0 : TALER_MERCHANTDB_product_details_free (&lookup_result);
937 0 : return 1;
938 : }
939 0 : GNUNET_free (categories);
940 : {
941 0 : const struct TALER_MERCHANTDB_ProductDetails *to_cmp = &product->product;
942 :
943 0 : if (0 != check_products_equal (&lookup_result,
944 : to_cmp))
945 : {
946 0 : GNUNET_break (0);
947 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
948 : "Lookup product failed: incorrect product returned\n");
949 0 : TALER_MERCHANTDB_product_details_free (&lookup_result);
950 0 : return 1;
951 : }
952 : }
953 0 : TALER_MERCHANTDB_product_details_free (&lookup_result);
954 0 : return 0;
955 : }
956 :
957 :
958 : /**
959 : * Closure for testing product lookup
960 : */
961 : struct TestLookupProducts_Closure
962 : {
963 : /**
964 : * Number of product ids to compare to
965 : */
966 : unsigned int products_to_cmp_length;
967 :
968 : /**
969 : * Pointer to array of product ids
970 : */
971 : const struct ProductData *products_to_cmp;
972 :
973 : /**
974 : * Pointer to array of number of matches for each product
975 : */
976 : unsigned int *results_matching;
977 :
978 : /**
979 : * Total number of results returned
980 : */
981 : unsigned int results_length;
982 : };
983 :
984 :
985 : /**
986 : * Function called after calling @e test_lookup_products
987 : *
988 : * @param cls a pointer to the lookup closure.
989 : * @param product_serial DB row ID
990 : * @param product_id the identifier of the product found.
991 : */
992 : static void
993 0 : lookup_products_cb (void *cls,
994 : uint64_t product_serial,
995 : const char *product_id)
996 : {
997 0 : struct TestLookupProducts_Closure *cmp = cls;
998 :
999 0 : GNUNET_assert (product_serial > 0);
1000 0 : if (NULL == cmp)
1001 0 : return;
1002 0 : cmp->results_length += 1;
1003 0 : for (unsigned int i = 0; cmp->products_to_cmp_length > i; ++i)
1004 : {
1005 0 : if (0 == strcmp (cmp->products_to_cmp[i].id,
1006 : product_id))
1007 0 : cmp->results_matching[i] += 1;
1008 : }
1009 : }
1010 :
1011 :
1012 : /**
1013 : * Tests looking up all products for an instance.
1014 : *
1015 : * @param instance the instance to query from.
1016 : * @param products_length the number of products we are expecting.
1017 : * @param products the list of products that we expect to be found.
1018 : * @return 0 when successful, 1 otherwise.
1019 : */
1020 : static int
1021 0 : test_lookup_products (const struct InstanceData *instance,
1022 : unsigned int products_length,
1023 : const struct ProductData *products)
1024 0 : {
1025 0 : unsigned int results_matching[GNUNET_NZL (products_length)];
1026 0 : struct TestLookupProducts_Closure cls = {
1027 : .products_to_cmp_length = products_length,
1028 : .products_to_cmp = products,
1029 : .results_matching = results_matching,
1030 : .results_length = 0
1031 : };
1032 0 : memset (results_matching, 0, sizeof (unsigned int) * products_length);
1033 0 : if (0 > plugin->lookup_products (plugin->cls,
1034 0 : instance->instance.id,
1035 : 0,
1036 : 20,
1037 : NULL,
1038 : NULL,
1039 : NULL,
1040 : &lookup_products_cb,
1041 : &cls))
1042 : {
1043 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1044 : "Lookup products failed\n");
1045 0 : return 1;
1046 : }
1047 0 : if (products_length != cls.results_length)
1048 : {
1049 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1050 : "Lookup products failed: incorrect number of results\n");
1051 0 : return 1;
1052 : }
1053 0 : for (unsigned int i = 0; products_length > i; ++i)
1054 : {
1055 0 : if (1 != cls.results_matching[i])
1056 : {
1057 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1058 : "Lookup products failed: mismatched data\n");
1059 0 : return 1;
1060 : }
1061 : }
1062 0 : return 0;
1063 : }
1064 :
1065 :
1066 : /**
1067 : * Tests deleting a product.
1068 : *
1069 : * @param instance the instance to delete the product from.
1070 : * @param product the product that should be deleted.
1071 : * @param expected_result the result that we expect the plugin to return.
1072 : * @return 0 when successful, 1 otherwise.
1073 : */
1074 : static int
1075 0 : test_delete_product (const struct InstanceData *instance,
1076 : const struct ProductData *product,
1077 : enum GNUNET_DB_QueryStatus expected_result)
1078 : {
1079 0 : TEST_COND_RET_ON_FAIL (expected_result ==
1080 : plugin->delete_product (plugin->cls,
1081 : instance->instance.id,
1082 : product->id),
1083 : "Delete product failed\n");
1084 0 : return 0;
1085 : }
1086 :
1087 :
1088 : /**
1089 : * Closure for product tests.
1090 : */
1091 : struct TestProducts_Closure
1092 : {
1093 : /**
1094 : * The instance to use for this test.
1095 : */
1096 : struct InstanceData instance;
1097 :
1098 : /**
1099 : * The array of products.
1100 : */
1101 : struct ProductData products[2];
1102 : };
1103 :
1104 :
1105 : /**
1106 : * Sets up the data structures used in the product tests.
1107 : *
1108 : * @param cls the closure to fill with test data.
1109 : */
1110 : static void
1111 0 : pre_test_products (struct TestProducts_Closure *cls)
1112 : {
1113 : /* Instance */
1114 0 : make_instance ("test_inst_products",
1115 : &cls->instance);
1116 :
1117 : /* Products */
1118 0 : make_product ("test_products_pd_0",
1119 : &cls->products[0]);
1120 :
1121 0 : make_product ("test_products_pd_1",
1122 : &cls->products[1]);
1123 0 : cls->products[1].product.description = "This is a another test product";
1124 0 : cls->products[1].product.unit = "cans";
1125 0 : cls->products[1].product.minimum_age = 0;
1126 0 : GNUNET_assert (GNUNET_OK ==
1127 : TALER_string_to_amount ("EUR:4.95",
1128 : &cls->products[1].product.price));
1129 0 : cls->products[1].product.total_stock = 5001;
1130 0 : }
1131 :
1132 :
1133 : /**
1134 : * Handles all teardown after testing.
1135 : *
1136 : * @param cls the closure containing memory to be freed.
1137 : */
1138 : static void
1139 0 : post_test_products (struct TestProducts_Closure *cls)
1140 : {
1141 0 : free_instance_data (&cls->instance);
1142 0 : free_product_data (&cls->products[0]);
1143 0 : free_product_data (&cls->products[1]);
1144 0 : }
1145 :
1146 :
1147 : /**
1148 : * Runs the tests for products.
1149 : *
1150 : * @param cls the container of the test data.
1151 : * @return 0 on success, 1 otherwise.
1152 : */
1153 : static int
1154 0 : run_test_products (struct TestProducts_Closure *cls)
1155 : {
1156 : struct GNUNET_Uuid uuid;
1157 : struct GNUNET_TIME_Timestamp refund_deadline =
1158 0 : GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_WEEKS);
1159 :
1160 : /* Test that insert without an instance fails */
1161 0 : TEST_RET_ON_FAIL (test_insert_product (&cls->instance,
1162 : &cls->products[0],
1163 : 0,
1164 : NULL,
1165 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
1166 : false,
1167 : true,
1168 : -1));
1169 : /* Insert the instance */
1170 0 : TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
1171 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
1172 : /* Test inserting a product */
1173 0 : TEST_RET_ON_FAIL (test_insert_product (&cls->instance,
1174 : &cls->products[0],
1175 : 0,
1176 : NULL,
1177 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
1178 : false,
1179 : false,
1180 : -1));
1181 : /* Test that double insert succeeds */
1182 0 : TEST_RET_ON_FAIL (test_insert_product (&cls->instance,
1183 : &cls->products[0],
1184 : 0,
1185 : NULL,
1186 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
1187 : false,
1188 : false,
1189 : -1));
1190 : /* Test that conflicting insert fails */
1191 : {
1192 0 : uint64_t cat = 42;
1193 :
1194 0 : TEST_RET_ON_FAIL (test_insert_product (&cls->instance,
1195 : &cls->products[0],
1196 : 1,
1197 : &cat,
1198 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
1199 : true,
1200 : false,
1201 : -1));
1202 : }
1203 : /* Test lookup of individual products */
1204 0 : TEST_RET_ON_FAIL (test_lookup_product (&cls->instance,
1205 : &cls->products[0]));
1206 : /* Make sure it fails correctly for products that don't exist */
1207 : {
1208 0 : size_t num_categories = 0;
1209 0 : uint64_t *categories = NULL;
1210 :
1211 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
1212 0 : plugin->lookup_product (plugin->cls,
1213 0 : cls->instance.instance.id,
1214 : "nonexistent_product",
1215 : NULL,
1216 : &num_categories,
1217 : &categories))
1218 : {
1219 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1220 : "Lookup product failed\n");
1221 0 : return 1;
1222 : }
1223 0 : GNUNET_free (categories);
1224 : }
1225 : /* Test product update */
1226 0 : cls->products[0].product.description =
1227 : "This is a test product that has been updated!";
1228 0 : GNUNET_assert (0 ==
1229 : json_array_append_new (
1230 : cls->products[0].product.description_i18n,
1231 : json_string (
1232 : "description in another language")));
1233 0 : cls->products[0].product.unit = "barrels";
1234 0 : GNUNET_assert (GNUNET_OK ==
1235 : TALER_string_to_amount ("EUR:7.68",
1236 : &cls->products[0].product.price));
1237 0 : GNUNET_assert (0 ==
1238 : json_array_append_new (cls->products[0].product.taxes,
1239 : json_string ("2% sales tax")));
1240 0 : cls->products[0].product.total_stock = 100;
1241 0 : cls->products[0].product.total_sold = 0; /* will be ignored! */
1242 0 : cls->products[0].product.total_lost = 7;
1243 0 : GNUNET_free (cls->products[0].product.image);
1244 0 : cls->products[0].product.image = GNUNET_strdup ("image");
1245 0 : GNUNET_assert (0 ==
1246 : json_array_append_new (cls->products[0].product.address,
1247 : json_string ("444 Some Street")));
1248 0 : cls->products[0].product.next_restock = GNUNET_TIME_timestamp_get ();
1249 0 : TEST_RET_ON_FAIL (test_update_product (
1250 : &cls->instance,
1251 : &cls->products[0],
1252 : 0,
1253 : NULL,
1254 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
1255 : false,
1256 : false,
1257 : false,
1258 : false,
1259 : false,
1260 : -1));
1261 :
1262 : {
1263 0 : struct ProductData stock_dec = cls->products[0];
1264 :
1265 0 : stock_dec.product.total_stock = 40;
1266 0 : TEST_RET_ON_FAIL (test_update_product (
1267 : &cls->instance,
1268 : &stock_dec,
1269 : 0,
1270 : NULL,
1271 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
1272 : false,
1273 : false,
1274 : false,
1275 : false,
1276 : true,
1277 : -1));
1278 : }
1279 : {
1280 0 : struct ProductData lost_dec = cls->products[0];
1281 :
1282 0 : lost_dec.product.total_lost = 1;
1283 0 : TEST_RET_ON_FAIL (test_update_product (
1284 : &cls->instance,
1285 : &lost_dec,
1286 : 0,
1287 : NULL,
1288 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
1289 : false,
1290 : false,
1291 : true,
1292 : false,
1293 : false,
1294 : -1));
1295 : }
1296 0 : TEST_RET_ON_FAIL (test_lookup_product (&cls->instance,
1297 : &cls->products[0]));
1298 0 : TEST_RET_ON_FAIL (test_update_product (
1299 : &cls->instance,
1300 : &cls->products[1],
1301 : 0,
1302 : NULL,
1303 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
1304 : false,
1305 : true,
1306 : false,
1307 : false,
1308 : false,
1309 : -1));
1310 : /* Test collective product lookup */
1311 0 : TEST_RET_ON_FAIL (test_insert_product (&cls->instance,
1312 : &cls->products[1],
1313 : 0,
1314 : NULL,
1315 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
1316 : false,
1317 : false,
1318 : -1));
1319 0 : TEST_RET_ON_FAIL (test_lookup_products (&cls->instance,
1320 : 2,
1321 : cls->products));
1322 : /* Test locking */
1323 0 : uuid.value[0] = 0x1287346a;
1324 0 : if (0 != plugin->lock_product (plugin->cls,
1325 0 : cls->instance.instance.id,
1326 : cls->products[0].id,
1327 : &uuid,
1328 : 256,
1329 : refund_deadline))
1330 : {
1331 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1332 : "Lock product failed\n");
1333 0 : return 1;
1334 : }
1335 0 : if (1 != plugin->lock_product (plugin->cls,
1336 0 : cls->instance.instance.id,
1337 : cls->products[0].id,
1338 : &uuid,
1339 : 1,
1340 : refund_deadline))
1341 : {
1342 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1343 : "Lock product failed\n");
1344 0 : return 1;
1345 : }
1346 : /* Test product deletion */
1347 0 : TEST_RET_ON_FAIL (test_delete_product (&cls->instance,
1348 : &cls->products[1],
1349 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
1350 : /* Test double deletion fails */
1351 0 : TEST_RET_ON_FAIL (test_delete_product (&cls->instance,
1352 : &cls->products[1],
1353 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
1354 0 : TEST_RET_ON_FAIL (test_lookup_products (&cls->instance,
1355 : 1,
1356 : cls->products));
1357 : /* Test unlocking */
1358 0 : if (1 != plugin->unlock_inventory (plugin->cls,
1359 : &uuid))
1360 : {
1361 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1362 : "Unlock inventory failed\n");
1363 0 : return 1;
1364 : }
1365 0 : if (0 != plugin->unlock_inventory (plugin->cls,
1366 : &uuid))
1367 : {
1368 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1369 : "Unlock inventory failed\n");
1370 0 : return 1;
1371 : }
1372 0 : TEST_RET_ON_FAIL (test_delete_product (&cls->instance,
1373 : &cls->products[0],
1374 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
1375 0 : TEST_RET_ON_FAIL (test_lookup_products (&cls->instance,
1376 : 0,
1377 : NULL));
1378 0 : return 0;
1379 : }
1380 :
1381 :
1382 : /**
1383 : * Takes care of product testing.
1384 : *
1385 : * @return 0 on success, 1 otherwise.
1386 : */
1387 : static int
1388 0 : test_products (void)
1389 : {
1390 : struct TestProducts_Closure test_cls;
1391 : int test_result;
1392 :
1393 0 : pre_test_products (&test_cls);
1394 0 : test_result = run_test_products (&test_cls);
1395 0 : post_test_products (&test_cls);
1396 0 : return test_result;
1397 : }
1398 :
1399 :
1400 : /* ********** Orders ********** */
1401 :
1402 :
1403 : /**
1404 : * Container for order data
1405 : */
1406 : struct OrderData
1407 : {
1408 : /**
1409 : * The id of the order
1410 : */
1411 : const char *id;
1412 :
1413 : /**
1414 : * The pay deadline for the order
1415 : */
1416 : struct GNUNET_TIME_Timestamp pay_deadline;
1417 :
1418 : /**
1419 : * The contract of the order
1420 : */
1421 : json_t *contract;
1422 :
1423 : /**
1424 : * The claim token for the order.
1425 : */
1426 : struct TALER_ClaimTokenP claim_token;
1427 : };
1428 :
1429 :
1430 : /**
1431 : * Builds an order for testing.
1432 : *
1433 : * @param order_id the identifier to use for the order.
1434 : * @param order the container to fill with data.
1435 : */
1436 : static void
1437 0 : make_order (const char *order_id,
1438 : struct OrderData *order)
1439 : {
1440 : struct GNUNET_TIME_Timestamp refund_deadline;
1441 :
1442 0 : order->id = order_id;
1443 0 : order->contract = json_object ();
1444 0 : GNUNET_assert (NULL != order->contract);
1445 0 : order->pay_deadline = GNUNET_TIME_relative_to_timestamp (
1446 : GNUNET_TIME_UNIT_DAYS);
1447 0 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
1448 0 : &order->claim_token,
1449 : sizeof (order->claim_token));
1450 0 : refund_deadline = GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_WEEKS);
1451 0 : GNUNET_assert (0 ==
1452 : json_object_set_new (order->contract,
1453 : "fulfillment_url",
1454 : json_string ("a")));
1455 0 : GNUNET_assert (0 ==
1456 : json_object_set_new (order->contract,
1457 : "summary",
1458 : json_string ("Test order")));
1459 0 : GNUNET_assert (0 ==
1460 : json_object_set_new (order->contract,
1461 : "order_id",
1462 : json_string (order_id)));
1463 0 : GNUNET_assert (0 ==
1464 : json_object_set_new (
1465 : order->contract,
1466 : "pay_deadline",
1467 : GNUNET_JSON_from_timestamp (order->pay_deadline))
1468 : );
1469 0 : GNUNET_assert (0 ==
1470 : json_object_set_new (order->contract,
1471 : "refund_deadline",
1472 : GNUNET_JSON_from_timestamp (
1473 : refund_deadline)));
1474 0 : }
1475 :
1476 :
1477 : /**
1478 : * Frees memory associated with an order.
1479 : *
1480 : * @param the order to free.
1481 : */
1482 : static void
1483 0 : free_order_data (struct OrderData *order)
1484 : {
1485 0 : json_decref (order->contract);
1486 0 : }
1487 :
1488 :
1489 : /**
1490 : * Tests inserting an order into the database.
1491 : *
1492 : * @param instance the instance to insert the order for.
1493 : * @param order the order to insert.
1494 : * @param expected_result the value we expect the db to return.
1495 : * @return 0 on success, 1 otherwise.
1496 : */
1497 : static int
1498 0 : test_insert_order (const struct InstanceData *instance,
1499 : const struct OrderData *order,
1500 : enum GNUNET_DB_QueryStatus expected_result)
1501 : {
1502 : struct TALER_MerchantPostDataHashP h_post;
1503 :
1504 0 : memset (&h_post,
1505 : 42,
1506 : sizeof (h_post));
1507 0 : TEST_COND_RET_ON_FAIL (expected_result ==
1508 : plugin->insert_order (plugin->cls,
1509 : instance->instance.id,
1510 : order->id,
1511 : NULL, /* session_id */
1512 : &h_post,
1513 : order->pay_deadline,
1514 : &order->claim_token,
1515 : order->contract,
1516 : NULL,
1517 : 0),
1518 : "Insert order failed\n");
1519 0 : return 0;
1520 : }
1521 :
1522 :
1523 : /**
1524 : * Tests looking up an order in the database.
1525 : *
1526 : * @param instance the instance to lookup from.
1527 : * @param order the order that should be looked up.
1528 : * @return 0 on success, 1 otherwise.
1529 : */
1530 : static int
1531 0 : test_lookup_order (const struct InstanceData *instance,
1532 : const struct OrderData *order)
1533 : {
1534 : struct TALER_ClaimTokenP ct;
1535 0 : json_t *lookup_terms = NULL;
1536 : struct TALER_MerchantPostDataHashP oh;
1537 : struct TALER_MerchantPostDataHashP wh;
1538 :
1539 0 : memset (&wh,
1540 : 42,
1541 : sizeof (wh));
1542 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
1543 0 : plugin->lookup_order (plugin->cls,
1544 0 : instance->instance.id,
1545 0 : order->id,
1546 : &ct,
1547 : &oh,
1548 : &lookup_terms))
1549 : {
1550 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1551 : "Lookup order failed\n");
1552 0 : if (NULL != lookup_terms)
1553 0 : json_decref (lookup_terms);
1554 0 : return 1;
1555 : }
1556 0 : if ( (1 != json_equal (order->contract,
1557 0 : lookup_terms)) ||
1558 0 : (0 != GNUNET_memcmp (&order->claim_token,
1559 0 : &ct)) ||
1560 0 : (0 != GNUNET_memcmp (&oh,
1561 : &wh)) )
1562 : {
1563 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1564 : "Lookup order failed: incorrect order returned\n");
1565 0 : if (NULL != lookup_terms)
1566 0 : json_decref (lookup_terms);
1567 0 : return 1;
1568 : }
1569 0 : json_decref (lookup_terms);
1570 0 : return 0;
1571 : }
1572 :
1573 :
1574 : /**
1575 : * Closure for testing order lookup
1576 : */
1577 : struct TestLookupOrders_Closure
1578 : {
1579 : /**
1580 : * Number of orders to compare to
1581 : */
1582 : unsigned int orders_to_cmp_length;
1583 :
1584 : /**
1585 : * Pointer to (ordered) array of order ids
1586 : */
1587 : const struct OrderData *orders_to_cmp;
1588 :
1589 : /**
1590 : * Pointer to array of bools indicating matches in the correct index
1591 : */
1592 : bool *results_match;
1593 :
1594 : /**
1595 : * Total number of results returned
1596 : */
1597 : unsigned int results_length;
1598 : };
1599 :
1600 :
1601 : /**
1602 : * Called after @e test_lookup_orders.
1603 : *
1604 : * @param cls the lookup closure.
1605 : * @param order_id the identifier of the order found.
1606 : * @param order_serial the row number of the order found.
1607 : * @param timestamp when the order was added to the database.
1608 : */
1609 : static void
1610 0 : lookup_orders_cb (void *cls,
1611 : const char *order_id,
1612 : uint64_t order_serial,
1613 : struct GNUNET_TIME_Timestamp timestamp)
1614 : {
1615 0 : struct TestLookupOrders_Closure *cmp = cls;
1616 : unsigned int i;
1617 :
1618 0 : if (NULL == cmp)
1619 0 : return;
1620 0 : i = cmp->results_length;
1621 0 : cmp->results_length += 1;
1622 0 : if (cmp->orders_to_cmp_length > i)
1623 : {
1624 : /* Compare the orders */
1625 0 : if (0 == strcmp (cmp->orders_to_cmp[i].id,
1626 : order_id))
1627 0 : cmp->results_match[i] = true;
1628 : else
1629 0 : cmp->results_match[i] = false;
1630 : }
1631 : }
1632 :
1633 :
1634 : /**
1635 : * Tests looking up orders for an instance.
1636 : *
1637 : * @param instance the instance.
1638 : * @param filter the filters applied on the lookup.
1639 : * @param orders_length the number of orders we expect to find.
1640 : * @param orders the orders we expect to find.
1641 : * @return 0 on success, 1 otherwise.
1642 : */
1643 : static int
1644 0 : test_lookup_orders (const struct InstanceData *instance,
1645 : const struct TALER_MERCHANTDB_OrderFilter *filter,
1646 : unsigned int orders_length,
1647 : const struct OrderData *orders)
1648 0 : {
1649 0 : bool results_match[GNUNET_NZL (orders_length)];
1650 0 : struct TestLookupOrders_Closure cls = {
1651 : .orders_to_cmp_length = orders_length,
1652 : .orders_to_cmp = orders,
1653 : .results_match = results_match,
1654 : .results_length = 0
1655 : };
1656 0 : memset (results_match,
1657 : 0,
1658 : sizeof (bool) * orders_length);
1659 0 : if (0 > plugin->lookup_orders (plugin->cls,
1660 0 : instance->instance.id,
1661 : filter,
1662 : &lookup_orders_cb,
1663 : &cls))
1664 : {
1665 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1666 : "Lookup orders failed\n");
1667 0 : return 1;
1668 : }
1669 0 : if (orders_length != cls.results_length)
1670 : {
1671 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1672 : "Lookup orders failed: incorrect number of results (%d)\n",
1673 : cls.results_length);
1674 0 : return 1;
1675 : }
1676 0 : for (unsigned int i = 0; orders_length > i; ++i)
1677 : {
1678 0 : if (false == cls.results_match[i])
1679 : {
1680 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1681 : "Lookup orders failed: mismatched data (index %d)\n",
1682 : i);
1683 0 : return 1;
1684 : }
1685 : }
1686 0 : return 0;
1687 : }
1688 :
1689 :
1690 : /**
1691 : * Container for data used for looking up the row number of an order.
1692 : */
1693 : struct LookupOrderSerial_Closure
1694 : {
1695 : /**
1696 : * The order that is being looked up.
1697 : */
1698 : const struct OrderData *order;
1699 :
1700 : /**
1701 : * The serial of the order that was found.
1702 : */
1703 : uint64_t serial;
1704 : };
1705 :
1706 :
1707 : /**
1708 : * Called after @e test_lookup_orders.
1709 : *
1710 : * @param cls the lookup closure.
1711 : * @param order_id the identifier of the order found.
1712 : * @param order_serial the row number of the order found.
1713 : * @param timestamp when the order was added to the database.
1714 : */
1715 : static void
1716 0 : get_order_serial_cb (void *cls,
1717 : const char *order_id,
1718 : uint64_t order_serial,
1719 : struct GNUNET_TIME_Timestamp timestamp)
1720 : {
1721 0 : struct LookupOrderSerial_Closure *lookup_cls = cls;
1722 0 : if (NULL == lookup_cls)
1723 0 : return;
1724 0 : if (0 == strcmp (lookup_cls->order->id,
1725 : order_id))
1726 0 : lookup_cls->serial = order_serial;
1727 : }
1728 :
1729 :
1730 : /**
1731 : * Convenience function for getting the row number of an order.
1732 : *
1733 : * @param instance the instance to look up from.
1734 : * @param order the order to lookup the serial for.
1735 : * @return the row number of the order.
1736 : */
1737 : static uint64_t
1738 0 : get_order_serial (const struct InstanceData *instance,
1739 : const struct OrderData *order)
1740 : {
1741 0 : struct LookupOrderSerial_Closure lookup_cls = {
1742 : .order = order,
1743 : .serial = 0
1744 : };
1745 0 : struct TALER_MERCHANTDB_OrderFilter filter = {
1746 : .paid = TALER_EXCHANGE_YNA_ALL,
1747 : .refunded = TALER_EXCHANGE_YNA_ALL,
1748 : .wired = TALER_EXCHANGE_YNA_ALL,
1749 : .date = GNUNET_TIME_UNIT_ZERO_TS,
1750 : .start_row = 0,
1751 : .delta = 256
1752 : };
1753 :
1754 0 : GNUNET_assert (0 < plugin->lookup_orders (plugin->cls,
1755 : instance->instance.id,
1756 : &filter,
1757 : &get_order_serial_cb,
1758 : &lookup_cls));
1759 0 : GNUNET_assert (0 != lookup_cls.serial);
1760 :
1761 0 : return lookup_cls.serial;
1762 : }
1763 :
1764 :
1765 : /**
1766 : * Tests deleting an order from the database.
1767 : *
1768 : * @param instance the instance to delete the order from.
1769 : * @param order the order to delete.
1770 : * @param expected_result the result we expect to receive.
1771 : * @return 0 on success, 1 otherwise.
1772 : */
1773 : static int
1774 0 : test_delete_order (const struct InstanceData *instance,
1775 : const struct OrderData *order,
1776 : enum GNUNET_DB_QueryStatus expected_result)
1777 : {
1778 0 : TEST_COND_RET_ON_FAIL (expected_result ==
1779 : plugin->delete_order (plugin->cls,
1780 : instance->instance.id,
1781 : order->id,
1782 : false),
1783 : "Delete order failed\n");
1784 0 : return 0;
1785 : }
1786 :
1787 :
1788 : /**
1789 : * Test inserting contract terms for an order.
1790 : *
1791 : * @param instance the instance.
1792 : * @param order the order containing the contract terms.
1793 : * @param expected_result the result we expect to receive.
1794 : * @return 0 on success, 1 otherwise.
1795 : */
1796 : static int
1797 0 : test_insert_contract_terms (const struct InstanceData *instance,
1798 : const struct OrderData *order,
1799 : enum GNUNET_DB_QueryStatus expected_result)
1800 : {
1801 : uint64_t os;
1802 :
1803 0 : TEST_COND_RET_ON_FAIL (expected_result ==
1804 : plugin->insert_contract_terms (plugin->cls,
1805 : instance->instance.id,
1806 : order->id,
1807 : order->contract,
1808 : &os),
1809 : "Insert contract terms failed\n");
1810 0 : return 0;
1811 : }
1812 :
1813 :
1814 : /**
1815 : * Test updating contract terms for an order.
1816 : *
1817 : * @param instance the instance.
1818 : * @param order the order containing the contract terms.
1819 : * @param expected_result the result we expect to receive.
1820 : * @return 0 on success, 1 otherwise.
1821 : */
1822 : static int
1823 0 : test_update_contract_terms (const struct InstanceData *instance,
1824 : const struct OrderData *order,
1825 : enum GNUNET_DB_QueryStatus expected_result)
1826 : {
1827 0 : TEST_COND_RET_ON_FAIL (expected_result ==
1828 : plugin->update_contract_terms (plugin->cls,
1829 : instance->instance.id,
1830 : order->id,
1831 : order->contract),
1832 : "Update contract terms failed\n");
1833 0 : return 0;
1834 : }
1835 :
1836 :
1837 : /**
1838 : * Tests lookup of contract terms
1839 : *
1840 : * @param instance the instance to lookup from.
1841 : * @param order the order to lookup for.
1842 : * @return 0 on success, 1 otherwise.
1843 : */
1844 : static int
1845 0 : test_lookup_contract_terms (const struct InstanceData *instance,
1846 : const struct OrderData *order)
1847 : {
1848 0 : json_t *contract = NULL;
1849 : uint64_t order_serial;
1850 :
1851 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
1852 0 : plugin->lookup_contract_terms (plugin->cls,
1853 0 : instance->instance.id,
1854 0 : order->id,
1855 : &contract,
1856 : &order_serial,
1857 : NULL))
1858 : {
1859 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1860 : "Lookup contract terms failed\n");
1861 0 : GNUNET_assert (NULL == contract);
1862 0 : return 1;
1863 : }
1864 0 : if (1 != json_equal (order->contract,
1865 : contract))
1866 : {
1867 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1868 : "Lookup contract terms failed: mismatched data\n");
1869 0 : json_decref (contract);
1870 0 : return 1;
1871 : }
1872 0 : json_decref (contract);
1873 0 : return 0;
1874 : }
1875 :
1876 :
1877 : /**
1878 : * Tests deleting contract terms for an order.
1879 : *
1880 : * @param instance the instance to delete from.
1881 : * @param order the order whose contract terms we should delete.
1882 : * @param legal_expiration how long we must wait after creating an order to delete it
1883 : * @param expected_result the result we expect to receive.
1884 : * @return 0 on success, 1 otherwise.
1885 : */
1886 : static int
1887 0 : test_delete_contract_terms (const struct InstanceData *instance,
1888 : const struct OrderData *order,
1889 : struct GNUNET_TIME_Relative legal_expiration,
1890 : enum GNUNET_DB_QueryStatus expected_result)
1891 : {
1892 0 : TEST_COND_RET_ON_FAIL (expected_result ==
1893 : plugin->delete_contract_terms (plugin->cls,
1894 : instance->instance.id,
1895 : order->id,
1896 : legal_expiration),
1897 : "Delete contract terms failed\n");
1898 0 : return 0;
1899 : }
1900 :
1901 :
1902 : /**
1903 : * Test marking a contract as paid in the database.
1904 : *
1905 : * @param instance the instance to use.
1906 : * @param order the order whose contract to use.
1907 : * @param expected_result the result we expect to receive.
1908 : * @return 0 on success, 1 otherwise.
1909 : */
1910 : static int
1911 0 : test_mark_contract_paid (const struct InstanceData *instance,
1912 : const struct OrderData *order,
1913 : enum GNUNET_DB_QueryStatus expected_result)
1914 : {
1915 : struct TALER_PrivateContractHashP h_contract_terms;
1916 :
1917 0 : GNUNET_assert (GNUNET_OK ==
1918 : TALER_JSON_contract_hash (order->contract,
1919 : &h_contract_terms));
1920 0 : TEST_COND_RET_ON_FAIL (expected_result ==
1921 : plugin->mark_contract_paid (plugin->cls,
1922 : instance->instance.id,
1923 : &h_contract_terms,
1924 : "test_orders_session",
1925 : -1),
1926 : "Mark contract paid failed\n");
1927 0 : return 0;
1928 : }
1929 :
1930 :
1931 : /**
1932 : * Tests looking up the status of an order.
1933 : *
1934 : * @param instance the instance to lookup from.
1935 : * @param order the order to lookup.
1936 : * @param expected_paid whether the order was paid or not.
1937 : * @return 0 on success, 1 otherwise.
1938 : */
1939 : static int
1940 0 : test_lookup_order_status (const struct InstanceData *instance,
1941 : const struct OrderData *order,
1942 : bool expected_paid)
1943 : {
1944 : struct TALER_PrivateContractHashP h_contract_terms_expected;
1945 : struct TALER_PrivateContractHashP h_contract_terms;
1946 0 : bool order_paid = false;
1947 :
1948 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
1949 0 : plugin->lookup_order_status (plugin->cls,
1950 0 : instance->instance.id,
1951 0 : order->id,
1952 : &h_contract_terms,
1953 : &order_paid))
1954 : {
1955 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1956 : "Lookup order status failed\n");
1957 0 : return 1;
1958 : }
1959 0 : GNUNET_assert (GNUNET_OK ==
1960 : TALER_JSON_contract_hash (order->contract,
1961 : &h_contract_terms_expected));
1962 0 : if ((expected_paid != order_paid) ||
1963 0 : (0 != GNUNET_memcmp (&h_contract_terms,
1964 : &h_contract_terms_expected)))
1965 : {
1966 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1967 : "Lookup order status/deposit failed: mismatched data\n");
1968 0 : return 1;
1969 : }
1970 0 : return 0;
1971 : }
1972 :
1973 :
1974 : /**
1975 : * Test looking up an order by its fulfillment.
1976 : *
1977 : * @param instance the instance to lookup from.
1978 : * @param order the order to lookup.
1979 : * @param the session id associated with the payment.
1980 : * @return 0 on success, 1 otherwise.
1981 : */
1982 : static int
1983 0 : test_lookup_order_by_fulfillment (const struct InstanceData *instance,
1984 : const struct OrderData *order,
1985 : const char *session_id)
1986 : {
1987 : char *order_id;
1988 : const char *fulfillment_url =
1989 0 : json_string_value (json_object_get (order->contract,
1990 : "fulfillment_url"));
1991 0 : GNUNET_assert (NULL != fulfillment_url);
1992 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
1993 0 : plugin->lookup_order_by_fulfillment (plugin->cls,
1994 0 : instance->instance.id,
1995 : fulfillment_url,
1996 : session_id,
1997 : false,
1998 : &order_id))
1999 : {
2000 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2001 : "Lookup order by fulfillment failed\n");
2002 0 : GNUNET_free (order_id);
2003 0 : return 1;
2004 : }
2005 0 : if (0 != strcmp (order->id,
2006 : order_id))
2007 : {
2008 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2009 : "Lookup order by fulfillment failed\n");
2010 0 : GNUNET_free (order_id);
2011 0 : return 1;
2012 : }
2013 0 : GNUNET_free (order_id);
2014 0 : return 0;
2015 : }
2016 :
2017 :
2018 : /**
2019 : * Test looking up the status of an order.
2020 : *
2021 : * @param order_id the row of the order in the database.
2022 : * @param session_id the session id associated with the payment.
2023 : * @param expected_paid whether the order was paid or not.
2024 : * @param expected_wired whether the order was wired or not.
2025 : * @return 0 on success, 1 otherwise.
2026 : */
2027 : static int
2028 0 : test_lookup_payment_status (const char *instance_id,
2029 : const char *order_id,
2030 : const char *session_id,
2031 : bool expected_paid,
2032 : bool expected_wired)
2033 : {
2034 : bool paid;
2035 : bool wired;
2036 : bool matches;
2037 : uint64_t os;
2038 : int16_t choice_index;
2039 :
2040 0 : TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
2041 : plugin->lookup_contract_terms3 (plugin->cls,
2042 : instance_id,
2043 : order_id,
2044 : session_id,
2045 : NULL,
2046 : &os,
2047 : &paid,
2048 : &wired,
2049 : &matches,
2050 : NULL,
2051 : &choice_index),
2052 : "Lookup payment status failed\n");
2053 0 : if ( (NULL != session_id) && (! matches) )
2054 : {
2055 0 : paid = false;
2056 0 : wired = false;
2057 : }
2058 0 : if (expected_wired != wired)
2059 : {
2060 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2061 : "Lookup payment status failed: wired status is wrong\n");
2062 0 : return 1;
2063 : }
2064 0 : if (expected_paid != paid)
2065 : {
2066 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2067 : "Lookup payment status failed: paid status is wrong\n");
2068 0 : return 1;
2069 : }
2070 0 : return 0;
2071 : }
2072 :
2073 :
2074 : /**
2075 : * Test marking an order as being wired.
2076 : *
2077 : * @param order_id the row of the order in the database.
2078 : * @param expected_result the result we expect the plugin to return.
2079 : * @return 0 on success, 1 otherwise.
2080 : */
2081 : static int
2082 0 : test_mark_order_wired (uint64_t order_id,
2083 : enum GNUNET_DB_QueryStatus expected_result)
2084 : {
2085 0 : TEST_COND_RET_ON_FAIL (expected_result ==
2086 : plugin->mark_order_wired (plugin->cls,
2087 : order_id),
2088 : "Mark order wired failed\n");
2089 0 : return 0;
2090 : }
2091 :
2092 :
2093 : /**
2094 : * Closure for order tests.
2095 : */
2096 : struct TestOrders_Closure
2097 : {
2098 : /**
2099 : * The instance to use for the order tests.
2100 : */
2101 : struct InstanceData instance;
2102 :
2103 : /**
2104 : * A product to use for the order tests.
2105 : */
2106 : struct ProductData product;
2107 :
2108 : /**
2109 : * The array of orders
2110 : */
2111 : struct OrderData orders[3];
2112 : };
2113 :
2114 :
2115 : /**
2116 : * Initializes order test data.
2117 : *
2118 : * @param cls the order test closure.
2119 : */
2120 : static void
2121 0 : pre_test_orders (struct TestOrders_Closure *cls)
2122 : {
2123 : /* Instance */
2124 0 : make_instance ("test_inst_orders",
2125 : &cls->instance);
2126 :
2127 : /* Product */
2128 0 : make_product ("test_orders_pd_0",
2129 : &cls->product);
2130 :
2131 : /* Orders */
2132 0 : make_order ("test_orders_od_0",
2133 : &cls->orders[0]);
2134 0 : make_order ("test_orders_od_1",
2135 : &cls->orders[1]);
2136 0 : make_order ("test_orders_od_2",
2137 : &cls->orders[2]);
2138 :
2139 0 : GNUNET_assert (0 ==
2140 : json_object_set_new (cls->orders[1].contract,
2141 : "other_field",
2142 : json_string ("Second contract")));
2143 :
2144 0 : cls->orders[2].pay_deadline = GNUNET_TIME_UNIT_ZERO_TS;
2145 0 : GNUNET_assert (0 ==
2146 : json_object_set_new (
2147 : cls->orders[2].contract,
2148 : "pay_deadline",
2149 : GNUNET_JSON_from_timestamp (cls->orders[2].pay_deadline)));
2150 0 : }
2151 :
2152 :
2153 : /**
2154 : * Frees memory after order tests.
2155 : *
2156 : * @param cls the order test closure.
2157 : */
2158 : static void
2159 0 : post_test_orders (struct TestOrders_Closure *cls)
2160 : {
2161 0 : free_instance_data (&cls->instance);
2162 0 : free_product_data (&cls->product);
2163 0 : free_order_data (&cls->orders[0]);
2164 0 : free_order_data (&cls->orders[1]);
2165 0 : free_order_data (&cls->orders[2]);
2166 0 : }
2167 :
2168 :
2169 : /**
2170 : * Run the tests for orders.
2171 : *
2172 : * @param cls the order test closure.
2173 : * @return 0 on success, 1 on failure.
2174 : */
2175 : static int
2176 0 : run_test_orders (struct TestOrders_Closure *cls)
2177 : {
2178 0 : struct TALER_MERCHANTDB_OrderFilter filter = {
2179 : .paid = TALER_EXCHANGE_YNA_ALL,
2180 : .refunded = TALER_EXCHANGE_YNA_ALL,
2181 : .wired = TALER_EXCHANGE_YNA_ALL,
2182 : .date = GNUNET_TIME_UNIT_ZERO_TS,
2183 : .start_row = 0,
2184 : .delta = 8
2185 : };
2186 : uint64_t serial;
2187 :
2188 : /* Insert the instance */
2189 0 : TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
2190 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
2191 : /* Test inserting an order */
2192 0 : TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
2193 : &cls->orders[0],
2194 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
2195 : /* Test double insert fails */
2196 0 : TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
2197 : &cls->orders[0],
2198 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
2199 : /* Test lookup order */
2200 0 : TEST_RET_ON_FAIL (test_lookup_order (&cls->instance,
2201 : &cls->orders[0]));
2202 : /* Make sure it fails correctly for nonexistent orders */
2203 : {
2204 : struct TALER_MerchantPostDataHashP unused;
2205 :
2206 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
2207 0 : plugin->lookup_order (plugin->cls,
2208 0 : cls->instance.instance.id,
2209 : cls->orders[1].id,
2210 : NULL,
2211 : &unused,
2212 : NULL))
2213 : {
2214 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2215 : "Lookup order failed\n");
2216 0 : return 1;
2217 : }
2218 : }
2219 : /* Test lookups on multiple orders */
2220 0 : TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
2221 : &cls->orders[1],
2222 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
2223 0 : serial = get_order_serial (&cls->instance,
2224 0 : &cls->orders[0]);
2225 0 : TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
2226 : &filter,
2227 : 2,
2228 : cls->orders));
2229 : /* Test inserting contract terms */
2230 0 : TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
2231 : &cls->orders[0],
2232 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
2233 : /* Test double insert fails */
2234 0 : TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
2235 : &cls->orders[0],
2236 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
2237 : /* Test order lock */
2238 0 : TEST_RET_ON_FAIL (test_insert_product (&cls->instance,
2239 : &cls->product,
2240 : 0,
2241 : NULL,
2242 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
2243 : false,
2244 : false,
2245 : -1));
2246 0 : if (1 != plugin->insert_order_lock (plugin->cls,
2247 0 : cls->instance.instance.id,
2248 : cls->orders[0].id,
2249 : cls->product.id,
2250 : 5))
2251 : {
2252 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2253 : "Insert order lock failed\n");
2254 0 : return 1;
2255 : }
2256 : /* Test lookup contract terms */
2257 0 : TEST_RET_ON_FAIL (test_lookup_contract_terms (&cls->instance,
2258 : &cls->orders[0]));
2259 : /* Test lookup fails for nonexistent contract terms */
2260 : {
2261 0 : json_t *lookup_contract = NULL;
2262 : uint64_t lookup_order_serial;
2263 :
2264 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
2265 0 : plugin->lookup_contract_terms (plugin->cls,
2266 0 : cls->instance.instance.id,
2267 : cls->orders[1].id,
2268 : &lookup_contract,
2269 : &lookup_order_serial,
2270 : NULL))
2271 : {
2272 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2273 : "Lookup contract terms failed\n");
2274 0 : GNUNET_assert (NULL == lookup_contract);
2275 0 : return 1;
2276 : }
2277 : }
2278 : /* Test update contract terms */
2279 0 : GNUNET_assert (0 ==
2280 : json_object_set_new (cls->orders[0].contract,
2281 : "some_new_field",
2282 : json_string ("another value")));
2283 0 : TEST_RET_ON_FAIL (test_update_contract_terms (&cls->instance,
2284 : &cls->orders[0],
2285 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
2286 0 : TEST_RET_ON_FAIL (test_lookup_contract_terms (&cls->instance,
2287 : &cls->orders[0]));
2288 : /* Test lookup order status */
2289 0 : TEST_RET_ON_FAIL (test_lookup_order_status (&cls->instance,
2290 : &cls->orders[0],
2291 : false));
2292 : {
2293 : struct TALER_PrivateContractHashP h_contract_terms;
2294 0 : bool order_paid = false;
2295 :
2296 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
2297 0 : plugin->lookup_order_status (plugin->cls,
2298 0 : cls->instance.instance.id,
2299 : cls->orders[1].id,
2300 : &h_contract_terms,
2301 : &order_paid))
2302 : {
2303 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2304 : "Lookup order status failed\n");
2305 0 : return 1;
2306 : }
2307 : }
2308 : /* Test lookup payment status */
2309 0 : TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
2310 : cls->orders[0].id,
2311 : NULL,
2312 : false,
2313 : false));
2314 : /* Test lookup order status fails for nonexistent order */
2315 : {
2316 : struct TALER_PrivateContractHashP h_contract_terms;
2317 : bool order_paid;
2318 :
2319 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
2320 0 : plugin->lookup_order_status (plugin->cls,
2321 0 : cls->instance.instance.id,
2322 : cls->orders[1].id,
2323 : &h_contract_terms,
2324 : &order_paid))
2325 : {
2326 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2327 : "Lookup order status failed\n");
2328 0 : return 1;
2329 : }
2330 : }
2331 : /* Test marking contracts as paid */
2332 0 : TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
2333 : &cls->orders[0],
2334 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
2335 0 : TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
2336 : cls->orders[0].id,
2337 : NULL,
2338 : true,
2339 : false));
2340 0 : TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
2341 : cls->orders[0].id,
2342 : "test_orders_session",
2343 : true,
2344 : false));
2345 0 : TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
2346 : cls->orders[0].id,
2347 : "bad_session",
2348 : false,
2349 : false));
2350 : /* Test lookup order by fulfillment */
2351 0 : TEST_RET_ON_FAIL (test_lookup_order_by_fulfillment (&cls->instance,
2352 : &cls->orders[0],
2353 : "test_orders_session"));
2354 : {
2355 : char *order_id;
2356 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
2357 0 : plugin->lookup_order_by_fulfillment (plugin->cls,
2358 0 : cls->instance.instance.id,
2359 : "fulfillment_url",
2360 : "test_orders_session",
2361 : false,
2362 : &order_id))
2363 : {
2364 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2365 : "Lookup order by fulfillment failed\n");
2366 0 : GNUNET_free (order_id);
2367 0 : return 1;
2368 : }
2369 : }
2370 : /* Test mark as paid fails for nonexistent order */
2371 0 : TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
2372 : &cls->orders[1],
2373 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
2374 0 : TEST_RET_ON_FAIL (test_lookup_order_status (&cls->instance,
2375 : &cls->orders[0],
2376 : true));
2377 0 : filter.paid = TALER_EXCHANGE_YNA_YES;
2378 0 : TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
2379 : &filter,
2380 : 1,
2381 : cls->orders));
2382 : /* Test marking orders as wired */
2383 0 : TEST_RET_ON_FAIL (test_mark_order_wired (serial,
2384 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT))
2385 : ;
2386 0 : TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
2387 : cls->orders[0].id,
2388 : NULL,
2389 : true,
2390 : true));
2391 0 : TEST_RET_ON_FAIL (test_mark_order_wired (1007,
2392 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS))
2393 : ;
2394 : /* If an order has been claimed and we aren't past
2395 : the pay deadline, we can't delete it. */
2396 0 : TEST_RET_ON_FAIL (test_delete_order (&cls->instance,
2397 : &cls->orders[0],
2398 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
2399 : /* Test we can't delete before the legal expiration */
2400 0 : TEST_RET_ON_FAIL (test_delete_contract_terms (&cls->instance,
2401 : &cls->orders[0],
2402 : GNUNET_TIME_UNIT_MONTHS,
2403 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
2404 : /* Test deleting contract terms */
2405 0 : TEST_RET_ON_FAIL (test_delete_contract_terms (&cls->instance,
2406 : &cls->orders[0],
2407 : GNUNET_TIME_UNIT_ZERO,
2408 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
2409 : /* Test we can't delete something that doesn't exist */
2410 0 : TEST_RET_ON_FAIL (test_delete_contract_terms (&cls->instance,
2411 : &cls->orders[0],
2412 : GNUNET_TIME_UNIT_ZERO,
2413 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
2414 : /* Test delete order where we aren't past
2415 : the deadline, but the order is unclaimed. */
2416 0 : TEST_RET_ON_FAIL (test_delete_order (&cls->instance,
2417 : &cls->orders[1],
2418 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
2419 0 : TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
2420 : &filter,
2421 : 0,
2422 : NULL));
2423 : /* Test we can't delete something that doesn't exist */
2424 0 : TEST_RET_ON_FAIL (test_delete_order (&cls->instance,
2425 : &cls->orders[1],
2426 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
2427 :
2428 : /* Test we can also delete a claimed order that's past the pay deadline. */
2429 0 : TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
2430 : &cls->orders[2],
2431 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
2432 0 : TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
2433 : &cls->orders[2],
2434 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
2435 0 : TEST_RET_ON_FAIL (test_delete_order (&cls->instance,
2436 : &cls->orders[2],
2437 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
2438 0 : return 0;
2439 : }
2440 :
2441 :
2442 : /**
2443 : * Does all tasks for testing orders.
2444 : *
2445 : * @return 0 when successful, 1 otherwise.
2446 : */
2447 : static int
2448 0 : test_orders (void)
2449 : {
2450 : struct TestOrders_Closure test_cls;
2451 : int test_result;
2452 :
2453 0 : pre_test_orders (&test_cls);
2454 0 : test_result = run_test_orders (&test_cls);
2455 0 : post_test_orders (&test_cls);
2456 0 : return test_result;
2457 : }
2458 :
2459 :
2460 : /* ********** Deposits ********** */
2461 :
2462 :
2463 : /**
2464 : * A container for exchange signing key data.
2465 : */
2466 : struct ExchangeSignkeyData
2467 : {
2468 : /**
2469 : * The master private key of the exchange.
2470 : */
2471 : struct TALER_MasterPrivateKeyP master_priv;
2472 :
2473 : /**
2474 : * The master public key of the exchange.
2475 : */
2476 : struct TALER_MasterPublicKeyP master_pub;
2477 :
2478 : /**
2479 : * A signature made with the master keys.
2480 : */
2481 : struct TALER_MasterSignatureP master_sig;
2482 :
2483 : /**
2484 : * The private key of the exchange.
2485 : */
2486 : struct TALER_ExchangePrivateKeyP exchange_priv;
2487 :
2488 : /**
2489 : * The public key of the exchange.
2490 : */
2491 : struct TALER_ExchangePublicKeyP exchange_pub;
2492 :
2493 : /**
2494 : * When the signing key becomes valid.
2495 : */
2496 : struct GNUNET_TIME_Timestamp start_date;
2497 :
2498 : /**
2499 : * When the signing key stops being used.
2500 : */
2501 : struct GNUNET_TIME_Timestamp expire_date;
2502 :
2503 : /**
2504 : * When the signing key becomes invalid for proof.
2505 : */
2506 : struct GNUNET_TIME_Timestamp end_date;
2507 : };
2508 :
2509 :
2510 : /**
2511 : * Creates an exchange signing key.
2512 : *
2513 : * @param signkey the signing key data to fill.
2514 : */
2515 : static void
2516 0 : make_exchange_signkey (struct ExchangeSignkeyData *signkey)
2517 : {
2518 0 : struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
2519 :
2520 0 : GNUNET_CRYPTO_eddsa_key_create (&signkey->exchange_priv.eddsa_priv);
2521 0 : GNUNET_CRYPTO_eddsa_key_get_public (&signkey->exchange_priv.eddsa_priv,
2522 : &signkey->exchange_pub.eddsa_pub);
2523 0 : GNUNET_CRYPTO_eddsa_key_create (&signkey->master_priv.eddsa_priv);
2524 0 : GNUNET_CRYPTO_eddsa_key_get_public (&signkey->master_priv.eddsa_priv,
2525 : &signkey->master_pub.eddsa_pub);
2526 0 : signkey->start_date = now;
2527 0 : signkey->expire_date = now;
2528 0 : signkey->end_date = now;
2529 0 : TALER_exchange_offline_signkey_validity_sign (
2530 0 : &signkey->exchange_pub,
2531 : signkey->start_date,
2532 : signkey->expire_date,
2533 : signkey->end_date,
2534 0 : &signkey->master_priv,
2535 : &signkey->master_sig);
2536 0 : }
2537 :
2538 :
2539 : /**
2540 : * A container for deposit data.
2541 : */
2542 : struct DepositData
2543 : {
2544 : /**
2545 : * When the deposit was made.
2546 : */
2547 : struct GNUNET_TIME_Timestamp timestamp;
2548 :
2549 : /**
2550 : * Hash of the associated order's contract terms.
2551 : */
2552 : struct TALER_PrivateContractHashP h_contract_terms;
2553 :
2554 : /**
2555 : * Public key of the coin that has been deposited.
2556 : */
2557 : struct TALER_CoinSpendPublicKeyP coin_pub;
2558 :
2559 : /**
2560 : * Signature of the coin that has been deposited.
2561 : */
2562 : struct TALER_CoinSpendSignatureP coin_sig;
2563 :
2564 : /**
2565 : * URL of the exchange.
2566 : */
2567 : const char *exchange_url;
2568 :
2569 : /**
2570 : * Value of the coin with fees applied.
2571 : */
2572 : struct TALER_Amount amount_with_fee;
2573 :
2574 : /**
2575 : * Fee charged for deposit.
2576 : */
2577 : struct TALER_Amount deposit_fee;
2578 :
2579 : /**
2580 : * Fee to be charged in case of a refund.
2581 : */
2582 : struct TALER_Amount refund_fee;
2583 :
2584 : /**
2585 : * Fee charged after the money is wired.
2586 : */
2587 : struct TALER_Amount wire_fee;
2588 :
2589 : /**
2590 : * Hash of the wire details.
2591 : */
2592 : struct TALER_MerchantWireHashP h_wire;
2593 :
2594 : /**
2595 : * Signature the exchange made on this deposit.
2596 : */
2597 : struct TALER_ExchangeSignatureP exchange_sig;
2598 :
2599 : };
2600 :
2601 :
2602 : /**
2603 : * Generates deposit data for an order.
2604 : *
2605 : * @param instance the instance to make the deposit to.
2606 : * @param account the merchant account to use.
2607 : * @param order the order this deposit is for.
2608 : * @param signkey the signing key to use.
2609 : * @param deposit the deposit data to fill.
2610 : */
2611 : static void
2612 0 : make_deposit (const struct InstanceData *instance,
2613 : const struct TALER_MERCHANTDB_AccountDetails *account,
2614 : const struct OrderData *order,
2615 : const struct ExchangeSignkeyData *signkey,
2616 : struct DepositData *deposit)
2617 : {
2618 : struct TALER_CoinSpendPrivateKeyP coin_priv;
2619 : struct GNUNET_TIME_Timestamp now;
2620 : struct TALER_Amount amount_without_fee;
2621 :
2622 0 : now = GNUNET_TIME_timestamp_get ();
2623 0 : deposit->timestamp = now;
2624 0 : GNUNET_assert (GNUNET_OK ==
2625 : TALER_JSON_contract_hash (order->contract,
2626 : &deposit->h_contract_terms));
2627 0 : GNUNET_CRYPTO_eddsa_key_create (&coin_priv.eddsa_priv);
2628 0 : GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv,
2629 : &deposit->coin_pub.eddsa_pub);
2630 0 : deposit->exchange_url = "https://test-exchange/";
2631 0 : GNUNET_assert (GNUNET_OK ==
2632 : TALER_string_to_amount ("EUR:50.00",
2633 : &deposit->amount_with_fee));
2634 0 : GNUNET_assert (GNUNET_OK ==
2635 : TALER_string_to_amount ("EUR:1.00",
2636 : &deposit->deposit_fee));
2637 0 : GNUNET_assert (GNUNET_OK ==
2638 : TALER_string_to_amount ("EUR:1.50",
2639 : &deposit->refund_fee));
2640 0 : GNUNET_assert (GNUNET_OK ==
2641 : TALER_string_to_amount ("EUR:2.00",
2642 : &deposit->wire_fee));
2643 0 : GNUNET_assert (0 <=
2644 : TALER_amount_subtract (&amount_without_fee,
2645 : &deposit->amount_with_fee,
2646 : &deposit->deposit_fee));
2647 0 : deposit->h_wire = account->h_wire;
2648 0 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
2649 0 : &deposit->exchange_sig,
2650 : sizeof (deposit->exchange_sig));
2651 0 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
2652 0 : &deposit->coin_sig,
2653 : sizeof (deposit->coin_sig));
2654 0 : }
2655 :
2656 :
2657 : /**
2658 : * Tests inserting an exchange signing key into the database.
2659 : *
2660 : * @param signkey the signing key to insert.
2661 : * @param expected_result the result we expect the database to return.
2662 : * @return 0 on success, 1 otherwise.
2663 : */
2664 : static int
2665 0 : test_insert_exchange_signkey (const struct ExchangeSignkeyData *signkey,
2666 : enum GNUNET_DB_QueryStatus expected_result)
2667 : {
2668 0 : TEST_COND_RET_ON_FAIL (expected_result ==
2669 : plugin->insert_exchange_signkey (plugin->cls,
2670 : &signkey->master_pub,
2671 : &signkey->exchange_pub
2672 : ,
2673 : signkey->start_date,
2674 : signkey->expire_date,
2675 : signkey->end_date,
2676 : &signkey->master_sig),
2677 : "Insert exchange signkey failed\n");
2678 0 : return 0;
2679 : }
2680 :
2681 :
2682 : /**
2683 : * Tests inserting a deposit into the database.
2684 : *
2685 : * @param instance the instance the deposit was made to.
2686 : * @param signkey the signing key used.
2687 : * @param deposit the deposit information to insert.
2688 : * @param expected_result the result we expect the database to return.
2689 : * @return 0 on success, 1 otherwise.
2690 : */
2691 : static int
2692 0 : test_insert_deposit (const struct InstanceData *instance,
2693 : const struct ExchangeSignkeyData *signkey,
2694 : const struct DepositData *deposit,
2695 : enum GNUNET_DB_QueryStatus expected_result)
2696 : {
2697 : uint64_t row;
2698 : struct TALER_Amount awf;
2699 :
2700 0 : GNUNET_assert (0 <=
2701 : TALER_amount_subtract (&awf,
2702 : &deposit->amount_with_fee,
2703 : &deposit->deposit_fee));
2704 0 : TEST_COND_RET_ON_FAIL (
2705 : expected_result ==
2706 : plugin->insert_deposit_confirmation (plugin->cls,
2707 : instance->instance.id,
2708 : deposit->timestamp,
2709 : &deposit->h_contract_terms,
2710 : deposit->exchange_url,
2711 : deposit->timestamp,
2712 : &awf,
2713 : &deposit->wire_fee,
2714 : &deposit->h_wire,
2715 : &deposit->exchange_sig,
2716 : &signkey->exchange_pub,
2717 : &row),
2718 : "Insert deposit confirmation failed\n");
2719 0 : TEST_COND_RET_ON_FAIL (
2720 : expected_result ==
2721 : plugin->insert_deposit (plugin->cls,
2722 : 0,
2723 : row,
2724 : &deposit->coin_pub,
2725 : &deposit->coin_sig,
2726 : &deposit->amount_with_fee,
2727 : &deposit->deposit_fee,
2728 : &deposit->refund_fee,
2729 : GNUNET_TIME_absolute_get ()),
2730 : "Insert deposit failed\n");
2731 0 : return 0;
2732 : }
2733 :
2734 :
2735 : /**
2736 : * Closure for testing deposit lookup
2737 : */
2738 : struct TestLookupDeposits_Closure
2739 : {
2740 : /**
2741 : * Number of deposits to compare to
2742 : */
2743 : unsigned int deposits_to_cmp_length;
2744 :
2745 : /**
2746 : * Pointer to array of deposit data
2747 : */
2748 : const struct DepositData *deposits_to_cmp;
2749 :
2750 : /**
2751 : * Pointer to array of number of matches per deposit
2752 : */
2753 : unsigned int *results_matching;
2754 :
2755 : /**
2756 : * Total number of results returned
2757 : */
2758 : unsigned int results_length;
2759 : };
2760 :
2761 :
2762 : /**
2763 : * Called after 'test_lookup_deposits'.
2764 : *
2765 : * @param cls pointer to the test lookup closure.
2766 : * @param coin_pub public key of the coin deposited.
2767 : * @param amount_with_fee amount of the deposit with fees.
2768 : * @param deposit_fee fee charged for the deposit.
2769 : * @param refund_fee fee charged in case of a refund.
2770 : */
2771 : static void
2772 0 : lookup_deposits_cb (void *cls,
2773 : const char *exchange_url,
2774 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
2775 : const struct TALER_Amount *amount_with_fee,
2776 : const struct TALER_Amount *deposit_fee,
2777 : const struct TALER_Amount *refund_fee)
2778 : {
2779 0 : struct TestLookupDeposits_Closure *cmp = cls;
2780 0 : if (NULL == cmp)
2781 0 : return;
2782 0 : cmp->results_length += 1;
2783 0 : for (unsigned int i = 0; cmp->deposits_to_cmp_length > i; ++i)
2784 : {
2785 0 : if ((GNUNET_OK ==
2786 0 : TALER_amount_cmp_currency (
2787 0 : &cmp->deposits_to_cmp[i].amount_with_fee,
2788 0 : amount_with_fee)) &&
2789 : (0 ==
2790 0 : TALER_amount_cmp (&cmp->deposits_to_cmp[i].amount_with_fee,
2791 0 : amount_with_fee)) &&
2792 : (GNUNET_OK ==
2793 0 : TALER_amount_cmp_currency (
2794 0 : &cmp->deposits_to_cmp[i].deposit_fee,
2795 0 : deposit_fee)) &&
2796 : (0 ==
2797 0 : TALER_amount_cmp (&cmp->deposits_to_cmp[i].deposit_fee,
2798 0 : deposit_fee)) &&
2799 : (GNUNET_OK ==
2800 0 : TALER_amount_cmp_currency (
2801 0 : &cmp->deposits_to_cmp[i].refund_fee,
2802 0 : refund_fee)) &&
2803 : (0 ==
2804 0 : TALER_amount_cmp (&cmp->deposits_to_cmp[i].refund_fee,
2805 : refund_fee)))
2806 : {
2807 0 : cmp->results_matching[i] += 1;
2808 : }
2809 :
2810 : }
2811 : }
2812 :
2813 :
2814 : /**
2815 : * Tests looking up deposits from the database.
2816 : *
2817 : * @param instance the instance to lookup deposits from.
2818 : * @param h_contract_terms the contract terms that the deposits should have.
2819 : * @param deposits_length length of @e deposits.
2820 : * @param deposits the deposits we expect to be found.
2821 : * @return 0 on success, 1 otherwise.
2822 : */
2823 : static int
2824 0 : test_lookup_deposits (const struct InstanceData *instance,
2825 : const struct TALER_PrivateContractHashP *h_contract_terms,
2826 : unsigned int deposits_length,
2827 : const struct DepositData *deposits)
2828 0 : {
2829 0 : unsigned int results_matching[GNUNET_NZL (deposits_length)];
2830 0 : struct TestLookupDeposits_Closure cmp = {
2831 : .deposits_to_cmp_length = deposits_length,
2832 : .deposits_to_cmp = deposits,
2833 : .results_matching = results_matching,
2834 : .results_length = 0
2835 : };
2836 0 : memset (results_matching,
2837 : 0,
2838 : sizeof (unsigned int) * deposits_length);
2839 0 : TEST_COND_RET_ON_FAIL (0 <=
2840 : plugin->lookup_deposits (plugin->cls,
2841 : instance->instance.id,
2842 : h_contract_terms,
2843 : &lookup_deposits_cb,
2844 : &cmp),
2845 : "Lookup deposits failed\n");
2846 0 : if (deposits_length != cmp.results_length)
2847 : {
2848 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2849 : "Lookup deposits failed: incorrect number of results returned (%d)\n",
2850 : cmp.results_length);
2851 0 : return 1;
2852 : }
2853 0 : for (unsigned int i = 0; deposits_length > i; ++i)
2854 : {
2855 0 : if (cmp.results_matching[i] != 1)
2856 : {
2857 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2858 : "Lookup deposits failed: mismatched data\n");
2859 0 : return 1;
2860 : }
2861 : }
2862 0 : return 0;
2863 : }
2864 :
2865 :
2866 : /**
2867 : * Called after 'test_lookup_deposits_contract_and_coin'.
2868 : *
2869 : * @param cls pointer to the test lookup closure.
2870 : * @param exchange_url URL to the exchange
2871 : * @param amount_with_fee amount of the deposit with fees.
2872 : * @param deposit_fee fee charged for the deposit.
2873 : * @param refund_fee fee charged in case of a refund.
2874 : * @param wire_fee fee charged when the money is wired.
2875 : * @param h_wire hash of the wire transfer details.
2876 : * @param deposit_timestamp when the deposit was made.
2877 : * @param refund_deadline deadline for refunding the deposit.
2878 : * @param exchange_sig signature the exchange made on the deposit.
2879 : * @param exchange_pub public key of the exchange.
2880 : */
2881 : static void
2882 0 : lookup_deposits_contract_coin_cb (
2883 : void *cls,
2884 : const char *exchange_url,
2885 : const struct TALER_Amount *amount_with_fee,
2886 : const struct TALER_Amount *deposit_fee,
2887 : const struct TALER_Amount *refund_fee,
2888 : const struct TALER_Amount *wire_fee,
2889 : const struct TALER_MerchantWireHashP *h_wire,
2890 : struct GNUNET_TIME_Timestamp deposit_timestamp,
2891 : struct GNUNET_TIME_Timestamp refund_deadline,
2892 : const struct TALER_ExchangeSignatureP *exchange_sig,
2893 : const struct TALER_ExchangePublicKeyP *exchange_pub)
2894 : {
2895 0 : struct TestLookupDeposits_Closure *cmp = cls;
2896 :
2897 0 : if (NULL == cmp)
2898 0 : return;
2899 0 : cmp->results_length++;
2900 0 : for (unsigned int i = 0; cmp->deposits_to_cmp_length > i; ++i)
2901 : {
2902 0 : if ((GNUNET_TIME_timestamp_cmp (cmp->deposits_to_cmp[i].timestamp,
2903 : ==,
2904 0 : deposit_timestamp)) &&
2905 0 : (0 == strcmp (cmp->deposits_to_cmp[i].exchange_url,
2906 0 : exchange_url)) &&
2907 0 : (GNUNET_OK == TALER_amount_cmp_currency (
2908 0 : &cmp->deposits_to_cmp[i].amount_with_fee,
2909 0 : amount_with_fee)) &&
2910 0 : (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].amount_with_fee,
2911 0 : amount_with_fee)) &&
2912 0 : (GNUNET_OK == TALER_amount_cmp_currency (
2913 0 : &cmp->deposits_to_cmp[i].deposit_fee,
2914 0 : deposit_fee)) &&
2915 0 : (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].deposit_fee,
2916 0 : deposit_fee)) &&
2917 0 : (GNUNET_OK == TALER_amount_cmp_currency (
2918 0 : &cmp->deposits_to_cmp[i].refund_fee,
2919 0 : refund_fee)) &&
2920 0 : (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].refund_fee,
2921 0 : refund_fee)) &&
2922 0 : (GNUNET_OK == TALER_amount_cmp_currency (
2923 0 : &cmp->deposits_to_cmp[i].wire_fee,
2924 0 : wire_fee)) &&
2925 0 : (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].wire_fee,
2926 0 : wire_fee)) &&
2927 0 : (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].h_wire,
2928 0 : h_wire)) &&
2929 0 : (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].exchange_sig,
2930 : exchange_sig)))
2931 : {
2932 0 : cmp->results_matching[i]++;
2933 : }
2934 : }
2935 : }
2936 :
2937 :
2938 : /**
2939 : * Tests lookup of deposits by contract and coin.
2940 : *
2941 : * @param instance the instance to lookup from.
2942 : * @param h_contract the contract terms the deposits should have.
2943 : * @param coin_pub the public key of the coin the deposits should have.
2944 : * @param deposits_length length of @e deposits.
2945 : * @param deposits the deposits the db is expected to find.
2946 : * @return 0 on success, 1 otherwise.
2947 : */
2948 : static int
2949 0 : test_lookup_deposits_contract_and_coin (
2950 : const struct InstanceData *instance,
2951 : const struct TALER_PrivateContractHashP *h_contract,
2952 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
2953 : unsigned int deposits_length,
2954 : const struct DepositData *deposits)
2955 0 : {
2956 0 : unsigned int results_matching[deposits_length];
2957 0 : struct TestLookupDeposits_Closure cmp = {
2958 : .deposits_to_cmp_length = deposits_length,
2959 : .deposits_to_cmp = deposits,
2960 : .results_matching = results_matching,
2961 : .results_length = 0
2962 : };
2963 0 : memset (results_matching,
2964 : 0,
2965 : sizeof (unsigned int) * deposits_length);
2966 0 : TEST_COND_RET_ON_FAIL (
2967 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
2968 : plugin->lookup_deposits_by_contract_and_coin (
2969 : plugin->cls,
2970 : instance->instance.id,
2971 : h_contract,
2972 : coin_pub,
2973 : &lookup_deposits_contract_coin_cb,
2974 : &cmp),
2975 : "Lookup deposits by contract and coin failed\n");
2976 0 : if (deposits_length != cmp.results_length)
2977 : {
2978 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2979 : "Lookup deposits failed: incorrect number of results returned\n");
2980 0 : return 1;
2981 : }
2982 0 : for (unsigned int i = 0; deposits_length > i; ++i)
2983 : {
2984 0 : if (cmp.results_matching[i] != 1)
2985 : {
2986 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2987 : "Lookup deposits failed: mismatched data\n");
2988 0 : return 1;
2989 : }
2990 : }
2991 0 : return 0;
2992 : }
2993 :
2994 :
2995 : /**
2996 : * Called after 'test_lookup_deposits_by_order'.
2997 : *
2998 : * @param cls pointer to the test lookup closure.
2999 : * @param deposit_serial row number of the deposit in the database.
3000 : * @param exchange_url URL to the exchange
3001 : * @param h_wire hash of the wire transfer details.
3002 : * @param deposit_timestamp when was the deposit made
3003 : * @param amount_with_fee amount of the deposit with fees.
3004 : * @param deposit_fee fee charged for the deposit.
3005 : * @param coin_pub public key of the coin deposited.
3006 : */
3007 : static void
3008 0 : lookup_deposits_order_cb (void *cls,
3009 : uint64_t deposit_serial,
3010 : const char *exchange_url,
3011 : const struct TALER_MerchantWireHashP *h_wire,
3012 : struct GNUNET_TIME_Timestamp deposit_timestamp,
3013 : const struct TALER_Amount *amount_with_fee,
3014 : const struct TALER_Amount *deposit_fee,
3015 : const struct TALER_CoinSpendPublicKeyP *coin_pub)
3016 : {
3017 0 : struct TestLookupDeposits_Closure *cmp = cls;
3018 :
3019 0 : if (NULL == cmp)
3020 0 : return;
3021 0 : cmp->results_length += 1;
3022 0 : for (unsigned int i = 0; i < cmp->deposits_to_cmp_length; ++i)
3023 : {
3024 0 : if ((0 == strcmp (cmp->deposits_to_cmp[i].exchange_url,
3025 0 : exchange_url)) &&
3026 0 : (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].h_wire,
3027 0 : h_wire)) &&
3028 0 : (GNUNET_OK == TALER_amount_cmp_currency (
3029 0 : &cmp->deposits_to_cmp[i].amount_with_fee,
3030 0 : amount_with_fee)) &&
3031 0 : (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].amount_with_fee,
3032 0 : amount_with_fee)) &&
3033 0 : (GNUNET_OK == TALER_amount_cmp_currency (
3034 0 : &cmp->deposits_to_cmp[i].deposit_fee,
3035 0 : deposit_fee)) &&
3036 0 : (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].deposit_fee,
3037 0 : deposit_fee)) &&
3038 0 : (0 == GNUNET_memcmp (&cmp->deposits_to_cmp[i].coin_pub,
3039 : coin_pub)))
3040 0 : cmp->results_matching[i] += 1;
3041 : }
3042 : }
3043 :
3044 :
3045 : /**
3046 : * Tests looking up deposits by associated order.
3047 : *
3048 : * @param order_serial row number of the order to lookup for.
3049 : * @param deposits_length length of @e deposits_length.
3050 : * @param deposits the deposits we expect to be found.
3051 : * @return 0 on success, 1 otherwise.
3052 : */
3053 : static int
3054 0 : test_lookup_deposits_by_order (uint64_t order_serial,
3055 : unsigned int deposits_length,
3056 : const struct DepositData *deposits)
3057 0 : {
3058 0 : unsigned int results_matching[deposits_length];
3059 0 : struct TestLookupDeposits_Closure cmp = {
3060 : .deposits_to_cmp_length = deposits_length,
3061 : .deposits_to_cmp = deposits,
3062 : .results_matching = results_matching,
3063 : .results_length = 0
3064 : };
3065 0 : memset (results_matching,
3066 : 0,
3067 : sizeof (unsigned int) * deposits_length);
3068 0 : if (deposits_length !=
3069 0 : plugin->lookup_deposits_by_order (plugin->cls,
3070 : order_serial,
3071 : &lookup_deposits_order_cb,
3072 : &cmp))
3073 : {
3074 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3075 : "Lookup deposits by order failed\n");
3076 0 : return 1;
3077 : }
3078 0 : if (deposits_length != cmp.results_length)
3079 : {
3080 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3081 : "Lookup deposits by order failed: incorrect number of results\n");
3082 0 : return 1;
3083 : }
3084 0 : for (unsigned int i = 0; i < deposits_length; ++i)
3085 : {
3086 0 : if (1 != cmp.results_matching[i])
3087 : {
3088 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3089 : "Lookup deposits by order failed: mismatched data\n");
3090 0 : return 1;
3091 : }
3092 : }
3093 0 : return 0;
3094 : }
3095 :
3096 :
3097 : /**
3098 : * Container for information for looking up the row number of a deposit.
3099 : */
3100 : struct LookupDepositSerial_Closure
3101 : {
3102 : /**
3103 : * The deposit we're looking for.
3104 : */
3105 : const struct DepositData *deposit;
3106 :
3107 : /**
3108 : * The serial found.
3109 : */
3110 : uint64_t serial;
3111 : };
3112 :
3113 :
3114 : /**
3115 : * Called after 'get_deposit_serial'.
3116 : *
3117 : * @param cls pointer to the test lookup closure.
3118 : * @param deposit_serial row number of the deposit in the database.
3119 : * @param exchange_url URL to the exchange
3120 : * @param h_wire hash of the wire transfer details.
3121 : * @param deposit_timestamp when was the deposit made.
3122 : * @param amount_with_fee amount of the deposit with fees.
3123 : * @param deposit_fee fee charged for the deposit.
3124 : * @param coin_pub public key of the coin deposited.
3125 : */
3126 : static void
3127 0 : get_deposit_serial_cb (void *cls,
3128 : uint64_t deposit_serial,
3129 : const char *exchange_url,
3130 : const struct TALER_MerchantWireHashP *h_wire,
3131 : struct GNUNET_TIME_Timestamp deposit_timestamp,
3132 : const struct TALER_Amount *amount_with_fee,
3133 : const struct TALER_Amount *deposit_fee,
3134 : const struct TALER_CoinSpendPublicKeyP *coin_pub)
3135 : {
3136 0 : struct LookupDepositSerial_Closure *lookup_cls = cls;
3137 :
3138 : (void) deposit_timestamp;
3139 0 : if (NULL == lookup_cls)
3140 0 : return;
3141 0 : if ((0 == strcmp (lookup_cls->deposit->exchange_url,
3142 0 : exchange_url)) &&
3143 0 : (0 == GNUNET_memcmp (&lookup_cls->deposit->h_wire,
3144 0 : h_wire)) &&
3145 0 : (GNUNET_OK == TALER_amount_cmp_currency (
3146 0 : &lookup_cls->deposit->amount_with_fee,
3147 0 : amount_with_fee)) &&
3148 0 : (0 == TALER_amount_cmp (&lookup_cls->deposit->amount_with_fee,
3149 0 : amount_with_fee)) &&
3150 0 : (GNUNET_OK == TALER_amount_cmp_currency (
3151 0 : &lookup_cls->deposit->deposit_fee,
3152 0 : deposit_fee)) &&
3153 0 : (0 == TALER_amount_cmp (&lookup_cls->deposit->deposit_fee,
3154 0 : deposit_fee)) &&
3155 0 : (0 == GNUNET_memcmp (&lookup_cls->deposit->coin_pub,
3156 : coin_pub)))
3157 0 : lookup_cls->serial = deposit_serial;
3158 : }
3159 :
3160 :
3161 : /**
3162 : * Convenience function to retrieve the row number of a deposit in the database.
3163 : *
3164 : * @param instance the instance to get deposits from.
3165 : * @param order the order associated with the deposit.
3166 : * @param deposit the deposit to lookup the serial for.
3167 : * @return the row number of the deposit.
3168 : */
3169 : static uint64_t
3170 0 : get_deposit_serial (const struct InstanceData *instance,
3171 : const struct OrderData *order,
3172 : const struct DepositData *deposit)
3173 : {
3174 0 : uint64_t order_serial = get_order_serial (instance,
3175 : order);
3176 0 : struct LookupDepositSerial_Closure lookup_cls = {
3177 : .deposit = deposit,
3178 : .serial = 0
3179 : };
3180 :
3181 0 : GNUNET_assert (0 <
3182 : plugin->lookup_deposits_by_order (plugin->cls,
3183 : order_serial,
3184 : &get_deposit_serial_cb,
3185 : &lookup_cls));
3186 0 : GNUNET_assert (0 != lookup_cls.serial);
3187 :
3188 0 : return lookup_cls.serial;
3189 : }
3190 :
3191 :
3192 : /**
3193 : * Closure for deposit tests.
3194 : */
3195 : struct TestDeposits_Closure
3196 : {
3197 : /**
3198 : * The instance settings
3199 : */
3200 : struct InstanceData instance;
3201 :
3202 : /**
3203 : * The merchant account
3204 : */
3205 : struct TALER_MERCHANTDB_AccountDetails account;
3206 :
3207 : /**
3208 : * The exchange signing key
3209 : */
3210 : struct ExchangeSignkeyData signkey;
3211 :
3212 : /**
3213 : * The order data
3214 : */
3215 : struct OrderData orders[2];
3216 :
3217 : /**
3218 : * The array of deposits
3219 : */
3220 : struct DepositData deposits[3];
3221 : };
3222 :
3223 :
3224 : /**
3225 : * Initializes data for testing deposits.
3226 : *
3227 : * @param cls the test closure to initialize.
3228 : */
3229 : static void
3230 0 : pre_test_deposits (struct TestDeposits_Closure *cls)
3231 : {
3232 : /* Instance */
3233 0 : make_instance ("test_inst_deposits",
3234 : &cls->instance);
3235 :
3236 : /* Account */
3237 0 : make_account (&cls->account);
3238 0 : cls->account.instance_id = cls->instance.instance.id;
3239 : /* Signing key */
3240 0 : make_exchange_signkey (&cls->signkey);
3241 :
3242 : /* Order */
3243 0 : make_order ("test_deposits_od_1",
3244 : &cls->orders[0]);
3245 0 : make_order ("test_deposits_od_2",
3246 : &cls->orders[1]);
3247 :
3248 : /* Deposit */
3249 0 : make_deposit (&cls->instance,
3250 0 : &cls->account,
3251 0 : &cls->orders[0],
3252 0 : &cls->signkey,
3253 : &cls->deposits[0]);
3254 0 : make_deposit (&cls->instance,
3255 0 : &cls->account,
3256 0 : &cls->orders[0],
3257 0 : &cls->signkey,
3258 : &cls->deposits[1]);
3259 0 : GNUNET_assert (GNUNET_OK ==
3260 : TALER_string_to_amount ("EUR:29.00",
3261 : &cls->deposits[1].amount_with_fee));
3262 0 : make_deposit (&cls->instance,
3263 0 : &cls->account,
3264 0 : &cls->orders[1],
3265 0 : &cls->signkey,
3266 : &cls->deposits[2]);
3267 0 : }
3268 :
3269 :
3270 : /**
3271 : * Cleans up memory after testing deposits.
3272 : *
3273 : * @param cls the closure containing memory to free.
3274 : */
3275 : static void
3276 0 : post_test_deposits (struct TestDeposits_Closure *cls)
3277 : {
3278 0 : free_instance_data (&cls->instance);
3279 0 : json_decref (cls->orders[0].contract);
3280 0 : json_decref (cls->orders[1].contract);
3281 0 : }
3282 :
3283 :
3284 : /**
3285 : * Runs tests for deposits.
3286 : *
3287 : * @param cls the closure containing test data.
3288 : * @return 0 on success, 1 otherwise.
3289 : */
3290 : static int
3291 0 : run_test_deposits (struct TestDeposits_Closure *cls)
3292 : {
3293 : /* Insert the instance */
3294 0 : TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
3295 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
3296 : /* Insert an account */
3297 0 : TEST_RET_ON_FAIL (test_insert_account (&cls->instance,
3298 : &cls->account,
3299 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
3300 : /* Insert a signing key */
3301 0 : TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey,
3302 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
3303 0 : TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey,
3304 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
3305 : /* Insert an order */
3306 0 : TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
3307 : &cls->orders[0],
3308 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
3309 : /* Insert contract terms */
3310 0 : TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
3311 : &cls->orders[0],
3312 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
3313 : /* Test inserting a deposit */
3314 0 : TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
3315 : &cls->signkey,
3316 : &cls->deposits[0],
3317 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
3318 : /* Test double inserts fail */
3319 0 : TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
3320 : &cls->signkey,
3321 : &cls->deposits[0],
3322 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
3323 : /* Test lookup deposits */
3324 0 : TEST_RET_ON_FAIL (test_lookup_deposits (&cls->instance,
3325 : &cls->deposits[0].h_contract_terms,
3326 : 1,
3327 : cls->deposits));
3328 0 : TEST_RET_ON_FAIL (test_lookup_deposits (&cls->instance,
3329 : &cls->deposits[2].h_contract_terms,
3330 : 0,
3331 : NULL));
3332 : /* Test lookup deposits by contract and coins */
3333 0 : TEST_RET_ON_FAIL (test_lookup_deposits_contract_and_coin (
3334 : &cls->instance,
3335 : &cls->deposits[0].h_contract_terms,
3336 : &cls->deposits[0].coin_pub,
3337 : 1,
3338 : cls->deposits));
3339 : /* Test multiple deposits */
3340 0 : TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
3341 : &cls->signkey,
3342 : &cls->deposits[1],
3343 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
3344 0 : TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
3345 : &cls->orders[1],
3346 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
3347 0 : TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
3348 : &cls->orders[1],
3349 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
3350 0 : TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
3351 : &cls->signkey,
3352 : &cls->deposits[2],
3353 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
3354 0 : TEST_RET_ON_FAIL (test_lookup_deposits (&cls->instance,
3355 : &cls->deposits[0].h_contract_terms,
3356 : 2,
3357 : cls->deposits));
3358 0 : TEST_RET_ON_FAIL (test_lookup_deposits (&cls->instance,
3359 : &cls->deposits[2].h_contract_terms,
3360 : 1,
3361 : &cls->deposits[2]));
3362 : /* Test lookup deposits by order */
3363 : {
3364 0 : uint64_t order_serial = get_order_serial (&cls->instance,
3365 0 : &cls->orders[0]);
3366 0 : TEST_RET_ON_FAIL (test_lookup_deposits_by_order (order_serial,
3367 : 2,
3368 : cls->deposits));
3369 0 : order_serial = get_order_serial (&cls->instance,
3370 0 : &cls->orders[1]);
3371 0 : TEST_RET_ON_FAIL (test_lookup_deposits_by_order (order_serial,
3372 : 1,
3373 : &cls->deposits[2]));
3374 : }
3375 0 : return 0;
3376 : }
3377 :
3378 :
3379 : /**
3380 : * Handles functionality for testing deposits.
3381 : *
3382 : * @return 0 on success, 1 otherwise.
3383 : */
3384 : static int
3385 0 : test_deposits (void)
3386 : {
3387 : struct TestDeposits_Closure test_cls;
3388 : int test_result;
3389 :
3390 0 : pre_test_deposits (&test_cls);
3391 0 : test_result = run_test_deposits (&test_cls);
3392 0 : post_test_deposits (&test_cls);
3393 0 : return test_result;
3394 : }
3395 :
3396 :
3397 : /* *********** Transfers ********** */
3398 :
3399 :
3400 : /**
3401 : * Container for wire fee data for an exchange.
3402 : */
3403 : struct WireFeeData
3404 : {
3405 : /**
3406 : * The method used.
3407 : */
3408 : const char *wire_method;
3409 :
3410 : /**
3411 : * Hash of the wire method.
3412 : */
3413 : struct GNUNET_HashCode h_wire_method;
3414 :
3415 : /**
3416 : * Wire fees charged.
3417 : */
3418 : struct TALER_WireFeeSet fees;
3419 :
3420 : /**
3421 : * Start date of the wire fee.
3422 : */
3423 : struct GNUNET_TIME_Timestamp wire_fee_start;
3424 :
3425 : /**
3426 : * End date of the wire fee.
3427 : */
3428 : struct GNUNET_TIME_Timestamp wire_fee_end;
3429 :
3430 : /**
3431 : * Signature on the wire fee.
3432 : */
3433 : struct TALER_MasterSignatureP fee_sig;
3434 : };
3435 :
3436 :
3437 : /**
3438 : * Creates data for an exchange wire fee.
3439 : *
3440 : * @param signkey the exchange signing key data.
3441 : * @param wire_fee where to store the wire fee data.
3442 : */
3443 : static void
3444 0 : make_wire_fee (const struct ExchangeSignkeyData *signkey,
3445 : struct WireFeeData *wire_fee)
3446 : {
3447 0 : wire_fee->wire_method = "wire-method";
3448 0 : GNUNET_CRYPTO_hash (wire_fee->wire_method,
3449 0 : strlen (wire_fee->wire_method) + 1,
3450 : &wire_fee->h_wire_method);
3451 0 : GNUNET_assert (GNUNET_OK ==
3452 : TALER_string_to_amount ("EUR:0.49",
3453 : &wire_fee->fees.wire));
3454 0 : GNUNET_assert (GNUNET_OK ==
3455 : TALER_string_to_amount ("EUR:0.49",
3456 : &wire_fee->fees.closing));
3457 0 : wire_fee->wire_fee_start = GNUNET_TIME_timestamp_get ();
3458 0 : wire_fee->wire_fee_end = GNUNET_TIME_relative_to_timestamp (
3459 : GNUNET_TIME_UNIT_MONTHS);
3460 0 : TALER_exchange_offline_wire_fee_sign (
3461 : wire_fee->wire_method,
3462 : wire_fee->wire_fee_start,
3463 : wire_fee->wire_fee_end,
3464 0 : &wire_fee->fees,
3465 : &signkey->master_priv,
3466 : &wire_fee->fee_sig);
3467 0 : }
3468 :
3469 :
3470 : /**
3471 : * Container for wire transfer data.
3472 : */
3473 : struct TransferData
3474 : {
3475 : /**
3476 : * Id of the transfer.
3477 : */
3478 : struct TALER_WireTransferIdentifierRawP wtid;
3479 :
3480 : /**
3481 : * The main data for the transfer.
3482 : */
3483 : struct TALER_EXCHANGE_TransferData data;
3484 :
3485 : /**
3486 : * URL to the exchange the transfer was made through.
3487 : */
3488 : const char *exchange_url;
3489 :
3490 : /**
3491 : * How much the fee for the deposit was.
3492 : */
3493 : struct TALER_Amount deposit_fee;
3494 :
3495 : /**
3496 : * Whether the transfer has been confirmed.
3497 : */
3498 : bool confirmed;
3499 :
3500 : /**
3501 : * Whether the transfer has been verified.
3502 : */
3503 : bool verified;
3504 : };
3505 :
3506 :
3507 : /**
3508 : * Creates a transfer for use with testing.
3509 : *
3510 : * @param deposits_length length of @e deposits.
3511 : * @param deposits list of deposits to combine into one transfer.
3512 : * @param transfer where to write the transfer data.
3513 : */
3514 : static void
3515 0 : make_transfer (const struct ExchangeSignkeyData *signkey,
3516 : unsigned int deposits_length,
3517 : const struct DepositData deposits[static deposits_length],
3518 : struct TransferData *transfer)
3519 0 : {
3520 0 : struct TALER_TrackTransferDetails *details = NULL;
3521 :
3522 0 : GNUNET_CRYPTO_seed_weak_random (585);
3523 0 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
3524 0 : &transfer->wtid,
3525 : sizeof (struct TALER_WireTransferIdentifierRawP));
3526 0 : transfer->exchange_url = deposits[0].exchange_url;
3527 0 : transfer->verified = false;
3528 0 : transfer->confirmed = false;
3529 0 : transfer->data.details_length = 0;
3530 0 : GNUNET_assert (GNUNET_OK ==
3531 : TALER_amount_set_zero (deposits[0].amount_with_fee.currency,
3532 : &transfer->data.total_amount));
3533 0 : GNUNET_assert (GNUNET_OK ==
3534 : TALER_amount_set_zero (deposits[0].amount_with_fee.currency,
3535 : &transfer->deposit_fee));
3536 0 : for (unsigned int i = 0; i < deposits_length; ++i)
3537 : {
3538 0 : GNUNET_array_grow (details,
3539 : transfer->data.details_length,
3540 : i + 1);
3541 0 : details[i].h_contract_terms = deposits[i].h_contract_terms;
3542 0 : details[i].coin_pub = deposits[i].coin_pub;
3543 0 : details[i].coin_value = deposits[i].amount_with_fee;
3544 0 : details[i].coin_fee = deposits[i].deposit_fee;
3545 0 : GNUNET_assert (0 <=
3546 : TALER_amount_add (&transfer->data.total_amount,
3547 : &transfer->data.total_amount,
3548 : &deposits[i].amount_with_fee));
3549 0 : GNUNET_assert (0 <=
3550 : TALER_amount_add (&transfer->deposit_fee,
3551 : &transfer->deposit_fee,
3552 : &deposits[i].deposit_fee));
3553 : }
3554 0 : transfer->data.exchange_pub = signkey->exchange_pub;
3555 0 : transfer->data.execution_time = GNUNET_TIME_timestamp_get ();
3556 0 : transfer->data.details = details;
3557 0 : GNUNET_assert (GNUNET_OK ==
3558 : TALER_string_to_amount ("EUR:0.50",
3559 : &transfer->data.wire_fee));
3560 0 : }
3561 :
3562 :
3563 : /**
3564 : * Closure for testing 'lookup_transfer_summary'
3565 : */
3566 : struct TestLookupTransferSummary_Closure
3567 : {
3568 : /**
3569 : * Id of the order the transfer was made for.
3570 : */
3571 : const char *order_id;
3572 :
3573 : /**
3574 : * The value of the deposit made.
3575 : */
3576 : const struct TALER_Amount *deposit_value;
3577 :
3578 : /**
3579 : * The fee on the deposit made.
3580 : */
3581 : const struct TALER_Amount *deposit_fee;
3582 :
3583 : /**
3584 : * 0 if the comparison is true, 1 if false.
3585 : */
3586 : int result;
3587 : };
3588 :
3589 :
3590 : /**
3591 : * Called after 'test_lookup_transfer_summary'.
3592 : *
3593 : * @param cls pointer to 'TestLookupTransferSummary_Closure'.
3594 : * @param order_id id of the order the transfer was made for.
3595 : * @param deposit_value the value of the deposit made.
3596 : * @param deposit_fee the fee on the deposit made.
3597 : */
3598 : static void
3599 0 : lookup_transfer_summary_cb (void *cls,
3600 : const char *order_id,
3601 : const struct TALER_Amount *deposit_value,
3602 : const struct TALER_Amount *deposit_fee)
3603 : {
3604 0 : struct TestLookupTransferSummary_Closure *cmp = cls;
3605 0 : if (NULL == cmp)
3606 0 : return;
3607 0 : if ((0 == strcmp (cmp->order_id,
3608 0 : order_id)) &&
3609 0 : (GNUNET_OK == TALER_amount_cmp_currency (cmp->deposit_value,
3610 0 : deposit_value)) &&
3611 0 : (0 == TALER_amount_cmp (cmp->deposit_value,
3612 0 : deposit_value)) &&
3613 0 : (GNUNET_OK == TALER_amount_cmp_currency (cmp->deposit_fee,
3614 0 : deposit_fee)) &&
3615 0 : (0 == TALER_amount_cmp (cmp->deposit_fee,
3616 : deposit_fee)))
3617 0 : cmp->result = 0;
3618 : else
3619 0 : cmp->result = 1;
3620 : }
3621 :
3622 :
3623 : /**
3624 : * Tests looking up a transfer's summary.
3625 : *
3626 : * @param exchange_url url to the exchange for the transfer.
3627 : * @param wtid identifier of the transfer.
3628 : * @param expected_order_id the id of the order associated with the transfer.
3629 : * @param expected_deposit_value the amount of the deposit made for the transfer.
3630 : * @param expected_deposit_fee the fee on the deposit made for the transfer.
3631 : * @return 1 on success, 0 otherwise.
3632 : */
3633 : static int
3634 0 : test_lookup_transfer_summary (
3635 : const char *exchange_url,
3636 : const struct TALER_WireTransferIdentifierRawP *wtid,
3637 : const char *expected_order_id,
3638 : const struct TALER_Amount *expected_deposit_value,
3639 : const struct TALER_Amount *expected_deposit_fee)
3640 : {
3641 0 : struct TestLookupTransferSummary_Closure cmp = {
3642 : .order_id = expected_order_id,
3643 : .deposit_value = expected_deposit_value,
3644 : .deposit_fee = expected_deposit_fee,
3645 : .result = 0
3646 : };
3647 0 : if (1 != plugin->lookup_transfer_summary (plugin->cls,
3648 : exchange_url,
3649 : wtid,
3650 : &lookup_transfer_summary_cb,
3651 : &cmp))
3652 : {
3653 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3654 : "Lookup transfer summary failed\n");
3655 0 : return 1;
3656 : }
3657 0 : if (0 != cmp.result)
3658 : {
3659 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3660 : "Lookup transfer summary failed: mismatched data\n");
3661 0 : return 1;
3662 : }
3663 0 : return 0;
3664 : }
3665 :
3666 :
3667 : /**
3668 : * Closure for testing 'lookup_transfer_details'.
3669 : */
3670 : struct TestLookupTransferDetails_Closure
3671 : {
3672 : /**
3673 : * Length of @e details_to_cmp.
3674 : */
3675 : unsigned int details_to_cmp_length;
3676 :
3677 : /**
3678 : * The details we expect to find.
3679 : */
3680 : const struct TALER_TrackTransferDetails *details_to_cmp;
3681 :
3682 : /**
3683 : * Number of results matching each detail in @e details_to_cmp.
3684 : */
3685 : unsigned int *results_matching;
3686 :
3687 : /**
3688 : * Total number of results found.
3689 : */
3690 : unsigned int results_length;
3691 : };
3692 :
3693 :
3694 : /**
3695 : * Called after 'test_lookup_transfer_details'.
3696 : *
3697 : * @param cls pointer to 'TestLookupTransferDetails_Closure'.
3698 : * @param current_offset offset within transfer details.
3699 : * @param details the details that were found.
3700 : */
3701 : static void
3702 0 : lookup_transfer_details_cb (void *cls,
3703 : unsigned int current_offset,
3704 : const struct TALER_TrackTransferDetails *details)
3705 : {
3706 0 : struct TestLookupTransferDetails_Closure *cmp = cls;
3707 0 : if (NULL == cmp)
3708 0 : return;
3709 0 : for (unsigned int i = 0; cmp->details_to_cmp_length > i; ++i)
3710 : {
3711 0 : if ((0 == GNUNET_memcmp (&cmp->details_to_cmp[i].h_contract_terms,
3712 0 : &details->h_contract_terms)) &&
3713 0 : (0 == GNUNET_memcmp (&cmp->details_to_cmp[i].coin_pub,
3714 0 : &details->coin_pub)) &&
3715 0 : (GNUNET_OK == TALER_amount_cmp_currency (
3716 0 : &cmp->details_to_cmp[i].coin_value,
3717 0 : &details->coin_value)) &&
3718 0 : (0 == TALER_amount_cmp (&cmp->details_to_cmp[i].coin_value,
3719 0 : &details->coin_value)) &&
3720 0 : (GNUNET_OK == TALER_amount_cmp_currency (
3721 0 : &cmp->details_to_cmp[i].coin_fee,
3722 0 : &details->coin_fee)) &&
3723 0 : (0 == TALER_amount_cmp (&cmp->details_to_cmp[i].coin_fee,
3724 : &details->coin_fee)))
3725 : {
3726 0 : cmp->results_matching[i] += 1;
3727 : }
3728 : }
3729 0 : cmp->results_length += 1;
3730 : }
3731 :
3732 :
3733 : /**
3734 : * Tests looking up details for a wire transfer.
3735 : *
3736 : * @param exchange_url url to the exchange.
3737 : * @param wtid id of the transfer.
3738 : * @param details_length the length of @e details.
3739 : * @param details the details we expect to be returned.
3740 : * @return 1 on success, 0 otherwise.
3741 : */
3742 : static int
3743 0 : test_lookup_transfer_details (
3744 : const char *exchange_url,
3745 : const struct TALER_WireTransferIdentifierRawP *wtid,
3746 : unsigned int details_length,
3747 : const struct TALER_TrackTransferDetails *details)
3748 0 : {
3749 0 : unsigned int results_matching[details_length];
3750 0 : struct TestLookupTransferDetails_Closure cmp = {
3751 : .details_to_cmp_length = details_length,
3752 : .details_to_cmp = details,
3753 : .results_matching = results_matching,
3754 : .results_length = 0
3755 : };
3756 0 : memset (results_matching,
3757 : 0,
3758 : sizeof (unsigned int) * details_length);
3759 0 : if (1 != plugin->lookup_transfer_details (plugin->cls,
3760 : exchange_url,
3761 : wtid,
3762 : &lookup_transfer_details_cb,
3763 : &cmp))
3764 : {
3765 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3766 : "Lookup transfer details failed\n");
3767 0 : return 1;
3768 : }
3769 0 : if (details_length != cmp.results_length)
3770 : {
3771 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3772 : "Lookup transfer details failed: incorrect number of results (%d)\n",
3773 : cmp.results_length);
3774 0 : return 1;
3775 : }
3776 0 : for (unsigned int i = 0; details_length > i; ++i)
3777 : {
3778 0 : if (1 != cmp.results_matching[i])
3779 : {
3780 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3781 : "Lookup transfer details failed: mismatched data\n");
3782 0 : return 1;
3783 : }
3784 : }
3785 0 : return 0;
3786 : }
3787 :
3788 :
3789 : /**
3790 : * Closure for 'lookup_transfer_details_by_order'.
3791 : */
3792 : struct TestLookupTransferDetailsByOrder_Closure
3793 : {
3794 : /**
3795 : * Length of @e transfers_to_cmp.
3796 : */
3797 : unsigned int transfers_to_cmp_length;
3798 :
3799 : /**
3800 : * List of transfers that we expect to find.
3801 : */
3802 : const struct TransferData *transfers_to_cmp;
3803 :
3804 : /**
3805 : * How many results match the corresponding element of @e transfers_to_cmp.
3806 : */
3807 : unsigned int *results_matching;
3808 :
3809 : /**
3810 : * Total number of results found.
3811 : */
3812 : unsigned int results_length;
3813 : };
3814 :
3815 :
3816 : /**
3817 : * Called after 'test_lookup_transfer_details_by_order'.
3818 : *
3819 : * @param cls pointer to 'TestLookupTransferDetailsByOrder_Closure'.
3820 : * @param wtid identifier of the transfer found.
3821 : * @param exchange_url exchange url of the transfer found.
3822 : * @param execution_time when the transfer found occurred.
3823 : * @param deposit_value amount of the deposit for the transfer found.
3824 : * @param deposit_fee amount of the fee for the deposit of the transfer.
3825 : * @param transfer_confirmed did the merchant confirm that a wire transfer with
3826 : * @a wtid over the total amount happened?
3827 : */
3828 : static void
3829 0 : lookup_transfer_details_order_cb (
3830 : void *cls,
3831 : const struct TALER_WireTransferIdentifierRawP *wtid,
3832 : const char *exchange_url,
3833 : struct GNUNET_TIME_Timestamp execution_time,
3834 : const struct TALER_Amount *deposit_value,
3835 : const struct TALER_Amount *deposit_fee,
3836 : bool transfer_confirmed)
3837 : {
3838 0 : struct TestLookupTransferDetailsByOrder_Closure *cmp = cls;
3839 :
3840 0 : if (NULL == cmp)
3841 0 : return;
3842 0 : cmp->results_length += 1;
3843 0 : for (unsigned int i = 0; i < cmp->transfers_to_cmp_length; ++i)
3844 : {
3845 : /* Right now lookup_transfer_details_by_order leaves execution_time
3846 : uninitialized */
3847 0 : if ((0 == GNUNET_memcmp (&cmp->transfers_to_cmp[i].wtid,
3848 0 : wtid)) &&
3849 0 : (0 == strcmp (cmp->transfers_to_cmp[i].exchange_url,
3850 0 : exchange_url)) &&
3851 : (GNUNET_OK ==
3852 0 : TALER_amount_cmp_currency (
3853 0 : &cmp->transfers_to_cmp[i].data.total_amount,
3854 0 : deposit_value)) &&
3855 : (0 ==
3856 0 : TALER_amount_cmp (&cmp->transfers_to_cmp[i].data.total_amount,
3857 0 : deposit_value)) &&
3858 : (GNUNET_OK ==
3859 0 : TALER_amount_cmp_currency (
3860 0 : &cmp->transfers_to_cmp[i].deposit_fee,
3861 0 : deposit_fee)) &&
3862 : (0 ==
3863 0 : TALER_amount_cmp (&cmp->transfers_to_cmp[i].deposit_fee,
3864 : deposit_fee)) )
3865 0 : cmp->results_matching[i] += 1;
3866 : }
3867 : }
3868 :
3869 :
3870 : /**
3871 : * Tests looking up wire transfers associated with an order.
3872 : *
3873 : * @param order_serial the order to be queried.
3874 : * @param transfers_length length of @e transfers.
3875 : * @param transfers the transfers we expect to be found.
3876 : * @return 0 on success, 1 otherwise.
3877 : */
3878 : static int
3879 0 : test_lookup_transfer_details_by_order (
3880 : uint64_t order_serial,
3881 : unsigned int transfers_length,
3882 : const struct TransferData *transfers)
3883 0 : {
3884 0 : unsigned int results_matching[transfers_length];
3885 0 : struct TestLookupTransferDetailsByOrder_Closure cmp = {
3886 : .transfers_to_cmp_length = transfers_length,
3887 : .transfers_to_cmp = transfers,
3888 : .results_matching = results_matching,
3889 : .results_length = 0
3890 : };
3891 0 : memset (results_matching,
3892 : 0,
3893 : sizeof (unsigned int) * transfers_length);
3894 0 : if (transfers_length !=
3895 0 : plugin->lookup_transfer_details_by_order (
3896 0 : plugin->cls,
3897 : order_serial,
3898 : &lookup_transfer_details_order_cb,
3899 : &cmp))
3900 : {
3901 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3902 : "Lookup transfer details by order failed\n");
3903 0 : return 1;
3904 : }
3905 0 : if (transfers_length != cmp.results_length)
3906 : {
3907 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3908 : "Lookup transfer details by order failed: incorrect number of results\n");
3909 0 : return 1;
3910 : }
3911 0 : for (unsigned int i = 0; i < transfers_length; ++i)
3912 : {
3913 0 : if (1 != cmp.results_matching[i])
3914 : {
3915 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3916 : "Lookup transfer details by order failed: mismatched data\n");
3917 0 : return 1;
3918 : }
3919 : }
3920 0 : return 0;
3921 : }
3922 :
3923 :
3924 : /**
3925 : * Tests inserting wire fee data for an exchange.
3926 : *
3927 : * @param signkey the signing key for the exchange.
3928 : * @param wire_fee the wire fee data.
3929 : * @param expected_result what the database should return.
3930 : * @return 0 on success, 1 otherwise.
3931 : */
3932 : static int
3933 0 : test_insert_wire_fee (const struct ExchangeSignkeyData *signkey,
3934 : const struct WireFeeData *wire_fee,
3935 : enum GNUNET_DB_QueryStatus expected_result)
3936 : {
3937 0 : TEST_COND_RET_ON_FAIL (expected_result ==
3938 : plugin->store_wire_fee_by_exchange (
3939 : plugin->cls,
3940 : &signkey->master_pub,
3941 : &wire_fee->h_wire_method,
3942 : &wire_fee->fees,
3943 : wire_fee->wire_fee_start,
3944 : wire_fee->wire_fee_end,
3945 : &wire_fee->fee_sig),
3946 : "Store wire fee by exchange failed\n");
3947 0 : return 0;
3948 : }
3949 :
3950 :
3951 : /**
3952 : * Tests looking up wire fee data for an exchange.
3953 : *
3954 : * @param signkey the signing key to use for lookup.
3955 : * @param wire_fee_data the wire fee data we expect to find.
3956 : * @return 0 on success, 1 otherwise.
3957 : */
3958 : static int
3959 0 : test_lookup_wire_fee (const struct ExchangeSignkeyData *signkey,
3960 : const struct WireFeeData *wire_fee_data)
3961 : {
3962 : struct TALER_WireFeeSet fees;
3963 : struct GNUNET_TIME_Timestamp start_date;
3964 : struct GNUNET_TIME_Timestamp end_date;
3965 : struct TALER_MasterSignatureP master_sig;
3966 0 : if (1 != plugin->lookup_wire_fee (plugin->cls,
3967 : &signkey->master_pub,
3968 0 : wire_fee_data->wire_method,
3969 : GNUNET_TIME_timestamp_get (),
3970 : &fees,
3971 : &start_date,
3972 : &end_date,
3973 : &master_sig))
3974 : {
3975 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3976 : "Lookup wire fee failed\n");
3977 0 : return 1;
3978 : }
3979 0 : if ((0 !=
3980 0 : TALER_wire_fee_set_cmp (&wire_fee_data->fees,
3981 0 : &fees)) ||
3982 0 : (GNUNET_TIME_timestamp_cmp (wire_fee_data->wire_fee_start,
3983 : !=,
3984 0 : start_date)) ||
3985 0 : (GNUNET_TIME_timestamp_cmp (wire_fee_data->wire_fee_end,
3986 : !=,
3987 0 : end_date)) ||
3988 0 : (0 != GNUNET_memcmp (&wire_fee_data->fee_sig,
3989 : &master_sig)))
3990 : {
3991 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3992 : "Lookup wire fee failed: mismatched data\n");
3993 0 : return 1;
3994 : }
3995 0 : return 0;
3996 : }
3997 :
3998 :
3999 : /**
4000 : * Closure for 'lookup_transfers'.
4001 : */
4002 : struct TestLookupTransfers_Closure
4003 : {
4004 : /**
4005 : * Length of @e transfers_to_cmp.
4006 : */
4007 : unsigned int transfers_to_cmp_length;
4008 :
4009 : /**
4010 : * The transfers we expect to find.
4011 : */
4012 : const struct TransferData *transfers_to_cmp;
4013 :
4014 : /**
4015 : * Number of results matching each transfer.
4016 : */
4017 : unsigned int *results_matching;
4018 :
4019 : /**
4020 : * Total number of results found.
4021 : */
4022 : unsigned int results_length;
4023 : };
4024 :
4025 :
4026 : /**
4027 : * Function called after 'test_lookup_transfers'.
4028 : *
4029 : * @param cls pointer to 'TestLookupTransfers_Closure'.
4030 : * @param credit_amount how much was wired to the merchant (minus fees)
4031 : * @param wtid wire transfer identifier
4032 : * @param payto_uri target account that received the wire transfer
4033 : * @param exchange_url base URL of the exchange that made the wire transfer
4034 : * @param transfer_serial_id serial number identifying the transfer in the backend
4035 : * @param execution_time when did the exchange make the transfer, #GNUNET_TIME_UNIT_FOREVER_TS
4036 : * if it did not yet happen
4037 : * @param confirmed true if the merchant confirmed this wire transfer
4038 : * false if it is so far only claimed to have been made by the exchange
4039 : */
4040 : static void
4041 0 : lookup_transfers_cb (void *cls,
4042 : const struct TALER_Amount *credit_amount,
4043 : const struct TALER_WireTransferIdentifierRawP *wtid,
4044 : struct TALER_FullPayto payto_uri,
4045 : const char *exchange_url,
4046 : uint64_t transfer_serial_id,
4047 : struct GNUNET_TIME_Absolute execution_time,
4048 : bool confirmed)
4049 : {
4050 0 : struct TestLookupTransfers_Closure *cmp = cls;
4051 0 : if (NULL == cmp)
4052 0 : return;
4053 0 : for (unsigned int i = 0; cmp->transfers_to_cmp_length > i; ++i)
4054 : {
4055 0 : if ( (GNUNET_OK ==
4056 0 : TALER_amount_cmp_currency (
4057 0 : &cmp->transfers_to_cmp[i].data.total_amount,
4058 0 : credit_amount)) &&
4059 0 : (0 == TALER_amount_cmp (&cmp->transfers_to_cmp[i].data.total_amount,
4060 : credit_amount)) )
4061 : {
4062 0 : cmp->results_matching[i]++;
4063 : }
4064 : }
4065 0 : cmp->results_length++;
4066 : }
4067 :
4068 :
4069 : /**
4070 : * Tests looking up transfers from the database.
4071 : *
4072 : * @param instance the instance to lookup from.
4073 : * @param account the account the transfer was made to.
4074 : * @param before do not return transfers before this time.
4075 : * @param after do not return transfers after this time.
4076 : * @param limit maximum number of transfers to return.
4077 : * @param offset row in the database to start with.
4078 : * @param filter_verified how to filter verified transfers.
4079 : * @param transfers_length length of @e transfers.
4080 : * @param transfers the transfers we expect to find.
4081 : * @return 0 on success, 1 otherwise.
4082 : */
4083 : static int
4084 0 : test_lookup_transfers (const struct InstanceData *instance,
4085 : const struct TALER_MERCHANTDB_AccountDetails *account,
4086 : struct GNUNET_TIME_Timestamp before,
4087 : struct GNUNET_TIME_Timestamp after,
4088 : int64_t limit,
4089 : uint64_t offset,
4090 : unsigned int transfers_length,
4091 : const struct TransferData *transfers)
4092 0 : {
4093 0 : unsigned int results_matching[transfers_length];
4094 0 : struct TestLookupTransfers_Closure cmp = {
4095 : .transfers_to_cmp_length = transfers_length,
4096 : .transfers_to_cmp = transfers,
4097 : .results_matching = results_matching,
4098 : .results_length = 0
4099 : };
4100 0 : memset (results_matching,
4101 : 0,
4102 : sizeof (unsigned int) * transfers_length);
4103 0 : if (1 != plugin->lookup_transfers (plugin->cls,
4104 0 : instance->instance.id,
4105 : account->payto_uri,
4106 : before,
4107 : after,
4108 : limit,
4109 : offset,
4110 : TALER_EXCHANGE_YNA_ALL,
4111 : &lookup_transfers_cb,
4112 : &cmp))
4113 : {
4114 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
4115 : "Lookup transfers failed\n");
4116 0 : return 1;
4117 : }
4118 0 : if (transfers_length != cmp.results_length)
4119 : {
4120 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
4121 : "Lookup transfers failed: incorrect number of results\n");
4122 0 : return 1;
4123 : }
4124 0 : for (unsigned int i = 0; transfers_length > i; ++i)
4125 : {
4126 0 : if (1 != cmp.results_matching[i])
4127 : {
4128 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
4129 : "Lookup transfers failed: mismatched data\n");
4130 0 : return 1;
4131 : }
4132 : }
4133 0 : return 0;
4134 : }
4135 :
4136 :
4137 : /**
4138 : * Tests inserting a transfer into the database.
4139 : *
4140 : * @param instance the instance to use.
4141 : * @param account the account to transfer to.
4142 : * @param transfer the transfer to insert.
4143 : * @param expected_result the result we expect the db to return.
4144 : * @return 0 on success, 1 otherwise.
4145 : */
4146 : static int
4147 0 : test_insert_transfer (const struct InstanceData *instance,
4148 : const struct TALER_MERCHANTDB_AccountDetails *account,
4149 : const struct TransferData *transfer,
4150 : enum GNUNET_DB_QueryStatus expected_result)
4151 : {
4152 0 : TEST_COND_RET_ON_FAIL (expected_result ==
4153 : plugin->insert_transfer (plugin->cls,
4154 : instance->instance.id,
4155 : transfer->exchange_url,
4156 : &transfer->wtid,
4157 : &transfer->data.total_amount,
4158 : account->payto_uri,
4159 : transfer->confirmed),
4160 : "Insert transfer failed\n");
4161 0 : return 0;
4162 : }
4163 :
4164 :
4165 : /**
4166 : * Tests linking a deposit to a transfer.
4167 : *
4168 : * @param instance the instance that the deposit and transfer are for.
4169 : * @param signkey the signing used on the deposit.
4170 : * @param order the order the deposit and transfer were made for.
4171 : * @param transfer the transfer.
4172 : * @param expected_result the result the database should return.
4173 : * @param expected_cleared clearance status the database should return.
4174 : * @return 0 on success, 1 otherwise.
4175 : */
4176 : static int
4177 0 : test_insert_deposit_to_transfer (const struct InstanceData *instance,
4178 : const struct ExchangeSignkeyData *signkey,
4179 : const struct OrderData *order,
4180 : const struct DepositData *deposit,
4181 : const struct TransferData *transfer,
4182 : enum GNUNET_DB_QueryStatus expected_result,
4183 : bool expect_cleared)
4184 : {
4185 0 : const struct TALER_EXCHANGE_DepositData deposit_data = {
4186 : .exchange_pub = signkey->exchange_pub,
4187 : .exchange_sig = deposit->exchange_sig,
4188 : .wtid = transfer->wtid,
4189 : .execution_time = transfer->data.execution_time,
4190 : .coin_contribution = deposit->amount_with_fee
4191 : };
4192 0 : uint64_t deposit_serial = get_deposit_serial (instance,
4193 : order,
4194 : deposit);
4195 :
4196 0 : TEST_COND_RET_ON_FAIL (expected_result ==
4197 : plugin->insert_deposit_to_transfer (
4198 : plugin->cls,
4199 : deposit_serial,
4200 : &deposit->h_wire,
4201 : deposit->exchange_url,
4202 : &deposit_data),
4203 : "insert deposit to transfer failed\n");
4204 0 : return 0;
4205 : }
4206 :
4207 :
4208 : /**
4209 : * Inserts details for a transfer into the database.
4210 : *
4211 : * @param instance the instance the transfer is in.
4212 : * @param account the destination account for the transfer.
4213 : * @param transfer the transfer we are adding details to.
4214 : * @param expected_result the result expected from the db.
4215 : * @return 0 on success, 1 otherwise.
4216 : */
4217 : static int
4218 0 : test_insert_transfer_details (
4219 : const struct InstanceData *instance,
4220 : const struct TALER_MERCHANTDB_AccountDetails *account,
4221 : const struct TransferData *transfer,
4222 : enum GNUNET_DB_QueryStatus expected_result)
4223 : {
4224 0 : TEST_COND_RET_ON_FAIL (expected_result ==
4225 : plugin->insert_transfer_details (
4226 : plugin->cls,
4227 : instance->instance.id,
4228 : transfer->exchange_url,
4229 : account->payto_uri,
4230 : &transfer->wtid,
4231 : &transfer->data),
4232 : "Insert transfer details failed\n");
4233 0 : return 0;
4234 : }
4235 :
4236 :
4237 : /**
4238 : * Container for data used when testing transfers.
4239 : */
4240 : struct TestTransfers_Closure
4241 : {
4242 : /**
4243 : * The instance.
4244 : */
4245 : struct InstanceData instance;
4246 :
4247 : /**
4248 : * The account.
4249 : */
4250 : struct TALER_MERCHANTDB_AccountDetails account;
4251 :
4252 : /**
4253 : * The exchange signing key.
4254 : */
4255 : struct ExchangeSignkeyData signkey;
4256 :
4257 : /**
4258 : * The order data.
4259 : */
4260 : struct OrderData order;
4261 :
4262 : /**
4263 : * The deposit data.
4264 : */
4265 : struct DepositData deposit;
4266 :
4267 : /**
4268 : * Wire fee data.
4269 : */
4270 : struct WireFeeData wire_fee[2];
4271 :
4272 : /**
4273 : * The transfers.
4274 : */
4275 : struct TransferData transfers[1];
4276 : };
4277 :
4278 :
4279 : /**
4280 : * Prepares for testing transfers.
4281 : *
4282 : * @param cls the test data.
4283 : */
4284 : static void
4285 0 : pre_test_transfers (struct TestTransfers_Closure *cls)
4286 : {
4287 : /* Instance */
4288 0 : make_instance ("test_inst_transfers",
4289 : &cls->instance);
4290 :
4291 : /* Account */
4292 0 : make_account (&cls->account);
4293 0 : cls->account.instance_id = cls->instance.instance.id;
4294 : /* Order */
4295 0 : make_order ("test_transfers_od_1",
4296 : &cls->order);
4297 :
4298 : /* Signing key */
4299 0 : make_exchange_signkey (&cls->signkey);
4300 :
4301 : /* Deposit */
4302 0 : make_deposit (&cls->instance,
4303 0 : &cls->account,
4304 0 : &cls->order,
4305 0 : &cls->signkey,
4306 : &cls->deposit);
4307 :
4308 : /* Wire fee */
4309 0 : make_wire_fee (&cls->signkey,
4310 : &cls->wire_fee[0]);
4311 0 : make_wire_fee (&cls->signkey,
4312 : &cls->wire_fee[1]);
4313 0 : cls->wire_fee[1].wire_method = "wire-method-2";
4314 0 : GNUNET_CRYPTO_hash (cls->wire_fee[1].wire_method,
4315 0 : strlen (cls->wire_fee[1].wire_method) + 1,
4316 : &cls->wire_fee[1].h_wire_method);
4317 :
4318 : /* Transfers */
4319 0 : make_transfer (&cls->signkey,
4320 : 1,
4321 0 : &cls->deposit,
4322 : &cls->transfers[0]);
4323 0 : cls->transfers[0].confirmed = true;
4324 0 : }
4325 :
4326 :
4327 : /**
4328 : * Cleans up after testing transfers.
4329 : *
4330 : * @param cls the test data.
4331 : */
4332 : static void
4333 0 : post_test_transfers (struct TestTransfers_Closure *cls)
4334 : {
4335 0 : GNUNET_array_grow (cls->transfers->data.details,
4336 : cls->transfers->data.details_length,
4337 : 0);
4338 0 : free_instance_data (&cls->instance);
4339 0 : free_order_data (&cls->order);
4340 0 : }
4341 :
4342 :
4343 : /**
4344 : * Runs the tests for transfers.
4345 : *
4346 : * @param cls the test data.
4347 : * @return 0 on success, 1 otherwise.
4348 : */
4349 : static int
4350 0 : run_test_transfers (struct TestTransfers_Closure *cls)
4351 : {
4352 : uint64_t order_serial;
4353 : struct TALER_WireFeeSet fees;
4354 :
4355 : /* Test lookup wire fee fails when it isn't in the db */
4356 0 : TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
4357 : plugin->lookup_wire_fee (plugin->cls,
4358 : &cls->signkey.master_pub,
4359 : cls->wire_fee[0].wire_method,
4360 : GNUNET_TIME_timestamp_get (),
4361 : &fees,
4362 : NULL,
4363 : NULL,
4364 : NULL),
4365 : "Lookup wire fee failed\n");
4366 : /* Test store wire fee by exchange */
4367 0 : TEST_RET_ON_FAIL (test_insert_wire_fee (&cls->signkey,
4368 : &cls->wire_fee[0],
4369 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
4370 : /* Test double insertion fails */
4371 0 : TEST_RET_ON_FAIL (test_insert_wire_fee (&cls->signkey,
4372 : &cls->wire_fee[0],
4373 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
4374 : /* Test lookup wire fee by exchange */
4375 0 : TEST_RET_ON_FAIL (test_lookup_wire_fee (&cls->signkey,
4376 : &cls->wire_fee[0]));
4377 : /* Test different wire fees for different methods. */
4378 0 : TEST_RET_ON_FAIL (test_insert_wire_fee (&cls->signkey,
4379 : &cls->wire_fee[1],
4380 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
4381 0 : TEST_RET_ON_FAIL (test_lookup_wire_fee (&cls->signkey,
4382 : &cls->wire_fee[1]));
4383 : /* Insert the instance */
4384 0 : TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
4385 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
4386 : /* Insert the account */
4387 0 : TEST_RET_ON_FAIL (test_insert_account (&cls->instance,
4388 : &cls->account,
4389 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
4390 : /* Insert a signing key */
4391 0 : TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey,
4392 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
4393 : /* Insert an order */
4394 0 : TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
4395 : &cls->order,
4396 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
4397 : /* Insert contract terms */
4398 0 : TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
4399 : &cls->order,
4400 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
4401 0 : order_serial = get_order_serial (&cls->instance,
4402 0 : &cls->order);
4403 : /* Insert the deposit */
4404 0 : TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
4405 : &cls->signkey,
4406 : &cls->deposit,
4407 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
4408 : /* Insert the transfer */
4409 0 : TEST_RET_ON_FAIL (test_insert_transfer (&cls->instance,
4410 : &cls->account,
4411 : &cls->transfers[0],
4412 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
4413 0 : TEST_RET_ON_FAIL (test_insert_transfer (&cls->instance,
4414 : &cls->account,
4415 : &cls->transfers[0],
4416 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
4417 0 : TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance,
4418 : &cls->signkey,
4419 : &cls->order,
4420 : &cls->deposit,
4421 : &cls->transfers[0],
4422 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
4423 : false));
4424 0 : TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance,
4425 : &cls->signkey,
4426 : &cls->order,
4427 : &cls->deposit,
4428 : &cls->transfers[0],
4429 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
4430 : false));
4431 0 : TEST_RET_ON_FAIL (test_insert_transfer_details (&cls->instance,
4432 : &cls->account,
4433 : &cls->transfers[0],
4434 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
4435 0 : TEST_RET_ON_FAIL (test_lookup_payment_status (cls->instance.instance.id,
4436 : cls->order.id,
4437 : NULL,
4438 : false,
4439 : false));
4440 0 : TEST_RET_ON_FAIL (test_insert_deposit_to_transfer (&cls->instance,
4441 : &cls->signkey,
4442 : &cls->order,
4443 : &cls->deposit,
4444 : &cls->transfers[0],
4445 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
4446 : true));
4447 :
4448 0 : TEST_RET_ON_FAIL (test_lookup_transfer_summary (cls->deposit.exchange_url,
4449 : &cls->transfers[0].wtid,
4450 : cls->order.id,
4451 : &cls->deposit.amount_with_fee,
4452 : &cls->deposit.deposit_fee));
4453 0 : TEST_RET_ON_FAIL (test_lookup_transfer_details (cls->deposit.exchange_url,
4454 : &cls->transfers[0].wtid,
4455 : 1,
4456 : &cls->transfers[0].data.
4457 : details[0]));
4458 0 : TEST_RET_ON_FAIL (test_lookup_transfer_details_by_order (order_serial,
4459 : 1,
4460 : &cls->transfers[0]));
4461 0 : TEST_RET_ON_FAIL (test_lookup_transfers (&cls->instance,
4462 : &cls->account,
4463 : GNUNET_TIME_UNIT_FOREVER_TS,
4464 : GNUNET_TIME_UNIT_ZERO_TS,
4465 : 8,
4466 : 0,
4467 : 1,
4468 : &cls->transfers[0]));
4469 0 : return 0;
4470 : }
4471 :
4472 :
4473 : /**
4474 : * Takes care of all work for testing transfers.
4475 : *
4476 : * @return 0 on success, 1 otherwise.
4477 : */
4478 : static int
4479 0 : test_transfers (void)
4480 : {
4481 : struct TestTransfers_Closure test_cls;
4482 : int test_result;
4483 :
4484 0 : pre_test_transfers (&test_cls);
4485 0 : test_result = run_test_transfers (&test_cls);
4486 0 : post_test_transfers (&test_cls);
4487 0 : return test_result;
4488 : }
4489 :
4490 :
4491 : /**
4492 : * Closure for testing lookup_refunds.
4493 : */
4494 : struct TestLookupRefunds_Closure
4495 : {
4496 : /**
4497 : * Length of @e coin_pub_to_cmp and @e refund_amount_to_cmp.
4498 : */
4499 : unsigned int refunds_to_cmp_length;
4500 :
4501 : /**
4502 : * Public keys of the refunded coins.
4503 : */
4504 : const struct TALER_CoinSpendPublicKeyP *coin_pub_to_cmp;
4505 :
4506 : /**
4507 : * Amount of each refund.
4508 : */
4509 : const struct TALER_Amount *refund_amount_to_cmp;
4510 :
4511 : /**
4512 : * Number of results matching each refund provided.
4513 : */
4514 : unsigned int *results_matching;
4515 :
4516 : /**
4517 : * Total number of results returned;
4518 : */
4519 : unsigned int results_length;
4520 : };
4521 :
4522 :
4523 : /**
4524 : * Called after test_lookup_refunds.
4525 : * @param cls pointer to a TestLookupRefunds_Closure.
4526 : * @param coin_pub the public key of the coin for the refund found.
4527 : * @param refund_amount the amount of the refund found.
4528 : */
4529 : static void
4530 0 : lookup_refunds_cb (void *cls,
4531 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
4532 : const struct TALER_Amount *refund_amount)
4533 : {
4534 0 : struct TestLookupRefunds_Closure *cmp = cls;
4535 0 : if (NULL == cmp)
4536 0 : return;
4537 0 : cmp->results_length += 1;
4538 0 : for (unsigned int i = 0; cmp->refunds_to_cmp_length > i; ++i)
4539 : {
4540 0 : if ((0 == GNUNET_memcmp (&cmp->coin_pub_to_cmp[i],
4541 0 : coin_pub)) &&
4542 0 : (GNUNET_OK == TALER_amount_cmp_currency (&cmp->refund_amount_to_cmp[i],
4543 0 : refund_amount)) &&
4544 0 : (0 == TALER_amount_cmp (&cmp->refund_amount_to_cmp[i],
4545 : refund_amount)))
4546 : {
4547 0 : cmp->results_matching[i] += 1;
4548 : }
4549 : }
4550 : }
4551 :
4552 :
4553 : /**
4554 : * Tests looking up refunds from the database.
4555 : * @param instance the instance to look up refunds for.
4556 : * @param h_contract_terms hash of the contract terms the refunds are for.
4557 : * @param refunds_length length of @e coin_pubs and @e refund_amounts.
4558 : * @param coin_pubs the public keys of the coins that were refunded.
4559 : * @param refund_amounts the amounts of the coins that were refunded.
4560 : *
4561 : * @return 0 on success, 1 otherwise.
4562 : */
4563 : static int
4564 0 : test_lookup_refunds (const struct InstanceData *instance,
4565 : const struct TALER_PrivateContractHashP *h_contract_terms,
4566 : unsigned int refunds_length,
4567 : const struct TALER_CoinSpendPublicKeyP *coin_pubs,
4568 : const struct TALER_Amount *refund_amounts)
4569 0 : {
4570 0 : unsigned int results_matching[refunds_length];
4571 0 : struct TestLookupRefunds_Closure cmp = {
4572 : .refunds_to_cmp_length = refunds_length,
4573 : .coin_pub_to_cmp = coin_pubs,
4574 : .refund_amount_to_cmp = refund_amounts,
4575 : .results_matching = results_matching,
4576 : .results_length = 0
4577 : };
4578 0 : memset (results_matching, 0, sizeof (unsigned int) * refunds_length);
4579 0 : if (1 != plugin->lookup_refunds (plugin->cls,
4580 0 : instance->instance.id,
4581 : h_contract_terms,
4582 : &lookup_refunds_cb,
4583 : &cmp))
4584 : {
4585 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
4586 : "Lookup refunds failed\n");
4587 0 : return 1;
4588 : }
4589 0 : if (refunds_length != cmp.results_length)
4590 : {
4591 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
4592 : "Lookup refunds failed: incorrect number of results returned\n")
4593 : ;
4594 0 : return 1;
4595 : }
4596 0 : for (unsigned int i = 0; refunds_length > i; ++i)
4597 : {
4598 0 : if (1 != cmp.results_matching[i])
4599 : {
4600 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
4601 : "Lookup refunds failed: mismatched data\n");
4602 0 : return 1;
4603 : }
4604 : }
4605 0 : return 0;
4606 : }
4607 :
4608 :
4609 : /**
4610 : * Container for refund data.
4611 : */
4612 : struct RefundData
4613 : {
4614 : /**
4615 : * When the refund occurred.
4616 : */
4617 : struct GNUNET_TIME_Timestamp timestamp;
4618 :
4619 : /**
4620 : * Reason for the refund.
4621 : */
4622 : const char *reason;
4623 :
4624 : /**
4625 : * Amount of the refund.
4626 : */
4627 : struct TALER_Amount refund_amount;
4628 :
4629 : /**
4630 : * Public key of the coin that was refunded.
4631 : */
4632 : const struct TALER_CoinSpendPublicKeyP *coin_pub;
4633 :
4634 : /**
4635 : * URL to the exchange that did the refund.
4636 : */
4637 : const char *exchange_url;
4638 : };
4639 :
4640 :
4641 : /**
4642 : * Creates a refund for testing with.
4643 : * @param deposit the deposit being refunded.
4644 : * @param refund the data to set.
4645 : */
4646 : static void
4647 0 : make_refund (const struct DepositData *deposit,
4648 : struct RefundData *refund)
4649 : {
4650 0 : refund->timestamp = GNUNET_TIME_timestamp_get ();
4651 0 : refund->reason = "some reason";
4652 0 : refund->refund_amount = deposit->amount_with_fee;
4653 0 : refund->coin_pub = &deposit->coin_pub;
4654 0 : refund->exchange_url = deposit->exchange_url;
4655 0 : }
4656 :
4657 :
4658 : /**
4659 : * Container for proof of a refund.
4660 : */
4661 : struct RefundProofData
4662 : {
4663 : /**
4664 : * Fee charged for the refund.
4665 : */
4666 : struct TALER_Amount refund_fee;
4667 :
4668 : /**
4669 : * The exchange's signature on the refund.
4670 : */
4671 : struct TALER_ExchangeSignatureP exchange_sig;
4672 : };
4673 :
4674 :
4675 : /**
4676 : * Closure for testing lookup_refunds_detailed.
4677 : */
4678 : struct TestLookupRefundsDetailed_Closure
4679 : {
4680 : /**
4681 : * Length of @e refunds_to_cmp.
4682 : */
4683 : unsigned int refunds_to_cmp_length;
4684 :
4685 : /**
4686 : * The refunds we expect to find.
4687 : */
4688 : const struct RefundData *refunds_to_cmp;
4689 :
4690 : /**
4691 : * Whether to compare the timestamps or not (if we don't have direct control
4692 : * of the timestamps, there will be differences on the order of microseconds
4693 : * that can be ignored).
4694 : */
4695 : bool cmp_timestamps;
4696 :
4697 : /**
4698 : * The number of results matching each refund.
4699 : */
4700 : unsigned int *results_matching;
4701 :
4702 : /**
4703 : * The total number of results from the db.
4704 : */
4705 : unsigned int results_length;
4706 : };
4707 :
4708 :
4709 : /**
4710 : * Called after test_lookup_refunds_detailed.
4711 : * @param cls pointer to a TestLookupRefundsDetailed_Closure.
4712 : * @param refund_serial unique serial number of the refund
4713 : * @param timestamp time of the refund (for grouping of refunds in the wallet UI)
4714 : * @param coin_pub public coin from which the refund comes from
4715 : * @param exchange_url URL of the exchange that issued @a coin_pub
4716 : * @param rtransaction_id identificator of the refund
4717 : * @param reason human-readable explanation of the refund
4718 : * @param refund_amount refund amount which is being taken from @a coin_pub
4719 : * @param pending true if this refund has not been processed by the wallet/exchange
4720 : */
4721 : static void
4722 0 : lookup_refunds_detailed_cb (void *cls,
4723 : uint64_t refund_serial,
4724 : struct GNUNET_TIME_Timestamp timestamp,
4725 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
4726 : const char *exchange_url,
4727 : uint64_t rtransaction_id,
4728 : const char *reason,
4729 : const struct TALER_Amount *refund_amount,
4730 : bool pending)
4731 : {
4732 0 : struct TestLookupRefundsDetailed_Closure *cmp = cls;
4733 0 : if (NULL == cmp)
4734 0 : return;
4735 0 : cmp->results_length += 1;
4736 0 : for (unsigned int i = 0; cmp->refunds_to_cmp_length > i; ++i)
4737 : {
4738 0 : if (((GNUNET_TIME_timestamp_cmp (cmp->refunds_to_cmp[i].timestamp,
4739 : ==,
4740 0 : timestamp)) ||
4741 0 : ! cmp->cmp_timestamps) &&
4742 0 : (0 == GNUNET_memcmp (cmp->refunds_to_cmp[i].coin_pub,
4743 0 : coin_pub)) &&
4744 0 : (0 == strcmp (cmp->refunds_to_cmp[i].exchange_url,
4745 0 : exchange_url)) &&
4746 0 : (0 == strcmp (cmp->refunds_to_cmp[i].reason,
4747 0 : reason)) &&
4748 : (GNUNET_OK ==
4749 0 : TALER_amount_cmp_currency (
4750 0 : &cmp->refunds_to_cmp[i].refund_amount,
4751 0 : refund_amount)) &&
4752 0 : (0 == TALER_amount_cmp (&cmp->refunds_to_cmp[i].refund_amount,
4753 : refund_amount)))
4754 : {
4755 0 : cmp->results_matching[i] += 1;
4756 : }
4757 : }
4758 : }
4759 :
4760 :
4761 : /**
4762 : * Tests looking up refunds with details from the database.
4763 : * @param instance the instance to lookup from.
4764 : * @param h_contract_terms the contract terms the refunds were made for.
4765 : * @param cmp_timestamps whether to compare timestamps or not.
4766 : * @param refunds_length length of @e refunds.
4767 : * @param refunds the refunds we expect to be returned.
4768 : *
4769 : * @return 0 on success, 1 otherwise.
4770 : */
4771 : static int
4772 0 : test_lookup_refunds_detailed (
4773 : const struct InstanceData *instance,
4774 : const struct TALER_PrivateContractHashP *h_contract_terms,
4775 : bool cmp_timestamps,
4776 : unsigned int refunds_length,
4777 : const struct RefundData *refunds)
4778 0 : {
4779 0 : unsigned int results_matching[refunds_length];
4780 0 : struct TestLookupRefundsDetailed_Closure cmp = {
4781 : .refunds_to_cmp_length = refunds_length,
4782 : .refunds_to_cmp = refunds,
4783 : .cmp_timestamps = cmp_timestamps,
4784 : .results_matching = results_matching,
4785 : .results_length = 0
4786 : };
4787 0 : memset (results_matching, 0, sizeof (unsigned int) * refunds_length);
4788 0 : if (0 > plugin->lookup_refunds_detailed (plugin->cls,
4789 0 : instance->instance.id,
4790 : h_contract_terms,
4791 : &lookup_refunds_detailed_cb,
4792 : &cmp))
4793 : {
4794 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
4795 : "Lookup refunds detailed failed\n");
4796 0 : return 1;
4797 : }
4798 0 : if (refunds_length != cmp.results_length)
4799 : {
4800 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
4801 : "Lookup refunds detailed failed: incorrect number of results\n")
4802 : ;
4803 0 : return 1;
4804 : }
4805 0 : for (unsigned int i = 0; refunds_length > i; ++i)
4806 : {
4807 0 : if (1 != cmp.results_matching[i])
4808 : {
4809 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
4810 : "Lookup refunds detailed failed: mismatched data\n");
4811 0 : return 1;
4812 : }
4813 : }
4814 0 : return 0;
4815 : }
4816 :
4817 :
4818 : /**
4819 : * Closure for get_refund_serial.
4820 : */
4821 : struct LookupRefundSerial_Closure
4822 : {
4823 : /**
4824 : * The refund we are looking up the id for.
4825 : */
4826 : const struct RefundData *refund;
4827 :
4828 : /**
4829 : * The row number found.
4830 : */
4831 : uint64_t serial;
4832 : };
4833 :
4834 :
4835 : /**
4836 : * Called after get_refund_serial.
4837 : * @param cls pointer to a LookupRefundSerial_Closure.
4838 : * @param refund_serial unique serial number of the refund
4839 : * @param timestamp time of the refund (for grouping of refunds in the wallet UI)
4840 : * @param coin_pub public coin from which the refund comes from
4841 : * @param exchange_url URL of the exchange that issued @a coin_pub
4842 : * @param rtransaction_id identificator of the refund
4843 : * @param reason human-readable explanation of the refund
4844 : * @param refund_amount refund amount which is being taken from @a coin_pub
4845 : * @param pending true if this refund has not been processed by the wallet/exchange
4846 : */
4847 : static void
4848 0 : get_refund_serial_cb (void *cls,
4849 : uint64_t refund_serial,
4850 : struct GNUNET_TIME_Timestamp timestamp,
4851 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
4852 : const char *exchange_url,
4853 : uint64_t rtransaction_id,
4854 : const char *reason,
4855 : const struct TALER_Amount *refund_amount,
4856 : bool pending)
4857 : {
4858 0 : struct LookupRefundSerial_Closure *lookup_cls = cls;
4859 0 : if (NULL == lookup_cls)
4860 0 : return;
4861 0 : if ((GNUNET_TIME_timestamp_cmp (lookup_cls->refund->timestamp,
4862 : ==,
4863 0 : timestamp)) &&
4864 0 : (0 == GNUNET_memcmp (lookup_cls->refund->coin_pub,
4865 0 : coin_pub)) &&
4866 0 : (0 == strcmp (lookup_cls->refund->exchange_url,
4867 0 : exchange_url)) &&
4868 0 : (0 == strcmp (lookup_cls->refund->reason,
4869 0 : reason)) &&
4870 : (GNUNET_OK ==
4871 0 : TALER_amount_cmp_currency (
4872 0 : &lookup_cls->refund->refund_amount,
4873 0 : refund_amount)) &&
4874 0 : (0 == TALER_amount_cmp (&lookup_cls->refund->refund_amount,
4875 : refund_amount)))
4876 0 : lookup_cls->serial = refund_serial;
4877 : }
4878 :
4879 :
4880 : /**
4881 : * Utility function for getting the database row number of a refund.
4882 : * @param instance the instance associated with the refund.
4883 : * @param h_contract_terms the contract terms the refund was made with.
4884 : * @param refund the refund we are querying the row number of.
4885 : *
4886 : * @return the row number of the refund.
4887 : */
4888 : static uint64_t
4889 0 : get_refund_serial (const struct InstanceData *instance,
4890 : const struct TALER_PrivateContractHashP *h_contract_terms,
4891 : const struct RefundData *refund)
4892 : {
4893 0 : struct LookupRefundSerial_Closure lookup_cls = {
4894 : .refund = refund,
4895 : .serial = 0
4896 : };
4897 :
4898 0 : GNUNET_assert (0 < plugin->lookup_refunds_detailed (plugin->cls,
4899 : instance->instance.id,
4900 : h_contract_terms,
4901 : &get_refund_serial_cb,
4902 : &lookup_cls));
4903 0 : GNUNET_assert (0 != lookup_cls.serial);
4904 :
4905 0 : return lookup_cls.serial;
4906 : }
4907 :
4908 :
4909 : /**
4910 : * Tests looking up proof of a refund.
4911 : * @param refund_serial the row number of the refund.
4912 : * @param expected_exchange_sig the exchange signature we are expecting.
4913 : * @param expected_exchange_pub the exchange public key we are expecting.
4914 : *
4915 : * @return 0 on success, 1 otherwise.
4916 : */
4917 : static int
4918 0 : test_lookup_refund_proof (uint64_t refund_serial,
4919 : const struct
4920 : TALER_ExchangeSignatureP *expected_exchange_sig,
4921 : const struct
4922 : TALER_ExchangePublicKeyP *expected_exchange_pub)
4923 : {
4924 : struct TALER_ExchangeSignatureP exchange_sig;
4925 : struct TALER_ExchangePublicKeyP exchange_pub;
4926 0 : if (1 != plugin->lookup_refund_proof (plugin->cls,
4927 : refund_serial,
4928 : &exchange_sig,
4929 : &exchange_pub))
4930 : {
4931 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
4932 : "Lookup refund proof failed\n");
4933 0 : return 1;
4934 : }
4935 0 : if ((0 != GNUNET_memcmp (expected_exchange_sig,
4936 0 : &exchange_sig)) ||
4937 0 : (0 != GNUNET_memcmp (expected_exchange_pub,
4938 : &exchange_pub)))
4939 : {
4940 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
4941 : "Lookup refund proof failed: mismatched data\n");
4942 0 : return 1;
4943 : }
4944 0 : return 0;
4945 : }
4946 :
4947 :
4948 : /**
4949 : * Closure for testing refund functionality.
4950 : */
4951 : struct TestRefunds_Closure
4952 : {
4953 : /**
4954 : * The instance.
4955 : */
4956 : struct InstanceData instance;
4957 :
4958 : /**
4959 : * The merchant account.
4960 : */
4961 : struct TALER_MERCHANTDB_AccountDetails account;
4962 :
4963 : /**
4964 : * The exchange signing key.
4965 : */
4966 : struct ExchangeSignkeyData signkey;
4967 :
4968 : /**
4969 : * The order data.
4970 : */
4971 : struct OrderData orders[2];
4972 :
4973 : /**
4974 : * The deposit data.
4975 : */
4976 : struct DepositData deposits[3];
4977 :
4978 : /**
4979 : * The refund data.
4980 : */
4981 : struct RefundData refunds[3];
4982 :
4983 : /**
4984 : * The refund proof data.
4985 : */
4986 : struct RefundProofData refund_proof;
4987 : };
4988 :
4989 :
4990 : /**
4991 : * Prepares for testing refunds.
4992 : * @param cls the closure to initialize.
4993 : */
4994 : static void
4995 0 : pre_test_refunds (struct TestRefunds_Closure *cls)
4996 : {
4997 : /* Instance */
4998 0 : make_instance ("test_inst_refunds",
4999 : &cls->instance);
5000 :
5001 : /* Account */
5002 0 : make_account (&cls->account);
5003 0 : cls->account.instance_id = cls->instance.instance.id;
5004 : /* Signing key */
5005 0 : make_exchange_signkey (&cls->signkey);
5006 :
5007 : /* Order */
5008 0 : make_order ("test_refunds_od_0",
5009 : &cls->orders[0]);
5010 0 : make_order ("test_refunds_od_1",
5011 : &cls->orders[1]);
5012 :
5013 : /* Deposit */
5014 0 : make_deposit (&cls->instance,
5015 0 : &cls->account,
5016 0 : &cls->orders[0],
5017 0 : &cls->signkey,
5018 : &cls->deposits[0]);
5019 0 : make_deposit (&cls->instance,
5020 0 : &cls->account,
5021 0 : &cls->orders[0],
5022 0 : &cls->signkey,
5023 : &cls->deposits[1]);
5024 0 : make_deposit (&cls->instance,
5025 0 : &cls->account,
5026 0 : &cls->orders[1],
5027 0 : &cls->signkey,
5028 : &cls->deposits[2]);
5029 :
5030 : /* Refund */
5031 0 : make_refund (&cls->deposits[0],
5032 : &cls->refunds[0]);
5033 0 : make_refund (&cls->deposits[2],
5034 : &cls->refunds[1]);
5035 0 : make_refund (&cls->deposits[2],
5036 : &cls->refunds[2]);
5037 0 : GNUNET_assert (GNUNET_OK ==
5038 : TALER_string_to_amount ("EUR:10.00",
5039 : &cls->refunds[1].refund_amount));
5040 0 : cls->refunds[1].reason = "refund 1";
5041 0 : GNUNET_assert (GNUNET_OK ==
5042 : TALER_string_to_amount ("EUR:10.00",
5043 : &cls->refunds[2].refund_amount));
5044 0 : cls->refunds[2].reason = "refund 2";
5045 :
5046 : /* Refund proof */
5047 0 : GNUNET_assert (GNUNET_OK ==
5048 : TALER_string_to_amount ("EUR:0.02",
5049 : &cls->refund_proof.refund_fee));
5050 0 : memset (&cls->refund_proof.exchange_sig,
5051 : 42,
5052 : sizeof (cls->refund_proof.exchange_sig));
5053 0 : }
5054 :
5055 :
5056 : /**
5057 : * Cleans up after testing refunds.
5058 : * @param cls the closure.
5059 : */
5060 : static void
5061 0 : post_test_refunds (struct TestRefunds_Closure *cls)
5062 : {
5063 0 : free_instance_data (&cls->instance);
5064 0 : free_order_data (&cls->orders[0]);
5065 0 : free_order_data (&cls->orders[1]);
5066 0 : }
5067 :
5068 :
5069 : /**
5070 : * Runs the refund tests.
5071 : * @param cls the closure.
5072 : *
5073 : * @return 0 on success, 1 otherwise.
5074 : */
5075 : static int
5076 0 : run_test_refunds (struct TestRefunds_Closure *cls)
5077 : {
5078 : struct TALER_Amount inc;
5079 : uint64_t refund_serial;
5080 :
5081 : /* Insert an instance */
5082 0 : TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
5083 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5084 : /* Insert an account */
5085 0 : TEST_RET_ON_FAIL (test_insert_account (&cls->instance,
5086 : &cls->account,
5087 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5088 : /* Insert an order */
5089 0 : TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
5090 : &cls->orders[0],
5091 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5092 : /* Insert contract terms */
5093 0 : TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
5094 : &cls->orders[0],
5095 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5096 : /* Insert a signing key */
5097 0 : TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey,
5098 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5099 : /* Insert a deposit */
5100 0 : TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
5101 : &cls->signkey,
5102 : &cls->deposits[0],
5103 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5104 0 : TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
5105 : &cls->signkey,
5106 : &cls->deposits[1],
5107 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5108 : /* Mark as paid */
5109 0 : TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
5110 : &cls->orders[0],
5111 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5112 : /* Test refund coin */
5113 0 : TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
5114 : plugin->refund_coin (plugin->cls,
5115 : cls->instance.instance.id,
5116 : &cls->deposits[0].h_contract_terms
5117 : ,
5118 : cls->refunds[0].timestamp,
5119 : cls->refunds[0].coin_pub,
5120 : cls->refunds[0].reason),
5121 : "Refund coin failed\n");
5122 0 : refund_serial = get_refund_serial (&cls->instance,
5123 0 : &cls->deposits[0].h_contract_terms,
5124 0 : &cls->refunds[0]);
5125 : /* Test double refund fails */
5126 0 : TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
5127 : plugin->refund_coin (plugin->cls,
5128 : cls->instance.instance.id,
5129 : &cls->deposits[0].h_contract_terms
5130 : ,
5131 : cls->refunds[0].timestamp,
5132 : cls->refunds[0].coin_pub,
5133 : cls->refunds[0].reason),
5134 : "Refund coin failed\n");
5135 : /* Test lookup refunds */
5136 0 : TEST_RET_ON_FAIL (test_lookup_refunds (&cls->instance,
5137 : &cls->deposits[0].h_contract_terms,
5138 : 1,
5139 : cls->refunds[0].coin_pub,
5140 : &cls->refunds[0].refund_amount));
5141 : /* Test lookup refunds detailed */
5142 0 : TEST_RET_ON_FAIL (test_lookup_refunds_detailed (&cls->instance,
5143 : &cls->deposits[0].
5144 : h_contract_terms,
5145 : true,
5146 : 1,
5147 : &cls->refunds[0]));
5148 : /* Test insert refund proof */
5149 0 : TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
5150 : plugin->insert_refund_proof (plugin->cls,
5151 : refund_serial,
5152 : &cls->refund_proof.
5153 : exchange_sig,
5154 : &cls->signkey.exchange_pub
5155 : ),
5156 : "Insert refund proof failed\n");
5157 0 : TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
5158 : plugin->insert_refund_proof (plugin->cls,
5159 : refund_serial,
5160 : &cls->refund_proof.
5161 : exchange_sig,
5162 : &cls->signkey.exchange_pub
5163 : ),
5164 : "Insert refund proof failed\n");
5165 : /* Test that we can't give too much in refunds */
5166 0 : GNUNET_assert (GNUNET_OK ==
5167 : TALER_string_to_amount ("EUR:1000.00",
5168 : &inc));
5169 0 : TEST_COND_RET_ON_FAIL (TALER_MERCHANTDB_RS_TOO_HIGH ==
5170 : plugin->increase_refund (plugin->cls,
5171 : cls->instance.instance.id,
5172 : cls->orders[0].id,
5173 : &inc,
5174 : NULL,
5175 : NULL,
5176 : "more"),
5177 : "Increase refund failed\n");
5178 : /* Test increase refund */
5179 0 : GNUNET_assert (GNUNET_OK ==
5180 : TALER_string_to_amount ("EUR:1.00",
5181 : &inc));
5182 0 : TEST_COND_RET_ON_FAIL (TALER_MERCHANTDB_RS_SUCCESS ==
5183 : plugin->increase_refund (plugin->cls,
5184 : cls->instance.instance.id,
5185 : cls->orders[0].id,
5186 : &inc,
5187 : NULL,
5188 : NULL,
5189 : "more"),
5190 : "Increase refund failed\n");
5191 : /* Test lookup refund proof */
5192 0 : TEST_RET_ON_FAIL (test_lookup_refund_proof (1,
5193 : &cls->refund_proof.exchange_sig,
5194 : &cls->signkey.exchange_pub));
5195 0 : TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
5196 : &cls->orders[1],
5197 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5198 0 : TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
5199 : &cls->orders[1],
5200 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5201 0 : TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
5202 : &cls->signkey,
5203 : &cls->deposits[2],
5204 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5205 0 : TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
5206 : &cls->orders[1],
5207 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5208 : /* Test refunding a small amount of the coin, then increasing it */
5209 0 : GNUNET_assert (GNUNET_OK ==
5210 : TALER_string_to_amount ("EUR:10.00",
5211 : &inc));
5212 0 : TEST_COND_RET_ON_FAIL (TALER_MERCHANTDB_RS_SUCCESS ==
5213 : plugin->increase_refund (plugin->cls,
5214 : cls->instance.instance.id,
5215 : cls->orders[1].id,
5216 : &inc,
5217 : NULL,
5218 : NULL,
5219 : cls->refunds[1].reason),
5220 : "Increase refund failed\n");
5221 0 : GNUNET_assert (GNUNET_OK ==
5222 : TALER_string_to_amount ("EUR:20.00",
5223 : &inc));
5224 0 : TEST_COND_RET_ON_FAIL (TALER_MERCHANTDB_RS_SUCCESS ==
5225 : plugin->increase_refund (plugin->cls,
5226 : cls->instance.instance.id,
5227 : cls->orders[1].id,
5228 : &inc,
5229 : NULL,
5230 : NULL,
5231 : cls->refunds[2].reason),
5232 : "Increase refund failed\n");
5233 0 : TEST_RET_ON_FAIL (test_lookup_refunds_detailed (&cls->instance,
5234 : &cls->deposits[2].
5235 : h_contract_terms,
5236 : false,
5237 : 2,
5238 : &cls->refunds[1]));
5239 0 : return 0;
5240 : }
5241 :
5242 :
5243 : /**
5244 : * All logic for testing refunds.
5245 : *
5246 : * @return 0 on success, 1 otherwise.
5247 : */
5248 : static int
5249 0 : test_refunds (void)
5250 : {
5251 : struct TestRefunds_Closure test_cls;
5252 : int test_result;
5253 :
5254 0 : pre_test_refunds (&test_cls);
5255 0 : test_result = run_test_refunds (&test_cls);
5256 0 : post_test_refunds (&test_cls);
5257 0 : return test_result;
5258 : }
5259 :
5260 :
5261 : /**
5262 : * Convenience function that reverses an array of orders.
5263 : * @param orders_length length of @e orders.
5264 : * @param orders the array to reverse.
5265 : */
5266 : static void
5267 0 : reverse_order_data_array (unsigned int orders_length,
5268 : struct OrderData *orders)
5269 0 : {
5270 0 : struct OrderData tmp[orders_length];
5271 0 : for (unsigned int i = 0; i < orders_length; ++i)
5272 0 : tmp[i] = orders[orders_length - 1 - i];
5273 0 : for (unsigned int i = 0; i < orders_length; ++i)
5274 0 : orders[i] = tmp[i];
5275 0 : }
5276 :
5277 :
5278 : /**
5279 : * Closure for testing all the filters for looking up orders.
5280 : */
5281 : struct TestLookupOrdersAllFilters_Closure
5282 : {
5283 : /**
5284 : * The instance.
5285 : */
5286 : struct InstanceData instance;
5287 :
5288 : /**
5289 : * The merchant account.
5290 : */
5291 : struct TALER_MERCHANTDB_AccountDetails account;
5292 :
5293 : /**
5294 : * The exchange signing key.
5295 : */
5296 : struct ExchangeSignkeyData signkey;
5297 :
5298 : /**
5299 : * The array of order ids.
5300 : */
5301 : char *order_ids[64];
5302 :
5303 : /**
5304 : * The array of orders.
5305 : */
5306 : struct OrderData orders[64];
5307 :
5308 : /**
5309 : * The array of deposits.
5310 : */
5311 : struct DepositData deposits[64];
5312 :
5313 : /**
5314 : * The array of refunds.
5315 : */
5316 : struct RefundData refunds[64];
5317 : };
5318 :
5319 :
5320 : /**
5321 : * Sets up for testing lookup order filters.
5322 : * @param cls the closure.
5323 : */
5324 : static void
5325 0 : pre_test_lookup_orders_all_filters (
5326 : struct TestLookupOrdersAllFilters_Closure *cls)
5327 : {
5328 0 : make_instance ("test_inst_lookup_orders_all_filters",
5329 : &cls->instance);
5330 0 : make_account (&cls->account);
5331 0 : cls->account.instance_id = cls->instance.instance.id;
5332 0 : make_exchange_signkey (&cls->signkey);
5333 0 : for (unsigned int i = 0; i < 64; ++i)
5334 : {
5335 0 : (void) GNUNET_asprintf (&cls->order_ids[i],
5336 : "test_orders_filter_od_%u",
5337 : i);
5338 0 : make_order (cls->order_ids[i],
5339 : &cls->orders[i]);
5340 0 : GNUNET_assert (0 ==
5341 : json_object_set_new (cls->orders[i].contract,
5342 : "order_id",
5343 : json_string (cls->order_ids[i])));
5344 0 : make_deposit (&cls->instance,
5345 0 : &cls->account,
5346 0 : &cls->orders[i],
5347 0 : &cls->signkey,
5348 : &cls->deposits[i]);
5349 0 : make_refund (&cls->deposits[i],
5350 : &cls->refunds[i]);
5351 : }
5352 0 : }
5353 :
5354 :
5355 : /**
5356 : * Cleans up after testing lookup order filters.
5357 : * @param cls the closure.
5358 : */
5359 : static void
5360 0 : post_test_lookup_orders_all_filters (
5361 : struct TestLookupOrdersAllFilters_Closure *cls)
5362 : {
5363 0 : free_instance_data (&cls->instance);
5364 0 : for (unsigned int i = 0; i < 64; ++i)
5365 : {
5366 0 : free_order_data (&cls->orders[i]);
5367 0 : GNUNET_free (cls->order_ids[i]);
5368 : }
5369 0 : }
5370 :
5371 :
5372 : /**
5373 : * Runs the tests for lookup order filters.
5374 : * @param cls the closure.
5375 : *
5376 : * @return 0 on success, 1 otherwise.
5377 : */
5378 : static int
5379 0 : run_test_lookup_orders_all_filters (
5380 : struct TestLookupOrdersAllFilters_Closure *cls)
5381 : {
5382 : /* Order filter extravaganza */
5383 : struct
5384 : {
5385 : bool claimed;
5386 : bool paid;
5387 : bool refunded;
5388 : bool wired;
5389 : } order_status[64];
5390 : unsigned int *permutation;
5391 :
5392 : /* Pseudorandomly generate variations for the filter to differentiate */
5393 0 : GNUNET_CRYPTO_seed_weak_random (1);
5394 0 : permutation = GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK,
5395 : 64);
5396 0 : for (unsigned int i = 0; i < 64; ++i)
5397 : {
5398 0 : unsigned int dest = permutation[i];
5399 0 : order_status[dest].claimed = (i & 1) ? true : false;
5400 0 : order_status[dest].paid = (3 == (i & 3)) ? true : false;
5401 0 : order_status[dest].refunded = (5 == (i & 5)) ? true : false;
5402 0 : order_status[dest].wired = (9 == (i & 9)) ? true : false;
5403 : }
5404 0 : GNUNET_free (permutation);
5405 :
5406 :
5407 0 : TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
5408 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5409 0 : TEST_RET_ON_FAIL (test_insert_account (&cls->instance,
5410 : &cls->account,
5411 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5412 0 : TEST_RET_ON_FAIL (test_insert_exchange_signkey (&cls->signkey,
5413 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5414 0 : for (unsigned int i = 0; i < 64; ++i)
5415 : {
5416 : uint64_t order_serial;
5417 :
5418 0 : TEST_RET_ON_FAIL (test_insert_order (&cls->instance,
5419 : &cls->orders[i],
5420 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5421 0 : order_serial = get_order_serial (&cls->instance,
5422 0 : &cls->orders[i]);
5423 :
5424 0 : if (order_status[i].claimed)
5425 : {
5426 0 : TEST_RET_ON_FAIL (test_insert_contract_terms (&cls->instance,
5427 : &cls->orders[i],
5428 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5429 : }
5430 : else
5431 : {
5432 0 : continue;
5433 : }
5434 :
5435 0 : if (order_status[i].paid)
5436 0 : TEST_RET_ON_FAIL (test_mark_contract_paid (&cls->instance,
5437 : &cls->orders[i],
5438 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5439 0 : if (order_status[i].refunded)
5440 : {
5441 0 : TEST_RET_ON_FAIL (test_insert_deposit (&cls->instance,
5442 : &cls->signkey,
5443 : &cls->deposits[i],
5444 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5445 0 : TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
5446 : plugin->refund_coin (plugin->cls,
5447 : cls->instance.instance.id,
5448 : &cls->deposits[i].
5449 : h_contract_terms,
5450 : cls->refunds[i].timestamp,
5451 : cls->refunds[i].coin_pub,
5452 : cls->refunds[i].reason),
5453 : "Refund coin failed\n");
5454 : }
5455 :
5456 0 : if (order_status[i].wired)
5457 0 : TEST_RET_ON_FAIL (test_mark_order_wired (order_serial,
5458 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5459 : }
5460 :
5461 : /* There are 3^3 = 27 possibilities here, not counting inc/dec and start row */
5462 0 : for (unsigned int i = 0; i < 27; ++i)
5463 : {
5464 0 : struct TALER_MERCHANTDB_OrderFilter filter = {
5465 0 : .paid = (i % 3) + 1,
5466 0 : .refunded = ((i / 3) % 3) + 1,
5467 0 : .wired = ((i / 9) % 3) + 1,
5468 : .date = GNUNET_TIME_UNIT_ZERO_TS,
5469 : .start_row = 0,
5470 : .delta = 64
5471 : };
5472 0 : unsigned int orders_length = 0;
5473 : struct OrderData orders[64];
5474 :
5475 : /* Now figure out which orders should make it through the filter */
5476 0 : for (unsigned int j = 0; j < 64; ++j)
5477 : {
5478 0 : if (((TALER_EXCHANGE_YNA_YES == filter.paid) &&
5479 0 : (true != order_status[j].paid)) ||
5480 0 : ((TALER_EXCHANGE_YNA_NO == filter.paid) &&
5481 0 : (false != order_status[j].paid)) ||
5482 0 : ((TALER_EXCHANGE_YNA_YES == filter.refunded) &&
5483 0 : (true != order_status[j].refunded)) ||
5484 0 : ((TALER_EXCHANGE_YNA_NO == filter.refunded) &&
5485 0 : (false != order_status[j].refunded)) ||
5486 0 : ((TALER_EXCHANGE_YNA_YES == filter.wired) &&
5487 0 : (true != order_status[j].wired)) ||
5488 0 : ((TALER_EXCHANGE_YNA_NO == filter.wired) &&
5489 0 : (false != order_status[j].wired)))
5490 0 : continue;
5491 0 : orders[orders_length] = cls->orders[j];
5492 0 : orders_length += 1;
5493 : }
5494 :
5495 : /* Test the lookup */
5496 0 : TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
5497 : &filter,
5498 : orders_length,
5499 : orders));
5500 :
5501 : /* Now test decreasing */
5502 0 : filter.start_row = 256;
5503 0 : filter.date = GNUNET_TIME_UNIT_FOREVER_TS;
5504 0 : filter.delta = -64;
5505 :
5506 0 : reverse_order_data_array (orders_length,
5507 : orders);
5508 :
5509 0 : TEST_RET_ON_FAIL (test_lookup_orders (&cls->instance,
5510 : &filter,
5511 : orders_length,
5512 : orders));
5513 : }
5514 :
5515 0 : return 0;
5516 : }
5517 :
5518 :
5519 : /**
5520 : * Handles all logic for testing lookup order filters.
5521 : *
5522 : * @return 0 on success, 1 otherwise.
5523 : */
5524 : static int
5525 0 : test_lookup_orders_all_filters (void)
5526 : {
5527 : struct TestLookupOrdersAllFilters_Closure test_cls;
5528 : int test_result;
5529 :
5530 0 : memset (&test_cls,
5531 : 0,
5532 : sizeof (test_cls));
5533 0 : pre_test_lookup_orders_all_filters (&test_cls);
5534 0 : test_result = run_test_lookup_orders_all_filters (&test_cls);
5535 0 : post_test_lookup_orders_all_filters (&test_cls);
5536 0 : return test_result;
5537 : }
5538 :
5539 :
5540 : static void
5541 0 : kyc_status_ok (
5542 : void *cls,
5543 : const struct TALER_MerchantWireHashP *h_wire,
5544 : struct TALER_FullPayto payto_uri,
5545 : const char *exchange_url,
5546 : struct GNUNET_TIME_Timestamp last_check,
5547 : bool kyc_ok,
5548 : const struct TALER_AccountAccessTokenP *access_token,
5549 : unsigned int last_http_status,
5550 : enum TALER_ErrorCode last_ec,
5551 : bool in_aml_review,
5552 : const json_t *jlimits)
5553 : {
5554 0 : bool *fail = cls;
5555 :
5556 0 : if (kyc_ok)
5557 0 : *fail = false;
5558 0 : }
5559 :
5560 :
5561 : static void
5562 0 : kyc_status_fail (
5563 : void *cls,
5564 : const struct TALER_MerchantWireHashP *h_wire,
5565 : struct TALER_FullPayto payto_uri,
5566 : const char *exchange_url,
5567 : struct GNUNET_TIME_Timestamp last_check,
5568 : bool kyc_ok,
5569 : const struct TALER_AccountAccessTokenP *access_token,
5570 : unsigned int last_http_status,
5571 : enum TALER_ErrorCode last_ec,
5572 : bool in_aml_review,
5573 : const json_t *jlimits)
5574 : {
5575 0 : bool *fail = cls;
5576 :
5577 0 : if (! kyc_ok)
5578 0 : *fail = false;
5579 0 : }
5580 :
5581 :
5582 : /**
5583 : * Function that tests the KYC table.
5584 : *
5585 : * @return 0 on success, 1 otherwise.
5586 : */
5587 : static int
5588 0 : test_kyc (void)
5589 : {
5590 : struct InstanceData instance;
5591 : struct TALER_MERCHANTDB_AccountDetails account;
5592 : bool fail;
5593 : struct GNUNET_TIME_Timestamp now;
5594 :
5595 0 : make_instance ("test_kyc",
5596 : &instance);
5597 0 : make_account (&account);
5598 0 : account.instance_id = instance.instance.id;
5599 0 : TEST_RET_ON_FAIL (test_insert_instance (&instance,
5600 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5601 0 : TEST_RET_ON_FAIL (test_insert_account (&instance,
5602 : &account,
5603 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
5604 0 : now = GNUNET_TIME_timestamp_get ();
5605 0 : TEST_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
5606 : plugin->account_kyc_set_status (plugin->cls,
5607 : instance.instance.id,
5608 : &account.h_wire,
5609 : "https://exchange.net/",
5610 : now,
5611 : MHD_HTTP_OK,
5612 : TALER_EC_NONE,
5613 : 42,
5614 : NULL,
5615 : NULL,
5616 : false,
5617 : false));
5618 0 : TEST_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
5619 : plugin->account_kyc_set_status (plugin->cls,
5620 : instance.instance.id,
5621 : &account.h_wire,
5622 : "https://exchange2.com/",
5623 : now,
5624 : MHD_HTTP_OK,
5625 : TALER_EC_NONE,
5626 : 42,
5627 : NULL,
5628 : NULL,
5629 : false,
5630 : false));
5631 0 : TEST_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
5632 : plugin->account_kyc_set_status (plugin->cls,
5633 : instance.instance.id,
5634 : &account.h_wire,
5635 : "https://exchange.net/",
5636 : now,
5637 : MHD_HTTP_OK,
5638 : TALER_EC_NONE,
5639 : 42,
5640 : NULL,
5641 : NULL,
5642 : false,
5643 : true));
5644 0 : fail = true;
5645 0 : TEST_RET_ON_FAIL (1 !=
5646 : plugin->account_kyc_get_status (plugin->cls,
5647 : instance.instance.id,
5648 : &account.h_wire,
5649 : "https://exchange.net/",
5650 : &kyc_status_ok,
5651 : &fail));
5652 0 : TEST_RET_ON_FAIL (fail);
5653 0 : fail = true;
5654 0 : TEST_RET_ON_FAIL (1 !=
5655 : plugin->account_kyc_get_status (plugin->cls,
5656 : instance.instance.id,
5657 : NULL,
5658 : "https://exchange2.com/",
5659 : &kyc_status_fail,
5660 : &fail));
5661 0 : TEST_RET_ON_FAIL (fail);
5662 0 : fail = true;
5663 0 : TEST_RET_ON_FAIL (2 !=
5664 : plugin->account_kyc_get_status (plugin->cls,
5665 : instance.instance.id,
5666 : NULL,
5667 : NULL,
5668 : &kyc_status_fail,
5669 : &fail));
5670 0 : TEST_RET_ON_FAIL (fail);
5671 0 : fail = true;
5672 0 : TEST_RET_ON_FAIL (2 !=
5673 : plugin->account_kyc_get_status (plugin->cls,
5674 : instance.instance.id,
5675 : NULL,
5676 : NULL,
5677 : &kyc_status_ok,
5678 : &fail));
5679 0 : TEST_RET_ON_FAIL (fail);
5680 0 : json_decref (instance.instance.address);
5681 0 : json_decref (instance.instance.jurisdiction);
5682 0 : return 0;
5683 : }
5684 :
5685 :
5686 : /* *********** Templates ********** */
5687 :
5688 : /**
5689 : * A container for data relevant to a template.
5690 : */
5691 : struct TemplateData
5692 : {
5693 : /**
5694 : * The identifier of the template.
5695 : */
5696 : const char *id;
5697 :
5698 : /**
5699 : * The details of the template.
5700 : */
5701 : struct TALER_MERCHANTDB_TemplateDetails template;
5702 : };
5703 :
5704 :
5705 : /**
5706 : * Creates a template for testing with.
5707 : *
5708 : * @param id the id of the template.
5709 : * @param template the template data to fill.
5710 : */
5711 : static void
5712 0 : make_template (const char *id,
5713 : struct TemplateData *template)
5714 : {
5715 0 : template->id = id;
5716 0 : template->template.template_description = "This is a test template";
5717 0 : template->template.otp_id = NULL;
5718 0 : template->template.template_contract = json_array ();
5719 0 : GNUNET_assert (NULL != template->template.template_contract);
5720 0 : }
5721 :
5722 :
5723 : /**
5724 : * Frees memory associated with @e TemplateData.
5725 : *
5726 : * @param template the container to free.
5727 : */
5728 : static void
5729 0 : free_template_data (struct TemplateData *template)
5730 : {
5731 0 : GNUNET_free (template->template.otp_id);
5732 0 : json_decref (template->template.template_contract);
5733 0 : }
5734 :
5735 :
5736 : /**
5737 : * Compare two templates for equality.
5738 : *
5739 : * @param a the first template.
5740 : * @param b the second template.
5741 : * @return 0 on equality, 1 otherwise.
5742 : */
5743 : static int
5744 0 : check_templates_equal (const struct TALER_MERCHANTDB_TemplateDetails *a,
5745 : const struct TALER_MERCHANTDB_TemplateDetails *b)
5746 : {
5747 0 : if ((0 != strcmp (a->template_description,
5748 0 : b->template_description)) ||
5749 0 : ( (NULL == a->otp_id) && (NULL != b->otp_id)) ||
5750 0 : ( (NULL != a->otp_id) && (NULL == b->otp_id)) ||
5751 0 : ( (NULL != a->otp_id) && (0 != strcmp (a->otp_id,
5752 0 : b->otp_id))) ||
5753 0 : (1 != json_equal (a->template_contract,
5754 0 : b->template_contract)))
5755 0 : return 1;
5756 0 : return 0;
5757 : }
5758 :
5759 :
5760 : /**
5761 : * Tests inserting template data into the database.
5762 : *
5763 : * @param instance the instance to insert the template for.
5764 : * @param template the template data to insert.
5765 : * @param expected_result the result we expect the db to return.
5766 : * @return 0 when successful, 1 otherwise.
5767 : */
5768 : static int
5769 0 : test_insert_template (const struct InstanceData *instance,
5770 : const struct TemplateData *template,
5771 : enum GNUNET_DB_QueryStatus expected_result)
5772 : {
5773 0 : TEST_COND_RET_ON_FAIL (expected_result ==
5774 : plugin->insert_template (plugin->cls,
5775 : instance->instance.id,
5776 : template->id,
5777 : 0,
5778 : &template->template),
5779 : "Insert template failed\n");
5780 0 : return 0;
5781 : }
5782 :
5783 :
5784 : /**
5785 : * Tests updating template data in the database.
5786 : *
5787 : * @param instance the instance to update the template for.
5788 : * @param template the template data to update.
5789 : * @param expected_result the result we expect the db to return.
5790 : * @return 0 when successful, 1 otherwise.
5791 : */
5792 : static int
5793 0 : test_update_template (const struct InstanceData *instance,
5794 : const struct TemplateData *template,
5795 : enum GNUNET_DB_QueryStatus expected_result)
5796 : {
5797 0 : TEST_COND_RET_ON_FAIL (expected_result ==
5798 : plugin->update_template (plugin->cls,
5799 : instance->instance.id,
5800 : template->id,
5801 : &template->template),
5802 : "Update template failed\n");
5803 0 : return 0;
5804 : }
5805 :
5806 :
5807 : /**
5808 : * Tests looking up a template from the db.
5809 : *
5810 : * @param instance the instance to query from.
5811 : * @param template the template to query and compare to.
5812 : * @return 0 when successful, 1 otherwise.
5813 : */
5814 : static int
5815 0 : test_lookup_template (const struct InstanceData *instance,
5816 : const struct TemplateData *template)
5817 : {
5818 0 : const struct TALER_MERCHANTDB_TemplateDetails *to_cmp
5819 : = &template->template;
5820 : struct TALER_MERCHANTDB_TemplateDetails lookup_result;
5821 :
5822 0 : if (0 > plugin->lookup_template (plugin->cls,
5823 0 : instance->instance.id,
5824 0 : template->id,
5825 : &lookup_result))
5826 : {
5827 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
5828 : "Lookup template failed\n");
5829 0 : TALER_MERCHANTDB_template_details_free (&lookup_result);
5830 0 : return 1;
5831 : }
5832 0 : if (0 != check_templates_equal (&lookup_result,
5833 : to_cmp))
5834 : {
5835 0 : GNUNET_break (0);
5836 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
5837 : "Lookup template failed: incorrect template returned\n");
5838 0 : TALER_MERCHANTDB_template_details_free (&lookup_result);
5839 0 : return 1;
5840 : }
5841 0 : TALER_MERCHANTDB_template_details_free (&lookup_result);
5842 0 : return 0;
5843 : }
5844 :
5845 :
5846 : /**
5847 : * Closure for testing template lookup
5848 : */
5849 : struct TestLookupTemplates_Closure
5850 : {
5851 : /**
5852 : * Number of template ids to compare to
5853 : */
5854 : unsigned int templates_to_cmp_length;
5855 :
5856 : /**
5857 : * Pointer to array of template ids
5858 : */
5859 : const struct TemplateData *templates_to_cmp;
5860 :
5861 : /**
5862 : * Pointer to array of number of matches for each template
5863 : */
5864 : unsigned int *results_matching;
5865 :
5866 : /**
5867 : * Total number of results returned
5868 : */
5869 : unsigned int results_length;
5870 : };
5871 :
5872 :
5873 : /**
5874 : * Function called after calling @e test_lookup_templates
5875 : *
5876 : * @param cls a pointer to the lookup closure.
5877 : * @param template_id the identifier of the template found.
5878 : */
5879 : static void
5880 0 : lookup_templates_cb (void *cls,
5881 : const char *template_id,
5882 : const char *template_description)
5883 : {
5884 0 : struct TestLookupTemplates_Closure *cmp = cls;
5885 :
5886 0 : if (NULL == cmp)
5887 0 : return;
5888 0 : cmp->results_length += 1;
5889 0 : for (unsigned int i = 0; cmp->templates_to_cmp_length > i; ++i)
5890 : {
5891 0 : if ( (0 == strcmp (cmp->templates_to_cmp[i].id,
5892 0 : template_id)) &&
5893 0 : (0 == strcmp (cmp->templates_to_cmp[i].template.template_description,
5894 : template_description)) )
5895 0 : cmp->results_matching[i] += 1;
5896 : }
5897 : }
5898 :
5899 :
5900 : /**
5901 : * Tests looking up all templates for an instance.
5902 : *
5903 : * @param instance the instance to query from.
5904 : * @param templates_length the number of templates we are expecting.
5905 : * @param templates the list of templates that we expect to be found.
5906 : * @return 0 when successful, 1 otherwise.
5907 : */
5908 : static int
5909 0 : test_lookup_templates (const struct InstanceData *instance,
5910 : unsigned int templates_length,
5911 : const struct TemplateData *templates)
5912 0 : {
5913 0 : unsigned int results_matching[templates_length];
5914 0 : struct TestLookupTemplates_Closure cls = {
5915 : .templates_to_cmp_length = templates_length,
5916 : .templates_to_cmp = templates,
5917 : .results_matching = results_matching,
5918 : .results_length = 0
5919 : };
5920 :
5921 0 : memset (results_matching,
5922 : 0,
5923 : sizeof (unsigned int) * templates_length);
5924 0 : if (0 > plugin->lookup_templates (plugin->cls,
5925 0 : instance->instance.id,
5926 : &lookup_templates_cb,
5927 : &cls))
5928 : {
5929 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
5930 : "Lookup templates failed\n");
5931 0 : return 1;
5932 : }
5933 0 : if (templates_length != cls.results_length)
5934 : {
5935 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
5936 : "Lookup templates failed: incorrect number of results\n");
5937 0 : return 1;
5938 : }
5939 0 : for (unsigned int i = 0; templates_length > i; ++i)
5940 : {
5941 0 : if (1 != cls.results_matching[i])
5942 : {
5943 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
5944 : "Lookup templates failed: mismatched data\n");
5945 0 : return 1;
5946 : }
5947 : }
5948 0 : return 0;
5949 : }
5950 :
5951 :
5952 : /**
5953 : * Tests deleting a template.
5954 : *
5955 : * @param instance the instance to delete the template from.
5956 : * @param template the template that should be deleted.
5957 : * @param expected_result the result that we expect the plugin to return.
5958 : * @return 0 when successful, 1 otherwise.
5959 : */
5960 : static int
5961 0 : test_delete_template (const struct InstanceData *instance,
5962 : const struct TemplateData *template,
5963 : enum GNUNET_DB_QueryStatus expected_result)
5964 : {
5965 0 : TEST_COND_RET_ON_FAIL (expected_result ==
5966 : plugin->delete_template (plugin->cls,
5967 : instance->instance.id,
5968 : template->id),
5969 : "Delete template failed\n");
5970 0 : return 0;
5971 : }
5972 :
5973 :
5974 : /**
5975 : * Closure for template tests.
5976 : */
5977 : struct TestTemplates_Closure
5978 : {
5979 : /**
5980 : * The instance to use for this test.
5981 : */
5982 : struct InstanceData instance;
5983 :
5984 : /**
5985 : * The array of templates.
5986 : */
5987 : struct TemplateData templates[2];
5988 : };
5989 :
5990 :
5991 : /**
5992 : * Sets up the data structures used in the template tests.
5993 : *
5994 : * @param cls the closure to fill with test data.
5995 : */
5996 : static void
5997 0 : pre_test_templates (struct TestTemplates_Closure *cls)
5998 : {
5999 : /* Instance */
6000 0 : make_instance ("test_inst_templates",
6001 : &cls->instance);
6002 :
6003 : /* Templates */
6004 0 : make_template ("test_templates_pd_0",
6005 : &cls->templates[0]);
6006 :
6007 0 : make_template ("test_templates_pd_1",
6008 : &cls->templates[1]);
6009 0 : cls->templates[1].template.template_description =
6010 : "This is a another test template";
6011 0 : }
6012 :
6013 :
6014 : /**
6015 : * Handles all teardown after testing.
6016 : *
6017 : * @param cls the closure containing memory to be freed.
6018 : */
6019 : static void
6020 0 : post_test_templates (struct TestTemplates_Closure *cls)
6021 : {
6022 0 : free_instance_data (&cls->instance);
6023 0 : free_template_data (&cls->templates[0]);
6024 0 : free_template_data (&cls->templates[1]);
6025 0 : }
6026 :
6027 :
6028 : /**
6029 : * Runs the tests for templates.
6030 : *
6031 : * @param cls the container of the test data.
6032 : * @return 0 on success, 1 otherwise.
6033 : */
6034 : static int
6035 0 : run_test_templates (struct TestTemplates_Closure *cls)
6036 : {
6037 :
6038 : /* Test that insert without an instance fails */
6039 0 : TEST_RET_ON_FAIL (test_insert_template (&cls->instance,
6040 : &cls->templates[0],
6041 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
6042 : /* Insert the instance */
6043 0 : TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
6044 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
6045 : /* Test inserting a template */
6046 0 : TEST_RET_ON_FAIL (test_insert_template (&cls->instance,
6047 : &cls->templates[0],
6048 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
6049 : /* Test that double insert fails */
6050 0 : TEST_RET_ON_FAIL (test_insert_template (&cls->instance,
6051 : &cls->templates[0],
6052 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
6053 : /* Test lookup of individual templates */
6054 0 : TEST_RET_ON_FAIL (test_lookup_template (&cls->instance,
6055 : &cls->templates[0]));
6056 : /* Make sure it fails correctly for templates that don't exist */
6057 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
6058 0 : plugin->lookup_template (plugin->cls,
6059 0 : cls->instance.instance.id,
6060 : "nonexistent_template",
6061 : NULL))
6062 : {
6063 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6064 : "Lookup template failed\n");
6065 0 : return 1;
6066 : }
6067 : /* Test template update */
6068 0 : cls->templates[0].template.template_description =
6069 : "This is a test template that has been updated!";
6070 0 : GNUNET_free (cls->templates[0].template.otp_id);
6071 0 : cls->templates[0].template.otp_id = GNUNET_strdup ("otp_id");
6072 : {
6073 : /* ensure OTP device exists */
6074 0 : struct TALER_MERCHANTDB_OtpDeviceDetails td = {
6075 : .otp_description = "my otp",
6076 : .otp_key = "my key",
6077 : .otp_algorithm = 1,
6078 : .otp_ctr = 42
6079 : };
6080 0 : GNUNET_assert (0 <=
6081 : plugin->insert_otp (plugin->cls,
6082 : cls->instance.instance.id,
6083 : "otp_id",
6084 : &td));
6085 : }
6086 0 : GNUNET_assert (0 ==
6087 : json_array_append_new (
6088 : cls->templates[0].template.template_contract,
6089 : json_string ("This is a test. 3CH.")));
6090 0 : TEST_RET_ON_FAIL (test_update_template (&cls->instance,
6091 : &cls->templates[0],
6092 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
6093 0 : TEST_RET_ON_FAIL (test_lookup_template (&cls->instance,
6094 : &cls->templates[0]));
6095 0 : TEST_RET_ON_FAIL (test_update_template (&cls->instance,
6096 : &cls->templates[1],
6097 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
6098 : /* Test collective template lookup */
6099 0 : TEST_RET_ON_FAIL (test_insert_template (&cls->instance,
6100 : &cls->templates[1],
6101 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
6102 0 : TEST_RET_ON_FAIL (test_lookup_templates (&cls->instance,
6103 : 2,
6104 : cls->templates));
6105 :
6106 : /* Test template deletion */
6107 0 : TEST_RET_ON_FAIL (test_delete_template (&cls->instance,
6108 : &cls->templates[1],
6109 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
6110 : /* Test double deletion fails */
6111 0 : TEST_RET_ON_FAIL (test_delete_template (&cls->instance,
6112 : &cls->templates[1],
6113 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
6114 0 : TEST_RET_ON_FAIL (test_lookup_templates (&cls->instance,
6115 : 1,
6116 : cls->templates));
6117 0 : return 0;
6118 : }
6119 :
6120 :
6121 : /**
6122 : * Takes care of template testing.
6123 : *
6124 : * @return 0 on success, 1 otherwise.
6125 : */
6126 : static int
6127 0 : test_templates (void)
6128 : {
6129 : struct TestTemplates_Closure test_cls;
6130 : int test_result;
6131 :
6132 0 : memset (&test_cls,
6133 : 0,
6134 : sizeof (test_cls));
6135 0 : pre_test_templates (&test_cls);
6136 0 : test_result = run_test_templates (&test_cls);
6137 0 : post_test_templates (&test_cls);
6138 0 : return test_result;
6139 : }
6140 :
6141 :
6142 : /* *********** Webhooks ********** */
6143 :
6144 : /**
6145 : * A container for data relevant to a webhook.
6146 : */
6147 : struct WebhookData
6148 : {
6149 :
6150 : /**
6151 : * The identifier of the webhook.
6152 : */
6153 : const char *id;
6154 :
6155 : /**
6156 : * The details of the webhook.
6157 : */
6158 : struct TALER_MERCHANTDB_WebhookDetails webhook;
6159 : };
6160 :
6161 :
6162 : /**
6163 : * Creates a webhook for testing with.
6164 : *
6165 : * @param id the id of the webhook.
6166 : * @param webhook the webhook data to fill.
6167 : */
6168 : static void
6169 0 : make_webhook (const char *id,
6170 : struct WebhookData *webhook)
6171 : {
6172 0 : webhook->id = id;
6173 0 : webhook->webhook.event_type = "Paid";
6174 0 : webhook->webhook.url = "https://exampletest.com";
6175 0 : webhook->webhook.http_method = "POST";
6176 0 : webhook->webhook.header_template = "Authorization:XYJAORKJEO";
6177 0 : webhook->webhook.body_template = "$Amount";
6178 0 : }
6179 :
6180 :
6181 : /**
6182 : * Compare two webhooks for equality.
6183 : *
6184 : * @param a the first webhook.
6185 : * @param b the second webhook.
6186 : * @return 0 on equality, 1 otherwise.
6187 : */
6188 : static int
6189 0 : check_webhooks_equal (const struct TALER_MERCHANTDB_WebhookDetails *a,
6190 : const struct TALER_MERCHANTDB_WebhookDetails *b)
6191 : {
6192 0 : if ((0 != strcmp (a->event_type,
6193 0 : b->event_type)) ||
6194 0 : (0 != strcmp (a->url,
6195 0 : b->url)) ||
6196 0 : (0 != strcmp (a->http_method,
6197 0 : b->http_method)) ||
6198 0 : (0 != strcmp (a->header_template,
6199 0 : b->header_template)) ||
6200 0 : (0 != strcmp (a->body_template,
6201 0 : b->body_template)))
6202 0 : return 1;
6203 0 : return 0;
6204 : }
6205 :
6206 :
6207 : /**
6208 : * Tests inserting webhook data into the database.
6209 : *
6210 : * @param instance the instance to insert the webhook for.
6211 : * @param webhook the webhook data to insert.
6212 : * @param expected_result the result we expect the db to return.
6213 : * @return 0 when successful, 1 otherwise.
6214 : */
6215 : static int
6216 0 : test_insert_webhook (const struct InstanceData *instance,
6217 : const struct WebhookData *webhook,
6218 : enum GNUNET_DB_QueryStatus expected_result)
6219 : {
6220 0 : TEST_COND_RET_ON_FAIL (expected_result ==
6221 : plugin->insert_webhook (plugin->cls,
6222 : instance->instance.id,
6223 : webhook->id,
6224 : &webhook->webhook),
6225 : "Insert webhook failed\n");
6226 0 : return 0;
6227 : }
6228 :
6229 :
6230 : /**
6231 : * Tests updating webhook data in the database.
6232 : *
6233 : * @param instance the instance to update the webhook for.
6234 : * @param webhook the webhook data to update.
6235 : * @param expected_result the result we expect the db to return.
6236 : * @return 0 when successful, 1 otherwise.
6237 : */
6238 : static int
6239 0 : test_update_webhook (const struct InstanceData *instance,
6240 : const struct WebhookData *webhook,
6241 : enum GNUNET_DB_QueryStatus expected_result)
6242 : {
6243 0 : TEST_COND_RET_ON_FAIL (expected_result ==
6244 : plugin->update_webhook (plugin->cls,
6245 : instance->instance.id,
6246 : webhook->id,
6247 : &webhook->webhook),
6248 : "Update webhook failed\n");
6249 0 : return 0;
6250 : }
6251 :
6252 :
6253 : /**
6254 : * Tests looking up a webhook from the db.
6255 : *
6256 : * @param instance the instance to query from.
6257 : * @param webhook the webhook to query and compare to.
6258 : * @return 0 when successful, 1 otherwise.
6259 : */
6260 : static int
6261 0 : test_lookup_webhook (const struct InstanceData *instance,
6262 : const struct WebhookData *webhook)
6263 : {
6264 0 : const struct TALER_MERCHANTDB_WebhookDetails *to_cmp = &webhook->webhook;
6265 : struct TALER_MERCHANTDB_WebhookDetails lookup_result;
6266 :
6267 0 : if (0 > plugin->lookup_webhook (plugin->cls,
6268 0 : instance->instance.id,
6269 0 : webhook->id,
6270 : &lookup_result))
6271 : {
6272 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6273 : "Lookup webhook failed\n");
6274 0 : TALER_MERCHANTDB_webhook_details_free (&lookup_result);
6275 0 : return 1;
6276 : }
6277 0 : if (0 != check_webhooks_equal (&lookup_result,
6278 : to_cmp))
6279 : {
6280 0 : GNUNET_break (0);
6281 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6282 : "Lookup webhook failed: incorrect webhook returned\n");
6283 0 : TALER_MERCHANTDB_webhook_details_free (&lookup_result);
6284 0 : return 1;
6285 : }
6286 0 : TALER_MERCHANTDB_webhook_details_free (&lookup_result);
6287 0 : return 0;
6288 : }
6289 :
6290 :
6291 : /**
6292 : * Closure for testing webhook lookup
6293 : */
6294 : struct TestLookupWebhooks_Closure
6295 : {
6296 : /**
6297 : * Number of webhook ids to compare to
6298 : */
6299 : unsigned int webhooks_to_cmp_length;
6300 :
6301 : /**
6302 : * Pointer to array of webhook ids
6303 : */
6304 : const struct WebhookData *webhooks_to_cmp;
6305 :
6306 : /**
6307 : * Pointer to array of number of matches for each webhook
6308 : */
6309 : unsigned int *results_matching;
6310 :
6311 : /**
6312 : * Total number of results returned
6313 : */
6314 : unsigned int results_length;
6315 : };
6316 :
6317 :
6318 : /**
6319 : * Function called after calling @e test_lookup_webhooks
6320 : *
6321 : * @param cls a pointer to the lookup closure.
6322 : * @param webhook_id the identifier of the webhook found.
6323 : */
6324 : static void
6325 0 : lookup_webhooks_cb (void *cls,
6326 : const char *webhook_id,
6327 : const char *event_type)
6328 : {
6329 0 : struct TestLookupWebhooks_Closure *cmp = cls;
6330 0 : if (NULL == cmp)
6331 0 : return;
6332 0 : cmp->results_length += 1;
6333 0 : for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i)
6334 : {
6335 0 : if ((0 == strcmp (cmp->webhooks_to_cmp[i].id,
6336 0 : webhook_id)) &&
6337 0 : (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.event_type,
6338 : event_type)) )
6339 0 : cmp->results_matching[i] += 1;
6340 : }
6341 : }
6342 :
6343 :
6344 : /**
6345 : * Tests looking up all webhooks for an instance.
6346 : *
6347 : * @param instance the instance to query from.
6348 : * @param webhooks_length the number of webhooks we are expecting.
6349 : * @param webhooks the list of webhooks that we expect to be found.
6350 : * @return 0 when successful, 1 otherwise.
6351 : */
6352 : static int
6353 0 : test_lookup_webhooks (const struct InstanceData *instance,
6354 : unsigned int webhooks_length,
6355 : const struct WebhookData *webhooks)
6356 0 : {
6357 0 : unsigned int results_matching[webhooks_length];
6358 0 : struct TestLookupWebhooks_Closure cls = {
6359 : .webhooks_to_cmp_length = webhooks_length,
6360 : .webhooks_to_cmp = webhooks,
6361 : .results_matching = results_matching,
6362 : .results_length = 0
6363 : };
6364 0 : memset (results_matching, 0, sizeof (unsigned int) * webhooks_length);
6365 0 : if (0 > plugin->lookup_webhooks (plugin->cls,
6366 0 : instance->instance.id,
6367 : &lookup_webhooks_cb,
6368 : &cls))
6369 : {
6370 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6371 : "Lookup webhooks failed\n");
6372 0 : return 1;
6373 : }
6374 0 : if (webhooks_length != cls.results_length)
6375 : {
6376 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6377 : "Lookup webhooks failed: incorrect number of results\n");
6378 0 : return 1;
6379 : }
6380 0 : for (unsigned int i = 0; webhooks_length > i; ++i)
6381 : {
6382 0 : if (1 != cls.results_matching[i])
6383 : {
6384 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6385 : "Lookup webhooks failed: mismatched data\n");
6386 0 : return 1;
6387 : }
6388 : }
6389 0 : return 0;
6390 : }
6391 :
6392 :
6393 : /**
6394 : * Function called after calling @e test_lookup_webhooks
6395 : *
6396 : * @param cls a pointer to the lookup closure.
6397 : * @param webhook_id the identifier of the webhook found.
6398 : */
6399 : static void
6400 0 : lookup_webhook_by_event_cb (void *cls,
6401 : uint64_t webhook_serial,
6402 : const char *event_type,
6403 : const char *url,
6404 : const char *http_method,
6405 : const char *header_template,
6406 : const char *body_template)
6407 : {
6408 0 : struct TestLookupWebhooks_Closure *cmp = cls;
6409 0 : if (NULL == cmp)
6410 0 : return;
6411 0 : cmp->results_length += 1;
6412 0 : for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i)
6413 : {
6414 0 : if ((0 == strcmp (cmp->webhooks_to_cmp[i].webhook.event_type,
6415 0 : event_type)) &&
6416 0 : (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.url,
6417 0 : url)) &&
6418 0 : (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.http_method,
6419 0 : http_method)) &&
6420 0 : (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.header_template,
6421 0 : header_template)) &&
6422 0 : (0 == strcmp (cmp->webhooks_to_cmp[i].webhook.body_template,
6423 : body_template)) )
6424 0 : cmp->results_matching[i] += 1;
6425 : }
6426 : }
6427 :
6428 :
6429 : /**
6430 : * Tests looking up webhooks by event for an instance.
6431 : *
6432 : * @param instance the instance to query from.
6433 : * @param webhooks_length the number of webhooks we are expecting.
6434 : * @param webhooks the list of webhooks that we expect to be found.
6435 : * @return 0 when successful, 1 otherwise.
6436 : */
6437 : static int
6438 0 : test_lookup_webhook_by_event (const struct InstanceData *instance,
6439 : unsigned int webhooks_length,
6440 : const struct WebhookData *webhooks)
6441 0 : {
6442 0 : unsigned int results_matching[webhooks_length];
6443 0 : struct TestLookupWebhooks_Closure cls = {
6444 : .webhooks_to_cmp_length = webhooks_length,
6445 : .webhooks_to_cmp = webhooks,
6446 : .results_matching = results_matching,
6447 : .results_length = 0
6448 : };
6449 0 : memset (results_matching, 0, sizeof (unsigned int) * webhooks_length);
6450 0 : if (0 > plugin->lookup_webhook_by_event (plugin->cls,
6451 0 : instance->instance.id,
6452 0 : webhooks->webhook.event_type,
6453 : &lookup_webhook_by_event_cb,
6454 : &cls))
6455 : {
6456 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6457 : "Lookup webhooks by event failed\n");
6458 0 : return 1;
6459 : }
6460 :
6461 0 : if (webhooks_length != cls.results_length)
6462 : {
6463 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6464 : "Lookup webhooks by event failed: incorrect number of results\n");
6465 0 : return 1;
6466 : }
6467 0 : for (unsigned int i = 0; webhooks_length > i; ++i)
6468 : {
6469 0 : if (1 != cls.results_matching[i])
6470 : {
6471 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6472 : "Lookup webhooks by event failed: mismatched data\n");
6473 0 : return 1;
6474 : }
6475 : }
6476 0 : return 0;
6477 : }
6478 :
6479 :
6480 : /**
6481 : * Tests deleting a webhook.
6482 : *
6483 : * @param instance the instance to delete the webhook from.
6484 : * @param webhook the webhook that should be deleted.
6485 : * @param expected_result the result that we expect the plugin to return.
6486 : * @return 0 when successful, 1 otherwise.
6487 : */
6488 : static int
6489 0 : test_delete_webhook (const struct InstanceData *instance,
6490 : const struct WebhookData *webhook,
6491 : enum GNUNET_DB_QueryStatus expected_result)
6492 : {
6493 0 : TEST_COND_RET_ON_FAIL (expected_result ==
6494 : plugin->delete_webhook (plugin->cls,
6495 : instance->instance.id,
6496 : webhook->id),
6497 : "Delete webhook failed\n");
6498 0 : return 0;
6499 : }
6500 :
6501 :
6502 : /**
6503 : * Closure for webhook tests.
6504 : */
6505 : struct TestWebhooks_Closure
6506 : {
6507 : /**
6508 : * The instance to use for this test.
6509 : */
6510 : struct InstanceData instance;
6511 :
6512 : /**
6513 : * The array of webhooks.
6514 : */
6515 : struct WebhookData webhooks[3];
6516 : };
6517 :
6518 :
6519 : /**
6520 : * Sets up the data structures used in the webhook tests.
6521 : *
6522 : * @param cls the closure to fill with test data.
6523 : */
6524 : static void
6525 0 : pre_test_webhooks (struct TestWebhooks_Closure *cls)
6526 : {
6527 : /* Instance */
6528 0 : make_instance ("test_inst_webhooks",
6529 : &cls->instance);
6530 :
6531 : /* Webhooks */
6532 0 : make_webhook ("test_webhooks_wb_0",
6533 : &cls->webhooks[0]);
6534 :
6535 0 : make_webhook ("test_webhooks_wb_1",
6536 : &cls->webhooks[1]);
6537 0 : cls->webhooks[1].webhook.event_type = "Test paid";
6538 0 : cls->webhooks[1].webhook.url = "https://example.com";
6539 0 : cls->webhooks[1].webhook.http_method = "POST";
6540 0 : cls->webhooks[1].webhook.header_template = "Authorization:1XYJAOR493O";
6541 0 : cls->webhooks[1].webhook.body_template = "$Amount";
6542 :
6543 0 : make_webhook ("test_webhooks_wb_2",
6544 : &cls->webhooks[2]);
6545 0 : cls->webhooks[2].webhook.event_type = "Test paid";
6546 0 : cls->webhooks[2].webhook.url = "https://examplerefund.com";
6547 0 : cls->webhooks[2].webhook.http_method = "POST";
6548 0 : cls->webhooks[2].webhook.header_template = "Authorization:XY6ORK52JEO";
6549 0 : cls->webhooks[2].webhook.body_template = "$Amount";
6550 0 : }
6551 :
6552 :
6553 : /**
6554 : * Handles all teardown after testing.
6555 : *
6556 : * @param cls the closure containing memory to be freed.
6557 : */
6558 : static void
6559 0 : post_test_webhooks (struct TestWebhooks_Closure *cls)
6560 : {
6561 0 : free_instance_data (&cls->instance);
6562 0 : }
6563 :
6564 :
6565 : /**
6566 : * Runs the tests for webhooks.
6567 : *
6568 : * @param cls the container of the test data.
6569 : * @return 0 on success, 1 otherwise.
6570 : */
6571 : static int
6572 0 : run_test_webhooks (struct TestWebhooks_Closure *cls)
6573 : {
6574 :
6575 : /* Test that insert without an instance fails */
6576 0 : TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
6577 : &cls->webhooks[0],
6578 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
6579 : /* Insert the instance */
6580 0 : TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
6581 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
6582 : /* Test inserting a webhook */
6583 0 : TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
6584 : &cls->webhooks[0],
6585 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
6586 : /* Test that double insert fails */
6587 0 : TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
6588 : &cls->webhooks[0],
6589 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
6590 : /* Test lookup of individual webhooks */
6591 0 : TEST_RET_ON_FAIL (test_lookup_webhook (&cls->instance,
6592 : &cls->webhooks[0]));
6593 : /* Make sure it fails correctly for webhooks that don't exist */
6594 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
6595 0 : plugin->lookup_webhook (plugin->cls,
6596 0 : cls->instance.instance.id,
6597 : "nonexistent_webhook",
6598 : NULL))
6599 : {
6600 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6601 : "Lookup webhook failed\n");
6602 0 : return 1;
6603 : }
6604 : /* Test webhook update */
6605 0 : cls->webhooks[0].webhook.event_type =
6606 : "Test paid";
6607 0 : cls->webhooks[0].webhook.url =
6608 : "example.com";
6609 0 : cls->webhooks[0].webhook.http_method =
6610 : "POST";
6611 0 : cls->webhooks[0].webhook.header_template =
6612 : "Authorization:WEKFOEKEXZ";
6613 0 : cls->webhooks[0].webhook.body_template =
6614 : "$Amount";
6615 0 : TEST_RET_ON_FAIL (test_update_webhook (&cls->instance,
6616 : &cls->webhooks[0],
6617 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
6618 :
6619 0 : TEST_RET_ON_FAIL (test_lookup_webhook (&cls->instance,
6620 : &cls->webhooks[0]));
6621 0 : TEST_RET_ON_FAIL (test_update_webhook (&cls->instance,
6622 : &cls->webhooks[1],
6623 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
6624 : /* Test collective webhook lookup */
6625 0 : TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
6626 : &cls->webhooks[1],
6627 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
6628 0 : TEST_RET_ON_FAIL (test_lookup_webhooks (&cls->instance,
6629 : 2,
6630 : cls->webhooks));
6631 0 : TEST_RET_ON_FAIL (test_lookup_webhook_by_event (&cls->instance,
6632 : 2,
6633 : cls->webhooks));
6634 0 : TEST_RET_ON_FAIL (test_insert_webhook (&cls->instance,
6635 : &cls->webhooks[2],
6636 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
6637 :
6638 : /* Test webhook deletion */
6639 0 : TEST_RET_ON_FAIL (test_delete_webhook (&cls->instance,
6640 : &cls->webhooks[1],
6641 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
6642 : /* Test double deletion fails */
6643 0 : TEST_RET_ON_FAIL (test_delete_webhook (&cls->instance,
6644 : &cls->webhooks[1],
6645 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
6646 0 : cls->webhooks[1] = cls->webhooks[2];
6647 0 : TEST_RET_ON_FAIL (test_lookup_webhooks (&cls->instance,
6648 : 2,
6649 : cls->webhooks));
6650 0 : TEST_RET_ON_FAIL (test_lookup_webhook_by_event (&cls->instance,
6651 : 2,
6652 : cls->webhooks));
6653 0 : return 0;
6654 : }
6655 :
6656 :
6657 : /**
6658 : * Takes care of webhook testing.
6659 : *
6660 : * @return 0 on success, 1 otherwise.
6661 : */
6662 : static int
6663 0 : test_webhooks (void)
6664 : {
6665 : struct TestWebhooks_Closure test_cls;
6666 : int test_result;
6667 :
6668 0 : pre_test_webhooks (&test_cls);
6669 0 : test_result = run_test_webhooks (&test_cls);
6670 0 : post_test_webhooks (&test_cls);
6671 0 : return test_result;
6672 : }
6673 :
6674 :
6675 : /* *********** Pending Webhooks ********** */
6676 :
6677 : /**
6678 : * A container for data relevant to a pending webhook.
6679 : */
6680 : struct PendingWebhookData
6681 : {
6682 : /**
6683 : * Reference to the configured webhook template.
6684 : */
6685 : uint64_t webhook_serial;
6686 :
6687 : /**
6688 : * The details of the pending webhook.
6689 : */
6690 : struct TALER_MERCHANTDB_PendingWebhookDetails pwebhook;
6691 : };
6692 :
6693 :
6694 : /**
6695 : * Creates a pending webhook for testing with.
6696 : *
6697 : * @param serial reference to the configured webhook template.
6698 : * @param pwebhook the pending webhook data to fill.
6699 : */
6700 : static void
6701 0 : make_pending_webhook (uint64_t webhook_serial,
6702 : struct PendingWebhookData *pwebhook)
6703 : {
6704 0 : pwebhook->webhook_serial = webhook_serial;
6705 0 : pwebhook->pwebhook.next_attempt = GNUNET_TIME_UNIT_ZERO_ABS;
6706 0 : pwebhook->pwebhook.retries = 0;
6707 0 : pwebhook->pwebhook.url = "https://exampletest.com";
6708 0 : pwebhook->pwebhook.http_method = "POST";
6709 0 : pwebhook->pwebhook.header = "Authorization:XYJAORKJEO";
6710 0 : pwebhook->pwebhook.body = "$Amount";
6711 0 : }
6712 :
6713 :
6714 : /**
6715 : * Tests inserting pending webhook data into the database.
6716 : *
6717 : * @param instance the instance to insert the pending webhook for.
6718 : * @param pending webhook the pending webhook data to insert.
6719 : * @param expected_result the result we expect the db to return.
6720 : * @return 0 when successful, 1 otherwise.
6721 : */
6722 : static int
6723 0 : test_insert_pending_webhook (const struct InstanceData *instance,
6724 : struct PendingWebhookData *pwebhook,
6725 : enum GNUNET_DB_QueryStatus expected_result)
6726 : {
6727 :
6728 0 : TEST_COND_RET_ON_FAIL (expected_result ==
6729 : plugin->insert_pending_webhook (plugin->cls,
6730 : instance->instance.id,
6731 : pwebhook->
6732 : webhook_serial,
6733 : pwebhook->pwebhook.url,
6734 : pwebhook->pwebhook.
6735 : http_method,
6736 : pwebhook->pwebhook.
6737 : header,
6738 : pwebhook->pwebhook.body
6739 : ),
6740 : "Insert pending webhook failed\n");
6741 0 : return 0;
6742 : }
6743 :
6744 :
6745 : /**
6746 : * Tests updating pending webhook data in the database.
6747 : *
6748 : * @param instance the instance to update the pending webhook for.
6749 : * @param pending webhook the pending webhook data to update.
6750 : * @param expected_result the result we expect the db to return.
6751 : * @return 0 when successful, 1 otherwise.
6752 : */
6753 : static int
6754 0 : test_update_pending_webhook (const struct InstanceData *instance,
6755 : struct PendingWebhookData *pwebhook,
6756 : enum GNUNET_DB_QueryStatus expected_result)
6757 : {
6758 0 : pwebhook->pwebhook.next_attempt = GNUNET_TIME_relative_to_absolute (
6759 : GNUNET_TIME_UNIT_HOURS);
6760 0 : pwebhook->pwebhook.retries++;
6761 0 : TEST_COND_RET_ON_FAIL (expected_result ==
6762 : plugin->update_pending_webhook (plugin->cls,
6763 : pwebhook->
6764 : webhook_serial,
6765 : pwebhook->pwebhook.
6766 : next_attempt),
6767 : "Update pending webhook failed\n");
6768 0 : return 0;
6769 : }
6770 :
6771 :
6772 : /**
6773 : * Container for information for looking up the row number of a deposit.
6774 : */
6775 : struct LookupPendingWebhookSerial_Closure
6776 : {
6777 : /**
6778 : * The pending webhook we're looking for.
6779 : */
6780 : const struct PendingWebhookData *pwebhook;
6781 :
6782 : /**
6783 : * The serial found.
6784 : */
6785 : uint64_t webhook_pending_serial;
6786 : };
6787 :
6788 :
6789 : /**
6790 : * Function called after calling @e test_lookup_all_webhook,
6791 : * test_lookup_future_webhook and test_lookup_pending_webhook
6792 : *
6793 : * @param cls a pointer to the lookup closure.
6794 : * @param webhook_serial reference to the configured webhook template.
6795 : */
6796 : static void
6797 0 : get_pending_serial_cb (void *cls,
6798 : uint64_t webhook_pending_serial,
6799 : struct GNUNET_TIME_Absolute next_attempt,
6800 : uint32_t retries,
6801 : const char *url,
6802 : const char *http_method,
6803 : const char *header,
6804 : const char *body)
6805 : {
6806 0 : struct LookupPendingWebhookSerial_Closure *lpw = cls;
6807 :
6808 0 : if ((0 == strcmp (lpw->pwebhook->pwebhook.url,
6809 0 : url)) &&
6810 0 : (0 == strcmp (lpw->pwebhook->pwebhook.http_method,
6811 0 : http_method)) &&
6812 0 : (0 == strcmp (lpw->pwebhook->pwebhook.header,
6813 0 : header)) &&
6814 0 : (0 == strcmp (lpw->pwebhook->pwebhook.body,
6815 : body)) )
6816 : {
6817 0 : lpw->webhook_pending_serial = webhook_pending_serial;
6818 : }
6819 : /* else
6820 : {
6821 : fprintf(stdout, "next_attempt: %lu vs %lu\n", lpw->pwebhook->pwebhook.next_attempt.abs_value_us, next_attempt.abs_value_us);
6822 : fprintf(stdout, "retries: %d vs %d\n", lpw->pwebhook->pwebhook.retries, retries);
6823 : fprintf(stdout, "url: %s vs %s\n", lpw->pwebhook->pwebhook.url, url);
6824 : fprintf(stdout, "http_method: %s vs %s\n", lpw->pwebhook->pwebhook.http_method, http_method);
6825 : fprintf(stdout, "header: %s vs %s\n", lpw->pwebhook->pwebhook.header, header);
6826 : fprintf(stdout, "body: %s vs %s\n", lpw->pwebhook->pwebhook.body, body);
6827 : }*/
6828 0 : }
6829 :
6830 :
6831 : /**
6832 : * Convenience function to retrieve the row number of a webhook pending in the database.
6833 : *
6834 : * @param instance the instance to get webhook pending(wp) from.
6835 : * @param webhook pending the wp to lookup the serial for.
6836 : * @return the row number of the deposit.
6837 : */
6838 : static uint64_t
6839 0 : get_pending_serial (const struct InstanceData *instance,
6840 : const struct PendingWebhookData *pwebhook)
6841 : {
6842 0 : struct LookupPendingWebhookSerial_Closure lpw = {
6843 : .pwebhook = pwebhook,
6844 : .webhook_pending_serial = 0
6845 : };
6846 :
6847 0 : GNUNET_assert (0 <
6848 : plugin->lookup_all_webhooks (plugin->cls,
6849 : instance->instance.id,
6850 : 0,
6851 : INT_MAX,
6852 : &get_pending_serial_cb,
6853 : &lpw));
6854 0 : GNUNET_assert (0 != lpw.webhook_pending_serial);
6855 :
6856 0 : return lpw.webhook_pending_serial;
6857 : }
6858 :
6859 :
6860 : /**
6861 : * Closure for testing pending webhook lookup
6862 : */
6863 : struct TestLookupPendingWebhooks_Closure
6864 : {
6865 : /**
6866 : * Number of webhook serial to compare to
6867 : */
6868 : unsigned int webhooks_to_cmp_length;
6869 :
6870 : /**
6871 : * Pointer to array of webhook serials
6872 : */
6873 : const struct PendingWebhookData *webhooks_to_cmp;
6874 :
6875 : /**
6876 : * Pointer to array of number of matches for each pending webhook
6877 : */
6878 : unsigned int *results_matching;
6879 :
6880 : /**
6881 : * Total number of results returned
6882 : */
6883 : unsigned int results_length;
6884 : };
6885 :
6886 :
6887 : /**
6888 : * Function called after calling @e test_lookup_all_webhook,
6889 : * test_lookup_future_webhook and test_lookup_pending_webhook
6890 : *
6891 : * @param cls a pointer to the lookup closure.
6892 : * @param webhook_serial reference to the configured webhook template.
6893 : */
6894 : static void
6895 0 : lookup_pending_webhooks_cb (void *cls,
6896 : uint64_t webhook_pending_serial,
6897 : struct GNUNET_TIME_Absolute next_attempt,
6898 : uint32_t retries,
6899 : const char *url,
6900 : const char *http_method,
6901 : const char *header,
6902 : const char *body)
6903 : {
6904 0 : struct TestLookupPendingWebhooks_Closure *cmp = cls;
6905 :
6906 0 : cmp->results_length++;
6907 0 : for (unsigned int i = 0; cmp->webhooks_to_cmp_length > i; ++i)
6908 : {
6909 0 : if ((0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.url,
6910 0 : url)) &&
6911 0 : (0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.http_method,
6912 0 : http_method)) &&
6913 0 : (0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.header,
6914 0 : header)) &&
6915 0 : (0 == strcmp (cmp->webhooks_to_cmp[i].pwebhook.body,
6916 : body)) )
6917 : {
6918 0 : cmp->results_matching[i]++;
6919 : }
6920 : }
6921 0 : }
6922 :
6923 :
6924 : /**
6925 : * Tests looking up the pending webhook for an instance.
6926 : *
6927 : * @param instance the instance to query from.
6928 : * @param pwebhooks_length the number of pending webhook we are expecting.
6929 : * @param pwebhooks the list of pending webhooks that we expect to be found.
6930 : * @return 0 when successful, 1 otherwise.
6931 : */
6932 : static int
6933 0 : test_lookup_pending_webhooks (const struct InstanceData *instance,
6934 : unsigned int pwebhooks_length,
6935 : const struct PendingWebhookData *pwebhooks)
6936 0 : {
6937 0 : unsigned int results_matching[pwebhooks_length];
6938 0 : struct TestLookupPendingWebhooks_Closure cls = {
6939 : .webhooks_to_cmp_length = pwebhooks_length,
6940 : .webhooks_to_cmp = pwebhooks,
6941 : .results_matching = results_matching,
6942 : .results_length = 0
6943 : };
6944 :
6945 0 : memset (results_matching, 0, sizeof (results_matching));
6946 0 : if (0 > plugin->lookup_pending_webhooks (plugin->cls,
6947 : &lookup_pending_webhooks_cb,
6948 : &cls))
6949 : {
6950 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6951 : "Lookup pending webhook failed\n");
6952 0 : return 1;
6953 : }
6954 0 : if (pwebhooks_length != cls.results_length)
6955 : {
6956 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6957 : "Lookup pending webhook failed: incorrect number of results\n");
6958 0 : return 1;
6959 : }
6960 0 : for (unsigned int i = 0; i < pwebhooks_length; i++)
6961 : {
6962 0 : if (1 != cls.results_matching[i])
6963 : {
6964 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
6965 : "Lookup pending webhook failed: mismatched data\n");
6966 0 : return 1;
6967 : }
6968 : }
6969 0 : return 0;
6970 : }
6971 :
6972 :
6973 : /**
6974 : * Tests looking up the future webhook to send for an instance.
6975 : *
6976 : * @param instance the instance to query from.
6977 : * @param pwebhooks_length the number of pending webhook we are expecting.
6978 : * @param pwebhooks the list of pending webhooks that we expect to be found.
6979 : * @return 0 when successful, 1 otherwise.
6980 : */
6981 : static int
6982 0 : test_lookup_future_webhook (const struct InstanceData *instance,
6983 : unsigned int pwebhooks_length,
6984 : const struct PendingWebhookData *pwebhooks)
6985 0 : {
6986 0 : unsigned int results_matching[pwebhooks_length];
6987 0 : struct TestLookupPendingWebhooks_Closure cls = {
6988 : .webhooks_to_cmp_length = pwebhooks_length,
6989 : .webhooks_to_cmp = pwebhooks,
6990 : .results_matching = results_matching,
6991 : .results_length = 0
6992 : };
6993 :
6994 0 : memset (results_matching, 0, sizeof (results_matching));
6995 0 : if (0 > plugin->lookup_future_webhook (plugin->cls,
6996 : &lookup_pending_webhooks_cb,
6997 : &cls))
6998 : {
6999 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
7000 : "Lookup future webhook failed\n");
7001 0 : return 1;
7002 : }
7003 0 : if (pwebhooks_length != cls.results_length)
7004 : {
7005 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
7006 : "Lookup future webhook failed: incorrect number of results\n");
7007 0 : return 1;
7008 : }
7009 0 : for (unsigned int i = 0; pwebhooks_length > i; ++i)
7010 : {
7011 0 : if (1 != cls.results_matching[i])
7012 : {
7013 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
7014 : "Lookup future webhook failed: mismatched data\n");
7015 0 : return 1;
7016 : }
7017 : }
7018 0 : return 0;
7019 : }
7020 :
7021 :
7022 : /**
7023 : * Tests looking up all the pending webhook for an instance.
7024 : *
7025 : * @param instance the instance to query from.
7026 : * @param pwebhooks_length the number of pending webhook we are expecting.
7027 : * @param pwebhooks the list of pending webhooks that we expect to be found.
7028 : * @return 0 when successful, 1 otherwise.
7029 : */
7030 : static int
7031 0 : test_lookup_all_webhooks (const struct InstanceData *instance,
7032 : unsigned int pwebhooks_length,
7033 : const struct PendingWebhookData *pwebhooks)
7034 0 : {
7035 0 : uint64_t max_results = 2;
7036 0 : uint64_t min_row = 0;
7037 0 : unsigned int results_matching[GNUNET_NZL (pwebhooks_length)];
7038 0 : struct TestLookupPendingWebhooks_Closure cls = {
7039 : .webhooks_to_cmp_length = pwebhooks_length,
7040 : .webhooks_to_cmp = pwebhooks,
7041 : .results_matching = results_matching,
7042 : .results_length = 0
7043 : };
7044 :
7045 0 : memset (results_matching,
7046 : 0,
7047 : sizeof (results_matching));
7048 0 : if (0 > plugin->lookup_all_webhooks (plugin->cls,
7049 0 : instance->instance.id,
7050 : min_row,
7051 : max_results,
7052 : &lookup_pending_webhooks_cb,
7053 : &cls))
7054 : {
7055 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
7056 : "Lookup all webhooks failed\n");
7057 0 : return 1;
7058 : }
7059 0 : if (pwebhooks_length != cls.results_length)
7060 : {
7061 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
7062 : "Lookup all webhooks failed: incorrect number of results\n");
7063 0 : return 1;
7064 : }
7065 0 : for (unsigned int i = 0; pwebhooks_length > i; ++i)
7066 : {
7067 0 : if (1 != cls.results_matching[i])
7068 : {
7069 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
7070 : "Lookup all webhooks failed: mismatched data\n");
7071 0 : return 1;
7072 : }
7073 : }
7074 0 : return 0;
7075 : }
7076 :
7077 :
7078 : /**
7079 : * Tests deleting a pending webhook.
7080 : *
7081 : * @param instance the instance to delete the pending webhook from.
7082 : * @param pwebhook the pending webhook that should be deleted.
7083 : * @param expected_result the result that we expect the plugin to return.
7084 : * @return 0 when successful, 1 otherwise.
7085 : */
7086 : static int
7087 0 : test_delete_pending_webhook (uint64_t webhooks_pending_serial,
7088 : enum GNUNET_DB_QueryStatus expected_result)
7089 : {
7090 :
7091 0 : TEST_COND_RET_ON_FAIL (expected_result ==
7092 : plugin->delete_pending_webhook (plugin->cls,
7093 : webhooks_pending_serial),
7094 : "Delete webhook failed\n");
7095 0 : return 0;
7096 : }
7097 :
7098 :
7099 : /**
7100 : * Closure for pending webhook tests.
7101 : */
7102 : struct TestPendingWebhooks_Closure
7103 : {
7104 : /**
7105 : * The instance to use for this test.
7106 : */
7107 : struct InstanceData instance;
7108 :
7109 : /**
7110 : * The array of pending webhooks.
7111 : */
7112 : struct PendingWebhookData pwebhooks[2];
7113 : };
7114 :
7115 :
7116 : /**
7117 : * Sets up the data structures used in the pending webhook tests.
7118 : *
7119 : * @param cls the closure to fill with test data.
7120 : */
7121 : static void
7122 0 : pre_test_pending_webhooks (struct TestPendingWebhooks_Closure *cls)
7123 : {
7124 : /* Instance */
7125 0 : make_instance ("test_inst_pending_webhooks",
7126 : &cls->instance);
7127 :
7128 : /* Webhooks */
7129 0 : make_pending_webhook (1,
7130 : &cls->pwebhooks[0]);
7131 :
7132 0 : make_pending_webhook (4,
7133 : &cls->pwebhooks[1]);
7134 0 : cls->pwebhooks[1].pwebhook.url = "https://test.com";
7135 0 : cls->pwebhooks[1].pwebhook.http_method = "POST";
7136 0 : cls->pwebhooks[1].pwebhook.header = "Authorization:XYJAO5R06EO";
7137 0 : cls->pwebhooks[1].pwebhook.body = "$Amount";
7138 0 : }
7139 :
7140 :
7141 : /**
7142 : * Handles all teardown after testing.
7143 : *
7144 : * @param cls the closure containing memory to be freed.
7145 : */
7146 : static void
7147 0 : post_test_pending_webhooks (struct TestPendingWebhooks_Closure *cls)
7148 : {
7149 0 : free_instance_data (&cls->instance);
7150 0 : }
7151 :
7152 :
7153 : /**
7154 : * Runs the tests for pending webhooks.
7155 : *
7156 : * @param cls the container of the test data.
7157 : * @return 0 on success, 1 otherwise.
7158 : */
7159 : static int
7160 0 : run_test_pending_webhooks (struct TestPendingWebhooks_Closure *cls)
7161 : {
7162 : uint64_t webhook_pending_serial0;
7163 : uint64_t webhook_pending_serial1;
7164 :
7165 : /* Test that insert without an instance fails */
7166 0 : TEST_RET_ON_FAIL (test_insert_pending_webhook (&cls->instance,
7167 : &cls->pwebhooks[0],
7168 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
7169 :
7170 : /* Insert the instance */
7171 0 : TEST_RET_ON_FAIL (test_insert_instance (&cls->instance,
7172 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
7173 :
7174 : /* Test inserting a pending webhook */
7175 0 : TEST_RET_ON_FAIL (test_insert_pending_webhook (&cls->instance,
7176 : &cls->pwebhooks[0],
7177 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
7178 0 : TEST_RET_ON_FAIL (test_insert_pending_webhook (&cls->instance,
7179 : &cls->pwebhooks[1],
7180 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
7181 : /* Test collective pending webhook lookup */
7182 0 : TEST_RET_ON_FAIL (test_lookup_pending_webhooks (&cls->instance,
7183 : 2,
7184 : cls->pwebhooks));
7185 : /* Test pending webhook update */
7186 0 : TEST_RET_ON_FAIL (test_update_pending_webhook (&cls->instance,
7187 : &cls->pwebhooks[0],
7188 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
7189 0 : TEST_RET_ON_FAIL (test_lookup_future_webhook (&cls->instance,
7190 : 1,
7191 : &cls->pwebhooks[1]));
7192 0 : TEST_RET_ON_FAIL (test_update_pending_webhook (&cls->instance,
7193 : &cls->pwebhooks[1],
7194 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
7195 : // ???
7196 0 : TEST_RET_ON_FAIL (test_lookup_all_webhooks (&cls->instance,
7197 : 2,
7198 : cls->pwebhooks));
7199 :
7200 0 : webhook_pending_serial0 = get_pending_serial (&cls->instance,
7201 0 : &cls->pwebhooks[0]);
7202 0 : webhook_pending_serial1 = get_pending_serial (&cls->instance,
7203 0 : &cls->pwebhooks[1]);
7204 :
7205 : /* Test webhook deletion */
7206 0 : TEST_RET_ON_FAIL (test_delete_pending_webhook (webhook_pending_serial1,
7207 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
7208 : /* Test double deletion fails */
7209 0 : TEST_RET_ON_FAIL (test_delete_pending_webhook (webhook_pending_serial1,
7210 : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS));
7211 0 : TEST_RET_ON_FAIL (test_delete_pending_webhook (webhook_pending_serial0,
7212 : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
7213 0 : TEST_RET_ON_FAIL (test_lookup_all_webhooks (&cls->instance,
7214 : 0,
7215 : NULL));
7216 0 : return 0;
7217 : }
7218 :
7219 :
7220 : /**
7221 : * Takes care of pending webhook testing.
7222 : *
7223 : * @return 0 on success, 1 otherwise.
7224 : */
7225 : static int
7226 0 : test_pending_webhooks (void)
7227 : {
7228 : struct TestPendingWebhooks_Closure test_cls;
7229 : int test_result;
7230 :
7231 0 : pre_test_pending_webhooks (&test_cls);
7232 0 : test_result = run_test_pending_webhooks (&test_cls);
7233 0 : post_test_pending_webhooks (&test_cls);
7234 0 : return test_result;
7235 : }
7236 :
7237 :
7238 : /**
7239 : * Function that runs all tests.
7240 : *
7241 : * @return 0 on success, 1 otherwise.
7242 : */
7243 : static int
7244 0 : run_tests (void)
7245 : {
7246 0 : TEST_RET_ON_FAIL (test_instances ());
7247 0 : TEST_RET_ON_FAIL (test_products ());
7248 0 : TEST_RET_ON_FAIL (test_orders ());
7249 0 : TEST_RET_ON_FAIL (test_deposits ());
7250 0 : TEST_RET_ON_FAIL (test_transfers ());
7251 0 : TEST_RET_ON_FAIL (test_refunds ());
7252 0 : TEST_RET_ON_FAIL (test_lookup_orders_all_filters ());
7253 0 : TEST_RET_ON_FAIL (test_kyc ());
7254 0 : TEST_RET_ON_FAIL (test_templates ());
7255 0 : TEST_RET_ON_FAIL (test_webhooks ());
7256 0 : TEST_RET_ON_FAIL (test_pending_webhooks ());
7257 0 : return 0;
7258 : }
7259 :
7260 :
7261 : /**
7262 : * Main function that will be run by the scheduler.
7263 : *
7264 : * @param cls closure with config
7265 : */
7266 : static void
7267 1 : run (void *cls)
7268 : {
7269 1 : struct GNUNET_CONFIGURATION_Handle *cfg = cls;
7270 : /* Data for 'store_payment()' */
7271 :
7272 : /* Drop the tables to cleanup anything that might cause issues */
7273 1 : if (NULL == (plugin = TALER_MERCHANTDB_plugin_load (cfg)))
7274 : {
7275 0 : result = 77;
7276 0 : return;
7277 : }
7278 1 : (void) plugin->drop_tables (plugin->cls);
7279 1 : if (GNUNET_OK !=
7280 1 : plugin->create_tables (plugin->cls))
7281 : {
7282 1 : result = 77;
7283 1 : return;
7284 : }
7285 0 : if (GNUNET_OK !=
7286 0 : plugin->connect (plugin->cls))
7287 : {
7288 0 : GNUNET_break (0);
7289 0 : result = 17;
7290 0 : return;
7291 : }
7292 :
7293 : /* Run the preflight */
7294 0 : plugin->preflight (plugin->cls);
7295 :
7296 0 : result = run_tests ();
7297 0 : if (0 == result)
7298 : /** result = run_test_templates ();
7299 : if (0 == result)*/
7300 : {
7301 : /* Test dropping tables */
7302 0 : if (GNUNET_OK != plugin->drop_tables (plugin->cls))
7303 : {
7304 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
7305 : "Dropping tables failed\n");
7306 0 : result = 77;
7307 0 : return;
7308 : }
7309 : }
7310 :
7311 0 : TALER_MERCHANTDB_plugin_unload (plugin);
7312 0 : plugin = NULL;
7313 : }
7314 :
7315 :
7316 : /**
7317 : * Entry point for the tests.
7318 : */
7319 : int
7320 1 : main (int argc,
7321 : char *const argv[])
7322 : {
7323 : const char *plugin_name;
7324 : char *config_filename;
7325 : char *testname;
7326 : struct GNUNET_CONFIGURATION_Handle *cfg;
7327 :
7328 1 : result = -1;
7329 1 : if (NULL == (plugin_name = strrchr (argv[0],
7330 : (int) '-')))
7331 : {
7332 0 : GNUNET_break (0);
7333 0 : return -1;
7334 : }
7335 1 : GNUNET_log_setup (argv[0], "DEBUG", NULL);
7336 1 : plugin_name++;
7337 1 : (void) GNUNET_asprintf (&testname,
7338 : "test-merchantdb-%s",
7339 : plugin_name);
7340 1 : (void) GNUNET_asprintf (&config_filename,
7341 : "%s.conf",
7342 : testname);
7343 1 : fprintf (stdout, "Using %s\n", config_filename);
7344 1 : cfg = GNUNET_CONFIGURATION_create (TALER_MERCHANT_project_data ());
7345 1 : if (GNUNET_OK !=
7346 1 : GNUNET_CONFIGURATION_parse (cfg,
7347 : config_filename))
7348 : {
7349 0 : GNUNET_break (0);
7350 0 : GNUNET_free (config_filename);
7351 0 : GNUNET_free (testname);
7352 0 : return 2;
7353 : }
7354 1 : GNUNET_SCHEDULER_run (&run,
7355 : cfg);
7356 1 : GNUNET_CONFIGURATION_destroy (cfg);
7357 1 : GNUNET_free (config_filename);
7358 1 : GNUNET_free (testname);
7359 1 : return result;
7360 : }
7361 :
7362 :
7363 : /* end of test_merchantdb.c */
|