github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/rest_api/tests/unit/test_batch_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_batch_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 BatchListTests(BaseApiTest):
    31      async def get_application(self):
    32          self.set_status_and_connection(
    33              Message.CLIENT_BATCH_LIST_REQUEST,
    34              client_batch_pb2.ClientBatchListRequest,
    35              client_batch_pb2.ClientBatchListResponse)
    36  
    37          handlers = self.build_handlers(self.loop, self.connection)
    38          return self.build_app(self.loop, '/batches', handlers.list_batches)
    39  
    40      @unittest_run_loop
    41      async def test_batch_list(self):
    42          """Verifies a GET /batches without parameters works properly.
    43  
    44          It will receive a Protobuf response with:
    45              - a head id of ID_C
    46              - a paging response with a start of ID_C and limit of 100
    47              - three batches with ids of 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 response status of 200
    54              - a head property of ID_C
    55              - a link property that ends in
    56                  '/batches?start={}&limit=100&head={}'.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 batches with ids ID_C, ID_B, and ID_A
    60          """
    61          paging = Mocks.make_paging_response("", ID_C, DEFAULT_LIMIT)
    62          batches = Mocks.make_batches(ID_C, ID_B, ID_A)
    63          self.connection.preset_response(
    64              head_id=ID_C, paging=paging, batches=batches)
    65  
    66          response = await self.get_assert_200('/batches')
    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, '/batches?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_batches_well_formed(response['data'], ID_C, ID_B, ID_A)
    76  
    77      @unittest_run_loop
    78      async def test_batch_list_with_validator_error(self):
    79          """Verifies a GET /batches 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('/batches', 500)
    90  
    91          self.assert_has_valid_error(response, 10)
    92  
    93      @unittest_run_loop
    94      async def test_batch_list_with_no_genesis(self):
    95          """Verifies a GET /batches 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('/batches', 503)
   106  
   107          self.assert_has_valid_error(response, 15)
   108  
   109      @unittest_run_loop
   110      async def test_batch_list_with_head(self):
   111          """Verifies a GET /batches with a head parameter works properly.
   112  
   113          It will receive a Protobuf response with:
   114              - a head id of ID_B
   115              - a paging response with a start ID_B and limit 100
   116              - two batches with ids of 1' 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 response status of 200
   124              - a head property of ID_B
   125              - a link property that ends in
   126                  '/batches?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 batches with ids ID_B and ID_A
   130          """
   131          paging = Mocks.make_paging_response("", ID_B, DEFAULT_LIMIT)
   132          batches = Mocks.make_batches(ID_B, ID_A)
   133          self.connection.preset_response(
   134              head_id=ID_B, paging=paging, batches=batches)
   135  
   136          response = await self.get_assert_200('/batches?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, '/batches?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_batches_well_formed(response['data'], ID_B, ID_A)
   147  
   148      @unittest_run_loop
   149      async def test_batch_list_with_bad_head(self):
   150          """Verifies a GET /batches 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 response 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(
   161              '/batches?head={}'.format(ID_D), 404)
   162  
   163          self.assert_has_valid_error(response, 50)
   164  
   165      @unittest_run_loop
   166      async def test_batch_list_with_ids(self):
   167          """Verifies GET /batches with an id filter works properly.
   168  
   169          It will receive a Protobuf response with:
   170              - a head id of ID_C
   171              - a paging response with start of ID_C and limit 100
   172              - two batches with ids of ID_A and ID_C
   173  
   174          It should send a Protobuf request with:
   175              - a batch_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                  '/batches?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 batches with ids ID_A and ID_C
   187          """
   188          paging = Mocks.make_paging_response("", ID_C, DEFAULT_LIMIT)
   189          batches = Mocks.make_batches(ID_A, ID_C)
   190          self.connection.preset_response(
   191              head_id=ID_C, paging=paging, batches=batches)
   192  
   193          response = await self.get_assert_200('/batches?id={},{}'.format(
   194              ID_A, ID_C))
   195          controls = Mocks.make_paging_controls()
   196          self.connection.assert_valid_request_sent(
   197              batch_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, '/batches?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_batches_well_formed(response['data'], ID_A, ID_C)
   206  
   207      @unittest_run_loop
   208      async def test_batch_list_with_bad_ids(self):
   209          """Verifies GET /batches 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 id 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                  '/batches?head={}&start={}&limit=100&id={},{}'
   220                      .format(ID_C, ID_C, ID_B, ID_D)
   221              - a paging property with only a total_count of 0
   222              - a data property that is an empty list
   223          """
   224          paging = Mocks.make_paging_response("", ID_C, DEFAULT_LIMIT)
   225          self.connection.preset_response(
   226              self.status.NO_RESOURCE, head_id=ID_C, paging=paging)
   227          response = await self.get_assert_200('/batches?id={},{}'.format(
   228              ID_B, ID_D))
   229  
   230          self.assert_has_valid_head(response, ID_C)
   231          self.assert_has_valid_link(
   232              response, '/batches?head={}&start={}&limit=100&id={},{}'.format(
   233                  ID_C, ID_C, ID_B, ID_D))
   234          self.assert_has_valid_paging(response, paging)
   235          self.assert_has_valid_data_list(response, 0)
   236  
   237      @unittest_run_loop
   238      async def test_batch_list_with_head_and_ids(self):
   239          """Verifies GET /batches with head and id parameters work properly.
   240  
   241          It should send a Protobuf request with:
   242              - a head_id property of ID_B
   243              - a paging reponse with a start of ID_B and limit of 100
   244              - a batch_ids property of [ID_A]
   245  
   246          It will receive a Protobuf response with:
   247              - a head id of ID_B
   248              - one batch with an id of ID_A
   249              - empty paging controls
   250  
   251          It should send back a JSON response with:
   252              - a response status of 200
   253              - a head property of ID_B
   254              - a link property that ends in
   255                  '/batches?head={}&start={}&limit=100&id={}'
   256                      .format(ID_B, ID_B, ID_A)
   257              - a paging property that matches the paging response
   258              - a data property that is a list of 1 dict
   259              - and that dict is a full batch with an id of ID_A
   260          """
   261          paging = Mocks.make_paging_response("", ID_B, DEFAULT_LIMIT)
   262          batches = Mocks.make_batches(ID_A)
   263          self.connection.preset_response(
   264              head_id=ID_B, paging=paging, batches=batches)
   265  
   266          response = await self.get_assert_200('/batches?id={}&head={}'.format(
   267              ID_A, ID_B))
   268          controls = Mocks.make_paging_controls()
   269          self.connection.assert_valid_request_sent(
   270              head_id=ID_B,
   271              batch_ids=[ID_A],
   272              paging=controls)
   273  
   274          self.assert_has_valid_head(response, ID_B)
   275          self.assert_has_valid_link(
   276              response, '/batches?head={}&start={}&limit=100&id={}'.format(
   277                  ID_B, ID_B, ID_A))
   278          self.assert_has_valid_paging(response, paging)
   279          self.assert_has_valid_data_list(response, 1)
   280          self.assert_batches_well_formed(response['data'], ID_A)
   281  
   282      @unittest_run_loop
   283      async def test_batch_list_paginated(self):
   284          """Verifies GET /batches paginated by start works properly.
   285  
   286          It will receive a Protobuf response with:
   287              - a head id of ID_D
   288              - a paging response with a start of ID_D, next of ID_C and
   289                limit of 1
   290              - one batch with the id ID_C
   291  
   292          It should send a Protobuf request with:
   293              - paging controls with a start of 1
   294  
   295          It should send back a JSON response with:
   296              - a response status of 200
   297              - a head property of ID_D
   298              - a link property that ends in
   299                  '/batches?head={}&start={}&limit=1'.format(ID_D, ID_D)
   300              - paging that matches the response, with next and previous links
   301              - a data property that is a list of 1 dict
   302              - and that dict is a full batch with the id ID_C
   303  
   304          """
   305          paging = Mocks.make_paging_response(ID_C, ID_D, 1)
   306          batches = Mocks.make_batches(ID_C)
   307          self.connection.preset_response(
   308              head_id=ID_D, paging=paging, batches=batches)
   309  
   310          response = await self.get_assert_200('/batches?start=1&limit=1')
   311          controls = Mocks.make_paging_controls(1, start="1")
   312          self.connection.assert_valid_request_sent(paging=controls)
   313  
   314          self.assert_has_valid_head(response, ID_D)
   315          self.assert_has_valid_link(
   316              response, '/batches?head={}&start={}&limit=1'.format(ID_D, ID_D))
   317          self.assert_has_valid_paging(
   318              response, paging, '/batches?head={}&start={}&limit=1'.format(
   319                  ID_D, ID_C))
   320          self.assert_has_valid_data_list(response, 1)
   321          self.assert_batches_well_formed(response['data'], ID_C)
   322  
   323      @unittest_run_loop
   324      async def test_batch_list_with_zero_limit(self):
   325          """Verifies a GET /batches with a limit of zero breaks properly.
   326  
   327          It should send back a JSON response with:
   328              - a response status of 400
   329              - an error property with a code of 53
   330          """
   331          response = await self.get_assert_status('/batches?start=2&limit=0',
   332                                                  400)
   333  
   334          self.assert_has_valid_error(response, 53)
   335  
   336      @unittest_run_loop
   337      async def test_batch_list_with_bad_paging(self):
   338          """Verifies a GET /batches with a bad paging breaks properly.
   339  
   340          It will receive a Protobuf response with:
   341              - a status of INVALID_PAGING
   342  
   343          It should send back a JSON response with:
   344              - a response status of 400
   345              - an error property with a code of 54
   346          """
   347          self.connection.preset_response(self.status.INVALID_PAGING)
   348          response = await self.get_assert_status('/batches?start=-1', 400)
   349  
   350          self.assert_has_valid_error(response, 54)
   351  
   352      @unittest_run_loop
   353      async def test_batch_list_paginated_with_just_limit(self):
   354          """Verifies GET /batches paginated just by limit works properly.
   355  
   356          It will receive a Protobuf response with:
   357              - a head id of ID_D
   358              - a paging response with a start ID_D, next ID_B, and limit pf 2
   359              - two batches with the ids ID_D and ID_C
   360  
   361          It should send a Protobuf request with:
   362              - paging controls with a limit of 2
   363  
   364          It should send back a JSON response with:
   365              - a response status of 200
   366              - a head property of ID_D
   367              - a link property that ends in
   368                  '/batches?head={}&start={}&limit=2'.format(ID_D, ID_D)
   369              - paging that matches the response with a next link
   370              - a data property that is a list of 2 dicts
   371              - and those dicts are full batches with ids ID_D and ID_C
   372          """
   373          paging = Mocks.make_paging_response(ID_B, ID_D, 2)
   374          batches = Mocks.make_batches(ID_D, ID_C)
   375          self.connection.preset_response(
   376              head_id=ID_D, paging=paging, batches=batches)
   377  
   378          response = await self.get_assert_200('/batches?limit=2')
   379          controls = Mocks.make_paging_controls(2)
   380          self.connection.assert_valid_request_sent(paging=controls)
   381  
   382          self.assert_has_valid_head(response, ID_D)
   383          self.assert_has_valid_link(
   384              response, '/batches?head={}&start={}&limit=2'.format(ID_D, ID_D))
   385          self.assert_has_valid_paging(
   386              response, paging, '/batches?head={}&start={}&limit=2'.format(
   387                  ID_D, ID_B))
   388          self.assert_has_valid_data_list(response, 2)
   389          self.assert_batches_well_formed(response['data'], ID_D, ID_C)
   390  
   391      @unittest_run_loop
   392      async def test_batch_list_paginated_without_count(self):
   393          """Verifies GET /batches paginated without count works properly.
   394  
   395          It will receive a Protobuf response with:
   396              - a head id of ID_D
   397              - a paging response with start of ID_B and limit of 100
   398              - two batches with the ids ID_B and ID_A
   399  
   400          It should send a Protobuf request with:
   401              - paging controls with a start of 2
   402  
   403          It should send back a JSON response with:
   404              - a response status of 200
   405              - a head property of ID_D
   406              - a link property that ends in
   407                  '/batches?head={}&start={}&limit=100'.format(ID_D, ID_B)
   408              - paging that matches the response, with a previous link
   409              - a data property that is a list of 2 dicts
   410              - and those dicts are full batches with ids ID_D and ID_C
   411          """
   412          paging = Mocks.make_paging_response("", ID_B, DEFAULT_LIMIT)
   413          batches = Mocks.make_batches(ID_B, ID_A)
   414          self.connection.preset_response(
   415              head_id=ID_D, paging=paging, batches=batches)
   416  
   417          response = await self.get_assert_200('/batches?start={}'.format(ID_B))
   418          controls = Mocks.make_paging_controls(None, start=ID_B)
   419          self.connection.assert_valid_request_sent(paging=controls)
   420  
   421          self.assert_has_valid_head(response, ID_D)
   422          self.assert_has_valid_link(
   423              response, '/batches?head={}&start={}&limit=100'.format(ID_D, ID_B))
   424          self.assert_has_valid_paging(response, paging)
   425          self.assert_has_valid_data_list(response, 2)
   426          self.assert_batches_well_formed(response['data'], ID_B, ID_A)
   427  
   428      @unittest_run_loop
   429      async def test_batch_list_paginated_by_start_id(self):
   430          """Verifies GET /batches paginated by a start id works properly.
   431  
   432          It will receive a Protobuf response with:
   433              - a head id of ID_D
   434              - an empty paging response
   435              - three batches with the ids ID_C, ID_B and ID_A
   436  
   437          It should send a Protobuf request with:
   438              - paging controls with a limit of 5, and a start of ID_C
   439  
   440          It should send back a JSON response with:
   441              - a response status of 200
   442              - a head property of ID_D
   443              - a link property that ends in
   444                  '/batches?head={}&start={}&limit=5'
   445                      .format(ID_D, 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 batches with ids ID_C, ID_B, and ID_A
   449          """
   450          paging = Mocks.make_paging_response("", ID_C, 5)
   451          batches = Mocks.make_batches(ID_C, ID_B, ID_A)
   452          self.connection.preset_response(
   453              head_id=ID_D, paging=paging, batches=batches)
   454  
   455          response = await self.get_assert_200(
   456              '/batches?start={}&limit=5'.format(ID_C))
   457          controls = Mocks.make_paging_controls(5, start=ID_C)
   458          self.connection.assert_valid_request_sent(paging=controls)
   459  
   460          self.assert_has_valid_head(response, ID_D)
   461          self.assert_has_valid_link(
   462              response, '/batches?head={}&start={}&limit=5'.format(ID_D, ID_C))
   463          self.assert_has_valid_paging(response, paging)
   464          self.assert_has_valid_data_list(response, 3)
   465          self.assert_batches_well_formed(response['data'], ID_C, ID_B, ID_A)
   466  
   467      @unittest_run_loop
   468      async def test_batch_list_sorted_in_reverse(self):
   469          """Verifies a GET /batches can send proper sort parameters.
   470  
   471          It will receive a Protobuf response with:
   472              - a head id of ID_C
   473              - a paging response with start of ID_C and limit of 100
   474              - three batches with ids ID_C, ID_B, and ID_A
   475  
   476          It should send a Protobuf request with:
   477              - empty paging controls
   478              - sort controls with a key of 'header_signature' that is reversed
   479  
   480          It should send back a JSON response with:
   481              - a status of 200
   482              - a head property of ID_C
   483              - a link property ending in
   484                  '/batches?head={}&start={}&limit=100&reverse'
   485                      .format(ID_C, ID_C))
   486              - a paging property that matches the paging response
   487              - a data property that is a list of 3 dicts
   488              - and those dicts are full batches with ids ID_C, ID_B, and ID_A
   489          """
   490          paging = Mocks.make_paging_response("", ID_C, DEFAULT_LIMIT)
   491          batches = Mocks.make_batches(ID_C, ID_B, ID_A)
   492          self.connection.preset_response(
   493              head_id=ID_C, paging=paging, batches=batches)
   494  
   495          response = await self.get_assert_200('/batches?reverse')
   496          page_controls = Mocks.make_paging_controls()
   497          sorting = Mocks.make_sort_controls('default', reverse=True)
   498          self.connection.assert_valid_request_sent(
   499              paging=page_controls,
   500              sorting=sorting)
   501  
   502          self.assert_has_valid_head(response, ID_C)
   503          self.assert_has_valid_link(
   504              response, '/batches?head={}&start={}&limit=100&reverse'.format(
   505                  ID_C, ID_C))
   506          self.assert_has_valid_paging(response, paging)
   507          self.assert_has_valid_data_list(response, 3)
   508          self.assert_batches_well_formed(response['data'], ID_C, ID_B, ID_A)
   509  
   510  
   511  class BatchGetTests(BaseApiTest):
   512      async def get_application(self):
   513          self.set_status_and_connection(
   514              Message.CLIENT_BATCH_GET_REQUEST,
   515              client_batch_pb2.ClientBatchGetRequest,
   516              client_batch_pb2.ClientBatchGetResponse)
   517  
   518          handlers = self.build_handlers(self.loop, self.connection)
   519          return self.build_app(
   520              self.loop, '/batches/{batch_id}', handlers.fetch_batch)
   521  
   522      @unittest_run_loop
   523      async def test_batch_get(self):
   524          """Verifies a GET /batches/{batch_id} works properly.
   525  
   526          It should send a Protobuf request with:
   527              - a batch_id property of ID_B
   528  
   529          It will receive a Protobuf response with:
   530              - a batch with an id of ID_B
   531  
   532          It should send back a JSON response with:
   533              - a response status of 200
   534              - no head property
   535              - a link property that ends in '/batches/{}'.format(ID_B)
   536              - a data property that is a full batch with an id of ID_B
   537          """
   538          self.connection.preset_response(batch=Mocks.make_batches(ID_B)[0])
   539  
   540          response = await self.get_assert_200('/batches/{}'.format(ID_B))
   541          self.connection.assert_valid_request_sent(batch_id=ID_B)
   542  
   543          self.assertNotIn('head', response)
   544          self.assert_has_valid_link(response, '/batches/{}'.format(ID_B))
   545          self.assertIn('data', response)
   546          self.assert_batches_well_formed(response['data'], ID_B)
   547  
   548      @unittest_run_loop
   549      async def test_batch_get_with_validator_error(self):
   550          """Verifies GET /batches/{batch_id} w/ validator error breaks properly.
   551  
   552          It will receive a Protobuf response with:
   553              - a status of INTERNAL_ERROR
   554  
   555          It should send back a JSON response with:
   556              - a status of 500
   557              - an error property with a code of 10
   558          """
   559          self.connection.preset_response(self.status.INTERNAL_ERROR)
   560          response = await self.get_assert_status('/batches/{}'.format(ID_B),
   561                                                  500)
   562  
   563          self.assert_has_valid_error(response, 10)
   564  
   565      @unittest_run_loop
   566      async def test_batch_get_with_bad_id(self):
   567          """Verifies a GET /batches/{batch_id} with unfound id breaks properly.
   568  
   569          It will receive a Protobuf response with:
   570              - a status of NO_RESOURCE
   571  
   572          It should send back a JSON response with:
   573              - a response status of 404
   574              - an error property with a code of 71
   575          """
   576          self.connection.preset_response(self.status.NO_RESOURCE)
   577          response = await self.get_assert_status('/batches/{}'.format(ID_D),
   578                                                  404)
   579  
   580          self.assert_has_valid_error(response, 71)