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)