Example #1
0
def test_machine_accepts_fields_as_kwargs(example):
    m1 = Machine.parse([],
                       comment="This is a comment",
                       version="1.0",
                       timeout_seconds=60)
    assert m1.comment == "This is a comment"
    assert m1.version == "1.0"
    assert m1.timeout_seconds == 60

    m2 = Machine.parse(example("hello_world"), timeout_seconds=35)
    assert m2.timeout_seconds == 35
Example #2
0
def test_inserts_and_removes_in_a_sequence():
    sequence = Machine.parse(["a", "c", "e"])
    assert sequence.dry_run() == ["a", "c", "e"]

    sequence.insert("b", before="c")
    assert sequence.dry_run() == ["a", "b", "c", "e"]

    sequence.insert("f", after="e")
    assert sequence.dry_run() == ["a", "b", "c", "e", "f"]

    sequence.remove("c")
    assert sequence.dry_run() == ["a", "b", "e", "f"]

    sequence.remove("a")
    assert sequence.dry_run() == ["b", "e", "f"]

    sequence.remove("f")
    assert sequence.dry_run() == ["b", "e"]

    sequence.insert("x", before="b")
    assert sequence.dry_run() == ["x", "b", "e"]

    sequence.remove("b")
    sequence.remove("e")
    sequence.remove("x")

    assert sequence.dry_run() == []
    assert sequence.start_at is None
Example #3
0
def test_create_state_machine(sfn_client):
    sm = Machine.parse([
        TActivities.a,
        TActivities.b,
        [
            [TActivities.d, TActivities.e],
            [TActivities.f],
        ],
        [
            [TActivities.g],
            [TActivities.h],
        ],
    ])

    activities: Dict[str, ActivityListItem] = {
        a.name: a
        for a in sfn_client.list_activities().activities
    }

    def state_visitor(state: State, compiled_state: Dict):
        if compiled_state.get(
                "Type") == "Task" and "Resource" not in compiled_state:
            compiled_state["Resource"] = activities[state.name].activity_arn

    sm_definition = sm.to_json(state_visitor=state_visitor)

    sfn_client.create_state_machine(
        name=f"{tests_profile.names_prefix}-something-statemachine",
        definition=sm_definition,
        role_arn=tests_profile.role_arn,
    )
Example #4
0
def test_simple_parallel():
    source = [["a"], ["b"]]
    s = Machine.parse(source)
    assert len(s.states) == 1
    assert isinstance(s.states[s.start_at], Parallel)
    assert s.dry_run() == source

    c = s.compile()
    assert c["States"][c["StartAt"]]["Type"] == "Parallel"
Example #5
0
def test_runs_hello_world_machine(example):
    sm = Machine.parse(example("hello_world"))

    runner = Runner(resources=ResourceManager(
        providers={
            "arn:aws:lambda:us-east-1:123456789012:function:HelloWorld":
            lambda x: "Hello, world!"
        }))

    assert runner.run(sm) == (sm.start_at_state, "Hello, world!")
Example #6
0
def test_machine_state(example):
    source = example("hello_world")

    machine = Machine.parse(source)

    assert machine.type == States.Machine
    assert isinstance(machine.start_at_state, Task)

    assert machine.start_at == "Hello World"
    assert machine.start_at_state.name == "Hello World"

    assert machine.compile() == source
Example #7
0
def test_executes_hello_world_state(example):
    hello_world_state = Machine.parse(example("hello_world")).start_at_state
    assert isinstance(hello_world_state, State)

    resources = ResourceManager(
        providers={
            "arn:aws:lambda:us-east-1:123456789012:function:HelloWorld":
            lambda x: "Hello, world!"
        })
    next_state, output = hello_world_state.execute({},
                                                   resource_resolver=resources)
    assert output == "Hello, world!"
Example #8
0
def test_visitor_visits_every_compiled_state_dictionary():
    m = Machine.parse([
        ["a", "b", "c"],
        ["1", "2"],
    ])

    def state_visitor(state: State, compiled_state: Dict):
        compiled_state["Resource"] = f"arn.funny.{state.name}"

    c = m.compile(state_visitor=state_visitor)
    assert c["States"][c["StartAt"]]["Branches"][0]["States"]["a"][
        "Resource"] == "arn.funny.a"
Example #9
0
def test_inserts_and_removes_in_parallels():
    sm = Machine.parse([
        ["b", "c"],
        ["1", "2"],
    ])

    sm.start_at_state.branches[0].insert("a", before="b")
    sm.start_at_state.branches[1].insert("3", after="2")

    assert sm.dry_run() == [
        ["a", "b", "c"],
        ["1", "2", "3"],
    ]
Example #10
0
def test_parallel_inside_sequence():
    source = [
        "a",
        [
            ["b11", "b12"],
            ["b21", "b22"],
        ],
        "c",
    ]
    s = Machine.parse(source)
    assert len(s.states) == 3
    assert s.start_at == "a"
    assert s.dry_run() == source

    c = s.compile()
    assert c["States"][c["States"]["a"]["Next"]]["Type"] == "Parallel"
Example #11
0
def test_custom_task():
    @dataclasses.dataclass
    class Task:
        name: str

        def __str__(self):
            return self.name

        def get_state_attrs(self, state):
            return {"Resource": f"arn:{self.name}"}

    m = Machine.parse([
        Task("first"),
        Task("second"),
    ])
    c = m.compile()
    assert c["States"]["first"]["Type"] == "Task"
    assert c["States"]["first"]["Resource"] == "arn:first"
    assert c["States"]["second"]["Resource"] == "arn:second"
Example #12
0
def test_parallel_inside_parallel():
    source = [[
        "a",
    ], [
        [
            [
                "b11",
            ],
            [
                "b21",
            ],
        ],
        "b3",
    ]]
    s = Machine.parse(source)
    assert s.dry_run() == source

    c = s.compile()
    assert c["States"][c["StartAt"]]["Type"] == "Parallel"
Example #13
0
def test_runs_job_status_poller(example):
    source = example("job_status_poller")
    sm = Machine.parse(source)
    assert sm.compile() == source

    runner = Runner()

    @runner.resource_provider("arn:aws:lambda:REGION:ACCOUNT_ID:function:SubmitJob")
    def submit_job(payload):
        return payload

    @runner.resource_provider("arn:aws:lambda:REGION:ACCOUNT_ID:function:CheckJob")
    def check_job(payload):
        if random.random() < 0.2:
            return "FAILED"
        else:
            return "SUCCEEDED"

    final_state, output = runner.run(sm)
    assert final_state.name in ("Get Final Job Status", "Job Failed")
Example #14
0
def test_dicts_as_states():
    m = Machine.parse([
        {
            "Comment": "first",
            "Type": "Task",
            "Resource": "arn:first",
        },
        {
            "Name": "second",
            "Type": "Pass",
            "Resource": "arn:second",
        },
        "third",
    ])

    c = m.compile()
    assert c["StartAt"] == "arn:first"
    assert len(c["States"]) == 3
    assert c["States"]["arn:first"]["Comment"] == "first"
    assert c["States"]["arn:first"]["Resource"] == "arn:first"
    assert "Name" not in c["States"]["second"]
    assert c["States"]["second"]["Type"] == "Pass"
Example #15
0
def test_input_passed_to_next_task():
    sm = Machine.parse([
        {
            "InputPath": "$.first_input",
            "ResultPath": "$.first_output",
            "Resource": "MultiplierByTwo",
        },
        {
            "InputPath": "$.first_output",
            "ResultPath": "$.second_output",
            "Resource": "MultiplierByThree",
        },
        {
            "Resource": "Validator",
        },
    ])

    runner = Runner()
    runner.resource_provider("MultiplierByTwo")(lambda x: x * 2)
    runner.resource_provider("MultiplierByThree")(lambda x: x * 3)

    @runner.resource_provider("Validator")
    def validate_input(input):
        assert input == {
            "first_input": 1111,
            "first_output": 2222,
            "second_output": 6666,
        }
        # NB!
        return input

    final_state, output = runner.run(sm, input={"first_input": 1111})
    assert output == {
        "first_input": 1111,
        "first_output": 2222,
        "second_output": 6666,
    }
def test_runs_job_status_poller(example):
    source = example("job_status_poller")
    sm = Machine.parse(source)
    assert sm.compile() == source
    assert sm.max_pages == 2
    assert sm.states['Submit Job'].parallel_pages == True

    runner = Runner()

    @runner.resource_provider("arn:aws:lambda:REGION:ACCOUNT_ID:function:SubmitJob")
    def submit_job(payload):
        return payload

    @runner.resource_provider("arn:aws:lambda:REGION:ACCOUNT_ID:function:CheckJob")
    def check_job(payload):
        if payload < 30:
            return "FAILED"
        else:
            return "SUCCEEDED"

    final_state, output = runner.run(sm, {'input': 25})
    assert final_state.name in "Job Failed"
    final_state, output = runner.run(sm, {'input': 40})
    assert final_state.name in "Consolidator Output"
Example #17
0
def test_simple_sequence():
    s = Machine.parse(["a", "b"])
    assert len(s.states) == 2
    assert s.start_at == "a"
    assert s.dry_run() == ["a", "b"]
Example #18
0
from aws_sfn_builder import Machine

machine = Machine.parse([
    "a",
    [
        ["b-10", "b-11"],
        ["b-20"],
    ],
    "c",
])

print(machine.to_json())
Example #19
0
def test_empty_machine():
    m = Machine.parse([])
    assert m.start_at is None
    assert not m.states
    assert m.dry_run() == []
Example #20
0
def test_append_to_a_sequence():
    sm = Machine.parse([])
    sm.append("a")
    sm.append("b")
    assert sm.dry_run() == ["a", "b"]