From e2333817a6767933608c6d3bd3216a9fcd3033e7 Mon Sep 17 00:00:00 2001 From: Jonathan Buchanan Date: Tue, 26 May 2020 18:25:52 -0400 Subject: wrote tests for deposits in the db api --- src/backenddb/Makefile.am | 1 + src/backenddb/test_merchantdb.c | 480 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 479 insertions(+), 2 deletions(-) diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am index 3edc4509..bec76cc7 100644 --- a/src/backenddb/Makefile.am +++ b/src/backenddb/Makefile.am @@ -54,6 +54,7 @@ libtaler_plugin_merchantdb_postgres_la_LDFLAGS = \ -ltalerpq \ -ltalerutil \ -ltalerjson \ + -ltalermhd \ -lpq \ -lgnunetutil $(XLIB) diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c index d45089a0..6ed0b217 100644 --- a/src/backenddb/test_merchantdb.c +++ b/src/backenddb/test_merchantdb.c @@ -22,6 +22,7 @@ #include "platform.h" #include #include +#include #include "taler_merchantdb_lib.h" @@ -375,7 +376,8 @@ run_test_instances (struct TestInstances_Closure *cls) instances)); /* Test account creation */ - TEST_RET_ON_FAIL (test_insert_account ("test_inst", &cls->accounts[0])); + TEST_RET_ON_FAIL (test_insert_account (cls->is.id, + &cls->accounts[0])); /* Test accounts from instance lookup */ instances[0].accounts_length = 1; @@ -399,9 +401,36 @@ run_test_instances (struct TestInstances_Closure *cls) /* Test multiple accounts */ + /* Test lookup account */ + uint64_t account_serial; + if (1 != plugin->lookup_account (plugin->cls, + cls->is.id, + cls->accounts[0].payto_uri, + &account_serial)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Lookup account failed\n"); + return 1; + } + if (1 != account_serial) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Lookup account failed: incorrect serial number found\n"); + return 1; + } + if (1 == plugin->lookup_account (plugin->cls, + cls->is.id, + "payto://other-uri", + &account_serial)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Lookup account failed: account found where there is none\n"); + return 1; + } + /* Test instance private key deletion */ if (0 > plugin->delete_instance_private_key (plugin->cls, - "test_inst")) + cls->is.id)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Delete instance private key failed\n"); @@ -1161,6 +1190,452 @@ test_orders (void *cls) } +/** + * A container for deposit data. + */ +struct DepositData +{ + struct GNUNET_TIME_Absolute timestamp; + + struct GNUNET_HashCode h_contract_terms; + + struct TALER_CoinSpendPublicKeyP coin_pub; + + const char *exchange_url; + + struct TALER_Amount amount_with_fee; + + struct TALER_Amount deposit_fee; + + struct TALER_Amount refund_fee; + + struct TALER_Amount wire_fee; + + struct GNUNET_HashCode h_wire; + + struct TALER_ExchangeSignatureP exchange_sig; + +}; + + +/** + * Closure for testing deposit lookup + */ +struct TestLookupDeposits_Closure +{ + /** + * Number of deposits to compare to + */ + unsigned int deposits_to_cmp_length; + + /** + * Pointer to array of deposit data + */ + const struct DepositData *deposits_to_cmp; + + /** + * Pointer to array of number of matches per deposit + */ + unsigned int *results_matching; + + /** + * Total number of results returned + */ + unsigned int results_length; +}; + + +static void +lookup_deposits_cb (void *cls, + const char *exchange_url, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *amount_with_fee, + const struct TALER_Amount *deposit_fee, + const struct TALER_Amount *refund_fee, + const struct TALER_Amount *wire_fee) +{ + if (NULL == cls) + return; + struct TestLookupDeposits_Closure *cmp = cls; + cmp->results_length += 1; + for (unsigned int i = 0; cmp->deposits_to_cmp_length > i; ++i) + { + if ((GNUNET_OK == TALER_amount_cmp_currency ( + &cmp->deposits_to_cmp[i].amount_with_fee, + amount_with_fee)) && + (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].amount_with_fee, + amount_with_fee)) && + (GNUNET_OK == TALER_amount_cmp_currency ( + &cmp->deposits_to_cmp[i].deposit_fee, + deposit_fee)) && + (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].deposit_fee, + deposit_fee)) && + (GNUNET_OK == TALER_amount_cmp_currency ( + &cmp->deposits_to_cmp[i].refund_fee, + refund_fee)) && + (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].refund_fee, + refund_fee)) && + (GNUNET_OK == TALER_amount_cmp_currency ( + &cmp->deposits_to_cmp[i].wire_fee, + wire_fee)) && + (0 == TALER_amount_cmp (&cmp->deposits_to_cmp[i].wire_fee, + wire_fee))) + { + cmp->results_matching[i] += 1; + } + + } +} + + +static int +test_insert_deposit (const char *is, + const struct TALER_ExchangePublicKeyP *exchange_pub, + const struct DepositData *deposit) +{ + if (1 != plugin->insert_deposit (plugin->cls, + is, + deposit->timestamp, + &deposit->h_contract_terms, + &deposit->coin_pub, + deposit->exchange_url, + &deposit->amount_with_fee, + &deposit->deposit_fee, + &deposit->refund_fee, + &deposit->wire_fee, + &deposit->h_wire, + &deposit->exchange_sig, + exchange_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Insert deposit failed\n"); + return 1; + } + return 0; +} + + +static int +test_lookup_deposits (const char *is, + const struct GNUNET_HashCode *h_contract_terms, + unsigned int deposits_length, + const struct DepositData *deposits) +{ + struct TestLookupDeposits_Closure cls; + cls.deposits_to_cmp_length = deposits_length; + cls.deposits_to_cmp = deposits; + unsigned int results_matching[deposits_length]; + for (unsigned int i = 0; deposits_length > i; ++i) + results_matching[i] = 0; + cls.results_matching = results_matching; + cls.results_length = 0; + if (0 > plugin->lookup_deposits (plugin->cls, + is, + h_contract_terms, + &lookup_deposits_cb, + &cls)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Lookup deposits failed\n"); + return 1; + } + if (deposits_length != cls.results_length) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Lookup deposits failed: incorrect number of results returned\n"); + return 1; + } + for (unsigned int i = 0; deposits_length > i; ++i) + { + if (cls.results_matching[i] != 1) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Lookup deposits failed: mismatched data\n"); + return 1; + } + } + return 0; +} + + +/** + * Closure for deposit tests. + */ +struct TestDeposits_Closure +{ + /** + * The instance settings + */ + struct TALER_MERCHANTDB_InstanceSettings is; + + /** + * The instance public key + */ + struct TALER_MerchantPublicKeyP merchant_pub; + + /** + * The instance private key + */ + struct TALER_MerchantPrivateKeyP merchant_priv; + + /** + * The merchant account + */ + struct TALER_MERCHANTDB_AccountDetails account; + + /** + * The exchange public key + */ + struct TALER_ExchangePublicKeyP exchange_pub; + + /** + * The exchange private key + */ + struct TALER_ExchangePrivateKeyP exchange_priv; + + /** + * The master public key + */ + struct TALER_MasterPublicKeyP master_pub; + + /** + * The master private key + */ + struct TALER_MasterPrivateKeyP master_priv; + + /** + * The master signature + */ + struct TALER_MasterSignatureP master_sig; + + /** + * The exchange signkey start date + */ + struct GNUNET_TIME_Absolute signkey_start; + + /** + * The exchange signkey expire date + */ + struct GNUNET_TIME_Absolute signkey_expire; + + /** + * The exchange signkey end date + */ + struct GNUNET_TIME_Absolute signkey_end; + + /** + * The order data + */ + struct OrderData order; + + /** + * The array of deposits + */ + struct DepositData deposits[2]; +}; + + +static void +pre_test_deposits (struct TestDeposits_Closure *cls) +{ + /* Instance */ + GNUNET_CRYPTO_eddsa_key_create (&cls->merchant_priv.eddsa_priv); + GNUNET_CRYPTO_eddsa_key_get_public (&cls->merchant_priv.eddsa_priv, + &cls->merchant_pub.eddsa_pub); + cls->is.id = "test_inst_deposits"; + cls->is.name = "Test"; + cls->is.address = json_array (); + GNUNET_assert (NULL != cls->is.address); + GNUNET_assert (0 == json_array_append (cls->is.address, + json_string ("123 Example St"))); + cls->is.jurisdiction = json_array (); + GNUNET_assert (NULL != cls->is.jurisdiction); + GNUNET_assert (0 == json_array_append (cls->is.jurisdiction, + json_string ("Ohio"))); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:1200.40", + &cls->is.default_max_deposit_fee)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:1200.40", + &cls->is.default_max_wire_fee)); + cls->is.default_wire_fee_amortization = 1; + cls->is.default_wire_transfer_delay = GNUNET_TIME_relative_get_minute_ (); + cls->is.default_pay_delay = GNUNET_TIME_relative_get_second_ (); + + /* Account */ + GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, + &cls->account.h_wire); + GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, + &cls->account.salt); + cls->account.payto_uri = "payto://x-taler-bank/bank.demo.taler.net/4"; + cls->account.active = true; + + /* Signing key */ + GNUNET_CRYPTO_eddsa_key_create (&cls->exchange_priv.eddsa_priv); + GNUNET_CRYPTO_eddsa_key_get_public (&cls->exchange_priv.eddsa_priv, + &cls->exchange_pub.eddsa_pub); + GNUNET_CRYPTO_eddsa_key_create (&cls->master_priv.eddsa_priv); + GNUNET_CRYPTO_eddsa_key_get_public (&cls->master_priv.eddsa_priv, + &cls->master_pub.eddsa_pub); + struct TALER_ExchangeSigningKeyValidityPS exch_sign; + exch_sign.purpose.size = htonl (sizeof (struct + TALER_ExchangeSigningKeyValidityPS)); + exch_sign.purpose.purpose = htonl ( + TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY); + GNUNET_CRYPTO_eddsa_sign (&cls->master_priv.eddsa_priv, + &exch_sign, + &cls->master_sig.eddsa_signature); + cls->signkey_start = GNUNET_TIME_absolute_get (); + cls->signkey_expire = GNUNET_TIME_absolute_get (); + cls->signkey_end = GNUNET_TIME_absolute_get (); + + /* Order */ + struct GNUNET_TIME_Absolute pay_deadline = + GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), + GNUNET_TIME_UNIT_DAYS); + struct GNUNET_TIME_Absolute refund_deadline = + GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), + GNUNET_TIME_UNIT_WEEKS); + GNUNET_TIME_round_abs (&pay_deadline); + GNUNET_TIME_round_abs (&refund_deadline); + cls->order.id = "test_orders_od_1"; + cls->order.pay_deadline = pay_deadline; + cls->order.contract = json_object (); + json_object_set (cls->order.contract, + "fulfillment_url", + json_string ("a")); + json_object_set (cls->order.contract, + "pay_deadline", + GNUNET_JSON_from_time_abs (pay_deadline)); + json_object_set (cls->order.contract, + "refund_deadline", + GNUNET_JSON_from_time_abs (refund_deadline)); + + /* Deposit */ + cls->deposits[0].timestamp = GNUNET_TIME_absolute_get (); + GNUNET_assert (GNUNET_OK == + TALER_JSON_hash (cls->order.contract, + &cls->deposits[0].h_contract_terms)); + struct TALER_CoinSpendPrivateKeyP coin_priv; + GNUNET_CRYPTO_eddsa_key_create (&coin_priv.eddsa_priv); + GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv, + &cls->deposits[0].coin_pub.eddsa_pub); + cls->deposits[0].exchange_url = "test-exchange"; + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:50.00", + &cls->deposits[0].amount_with_fee)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:1.00", + &cls->deposits[0].deposit_fee)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:1.50", + &cls->deposits[0].refund_fee)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:2.00", + &cls->deposits[0].wire_fee)); + cls->deposits[0].h_wire = cls->account.h_wire; + struct TALER_DepositRequestPS deposit_sign; + deposit_sign.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS)); + deposit_sign.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT); + deposit_sign.h_contract_terms = cls->deposits[0].h_contract_terms; + deposit_sign.h_wire = cls->deposits[0].h_wire; + deposit_sign.wallet_timestamp = GNUNET_TIME_absolute_hton ( + GNUNET_TIME_absolute_get ()); + deposit_sign.refund_deadline = GNUNET_TIME_absolute_hton ( + GNUNET_TIME_absolute_get ()); + TALER_amount_hton (&deposit_sign.amount_with_fee, + &cls->deposits[0].amount_with_fee); + TALER_amount_hton (&deposit_sign.deposit_fee, + &cls->deposits[0].deposit_fee); + deposit_sign.merchant = cls->merchant_pub; + deposit_sign.coin_pub = cls->deposits[0].coin_pub; + GNUNET_CRYPTO_eddsa_sign (&cls->exchange_priv.eddsa_priv, + &deposit_sign, + &cls->deposits[0].exchange_sig.eddsa_signature); +} + + +static void +post_test_deposits (struct TestDeposits_Closure *cls) +{ + /* Instance */ + json_decref (cls->is.address); + json_decref (cls->is.jurisdiction); + + /* Order */ + json_decref (cls->order.contract); +} + + +static int +run_test_deposits (struct TestDeposits_Closure *cls) +{ + /* Insert the instance */ + TEST_RET_ON_FAIL (test_insert_instance (&cls->merchant_pub, + &cls->merchant_priv, + &cls->is)); + + /* Insert an account */ + TEST_RET_ON_FAIL (test_insert_account (cls->is.id, + &cls->account)); + + /* Insert a signing key */ + if (1 != plugin->insert_exchange_signkey (plugin->cls, + &cls->master_pub, + &cls->exchange_pub, + cls->signkey_start, + cls->signkey_expire, + cls->signkey_end, + &cls->master_sig)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Insert signing key failed\n"); + return 1; + } + + /* Insert an order */ + TEST_RET_ON_FAIL (test_insert_order (cls->is.id, + cls->order.id, + cls->order.pay_deadline, + cls->order.contract)); + + /* Insert contract terms */ + if (1 != plugin->insert_contract_terms (plugin->cls, + cls->is.id, + cls->order.id, + cls->order.contract)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Insert contract terms failed\n"); + return 1; + } + + /* Test inserting a deposit */ + TEST_RET_ON_FAIL (test_insert_deposit (cls->is.id, + &cls->exchange_pub, + &cls->deposits[0])); + + /* Test lookup deposits */ + TEST_RET_ON_FAIL (test_lookup_deposits (cls->is.id, + &cls->deposits[0].h_contract_terms, + 1, + cls->deposits)); + + return 0; +} + + +static int +test_deposits (void *cls) +{ + struct TestDeposits_Closure test_cls; + pre_test_deposits (&test_cls); + int test_result = run_test_deposits (&test_cls); + post_test_deposits (&test_cls); + return test_result; +} + + /** * Function that runs all tests and returns 1 upon error, 0 on success. */ @@ -1170,6 +1645,7 @@ run_tests (void *cls) TEST_RET_ON_FAIL (test_instances (cls)); TEST_RET_ON_FAIL (test_products (cls)); TEST_RET_ON_FAIL (test_orders (cls)); + TEST_RET_ON_FAIL (test_deposits (cls)); return 0; } -- cgit v1.2.3