github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/rest_api/tests/unit/test_block_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  from components import Mocks, BaseApiTest
    18  from sawtooth_rest_api.protobuf.validator_pb2 import Message
    19  from sawtooth_rest_api.protobuf import client_block_pb2
    20  
    21  
    22  ID_A = 'a' * 128
    23  ID_B = 'b' * 128
    24  ID_C = 'c' * 128
    25  ID_D = 'd' * 128
    26  
    27  DEFAULT_LIMIT = 100
    28  
    29  
    30  class BlockListTests(BaseApiTest):
    31      async def get_application(self):
    32          self.set_status_and_connection(
    33              Message.CLIENT_BLOCK_LIST_REQUEST,
    34              client_block_pb2.ClientBlockListRequest,
    35              client_block_pb2.ClientBlockListResponse)
    36  
    37          handlers = self.build_handlers(self.loop, self.connection)
    38          return self.build_app(self.loop, '/blocks', handlers.list_blocks)
    39  
    40      @unittest_run_loop
    41      async def test_block_list(self):
    42          """Verifies a GET /blocks without parameters works properly.
    43  
    44          It will receive a Protobuf response with:
    45              - a head id of ID_C
    46              - a paging reponse with start of ID_C and limit of 100
    47              - three blocks with ids ID_C, ID_B, and ID_A
    48  
    49          It should send a Protobuf request with:
    50              - empty paging controls
    51  
    52          It should send back a JSON response with:
    53              - a status of 200
    54              - a head property of ID_C
    55              - a link property that ends in
    56                  '/blocks?head={}&start={}&limit=100'.format(ID_C, ID_C))
    57              - a paging property that matches the paging response
    58              - a data property that is a list of 3 dicts
    59              - and those dicts are full blocks with ids ID_C, ID_B, and ID_A
    60          """
    61          paging = Mocks.make_paging_response("", ID_C, DEFAULT_LIMIT)
    62          blocks = Mocks.make_blocks(ID_C, ID_B, ID_A)
    63          self.connection.preset_response(
    64              head_id=ID_C, paging=paging, blocks=blocks)
    65  
    66          response = await self.get_assert_200('/blocks')
    67          controls = Mocks.make_paging_controls()
    68          self.connection.assert_valid_request_sent(paging=controls)
    69  
    70          self.assert_has_valid_head(response, ID_C)
    71          self.assert_has_valid_link(
    72              response, '/blocks?head={}&start={}&limit=100'.format(ID_C, ID_C))
    73          self.assert_has_valid_paging(response, paging)
    74          self.assert_has_valid_data_list(response, 3)
    75          self.assert_blocks_well_formed(response['data'], ID_C, ID_B, ID_A)
    76  
    77      @unittest_run_loop
    78      async def test_block_list_with_validator_error(self):
    79          """Verifies a GET /blocks with a validator error breaks properly.
    80  
    81          It will receive a Protobuf response with:
    82              - a status of INTERNAL_ERROR
    83  
    84          It should send back a JSON response with:
    85              - a status of 500
    86              - an error property with a code of 10
    87          """
    88          self.connection.preset_response(self.status.INTERNAL_ERROR)
    89          response = await self.get_assert_status('/blocks', 500)
    90  
    91          self.assert_has_valid_error(response, 10)
    92  
    93      @unittest_run_loop
    94      async def test_block_list_with_no_genesis(self):
    95          """Verifies a GET /blocks with validator not ready breaks properly.
    96  
    97          It will receive a Protobuf response with:
    98              - a status of NOT_READY
    99  
   100          It should send back a JSON response with:
   101              - a status of 503
   102              - an error property with a code of 15
   103          """
   104          self.connection.preset_response(self.status.NOT_READY)
   105          response = await self.get_assert_status('/blocks', 503)
   106  
   107          self.assert_has_valid_error(response, 15)
   108  
   109      @unittest_run_loop
   110      async def test_block_list_with_head(self):
   111          """Verifies a GET /blocks with a head parameter works properly.
   112  
   113          It will receive a Protobuf response with:
   114              - a head id of ID_C
   115              - an empty paging response
   116              - three blocks with ids ID_B and ID_A
   117  
   118          It should send a Protobuf request with:
   119              - a head_id property of ID_B
   120              - empty paging controls
   121  
   122          It should send back a JSON response with:
   123              - a status of 200
   124              - a head property of ID_B
   125              - a link property that ends in
   126                  '/blocks?head={}&start={}&limit=100'.format(ID_B, ID_B))
   127              - a paging property that matches the paging response
   128              - a data property that is a list of 2 dicts
   129              - and those dicts are full blocks with ids ID_B and ID_A
   130          """
   131          paging = Mocks.make_paging_response("", ID_B, DEFAULT_LIMIT)
   132          blocks = Mocks.make_blocks(ID_B, ID_A)
   133          self.connection.preset_response(
   134              head_id=ID_B, paging=paging, blocks=blocks)
   135  
   136          response = await self.get_assert_200('/blocks?head={}'.format(ID_B))
   137          controls = Mocks.make_paging_controls()
   138          self.connection.assert_valid_request_sent(
   139              head_id=ID_B, paging=controls)
   140  
   141          self.assert_has_valid_head(response, ID_B)
   142          self.assert_has_valid_link(
   143              response, '/blocks?head={}&start={}&limit=100'.format(ID_B, ID_B))
   144          self.assert_has_valid_paging(response, paging)
   145          self.assert_has_valid_data_list(response, 2)
   146          self.assert_blocks_well_formed(response['data'], ID_B, ID_A)
   147  
   148      @unittest_run_loop
   149      async def test_block_list_with_bad_head(self):
   150          """Verifies a GET /blocks with a bad head breaks properly.
   151  
   152          It will receive a Protobuf response with:
   153              - a status of NO_ROOT
   154  
   155          It should send back a JSON response with:
   156              - a status of 404
   157              - an error property with a code of 50
   158          """
   159          self.connection.preset_response(self.status.NO_ROOT)
   160          response = await self.get_assert_status('/blocks?head={}'.format(ID_D),
   161                                                  404)
   162  
   163          self.assert_has_valid_error(response, 50)
   164  
   165      @unittest_run_loop
   166      async def test_block_list_with_ids(self):
   167          """Verifies GET /blocks with an id filter works properly.
   168  
   169          It will receive a Protobuf response with:
   170              - a head id of ID_C
   171              - an empty paging response
   172              - two blocks with ids ID_A and ID_C
   173  
   174          It should send a Protobuf request with:
   175              - a block_ids property of [ID_A, ID_C]
   176              - empty paging controls
   177  
   178          It should send back a JSON response with:
   179              - a response status of 200
   180              - a head property of ID_C, the latest
   181              - a link property that ends in
   182                  '/blocks?head={}&start={}&limit=100&id={},{}'
   183                      .format(ID_C, ID_C, ID_A, ID_C))
   184              - a paging property that matches the paging response
   185              - a data property that is a list of 2 dicts
   186              - and those dicts are full blocks with ids ID_A and ID_C
   187          """
   188          paging = Mocks.make_paging_response("", ID_C, DEFAULT_LIMIT)
   189          blocks = Mocks.make_blocks(ID_A, ID_C)
   190          self.connection.preset_response(
   191              head_id=ID_C, paging=paging, blocks=blocks)
   192  
   193          response = await self.get_assert_200('/blocks?id={},{}'.format(
   194              ID_A, ID_C))
   195          controls = Mocks.make_paging_controls()
   196          self.connection.assert_valid_request_sent(
   197              block_ids=[ID_A, ID_C], paging=controls)
   198  
   199          self.assert_has_valid_head(response, ID_C)
   200          self.assert_has_valid_link(
   201              response, '/blocks?head={}&start={}&limit=100&id={},{}'.format(
   202                  ID_C, ID_C, ID_A, ID_C))
   203          self.assert_has_valid_paging(response, paging)
   204          self.assert_has_valid_data_list(response, 2)
   205          self.assert_blocks_well_formed(response['data'], ID_A, ID_C)
   206  
   207      @unittest_run_loop
   208      async def test_block_list_with_bad_ids(self):
   209          """Verifies GET /blocks with a bad id filter breaks properly.
   210  
   211          It will receive a Protobuf response with:
   212              - a status of NO_RESOURCE
   213              - a head property of ID_C
   214  
   215          It should send back a JSON response with:
   216              - a response status of 200
   217              - a head property of ID_C, the latest
   218              - a link property that ends in
   219                  '/blocks?head={}&start={}&limit=100&id={},{}'
   220                      .format(ID_C, ID_C, ID_B, ID_D))
   221              - a data property that is an empty list
   222          """
   223          paging = Mocks.make_paging_response("", ID_C, DEFAULT_LIMIT)
   224          self.connection.preset_response(
   225              self.status.NO_RESOURCE, head_id=ID_C, paging=paging)
   226          response = await self.get_assert_200('/blocks?id={},{}'.format(
   227              ID_B, ID_D))
   228  
   229          self.assert_has_valid_head(response, ID_C)
   230          self.assert_has_valid_link(
   231              response, '/blocks?head={}&start={}&limit=100&id={},{}'.format(
   232                  ID_C, ID_C, ID_B, ID_D))
   233          self.assert_has_valid_paging(response, paging)
   234          self.assert_has_valid_data_list(response, 0)
   235  
   236      @unittest_run_loop
   237      async def test_block_list_with_head_and_ids(self):
   238          """Verifies GET /blocks with head and id parameters work properly.
   239  
   240          It will receive a Protobuf response with:
   241              - a head id of ID_B
   242              - an empty paging response
   243              - one block with an id of ID_A
   244  
   245          It should send a Protobuf request with:
   246              - a head_id property of ID_B
   247              - a block_ids property of [ID_A]
   248              - empty paging controls
   249  
   250          It should send back a JSON response with:
   251              - a response status of 200
   252              - a head property of ID_B
   253              - a link property that ends in
   254                  '/blocks?head={}&id={}'.format(ID_B, ID_A)
   255              - a paging property that matches the paging response
   256              - a data property that is a list of 1 dict
   257              - and that dict is a full block with an id of ID_A
   258          """
   259          paging = Mocks.make_paging_response("", ID_B, DEFAULT_LIMIT)
   260          blocks = Mocks.make_blocks(ID_A)
   261          self.connection.preset_response(
   262              head_id=ID_B, paging=paging, blocks=blocks)
   263  
   264          response = await self.get_assert_200('/blocks?id={}&head={}'.format(
   265              ID_A, ID_B))
   266          self.connection.assert_valid_request_sent(
   267              head_id=ID_B,
   268              block_ids=[ID_A],
   269              paging=Mocks.make_paging_controls())
   270  
   271          self.assert_has_valid_head(response, ID_B)
   272          self.assert_has_valid_link(
   273              response, '/blocks?head={}&start={}&limit=100&id={}'.format(
   274                  ID_B, ID_B, ID_A))
   275          self.assert_has_valid_paging(response, paging)
   276          self.assert_has_valid_data_list(response, 1)
   277          self.assert_blocks_well_formed(response['data'], ID_A)
   278  
   279      @unittest_run_loop
   280      async def test_block_list_paginated(self):
   281          """Verifies GET /blocks paginated by min id works properly.
   282  
   283          It will receive a Protobuf response with:
   284              - a head id of ID_D
   285              - a paging response with a next of '0x0002', start of
   286                0x0003 and limit of 1
   287              - one block with the id ID_C
   288  
   289          It should send a Protobuf request with:
   290              - paging controls with a limit of 1, and a start_id of
   291                start='0x0003'
   292  
   293          It should send back a JSON response with:
   294              - a response status of 200
   295              - a head property of ID_D
   296              - a link property that ends in
   297                  '/blocks?head={}&start=1&limit=1'.format(ID_D)
   298              - paging that matches the response, with next and previous links
   299              - a data property that is a list of 1 dict
   300              - and that dict is a full block with the id ID_C
   301  
   302          """
   303          # Block list only returns a next id
   304          paging = Mocks.make_paging_response('0x0002', "0x0003", 1)
   305          blocks = Mocks.make_blocks(ID_C)
   306          self.connection.preset_response(
   307              head_id=ID_D, paging=paging, blocks=blocks)
   308  
   309          response = await self.get_assert_200('/blocks?start=0x0003&limit=1')
   310          controls = Mocks.make_paging_controls(1, start='0x0003')
   311          self.connection.assert_valid_request_sent(paging=controls)
   312  
   313          self.assert_has_valid_head(response, ID_D)
   314          self.assert_has_valid_link(
   315              response, '/blocks?head={}&start=0x0003&limit=1'.format(ID_D))
   316          self.assert_has_valid_paging(
   317              response, paging,
   318              '/blocks?head={}&start=0x0002&limit=1'.format(ID_D))
   319          self.assert_has_valid_data_list(response, 1)
   320          self.assert_blocks_well_formed(response['data'], ID_C)
   321  
   322      @unittest_run_loop
   323      async def test_block_list_with_zero_limit(self):
   324          """Verifies a GET /blocks with a limit of zero breaks properly.
   325  
   326          It should send back a JSON response with:
   327              - a response status of 400
   328              - an error property with a code of 53
   329          """
   330          response = await self.get_assert_status('/blocks?start=2&limit=0', 400)
   331  
   332          self.assert_has_valid_error(response, 53)
   333  
   334      @unittest_run_loop
   335      async def test_block_list_with_bad_paging(self):
   336          """Verifies a GET /blocks with a bad paging breaks properly.
   337  
   338          It will receive a Protobuf response with:
   339              - a status of INVALID_PAGING
   340  
   341          It should send back a JSON response with:
   342              - a response status of 400
   343              - an error property with a code of 54
   344          """
   345          self.connection.preset_response(self.status.INVALID_PAGING)
   346          response = await self.get_assert_status('/blocks?start=-1', 400)
   347  
   348          self.assert_has_valid_error(response, 54)
   349  
   350      @unittest_run_loop
   351      async def test_block_list_paginated_with_just_limit(self):
   352          """Verifies GET /blocks paginated just by limit works properly.
   353  
   354          It will receive a Protobuf response with:
   355              - a head id of ID_D
   356              - a paging response with a next of 0x0002, start of 0x004
   357                and limit of 2
   358              - two blocks with the ids ID_D and ID_C
   359  
   360          It should send a Protobuf request with:
   361              - paging controls with a limit of 2
   362  
   363          It should send back a JSON response with:
   364              - a response status of 200
   365              - a head property of ID_D
   366              - a link property that ends in
   367                  '/blocks?head={}&start=0x0004&limit=2'.format(ID_D)
   368              - paging that matches the response with a next link
   369              - a data property that is a list of 2 dicts
   370              - and those dicts are full blocks with ids ID_D and ID_C
   371  
   372          """
   373          # Block list only returns a next id
   374          paging = Mocks.make_paging_response('0x0002', '0x0004', 2)
   375          blocks = Mocks.make_blocks(ID_D, ID_C)
   376          self.connection.preset_response(
   377              head_id=ID_D, paging=paging, blocks=blocks)
   378  
   379          response = await self.get_assert_200('/blocks?limit=2')
   380          controls = Mocks.make_paging_controls(2)
   381          self.connection.assert_valid_request_sent(paging=controls)
   382  
   383          self.assert_has_valid_head(response, ID_D)
   384          self.assert_has_valid_link(
   385              response, '/blocks?head={}&start=0x0004&limit=2'.format(ID_D))
   386          self.assert_has_valid_paging(
   387              response, paging,
   388              '/blocks?head={}&start=0x0002&limit=2'.format(ID_D))
   389          self.assert_has_valid_data_list(response, 2)
   390          self.assert_blocks_well_formed(response['data'], ID_D, ID_C)
   391  
   392      @unittest_run_loop
   393      async def test_block_list_paginated_without_limit(self):
   394          """Verifies GET /blocks paginated without limit works properly.
   395  
   396          It will receive a Protobuf response with:
   397              - a head id of ID_D
   398              - a paging response with start of 0x0002 and limit of 100
   399              - two blocks with the ids ID_B and ID_A
   400  
   401          It should send a Protobuf request with:
   402              - paging controls with a start of 2
   403  
   404          It should send back a JSON response with:
   405              - a response status of 200
   406              - a head property of ID_D
   407              - a link property that ends in
   408                  '/blocks?head={}&start=0x0002&limit=100'.format(ID_D)
   409              - paging that matches the response, with a previous link
   410              - a data property that is a list of 2 dicts
   411              - and those dicts are full blocks with ids ID_D and ID_C
   412          """
   413          paging = Mocks.make_paging_response("", "0x0002", DEFAULT_LIMIT)
   414          blocks = Mocks.make_blocks(ID_B, ID_A)
   415          self.connection.preset_response(
   416              head_id=ID_D, paging=paging, blocks=blocks)
   417  
   418          response = await self.get_assert_200('/blocks?start=0x0002')
   419          controls = Mocks.make_paging_controls(None, start="0x0002")
   420          self.connection.assert_valid_request_sent(paging=controls)
   421  
   422          self.assert_has_valid_head(response, ID_D)
   423          self.assert_has_valid_link(
   424              response, '/blocks?head={}&start=0x0002&limit=100'.format(ID_D))
   425          self.assert_has_valid_paging(response, paging)
   426          self.assert_has_valid_data_list(response, 2)
   427          self.assert_blocks_well_formed(response['data'], ID_B, ID_A)
   428  
   429      @unittest_run_loop
   430      async def test_block_list_paginated_by_start_id(self):
   431          """Verifies GET /blocks paginated by a start id works properly.
   432  
   433          It will receive a Protobuf response with:
   434              - a head id of ID_D
   435              - a paging response with start of 0x0003 and limit of 5
   436              - three blocks with the ids ID_C, ID_B and ID_A
   437  
   438          It should send a Protobuf request with:
   439              - paging controls with a limit of 5, and a startof ID_C
   440  
   441          It should send back a JSON response with:
   442              - a response status of 200
   443              - a head property of ID_D
   444              - a link property that ends in
   445                  '/blocks?head={}&start=0x0003&limit=5'.format(ID_C)
   446              - paging that matches the response, with a previous link
   447              - a data property that is a list of 3 dicts
   448              - and those dicts are full blocks with ids ID_C, ID_B, and ID_A
   449          """
   450          paging = Mocks.make_paging_response("", "0x0003", 5)
   451          blocks = Mocks.make_blocks(ID_C, ID_B, ID_A)
   452          self.connection.preset_response(
   453              head_id=ID_D, paging=paging, blocks=blocks)
   454  
   455          response = await self.get_assert_200('/blocks?start=0x0003&limit=5')
   456          controls = Mocks.make_paging_controls(5, start="0x0003")
   457          self.connection.assert_valid_request_sent(paging=controls)
   458  
   459          self.assert_has_valid_head(response, ID_D)
   460          self.assert_has_valid_link(
   461              response, '/blocks?head={}&start=0x0003&limit=5'.format(ID_D))
   462          self.assert_has_valid_paging(response, paging)
   463          self.assert_has_valid_data_list(response, 3)
   464          self.assert_blocks_well_formed(response['data'], ID_C, ID_B, ID_A)
   465  
   466      @unittest_run_loop
   467      async def test_block_list_sorted_in_reverse(self):
   468          """Verifies a GET /blocks can send proper sort parameters.
   469  
   470          It will receive a Protobuf response with:
   471              - a head id of ID_C
   472              - a paging response with start ID_C and limit of 100
   473              - three blocks with ids ID_C, ID_B, and ID_A
   474  
   475          It should send a Protobuf request with:
   476              - empty paging controls
   477              - sort controls with a key of 'header_signature' that is reversed
   478  
   479          It should send back a JSON response with:
   480              - a status of 200
   481              - a head property of ID_C
   482              - a link property ending in
   483                  '/blocks?head={}&start={}&limit=100&reverse'.format(ID_C, ID_C)
   484              - a paging property that matches the paging response
   485              - a data property that is a list of 3 dicts
   486              - and those dicts are full blocks with ids ID_C, ID_B, and ID_A
   487          """
   488          paging = Mocks.make_paging_response("", ID_C, DEFAULT_LIMIT)
   489          blocks = Mocks.make_blocks(ID_C, ID_B, ID_A)
   490          self.connection.preset_response(
   491              head_id=ID_C, paging=paging, blocks=blocks)
   492  
   493          response = await self.get_assert_200('/blocks?reverse')
   494          page_controls = Mocks.make_paging_controls()
   495          sorting = Mocks.make_sort_controls('block_num', reverse=True)
   496          self.connection.assert_valid_request_sent(
   497              paging=page_controls, sorting=sorting)
   498  
   499          self.assert_has_valid_head(response, ID_C)
   500          self.assert_has_valid_link(
   501              response, '/blocks?head={}&start={}&limit=100&reverse'.format(
   502                  ID_C, ID_C))
   503          self.assert_has_valid_paging(response, paging)
   504          self.assert_has_valid_data_list(response, 3)
   505          self.assert_blocks_well_formed(response['data'], ID_C, ID_B, ID_A)
   506  
   507  
   508  class BlockGetTests(BaseApiTest):
   509      async def get_application(self):
   510          self.set_status_and_connection(
   511              Message.CLIENT_BLOCK_GET_BY_ID_REQUEST,
   512              client_block_pb2.ClientBlockGetByIdRequest,
   513              client_block_pb2.ClientBlockGetResponse)
   514  
   515          handlers = self.build_handlers(self.loop, self.connection)
   516          return self.build_app(
   517              self.loop, '/blocks/{block_id}', handlers.fetch_block)
   518  
   519      @unittest_run_loop
   520      async def test_block_get(self):
   521          """Verifies a GET /blocks/{block_id} works properly.
   522  
   523          It should send a Protobuf request with:
   524              - a block_id property of ID_B
   525  
   526          It will receive a Protobuf response with:
   527              - a block with an id of ID_B
   528  
   529          It should send back a JSON response with:
   530              - a response status of 200
   531              - no head property
   532              - a link property that ends in '/blocks/{}'.format(ID_B)
   533              - a data property that is a full block with an id of ID_A
   534          """
   535          self.connection.preset_response(block=Mocks.make_blocks(ID_B)[0])
   536  
   537          response = await self.get_assert_200('/blocks/{}'.format(ID_B))
   538          self.connection.assert_valid_request_sent(block_id=ID_B)
   539  
   540          self.assertNotIn('head', response)
   541          self.assert_has_valid_link(response, '/blocks/{}'.format(ID_B))
   542          self.assertIn('data', response)
   543          self.assert_blocks_well_formed(response['data'], ID_B)
   544  
   545      @unittest_run_loop
   546      async def test_block_get_with_validator_error(self):
   547          """Verifies GET /blocks/{block_id} w/ validator error breaks properly.
   548  
   549          It will receive a Protobuf response with:
   550              - a status of INTERNAL_ERROR
   551  
   552          It should send back a JSON response with:
   553              - a status of 500
   554              - an error property with a code of 10
   555          """
   556          self.connection.preset_response(self.status.INTERNAL_ERROR)
   557          response = await self.get_assert_status('/blocks/{}'.format(ID_B), 500)
   558  
   559          self.assert_has_valid_error(response, 10)
   560  
   561      @unittest_run_loop
   562      async def test_block_get_with_bad_id(self):
   563          """Verifies a GET /blocks/{block_id} with unfound id breaks properly.
   564  
   565          It will receive a Protobuf response with:
   566              - a status of NO_RESOURCE
   567  
   568          It should send back a JSON response with:
   569              - a response status of 404
   570              - an error property with a code of 70
   571          """
   572          self.connection.preset_response(self.status.NO_RESOURCE)
   573          response = await self.get_assert_status('/blocks/{}'.format(ID_D), 404)
   574  
   575          self.assert_has_valid_error(response, 70)