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