def test_parse_of_state_is_the_state_itself(): x1 = State() assert State.parse(x1) is x1 x2 = State.parse(x1, comment="x2") assert x2 is not x1 assert x2.comment == "x2" assert x1.comment is None assert State.parse(x2) is x2
def test_task_state_with_retry(): source = { "Type": "Task", "Resource": "arn:aws:swf:us-east-1:123456789012:task:X", "Next": "Y", "Retry": [{ "ErrorEquals": ["ErrorA", "ErrorB"], "IntervalSeconds": 1, "BackoffRate": 2, "MaxAttempts": 2 }, { "ErrorEquals": ["ErrorC"], "IntervalSeconds": 5 }], "Catch": [{ "ErrorEquals": ["States.ALL"], "Next": "Z", }] } state = State.parse(source) assert isinstance(state, Task) assert len(state.retry) == 2 assert len(state.catch) == 1 assert state.compile() == source
def test_format_result_selector_returns_applied_result(result_path, expected_result): state = State.parse(result_path) input = { "resourceType": "elasticmapreduce", "resource": "createCluster.sync", "output": { "SdkHttpMetadata": { "HttpHeaders": { "Content-Length": "1112", "Content-Type": "application/x-amz-JSON-1.1", "Date": "Mon, 25 Nov 2019 19:41:29 GMT", "x-amzn-RequestId": "1234-5678-9012" }, "HttpStatusCode": 200 }, "SdkResponseMetadata": { "RequestId": "1234-5678-9012" }, "ClusterId": "AKIAIOSFODNN7EXAMPLE" } } result = state.format_result_selector(input) result_final = state.format_result({"OtherDataFromInput": {}}, result) assert expected_result == result_final
def test_format_result_returns_applied_result(result_path, expected_result): state = State.parse({ "ResultPath": result_path, }) result = state.format_result_selector("ok") result = state.format_result({"guid": "123-456"}, result) assert expected_result == result
def test_dictionary_with_no_type_defaults_to_task(): state = State.parse({ "InputPath": "$.first_input", "ResultPath": "$.first_output", "Resource": "MultiplierByTwo", }) assert state.type == States.Task
def test_executes_wait_state(input, expected_output): wait = State.parse({ "Type": "Wait", "Seconds": 10, "Next": "NextState", }) next_state, output = wait.execute(input=input) assert next_state == "NextState" assert expected_output == output
def test_task_uses_resource_as_default_name_ahead_of_comment(): source = { "Type": "Task", "Resource": "arn:something", "Comment": "this is doing something", } state = State.parse(source) assert state.name == "arn:something"
def test_choice_state(example): source = example("choice_state_x")["States"]["ChoiceStateX"] state = State.parse(source) assert isinstance(state, Choice) assert isinstance(state.choices[0], ChoiceRule) assert state.choices[0].operator assert state.compile() == source
def test_suceed_state(): source = { "Type": "Succeed", } state = State.parse(source) assert isinstance(state, Succeed) assert state.type == "Succeed" assert state.compile() == source
def test_executes_fail_state(): fail = State.parse({ "Type": "Fail", "Error": "ErrorA", "Cause": "Kaiju attack", }) # TODO No idea what should be the next state or output of fail state. # TODO Should it just raise an exception? next_state, output = fail.execute(input=input) assert next_state is None
def test_fail_state(): source = { "Type": "Fail", "Error": "ErrorA", "Cause": "Kaiju attack", } state = State.parse(source) assert isinstance(state, Fail) assert state.compile() == source
def test_wait_state(extras): source = { "Type": "Wait", "Next": "NextState", **extras, } state = State.parse(source) assert isinstance(state, Wait) assert state.type == "Wait" assert state.next == "NextState" assert state.compile() == source
def test_task_state(): source = { "Comment": "Task State example", "Type": "Task", "Resource": "arn:aws:swf:us-east-1:123456789012:task:HelloWorld", "Next": "NextState", "TimeoutSeconds": 300, "HeartbeatSeconds": 60 } state = State.parse(source) assert isinstance(state, Task) assert state.compile() == source
def test_pass_state(): source = { "Type": "Pass", "Result": { "x-datum": 0.381018, "y-datum": 622.2269926397355 }, "ResultPath": "$.coords", "Next": "End", } state = State.parse(source) assert isinstance(state, Pass) assert state.result_path == "$.coords" assert state.compile() == source
def test_executes_simple_choice(input, expected): choice = State.parse({ "Type": "Choice", "Choices": [{ "Variable": "$.status", "StringEquals": "FAILED", "Next": "Job Failed", }, { "Variable": "$.status", "StringEquals": "SUCCEEDED", "Next": "Consolidator Output", }], "Default": "Wait X Seconds", }) returned = choice.execute(input=input, resource_resolver=jsp_resource_resolver) assert returned == expected
def test_parallel_state(): source = { "Type": "Parallel", "Branches": [ { "StartAt": "LookupAddress", "States": { "LookupAddress": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:AddressFinder", "End": True, }, }, }, { "StartAt": "LookupPhone", "States": { "LookupPhone": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:PhoneFinder", "End": True, }, }, }, ], "Next": "NextState", } state = State.parse(source) assert isinstance(state, Parallel) assert isinstance(state.branches[0], Sequence) assert isinstance(state.branches[0].start_at_state, Task) assert json.dumps(state.compile(), sort_keys=True) == json.dumps(source, sort_keys=True)
def test_executes_mipres_input(example): test = [{ "InputPath": "$", "Parameters": { "staticValue": "Just a string", "page[*]": { "staticValue": "Just a string", "error_count.$": "$.page[*].error_count", "page.$": "$.page[*].page", "TAG.$": "$.page[*].status.mipres_result.MIPRES_TAG" } } }, { 'staticValue': 'Just a string', 'page': [{ 'error_count': 1, 'page': '2' }, { 'error_count': 1, 'page': '3' }, { 'error_count': 0, 'page': '1', 'TAG': 'MiPres' }, { 'error_count': 1, 'page': '4' }] }] result_path = test[0] expected_result = test[1] mipres_out = example("mipres.out") state = State.parse(result_path) result = state.get_input(mipres_out) assert expected_result == result
def test_format_input_all_applied_result(result_path, expected_result): state = State.parse(result_path) input = { "version": 4, "library": { "movies": [{ "genre": "crime", "director": "Quentin Tarantino", "title": "Reservoir Dogs", "year": 1992 }, { "genre": "action", "director": "Brian De Palma", "title": "Mission: Impossible", "year": 1996, "staring": ["Tom Cruise"] }], "metadata": { "lastUpdated": "2020-05-27T08:00:00.000Z" } } } result = state.get_input(input) assert expected_result == result
def test_format_result_all_applied_result(result_path, expected_result): state = State.parse(result_path) output = { "ExecutedVersion": "$LATEST", "Payload": { "statusCode": "200", "body": "hello, world!" }, "SdkHttpMetadata": { "HttpHeaders": { "Connection": "keep-alive", "Content-Length": "43", "Content-Type": "application/json", "Date": "Thu, 16 Apr 2020 17:58:15 GMT", "X-Amz-Executed-Version": "$LATEST", "x-amzn-Remapped-Content-Length": "0", "x-amzn-RequestId": "88fba57b-adbe-467f-abf4-daca36fc9028", "X-Amzn-Trace-Id": "root=1-5e989cb6-90039fd8971196666b022b62;sampled=0" }, "HttpStatusCode": 200 }, "SdkResponseMetadata": { "RequestId": "88fba57b-adbe-467f-abf4-daca36fc9028" }, "StatusCode": 200 } next_state, result = state.get_output({"OtherDataFromInput": {}}, output) assert expected_result == result
def test_format_state_output_returns_filtered_output(output_path, expected_state_output): state = State.parse({"OutputPath": output_path}) state_output = state.format_state_output({"guid": "123-456"}) assert expected_state_output == state_output
def test_format_resource_input_returns_filtered_input(input_path, expected_resource_input): state = State.parse({"InputPath": input_path}) resource_input = state.format_state_input({"guid": "123-456"}) assert expected_resource_input == resource_input
def test_task_state_compiled_always_has_next_or_end(): task = State.parse({ "Type": "Task", }).compile() assert task.get("Next") or task["End"] is True