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