def test_it_applies_filters(self, table): stub = job_event_stub() table.get_item.return_value = {"Item": job_stub()} table.query.return_value = {"Items": [stub]} response = handlers.list_job_events_handler( { "pathParameters": { "job_id": "test" }, "queryStringParameters": { "start_at": "0" }, "multiValueQueryStringParameters": { "filter": ["EventName=QuerySucceeded"] } }, SimpleNamespace()) resp_body = json.loads(response["body"]) assert 200 == response["statusCode"] assert "NextStart" in resp_body assert 1 == table.query.call_count assert 1 == len(resp_body["JobEvents"]) # Filter expression should be an And condition with 2 components # get_expression()["values"] returns the filters being applied as part of the And condition filter_expression = table.query.call_args_list[0][1][ "FilterExpression"] assert isinstance(table.query.call_args_list[0][1]["FilterExpression"], And) assert Attr("Type").eq( "JobEvent") in filter_expression.get_expression()["values"] assert Attr("EventName").begins_with( "QuerySucceeded") in filter_expression.get_expression()["values"] assert 2 == len(filter_expression.get_expression()["values"])
def test_it_returns_page_size_where_job_item_in_initial_query_response( self, table): job = job_stub(JobFinishTime=12345, JobStatus="COMPLETED") table.get_item.return_value = {"Item": job} table.query.side_effect = [ { "Items": [job_event_stub(Sk=str(i)) for i in range(0, 19)], "LastEvaluatedKey": { "Id": job["Id"], "Sk": "19" } }, { "Items": [job_event_stub(Sk="20")] }, ] response = handlers.list_job_events_handler( { "queryStringParameters": None, "pathParameters": { "job_id": "test" }, "multiValueQueryStringParameters": None, }, SimpleNamespace()) resp_body = json.loads(response["body"]) assert 2 == table.query.call_count assert 200 == response["statusCode"] assert 20 == len(resp_body["JobEvents"]) assert "NextStart" not in resp_body
def test_it_returns_last_item_watermark_where_not_last_page_and_job_complete( self, table): job = job_stub(JobFinishTime=12345, JobStatus="COMPLETED") table.get_item.return_value = {"Item": job} table.query.return_value = { "Items": [job_event_stub(Sk=str(i)) for i in range(0, 20)], "LastEvaluatedKey": { "Id": job["Id"], "Sk": "19" }, } response = handlers.list_job_events_handler( { "queryStringParameters": {}, "pathParameters": { "job_id": "test" }, "multiValueQueryStringParameters": { "filter": ["EventName=QuerySucceeded"] }, }, SimpleNamespace(), ) resp_body = json.loads(response["body"]) assert 200 == response["statusCode"] assert "19" == resp_body["NextStart"]
def test_it_accepts_start_at_earliest_watermark(self, table): stub = job_event_stub() table.get_item.return_value = {"Item": job_stub()} table.query.return_value = { "Items": [stub], "LastEvaluatedKey": { "Id": "test", "Sk": "12345#test" } } response = handlers.list_job_events_handler( { "pathParameters": { "job_id": "test" }, "queryStringParameters": { "start_at": "0" }, "multiValueQueryStringParameters": { "start_at": ["0"] }, }, SimpleNamespace()) resp_body = json.loads(response["body"]) assert 200 == response["statusCode"] assert "NextStart" in resp_body assert { "KeyConditionExpression": mock.ANY, "ScanIndexForward": mock.ANY, "Limit": mock.ANY, "FilterExpression": mock.ANY, "ExclusiveStartKey": { "Id": "test", "Sk": "0" } } == table.query.call_args_list[0][1]
def test_it_handles_watermark_with_microseconds_in_same_second( self, table): stub = job_event_stub() table.get_item.return_value = {"Item": job_stub(JobFinishTime=12345)} table.query.return_value = {"Items": [stub]} response = handlers.list_job_events_handler( { "pathParameters": { "job_id": "test" }, "queryStringParameters": { "start_at": "12345001#test" }, "multiValueQueryStringParameters": { "start_at": ["12345001#test"] }, }, SimpleNamespace()) resp_body = json.loads(response["body"]) assert 200 == response["statusCode"] assert "NextStart" in resp_body table.query.assert_called_with( KeyConditionExpression=mock.ANY, ScanIndexForward=mock.ANY, Limit=mock.ANY, FilterExpression=mock.ANY, ExclusiveStartKey={ "Id": "test", "Sk": "12345001#test" }, )
def test_it_returns_requested_page_size_for_jobs_events(self, table): table.get_item.return_value = {"Item": job_stub()} table.query.return_value = { "Items": [job_event_stub(job_id="job123", sk=str(i)) for i in range(1, 5)], "LastEvaluatedKey": { "Id": "test", "Sk": "12345" } } response = handlers.list_job_events_handler( { "pathParameters": { "job_id": "test" }, "queryStringParameters": { "page_size": "3" }, "multiValueQueryStringParameters": { "page_size": ["3"] }, }, SimpleNamespace()) resp_body = json.loads(response["body"]) table.query.assert_called_with( KeyConditionExpression=mock.ANY, ScanIndexForward=True, Limit=4, FilterExpression=mock.ANY, ExclusiveStartKey=mock.ANY, ) assert 200 == response["statusCode"] assert 3 == len(resp_body["JobEvents"]) assert "3" == resp_body["JobEvents"][len(resp_body["JobEvents"]) - 1]["Sk"] assert "NextStart" in resp_body
def test_it_returns_last_item_watermark_where_ddb_response_is_greater_than_page_size( self, table): job = job_stub(JobStatus="RUNNING") table.get_item.return_value = {"Item": job} # LastEvaluatedKey is determined before the Filter Expression is applied table.query.return_value = { "Items": [job_event_stub(Sk=str(i)) for i in range(0, 100)], "LastEvaluatedKey": { "Id": job["Id"], "Sk": "99" } } response = handlers.list_job_events_handler( { "pathParameters": { "job_id": "test" }, "queryStringParameters": { "start_at": "0" }, "multiValueQueryStringParameters": { "filter": ["EventName=QuerySucceeded"] } }, SimpleNamespace()) resp_body = json.loads(response["body"]) assert 200 == response["statusCode"] assert "19" == resp_body["NextStart"]
def test_it_does_not_return_watermark_if_last_page_reached_on_complete_job( self, table): events = [ "COMPLETED_CLEANUP_FAILED", "COMPLETED", "FAILED", "FIND_FAILED", "FORGET_FAILED", "FORGET_PARTIALLY_FAILED" ] for s in events: event_stub = job_event_stub() table.get_item.return_value = {"Item": job_stub(JobStatus=s)} table.query.return_value = {"Items": [event_stub]} response = handlers.list_job_events_handler( { "pathParameters": { "job_id": "test" }, "queryStringParameters": { "start_at": "0" }, "multiValueQueryStringParameters": { "filter": ["EventName=QuerySucceeded"] } }, SimpleNamespace()) resp_body = json.loads(response["body"]) assert 200 == response["statusCode"] assert "NextStart" not in resp_body
def test_it_rejects_invalid_page_size_for_list_job_events(self): response = handlers.list_job_events_handler( {"queryStringParameters": { "page_size": "NaN" }}, SimpleNamespace()) assert 422 == response["statusCode"] response = handlers.list_job_events_handler( {"queryStringParameters": { "page_size": "0" }}, SimpleNamespace()) assert 422 == response["statusCode"] response = handlers.list_job_events_handler( {"queryStringParameters": { "page_size": "1001" }}, SimpleNamespace()) assert 422 == response["statusCode"]
def test_it_starts_at_earliest_by_default(self, table): stub = job_event_stub() table.get_item.return_value = {"Item": job_stub()} table.query.return_value = {"Items": [stub]} response = handlers.list_job_events_handler( { "queryStringParameters": None, "pathParameters": { "job_id": "test" }, "multiValueQueryStringParameters": None, }, SimpleNamespace(), ) assert 200 == response["statusCode"] table.query.assert_called_with( KeyConditionExpression=mock.ANY, ScanIndexForward=True, Limit=mock.ANY, ExclusiveStartKey={ "Id": "test", "Sk": "0" }, FilterExpression=mock.ANY, )
def test_it_returns_last_item_watermark_for_incomplete_job(self, table): stub = job_event_stub() table.get_item.return_value = {"Item": job_stub(JobStatus="RUNNING")} table.query.return_value = {"Items": [stub]} response = handlers.list_job_events_handler( { "pathParameters": { "job_id": "test" }, }, SimpleNamespace()) resp_body = json.loads(response["body"]) assert 200 == response["statusCode"] assert stub["Sk"] == resp_body["NextStart"]
def test_it_rejects_invalid_filters(self): response = handlers.list_job_events_handler( { "pathParameters": { "job_id": "test" }, "queryStringParameters": { "start_at": "0" }, "multiValueQueryStringParameters": { "filter": ["Invalid=Filter"] } }, SimpleNamespace()) assert 422 == response["statusCode"]
def test_it_returns_error_if_invalid_watermark_supplied_for_running_job( self, table): table.get_item.return_value = {"Item": job_stub()} response = handlers.list_job_events_handler( { "pathParameters": { "job_id": "test" }, "queryStringParameters": { "start_at": "999999999999999#test" }, "multiValueQueryStringParameters": {}, }, SimpleNamespace()) assert 400 == response["statusCode"]
def test_it_lists_jobs_events(self, table): stub = job_event_stub() table.get_item.return_value = {"Item": job_stub()} table.query.return_value = {"Items": [stub]} response = handlers.list_job_events_handler( { "queryStringParameters": None, "pathParameters": { "job_id": "test" }, "multiValueQueryStringParameters": None, }, SimpleNamespace()) resp_body = json.loads(response["body"]) assert 200 == response["statusCode"] assert 1 == len(resp_body["JobEvents"]) assert stub == resp_body["JobEvents"][0]
def test_it_errors_if_job_not_found(self, table): table.get_item.side_effect = ClientError( {"ResponseMetadata": { "HTTPStatusCode": 404 }}, "get_item") response = handlers.list_job_events_handler( { "pathParameters": { "job_id": "test" }, "queryStringParameters": { "start_at": "12345#test" }, "multiValueQueryStringParameters": {}, }, SimpleNamespace()) assert 404 == response["statusCode"]
def test_it_returns_error_if_invalid_watermark_supplied_for_completed_job( self, table): stub = job_event_stub() table.get_item.return_value = {"Item": job_stub(JobFinishTime=12345)} table.query.return_value = {"Items": [stub]} response = handlers.list_job_events_handler( { "pathParameters": { "job_id": "test" }, "queryStringParameters": { "start_at": "12346001#test" }, "multiValueQueryStringParameters": { "start_at": ["12346001#test"] }, }, SimpleNamespace()) assert 400 == response["statusCode"]
def test_it_returns_provided_watermark_for_no_events_for_incomplete_job( self, table): table.get_item.return_value = {"Item": job_stub(JobStatus="RUNNING")} table.query.return_value = {"Items": []} response = handlers.list_job_events_handler( { "pathParameters": { "job_id": "test" }, "queryStringParameters": { "start_at": "111111#trgwtrwgergewrgwgrw" }, "multiValueQueryStringParameters": { "start_at": ["111111#trgwtrwgergewrgwgrw"] }, }, SimpleNamespace()) resp_body = json.loads(response["body"]) assert 200 == response["statusCode"] assert "111111#trgwtrwgergewrgwgrw" == resp_body["NextStart"]