sync

Backup service to store encrypted wallet databases (experimental)
Log | Files | Refs | Submodules | README | LICENSE

test_sync_api.c (29763B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2024 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/test_sync_api.c
     21  * @brief testcase to test sync's HTTP API interface
     22  * @author Christian Grothoff
     23  */
     24 #include "platform.h"
     25 #include <taler/taler_util.h>
     26 #include <taler/taler_signatures.h>
     27 #include <taler/taler_exchange_service.h>
     28 #include <taler/taler_json_lib.h>
     29 #include <gnunet/gnunet_util_lib.h>
     30 /* The taler testing library still includes the MHD1 header
     31    (microhttpd.h); until that is resolved, include it here too,
     32    as the two headers cannot be mixed in one translation unit. */
     33 #include <microhttpd.h>
     34 #include <taler/taler_bank_service.h>
     35 #include <taler/taler_testing_lib.h>
     36 #include <taler/taler_merchant_testing_lib.h>
     37 #include <taler/taler_error_codes.h>
     38 #include "sync/sync_service.h"
     39 #include "sync/sync_testing_lib.h"
     40 #include "sync_util.h"
     41 
     42 
     43 /**
     44  * Configuration file we use.  One (big) configuration is used
     45  * for the various components for this test.
     46  */
     47 #define CONFIG_FILE "test_sync_api.conf"
     48 
     49 /**
     50  * Account number of some user.
     51  */
     52 #define USER_ACCOUNT_NAME "62"
     53 
     54 /**
     55  * Account number used by the merchant
     56  */
     57 #define MERCHANT_ACCOUNT_NAME "3"
     58 
     59 /**
     60  * Payto URI of the customer (payer).
     61  */
     62 static struct TALER_FullPayto payer_payto;
     63 
     64 /**
     65  * Payto URI of the merchant (receiver).
     66  */
     67 static struct TALER_FullPayto merchant_payto;
     68 
     69 /**
     70  * Configuration of the bank.
     71  */
     72 static struct TALER_TESTING_Credentials cred;
     73 
     74 /**
     75  * Merchant base URL.
     76  */
     77 static const char *merchant_url = "http://localhost:8080/";
     78 
     79 /**
     80  * Sync base URL.
     81  */
     82 static const char *sync_url = "http://localhost:8084/";
     83 
     84 
     85 /**
     86  * Execute the taler-exchange-wirewatch command with
     87  * our configuration file.
     88  *
     89  * @param label label to use for the command.
     90  */
     91 static struct TALER_TESTING_Command
     92 cmd_exec_wirewatch (const char *label)
     93 {
     94   return TALER_TESTING_cmd_exec_wirewatch (label,
     95                                            CONFIG_FILE);
     96 }
     97 
     98 
     99 /**
    100  * Run wire transfer of funds from some user's account to the
    101  * exchange.
    102  *
    103  * @param label label to use for the command.
    104  * @param amount amount to transfer, i.e. "EUR:1"
    105  * @param url exchange_url
    106  */
    107 static struct TALER_TESTING_Command
    108 cmd_transfer_to_exchange (const char *label,
    109                           const char *amount)
    110 {
    111   return TALER_TESTING_cmd_admin_add_incoming (label,
    112                                                amount,
    113                                                &cred.ba,
    114                                                payer_payto);
    115 }
    116 
    117 
    118 /**
    119  * Main function that will tell the interpreter what commands to
    120  * run.
    121  *
    122  * @param cls closure
    123  * @param is interpreter state
    124  */
    125 static void
    126 run (void *cls,
    127      struct TALER_TESTING_Interpreter *is)
    128 {
    129   /* Payload of the blocks we upload.  data[0] is the initial content of
    130      block A, which is later updated to data[1]; hashes[] holds the
    131      hashes of the *final* content of blocks A, C and D. */
    132   static const char *const data[] = {
    133     "Hello World",
    134     "Block B data",
    135     "Block C data",
    136     "Block D data"
    137   };
    138   static const char obj_data_A[] = "object-A-data";
    139   static const char obj_data_B[] = "object-B-data";
    140   /* Nonces of blocks A, C and D, in chain order. */
    141   struct SYNC_BlockNonce nonces[3];
    142   /* Hashes of blocks A, C and D, in chain order. */
    143   struct GNUNET_HashCode hashes[3];
    144   /* Mixing salt used for the reconcile tests. */
    145   uint32_t reconcile_prefix;
    146   /* Nonce of a block that is never successfully uploaded. */
    147   struct SYNC_BlockNonce nonce_stale;
    148   /* Nonce that never exists on the server. */
    149   struct SYNC_BlockNonce bogus_nonce;
    150   /* Nonces of the two blocks used for the refcount tests. */
    151   struct SYNC_BlockNonce ref_nonce;
    152   struct SYNC_BlockNonce ref_nonce2;
    153   /* Nonce of a block whose append is rejected for a bad object ref. */
    154   struct SYNC_BlockNonce orphan_nonce;
    155   /* Nonce of the block appended after that rejected append. */
    156   struct SYNC_BlockNonce tail_nonce;
    157   /* UIDs of the two objects used for the refcount tests. */
    158   struct SYNC_ObjectUID obj_uids[2];
    159   /* UID of an object that is never uploaded. */
    160   struct SYNC_ObjectUID unknown_uid;
    161   const int16_t ref_incs[2] = { 1, 1 };
    162   const int16_t ref_decs[2] = { -1, -1 };
    163   const int16_t ref_inc1[1] = { 1 };
    164   const int16_t ref_dec2[1] = { -2 };
    165 
    166   (void) cls;
    167   for (unsigned int i = 0; i < 3; i++)
    168     SYNC_block_nonce_generate (&nonces[i]);
    169   SYNC_block_nonce_generate (&nonce_stale);
    170   SYNC_block_nonce_generate (&bogus_nonce);
    171   SYNC_block_nonce_generate (&ref_nonce);
    172   SYNC_block_nonce_generate (&ref_nonce2);
    173   SYNC_block_nonce_generate (&orphan_nonce);
    174   SYNC_block_nonce_generate (&tail_nonce);
    175   for (unsigned int i = 0; i < 2; i++)
    176     GNUNET_CRYPTO_random_block (&obj_uids[i],
    177                                 sizeof (obj_uids[i]));
    178   GNUNET_CRYPTO_random_block (&unknown_uid,
    179                               sizeof (unknown_uid));
    180   for (unsigned int i = 0; i < 3; i++)
    181     GNUNET_CRYPTO_hash (data[i + 1],
    182                         strlen (data[i + 1]),
    183                         &hashes[i]);
    184   reconcile_prefix = 0x01020304;
    185 
    186   {
    187     struct TALER_TESTING_Command commands[] = {
    188       TALER_TESTING_cmd_system_start ("start-taler",
    189                                       CONFIG_FILE,
    190                                       "-efms",
    191                                       "-u", "exchange-account-exchange",
    192                                       "-r", "merchant-exchange-default",
    193                                       NULL),
    194       TALER_TESTING_cmd_get_exchange ("get-exchange",
    195                                       cred.cfg,
    196                                       NULL,
    197                                       true,
    198                                       true),
    199       TALER_TESTING_cmd_merchant_post_instances ("instance-create-admin",
    200                                                  merchant_url,
    201                                                  "admin",
    202                                                  MHD_HTTP_NO_CONTENT),
    203       TALER_TESTING_cmd_merchant_post_account (
    204         "instance-create-default-account",
    205         merchant_url,
    206         merchant_payto,
    207         NULL, NULL,
    208         MHD_HTTP_OK),
    209 
    210       /**
    211        * Move money to the exchange's bank account.
    212        */
    213       cmd_transfer_to_exchange ("create-reserve-1",
    214                                 "EUR:10.02"),
    215       /**
    216        * Make a reserve exist, according to the previous
    217        * transfer.
    218        */
    219       cmd_exec_wirewatch ("wirewatch-1"),
    220       TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1",
    221                                          "create-reserve-1",
    222                                          "EUR:5",
    223                                          0,
    224                                          MHD_HTTP_OK),
    225       TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2",
    226                                          "create-reserve-1",
    227                                          "EUR:5",
    228                                          0,
    229                                          MHD_HTTP_OK),
    230 
    231       /* Upload first block; no lifetime yet → 402 Payment Required */
    232       SYNC_TESTING_cmd_block_append ("upload-block-1",
    233                                      sync_url,
    234                                      NULL,
    235                                      &nonces[0],
    236                                      MHD_HTTP_PAYMENT_REQUIRED,
    237                                      data[0],
    238                                      strlen (data[0]),
    239                                      true,
    240                                      0,
    241                                      NULL,
    242                                      NULL),
    243       /* Claim the order from the merchant */
    244       TALER_TESTING_cmd_merchant_claim_order ("fetch-proposal",
    245                                               merchant_url,
    246                                               MHD_HTTP_OK,
    247                                               "upload-block-1",
    248                                               NULL),
    249       /* Pay with one withdrawn coin */
    250       TALER_TESTING_cmd_merchant_pay_order ("pay-account",
    251                                             merchant_url,
    252                                             MHD_HTTP_OK,
    253                                             "fetch-proposal",
    254                                             "withdraw-coin-1",
    255                                             "EUR:5",
    256                                             "EUR:4.99",
    257                                             "session-id"),
    258       /* Retry same upload; server finds paid order → 204 */
    259       SYNC_TESTING_cmd_block_append ("upload-block-2",
    260                                      sync_url,
    261                                      "upload-block-1",
    262                                      &nonces[0],
    263                                      MHD_HTTP_NO_CONTENT,
    264                                      data[0],
    265                                      strlen (data[0]),
    266                                      true,
    267                                      0,
    268                                      NULL,
    269                                      NULL),
    270       /* Update the existing block with different content */
    271       SYNC_TESTING_cmd_block_update ("upload-block-B",
    272                                      sync_url,
    273                                      "upload-block-2",
    274                                      &nonces[0],
    275                                      NULL,
    276                                      MHD_HTTP_NO_CONTENT,
    277                                      data[1],
    278                                      strlen (data[1]),
    279                                      0,
    280                                      NULL,
    281                                      NULL),
    282       /* Append a new block C at the end of block B */
    283       SYNC_TESTING_cmd_block_append ("upload-block-C",
    284                                      sync_url,
    285                                      "upload-block-B",
    286                                      &nonces[1],
    287                                      MHD_HTTP_NO_CONTENT,
    288                                      data[2],
    289                                      strlen (data[2]),
    290                                      false,
    291                                      0,
    292                                      NULL,
    293                                      NULL),
    294       /* List blocks for the account */
    295       SYNC_TESTING_cmd_block_list ("list-blocks",
    296                                    sync_url,
    297                                    "upload-block-2",
    298                                    10,
    299                                    NULL,
    300                                    MHD_HTTP_OK,
    301                                    2,
    302                                    nonces,
    303                                    hashes),
    304 
    305       /* Append with stale prev_nonce (nonces[0]),
    306          but last_block is now nonces[1] → 409 */
    307       SYNC_TESTING_cmd_block_append ("append-stale-prev",
    308                                      sync_url,
    309                                      "upload-block-B",
    310                                      &nonce_stale,
    311                                      MHD_HTTP_CONFLICT,
    312                                      data[0],
    313                                      strlen (data[0]),
    314                                      false,
    315                                      0,
    316                                      NULL,
    317                                      NULL),
    318       /* Update with stale old_data_hash
    319          (references upload-block-2 whose curr_hash is hash("Hello World"),
    320           but block now has hash("Block B data")) → 409 */
    321       SYNC_TESTING_cmd_block_update ("update-conflict",
    322                                      sync_url,
    323                                      "upload-block-2",
    324                                      &nonces[0],
    325                                      NULL,
    326                                      MHD_HTTP_CONFLICT,
    327                                      data[2],
    328                                      strlen (data[2]),
    329                                      0,
    330                                      NULL,
    331                                      NULL),
    332       /* Re-upload same data → 304 Not Modified */
    333       SYNC_TESTING_cmd_block_append ("reupload-same-data",
    334                                      sync_url,
    335                                      "upload-block-B",
    336                                      &nonces[0],
    337                                      MHD_HTTP_NOT_MODIFIED,
    338                                      data[1],
    339                                      strlen (data[1]),
    340                                      true,
    341                                      0,
    342                                      NULL,
    343                                      NULL),
    344       /* List with non-existent start_nonce → 404 */
    345       SYNC_TESTING_cmd_block_list ("list-bogus-start",
    346                                    sync_url,
    347                                    "upload-block-2",
    348                                    10,
    349                                    &bogus_nonce,
    350                                    MHD_HTTP_NOT_FOUND,
    351                                    0,
    352                                    NULL,
    353                                    NULL),
    354       /* Final list confirms error cases left state intact */
    355       SYNC_TESTING_cmd_block_list ("list-blocks-final",
    356                                    sync_url,
    357                                    "upload-block-2",
    358                                    10,
    359                                    NULL,
    360                                    MHD_HTTP_OK,
    361                                    2,
    362                                    nonces,
    363                                    hashes),
    364 
    365       /* ========== Delete block tests ========== */
    366 
    367       /* Upload a third block D */
    368       SYNC_TESTING_cmd_block_append ("upload-block-D",
    369                                      sync_url,
    370                                      "upload-block-C",
    371                                      &nonces[2],
    372                                      MHD_HTTP_NO_CONTENT,
    373                                      data[3],
    374                                      strlen (data[3]),
    375                                      false,
    376                                      0,
    377                                      NULL,
    378                                      NULL),
    379       /* List all 3 blocks */
    380       SYNC_TESTING_cmd_block_list ("list-3-blocks",
    381                                    sync_url,
    382                                    "upload-block-D",
    383                                    10,
    384                                    NULL,
    385                                    MHD_HTTP_OK,
    386                                    3,
    387                                    nonces,
    388                                    hashes),
    389       /* Delete block D */
    390       SYNC_TESTING_cmd_block_delete ("delete-block-D",
    391                                      sync_url,
    392                                      "upload-block-D",
    393                                      MHD_HTTP_NO_CONTENT,
    394                                      0,
    395                                      NULL,
    396                                      NULL),
    397       /* List: only A and B remain */
    398       SYNC_TESTING_cmd_block_list ("list-after-delete-D",
    399                                    sync_url,
    400                                    "upload-block-2",
    401                                    10,
    402                                    NULL,
    403                                    MHD_HTTP_OK,
    404                                    2,
    405                                    nonces,
    406                                    hashes),
    407       /* Delete block D again → 404 (already gone) */
    408       SYNC_TESTING_cmd_block_delete ("delete-block-D-again",
    409                                      sync_url,
    410                                      "upload-block-D",
    411                                      MHD_HTTP_NOT_FOUND,
    412                                      0,
    413                                      NULL,
    414                                      NULL),
    415 
    416       /* ========== Reconcile (invertible bloom filter) tests ========== */
    417 
    418       /* A wallet holding exactly the server's blocks decodes an empty
    419          difference from the filter. */
    420       SYNC_TESTING_cmd_block_reconcile ("reconcile-in-sync",
    421                                         sync_url,
    422                                         "upload-block-2",
    423                                         MHD_HTTP_OK,
    424                                         2,
    425                                         NULL,
    426                                         2,
    427                                         nonces,
    428                                         hashes,
    429                                         0,
    430                                         NULL,
    431                                         NULL,
    432                                         0,
    433                                         NULL,
    434                                         NULL),
    435       /* With a fresh salt the filter still matches every block, and
    436          the reply echoes the requested salt. */
    437       SYNC_TESTING_cmd_block_reconcile ("reconcile-prefix",
    438                                         sync_url,
    439                                         "upload-block-2",
    440                                         MHD_HTTP_OK,
    441                                         2,
    442                                         &reconcile_prefix,
    443                                         2,
    444                                         nonces,
    445                                         hashes,
    446                                         0,
    447                                         NULL,
    448                                         NULL,
    449                                         0,
    450                                         NULL,
    451                                         NULL),
    452       /* A wallet that still holds the deleted block D sees it as
    453          stale (local-only); nothing is missing. */
    454       SYNC_TESTING_cmd_block_reconcile ("reconcile-stale",
    455                                         sync_url,
    456                                         "upload-block-2",
    457                                         MHD_HTTP_OK,
    458                                         2,
    459                                         NULL,
    460                                         3,
    461                                         nonces,
    462                                         hashes,
    463                                         0,
    464                                         NULL,
    465                                         NULL,
    466                                         1,
    467                                         &nonces[2],
    468                                         &hashes[2]),
    469       /* A wallet holding only block A enumerates block B as missing
    470          (server-only). */
    471       SYNC_TESTING_cmd_block_reconcile ("reconcile-missing",
    472                                         sync_url,
    473                                         "upload-block-2",
    474                                         MHD_HTTP_OK,
    475                                         2,
    476                                         NULL,
    477                                         1,
    478                                         nonces,
    479                                         hashes,
    480                                         1,
    481                                         &nonces[1],
    482                                         &hashes[1],
    483                                         0,
    484                                         NULL,
    485                                         NULL),
    486 
    487       /* ========== Single block fetch tests ========== */
    488 
    489       /* A block is fetchable by nonce through the list endpoint with
    490          limit 1 and an inclusive start_nonce, with its payload and
    491          signature intact. */
    492       SYNC_TESTING_cmd_block_list ("fetch-block-B",
    493                                    sync_url,
    494                                    "upload-block-2",
    495                                    1,
    496                                    &nonces[1],
    497                                    MHD_HTTP_OK,
    498                                    1,
    499                                    &nonces[1],
    500                                    &hashes[1]),
    501       /* A nonce that is not part of the linked list is not found. */
    502       SYNC_TESTING_cmd_block_list ("fetch-block-unknown",
    503                                    sync_url,
    504                                    "upload-block-2",
    505                                    1,
    506                                    &bogus_nonce,
    507                                    MHD_HTTP_NOT_FOUND,
    508                                    0,
    509                                    NULL,
    510                                    NULL),
    511       /* Final list confirms all is well */
    512       SYNC_TESTING_cmd_block_list ("list-blocks-end",
    513                                    sync_url,
    514                                    "upload-block-2",
    515                                    10,
    516                                    NULL,
    517                                    MHD_HTTP_OK,
    518                                    2,
    519                                    nonces,
    520                                    hashes),
    521 
    522       /* ========== Object store and refcount tests ========== */
    523 
    524       /* Upload object A */
    525       SYNC_TESTING_cmd_object_upload ("upload-obj-A",
    526                                       sync_url,
    527                                       "upload-block-2",
    528                                       &obj_uids[0],
    529                                       MHD_HTTP_NO_CONTENT,
    530                                       obj_data_A,
    531                                       sizeof obj_data_A - 1),
    532       /* Upload object B */
    533       SYNC_TESTING_cmd_object_upload ("upload-obj-B",
    534                                       sync_url,
    535                                       "upload-block-2",
    536                                       &obj_uids[1],
    537                                       MHD_HTTP_NO_CONTENT,
    538                                       obj_data_B,
    539                                       sizeof obj_data_B - 1),
    540 
    541       /* Fetch object A and verify content and hash */
    542       SYNC_TESTING_cmd_object_fetch ("fetch-obj-A",
    543                                      sync_url,
    544                                      "upload-block-2",
    545                                      &obj_uids[0],
    546                                      MHD_HTTP_OK,
    547                                      obj_data_A,
    548                                      sizeof obj_data_A - 1),
    549 
    550       /* Fetch object B and verify content and hash */
    551       SYNC_TESTING_cmd_object_fetch ("fetch-obj-B",
    552                                      sync_url,
    553                                      "upload-block-2",
    554                                      &obj_uids[1],
    555                                      MHD_HTTP_OK,
    556                                      obj_data_B,
    557                                      sizeof obj_data_B - 1),
    558 
    559       /* Try uploading a duplicate UID → idempotent (204) */
    560       SYNC_TESTING_cmd_object_upload ("upload-obj-A-dup",
    561                                       sync_url,
    562                                       "upload-block-2",
    563                                       &obj_uids[0],
    564                                       MHD_HTTP_NO_CONTENT,
    565                                       obj_data_B,
    566                                       sizeof obj_data_B - 1),
    567 
    568       /* Upload a block that references objects A (+1) and B (+1) */
    569       SYNC_TESTING_cmd_block_append ("upload-block-refs",
    570                                      sync_url,
    571                                      "upload-block-C",
    572                                      &ref_nonce,
    573                                      MHD_HTTP_NO_CONTENT,
    574                                      data[0],
    575                                      strlen (data[0]),
    576                                      false,
    577                                      2,
    578                                      obj_uids,
    579                                      ref_incs),
    580 
    581       /* Delete the referencing block with matching refs (decrement).
    582          Objects reach refcount 0 and are auto-deleted from the DB. */
    583       SYNC_TESTING_cmd_block_delete ("delete-block-refs",
    584                                      sync_url,
    585                                      "upload-block-refs",
    586                                      MHD_HTTP_NO_CONTENT,
    587                                      2,
    588                                      obj_uids,
    589                                      ref_decs),
    590 
    591       /* Re-upload object A (it was deleted when refcount hit 0) */
    592       SYNC_TESTING_cmd_object_upload ("upload-obj-A-again",
    593                                       sync_url,
    594                                       "upload-block-2",
    595                                       &obj_uids[0],
    596                                       MHD_HTTP_NO_CONTENT,
    597                                       obj_data_A,
    598                                       sizeof obj_data_A - 1),
    599 
    600       /* Upload another block referencing A (+1) */
    601       SYNC_TESTING_cmd_block_append ("upload-block-refs2",
    602                                      sync_url,
    603                                      "upload-block-C",
    604                                      &ref_nonce2,
    605                                      MHD_HTTP_NO_CONTENT,
    606                                      data[0],
    607                                      strlen (data[0]),
    608                                      false,
    609                                      1,
    610                                      obj_uids,
    611                                      ref_inc1),
    612 
    613       /* Try to delete with refs that would underflow → 409 conflict */
    614       SYNC_TESTING_cmd_block_delete ("delete-block-refs-underflow",
    615                                      sync_url,
    616                                      "upload-block-refs2",
    617                                      MHD_HTTP_CONFLICT,
    618                                      1,
    619                                      obj_uids,
    620                                      ref_dec2),
    621       /* The rejected delete left the block listed ... */
    622       SYNC_TESTING_cmd_block_list ("list-after-underflow",
    623                                    sync_url,
    624                                    "upload-block-2",
    625                                    10,
    626                                    NULL,
    627                                    MHD_HTTP_OK,
    628                                    0,
    629                                    NULL,
    630                                    NULL),
    631       /* ... and the object it references is still fetchable */
    632       SYNC_TESTING_cmd_object_fetch ("fetch-obj-A-after-underflow",
    633                                      sync_url,
    634                                      "upload-block-2",
    635                                      &obj_uids[0],
    636                                      MHD_HTTP_OK,
    637                                      obj_data_A,
    638                                      sizeof obj_data_A - 1),
    639 
    640       /* ========== Object and reference error paths ========== */
    641 
    642       /* Fetching an object that was never uploaded → 404 */
    643       SYNC_TESTING_cmd_object_fetch ("fetch-obj-unknown",
    644                                      sync_url,
    645                                      "upload-block-2",
    646                                      &unknown_uid,
    647                                      MHD_HTTP_NOT_FOUND,
    648                                      NULL,
    649                                      0),
    650 
    651       /* Referencing an object that was never uploaded → 409 ... */
    652       SYNC_TESTING_cmd_block_append ("append-unknown-object-ref",
    653                                      sync_url,
    654                                      "upload-block-refs2",
    655                                      &orphan_nonce,
    656                                      MHD_HTTP_CONFLICT,
    657                                      data[0],
    658                                      strlen (data[0]),
    659                                      false,
    660                                      1,
    661                                      &unknown_uid,
    662                                      ref_inc1),
    663       /* ... and must not have moved the tail, so the next append
    664          onto upload-block-refs2 still succeeds */
    665       SYNC_TESTING_cmd_block_append ("append-after-rejected-ref",
    666                                      sync_url,
    667                                      "upload-block-refs2",
    668                                      &tail_nonce,
    669                                      MHD_HTTP_NO_CONTENT,
    670                                      data[2],
    671                                      strlen (data[2]),
    672                                      false,
    673                                      0,
    674                                      NULL,
    675                                      NULL),
    676 
    677       TALER_TESTING_cmd_end ()
    678     };
    679 
    680     TALER_TESTING_run (is,
    681                        commands);
    682   }
    683 }
    684 
    685 
    686 int
    687 main (int argc,
    688       char *const *argv)
    689 {
    690   (void) argc;
    691   payer_payto.full_payto =
    692     (char *) "payto://x-taler-bank/localhost/" USER_ACCOUNT_NAME
    693     "?receiver-name=user";
    694   merchant_payto.full_payto =
    695     (char *) "payto://x-taler-bank/localhost/" MERCHANT_ACCOUNT_NAME
    696     "?receiver-name=merchant";
    697   return TALER_TESTING_main (argv,
    698                              "DEBUG",
    699                              CONFIG_FILE,
    700                              "exchange-account-exchange",
    701                              TALER_TESTING_BS_FAKEBANK,
    702                              &cred,
    703                              &run,
    704                              NULL);
    705 }
    706 
    707 
    708 /* end of test_sync_api.c */