LCOV - code coverage report
Current view: top level - lib - testing_api_cmd_proposal.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 98 138 71.0 %
Date: 2018-05-16 06:16:58 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             : struct ProposalState
      33             : {
      34             : 
      35             :   /**
      36             :    * The order.
      37             :    */
      38             :   const char *order;
      39             : 
      40             :   /**
      41             :    * Expected status code.
      42             :    */
      43             :   unsigned int http_status;
      44             : 
      45             :   /**
      46             :    * Order id.
      47             :    */
      48             :   const char *order_id;
      49             : 
      50             :   /**
      51             :    * Contract terms obtained from the backend.
      52             :    */
      53             :   const char *contract_terms;
      54             : 
      55             :   /**
      56             :    * Proposal data hash code.  Recall: proposal data is the part
      57             :    * of the contract terms without the signature of the merchant.
      58             :    */
      59             :   struct GNUNET_HashCode h_contract_terms;
      60             : 
      61             :   /**
      62             :    * The /proposal operation handle.
      63             :    */
      64             :   struct TALER_MERCHANT_ProposalOperation *po;
      65             : 
      66             :   /**
      67             :    * The (initial) /proposal/lookup operation handle.
      68             :    */
      69             :   struct TALER_MERCHANT_ProposalLookupOperation *plo;
      70             : 
      71             :   /**
      72             :    * The nonce.
      73             :    */
      74             :   struct GNUNET_CRYPTO_EddsaPublicKey nonce;
      75             : 
      76             :   /**
      77             :    * The merchant instance.
      78             :    */
      79             :   const char *instance;
      80             : 
      81             :   /**
      82             :    * URL of the merchant backend.
      83             :    */
      84             :   const char *merchant_url;
      85             : 
      86             :   /**
      87             :    * The curl context; used to be fed to the merchant lib.
      88             :    */
      89             :   struct GNUNET_CURL_Context *ctx;
      90             : 
      91             :   /**
      92             :    * The interpreter state.
      93             :    */
      94             :   struct TALER_TESTING_Interpreter *is;
      95             : 
      96             :   /**
      97             :    * Merchant signature over the proposal.
      98             :    */
      99             :   struct TALER_MerchantSignatureP merchant_sig;
     100             : 
     101             :   /**
     102             :    * Merchant public key.
     103             :    */
     104             :   struct TALER_MerchantPublicKeyP merchant_pub;
     105             : };
     106             : 
     107             : struct ProposalLookupState
     108             : {
     109             :   /**
     110             :    * The interpreter state.
     111             :    */
     112             :   struct TALER_TESTING_Interpreter *is;
     113             : 
     114             :   /**
     115             :    * URL of the merchant backend.
     116             :    */
     117             :   const char *merchant_url;
     118             : 
     119             :   /**
     120             :    * The curl context; used to be fed to the merchant lib.
     121             :    */
     122             :   struct GNUNET_CURL_Context *ctx;
     123             : 
     124             :   /**
     125             :    * Expected status code.
     126             :    */
     127             :   unsigned int http_status;
     128             : 
     129             :   /**
     130             :    * The (initial) /proposal/lookup operation handle.
     131             :    */
     132             :   struct TALER_MERCHANT_ProposalLookupOperation *plo;
     133             : 
     134             :   /**
     135             :    * Reference to a proposal operation.
     136             :    */
     137             :   const char *proposal_reference;
     138             : 
     139             :   const char *order_id;
     140             : };
     141             : 
     142             : /**
     143             :  * Extract information from a command that is useful for other
     144             :  * commands.
     145             :  *
     146             :  * @param cls closure
     147             :  * @param ret[out] result (could be anything)
     148             :  * @param trait name of the trait
     149             :  * @param selector more detailed information about which object
     150             :  *                 to return in case there were multiple generated
     151             :  *                 by the command
     152             :  * @return #GNUNET_OK on success
     153             :  */
     154             : static int
     155          72 : proposal_traits (void *cls,
     156             :                  void **ret,
     157             :                  const char *trait,
     158             :                  unsigned int index)
     159             : {
     160             : 
     161          72 :   struct ProposalState *ps = cls;
     162             :   #define MAKE_TRAIT_NONCE(ptr) \
     163             :     TALER_TESTING_make_trait_peer_key_pub (1, ptr)
     164             : 
     165         432 :   struct TALER_TESTING_Trait traits[] = {
     166          72 :     TALER_TESTING_make_trait_order_id (0, ps->order_id),
     167          72 :     TALER_TESTING_make_trait_contract_terms
     168             :       (0, ps->contract_terms),
     169             :     TALER_TESTING_make_trait_h_contract_terms
     170          72 :       (0, &ps->h_contract_terms),
     171          72 :     TALER_TESTING_make_trait_merchant_sig (0, &ps->merchant_sig),
     172          72 :     TALER_TESTING_make_trait_peer_key_pub
     173             :       (0, &ps->merchant_pub.eddsa_pub),
     174          72 :     MAKE_TRAIT_NONCE (&ps->nonce),
     175             :     TALER_TESTING_trait_end ()
     176             :   };
     177             :   
     178          72 :   return TALER_TESTING_get_trait (traits,
     179             :                                   ret,
     180             :                                   trait,
     181             :                                   index);
     182             :   return GNUNET_SYSERR;
     183             : }
     184             : 
     185             : 
     186             : /**
     187             :  * Used to initialize the proposal after it was created.
     188             :  *
     189             :  * @param cls closure
     190             :  * @param http_status HTTP status code we got
     191             :  * @param json full response we got
     192             :  */
     193             : static void
     194           5 : proposal_lookup_initial_cb
     195             :   (void *cls,
     196             :    unsigned int http_status,
     197             :    const json_t *json,
     198             :    const json_t *contract_terms,
     199             :    const struct TALER_MerchantSignatureP *sig,
     200             :    const struct GNUNET_HashCode *hash)
     201             : {
     202           5 :   struct ProposalState *ps = cls;
     203             :   struct TALER_MerchantPublicKeyP merchant_pub;
     204             :   const char *error_name;
     205             :   unsigned int error_line;
     206             : 
     207           5 :   ps->plo = NULL;
     208           5 :   if (ps->http_status != http_status)
     209           0 :     TALER_TESTING_FAIL (ps->is);
     210             : 
     211           5 :   ps->contract_terms = json_dumps (contract_terms,
     212             :                                    JSON_COMPACT);
     213           5 :   ps->h_contract_terms = *hash;
     214           5 :   ps->merchant_sig = *sig;
     215             : 
     216             : 
     217           5 :   struct GNUNET_JSON_Specification spec[] = {
     218             :     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     219             :                                  &merchant_pub),
     220             :     GNUNET_JSON_spec_end()
     221             :   };
     222             : 
     223           5 :   if (GNUNET_OK !=
     224           5 :       GNUNET_JSON_parse (contract_terms,
     225             :                          spec,
     226             :                          &error_name,
     227             :                          &error_line))
     228             :   {
     229           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     230             :                 "Parser failed on %s:%u\n",
     231             :                 error_name,
     232             :                 error_line);
     233           0 :     fprintf (stderr, "%s\n", ps->contract_terms);
     234           0 :     TALER_TESTING_FAIL (ps->is);
     235             :   }
     236             : 
     237           5 :   ps->merchant_pub = merchant_pub;
     238             : 
     239           5 :   TALER_TESTING_interpreter_next (ps->is);
     240             : }
     241             : 
     242             : 
     243             : /**
     244             :  * Callback that processes the response following a
     245             :  * proposal's put.  NOTE: no contract terms are included
     246             :  * here; they need to be taken via the "proposal lookup"
     247             :  * method.
     248             :  *
     249             :  * @param cls closure.
     250             :  * @param http_status HTTP response code coming from
     251             :  *        the backend.
     252             :  * @param ec error code.
     253             :  * @param obj when successful, it matches the format:
     254             :  *        '{"order_id": "<order_id>"}'
     255             :  */
     256             : 
     257             : static void
     258           5 : proposal_cb (void *cls,
     259             :              unsigned int http_status,
     260             :              enum TALER_ErrorCode ec,
     261             :              const json_t *obj,
     262             :              const char *order_id)
     263             : {
     264           5 :   struct ProposalState *ps = cls;
     265             : 
     266           5 :   ps->po = NULL;
     267             : 
     268           5 :   if (ps->http_status != http_status)
     269           0 :     TALER_TESTING_FAIL (ps->is);
     270             : 
     271           5 :   if (0 == ps->http_status)
     272             :   {
     273           0 :     TALER_LOG_DEBUG ("/proposal, expected 0 status code\n");
     274           0 :     TALER_TESTING_interpreter_next (ps->is);
     275           0 :     return;
     276             :   }
     277             : 
     278           5 :   switch (http_status)
     279             :   {
     280             :   case MHD_HTTP_OK:
     281           5 :     ps->order_id = GNUNET_strdup (order_id);
     282           5 :     break;
     283             :   default:
     284             :   {
     285           0 :     char *s = json_dumps (obj, JSON_COMPACT);
     286           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     287             :                 "Unexpected status code from /proposal:" \
     288             :                 " %u (%d). Command %s, response: %s\n",
     289             :                 http_status,
     290             :                 ec,
     291             :                 TALER_TESTING_interpreter_get_current_label (
     292             :                   ps->is),
     293             :                 s);
     294           0 :     GNUNET_free_non_null (s);
     295             :     /**
     296             :      * Not failing, as test cases are _supposed_
     297             :      * to create non 200 OK situations.
     298             :      */
     299           0 :     TALER_TESTING_interpreter_next (ps->is);
     300             :   }
     301           0 :   return;
     302             :   }
     303             : 
     304           5 :   if (NULL ==
     305           5 :      (ps->plo = TALER_MERCHANT_proposal_lookup
     306             :        (ps->ctx,
     307             :         ps->merchant_url,
     308             :         ps->order_id,
     309             :         "default",
     310           5 :         &ps->nonce,
     311             :         &proposal_lookup_initial_cb,
     312             :         ps)))
     313           0 :     TALER_TESTING_FAIL (ps->is);
     314             : }
     315             : 
     316             : 
     317             : /**
     318             :  * Runs the command.  Note that upon return, the interpreter
     319             :  * will not automatically run the next command, as the command
     320             :  * may continue asynchronously in other scheduler tasks.  Thus,
     321             :  * the command must ensure to eventually call
     322             :  * #TALER_TESTING_interpreter_next() or
     323             :  * #TALER_TESTING_interpreter_fail().
     324             :  *
     325             :  * @param is interpreter state
     326             :  */
     327             : static void
     328           5 : proposal_run (void *cls,
     329             :               const struct TALER_TESTING_Command *cmd,
     330             :               struct TALER_TESTING_Interpreter *is)
     331             : {
     332           5 :   struct ProposalState *ps = cls;
     333             :   json_t *order;
     334             :   json_error_t error;
     335             : 
     336           5 :   ps->is = is;
     337           5 :   order = json_loads (ps->order,
     338             :                       JSON_REJECT_DUPLICATES,
     339             :                       &error);
     340           5 :   if (NULL == order)
     341             :   {
     342             :     // human error here.
     343           0 :     GNUNET_break (0);
     344           0 :     fprintf (stderr, "%s\n", error.text);
     345           0 :     TALER_TESTING_interpreter_fail (is); 
     346           0 :     return;
     347             :   }
     348             : 
     349           5 :   if (NULL == json_object_get (order,
     350             :                                "order_id"))
     351             :   {
     352             :     struct GNUNET_TIME_Absolute now;
     353             :     char *order_id;
     354             : 
     355           0 :     now = GNUNET_TIME_absolute_get (); 
     356             : 
     357           0 :     order_id = GNUNET_STRINGS_data_to_string_alloc
     358             :       (&now.abs_value_us,
     359             :        sizeof (now.abs_value_us));
     360             : 
     361           0 :     json_object_set (order,
     362             :                      "order_id",
     363             :                      json_string (order_id));
     364             : 
     365           0 :     GNUNET_free (order_id);
     366             :   }
     367             : 
     368           5 :   GNUNET_CRYPTO_random_block
     369             :     (GNUNET_CRYPTO_QUALITY_WEAK,
     370           5 :      &ps->nonce,
     371             :      sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
     372           5 :   if (NULL != ps->instance)
     373             :   {
     374             :     json_t *merchant;
     375           0 :     merchant = json_object ();
     376           0 :     json_object_set_new (merchant,
     377             :                          "instance",
     378             :                          json_string (ps->instance));
     379           0 :     json_object_set_new (order,
     380             :                          "merchant",
     381             :                          merchant);
     382             :   }
     383             : 
     384           5 :   ps->po = TALER_MERCHANT_order_put (ps->ctx,
     385             :                                      ps->merchant_url,
     386             :                                      order,
     387             :                                      &proposal_cb,
     388             :                                      ps);
     389           5 :   json_decref (order);
     390           5 :   GNUNET_assert (NULL != ps->po);
     391             : }
     392             : 
     393             : /**
     394             :  * Clean up after the command.  Run during forced termination
     395             :  * (CTRL-C) or test failure or test success.
     396             :  *
     397             :  * @param cls closure
     398             :  */
     399             : static void
     400           5 : proposal_cleanup (void *cls,
     401             :                   const struct TALER_TESTING_Command *cmd)
     402             : {
     403           5 :   struct ProposalState *ps = cls;
     404             : 
     405           5 :   if (NULL != ps->po)
     406             :   {
     407           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     408             :                 "Command '%s' did not complete (proposal put)\n",
     409             :                 cmd->label);
     410           0 :     TALER_MERCHANT_proposal_cancel (ps->po);
     411           0 :     ps->po = NULL;
     412             :   }
     413             : 
     414           5 :   if (NULL != ps->plo)
     415             :   {
     416           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     417             :                 "Command '%s' did not complete"
     418             :                 " (proposal lookup)\n",
     419             :                 cmd->label);
     420           0 :     TALER_MERCHANT_proposal_lookup_cancel (ps->plo);
     421           0 :     ps->plo = NULL;
     422             :   }
     423             : 
     424           5 :   GNUNET_free_non_null ((void *) ps->order_id);
     425           5 :   GNUNET_free_non_null ((void *) ps->contract_terms);
     426           5 :   GNUNET_free (ps);
     427           5 : }
     428             : 
     429             : /**
     430             :  * Clean up after the command.  Run during forced termination
     431             :  * (CTRL-C) or test failure or test success.
     432             :  *
     433             :  * @param cls closure
     434             :  */
     435             : static void
     436           1 : proposal_lookup_cleanup (void *cls,
     437             :                          const struct TALER_TESTING_Command *cmd)
     438             : {
     439           1 :   struct ProposalLookupState *pls = cls;
     440             : 
     441           1 :   if (NULL != pls->plo)
     442             :   {
     443           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     444             :                 "Command '%s' did not complete\n",
     445             :                 cmd->label);
     446           0 :     TALER_MERCHANT_proposal_lookup_cancel (pls->plo);
     447             :   }
     448           1 :   GNUNET_free (pls);
     449           1 : }
     450             : 
     451             : 
     452             : /**
     453             :  * Make the /proposal command.
     454             :  *
     455             :  * @param label command label
     456             :  * @param merchant_reference label to the merchant command.  Used
     457             :  *        to get its base url.
     458             :  * @param ctx context
     459             :  * @param http_status HTTP status code.
     460             :  * @param order the order
     461             :  * @param instance the merchant instance
     462             :  * @param merchant_url the merchant backend (base) url
     463             :  *
     464             :  * @return the command
     465             :  */
     466             : struct TALER_TESTING_Command
     467           5 : TALER_TESTING_cmd_proposal (const char *label,
     468             :                             const char *merchant_url,
     469             :                             struct GNUNET_CURL_Context *ctx,
     470             :                             unsigned int http_status,
     471             :                             const char *order,
     472             :                             const char *instance)
     473             : {
     474             :   struct TALER_TESTING_Command cmd;
     475             :   struct ProposalState *ps;
     476             : 
     477           5 :   ps = GNUNET_new (struct ProposalState);
     478           5 :   ps->order = order;
     479           5 :   ps->http_status = http_status;
     480           5 :   ps->ctx = ctx;
     481           5 :   ps->merchant_url = merchant_url;
     482           5 :   ps->instance = instance;
     483             : 
     484           5 :   cmd.cls = ps;
     485           5 :   cmd.label = label;
     486           5 :   cmd.run = &proposal_run;
     487           5 :   cmd.cleanup = &proposal_cleanup;
     488           5 :   cmd.traits = &proposal_traits;
     489           5 :   return cmd;
     490             : }
     491             : 
     492             : /**
     493             :  * Callback for GET /proposal issued at backend. Just check
     494             :  * whether response code is as expected.
     495             :  *
     496             :  * @param cls closure
     497             :  * @param http_status HTTP status code we got
     498             :  * @param json full response we got
     499             :  */
     500             : static void
     501           1 : proposal_lookup_cb (void *cls,
     502             :                     unsigned int http_status,
     503             :                     const json_t *json,
     504             :                     const json_t *contract_terms,
     505             :                     const struct TALER_MerchantSignatureP *sig,
     506             :                     const struct GNUNET_HashCode *hash)
     507             : {
     508           1 :   struct ProposalLookupState *pls = cls;
     509             : 
     510           1 :   pls->plo = NULL;
     511           1 :   if (pls->http_status != http_status)
     512           0 :     TALER_TESTING_FAIL (pls->is);
     513             : 
     514           1 :   TALER_TESTING_interpreter_next (pls->is);
     515             : }
     516             : 
     517             : 
     518             : /**
     519             :  * Runs the command.  Note that upon return, the interpreter
     520             :  * will not automatically run the next command, as the command
     521             :  * may continue asynchronously in other scheduler tasks.  Thus,
     522             :  * the command must ensure to eventually call
     523             :  * #TALER_TESTING_interpreter_next() or
     524             :  * #TALER_TESTING_interpreter_fail().
     525             :  *
     526             :  * @param is interpreter state
     527             :  */
     528             : static void
     529           1 : proposal_lookup_run (void *cls,
     530             :                      const struct TALER_TESTING_Command *cmd,
     531             :                      struct TALER_TESTING_Interpreter *is)
     532             : {
     533           1 :   struct ProposalLookupState *pls = cls;
     534             :   const struct TALER_TESTING_Command *proposal_cmd;
     535             :   const char *order_id;
     536             :   const struct GNUNET_CRYPTO_EddsaPublicKey *nonce;
     537             :   /* Only used if we do NOT use the nonce from traits.  */
     538             :   struct GNUNET_CRYPTO_EddsaPublicKey dummy_nonce;
     539             :   #define GET_TRAIT_NONCE(cmd,ptr) \
     540             :     TALER_TESTING_get_trait_peer_key_pub (cmd, 1, ptr)
     541             : 
     542           1 :   pls->is = is;
     543             : 
     544           1 :   if (NULL != pls->order_id)
     545             :   {
     546           0 :     order_id = pls->order_id;
     547           0 :     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
     548             :                                 &dummy_nonce,
     549             :                                 sizeof (dummy_nonce));
     550           0 :     nonce = &dummy_nonce;
     551             :   }
     552             :   else
     553             :   {
     554           1 :     proposal_cmd = TALER_TESTING_interpreter_lookup_command
     555             :       (is, pls->proposal_reference);
     556             : 
     557           1 :     if (NULL == proposal_cmd)
     558           0 :       TALER_TESTING_FAIL (is);
     559             : 
     560           1 :     if (GNUNET_OK != GET_TRAIT_NONCE (proposal_cmd,
     561             :                                       &nonce))
     562           0 :       TALER_TESTING_FAIL (is); 
     563             : 
     564           1 :     if (GNUNET_OK != TALER_TESTING_get_trait_order_id
     565             :         (proposal_cmd, 0, &order_id))
     566           0 :       TALER_TESTING_FAIL (is);
     567             :   }
     568           1 :   pls->plo = TALER_MERCHANT_proposal_lookup (pls->ctx,
     569             :                                              pls->merchant_url,
     570             :                                              order_id,
     571             :                                              "default",
     572             :                                              nonce,
     573             :                                              &proposal_lookup_cb,
     574             :                                              pls);
     575           1 :   GNUNET_assert (NULL != pls->plo);
     576             : }
     577             : 
     578             : 
     579             : /**
     580             :  * Make a "proposal lookup" command.
     581             :  *
     582             :  * @param label command label
     583             :  * @param 
     584             :  */
     585             : struct TALER_TESTING_Command
     586           1 : TALER_TESTING_cmd_proposal_lookup
     587             :   (const char *label,
     588             :    struct GNUNET_CURL_Context *ctx,
     589             :    const char *merchant_url,
     590             :    unsigned int http_status,
     591             :    const char *proposal_reference,
     592             :    const char *order_id)
     593             : {
     594             :   struct ProposalLookupState *pls;
     595             :   struct TALER_TESTING_Command cmd;
     596             :   
     597           1 :   pls = GNUNET_new (struct ProposalLookupState);
     598           1 :   pls->http_status = http_status;
     599           1 :   pls->proposal_reference = proposal_reference;
     600           1 :   pls->merchant_url = merchant_url;
     601           1 :   pls->ctx = ctx;
     602           1 :   pls->order_id = order_id;
     603             : 
     604           1 :   cmd.cls = pls;
     605           1 :   cmd.label = label;
     606           1 :   cmd.run = &proposal_lookup_run;
     607           1 :   cmd.cleanup = &proposal_lookup_cleanup;
     608             : 
     609           1 :   return cmd;
     610             : }

Generated by: LCOV version 1.13