Beispiel #1
0
def test_map_state_bad_items_path(resource, iterator, state_input,
                                  capture_stdout, mock_fn):
    map_state = MapState(
        "Validate-All",
        input_path="$.detail",
        items_path="$.delivery-partner",
        max_concurrency=0,
        iterator=iterator,
    )
    state_machine = StateMachine(start_state=map_state)

    stdout = capture_stdout(lambda: state_machine.simulate(
        state_input,
        resource_to_mock_fn={resource: mock_fn},
    ))

    assert (stdout == """Starting simulation of state machine
Executing MapState('Validate-All')
State input: {'ship-date': '2016-03-14T01:59:00Z', 'detail': {'delivery-partner': 'UQS', 'shipped': [{'prod': 'R31', 'dest-code': 9511, 'quantity': 1344}, {'prod': 'S39', 'dest-code': 9511, 'quantity': 40}]}}
State input after applying input path of $.detail: {'delivery-partner': 'UQS', 'shipped': [{'prod': 'R31', 'dest-code': 9511, 'quantity': 1344}, {'prod': 'S39', 'dest-code': 9511, 'quantity': 40}]}
Items after applying items_path of $.delivery-partner: UQS
StateSimulationError encountered in state
Checking for catchers
No catchers were matched
State output: {}
Terminating simulation of state machine
""")
Beispiel #2
0
def test_pass_state_result(capture_stdout):
    result = {"Hello": "world!"}
    pass_state = PassState("Passing", result=result)
    state_machine = StateMachine(start_state=pass_state)

    assert state_machine.compile() == {
        "StartAt": "Passing",
        "States": {
            "Passing": {
                "Type": "Pass",
                "End": True,
                "Result": {
                    "Hello": "world!"
                }
            }
        },
    }

    stdout = capture_stdout(lambda: state_machine.simulate())

    assert (stdout == """Starting simulation of state machine
Executing PassState('Passing')
State input: {}
State input after applying input path of $: {}
Output from applying result path of $: {'Hello': 'world!'}
State output after applying output path of $: {'Hello': 'world!'}
State output: {'Hello': 'world!'}
Terminating simulation of state machine
""")
Beispiel #3
0
def test_result_path_only_state_input(dummy_resource):
    task_state = TaskState("Task", resource=dummy_resource, result_path=None)
    state_machine = StateMachine(start_state=task_state)

    # Check the output from compiling
    assert state_machine.compile() == {
        "StartAt": task_state.name,
        "States": {
            task_state.name: {
                "Resource": dummy_resource,
                "ResultPath": None,
                "Type": "Task",
                "End": True,
            },
        },
    }

    # Simulate the state machine
    state_input = {
        "comment": "This is a test of the input and output of a Task state.",
        "details": "Default example",
        "who": "AWS Step Functions",
    }

    def mock_fn(event, context):
        return "Hello, AWS Step Functions!"

    state_output = state_machine.simulate(
        state_input,
        resource_to_mock_fn={dummy_resource: mock_fn},
    )

    # Keeps the only the state output
    assert state_output == state_input
Beispiel #4
0
def test_task_state(dummy_resource, capture_stdout):
    pass_state = PassState("Pass", comment="The starting state")
    timeout_seconds = 10
    task_state = TaskState(
        "Task", resource=dummy_resource, timeout_seconds=timeout_seconds
    )

    # Define the state machine
    pass_state >> task_state
    state_machine = StateMachine(start_state=pass_state)

    # Check the output from compiling
    assert state_machine.compile() == {
        "StartAt": pass_state.name,
        "States": {
            pass_state.name: {
                "Comment": pass_state.comment,
                "Type": "Pass",
                "Next": task_state.name,
            },
            task_state.name: {
                "Type": "Task",
                "Resource": dummy_resource,
                "TimeoutSeconds": timeout_seconds,
                "End": True,
            },
        },
    }

    # Simulate the state machine

    def mock_fn(event, context):
        event["foo"] *= 2
        return event

    stdout = capture_stdout(
        lambda: state_machine.simulate(
            {"foo": 5, "bar": 1},
            resource_to_mock_fn={dummy_resource: mock_fn},
        )
    )
    assert (
        stdout
        == """Starting simulation of state machine
Executing PassState('Pass')
State input: {'foo': 5, 'bar': 1}
State input after applying input path of $: {'foo': 5, 'bar': 1}
Output from applying result path of $: {'foo': 5, 'bar': 1}
State output after applying output path of $: {'foo': 5, 'bar': 1}
State output: {'foo': 5, 'bar': 1}
Executing TaskState('Task')
State input: {'foo': 5, 'bar': 1}
State input after applying input path of $: {'foo': 5, 'bar': 1}
Output from applying result path of $: {'foo': 10, 'bar': 1}
State output after applying output path of $: {'foo': 10, 'bar': 1}
State output: {'foo': 10, 'bar': 1}
Terminating simulation of state machine
"""
    )
Beispiel #5
0
def test_future_timestamp(capture_stdout):
    timestamp = datetime.now() + timedelta(seconds=2)
    wait_state = WaitState("Wait", timestamp=timestamp)
    state_machine = StateMachine(start_state=wait_state)
    state_input = {"foo": "bar"}

    stdout = capture_stdout(lambda: state_machine.simulate(state_input))
    assert f"Waiting until {timestamp.isoformat()}" in stdout
Beispiel #6
0
def test_pass_state(capture_stdout):
    pass_state1 = PassState("Pass 1", comment="The starting state")
    pass_state2 = PassState("Pass 2")
    pass_state3 = PassState("Pass 3")

    pass_state1 >> pass_state2 >> pass_state3
    state_machine = StateMachine(start_state=pass_state1)

    assert [state.name for state in state_machine.start_state] == [
        "Pass 1",
        "Pass 2",
        "Pass 3",
    ]

    assert state_machine.compile() == {
        "StartAt": "Pass 1",
        "States": {
            "Pass 2": {
                "Type": "Pass",
                "Next": "Pass 3"
            },
            "Pass 1": {
                "Type": "Pass",
                "Comment": "The starting state",
                "Next": "Pass 2",
            },
            "Pass 3": {
                "Type": "Pass",
                "End": True
            },
        },
    }

    stdout = capture_stdout(lambda: state_machine.simulate())

    assert (stdout == """Starting simulation of state machine
Executing PassState('Pass 1')
State input: {}
State input after applying input path of $: {}
Output from applying result path of $: {}
State output after applying output path of $: {}
State output: {}
Executing PassState('Pass 2')
State input: {}
State input after applying input path of $: {}
Output from applying result path of $: {}
State output after applying output path of $: {}
State output: {}
Executing PassState('Pass 3')
State input: {}
State input after applying input path of $: {}
Output from applying result path of $: {}
State output after applying output path of $: {}
State output: {}
Terminating simulation of state machine
""")
Beispiel #7
0
def test_result_selector(dummy_resource):
    result_selector = {
        "ClusterId.$": "$.output.ClusterId",
        "ResourceType.$": "$.resourceType",
        "SomethingElse.$": "$.keyDoesntExist",
    }
    task_state = TaskState(
        "Task", resource=dummy_resource, result_selector=result_selector
    )
    state_machine = StateMachine(start_state=task_state)

    # Check the output from compiling
    assert state_machine.compile() == {
        "StartAt": task_state.name,
        "States": {
            task_state.name: {
                "Resource": dummy_resource,
                "ResultSelector": result_selector,
                "Type": "Task",
                "End": True,
            },
        },
    }

    # Simulate the state machine

    def mock_fn(event, context):
        return {
            "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",
            },
        }

    state_output = state_machine.simulate(
        resource_to_mock_fn={dummy_resource: mock_fn},
    )

    assert state_output == {
        "ResourceType": "elasticmapreduce",
        "ClusterId": "AKIAIOSFODNN7EXAMPLE",
    }
Beispiel #8
0
def test_past_timestamp(capture_stdout):
    wait_state = WaitState("Wait!", timestamp=datetime(2020, 1, 1))
    state_machine = StateMachine(start_state=wait_state)
    stdout = capture_stdout(lambda: state_machine.simulate())
    assert (stdout == """Starting simulation of state machine
Executing WaitState('Wait!', timestamp='2020-01-01T00:00:00')
State input: {}
State input after applying input path of $: {}
State output after applying output path of $: {}
State output: {}
Terminating simulation of state machine
""")
def test_multiple_catchers(capture_stdout):
    resource = "123"
    task_state = TaskState("Task", resource=resource)
    timeout_state = PassState("Timeout")
    task_failed_state = PassState("Task Failed")
    task_state.add_catcher(["States.Timeout"], next_state=timeout_state).add_catcher(
        ["States.TaskFailed"], next_state=task_failed_state
    )
    state_machine = StateMachine(start_state=task_state)
    assert state_machine.compile() == {
        "StartAt": "Task",
        "States": {
            "Task Failed": {"Type": "Pass", "End": True},
            "Timeout": {"Type": "Pass", "End": True},
            "Task": {
                "Type": "Task",
                "End": True,
                "Catch": [
                    {"ErrorEquals": ["States.Timeout"], "Next": "Timeout"},
                    {"ErrorEquals": ["States.TaskFailed"], "Next": "Task Failed"},
                ],
                "Resource": "123",
            },
        },
    }

    def failure_mock_fn(event, context):
        # Will cause a TaskFailedError
        assert False  # noqa: PT015

    stdout = capture_stdout(
        lambda: state_machine.simulate(resource_to_mock_fn={resource: failure_mock_fn})
    )

    assert (
        stdout
        == """Starting simulation of state machine
Executing TaskState('Task')
State input: {}
State input after applying input path of $: {}
TaskFailedError encountered in state
Checking for catchers
Found catcher, transitioning to PassState('Task Failed')
State output: {}
Executing PassState('Task Failed')
State input: {}
State input after applying input path of $: {}
Output from applying result path of $: {}
State output after applying output path of $: {}
State output: {}
Terminating simulation of state machine
"""
    )
Beispiel #10
0
def test_wait_state(capture_stdout):
    wait_state = WaitState("Wait!", seconds=1)
    state_machine = StateMachine(start_state=wait_state)
    stdout = capture_stdout(lambda: state_machine.simulate())
    assert (stdout == """Starting simulation of state machine
Executing WaitState('Wait!', seconds=1)
State input: {}
State input after applying input path of $: {}
Waiting 1 seconds
State output after applying output path of $: {}
State output: {}
Terminating simulation of state machine
""")
Beispiel #11
0
def test_seconds_path(capture_stdout):
    wait_state = WaitState("Wait!", seconds_path="$.numSeconds")
    state_machine = StateMachine(start_state=wait_state)
    stdout = capture_stdout(lambda: state_machine.simulate({"numSeconds": 1}))
    assert (stdout == """Starting simulation of state machine
Executing WaitState('Wait!', seconds_path='$.numSeconds')
State input: {'numSeconds': 1}
State input after applying input path of $: {'numSeconds': 1}
Waiting 1 seconds
State output after applying output path of $: {'numSeconds': 1}
State output: {'numSeconds': 1}
Terminating simulation of state machine
""")
Beispiel #12
0
def test_invalid_seconds_path(capture_stdout):
    wait_state = WaitState("Wait!", seconds_path="$.numSeconds")
    state_machine = StateMachine(start_state=wait_state)
    stdout = capture_stdout(
        lambda: state_machine.simulate({"numSeconds": "hello"}))
    assert (stdout == """Starting simulation of state machine
Executing WaitState('Wait!', seconds_path='$.numSeconds')
State input: {'numSeconds': 'hello'}
State input after applying input path of $: {'numSeconds': 'hello'}
StateSimulationError encountered in state
Checking for catchers
State output: {}
Terminating simulation of state machine
""")
def test_succeed_state(capture_stdout):
    succeed_state = SucceedState("Success!")
    state_machine = StateMachine(start_state=succeed_state)
    stdout = capture_stdout(lambda: state_machine.simulate({"Hello": "world!"}))
    assert (
        stdout
        == """Starting simulation of state machine
Executing SucceedState('Success!')
State input: {'Hello': 'world!'}
State input after applying input path of $: {'Hello': 'world!'}
State output after applying output path of $: {'Hello': 'world!'}
State output: {'Hello': 'world!'}
Terminating simulation of state machine
"""
    )
Beispiel #14
0
def test_timestamp_path(capture_stdout):
    wait_state = WaitState("Wait!", timestamp_path="$.meta.timeToWait")
    state_machine = StateMachine(start_state=wait_state)
    stdout = capture_stdout(lambda: state_machine.simulate(
        {"meta": {
            "timeToWait": "2020-01-01T00:00:00"
        }}))
    assert (stdout == """Starting simulation of state machine
Executing WaitState('Wait!', timestamp_path='$.meta.timeToWait')
State input: {'meta': {'timeToWait': '2020-01-01T00:00:00'}}
State input after applying input path of $: {'meta': {'timeToWait': '2020-01-01T00:00:00'}}
Waiting until 2020-01-01T00:00:00
State output after applying output path of $: {'meta': {'timeToWait': '2020-01-01T00:00:00'}}
State output: {'meta': {'timeToWait': '2020-01-01T00:00:00'}}
Terminating simulation of state machine
""")
Beispiel #15
0
def test_fail_state(capture_stdout):
    fail_state = FailState("Failure", error="IFailed", cause="I failed!")
    state_machine = StateMachine(start_state=fail_state)
    state_machine.compile() == {
        "Type": "Fail",
        "Error": "IFailed",
        "Cause": "I failed!",
    }
    stdout = capture_stdout(lambda: state_machine.simulate())
    assert (stdout == """Starting simulation of state machine
Executing FailState('Failure', error='IFailed', cause='I failed!')
State input: {}
FailStateError encountered in state
Checking for catchers
State output: {}
Terminating simulation of state machine
""")
Beispiel #16
0
def test_duplicate_names():
    duplicate_name = "My Pass"
    pass_state1 = PassState(duplicate_name)
    pass_state2 = PassState(duplicate_name)
    pass_state1 >> pass_state2
    with pytest.raises(
            AWSStepFuncsValueError,
            match=
            "Duplicate names detected in state machine. Names must be unique",
    ):
        StateMachine(start_state=pass_state1)
def test_result_path():
    pass_state = PassState(
        "Passing", result={"Hello": "world!"}, result_path="$.result"
    )
    state_machine = StateMachine(start_state=pass_state)

    assert state_machine.compile() == {
        "StartAt": "Passing",
        "States": {
            "Passing": {
                "Type": "Pass",
                "End": True,
                "ResultPath": "$.result",
                "Result": {"Hello": "world!"},
            }
        },
    }

    state_output = state_machine.simulate({"sum": 42})
    assert state_output == {"sum": 42, "result": {"Hello": "world!"}}
Beispiel #18
0
def test_duplicate_names_catcher():
    duplicate_name = "Duplicated"
    task_state = TaskState(duplicate_name, resource="123")
    transition_state = FailState(duplicate_name,
                                 error="MyError",
                                 cause="Negligence")
    task_state.add_catcher(["Something"], next_state=transition_state)
    with pytest.raises(
            AWSStepFuncsValueError,
            match=
            "Duplicate names detected in state machine. Names must be unique",
    ):
        StateMachine(start_state=task_state)
Beispiel #19
0
def test_to_json(tmp_path):
    pass_state = PassState("My Pass", comment="The only state")
    state_machine = StateMachine(start_state=pass_state,
                                 comment="My state machine",
                                 version="1.1")

    compiled_path = tmp_path / "state_machine.json"
    state_machine.to_json(compiled_path)
    with compiled_path.open() as fp:
        compiled = json.load(fp)

    assert compiled == {
        "StartAt": pass_state.name,
        "Comment": state_machine.comment,
        "Version": state_machine.version,
        "States": {
            pass_state.name: {
                "Comment": pass_state.comment,
                "Type": "Pass",
                "End": True,
            },
        },
    }
def test_input_output_paths(capture_stdout):
    input_path = "$.dataset2"
    output_path = "$.val1"
    pass_state = PassState("Pass 1", input_path=input_path, output_path=output_path)
    state_machine = StateMachine(start_state=pass_state)
    assert state_machine.compile() == {
        "StartAt": "Pass 1",
        "States": {
            "Pass 1": {
                "Type": "Pass",
                "InputPath": "$.dataset2",
                "OutputPath": "$.val1",
                "End": True,
            }
        },
    }
    stdout = capture_stdout(
        lambda: state_machine.simulate(
            {
                "comment": "Example for InputPath.",
                "dataset1": {"val1": 1, "val2": 2, "val3": 3},
                "dataset2": {"val1": "a", "val2": "b", "val3": "c"},
            }
        )
    )
    assert (
        stdout
        == """Starting simulation of state machine
Executing PassState('Pass 1')
State input: {'comment': 'Example for InputPath.', 'dataset1': {'val1': 1, 'val2': 2, 'val3': 3}, 'dataset2': {'val1': 'a', 'val2': 'b', 'val3': 'c'}}
State input after applying input path of $.dataset2: {'val1': 'a', 'val2': 'b', 'val3': 'c'}
Output from applying result path of $: {'val1': 'a', 'val2': 'b', 'val3': 'c'}
State output after applying output path of $.val1: a
State output: a
Terminating simulation of state machine
"""
    )
Beispiel #21
0
def test_map_state_foo(resource, iterator, state_input, capture_stdout,
                       mock_fn):
    map_state = MapState(
        "Validate-All",
        input_path="$.detail",
        items_path="$.shipped",
        max_concurrency=0,
        iterator=iterator,
    )
    state_machine = StateMachine(start_state=map_state)

    stdout = capture_stdout(lambda: state_machine.simulate(
        state_input,
        resource_to_mock_fn={resource: mock_fn},
    ))

    assert (stdout == """Starting simulation of state machine
Executing MapState('Validate-All')
State input: {'ship-date': '2016-03-14T01:59:00Z', 'detail': {'delivery-partner': 'UQS', 'shipped': [{'prod': 'R31', 'dest-code': 9511, 'quantity': 1344}, {'prod': 'S39', 'dest-code': 9511, 'quantity': 40}]}}
State input after applying input path of $.detail: {'delivery-partner': 'UQS', 'shipped': [{'prod': 'R31', 'dest-code': 9511, 'quantity': 1344}, {'prod': 'S39', 'dest-code': 9511, 'quantity': 40}]}
Items after applying items_path of $.shipped: [{'prod': 'R31', 'dest-code': 9511, 'quantity': 1344}, {'prod': 'S39', 'dest-code': 9511, 'quantity': 40}]
Starting simulation of state machine
Executing TaskState('Validate')
State input: {'prod': 'R31', 'dest-code': 9511, 'quantity': 1344}
State input after applying input path of $: {'prod': 'R31', 'dest-code': 9511, 'quantity': 1344}
Output from applying result path of $: {'prod': 'R31', 'dest-code': 9511, 'quantity': 2688}
State output after applying output path of $: {'prod': 'R31', 'dest-code': 9511, 'quantity': 2688}
State output: {'prod': 'R31', 'dest-code': 9511, 'quantity': 2688}
Terminating simulation of state machine
Starting simulation of state machine
Executing TaskState('Validate')
State input: {'prod': 'S39', 'dest-code': 9511, 'quantity': 40}
State input after applying input path of $: {'prod': 'S39', 'dest-code': 9511, 'quantity': 40}
Output from applying result path of $: {'prod': 'S39', 'dest-code': 9511, 'quantity': 80}
State output after applying output path of $: {'prod': 'S39', 'dest-code': 9511, 'quantity': 80}
State output: {'prod': 'S39', 'dest-code': 9511, 'quantity': 80}
Terminating simulation of state machine
Output from applying result path of $: [{'prod': 'R31', 'dest-code': 9511, 'quantity': 2688}, {'prod': 'S39', 'dest-code': 9511, 'quantity': 80}]
State output after applying output path of $: [{'prod': 'R31', 'dest-code': 9511, 'quantity': 2688}, {'prod': 'S39', 'dest-code': 9511, 'quantity': 80}]
State output: [{'prod': 'R31', 'dest-code': 9511, 'quantity': 2688}, {'prod': 'S39', 'dest-code': 9511, 'quantity': 80}]
Terminating simulation of state machine
""")

    assert state_machine.compile() == {
        "StartAt": "Validate-All",
        "States": {
            "Validate-All": {
                "Type": "Map",
                "InputPath": "$.detail",
                "End": True,
                "ItemsPath": "$.shipped",
                "MaxConcurrency": 0,
                "Iterator": {
                    "StartAt": "Validate",
                    "States": {
                        "Validate": {
                            "Type": "Task",
                            "End": True,
                            "Resource": "<arn>"
                        }
                    },
                },
            }
        },
    }
Beispiel #22
0
def iterator(resource):
    task_state = TaskState("Validate", resource=resource)
    return StateMachine(start_state=task_state)
def test_catcher(capture_stdout):
    resource = "123"
    task_state = TaskState("Task", resource=resource)
    succeed_state = SucceedState("Success")
    pass_state = PassState("Pass")
    fail_state = FailState("Failure", error="IFailed", cause="I failed!")
    task_state >> succeed_state
    pass_state >> fail_state
    task_state.add_catcher(["States.ALL"], next_state=pass_state)
    state_machine = StateMachine(start_state=task_state)

    def failure_mock_fn(event, context):
        assert False  # noqa: PT015

    stdout = capture_stdout(
        lambda: state_machine.simulate(resource_to_mock_fn={resource: failure_mock_fn})
    )
    assert (
        stdout
        == """Starting simulation of state machine
Executing TaskState('Task')
State input: {}
State input after applying input path of $: {}
TaskFailedError encountered in state
Checking for catchers
Found catcher, transitioning to PassState('Pass')
State output: {}
Executing PassState('Pass')
State input: {}
State input after applying input path of $: {}
Output from applying result path of $: {}
State output after applying output path of $: {}
State output: {}
Executing FailState('Failure', error='IFailed', cause='I failed!')
State input: {}
FailStateError encountered in state
Checking for catchers
State output: {}
Terminating simulation of state machine
"""
    )

    # Test no catcher matched
    task_state.catchers.pop()  # Remove States.ALL catcher
    task_state.add_catcher(["Timeout"], next_state=pass_state)
    stdout = capture_stdout(
        lambda: state_machine.simulate(resource_to_mock_fn={resource: failure_mock_fn})
    )
    assert (
        stdout
        == """Starting simulation of state machine
Executing TaskState('Task')
State input: {}
State input after applying input path of $: {}
TaskFailedError encountered in state
Checking for catchers
No catchers were matched
State output: {}
Terminating simulation of state machine
"""
    )