LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_post_using_templates.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 108 157 68.8 %
Date: 2025-06-23 16:22:09 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2022-2023 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             :  * @file testing_api_cmd_post_using_templates.c
      21             :  * @brief command to test POST /using-templates
      22             :  * @author Priscilla HUANG
      23             :  */
      24             : #include "platform.h"
      25             : #include <taler/taler_exchange_service.h>
      26             : #include <taler/taler_testing_lib.h>
      27             : #include "taler_merchant_service.h"
      28             : #include "taler_merchant_testing_lib.h"
      29             : 
      30             : 
      31             : /**
      32             :  * State of a "POST /templates" CMD.
      33             :  */
      34             : struct PostUsingTemplatesState
      35             : {
      36             : 
      37             :   /**
      38             :    * Handle for a "GET using-template" request.
      39             :    */
      40             :   struct TALER_MERCHANT_UsingTemplatesPostHandle *iph;
      41             : 
      42             :   /**
      43             :    * The interpreter state.
      44             :    */
      45             :   struct TALER_TESTING_Interpreter *is;
      46             : 
      47             :   /**
      48             :    * The (initial) POST /orders/$ID/claim operation handle.
      49             :    * The logic is such that after an order creation,
      50             :    * we immediately claim the order.
      51             :    */
      52             :   struct TALER_MERCHANT_OrderClaimHandle *och;
      53             : 
      54             :   /**
      55             :    * Base URL of the merchant serving the request.
      56             :    */
      57             :   const char *merchant_url;
      58             : 
      59             :   /**
      60             :    * ID of the using template to run.
      61             :    */
      62             :   const char *using_template_id;
      63             : 
      64             :   /**
      65             :    * Summary given by the customer.
      66             :    */
      67             :   const char *summary;
      68             : 
      69             :   /**
      70             :    * Amount given by the customer.
      71             :    */
      72             :   struct TALER_Amount amount;
      73             : 
      74             :   /**
      75             :    * Label of a command that created the template we should use.
      76             :    */
      77             :   const char *template_ref;
      78             : 
      79             :   /**
      80             :    * Order id.
      81             :    */
      82             :   char *order_id;
      83             : 
      84             :   /**
      85             :    * The order id we expect the merchant to assign (if not NULL).
      86             :    */
      87             :   const char *expected_order_id;
      88             : 
      89             :   /**
      90             :    * Contract terms obtained from the backend.
      91             :    */
      92             :   json_t *contract_terms;
      93             : 
      94             :   /**
      95             :    * Order submitted to the backend.
      96             :    */
      97             :   json_t *order_terms;
      98             : 
      99             :   /**
     100             :    * Contract terms hash code.
     101             :    */
     102             :   struct TALER_PrivateContractHashP h_contract_terms;
     103             : 
     104             :   /**
     105             :    * Merchant signature over the orders.
     106             :    */
     107             :   struct TALER_MerchantSignatureP merchant_sig;
     108             : 
     109             :   /**
     110             :    * Merchant public key.
     111             :    */
     112             :   struct TALER_MerchantPublicKeyP merchant_pub;
     113             : 
     114             :   /**
     115             :    * The nonce.
     116             :    */
     117             :   struct GNUNET_CRYPTO_EddsaPublicKey nonce;
     118             : 
     119             :   /**
     120             :    * The claim token
     121             :    */
     122             :   struct TALER_ClaimTokenP claim_token;
     123             : 
     124             :   /**
     125             :    * Should the command also CLAIM the order?
     126             :    */
     127             :   bool with_claim;
     128             : 
     129             :   /**
     130             :    * If not NULL, the command should duplicate the request and verify the
     131             :    * response is the same as in this command.
     132             :    */
     133             :   const char *duplicate_of;
     134             : 
     135             :   /**
     136             :    * Label of command creating/updating OTP device, or NULL.
     137             :    */
     138             :   const char *otp_ref;
     139             : 
     140             :   /**
     141             :    * Encoded key for the payment verification.
     142             :    */
     143             :   const char *otp_key;
     144             : 
     145             :   /**
     146             :    * Option that add amount of the order
     147             :    */
     148             :   const enum TALER_MerchantConfirmationAlgorithm *otp_alg;
     149             : 
     150             :   /**
     151             :    * Expected HTTP response code.
     152             :    */
     153             :   unsigned int http_status;
     154             : 
     155             : };
     156             : 
     157             : /**
     158             :  * Used to fill the "using_template" CMD state with backend-provided
     159             :  * values.  Also double-checks that the using_template was correctly
     160             :  * created.
     161             :  *
     162             :  * @param cls closure
     163             :  * @param ocr response we got
     164             :  */
     165             : static void
     166           4 : using_claim_cb (void *cls,
     167             :                 const struct TALER_MERCHANT_OrderClaimResponse *ocr)
     168             : {
     169           4 :   struct PostUsingTemplatesState *tis = cls;
     170             :   const char *error_name;
     171             :   unsigned int error_line;
     172             :   struct GNUNET_JSON_Specification spec[] = {
     173           4 :     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     174             :                                  &tis->merchant_pub),
     175           4 :     GNUNET_JSON_spec_end ()
     176             :   };
     177             : 
     178           4 :   tis->och = NULL;
     179           4 :   if (tis->http_status != ocr->hr.http_status)
     180             :   {
     181           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     182             :                 "Expected status %u, got %u\n",
     183             :                 tis->http_status,
     184             :                 ocr->hr.http_status);
     185           0 :     TALER_TESTING_FAIL (tis->is);
     186             :   }
     187           4 :   if (MHD_HTTP_OK != ocr->hr.http_status)
     188             :   {
     189           0 :     TALER_TESTING_interpreter_next (tis->is);
     190           0 :     return;
     191             :   }
     192           8 :   tis->contract_terms = json_deep_copy (
     193           4 :     (json_t *) ocr->details.ok.contract_terms);
     194           4 :   tis->h_contract_terms = ocr->details.ok.h_contract_terms;
     195           4 :   tis->merchant_sig = ocr->details.ok.sig;
     196           4 :   if (GNUNET_OK !=
     197           4 :       GNUNET_JSON_parse (tis->contract_terms,
     198             :                          spec,
     199             :                          &error_name,
     200             :                          &error_line))
     201             :   {
     202             :     char *log;
     203             : 
     204           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     205             :                 "Parser failed on %s:%u\n",
     206             :                 error_name,
     207             :                 error_line);
     208           0 :     log = json_dumps (tis->contract_terms,
     209             :                       JSON_INDENT (1));
     210           0 :     fprintf (stderr,
     211             :              "%s\n",
     212             :              log);
     213           0 :     free (log);
     214           0 :     TALER_TESTING_FAIL (tis->is);
     215             :   }
     216           4 :   TALER_TESTING_interpreter_next (tis->is);
     217             : }
     218             : 
     219             : 
     220             : /**
     221             :  * Callback for a POST /using-templates operation.
     222             :  *
     223             :  * @param cls closure for this function
     224             :  * @param por response being processed
     225             :  */
     226             : static void
     227          14 : post_using_templates_cb (void *cls,
     228             :                          const struct TALER_MERCHANT_PostOrdersReply *por)
     229             : {
     230          14 :   struct PostUsingTemplatesState *tis = cls;
     231             : 
     232          14 :   tis->iph = NULL;
     233          14 :   if (tis->http_status != por->hr.http_status)
     234             :   {
     235           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     236             :                 "Unexpected response code %u (%d) to command %s\n",
     237             :                 por->hr.http_status,
     238             :                 (int) por->hr.ec,
     239             :                 TALER_TESTING_interpreter_get_current_label (tis->is));
     240           0 :     TALER_TESTING_interpreter_fail (tis->is);
     241           0 :     return;
     242             :   }
     243          14 :   if (0 == tis->http_status)
     244             :   {
     245           0 :     TALER_LOG_DEBUG ("/using_templates, expected 0 status code\n");
     246           0 :     TALER_TESTING_interpreter_next (tis->is);
     247           0 :     return;
     248             :   }
     249             :   // check for order
     250          14 :   switch (por->hr.http_status)
     251             :   {
     252           4 :   case MHD_HTTP_OK:
     253           4 :     if (NULL != por->details.ok.token)
     254           4 :       tis->claim_token = *por->details.ok.token;
     255           4 :     tis->order_id = GNUNET_strdup (por->details.ok.order_id);
     256           4 :     if ((NULL != tis->expected_order_id) &&
     257           0 :         (0 != strcmp (por->details.ok.order_id,
     258             :                       tis->expected_order_id)))
     259             :     {
     260           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     261             :                   "Order id assigned does not match\n");
     262           0 :       TALER_TESTING_interpreter_fail (tis->is);
     263           0 :       return;
     264             :     }
     265           4 :     if (NULL != tis->duplicate_of)
     266             :     {
     267             :       const struct TALER_TESTING_Command *order_cmd;
     268             :       const struct TALER_ClaimTokenP *prev_token;
     269           0 :       struct TALER_ClaimTokenP zero_token = {0};
     270             : 
     271           0 :       order_cmd = TALER_TESTING_interpreter_lookup_command (
     272             :         tis->is,
     273             :         tis->duplicate_of);
     274           0 :       if (GNUNET_OK !=
     275           0 :           TALER_TESTING_get_trait_claim_token (order_cmd,
     276             :                                                &prev_token))
     277             :       {
     278           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     279             :                     "Could not fetch previous order claim token\n");
     280           0 :         TALER_TESTING_interpreter_fail (tis->is);
     281           0 :         return;
     282             :       }
     283           0 :       if (NULL == por->details.ok.token)
     284           0 :         prev_token = &zero_token;
     285           0 :       if (0 != GNUNET_memcmp (prev_token,
     286             :                               por->details.ok.token))
     287             :       {
     288           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     289             :                     "Claim tokens for identical requests do not match\n");
     290           0 :         TALER_TESTING_interpreter_fail (tis->is);
     291           0 :         return;
     292             :       }
     293             :     }
     294           4 :     break;
     295           2 :   case MHD_HTTP_NOT_FOUND:
     296           2 :     TALER_TESTING_interpreter_next (tis->is);
     297           2 :     return;
     298           0 :   case MHD_HTTP_GONE:
     299           0 :     TALER_TESTING_interpreter_next (tis->is);
     300           0 :     return;
     301           8 :   case MHD_HTTP_CONFLICT:
     302           8 :     TALER_TESTING_interpreter_next (tis->is);
     303           8 :     return;
     304           0 :   default:
     305             :     {
     306           0 :       char *s = json_dumps (por->hr.reply,
     307             :                             JSON_COMPACT);
     308           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     309             :                   "Unexpected status code from /orders: %u (%d) at %s; JSON: %s\n",
     310             :                   por->hr.http_status,
     311             :                   (int) por->hr.ec,
     312             :                   TALER_TESTING_interpreter_get_current_label (tis->is),
     313             :                   s);
     314           0 :       GNUNET_free (s);
     315             :       /**
     316             :        * Not failing, as test cases are _supposed_
     317             :        * to create non 200 OK situations.
     318             :        */
     319           0 :       TALER_TESTING_interpreter_next (tis->is);
     320             :     }
     321           0 :     return;
     322             :   }
     323             : 
     324           4 :   if (! tis->with_claim)
     325             :   {
     326           0 :     TALER_TESTING_interpreter_next (tis->is);
     327           0 :     return;
     328             :   }
     329           4 :   if (NULL ==
     330           4 :       (tis->och = TALER_MERCHANT_order_claim (
     331             :          TALER_TESTING_interpreter_get_context (tis->is),
     332             :          tis->merchant_url,
     333           4 :          tis->order_id,
     334           4 :          &tis->nonce,
     335           4 :          &tis->claim_token,
     336             :          &using_claim_cb,
     337             :          tis)))
     338           0 :     TALER_TESTING_FAIL (tis->is);
     339             : }
     340             : 
     341             : 
     342             : /**
     343             :  * Run the "POST /using-templates" CMD.
     344             :  *
     345             :  *
     346             :  * @param cls closure.
     347             :  * @param cmd command being run now.
     348             :  * @param is interpreter state.
     349             :  */
     350             : static void
     351          14 : post_using_templates_run (void *cls,
     352             :                           const struct TALER_TESTING_Command *cmd,
     353             :                           struct TALER_TESTING_Interpreter *is)
     354             : {
     355          14 :   struct PostUsingTemplatesState *tis = cls;
     356             :   const struct TALER_TESTING_Command *ref;
     357             :   const char *template_id;
     358             : 
     359          14 :   tis->is = is;
     360          14 :   ref = TALER_TESTING_interpreter_lookup_command (is,
     361             :                                                   tis->template_ref);
     362          14 :   if (GNUNET_OK !=
     363          14 :       TALER_TESTING_get_trait_template_id (ref,
     364             :                                            &template_id))
     365           0 :     TALER_TESTING_FAIL (is);
     366          14 :   if (NULL != tis->otp_ref)
     367             :   {
     368           2 :     ref = TALER_TESTING_interpreter_lookup_command (is,
     369             :                                                     tis->otp_ref);
     370           2 :     if (GNUNET_OK !=
     371           2 :         TALER_TESTING_get_trait_otp_key (ref,
     372             :                                          &tis->otp_key))
     373           0 :       TALER_TESTING_FAIL (is);
     374           2 :     if (GNUNET_OK !=
     375           2 :         TALER_TESTING_get_trait_otp_alg (ref,
     376             :                                          &tis->otp_alg))
     377           0 :       TALER_TESTING_FAIL (is);
     378             :   }
     379          14 :   tis->iph = TALER_MERCHANT_using_templates_post (
     380             :     TALER_TESTING_interpreter_get_context (is),
     381             :     tis->merchant_url,
     382             :     template_id,
     383             :     tis->summary,
     384          14 :     TALER_amount_is_valid (&tis->amount)
     385             :     ? &tis->amount
     386             :     : NULL,
     387             :     &post_using_templates_cb,
     388             :     tis);
     389          14 :   GNUNET_assert (NULL != tis->iph);
     390             : }
     391             : 
     392             : 
     393             : /**
     394             :  * Offers information from the POST /using-templates CMD state to other
     395             :  * commands.
     396             :  *
     397             :  * @param cls closure
     398             :  * @param[out] ret result (could be anything)
     399             :  * @param trait name of the trait
     400             :  * @param index index number of the object to extract.
     401             :  * @return #GNUNET_OK on success
     402             :  */
     403             : static enum GNUNET_GenericReturnValue
     404          20 : post_using_templates_traits (void *cls,
     405             :                              const void **ret,
     406             :                              const char *trait,
     407             :                              unsigned int index)
     408             : {
     409          20 :   struct PostUsingTemplatesState *pts = cls;
     410             :   struct TALER_TESTING_Trait traits[] = {
     411          20 :     TALER_TESTING_make_trait_order_id (pts->order_id),
     412          20 :     TALER_TESTING_make_trait_contract_terms (pts->contract_terms),
     413          20 :     TALER_TESTING_make_trait_order_terms (pts->order_terms),
     414          20 :     TALER_TESTING_make_trait_h_contract_terms (&pts->h_contract_terms),
     415          20 :     TALER_TESTING_make_trait_merchant_sig (&pts->merchant_sig),
     416          20 :     TALER_TESTING_make_trait_merchant_pub (&pts->merchant_pub),
     417          20 :     TALER_TESTING_make_trait_claim_nonce (&pts->nonce),
     418          20 :     TALER_TESTING_make_trait_claim_token (&pts->claim_token),
     419          20 :     TALER_TESTING_make_trait_otp_key (pts->otp_key),
     420          20 :     TALER_TESTING_make_trait_otp_alg (pts->otp_alg),
     421          20 :     TALER_TESTING_trait_end (),
     422             :   };
     423             : 
     424             :   (void) pts;
     425          20 :   return TALER_TESTING_get_trait (traits,
     426             :                                   ret,
     427             :                                   trait,
     428             :                                   index);
     429             : }
     430             : 
     431             : 
     432             : /**
     433             :  * Free the state of a "POST using-template" CMD, and possibly
     434             :  * cancel a pending operation thereof.
     435             :  *
     436             :  * @param cls closure.
     437             :  * @param cmd command being run.
     438             :  */
     439             : static void
     440          14 : post_using_templates_cleanup (void *cls,
     441             :                               const struct TALER_TESTING_Command *cmd)
     442             : {
     443          14 :   struct PostUsingTemplatesState *tis = cls;
     444             : 
     445          14 :   if (NULL != tis->iph)
     446             :   {
     447           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     448             :                 "POST /using-templates operation did not complete\n");
     449           0 :     TALER_MERCHANT_using_templates_post_cancel (tis->iph);
     450             :   }
     451          14 :   json_decref (tis->order_terms);
     452          14 :   json_decref (tis->contract_terms);
     453          14 :   GNUNET_free (tis->order_id);
     454          14 :   GNUNET_free (tis);
     455          14 : }
     456             : 
     457             : 
     458             : /**
     459             :  * Mark part of the contract terms as possible to forget.
     460             :  *
     461             :  * @param cls pointer to the result of the forget operation.
     462             :  * @param object_id name of the object to forget.
     463             :  * @param parent parent of the object at @e object_id.
     464             :  */
     465             : static void
     466          56 : mark_forgettable (void *cls,
     467             :                   const char *object_id,
     468             :                   json_t *parent)
     469             : {
     470          56 :   GNUNET_assert (GNUNET_OK ==
     471             :                  TALER_JSON_contract_mark_forgettable (parent,
     472             :                                                        object_id));
     473          56 : }
     474             : 
     475             : 
     476             : /**
     477             :  * Constructs the json for a POST using template request.
     478             :  *
     479             :  * @param using_template_id the name of the using_template to add, can be NULL.
     480             :  * @param refund_deadline the deadline for refunds on this using template.
     481             :  * @param pay_deadline the deadline for payment on this using template.
     482             :  * @param amount the amount this using template is for.
     483             :  * @param[out] using_template where to write the json string.
     484             :  */
     485             : static void
     486          14 : make_order_json (const char *using_template_id,
     487             :                  struct GNUNET_TIME_Timestamp refund_deadline,
     488             :                  struct GNUNET_TIME_Timestamp pay_deadline,
     489             :                  const char *amount,
     490             :                  json_t **using_template)
     491             : {
     492          14 :   struct GNUNET_TIME_Timestamp refund = refund_deadline;
     493          14 :   struct GNUNET_TIME_Timestamp pay = pay_deadline;
     494             :   json_t *contract_terms;
     495             :   struct TALER_Amount tamount;
     496             :   json_t *arr;
     497             : 
     498          14 :   if (NULL != amount)
     499          12 :     GNUNET_assert (GNUNET_OK ==
     500             :                    TALER_string_to_amount (amount,
     501             :                                            &tamount));
     502             :   /* Include required fields and some dummy objects to test forgetting. */
     503          14 :   arr = json_array ();
     504          14 :   GNUNET_assert (NULL != arr);
     505          14 :   GNUNET_assert (0 ==
     506             :                  json_array_append_new (
     507             :                    arr,
     508             :                    GNUNET_JSON_PACK (
     509             :                      GNUNET_JSON_pack_string (
     510             :                        "item", "speakers"))));
     511          14 :   GNUNET_assert (0 ==
     512             :                  json_array_append_new (
     513             :                    arr,
     514             :                    GNUNET_JSON_PACK (
     515             :                      GNUNET_JSON_pack_string (
     516             :                        "item", "headphones"))));
     517          14 :   GNUNET_assert (0 ==
     518             :                  json_array_append_new (
     519             :                    arr,
     520             :                    GNUNET_JSON_PACK (
     521             :                      GNUNET_JSON_pack_string (
     522             :                        "item", "earbuds"))));
     523          14 :   contract_terms = GNUNET_JSON_PACK (
     524             :     GNUNET_JSON_pack_string ("summary",
     525             :                              "merchant-lib testcase"),
     526             :     GNUNET_JSON_pack_allow_null (
     527             :       GNUNET_JSON_pack_string (
     528             :         "using_template_id", using_template_id)),
     529             :     NULL == amount
     530             :     ? GNUNET_JSON_pack_allow_null (
     531             :       GNUNET_JSON_pack_string ("amount",
     532             :                                NULL))
     533             :     : TALER_JSON_pack_amount ("amount",
     534             :                               &tamount),
     535             :     GNUNET_JSON_pack_string ("fulfillment_url",
     536             :                              "https://example.com"),
     537             :     GNUNET_JSON_pack_allow_null (
     538             :       GNUNET_JSON_pack_timestamp ("refund_deadline",
     539             :                                   refund)),
     540             :     GNUNET_JSON_pack_allow_null (
     541             :       GNUNET_JSON_pack_timestamp ("pay_deadline",
     542             :                                   pay)),
     543             :     GNUNET_JSON_pack_string ("dummy_obj",
     544             :                              "EUR:1.0"),
     545             :     GNUNET_JSON_pack_array_steal ("dummy_array",
     546             :                                   arr));
     547          14 :   GNUNET_assert (GNUNET_OK ==
     548             :                  TALER_JSON_expand_path (contract_terms,
     549             :                                          "$.dummy_obj",
     550             :                                          &mark_forgettable,
     551             :                                          NULL));
     552          14 :   GNUNET_assert (GNUNET_OK ==
     553             :                  TALER_JSON_expand_path (contract_terms,
     554             :                                          "$.dummy_array[*].item",
     555             :                                          &mark_forgettable,
     556             :                                          NULL));
     557          14 :   *using_template = contract_terms;
     558          14 : }
     559             : 
     560             : 
     561             : struct TALER_TESTING_Command
     562          14 : TALER_TESTING_cmd_merchant_post_using_templates (
     563             :   const char *label,
     564             :   const char *template_ref,
     565             :   const char *otp_ref,
     566             :   const char *merchant_url,
     567             :   const char *using_template_id,
     568             :   const char *summary,
     569             :   const char *amount,
     570             :   struct GNUNET_TIME_Timestamp refund_deadline,
     571             :   struct GNUNET_TIME_Timestamp pay_deadline,
     572             :   unsigned int http_status)
     573             : {
     574             :   struct PostUsingTemplatesState *tis;
     575             : 
     576          14 :   tis = GNUNET_new (struct PostUsingTemplatesState);
     577          14 :   tis->template_ref = template_ref;
     578          14 :   tis->otp_ref = otp_ref;
     579          14 :   tis->merchant_url = merchant_url;
     580          14 :   tis->using_template_id = using_template_id;
     581          14 :   tis->http_status = http_status;
     582          14 :   tis->summary = summary;
     583          14 :   tis->with_claim = true;
     584          14 :   make_order_json (using_template_id,
     585             :                    refund_deadline,
     586             :                    pay_deadline,
     587             :                    amount,
     588             :                    &tis->order_terms);
     589          14 :   if (NULL != amount)
     590          12 :     GNUNET_assert (GNUNET_OK ==
     591             :                    TALER_string_to_amount (amount,
     592             :                                            &tis->amount));
     593             :   {
     594          14 :     struct TALER_TESTING_Command cmd = {
     595             :       .cls = tis,
     596             :       .label = label,
     597             :       .run = &post_using_templates_run,
     598             :       .cleanup = &post_using_templates_cleanup,
     599             :       .traits = &post_using_templates_traits
     600             :     };
     601             : 
     602          14 :     return cmd;
     603             :   }
     604             : }
     605             : 
     606             : 
     607             : /* end of testing_api_cmd_post_using_templates.c */

Generated by: LCOV version 1.16