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