github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/rest_api/tests/unit/test_txn_requests.py (about)

     1  # Copyright 2017 Intel Corporation
     2  #
     3  # Licensed under the Apache License, Version 2.0 (the "License");
     4  # you may not use this file except in compliance with the License.
     5  # You may obtain a copy of the License at
     6  #
     7  #     http://www.apache.org/licenses/LICENSE-2.0
     8  #
     9  # Unless required by applicable law or agreed to in writing, software
    10  # distributed under the License is distributed on an "AS IS" BASIS,
    11  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  # See the License for the specific language governing permissions and
    13  # limitations under the License.
    14  # ------------------------------------------------------------------------------
    15  
    16  from aiohttp.test_utils import unittest_run_loop
    17  
    18  from components import Mocks, BaseApiTest
    19  from sawtooth_rest_api.protobuf.validator_pb2 import Message
    20  from sawtooth_rest_api.protobuf import client_transaction_pb2
    21  
    22  
    23  ID_A = 'a' * 128
    24  ID_B = 'b' * 128
    25  ID_C = 'c' * 128
    26  ID_D = 'd' * 128
    27  
    28  DEFAULT_LIMIT = 100
    29  
    30  
    31  class TransactionListTests(BaseApiTest):
    32      async def get_application(self):
    33          self.set_status_and_connection(
    34              Message.CLIENT_TRANSACTION_LIST_REQUEST,
    35              client_transaction_pb2.ClientTransactionListRequest,
    36              client_transaction_pb2.ClientTransactionListResponse)
    37  
    38          handlers = self.build_handlers(self.loop, self.connection)
    39          app = self.build_app(self.loop, '/transactions',
    40                               handlers.list_transactions)
    41          return app
    42  
    43      @unittest_run_loop
    44      async def test_txn_list(self):
    45          """Verifies a GET /transactions without parameters works properly.
    46  
    47          It will receive a Protobuf response with:
    48              - a head id of ID_C
    49              - a paging response with a start of ID_C and limit of 100
    50              - three transactions with ids of ID_C, ID_B, and ID_A
    51  
    52          It should send a Protobuf request with:
    53              - empty paging controls
    54  
    55          It should send back a JSON response with:
    56              - a response status of 200
    57              - a head property of ID_C
    58              - a link property that ends in
    59                  '/transactions?head={}&start={}&limit=100'.format(ID_C, ID_C)
    60              - a paging property that matches the paging response
    61              - a data property that is a list of 3 dicts
    62              - those dicts are full transactions with ids ID_C, ID_B, and ID_A
    63          """
    64          paging = Mocks.make_paging_response("", ID_C, DEFAULT_LIMIT)
    65          self.connection.preset_response(
    66              head_id=ID_C,
    67              paging=paging,
    68              transactions=Mocks.make_txns(ID_C, ID_B, ID_A))
    69  
    70          response = await self.get_assert_200('/transactions')
    71          controls = Mocks.make_paging_controls()
    72          self.connection.assert_valid_request_sent(paging=controls)
    73  
    74          self.assert_has_valid_head(response, ID_C)
    75          self.assert_has_valid_link(
    76              response,
    77              '/transactions?head={}&start={}&limit=100'.format(ID_C, ID_C))
    78          self.assert_has_valid_paging(response, paging)
    79          self.assert_has_valid_data_list(response, 3)
    80          self.assert_txns_well_formed(response['data'], ID_C, ID_B, ID_A)
    81  
    82      @unittest_run_loop
    83      async def test_txn_list_with_validator_error(self):
    84          """Verifies a GET /transactions with a validator error breaks properly.
    85  
    86          It will receive a Protobuf response with:
    87              - a status of INTERNAL_ERROR
    88  
    89          It should send back a JSON response with:
    90              - a status of 500
    91              - an error property with a code of 10
    92          """
    93          self.connection.preset_response(self.status.INTERNAL_ERROR)
    94          response = await self.get_assert_status('/transactions', 500)
    95  
    96          self.assert_has_valid_error(response, 10)
    97  
    98      @unittest_run_loop
    99      async def test_txn_list_with_no_genesis(self):
   100          """Verifies GET /transactions with validator not ready breaks properly.
   101  
   102          It will receive a Protobuf response with:
   103              - a status of NOT_READY
   104  
   105          It should send back a JSON response with:
   106              - a status of 503
   107              - an error property with a code of 15
   108          """
   109          self.connection.preset_response(self.status.NOT_READY)
   110          response = await self.get_assert_status('/transactions', 503)
   111  
   112          self.assert_has_valid_error(response, 15)
   113  
   114      @unittest_run_loop
   115      async def test_txn_list_with_head(self):
   116          """Verifies a GET /transactions with a head parameter works properly.
   117  
   118          It will receive a Protobuf response with:
   119              - a head id of ID_B
   120              - a paging response with a start of ID_B and limit of 100
   121              - two transactions with ids of 1' and ID_A
   122  
   123          It should send a Protobuf request with:
   124              - a head_id property of ID_B
   125              - empty paging controls
   126  
   127          It should send back a JSON response with:
   128              - a response status of 200
   129              - a head property of ID_B
   130              - a link property that ends in
   131                  '/transactions?head={}&start={}&limit=100'.format(ID_B, ID_B))
   132              - a paging property that matches the paging response
   133              - a data property that is a list of 2 dicts
   134              - those dicts are full transactions with ids ID_B and ID_A
   135          """
   136          paging = Mocks.make_paging_response("", ID_B, DEFAULT_LIMIT)
   137          self.connection.preset_response(
   138              head_id=ID_B,
   139              paging=paging,
   140              transactions=Mocks.make_txns(ID_B, ID_A))
   141  
   142          response = await self.get_assert_200(
   143              '/transactions?head={}'.format(ID_B))
   144          controls = Mocks.make_paging_controls()
   145          self.connection.assert_valid_request_sent(
   146              head_id=ID_B, paging=controls)
   147          self.assert_has_valid_head(response, ID_B)
   148          self.assert_has_valid_link(
   149              response,
   150              '/transactions?head={}&start={}&limit=100'.format(ID_B, ID_B))
   151          self.assert_has_valid_paging(response, paging)
   152          self.assert_has_valid_data_list(response, 2)
   153          self.assert_txns_well_formed(response['data'], ID_B, ID_A)
   154  
   155      @unittest_run_loop
   156      async def test_txn_list_with_bad_head(self):
   157          """Verifies a GET /transactions with a bad head id breaks properly.
   158  
   159          It will receive a Protobuf response with:
   160              - a status of NO_ROOT
   161  
   162          It should send back a JSON response with:
   163              - a response status of 404
   164              - an error property with a code of 50
   165          """
   166          self.connection.preset_response(self.status.NO_ROOT)
   167          response = await self.get_assert_status(
   168              '/transactions?head={}'.format(ID_D), 404)
   169  
   170          self.assert_has_valid_error(response, 50)
   171  
   172      @unittest_run_loop
   173      async def test_txn_list_with_ids(self):
   174          """Verifies GET /transactions with an id filter works properly.
   175  
   176          It will receive a Protobuf response with:
   177              - a head id of ID_C
   178              - a paging response with a start of ID_C and limit of 100
   179              - two transactions with ids of ID_A and ID_C
   180  
   181          It should send a Protobuf request with:
   182              - a transaction_ids property of [ID_A, ID_C]
   183              - empty paging controls
   184  
   185          It should send back a JSON response with:
   186              - a response status of 200
   187              - a head property of ID_C, the latest
   188              - a link property that ends in
   189                  '/transactions?head={}&start={}&limit=100&id={},{}'
   190                      .format(ID_C, ID_C, ID_A, ID_C))
   191              - a paging property that matches the paging response
   192              - a data property that is a list of 2 dicts
   193              - those dicts are full transactions with ids ID_A and ID_C
   194          """
   195          paging = Mocks.make_paging_response("", ID_C, DEFAULT_LIMIT)
   196          transactions = Mocks.make_txns(ID_A, ID_C)
   197          self.connection.preset_response(
   198              head_id=ID_C, paging=paging, transactions=transactions)
   199  
   200          response = await self.get_assert_200('/transactions?id={},{}'.format(
   201              ID_A, ID_C))
   202          controls = Mocks.make_paging_controls()
   203          self.connection.assert_valid_request_sent(
   204              transaction_ids=[ID_A, ID_C], paging=controls)
   205  
   206          self.assert_has_valid_head(response, ID_C)
   207          self.assert_has_valid_link(
   208              response,
   209              '/transactions?head={}&start={}&limit=100&id={},{}'.format(
   210                  ID_C, ID_C, ID_A, ID_C))
   211          self.assert_has_valid_paging(response, paging)
   212          self.assert_has_valid_data_list(response, 2)
   213          self.assert_txns_well_formed(response['data'], ID_A, ID_C)
   214  
   215      @unittest_run_loop
   216      async def test_txn_list_with_bad_ids(self):
   217          """Verifies GET /transactions with a bad id filter breaks properly.
   218  
   219          It will receive a Protobuf response with:
   220              - a status of NO_RESOURCE
   221              - a head id of ID_C
   222  
   223          It should send back a JSON response with:
   224              - a response status of 200
   225              - a head property of ID_C, the latest
   226              - a link property that ends in
   227                  '/transactions?head={}&start={}&limit=100&id={},{}'
   228                      .format(ID_C, ID_C, ID_B, ID_D))
   229              - a paging property with only a total_count of 0
   230              - a data property that is an empty list
   231          """
   232          paging = Mocks.make_paging_response("", ID_C, DEFAULT_LIMIT)
   233          self.connection.preset_response(
   234              self.status.NO_RESOURCE, head_id=ID_C, paging=paging)
   235          response = await self.get_assert_200('/transactions?id={},{}'.format(
   236              ID_B, ID_D))
   237  
   238          self.assert_has_valid_head(response, ID_C)
   239          self.assert_has_valid_link(
   240              response,
   241              '/transactions?head={}&start={}&limit=100&id={},{}'.format(
   242                  ID_C, ID_C, ID_B, ID_D))
   243          self.assert_has_valid_paging(response, paging)
   244          self.assert_has_valid_data_list(response, 0)
   245  
   246      @unittest_run_loop
   247      async def test_txn_list_with_head_and_ids(self):
   248          """Verifies GET /transactions with head and id parameters work
   249          properly.
   250  
   251          It should send a Protobuf request with:
   252              - a head_id property of ID_B
   253              - a paging response with a start of ID_B and limit of 100
   254              - a transaction_ids property of [ID_A]
   255  
   256          It will receive a Protobuf response with:
   257              - a head id of ID_B
   258              - one transaction with an id of ID_A
   259              - empty paging controls
   260  
   261          It should send back a JSON response with:
   262              - a response status of 200
   263              - a head property of ID_B
   264              - a link property that ends in
   265                  '/transactions?head={}&start={}&limit=100&id={}'
   266                      .format(ID_B, ID_B, ID_A))
   267              - a paging property that matches the paging response
   268              - a data property that is a list of 1 dict
   269              - that dict is a full transaction with an id of ID_A
   270          """
   271          paging = Mocks.make_paging_response("", ID_B, DEFAULT_LIMIT)
   272          self.connection.preset_response(
   273              head_id=ID_B, paging=paging, transactions=Mocks.make_txns(ID_A))
   274  
   275          response = await self.get_assert_200(
   276              '/transactions?id={}&head={}'.format(ID_A, ID_B))
   277          controls = Mocks.make_paging_controls()
   278          self.connection.assert_valid_request_sent(
   279              head_id=ID_B, transaction_ids=[ID_A], paging=controls)
   280  
   281          self.assert_has_valid_head(response, ID_B)
   282          self.assert_has_valid_link(
   283              response, '/transactions?head={}&start={}&limit=100&id={}'.format(
   284                  ID_B, ID_B, ID_A))
   285          self.assert_has_valid_paging(response, paging)
   286          self.assert_has_valid_data_list(response, 1)
   287          self.assert_txns_well_formed(response['data'], ID_A)
   288  
   289      @unittest_run_loop
   290      async def test_txn_list_with_zero_count(self):
   291          """Verifies a GET /transactions with a count of zero breaks properly.
   292  
   293          It should send back a JSON response with:
   294              - a response status of 400
   295              - an error property with a code of 53
   296          """
   297          response = await self.get_assert_status('/transactions?limit=0', 400)
   298  
   299          self.assert_has_valid_error(response, 53)
   300  
   301      @unittest_run_loop
   302      async def test_txn_list_with_bad_paging(self):
   303          """Verifies a GET /transactions with a bad paging breaks properly.
   304  
   305          It will receive a Protobuf response with:
   306              - a status of INVALID_PAGING
   307  
   308          It should send back a JSON response with:
   309              - a response status of 400
   310              - an error property with a code of 54
   311          """
   312          self.connection.preset_response(self.status.INVALID_PAGING)
   313          response = await self.get_assert_status('/transactions?min=-1', 400)
   314  
   315          self.assert_has_valid_error(response, 54)
   316  
   317      @unittest_run_loop
   318      async def test_txn_list_paginated_with_just_limit(self):
   319          """Verifies GET /transactions paginated just by limit works properly.
   320  
   321          It will receive a Protobuf response with:
   322              - a head id of ID_D
   323              - a paging response with a start of ID_D, next of ID_B and
   324                limit of 2
   325              - two transactions with the ids ID_D and ID_C
   326  
   327          It should send a Protobuf request with:
   328              - paging controls with a limit of 2
   329  
   330          It should send back a JSON response with:
   331              - a response status of 200
   332              - a head property of ID_D
   333              - a link property that ends in
   334                  '/transactions?head={}&start={}&limit=2'.format(ID_D, ID_D))
   335              - paging that matches the response with a next link
   336              - a data property that is a list of 2 dicts
   337              - those dicts are full transactions with ids ID_D and ID_C
   338          """
   339          paging = Mocks.make_paging_response(ID_B, ID_D, 2)
   340          self.connection.preset_response(
   341              head_id=ID_D,
   342              paging=paging,
   343              transactions=Mocks.make_txns(ID_D, ID_C))
   344  
   345          response = await self.get_assert_200('/transactions?limit=2')
   346          controls = Mocks.make_paging_controls(2)
   347          self.connection.assert_valid_request_sent(paging=controls)
   348  
   349          self.assert_has_valid_head(response, ID_D)
   350          self.assert_has_valid_link(
   351              response, '/transactions?head={}&start={}&limit=2'.format(
   352                  ID_D, ID_D))
   353          self.assert_has_valid_paging(
   354              response, paging, '/transactions?head={}&start={}&limit=2'.format(
   355                  ID_D, ID_B))
   356          self.assert_has_valid_data_list(response, 2)
   357          self.assert_txns_well_formed(response['data'], ID_D, ID_C)
   358  
   359      @unittest_run_loop
   360      async def test_txn_list_paginated_without_count(self):
   361          """Verifies GET /transactions paginated without count works properly.
   362  
   363          It will receive a Protobuf response with:
   364              - a head id of ID_D
   365              - a paging response with a start of ID_D and a limit of 100
   366              - two transactions with the ids ID_B and ID_A
   367  
   368          It should send a Protobuf request with:
   369              - paging controls with a start of 2
   370  
   371          It should send back a JSON response with:
   372              - a response status of 200
   373              - a head property of ID_D
   374              - a link property that ends in
   375                  '/transactions?head={}&start={}&limit=100'.format(ID_D, ID_D))
   376              - paging that matches the response, with a previous link
   377              - a data property that is a list of 2 dicts
   378              - those dicts are full transactions with ids ID_D and ID_C
   379          """
   380          paging = Mocks.make_paging_response("", ID_D, DEFAULT_LIMIT)
   381          self.connection.preset_response(
   382              head_id=ID_D,
   383              paging=paging,
   384              transactions=Mocks.make_txns(ID_B, ID_A))
   385  
   386          response = await self.get_assert_200('/transactions?start=2')
   387          controls = Mocks.make_paging_controls(None, start="2")
   388          self.connection.assert_valid_request_sent(paging=controls)
   389  
   390          self.assert_has_valid_head(response, ID_D)
   391          self.assert_has_valid_link(
   392              response, '/transactions?head={}&start={}&limit=100'.format(
   393                  ID_D, ID_D))
   394          self.assert_has_valid_paging(response, paging)
   395          self.assert_has_valid_data_list(response, 2)
   396          self.assert_txns_well_formed(response['data'], ID_B, ID_A)
   397  
   398      @unittest_run_loop
   399      async def test_txn_list_paginated_by_start_id(self):
   400          """Verifies GET /transactions paginated by a start id works properly.
   401  
   402          It will receive a Protobuf response with:
   403              - a head id of ID_D
   404              - a paging response with start of ID_C and limit of 5
   405              - three transactions with the ids ID_C, ID_B and ID_A
   406  
   407          It should send a Protobuf request with:
   408              - paging controls with a count of 5, and a start_id of ID_C
   409  
   410          It should send back a JSON response with:
   411              - a response status of 200
   412              - a head property of ID_D
   413              - a link property that ends in
   414                  '/transactions?head={}&start={}&limit=5'.format(ID_D, ID_C))
   415              - paging that matches the response, with a previous link
   416              - a data property that is a list of 3 dicts
   417              - those dicts are full transactions with ids ID_C, ID_B, and ID_A
   418          """
   419          paging = Mocks.make_paging_response("", ID_C, 5)
   420          self.connection.preset_response(
   421              head_id=ID_D,
   422              paging=paging,
   423              transactions=Mocks.make_txns(ID_C, ID_B, ID_A))
   424  
   425          response = await self.get_assert_200(
   426              '/transactions?start={}&limit=5'.format(ID_C))
   427          controls = Mocks.make_paging_controls(5, start=ID_C)
   428          self.connection.assert_valid_request_sent(paging=controls)
   429  
   430          self.assert_has_valid_head(response, ID_D)
   431          self.assert_has_valid_link(
   432              response, '/transactions?head={}&start={}&limit=5'.format(
   433                  ID_D, ID_C))
   434          self.assert_has_valid_paging(response, paging)
   435          self.assert_has_valid_data_list(response, 3)
   436          self.assert_txns_well_formed(response['data'], ID_C, ID_B, ID_A)
   437  
   438      @unittest_run_loop
   439      async def test_txn_list_sorted_in_reverse(self):
   440          """Verifies a GET /transactions can send proper sort parameters.
   441  
   442          It will receive a Protobuf response with:
   443              - a head id of ID_C
   444              - a paging response with start of ID_C and limit of 100
   445              - three transactions with ids ID_C, ID_B, and ID_A
   446  
   447          It should send a Protobuf request with:
   448              - empty paging controls
   449              - sort controls with a key of 'header_signature' that is reversed
   450  
   451          It should send back a JSON response with:
   452              - a status of 200
   453              - a head property of ID_C
   454              - a link property ending in
   455                  '/transactions?head={}&start={}&limit=100&reverse'
   456                      .format(ID_C, ID_C))
   457              - a paging property that matches the paging response
   458              - a data property that is a list of 3 dicts
   459              - and those dicts are full transactions with ids ID_C,
   460                ID_B, and ID_A
   461          """
   462          paging = Mocks.make_paging_response("", ID_C, DEFAULT_LIMIT)
   463          transactions = Mocks.make_txns(ID_C, ID_B, ID_A)
   464          self.connection.preset_response(
   465              head_id=ID_C, paging=paging, transactions=transactions)
   466          response = await self.get_assert_200('/transactions?reverse')
   467          page_controls = Mocks.make_paging_controls()
   468          sorting = Mocks.make_sort_controls("default", reverse=True)
   469          self.connection.assert_valid_request_sent(
   470              paging=page_controls,
   471              sorting=sorting)
   472  
   473          self.assert_has_valid_head(response, ID_C)
   474          self.assert_has_valid_link(
   475              response,
   476              '/transactions?head={}&start={}&limit=100&reverse'.format(
   477                  ID_C, ID_C))
   478          self.assert_has_valid_paging(response, paging)
   479          self.assert_has_valid_data_list(response, 3)
   480          self.assert_txns_well_formed(response['data'], ID_C, ID_B, ID_A)
   481  
   482  
   483  class TransactionGetTests(BaseApiTest):
   484      async def get_application(self):
   485          self.set_status_and_connection(
   486              Message.CLIENT_TRANSACTION_GET_REQUEST,
   487              client_transaction_pb2.ClientTransactionGetRequest,
   488              client_transaction_pb2.ClientTransactionGetResponse)
   489  
   490          handlers = self.build_handlers(self.loop, self.connection)
   491          return self.build_app(
   492              self.loop,
   493              '/transactions/{transaction_id}',
   494              handlers.fetch_transaction)
   495  
   496      @unittest_run_loop
   497      async def test_txn_get(self):
   498          """Verifies a GET /transactions/{transaction_id} works properly.
   499  
   500          It should send a Protobuf request with:
   501              - a transaction_id property of ID_B
   502  
   503          It will receive a Protobuf response with:
   504              - a transaction with an id of ID_B
   505  
   506          It should send back a JSON response with:
   507              - a response status of 200
   508              - no head property
   509              - a link property that ends in '/transactions/{}'.format(ID_B)
   510              - a data property that is a full batch with an id of ID_B
   511          """
   512          self.connection.preset_response(transaction=Mocks.make_txns(ID_B)[0])
   513  
   514          response = await self.get_assert_200('/transactions/{}'.format(ID_B))
   515          self.connection.assert_valid_request_sent(transaction_id=ID_B)
   516  
   517          self.assertNotIn('head', response)
   518          self.assert_has_valid_link(response, '/transactions/{}'.format(ID_B))
   519          self.assertIn('data', response)
   520          self.assert_txns_well_formed(response['data'], ID_B)
   521  
   522      @unittest_run_loop
   523      async def test_txn_get_with_validator_error(self):
   524          """Verifies GET /transactions/{transaction_id} w/ validator error
   525          breaks.
   526  
   527          It will receive a Protobuf response with:
   528              - a status of INTERNAL_ERROR
   529  
   530          It should send back a JSON response with:
   531              - a status of 500
   532              - an error property with a code of 10
   533          """
   534          self.connection.preset_response(self.status.INTERNAL_ERROR)
   535          response = await self.get_assert_status(
   536              '/transactions/{}'.format(ID_B), 500)
   537  
   538          self.assert_has_valid_error(response, 10)
   539  
   540      @unittest_run_loop
   541      async def test_txn_get_with_bad_id(self):
   542          """Verifies a GET /transactions/{transaction_id} with unfound id
   543          breaks.
   544  
   545          It will receive a Protobuf response with:
   546              - a status of NO_RESOURCE
   547  
   548          It should send back a JSON response with:
   549              - a response status of 404
   550              - an error property with a code of 72
   551          """
   552          self.connection.preset_response(self.status.NO_RESOURCE)
   553          response = await self.get_assert_status(
   554              '/transactions/{}'.format(ID_D), 404)
   555  
   556          self.assert_has_valid_error(response, 72)