LCOV - code coverage report
Current view: top level - lib - testing_api_cmd_proposal.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 116 138 84.1 %
Date: 2018-09-21 06:18:36 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2018 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify
       6             :   it under the terms of the GNU General Public License as
       7             :   published by the Free Software Foundation; either version 3, or
       8             :   (at your option) any later version.
       9             : 
      10             :   TALER is distributed in the hope that it will be useful, but
      11             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :   GNU General Public License for more details.
      14             : 
      15             :   You should have received a copy of the GNU General Public
      16             :   License along with TALER; see the file COPYING.  If not, see
      17             :   <http://www.gnu.org/licenses/>
      18             : */
      19             : 
      20             : /**
      21             :  * @file exchange/testing_api_cmd_exec_merchant.c
      22             :  * @brief command to execute the merchant backend service.
      23             :  * @author Marcello Stanisci
      24             :  */
      25             : 
      26             : #include "platform.h"
      27             : #include <taler/taler_exchange_service.h>
      28             : #include <taler/taler_testing_lib.h>
      29             : #include "taler_merchant_service.h"
      30             : #include "taler_merchant_testing_lib.h"
      31             : 
      32             : /**
      33             :  * State for a "proposal" CMD.
      34             :  */
      35             : struct ProposalState
      36             : {
      37             : 
      38             :   /**
      39             :    * The order.
      40             :    */
      41             :   const char *order;
      42             : 
      43             :   /**
      44             :    * Expected status code.
      45             :    */
      46             :   unsigned int http_status;
      47             : 
      48             :   /**
      49             :    * Order id.
      50             :    */
      51             :   const char *order_id;
      52             : 
      53             :   /**
      54             :    * Contract terms obtained from the backend.
      55             :    */
      56             :   const char *contract_terms;
      57             : 
      58             :   /**
      59             :    * Contract terms hash code.
      60             :    */
      61             :   struct GNUNET_HashCode h_contract_terms;
      62             : 
      63             :   /**
      64             :    * The /proposal operation handle.
      65             :    */
      66             :   struct TALER_MERCHANT_ProposalOperation *po;
      67             : 
      68             :   /**
      69             :    * The (initial) /proposal/lookup operation handle.
      70             :    * The logic is such that after a proposal creation,
      71             :    * it soon makes a proposal lookup in order to check
      72             :    * if the merchant backend is actually aware.
      73             :    */
      74             :   struct TALER_MERCHANT_ProposalLookupOperation *plo;
      75             : 
      76             :   /**
      77             :    * The nonce.
      78             :    */
      79             :   struct GNUNET_CRYPTO_EddsaPublicKey nonce;
      80             : 
      81             :   /**
      82             :    * The merchant instance.
      83             :    */
      84             :   const char *instance;
      85             : 
      86             :   /**
      87             :    * URL of the merchant backend.
      88             :    */
      89             :   const char *merchant_url;
      90             : 
      91             :   /**
      92             :    * The curl context.
      93             :    */
      94             :   struct GNUNET_CURL_Context *ctx;
      95             : 
      96             :   /**
      97             :    * The interpreter state.
      98             :    */
      99             :   struct TALER_TESTING_Interpreter *is;
     100             : 
     101             :   /**
     102             :    * Merchant signature over the proposal.
     103             :    */
     104             :   struct TALER_MerchantSignatureP merchant_sig;
     105             : 
     106             :   /**
     107             :    * Merchant public key.
     108             :    */
     109             :   struct TALER_MerchantPublicKeyP merchant_pub;
     110             : };
     111             : 
     112             : 
     113             : /**
     114             :  * State for a "proposal lookup" CMD.  Not used by
     115             :  * the initial lookup operation.
     116             :  */
     117             : struct ProposalLookupState
     118             : {
     119             :   /**
     120             :    * The interpreter state.
     121             :    */
     122             :   struct TALER_TESTING_Interpreter *is;
     123             : 
     124             :   /**
     125             :    * URL of the merchant backend.
     126             :    */
     127             :   const char *merchant_url;
     128             : 
     129             :   /**
     130             :    * The curl context.
     131             :    */
     132             :   struct GNUNET_CURL_Context *ctx;
     133             : 
     134             :   /**
     135             :    * Expected status code.
     136             :    */
     137             :   unsigned int http_status;
     138             : 
     139             :   /**
     140             :    * /proposal/lookup operation handle.
     141             :    */
     142             :   struct TALER_MERCHANT_ProposalLookupOperation *plo;
     143             : 
     144             :   /**
     145             :    * Reference to a proposal operation.  Will offer the
     146             :    * nonce for the operation.
     147             :    */
     148             :   const char *proposal_reference;
     149             : 
     150             :   /**
     151             :    * Order id to lookup upon.  If null, the @a proposal_reference
     152             :    * will offer this value.
     153             :    */
     154             :   const char *order_id;
     155             : };
     156             : 
     157             : /**
     158             :  * Offer internal data to other commands.
     159             :  *
     160             :  * @param cls closure
     161             :  * @param ret[out] result (could be anything)
     162             :  * @param trait name of the trait
     163             :  * @param index index number of the object to extract.
     164             :  * @return #GNUNET_OK on success
     165             :  */
     166             : static int
     167         177 : proposal_traits (void *cls,
     168             :                  void **ret,
     169             :                  const char *trait,
     170             :                  unsigned int index)
     171             : {
     172             : 
     173         177 :   struct ProposalState *ps = cls;
     174             :   #define MAKE_TRAIT_NONCE(ptr) \
     175             :     TALER_TESTING_make_trait_peer_key_pub (1, ptr)
     176             : 
     177        1062 :   struct TALER_TESTING_Trait traits[] = {
     178         177 :     TALER_TESTING_make_trait_order_id (0, ps->order_id),
     179         177 :     TALER_TESTING_make_trait_contract_terms
     180             :       (0, ps->contract_terms),
     181             :     TALER_TESTING_make_trait_h_contract_terms
     182         177 :       (0, &ps->h_contract_terms),
     183         177 :     TALER_TESTING_make_trait_merchant_sig (0, &ps->merchant_sig),
     184         177 :     TALER_TESTING_make_trait_peer_key_pub
     185             :       (0, &ps->merchant_pub.eddsa_pub),
     186         177 :     MAKE_TRAIT_NONCE (&ps->nonce),
     187             :     TALER_TESTING_trait_end ()
     188             :   };
     189             :   
     190         177 :   return TALER_TESTING_get_trait (traits,
     191             :                                   ret,
     192             :                                   trait,
     193             :                                   index);
     194             :   return GNUNET_SYSERR;
     195             : }
     196             : 
     197             : 
     198             : /**
     199             :  * Used to fill the "proposal" CMD state with backend-provided
     200             :  * values.  Also double-checks that the proposal was correctly
     201             :  * created.
     202             :  *
     203             :  * @param cls closure
     204             :  * @param http_status HTTP status code we got
     205             :  * @param json full response we got
     206             :  */
     207             : static void
     208          15 : proposal_lookup_initial_cb
     209             :   (void *cls,
     210             :    unsigned int http_status,
     211             :    const json_t *json,
     212             :    const json_t *contract_terms,
     213             :    const struct TALER_MerchantSignatureP *sig,
     214             :    const struct GNUNET_HashCode *hash)
     215             : {
     216          15 :   struct ProposalState *ps = cls;
     217             :   struct TALER_MerchantPublicKeyP merchant_pub;
     218             :   const char *error_name;
     219             :   unsigned int error_line;
     220             : 
     221          15 :   ps->plo = NULL;
     222          15 :   if (ps->http_status != http_status)
     223           0 :     TALER_TESTING_FAIL (ps->is);
     224             : 
     225          15 :   ps->contract_terms = json_dumps (contract_terms,
     226             :                                    JSON_COMPACT);
     227          15 :   ps->h_contract_terms = *hash;
     228          15 :   ps->merchant_sig = *sig;
     229             : 
     230             : 
     231          15 :   struct GNUNET_JSON_Specification spec[] = {
     232             :     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     233             :                                  &merchant_pub),
     234             :     GNUNET_JSON_spec_end()
     235             :   };
     236             : 
     237          15 :   if (GNUNET_OK !=
     238          15 :       GNUNET_JSON_parse (contract_terms,
     239             :                          spec,
     240             :                          &error_name,
     241             :                          &error_line))
     242             :   {
     243           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     244             :                 "Parser failed on %s:%u\n",
     245             :                 error_name,
     246             :                 error_line);
     247           0 :     fprintf (stderr, "%s\n", ps->contract_terms);
     248           0 :     TALER_TESTING_FAIL (ps->is);
     249             :   }
     250             : 
     251          15 :   ps->merchant_pub = merchant_pub;
     252             : 
     253          15 :   TALER_TESTING_interpreter_next (ps->is);
     254             : }
     255             : 
     256             : 
     257             : /**
     258             :  * Callback that processes the response following a
     259             :  * proposal's put.  NOTE: no contract terms are included
     260             :  * here; they need to be taken via the "proposal lookup"
     261             :  * method.
     262             :  *
     263             :  * @param cls closure.
     264             :  * @param http_status HTTP response code coming from
     265             :  *        the backend.
     266             :  * @param ec error code.
     267             :  * @param obj when successful, it has the format:
     268             :  *        '{"order_id": "<order_id>"}'
     269             :  * @param order_id order id of the proposal.
     270             :  */
     271             : static void
     272          20 : proposal_cb (void *cls,
     273             :              unsigned int http_status,
     274             :              enum TALER_ErrorCode ec,
     275             :              const json_t *obj,
     276             :              const char *order_id)
     277             : {
     278          20 :   struct ProposalState *ps = cls;
     279             : 
     280          20 :   ps->po = NULL;
     281             : 
     282          20 :   if (ps->http_status != http_status)
     283           0 :     TALER_TESTING_FAIL (ps->is);
     284             : 
     285          20 :   if (0 == ps->http_status)
     286             :   {
     287           2 :     TALER_LOG_DEBUG ("/proposal, expected 0 status code\n");
     288           2 :     TALER_TESTING_interpreter_next (ps->is);
     289           2 :     return;
     290             :   }
     291             : 
     292          18 :   switch (http_status)
     293             :   {
     294             :   case MHD_HTTP_OK:
     295          15 :     ps->order_id = GNUNET_strdup (order_id);
     296          15 :     break;
     297             :   default:
     298             :   {
     299           3 :     char *s = json_dumps (obj, JSON_COMPACT);
     300           3 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     301             :                 "Unexpected status code from /proposal:" \
     302             :                 " %u (%d). Command %s, response: %s\n",
     303             :                 http_status,
     304             :                 ec,
     305             :                 TALER_TESTING_interpreter_get_current_label (
     306             :                   ps->is),
     307             :                 s);
     308           3 :     GNUNET_free_non_null (s);
     309             :     /**
     310             :      * Not failing, as test cases are _supposed_
     311             :      * to create non 200 OK situations.
     312             :      */
     313           3 :     TALER_TESTING_interpreter_next (ps->is);
     314             :   }
     315           3 :   return;
     316             :   }
     317             : 
     318          15 :   if (NULL ==
     319          15 :      (ps->plo = TALER_MERCHANT_proposal_lookup
     320             :        (ps->ctx,
     321             :         ps->merchant_url,
     322             :         ps->order_id,
     323             :         ps->instance,
     324          15 :         &ps->nonce,
     325             :         &proposal_lookup_initial_cb,
     326             :         ps)))
     327           0 :     TALER_TESTING_FAIL (ps->is);
     328             : }
     329             : 
     330             : 
     331             : /**
     332             :  * Run a "proposal" CMD.
     333             :  *
     334             :  * @param cls closure.
     335             :  * @param cmd command currently being run.
     336             :  * @param is interpreter state.
     337             :  */
     338             : static void
     339          20 : proposal_run (void *cls,
     340             :               const struct TALER_TESTING_Command *cmd,
     341             :               struct TALER_TESTING_Interpreter *is)
     342             : {
     343          20 :   struct ProposalState *ps = cls;
     344             :   json_t *order;
     345             :   json_error_t error;
     346             : 
     347          20 :   ps->is = is;
     348          20 :   order = json_loads (ps->order,
     349             :                       JSON_REJECT_DUPLICATES,
     350             :                       &error);
     351          20 :   if (NULL == order)
     352             :   {
     353             :     // human error here.
     354           0 :     GNUNET_break (0);
     355           0 :     fprintf (stderr, "%s\n", error.text);
     356           0 :     TALER_TESTING_interpreter_fail (is); 
     357           0 :     return;
     358             :   }
     359             : 
     360          20 :   if (NULL == json_object_get (order,
     361             :                                "order_id"))
     362             :   {
     363             :     struct GNUNET_TIME_Absolute now;
     364             :     char *order_id;
     365             : 
     366           4 :     now = GNUNET_TIME_absolute_get (); 
     367             : 
     368           4 :     order_id = GNUNET_STRINGS_data_to_string_alloc
     369             :       (&now.abs_value_us,
     370             :        sizeof (now.abs_value_us));
     371             : 
     372           4 :     json_object_set (order,
     373             :                      "order_id",
     374             :                      json_string (order_id));
     375             : 
     376           4 :     GNUNET_free (order_id);
     377             :   }
     378             : 
     379          20 :   GNUNET_CRYPTO_random_block
     380             :     (GNUNET_CRYPTO_QUALITY_WEAK,
     381          20 :      &ps->nonce,
     382             :      sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
     383          20 :   if (NULL != ps->instance)
     384             :   {
     385             :     json_t *merchant;
     386          20 :     merchant = json_object ();
     387          20 :     json_object_set_new (merchant,
     388             :                          "instance",
     389             :                          json_string (ps->instance));
     390          20 :     json_object_set_new (order,
     391             :                          "merchant",
     392             :                          merchant);
     393             :   }
     394             : 
     395          20 :   ps->po = TALER_MERCHANT_order_put (ps->ctx,
     396             :                                      ps->merchant_url,
     397             :                                      order,
     398             :                                      &proposal_cb,
     399             :                                      ps);
     400          20 :   json_decref (order);
     401          20 :   GNUNET_assert (NULL != ps->po);
     402             : }
     403             : 
     404             : /**
     405             :  * Free the state of a "proposal" CMD, and possibly
     406             :  * cancel it if it did not complete.
     407             :  *
     408             :  * @param cls closure.
     409             :  * @param cmd command being freed.
     410             :  */
     411             : static void
     412          20 : proposal_cleanup (void *cls,
     413             :                   const struct TALER_TESTING_Command *cmd)
     414             : {
     415          20 :   struct ProposalState *ps = cls;
     416             : 
     417          20 :   if (NULL != ps->po)
     418             :   {
     419           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     420             :                 "Command '%s' did not complete (proposal put)\n",
     421             :                 cmd->label);
     422           0 :     TALER_MERCHANT_proposal_cancel (ps->po);
     423           0 :     ps->po = NULL;
     424             :   }
     425             : 
     426          20 :   if (NULL != ps->plo)
     427             :   {
     428           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     429             :                 "Command '%s' did not complete"
     430             :                 " (proposal lookup)\n",
     431             :                 cmd->label);
     432           0 :     TALER_MERCHANT_proposal_lookup_cancel (ps->plo);
     433           0 :     ps->plo = NULL;
     434             :   }
     435             : 
     436          20 :   GNUNET_free_non_null ((void *) ps->order_id);
     437          20 :   GNUNET_free_non_null ((void *) ps->contract_terms);
     438          20 :   GNUNET_free (ps);
     439          20 : }
     440             : 
     441             : /**
     442             :  * Free the state of a "proposal lookup" CMD, and possibly
     443             :  * cancel it if it did not complete.
     444             :  *
     445             :  * @param cls closure.
     446             :  * @param cmd command being freed.
     447             :  */
     448             : static void
     449           4 : proposal_lookup_cleanup (void *cls,
     450             :                          const struct TALER_TESTING_Command *cmd)
     451             : {
     452           4 :   struct ProposalLookupState *pls = cls;
     453             : 
     454           4 :   if (NULL != pls->plo)
     455             :   {
     456           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     457             :                 "Command '%s' did not complete\n",
     458             :                 cmd->label);
     459           0 :     TALER_MERCHANT_proposal_lookup_cancel (pls->plo);
     460             :   }
     461           4 :   GNUNET_free (pls);
     462           4 : }
     463             : 
     464             : 
     465             : /**
     466             :  * Make the "proposal" command.
     467             :  *
     468             :  * @param label command label
     469             :  * @param merchant_url base URL of the merchant serving
     470             :  *        the proposal request.
     471             :  * @param ctx CURL context.
     472             :  * @param http_status expected HTTP status.
     473             :  * @param order the order to PUT to the merchant.
     474             :  * @param instance merchant instance performing the operation.
     475             :  *
     476             :  * @return the command
     477             :  */
     478             : struct TALER_TESTING_Command
     479          20 : TALER_TESTING_cmd_proposal (const char *label,
     480             :                             const char *merchant_url,
     481             :                             struct GNUNET_CURL_Context *ctx,
     482             :                             unsigned int http_status,
     483             :                             const char *order,
     484             :                             const char *instance)
     485             : {
     486             :   struct TALER_TESTING_Command cmd;
     487             :   struct ProposalState *ps;
     488             : 
     489          20 :   ps = GNUNET_new (struct ProposalState);
     490          20 :   ps->order = order;
     491          20 :   ps->http_status = http_status;
     492          20 :   ps->ctx = ctx;
     493          20 :   ps->merchant_url = merchant_url;
     494          20 :   ps->instance = (NULL == instance) ? "default": instance;
     495             : 
     496          20 :   cmd.cls = ps;
     497          20 :   cmd.label = label;
     498          20 :   cmd.run = &proposal_run;
     499          20 :   cmd.cleanup = &proposal_cleanup;
     500          20 :   cmd.traits = &proposal_traits;
     501          20 :   return cmd;
     502             : }
     503             : 
     504             : /**
     505             :  * Callback for "proposal lookup" operation, to check the
     506             :  * response code is as expected.
     507             :  *
     508             :  * @param cls closure
     509             :  * @param http_status HTTP status code we got
     510             :  * @param json full response we got
     511             :  * @param contract_terms the contract terms; they are the
     512             :  *        backend-filled up proposal minus cryptographic
     513             :  *        information.
     514             :  * @param sig merchant signature over the contract terms.
     515             :  * @param hash hash code of the contract terms.
     516             :  */
     517             : static void
     518           4 : proposal_lookup_cb (void *cls,
     519             :                     unsigned int http_status,
     520             :                     const json_t *json,
     521             :                     const json_t *contract_terms,
     522             :                     const struct TALER_MerchantSignatureP *sig,
     523             :                     const struct GNUNET_HashCode *hash)
     524             : {
     525           4 :   struct ProposalLookupState *pls = cls;
     526             : 
     527           4 :   pls->plo = NULL;
     528           4 :   if (pls->http_status != http_status)
     529           0 :     TALER_TESTING_FAIL (pls->is);
     530             : 
     531           4 :   TALER_TESTING_interpreter_next (pls->is);
     532             : }
     533             : 
     534             : 
     535             : /**
     536             :  * Run the "proposal lookup" CMD.
     537             :  *
     538             :  * @param cls closure.
     539             :  * @param cmd command currently being run.
     540             :  * @param is interpreter state.
     541             :  */
     542             : static void
     543           4 : proposal_lookup_run (void *cls,
     544             :                      const struct TALER_TESTING_Command *cmd,
     545             :                      struct TALER_TESTING_Interpreter *is)
     546             : {
     547           4 :   struct ProposalLookupState *pls = cls;
     548             :   const struct TALER_TESTING_Command *proposal_cmd;
     549             :   const char *order_id;
     550             :   const struct GNUNET_CRYPTO_EddsaPublicKey *nonce;
     551             :   /* Only used if we do NOT use the nonce from traits.  */
     552             :   struct GNUNET_CRYPTO_EddsaPublicKey dummy_nonce;
     553             :   #define GET_TRAIT_NONCE(cmd,ptr) \
     554             :     TALER_TESTING_get_trait_peer_key_pub (cmd, 1, ptr)
     555             : 
     556           4 :   pls->is = is;
     557             : 
     558           4 :   if (NULL != pls->order_id)
     559             :   {
     560           2 :     order_id = pls->order_id;
     561           2 :     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
     562             :                                 &dummy_nonce,
     563             :                                 sizeof (dummy_nonce));
     564           2 :     nonce = &dummy_nonce;
     565             :   }
     566             :   else
     567             :   {
     568           2 :     proposal_cmd = TALER_TESTING_interpreter_lookup_command
     569             :       (is, pls->proposal_reference);
     570             : 
     571           2 :     if (NULL == proposal_cmd)
     572           0 :       TALER_TESTING_FAIL (is);
     573             : 
     574           2 :     if (GNUNET_OK != GET_TRAIT_NONCE (proposal_cmd,
     575             :                                       &nonce))
     576           0 :       TALER_TESTING_FAIL (is); 
     577             : 
     578           2 :     if (GNUNET_OK != TALER_TESTING_get_trait_order_id
     579             :         (proposal_cmd, 0, &order_id))
     580           0 :       TALER_TESTING_FAIL (is);
     581             :   }
     582           4 :   pls->plo = TALER_MERCHANT_proposal_lookup (pls->ctx,
     583             :                                              pls->merchant_url,
     584             :                                              order_id,
     585             :                                              "default",
     586             :                                              nonce,
     587             :                                              &proposal_lookup_cb,
     588             :                                              pls);
     589           4 :   GNUNET_assert (NULL != pls->plo);
     590             : }
     591             : 
     592             : 
     593             : /**
     594             :  * Make a "proposal lookup" command.
     595             :  *
     596             :  * @param label command label.
     597             :  * @param ctx CURL context.
     598             :  * @param merchant_url base URL of the merchant backend
     599             :  *        serving the proposal lookup request.
     600             :  * @param http_status expected HTTP response code.
     601             :  * @param proposal_reference reference to a "proposal" CMD.
     602             :  * @param order_id order id to lookup, can be NULL.
     603             :  *
     604             :  * @return the command.
     605             :  */
     606             : struct TALER_TESTING_Command
     607           4 : TALER_TESTING_cmd_proposal_lookup
     608             :   (const char *label,
     609             :    struct GNUNET_CURL_Context *ctx,
     610             :    const char *merchant_url,
     611             :    unsigned int http_status,
     612             :    const char *proposal_reference,
     613             :    const char *order_id)
     614             : {
     615             :   struct ProposalLookupState *pls;
     616             :   struct TALER_TESTING_Command cmd;
     617             :   
     618           4 :   pls = GNUNET_new (struct ProposalLookupState);
     619           4 :   pls->http_status = http_status;
     620           4 :   pls->proposal_reference = proposal_reference;
     621           4 :   pls->merchant_url = merchant_url;
     622           4 :   pls->ctx = ctx;
     623           4 :   pls->order_id = order_id;
     624             : 
     625           4 :   cmd.cls = pls;
     626           4 :   cmd.label = label;
     627           4 :   cmd.run = &proposal_lookup_run;
     628           4 :   cmd.cleanup = &proposal_lookup_cleanup;
     629             : 
     630           4 :   return cmd;
     631             : }

Generated by: LCOV version 1.13