gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-merchant] branch master updated: implementation/tests for idempot


From: gnunet
Subject: [taler-merchant] branch master updated: implementation/tests for idempotent POST /orders requests
Date: Sun, 02 Aug 2020 03:53:30 +0200

This is an automated email from the git hooks/post-receive script.

jonathan-buchanan pushed a commit to branch master
in repository merchant.

The following commit(s) were added to refs/heads/master by this push:
     new b90d7d2  implementation/tests for idempotent POST /orders requests
b90d7d2 is described below

commit b90d7d27871ca09772a5ff4a5af15928bf3e68cb
Author: Jonathan Buchanan <jonathan.russ.buchanan@gmail.com>
AuthorDate: Sat Aug 1 21:53:16 2020 -0400

    implementation/tests for idempotent POST /orders requests
---
 .../taler-merchant-httpd_private-post-orders.c     | 90 ++++++++++++++--------
 src/include/taler_merchant_testing_lib.h           |  5 +-
 src/testing/test_merchant_api.c                    | 19 +++--
 src/testing/testing_api_cmd_post_orders.c          | 81 ++++++++++++++++++-
 4 files changed, 151 insertions(+), 44 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c 
b/src/backend/taler-merchant-httpd_private-post-orders.c
index 133d167..6fd65ae 100644
--- a/src/backend/taler-merchant-httpd_private-post-orders.c
+++ b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -417,6 +417,63 @@ execute_order (struct MHD_Connection *connection,
                                        "order:products");
   }
 
+  /* Test if we already have an order with this id */
+  {
+    struct TALER_ClaimTokenP token;
+    json_t *contract_terms;
+    TMH_db->preflight (TMH_db->cls);
+    qs = TMH_db->lookup_order (TMH_db->cls,
+                               hc->instance->settings.id,
+                               order_id,
+                               &token,
+                               &contract_terms);
+    /* If yes, check for idempotency */
+    if (0 > qs)
+    {
+      TMH_db->rollback (TMH_db->cls);
+      return qs;
+    }
+    else if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+    {
+      /* Comparing the contract terms is sufficient because all the other
+         params get added to it at some point. */
+      if (1 == json_equal (order,
+                           contract_terms))
+      {
+        MHD_RESULT ret;
+
+        ret = TALER_MHD_reply_json_pack (
+          connection,
+          MHD_HTTP_OK,
+          "{s:s, s:o?}",
+          "order_id",
+          order_id,
+          "token",
+          (0 == GNUNET_is_zero (&token))
+          ? NULL
+          : GNUNET_JSON_from_data_auto (&token));
+        GNUNET_JSON_parse_free (spec);
+        return ret;
+      }
+      else
+      {
+        /* This request is not idempotent */
+        int rv;
+        char *msg;
+
+        GNUNET_JSON_parse_free (spec);
+        GNUNET_asprintf (&msg,
+                         "order ID `%s' already exists, and the request was 
not idempotent",
+                         order_id);
+        rv = TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_BAD_REQUEST, /* or conflict? 
*/
+                                         
TALER_EC_PROPOSAL_STORE_DB_ERROR_ALREADY_EXISTS,
+                                         msg);
+        GNUNET_free (msg);
+        return rv;
+      }
+    }
+  }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Executing database transaction to create order '%s' for 
instance '%s'\n",
               order_id,
@@ -476,39 +533,6 @@ execute_order (struct MHD_Connection *connection,
                                           pd.next_restock));
     }
 
-    {
-      /* Hard error could be constraint violation,
-         check if order already exists */
-      TMH_db->preflight (TMH_db->cls);
-      qs = TMH_db->lookup_order (TMH_db->cls,
-                                 settings->id,
-                                 order_id,
-                                 NULL,
-                                 NULL);
-      if (0 < qs)
-      {
-        /* Yep, indeed uniqueness constraint violation */
-        int rv;
-        char *msg;
-
-        GNUNET_JSON_parse_free (spec);
-        GNUNET_asprintf (&msg,
-                         "order ID `%s' already exists",
-                         order_id);
-        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                    "Order `%s' already exists\n",
-                    order_id);
-        /* contract_terms may be private, only expose
-         * duplicate order_id to the network */
-        rv = TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_BAD_REQUEST, /* or conflict? 
*/
-                                         
TALER_EC_PROPOSAL_STORE_DB_ERROR_ALREADY_EXISTS,
-                                         msg);
-        GNUNET_free (msg);
-        return rv;
-      }
-    }
-
     /* Other hard transaction error (disk full, etc.) */
     GNUNET_JSON_parse_free (spec);
     return TALER_MHD_reply_with_error (
diff --git a/src/include/taler_merchant_testing_lib.h 
b/src/include/taler_merchant_testing_lib.h
index edbb6ad..a9aa2b4 100644
--- a/src/include/taler_merchant_testing_lib.h
+++ b/src/include/taler_merchant_testing_lib.h
@@ -533,6 +533,8 @@ TALER_TESTING_cmd_merchant_post_orders_no_claim (const char 
*label,
  *        "[product_id]/[quantity];...".
  * @param locks a string of references to lock product commands that should
  *        be formatted as "[lock_1];[lock_2];...".
+ * @param duplicate_of if not NULL, a reference to a previous order command
+ *        that should be duplicated and checked for an identical response.
  * @return the command
  */
 struct TALER_TESTING_Command
@@ -548,7 +550,8 @@ TALER_TESTING_cmd_merchant_post_orders2 (const char *label,
                                          const char *amount,
                                          const char *payment_target,
                                          const char *products,
-                                         const char *locks);
+                                         const char *locks,
+                                         const char *duplicate_of);
 
 
 /**
diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c
index b8ef13c..b4e06ed 100644
--- a/src/testing/test_merchant_api.c
+++ b/src/testing/test_merchant_api.c
@@ -285,8 +285,8 @@ run (void *cls,
                                              "EUR:5.0",
                                              "x-taler-bank",
                                              "",
-                                             ""),
-    /*
+                                             "",
+                                             NULL),
     TALER_TESTING_cmd_merchant_post_orders2 ("create-proposal-1-idem",
                                              merchant_url,
                                              MHD_HTTP_OK,
@@ -297,7 +297,8 @@ run (void *cls,
                                              "EUR:5.0",
                                              "x-taler-bank",
                                              "",
-                                             ""),*/
+                                             "",
+                                             "create-proposal-1"),
     TALER_TESTING_cmd_merchant_claim_order ("reclaim-1",
                                             merchant_url,
                                             MHD_HTTP_OK,
@@ -520,7 +521,8 @@ run (void *cls,
                                              "EUR:5.0",
                                              "unsupported-wire-method",
                                              "product-3/2",
-                                             ""),
+                                             "",
+                                             NULL),
     TALER_TESTING_cmd_merchant_post_orders2 ("create-proposal-p3-pd-nx",
                                              merchant_url,
                                              MHD_HTTP_NOT_FOUND,
@@ -531,7 +533,8 @@ run (void *cls,
                                              "EUR:5.0",
                                              "x-taler-bank",
                                              "unknown-product/2",
-                                             ""),
+                                             "",
+                                             NULL),
     TALER_TESTING_cmd_merchant_post_orders2 (
       "create-proposal-p3-not-enough-stock",
       merchant_url,
@@ -543,7 +546,8 @@ run (void *cls,
       "EUR:5.0",
       "x-taler-bank",
       "product-3/24",
-      ""),
+      "",
+      NULL),
     TALER_TESTING_cmd_merchant_post_orders2 ("create-proposal-p3",
                                              merchant_url,
                                              MHD_HTTP_OK,
@@ -554,7 +558,8 @@ run (void *cls,
                                              "EUR:5.0",
                                              "x-taler-bank",
                                              "product-3/3",
-                                             "lock-product-p3"),
+                                             "lock-product-p3",
+                                             NULL),
     TALER_TESTING_cmd_merchant_delete_order ("delete-order-1",
                                              merchant_url,
                                              "1",
diff --git a/src/testing/testing_api_cmd_post_orders.c 
b/src/testing/testing_api_cmd_post_orders.c
index f95d94a..f7a8dbb 100644
--- a/src/testing/testing_api_cmd_post_orders.c
+++ b/src/testing/testing_api_cmd_post_orders.c
@@ -50,6 +50,11 @@ struct OrdersState
    */
   const char *order_id;
 
+  /**
+   * The order id we expect the merchant to assign (if not NULL).
+   */
+  const char *expected_order_id;
+
   /**
    * Contract terms obtained from the backend.
    */
@@ -126,6 +131,12 @@ struct OrdersState
    * Should the command also CLAIM the order?
    */
   bool with_claim;
+
+  /**
+   * If not NULL, the command should duplicate the request and verify the
+   * response is the same as in this command.
+   */
+  const char *duplicate_of;
 };
 
 
@@ -154,6 +165,7 @@ orders_traits (void *cls,
     TALER_TESTING_make_trait_merchant_pub (0, &ps->merchant_pub),
     TALER_TESTING_make_trait_claim_nonce (0, &ps->nonce),
     TALER_TESTING_make_trait_claim_token (0, &ps->claim_token),
+    TALER_TESTING_make_trait_string (0, ps->order),
     TALER_TESTING_trait_end ()
   };
 
@@ -262,6 +274,44 @@ order_cb (void *cls,
   {
   case MHD_HTTP_OK:
     ps->order_id = GNUNET_strdup (order_id);
+    if ((NULL != ps->expected_order_id) &&
+        (0 != strcmp (order_id,
+                      ps->expected_order_id)))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Order id assigned does not match\n");
+      TALER_TESTING_interpreter_fail (ps->is);
+      return;
+    }
+    if (NULL != ps->duplicate_of)
+    {
+      const struct TALER_TESTING_Command *order_cmd;
+      const struct TALER_ClaimTokenP *prev_token;
+      struct TALER_ClaimTokenP zero_token = {0};
+      order_cmd = TALER_TESTING_interpreter_lookup_command (
+        ps->is,
+        ps->duplicate_of);
+      if (GNUNET_OK !=
+          TALER_TESTING_get_trait_claim_token (order_cmd,
+                                               0,
+                                               &prev_token))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Could not fetch previous order claim token\n");
+        TALER_TESTING_interpreter_fail (ps->is);
+        return;
+      }
+      if (NULL == claim_token)
+        prev_token = &zero_token;
+      if (0 != GNUNET_memcmp (prev_token,
+                              claim_token))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Claim tokens for identical requests do not match\n");
+        TALER_TESTING_interpreter_fail (ps->is);
+        return;
+      }
+    }
     break;
   default:
     {
@@ -372,6 +422,7 @@ orders_run2 (void *cls,
              struct TALER_TESTING_Interpreter *is)
 {
   struct OrdersState *ps = cls;
+  const char *order_str = ps->order;
   json_t *order;
   json_error_t error;
 
@@ -385,7 +436,24 @@ orders_run2 (void *cls,
   unsigned int locks_length = 0;
 
   ps->is = is;
-  order = json_loads (ps->order,
+  if (NULL != ps->duplicate_of)
+  {
+    const struct TALER_TESTING_Command *order_cmd;
+    order_cmd = TALER_TESTING_interpreter_lookup_command (
+      is,
+      ps->duplicate_of);
+    if (GNUNET_OK !=
+        TALER_TESTING_get_trait_string (order_cmd,
+                                        0,
+                                        &order_str))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Could not fetch previous order string\n");
+      TALER_TESTING_interpreter_fail (is);
+      return;
+    }
+  }
+  order = json_loads (order_str,
                       JSON_REJECT_DUPLICATES,
                       &error);
   if (NULL == order)
@@ -644,6 +712,7 @@ TALER_TESTING_cmd_merchant_post_orders_no_claim (const char 
*label,
                    amount,
                    &ps->order);
   ps->http_status = http_status;
+  ps->expected_order_id = order_id;
   ps->merchant_url = merchant_url;
   ps->with_claim = false;
   {
@@ -693,6 +762,7 @@ TALER_TESTING_cmd_merchant_post_orders (const char *label,
                    amount,
                    &ps->order);
   ps->http_status = http_status;
+  ps->expected_order_id = order_id;
   ps->merchant_url = merchant_url;
   ps->with_claim = true;
   {
@@ -727,6 +797,8 @@ TALER_TESTING_cmd_merchant_post_orders (const char *label,
  *        "[product_id]/[quantity];...".
  * @param locks a string of references to lock product commands that should
  *        be formatted as "[lock_1];[lock_2];...".
+ * @param duplicate_of if not NULL, a reference to a previous order command
+ *        that should be duplicated and checked for an identical response.
  * @return the command
  */
 struct TALER_TESTING_Command
@@ -742,7 +814,8 @@ TALER_TESTING_cmd_merchant_post_orders2 (const char *label,
                                          const char *amount,
                                          const char *payment_target,
                                          const char *products,
-                                         const char *locks)
+                                         const char *locks,
+                                         const char *duplicate_of)
 {
   struct OrdersState *ps;
 
@@ -754,12 +827,14 @@ TALER_TESTING_cmd_merchant_post_orders2 (const char 
*label,
                    &ps->order);
 
   ps->http_status = http_status;
+  ps->expected_order_id = order_id;
   ps->merchant_url = merchant_url;
   ps->payment_target = payment_target;
   ps->products = products;
   ps->locks = locks;
-  ps->with_claim = true;
+  ps->with_claim = (NULL == duplicate_of);
   ps->make_claim_token = claim_token;
+  ps->duplicate_of = duplicate_of;
   {
     struct TALER_TESTING_Command cmd = {
       .cls = ps,

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]