summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2017-10-24 13:12:11 +0200
committerChristian Grothoff <christian@grothoff.org>2017-10-24 13:12:11 +0200
commit868a853f0e29c9611542c3e18cb4dfee84779ee3 (patch)
treec19660485847e6ec598d43d7f8d8ea40cb7018de
parentc94688c3e908b551b5b3f0763c4d6d64ba69caef (diff)
downloadmerchant-868a853f0e29c9611542c3e18cb4dfee84779ee3.tar.gz
merchant-868a853f0e29c9611542c3e18cb4dfee84779ee3.tar.bz2
merchant-868a853f0e29c9611542c3e18cb4dfee84779ee3.zip
add testcases for merchantdb tipping functions, fix bugs found
-rw-r--r--src/backenddb/plugin_merchantdb_postgres.c114
-rw-r--r--src/backenddb/test_merchantdb.c232
2 files changed, 320 insertions, 26 deletions
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c
index bd764815..92287c4c 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -188,6 +188,16 @@ postgres_initialize (void *cls)
",balance_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL"
",PRIMARY KEY (reserve_priv)"
");"),
+ /* table where we remember when tipping reserves where established / enabled */
+ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_tip_reserve_credits ("
+ " reserve_priv BYTEA NOT NULL CHECK (LENGTH(reserve_priv)=32)"
+ ",credit_uuid BYTEA NOT NULL CHECK (LENGTH(credit_uuid)=64)"
+ ",timestamp INT8 NOT NULL"
+ ",amount_val INT8 NOT NULL"
+ ",amount_frac INT4 NOT NULL"
+ ",amount_curr VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL"
+ ",PRIMARY KEY (credit_uuid)"
+ ");"),
/* tips that have been authorized */
GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_tips ("
" reserve_priv BYTEA NOT NULL CHECK (LENGTH(reserve_priv)=32)"
@@ -549,7 +559,8 @@ postgres_initialize (void *cls)
10),
GNUNET_PQ_make_prepare ("lookup_reserve_by_tip_id",
"SELECT"
- " left_val"
+ " reserve_priv"
+ ",left_val"
",left_frac"
",left_curr"
" FROM merchant_tips"
@@ -581,6 +592,17 @@ postgres_initialize (void *cls)
") VALUES "
"($1, $2, $3, $4, $5)",
5),
+ GNUNET_PQ_make_prepare ("insert_tip_credit_uuid",
+ "INSERT INTO merchant_tip_reserve_credits"
+ "(reserve_priv"
+ ",credit_uuid"
+ ",timestamp"
+ ",amount_val"
+ ",amount_frac"
+ ",amount_curr)"
+ " VALUES "
+ "($1, $2, $3, $4, $5, $6)",
+ 6),
GNUNET_PQ_PREPARED_STATEMENT_END
};
@@ -2596,19 +2618,8 @@ postgres_enable_tip_reserve (void *cls,
struct GNUNET_TIME_Absolute expiration)
{
struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reserve_priv),
- GNUNET_PQ_query_param_end
- };
struct GNUNET_TIME_Absolute old_expiration;
struct TALER_Amount old_balance;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_absolute_time ("expiration",
- &old_expiration),
- TALER_PQ_result_spec_amount ("balance",
- &old_balance),
- GNUNET_PQ_result_spec_end
- };
enum GNUNET_DB_QueryStatus qs;
struct GNUNET_TIME_Absolute new_expiration;
struct TALER_Amount new_balance;
@@ -2620,12 +2631,58 @@ postgres_enable_tip_reserve (void *cls,
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "lookup_tip_reserve_balance",
- params,
- rs);
+
+ /* ensure that credit_uuid is new/unique */
+ {
+ struct GNUNET_TIME_Absolute now;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (reserve_priv),
+ GNUNET_PQ_query_param_auto_from_type (credit_uuid),
+ GNUNET_PQ_query_param_absolute_time (&now),
+ TALER_PQ_query_param_amount (credit),
+ GNUNET_PQ_query_param_end
+ };
+
+ now = GNUNET_TIME_absolute_get ();
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_tip_credit_uuid",
+ params);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ postgres_rollback (pg);
+ return qs;
+ }
+ /* UUID already exists, we are done! */
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ postgres_rollback (pg);
+ return qs;
+ }
+ }
+
+ /* Obtain existing reserve balance */
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (reserve_priv),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_absolute_time ("expiration",
+ &old_expiration),
+ TALER_PQ_result_spec_amount ("balance",
+ &old_balance),
+ GNUNET_PQ_result_spec_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "lookup_tip_reserve_balance",
+ params,
+ rs);
+ }
if (0 > qs)
{
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
postgres_rollback (pg);
return qs;
}
@@ -2674,11 +2731,16 @@ postgres_enable_tip_reserve (void *cls,
params);
if (0 > qs)
{
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
postgres_rollback (pg);
return qs;
}
}
- return postgres_commit (pg);
+ qs = postgres_commit (pg);
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
}
@@ -2913,16 +2975,18 @@ postgres_pickup_tip (void *cls,
? TALER_EC_TIP_PICKUP_DB_ERROR_HARD
: TALER_EC_TIP_PICKUP_DB_ERROR_SOFT;
}
- if (0 !=
- TALER_amount_cmp (&existing_amount,
- amount))
- {
- GNUNET_break_op (0);
- postgres_rollback (pg);
- return TALER_EC_TIP_PICKUP_AMOUNT_CHANGED;
- }
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+ {
+ if (0 !=
+ TALER_amount_cmp (&existing_amount,
+ amount))
+ {
+ GNUNET_break_op (0);
+ postgres_rollback (pg);
+ return TALER_EC_TIP_PICKUP_AMOUNT_CHANGED;
+ }
return TALER_EC_NONE; /* we are done! */
+ }
}
/* Calculate new balance */
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
index eb4fe14b..05db7c2b 100644
--- a/src/backenddb/test_merchantdb.c
+++ b/src/backenddb/test_merchantdb.c
@@ -17,6 +17,7 @@
* @file merchant/test_merchantdb_postgres.c
* @brief testcase for merchant's postgres db plugin
* @author Marcello Stanisci
+ * @author Christian Grothoff
*/
#include "platform.h"
@@ -510,6 +511,232 @@ test_wire_fee ()
/**
+ * Test APIs related to tipping.
+ *
+ * @return #GNUNET_OK upon success
+ */
+static int
+test_tipping ()
+{
+ struct TALER_ReservePrivateKeyP tip_reserve_priv;
+ struct TALER_ReservePrivateKeyP pres;
+ struct GNUNET_HashCode tip_id;
+ struct GNUNET_HashCode tip_credit_uuid;
+ struct GNUNET_HashCode pickup_id;
+ struct GNUNET_TIME_Absolute tip_expiration;
+ struct GNUNET_TIME_Absolute reserve_expiration;
+ struct TALER_Amount total;
+ struct TALER_Amount amount;
+ struct TALER_Amount inc;
+
+ RND_BLK (&tip_reserve_priv);
+ if (TALER_EC_TIP_AUTHORIZE_RESERVE_UNKNOWN !=
+ plugin->authorize_tip (plugin->cls,
+ "testing tips reserve unknown",
+ &amount,
+ &tip_reserve_priv,
+ &tip_expiration,
+ &tip_id))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ RND_BLK (&tip_credit_uuid);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":5",
+ &total));
+ /* Pick short expiration, but long enough to
+ run 2 DB interactions even on very slow systems. */
+ reserve_expiration = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
+ 2));
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->enable_tip_reserve (plugin->cls,
+ &tip_reserve_priv,
+ &tip_credit_uuid,
+ &total,
+ reserve_expiration))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ /* check idempotency */
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+ plugin->enable_tip_reserve (plugin->cls,
+ &tip_reserve_priv,
+ &tip_credit_uuid,
+ &total,
+ reserve_expiration))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ /* Make sure it has expired, so at this point the value is back at ZERO! */
+ sleep (3);
+ if (TALER_EC_TIP_AUTHORIZE_RESERVE_EXPIRED !=
+ plugin->authorize_tip (plugin->cls,
+ "testing tips too late",
+ &amount,
+ &tip_reserve_priv,
+ &tip_expiration,
+ &tip_id))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ /* Re-add some funds again */
+ RND_BLK (&tip_credit_uuid);
+ reserve_expiration = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
+ 2));
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->enable_tip_reserve (plugin->cls,
+ &tip_reserve_priv,
+ &tip_credit_uuid,
+ &total,
+ reserve_expiration))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ /* top it up by adding more with a fresh UUID
+ and even longer expiration time (until end of test) */
+ RND_BLK (&tip_credit_uuid);
+ reserve_expiration = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS);
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ plugin->enable_tip_reserve (plugin->cls,
+ &tip_reserve_priv,
+ &tip_credit_uuid,
+ &total,
+ reserve_expiration))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ /* Now authorize some tips */
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":4",
+ &amount));
+ if (TALER_EC_NONE !=
+ plugin->authorize_tip (plugin->cls,
+ "testing tips",
+ &amount,
+ &tip_reserve_priv,
+ &tip_expiration,
+ &tip_id))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (tip_expiration.abs_value_us != reserve_expiration.abs_value_us)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (TALER_EC_NONE !=
+ plugin->authorize_tip (plugin->cls,
+ "testing tips more",
+ &amount,
+ &tip_reserve_priv,
+ &tip_expiration,
+ &tip_id))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (tip_expiration.abs_value_us != reserve_expiration.abs_value_us)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ /* Let's try to pick up the authorized tip in 2 increments */
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (CURRENCY ":2",
+ &inc));
+ RND_BLK (&pickup_id);
+ if (TALER_EC_NONE !=
+ plugin->pickup_tip (plugin->cls,
+ &inc,
+ &tip_id,
+ &pickup_id,
+ &pres))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (0 != memcmp (&pres,
+ &tip_reserve_priv,
+ sizeof (pres)))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ RND_BLK (&pickup_id);
+ if (TALER_EC_NONE !=
+ plugin->pickup_tip (plugin->cls,
+ &inc,
+ &tip_id,
+ &pickup_id,
+ &pres))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (0 != memcmp (&pres,
+ &tip_reserve_priv,
+ sizeof (pres)))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ /* Third attempt should fail, as we've picked up 4/4 in amount */
+ RND_BLK (&pickup_id);
+ if (TALER_EC_TIP_PICKUP_NO_FUNDS !=
+ plugin->pickup_tip (plugin->cls,
+ &inc,
+ &tip_id,
+ &pickup_id,
+ &pres))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ /* We authorized 8 out of 10, so going for another 4 should fail with insufficient funds */
+ if (TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS !=
+ plugin->authorize_tip (plugin->cls,
+ "testing tips insufficient funds",
+ &amount,
+ &tip_reserve_priv,
+ &tip_expiration,
+ &tip_id))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ /* Test that picking up with random (unauthorized) tip_id fails as well */
+ RND_BLK (&tip_id);
+ RND_BLK (&pickup_id);
+ if (TALER_EC_TIP_PICKUP_TIP_ID_UNKNOWN !=
+ plugin->pickup_tip (plugin->cls,
+ &inc,
+ &tip_id,
+ &pickup_id,
+ &pres))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+
+ return GNUNET_OK;
+}
+
+
+/**
* Main function that will be run by the scheduler.
*
* @param cls closure with config
@@ -519,6 +746,7 @@ run (void *cls)
{
struct GNUNET_CONFIGURATION_Handle *cfg = cls;
struct GNUNET_TIME_Absolute fake_now;
+ json_t *out;
/* Data for 'store_payment()' */
if (NULL == (plugin = TALER_MERCHANTDB_plugin_load (cfg)))
@@ -615,7 +843,6 @@ run (void *cls)
&h_contract_terms,
&merchant_pub));
- json_t *out;
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->find_contract_terms (plugin->cls,
@@ -787,6 +1014,9 @@ run (void *cls)
FAILIF (GNUNET_OK !=
test_wire_fee ());
+ FAILIF (GNUNET_OK !=
+ test_tipping ());
+
if (-1 == result)
result = 0;