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