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)