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