예제 #1
0
    def test_submit_resolves_priority(self):
        response = Decider(DOMAIN, "test-task-list").poll()
        executor = Executor(DOMAIN, ExampleWorkflow)
        decisions, _ = executor.replay(response)

        expect(decisions).to.have.length_of(5)

        def get_task_priority(decision):
            return decision["scheduleActivityTaskDecisionAttributes"].get(
                "taskPriority")

        # default priority for the whole workflow
        expect(get_task_priority(decisions[0])).to.equal("12")

        # priority passed explicitly
        expect(get_task_priority(decisions[1])).to.equal("5")

        # priority == None
        expect(get_task_priority(decisions[2])).to.be.none

        # priority set at decorator level
        expect(get_task_priority(decisions[3])).to.equal("32")

        # priority set at decorator level but overridden in self.submit()
        expect(get_task_priority(decisions[4])).to.equal("30")
예제 #2
0
def test_on_failure_callback():
    workflow = TestOnFailureDefinition
    executor = Executor(DOMAIN, workflow)
    history = builder.History(workflow)

    history.add_activity_task(
        raise_error,
        decision_id=history.last_id,
        activity_id='activity-tests.test_dataflow.raise_error-1',
        last_state='failed',
        reason='error')

    (history
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # The executor should fail the workflow and extract the reason from the
    # exception raised in the workflow definition.
    decisions, _ = executor.replay(history)

    assert executor._workflow.failed is True

    workflow_failed = swf.models.decision.WorkflowExecutionDecision()
    workflow_failed.fail(
        reason='Workflow execution failed: FAIL')

    assert decisions[0] == workflow_failed
예제 #3
0
def test_activity_task_timeout():
    workflow = TestDefinition
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)
    decision_id = history.last_id
    (history
        .add_activity_task(
            increment,
            activity_id='activity-tests.test_dataflow.increment-1',
            decision_id=decision_id,
            last_state='timed_out',
            timeout_type='START_TO_CLOSE'))

    decisions, _ = executor.replay(history)
    # The task timed out and there is no retry.
    assert len(decisions) == 1

    reason = (
        "Cannot replay the workflow: MultipleExceptions("
        "('futures failed', [TimeoutError(START_TO_CLOSE)]))"
    )
    workflow_failed = swf.models.decision.WorkflowExecutionDecision()
    workflow_failed.fail(reason=reason)

    decision = decisions[0]
    assert decision.type == 'FailWorkflowExecution'
    assert decision['failWorkflowExecutionDecisionAttributes']['reason'] == reason
예제 #4
0
def test_more_than_1000_open_activities_scheduled_and_running():
    def get_random_state():
        import random
        return random.choice(['scheduled', 'started'])

    workflow = TestDefinitionMoreThanMaxOpenActivities
    executor = Executor(DOMAIN, workflow)
    history = builder.History(workflow)

    # The first time, the executor should schedule
    # ``constants.MAX_OPEN_ACTIVITY_COUNT`` decisions.
    # No timer because we wait for at least an activity to complete.
    for i in xrange(constants.MAX_OPEN_ACTIVITY_COUNT / constants.MAX_DECISIONS):
        decisions, _ = executor.replay(history)
        assert len(decisions) == constants.MAX_DECISIONS

    decision_id = history.last_id
    for i in xrange(constants.MAX_OPEN_ACTIVITY_COUNT):
        history.add_activity_task(
            increment,
            decision_id=decision_id,
            activity_id='activity-tests.test_dataflow.increment-{}'.format(
                i + 1),
            last_state=get_random_state(),
            result=i + 1)
    (history
        .add_decision_task_scheduled()
        .add_decision_task_started())

    decisions, _ = executor.replay(history)
    assert len(decisions) == 0
예제 #5
0
def test_workflow_with_input():
    workflow = ATestDefinitionWithInput
    executor = Executor(DOMAIN, workflow)

    result = 5
    history = builder.History(workflow, input={'args': (4, )})

    # The executor should only schedule the *increment* task.
    decisions, _ = executor.replay(Response(history=history))
    check_task_scheduled_decision(decisions[0], increment)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history.add_activity_task(
        increment,
        decision_id=decision_id,
        last_state='completed',
        activity_id='activity-tests.data.activities.increment-1',
        input={
            'args': 1
        },
        result=result).add_decision_task_scheduled().add_decision_task_started(
        ))

    # As there is only a single task, the executor should now complete the
    # workflow and set its result accordingly.
    decisions, _ = executor.replay(Response(history=history))
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json_dumps(result))

    assert decisions[0] == workflow_completed
예제 #6
0
def test_workflow_map():
    workflow = ATestDefinitionMap
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)

    nb_parts = ATestDefinitionMap.nb_parts

    # All the futures returned by the map are passed to wait().
    # The executor should then schedule all of them.
    decisions, _ = executor.replay(Response(history=history))
    for i in range(nb_parts):
        check_task_scheduled_decision(decisions[i], increment)

    # Let's add all tasks of the map to the history to simulate their
    # completion.
    decision_id = history.last_id
    for i in range(nb_parts):
        history.add_activity_task(
            increment,
            decision_id=decision_id,
            activity_id='activity-tests.data.activities.increment-{}'.format(
                i + 1),
            last_state='completed',
            input={'args': i},
            result=i + 1)
    (history.add_decision_task_scheduled().add_decision_task_started())

    # All tasks are finished, the executor should complete the workflow.
    decisions, _ = executor.replay(Response(history=history))
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(
        result=json_dumps([i + 1 for i in range(nb_parts)]))

    assert decisions[0] == workflow_completed
예제 #7
0
def test_workflow_with_input():
    workflow = TestDefinitionWithInput
    executor = Executor(DOMAIN, workflow)

    result = 5
    history = builder.History(workflow,
                              input={'args': (4,)})

    # The executor should only schedule the *increment* task.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history
        .add_activity_task(increment,
                           decision_id=decision_id,
                           last_state='completed',
                           activity_id='activity-tests.test_dataflow.increment-1',
                           input={'args': 1},
                           result=result)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # As there is only a single task, the executor should now complete the
    # workflow and set its result accordingly.
    decisions, _ = executor.replay(history)
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json.dumps(result))

    assert decisions[0] == workflow_completed
예제 #8
0
def test_multiple_scheduled_activities():
    """
    When ``Future.exception`` was made blocking if the future is not finished,
    :py:meth:`swf.executor.Executor.resume` did not check ``future.finished``
    before ``future.exception is None``. It mades the call to ``.resume()`` to
    block for the first scheduled task it encountered instead of returning it.
    This issue was fixed in commit 6398aa8.
    With the wrong behaviour, the call to ``executor.replay()`` would not
    schedule the ``double`` task even after the task represented by *b*
    (``self.submit(increment, 2)``) has completed.
    """
    workflow = TestMultipleScheduledActivitiesDefinition
    executor = Executor(DOMAIN, workflow)
    history = builder.History(workflow)

    decision_id = history.last_id
    (history
        .add_activity_task_scheduled(
            increment,
            decision_id=decision_id,
            activity_id='activity-tests.data.activities.increment-1',
            input={'args': 1})
        # The right behaviour is to schedule the ``double`` task when *b* is in
        # state finished.
        .add_activity_task(
            increment,
            decision_id=decision_id,
            activity_id='activity-tests.data.activities.increment-2',
            last_state='completed',
            input={'args': 2},
            result='3'))

    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], double)
예제 #9
0
def test_workflow_activity_raises_on_failure():
    workflow = TestDefinitionActivityRaisesOnFailure
    executor = Executor(DOMAIN, workflow)
    history = builder.History(workflow)

    history.add_activity_task(
        raise_on_failure,
        decision_id=history.last_id,
        activity_id='activity-tests.data.activities.raise_on_failure-1',
        last_state='failed',
        reason='error')

    (history
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # The executor should fail the workflow and extract the reason from the
    # exception raised in the workflow definition.
    decisions, _ = executor.replay(history)

    assert executor._workflow.failed is True

    workflow_failed = swf.models.decision.WorkflowExecutionDecision()
    workflow_failed.fail(
        details='DETAILS',
        reason='Workflow execution error in task '
               'activity-tests.data.activities.raise_on_failure: '
               '"error"')

    assert decisions[0] == workflow_failed
예제 #10
0
def test_workflow_failed_from_definition():
    workflow = ATestDefinitionFailWorkflow
    executor = Executor(DOMAIN, workflow)
    history = builder.History(workflow)

    # Let's directly add the task in state ``failed`` to make the executor fail
    # the workflow.
    history.add_activity_task(
        raise_error,
        decision_id=history.last_id,
        activity_id='activity-tests.data.activities.raise_error-1',
        last_state='failed',
        result=json_dumps(None))

    (history.add_decision_task_scheduled().add_decision_task_started())

    # Now the workflow definition calls ``Workflow.fail('error')`` that should
    # fail the whole workflow.
    decisions, _ = executor.replay(Response(history=history))

    assert executor.workflow.failed is True

    workflow_failed = swf.models.decision.WorkflowExecutionDecision()
    workflow_failed.fail(reason='Workflow execution failed: error')

    assert decisions[0] == workflow_failed
예제 #11
0
def test_workflow_activity_raises_on_failure():
    workflow = ATestDefinitionActivityRaisesOnFailure
    executor = Executor(DOMAIN, workflow)
    history = builder.History(workflow)

    history.add_activity_task(
        raise_on_failure,
        decision_id=history.last_id,
        activity_id='activity-tests.data.activities.raise_on_failure-1',
        last_state='failed',
        reason='error')

    (history.add_decision_task_scheduled().add_decision_task_started())

    # The executor should fail the workflow and extract the reason from the
    # exception raised in the workflow definition.
    decisions, _ = executor.replay(Response(history=history))

    assert executor.workflow.failed is True

    workflow_failed = swf.models.decision.WorkflowExecutionDecision()
    workflow_failed.fail(reason='Workflow execution error in task '
                         'activity-tests.data.activities.raise_on_failure: '
                         '"error"')

    assert decisions[0] == workflow_failed
예제 #12
0
def test_more_than_1000_open_activities_scheduled_and_running():
    def get_random_state():
        import random
        return random.choice(['scheduled', 'started'])

    workflow = ATestDefinitionMoreThanMaxOpenActivities
    executor = Executor(DOMAIN, workflow)
    history = builder.History(workflow)

    # The first time, the executor should schedule
    # ``constants.MAX_OPEN_ACTIVITY_COUNT`` decisions.
    # No timer because we wait for at least an activity to complete.
    for i in range(constants.MAX_OPEN_ACTIVITY_COUNT //
                   constants.MAX_DECISIONS):
        decisions, _ = executor.replay(Response(history=history))
        assert len(decisions) == constants.MAX_DECISIONS

    decision_id = history.last_id
    for i in range(constants.MAX_OPEN_ACTIVITY_COUNT):
        history.add_activity_task(
            increment,
            decision_id=decision_id,
            activity_id='activity-tests.data.activities.increment-{}'.format(
                i + 1),
            last_state=get_random_state(),
            result=i + 1)
    (history.add_decision_task_scheduled().add_decision_task_started())

    decisions, _ = executor.replay(Response(history=history))
    assert len(decisions) == 0
예제 #13
0
def test_more_than_1000_open_activities_scheduled():
    workflow = TestDefinitionMoreThanMaxOpenActivities
    executor = Executor(DOMAIN, workflow)
    history = builder.History(workflow)

    # The first time, the executor should schedule
    # ``constants.MAX_OPEN_ACTIVITY_COUNT`` decisions.
    # No timer because we wait for at least an activity to complete.
    for i in xrange(constants.MAX_OPEN_ACTIVITY_COUNT / constants.MAX_DECISIONS):
        decisions, _ = executor.replay(history)
        assert len(decisions) == constants.MAX_DECISIONS

    decision_id = history.last_id
    for i in xrange(constants.MAX_OPEN_ACTIVITY_COUNT):
        history.add_activity_task(
            increment,
            decision_id=decision_id,
            activity_id='activity-tests.test_dataflow.increment-{}'.format(
                i + 1),
            last_state='scheduled',
            result=i + 1)
    (history
        .add_decision_task_scheduled()
        .add_decision_task_started())

    decisions, _ = executor.replay(history)
    assert executor._open_activity_count == constants.MAX_OPEN_ACTIVITY_COUNT
    assert len(decisions) == 0
예제 #14
0
def test_on_failure_callback():
    workflow = TestOnFailureDefinition
    executor = Executor(DOMAIN, workflow)
    history = builder.History(workflow)

    history.add_activity_task(
        raise_error,
        decision_id=history.last_id,
        activity_id='activity-tests.test_dataflow.raise_error-1',
        last_state='failed',
        reason='error')

    (history
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # The executor should fail the workflow and extract the reason from the
    # exception raised in the workflow definition.
    decisions, _ = executor.replay(history)

    assert executor._workflow.failed is True

    workflow_failed = swf.models.decision.WorkflowExecutionDecision()
    workflow_failed.fail(
        reason='Workflow execution failed: FAIL')

    assert decisions[0] == workflow_failed
예제 #15
0
def test_multiple_scheduled_activities():
    """
    When ``Future.exception`` was made blocking if the future is not finished,
    :py:meth:`swf.executor.Executor.resume` did not check ``future.finished``
    before ``future.exception is None``. It mades the call to ``.resume()`` to
    block for the first scheduled task it encountered instead of returning it.
    This issue was fixed in commit 6398aa8.
    With the wrong behaviour, the call to ``executor.replay()`` would not
    schedule the ``double`` task even after the task represented by *b*
    (``self.submit(increment, 2)``) has completed.
    """
    workflow = ATestMultipleScheduledActivitiesDefinition
    executor = Executor(DOMAIN, workflow)
    history = builder.History(workflow)

    decision_id = history.last_id
    (history.add_activity_task_scheduled(
        increment,
        decision_id=decision_id,
        activity_id='activity-tests.data.activities.increment-1',
        input={'args': 1})
     # The right behaviour is to schedule the ``double`` task when *b* is in
     # state finished.
     .add_activity_task(
         increment,
         decision_id=decision_id,
         activity_id='activity-tests.data.activities.increment-2',
         last_state='completed',
         input={'args': 2},
         result='3'))

    decisions, _ = executor.replay(Response(history=history))
    check_task_scheduled_decision(decisions[0], double)
예제 #16
0
def test_workflow_with_repair_and_force_activities():
    workflow = ATestDefinitionWithInput
    history = builder.History(workflow, input={'args': [4]})

    # Now let's build the history to repair
    previous_history = builder.History(workflow, input={'args': [4]})
    decision_id = previous_history.last_id
    (previous_history.add_activity_task(
        increment,
        decision_id=decision_id,
        last_state='completed',
        activity_id='activity-tests.data.activities.increment-1',
        input={'args': 4},
        result=57)  # obviously wrong but helps see if things work
     )
    to_repair = History(previous_history)
    to_repair.parse()

    executor = Executor(DOMAIN,
                        workflow,
                        repair_with=to_repair,
                        force_activities="increment|something_else")

    # The executor should not schedule anything, it should use previous history
    decisions, _ = executor.replay(Response(history=history))
    assert len(decisions) == 1
    assert decisions[0]['decisionType'] == 'ScheduleActivityTask'
    attrs = decisions[0]['scheduleActivityTaskDecisionAttributes']
    assert not attrs['taskList']['name'].startswith("FAKE-")
    check_task_scheduled_decision(decisions[0], increment)
예제 #17
0
def test_workflow_failed_from_definition():
    workflow = TestDefinitionFailWorkflow
    executor = Executor(DOMAIN, workflow)
    history = builder.History(workflow)

    # Let's directly add the task in state ``failed`` to make the executor fail
    # the workflow.
    history.add_activity_task(
        raise_error,
        decision_id=history.last_id,
        activity_id='activity-tests.test_dataflow.raise_error-1',
        last_state='failed',
        result=json.dumps(None))

    (history
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # Now the workflow definition calls ``Workflow.fail('error')`` that should
    # fail the whole workflow.
    decisions, _ = executor.replay(history)

    assert executor._workflow.failed is True

    workflow_failed = swf.models.decision.WorkflowExecutionDecision()
    workflow_failed.fail(reason='Workflow execution failed: error')

    assert decisions[0] == workflow_failed
예제 #18
0
def test_workflow_with_after_closed():
    workflow = ATestDefinitionWithAfterClosed
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow, input={'args': (4, )})

    # The executor should only schedule the *increment* task.
    assert not hasattr(executor.workflow, 'b')
    decisions, _ = executor.replay(Response(history=history))
    check_task_scheduled_decision(decisions[0], increment)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history.add_activity_task(
        increment,
        decision_id=decision_id,
        last_state='completed',
        activity_id='activity-tests.data.activities.increment-1',
        input={
            'args': 4
        },
        result=5).add_decision_task_scheduled().add_decision_task_started())

    # *double* has completed and the ``b.result``is now available. The executor
    # should complete the workflow and its result to ``b.result``.
    assert not hasattr(executor.workflow, 'b')
    decisions, _ = executor.replay(Response(history=history))
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json_dumps(5))

    assert decisions[0] == workflow_completed
    assert executor.workflow.b == 5
예제 #19
0
def test_workflow_with_two_tasks_same_future():
    workflow = ATestDefinitionTwoTasksSameFuture
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)

    # ``b.result`` and ``c.result`` requires the execution of ``double(a)`` and
    # ``increment(a)``. They both depend on the execution of ``increment(1)``so
    # the executor should schedule ``increment(1)``.
    decisions, _ = executor.replay(Response(history=history))
    check_task_scheduled_decision(decisions[0], increment)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history.add_activity_task(
        increment,
        decision_id=decision_id,
        last_state='completed',
        activity_id='activity-tests.data.activities.increment-1',
        input={
            'args': 1
        },
        result=2).add_decision_task_scheduled().add_decision_task_started())

    # Now ``a.result`` is available and the executor should schedule the
    # execution of ``double(a)`` and ``increment(a)`` at the same time.
    decisions, _ = executor.replay(Response(history=history))
    check_task_scheduled_decision(decisions[0], double)
    check_task_scheduled_decision(decisions[1], increment)

    # Let's add both tasks to the history to simulate their completion.
    decision_id = history.last_id
    (history.add_activity_task(
        double,
        decision_id=decision_id,
        last_state='completed',
        activity_id='activity-tests.data.activities.double-1',
        input={
            'args': 2
        },
        result=4).add_activity_task(
            increment,
            decision_id=decision_id,
            last_state='completed',
            activity_id='activity-tests.data.activities.increment-2',
            input={
                'args': 2
            },
            result=3).add_decision_task_scheduled().add_decision_task_started(
            ))

    # Both tasks completed, hence the executor should complete the workflow.
    decisions, _ = executor.replay(Response(history=history))
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json_dumps((4, 3)))

    assert decisions[0] == workflow_completed
예제 #20
0
def test_workflow_with_before_replay():
    workflow = ATestDefinitionWithBeforeReplay
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow, input={'args': (4, )})

    # The executor should only schedule the *increment* task.
    assert not hasattr(executor.workflow, 'a')
    decisions, _ = executor.replay(Response(history=history))
    assert executor.workflow.a == 4
예제 #21
0
    def test_get_event_details(self):
        history = builder.History(ExampleWorkflow, input={})
        signal_input = {'x': 42, 'foo': 'bar', '__propagate': False}
        marker_details = {'baz': 'bae'}
        history.add_signal('a_signal', signal_input)
        history.add_marker('a_marker', marker_details)
        history.add_timer_started('a_timer', 1, decision_id=2)
        history.add_timer_fired('a_timer')

        executor = Executor(DOMAIN, ExampleWorkflow)
        executor.replay(Response(history=history, execution=None))

        details = executor.get_event_details('signal', 'a_signal')
        del details['timestamp']
        expect(details).to.equal({
            'type': 'signal',
            'state': 'signaled',
            'name': 'a_signal',
            'input': signal_input,
            'event_id': 4,
            'external_initiated_event_id': 0,
            'external_run_id': None,
            'external_workflow_id': None,
        })

        details = executor.get_event_details('signal', 'another_signal')
        expect(details).to.be.none

        details = executor.get_event_details('marker', 'a_marker')
        del details['timestamp']
        expect(details).to.equal({
            'type': 'marker',
            'state': 'recorded',
            'name': 'a_marker',
            'details': marker_details,
            'event_id': 5,
        })
        details = executor.get_event_details('marker', 'another_marker')
        expect(details).to.be.none

        details = executor.get_event_details('timer', 'a_timer')
        del details['started_event_timestamp']
        del details['fired_event_timestamp']
        expect(details).to.equal({
            'type': 'timer',
            'state': 'fired',
            'id': 'a_timer',
            'decision_task_completed_event_id': 2,
            'start_to_fire_timeout': 1,
            'started_event_id': 6,
            'fired_event_id': 7,
            'control': None,
        })
        details = executor.get_event_details('timer', 'another_timer')
        expect(details).to.be.none
예제 #22
0
def test_workflow_with_after_run():
    workflow = TestDefinitionWithAfterRun
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow,
                              input={'args': (4,)})

    # The executor should only schedule the *increment* task.
    assert not hasattr(executor._workflow, 'b')
    decisions, _ = executor.replay(history)
    assert executor._workflow.b == 5
예제 #23
0
def test_workflow_with_two_tasks_same_future():
    workflow = TestDefinitionTwoTasksSameFuture
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)

    # ``b.result`` and ``c.result`` requires the execution of ``double(a)`` and
    # ``increment(a)``. They both depend on the execution of ``increment(1)``so
    # the executor should schedule ``increment(1)``.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history
        .add_activity_task(increment,
                           decision_id=decision_id,
                           last_state='completed',
                           activity_id='activity-tests.test_dataflow.increment-1',
                           input={'args': 1},
                           result=2)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # Now ``a.result`` is available and the executor should schedule the
    # execution of ``double(a)`` and ``increment(a)`` at the same time.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], double)
    check_task_scheduled_decision(decisions[1], increment)

    # Let's add both tasks to the history to simulate their completion.
    decision_id = history.last_id
    (history
        .add_activity_task(double,
                           decision_id=decision_id,
                           last_state='completed',
                           activity_id='activity-tests.test_dataflow.double-1',
                           input={'args': 2},
                           result=4)
        .add_activity_task(increment,
                           decision_id=decision_id,
                           last_state='completed',
                           activity_id='activity-tests.test_dataflow.increment-2',
                           input={'args': 2},
                           result=3)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # Both tasks completed, hence the executor should complete the workflow.
    decisions, _ = executor.replay(history)
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json.dumps((4, 3)))

    assert decisions[0] == workflow_completed
예제 #24
0
def test_workflow_with_timeout_override():
    workflow = TestDefinitionWithInputWithTaskTimeoutOverride
    executor = Executor(DOMAIN, workflow)

    result = 5
    history = builder.History(workflow,
                              input={'args': (4,), 'kwargs': { 'task_start_to_close_timeout': '100' }})

    # The executor should only schedule the *increment* task.
    decisions, _ = executor.replay(history)

    assert decisions[0]['scheduleActivityTaskDecisionAttributes']['startToCloseTimeout'] == str(100 + int(default.ACTIVITY_SOFT_TIMEOUT_BUFFER) + int(default.ACTIVITY_HARD_TIMEOUT_BUFFER))
예제 #25
0
def test_workflow_with_after_replay():
    workflow = ATestDefinitionWithAfterReplay
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow, input={'args': (4, )})

    # The executor should only schedule the *increment* task.
    assert not hasattr(executor.workflow, 'b')
    decisions, _ = executor.replay(Response(history=history))
    assert executor.workflow.b == 5
    # Check that workflow is not marked as finished
    assert not hasattr(executor.workflow, 'c')
예제 #26
0
def test_workflow_retry_activity_failed_again():
    workflow = TestDefinitionRetryActivity
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)

    # There is a single task, hence the executor should schedule it first.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment_retry)

    # Let's add the task in ``failed`` state.
    decision_id = history.last_id
    (history
        .add_activity_task(
            increment_retry,
            decision_id=decision_id,
            last_state='failed',
            activity_id='activity-tests.test_dataflow.increment_retry-1')
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # As the retry value is one, the executor should retry i.e. schedule the
    # task again.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment_retry)

    # Let's add the task in ``failed`` state again.
    decision_id = history.last_id
    (history
        .add_activity_task(
            increment_retry,
            decision_id=decision_id,
            last_state='failed',
            activity_id='activity-tests.test_dataflow.increment_retry-1')
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # There is no more retry. The executor should set `Future.exception` and
    # complete the workflow as there is no further task.
    decisions, _ = executor.replay(history)

    reason = (
        "Cannot replay the workflow: TaskFailed("
        "('activity-tests.test_dataflow.increment_retry-1', 'REASON', 'DETAILS'))"
    )

    workflow_failed = swf.models.decision.WorkflowExecutionDecision()
    workflow_failed.fail(reason=reason)

    decision = decisions[0]
    assert decision.type == 'FailWorkflowExecution'
    assert decision['failWorkflowExecutionDecisionAttributes']['reason'] == reason
예제 #27
0
def test_workflow_with_same_task_called_two_times():
    """
    This test checks how the executor behaves when the same task is executed
    two times with a different argument.

    """
    workflow = TestDefinitionSameTask
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)

    # As the second task depends on the first, the executor should only
    # schedule the first task.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history
        .add_activity_task(increment,
                           decision_id=decision_id,
                           last_state='completed',
                           activity_id='activity-tests.test_dataflow.increment-1',
                           input={'args': 1},
                           result=2)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # The first task is finished, the executor should schedule the second one.
    decision_id = history.last_id
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history
        .add_activity_task(increment,
                           decision_id=decision_id,
                           last_state='completed',
                           activity_id='activity-tests.test_dataflow.increment-2',
                           input={'args': 2},
                           result=3)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # The executor should now complete the workflow.
    decisions, _ = executor.replay(history)
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json.dumps(3))

    assert decisions[0] == workflow_completed
예제 #28
0
def test_workflow_with_two_tasks_not_completed():
    """
    This test checks how the executor behaves when a task is still running.

    """
    workflow = TestDefinitionWithInput
    executor = Executor(DOMAIN, workflow)

    arg = 4
    result = 5
    history = builder.History(workflow,
                              input={'args': (arg,)})

    # The executor should schedule *increment*.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment)

    # Let's add the task in state ``started`` to the history.
    decision_id = history.last_id
    scheduled_id = decision_id + 1
    (history
        .add_activity_task(increment,
                           decision_id=decision_id,
                           last_state='started',
                           activity_id='activity-tests.test_dataflow.increment-1',
                           input={'args': 1},
                           result=5)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # The executor cannot schedule any other task, it returns an empty
    # decision.
    decisions, _ = executor.replay(history)
    assert len(decisions) == 0

    # Let's now set the task as ``completed`` in the history.
    decision_id = history.last_id
    (history
        .add_activity_task_completed(scheduled=scheduled_id,
                                     started=scheduled_id + 1,
                                     result=result)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # As there is a single task and it is now finished, the executor should
    # complete the workflow.
    decisions, _ = executor.replay(history)
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json.dumps(result))

    assert decisions[0] == workflow_completed
예제 #29
0
def test_workflow_with_two_tasks_not_completed():
    """
    This test checks how the executor behaves when a task is still running.

    """
    workflow = TestDefinitionWithInput
    executor = Executor(DOMAIN, workflow)

    arg = 4
    result = 5
    history = builder.History(workflow,
                              input={'args': (arg,)})

    # The executor should schedule *increment*.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment)

    # Let's add the task in state ``started`` to the history.
    decision_id = history.last_id
    scheduled_id = decision_id + 1
    (history
        .add_activity_task(increment,
                           decision_id=decision_id,
                           last_state='started',
                           activity_id='activity-tests.test_dataflow.increment-1',
                           input={'args': 1},
                           result=5)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # The executor cannot schedule any other task, it returns an empty
    # decision.
    decisions, _ = executor.replay(history)
    assert len(decisions) == 0

    # Let's now set the task as ``completed`` in the history.
    decision_id = history.last_id
    (history
        .add_activity_task_completed(scheduled=scheduled_id,
                                     started=scheduled_id + 1,
                                     result=result)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # As there is a single task and it is now finished, the executor should
    # complete the workflow.
    decisions, _ = executor.replay(history)
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json.dumps(result))

    assert decisions[0] == workflow_completed
예제 #30
0
def test_workflow_with_same_task_called_two_times():
    """
    This test checks how the executor behaves when the same task is executed
    two times with a different argument.

    """
    workflow = TestDefinitionSameTask
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)

    # As the second task depends on the first, the executor should only
    # schedule the first task.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history
        .add_activity_task(increment,
                           decision_id=decision_id,
                           last_state='completed',
                           activity_id='activity-tests.test_dataflow.increment-1',
                           input={'args': 1},
                           result=2)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # The first task is finished, the executor should schedule the second one.
    decision_id = history.last_id
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history
        .add_activity_task(increment,
                           decision_id=decision_id,
                           last_state='completed',
                           activity_id='activity-tests.test_dataflow.increment-2',
                           input={'args': 2},
                           result=3)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # The executor should now complete the workflow.
    decisions, _ = executor.replay(history)
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json.dumps(3))

    assert decisions[0] == workflow_completed
예제 #31
0
    def test_get_event_details(self):
        history = builder.History(ExampleWorkflow, input={})
        signal_input = {'x': 42, 'foo': 'bar', '__propagate': False}
        marker_details = {'baz': 'bae'}
        history.add_signal('a_signal', signal_input)
        history.add_marker('a_marker', marker_details)
        history.add_timer_started('a_timer', 1, decision_id=2)
        history.add_timer_fired('a_timer')

        executor = Executor(DOMAIN, ExampleWorkflow)
        executor.replay(Response(history=history, execution=None))

        details = executor.get_event_details('signal', 'a_signal')
        del details['timestamp']
        expect(details).to.equal({
            'type': 'signal',
            'state': 'signaled',
            'name': 'a_signal',
            'input': signal_input,
            'event_id': 4,
            'external_initiated_event_id': 0,
            'external_run_id': None,
            'external_workflow_id': None,
        })

        details = executor.get_event_details('signal', 'another_signal')
        expect(details).to.be.none

        details = executor.get_event_details('marker', 'a_marker')
        del details['timestamp']
        expect(details).to.equal({
            'type': 'marker',
            'state': 'recorded',
            'name': 'a_marker',
            'details': marker_details,
            'event_id': 5,
        })
        details = executor.get_event_details('marker', 'another_marker')
        expect(details).to.be.none

        details = executor.get_event_details('timer', 'a_timer')
        del details['started_event_timestamp']
        del details['fired_event_timestamp']
        expect(details).to.equal({
            'type': 'timer',
            'state': 'fired',
            'id': 'a_timer',
            'decision_task_completed_event_id': 2,
            'start_to_fire_timeout': 1,
            'started_event_id': 6,
            'fired_event_id': 7,
            'control': None,
        })
        details = executor.get_event_details('timer', 'another_timer')
        expect(details).to.be.none
예제 #32
0
def test_workflow_with_two_tasks():
    workflow = ATestDefinition
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)

    # *double* requires the result of *increment*, hold by the *a* future.
    # Hence the executor schedule *increment*.
    decisions, _ = executor.replay(Response(history=history))
    check_task_scheduled_decision(decisions[0], increment)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history.add_activity_task(
        increment,
        decision_id=decision_id,
        last_state='completed',
        activity_id='activity-tests.data.activities.increment-1',
        input={
            'args': 1
        },
        result=2).add_decision_task_scheduled().add_decision_task_started())

    # Now ``a.result``contains the result of *increment*'s that is finished.
    # The line ``return b.result`` requires the computation of *double* with
    # ``a.result``, then the executor should schedule *double*.
    decisions, _ = executor.replay(Response(history=history))
    check_task_scheduled_decision(decisions[0], double)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history.add_activity_task(
        double,
        decision_id=decision_id,
        last_state='completed',
        activity_id='activity-tests.data.activities.double-1',
        input={
            'args': 2
        },
        result=4).add_decision_task_scheduled().add_decision_task_started())

    # *double* has completed and the ``b.result``is now available. The executor
    # should complete the workflow and its result to ``b.result``.
    decisions, _ = executor.replay(Response(history=history))
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json_dumps(4))

    assert decisions[0] == workflow_completed
예제 #33
0
def test_workflow_with_two_tasks():
    workflow = TestDefinition
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)

    # *double* requires the result of *increment*, hold by the *a* future.
    # Hence the executor schedule *increment*.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history
        .add_activity_task(increment,
                           decision_id=decision_id,
                           last_state='completed',
                           activity_id='activity-tests.test_dataflow.increment-1',
                           input={'args': 1},
                           result=2)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # Now ``a.result``contains the result of *increment*'s that is finished.
    # The line ``return b.result`` requires the computation of *double* with
    # ``a.result``, then the executor should schedule *double*.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], double)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history
        .add_activity_task(double,
                           decision_id=decision_id,
                           last_state='completed',
                           activity_id='activity-tests.test_dataflow.double-1',
                           input={'args': 2},
                           result=4)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # *double* has completed and the ``b.result``is now available. The executor
    # should complete the workflow and its result to ``b.result``.
    decisions, _ = executor.replay(history)
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json.dumps(4))

    assert decisions[0] == workflow_completed
예제 #34
0
파일: helpers.py 프로젝트: di/simpleflow
def load_workflow(domain,
                  workflow_name,
                  task_list=None,
                  repair_with=None,
                  force_activities=None):
    """
    Load a workflow.
    :param domain:
    :type domain: str
    :param workflow_name:
    :type workflow_name: str
    :param task_list:
    :type task_list: Optional[str]
    :param repair_with:
    :type repair_with: Optional[simpleflow.history.History]
    :param force_activities:
    :type force_activities: Optional[str]
    :return: Executor for this workflow
    :rtype: Executor
    """
    module_name, object_name = workflow_name.rsplit('.', 1)
    module = __import__(module_name, fromlist=['*'])

    workflow = getattr(module, object_name)
    return Executor(swf.models.Domain(domain),
                    workflow,
                    task_list,
                    repair_with=repair_with,
                    force_activities=force_activities)
예제 #35
0
def test_activity_task_timeout_retry():
    workflow = ATestDefinitionRetryActivity
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)
    decision_id = history.last_id
    (history.add_activity_task(
        increment_retry,
        activity_id='activity-tests.data.activities.increment_retry-1',
        decision_id=decision_id,
        last_state='timed_out',
        timeout_type='START_TO_CLOSE'))

    decisions, _ = executor.replay(Response(history=history))
    assert len(decisions) == 1
    check_task_scheduled_decision(decisions[0], increment_retry)
예제 #36
0
def test_workflow_with_more_than_max_decisions():
    workflow = TestDefinitionMoreThanMaxDecisions
    executor = Executor(DOMAIN, workflow)
    history = builder.History(workflow)

    # The first time, the executor should schedule ``constants.MAX_DECISIONS``
    # decisions and a timer to force the scheduling of the remaining tasks.
    decisions, _ = executor.replay(history)
    assert len(decisions) == constants.MAX_DECISIONS
    assert decisions[-1].type == 'StartTimer'

    decision_id = history.last_id
    for i in xrange(constants.MAX_DECISIONS):
        history.add_activity_task(
            increment,
            decision_id=decision_id,
            activity_id='activity-tests.test_dataflow.increment-{}'.format(
                i + 1),
            last_state='completed',
            result=i + 1)
    (history
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # Once the first batch of ``constants.MAX_DECISIONS`` tasks is finished,
    # the executor should schedule the 20 remaining ones.
    decisions, _ = executor.replay(history)
    assert len(decisions) == 20

    for i in xrange(constants.MAX_DECISIONS - 1, constants.MAX_DECISIONS + 20):
        history.add_activity_task(
            increment,
            decision_id=decision_id,
            activity_id='activity-tests.test_dataflow.increment-{}'.format(
                i + 1),
            last_state='completed',
            result=i + 1)
    (history
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # All tasks are finised, the executor should complete the workflow.
    decisions, _ = executor.replay(history)
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result='null')

    assert decisions[0] == workflow_completed
예제 #37
0
def test_workflow_with_more_than_max_decisions():
    workflow = TestDefinitionMoreThanMaxDecisions
    executor = Executor(DOMAIN, workflow)
    history = builder.History(workflow)

    # The first time, the executor should schedule ``constants.MAX_DECISIONS``
    # decisions and a timer to force the scheduling of the remaining tasks.
    decisions, _ = executor.replay(history)
    assert len(decisions) == constants.MAX_DECISIONS
    assert decisions[-1].type == 'StartTimer'

    decision_id = history.last_id
    for i in xrange(constants.MAX_DECISIONS):
        history.add_activity_task(
            increment,
            decision_id=decision_id,
            activity_id='activity-tests.test_dataflow.increment-{}'.format(
                i + 1),
            last_state='completed',
            result=i + 1)
    (history
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # Once the first batch of ``constants.MAX_DECISIONS`` tasks is finished,
    # the executor should schedule the 20 remaining ones.
    decisions, _ = executor.replay(history)
    assert len(decisions) == 20

    for i in xrange(constants.MAX_DECISIONS - 1, constants.MAX_DECISIONS + 20):
        history.add_activity_task(
            increment,
            decision_id=decision_id,
            activity_id='activity-tests.test_dataflow.increment-{}'.format(
                i + 1),
            last_state='completed',
            result=i + 1)
    (history
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # All tasks are finised, the executor should complete the workflow.
    decisions, _ = executor.replay(history)
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result='null')

    assert decisions[0] == workflow_completed
예제 #38
0
def test_workflow_retry_activity_failed_again():
    workflow = TestDefinitionRetryActivity
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)

    # There is a single task, hence the executor should schedule it first.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment_retry)

    # Let's add the task in ``failed`` state.
    decision_id = history.last_id
    (history
        .add_activity_task(
            increment_retry,
            decision_id=decision_id,
            last_state='failed',
            activity_id='activity-tests.test_dataflow.increment_retry-1')
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # As the retry value is one, the executor should retry i.e. schedule the
    # task again.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment_retry)

    # Let's add the task in ``failed`` state again.
    decision_id = history.last_id
    (history
        .add_activity_task(
            increment_retry,
            decision_id=decision_id,
            last_state='failed',
            activity_id='activity-tests.test_dataflow.increment_retry-1')
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # There is no more retry. The executor should set `Future.exception` and
    # complete the workflow as there is no further task.
    decisions, _ = executor.replay(history)

    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    # ``a.result`` is ``None`` because it was not set.
    workflow_completed.complete(result=json.dumps(None))

    assert decisions[0] == workflow_completed
예제 #39
0
def test_activity_task_timeout_retry():
    workflow = TestDefinitionRetryActivity
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)
    decision_id = history.last_id
    (history
        .add_activity_task(
            increment_retry,
            activity_id='activity-tests.test_dataflow.increment_retry-1',
            decision_id=decision_id,
            last_state='timed_out',
            timeout_type='START_TO_CLOSE'))

    decisions, _ = executor.replay(history)
    assert len(decisions) == 1
    check_task_scheduled_decision(decisions[0], increment_retry)
예제 #40
0
def test_workflow_reuse_same_future():
    workflow = ATestDefinitionSameFuture
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)

    # *double* depends on *increment*, then the executor should only schedule
    # *increment* at first.
    decisions, _ = executor.replay(Response(history=history))
    check_task_scheduled_decision(decisions[0], increment)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history.add_activity_task(
        increment,
        decision_id=decision_id,
        last_state='completed',
        input={
            'args': 1
        },
        activity_id='activity-tests.data.activities.increment-1',
        result=2).add_decision_task_scheduled().add_decision_task_started())

    # *increment* is finished, the executor should schedule *double*.
    decisions, _ = executor.replay(Response(history=history))
    check_task_scheduled_decision(decisions[0], double)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history.add_activity_task(
        double,
        decision_id=decision_id,
        last_state='completed',
        activity_id='activity-tests.data.activities.double-1',
        input={
            'args': 2
        },
        result=4).add_decision_task_scheduled().add_decision_task_started())

    # The executor should now complete the workflow.
    decisions, _ = executor.replay(Response(history=history))
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json_dumps(4))

    assert decisions[0] == workflow_completed
예제 #41
0
def test_workflow_reuse_same_future():
    workflow = TestDefinitionSameFuture
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)

    # *double* depends on *increment*, then the executor should only schedule
    # *increment* at first.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history
        .add_activity_task(increment,
                           decision_id=decision_id,
                           last_state='completed',
                           input={'args': 1},
                           activity_id='activity-tests.test_dataflow.increment-1',
                           result=2)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # *increment* is finished, the executor should schedule *double*.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], double)

    # Let's add the task to the history to simulate its completion.
    decision_id = history.last_id
    (history
        .add_activity_task(double,
                           decision_id=decision_id,
                           last_state='completed',
                           activity_id='activity-tests.test_dataflow.double-1',
                           input={'args': 2},
                           result=4)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # The executor should now complete the workflow.
    decisions, _ = executor.replay(history)
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json.dumps(4))

    assert decisions[0] == workflow_completed
예제 #42
0
def test_activity_not_found_schedule_failed():
    workflow = TestDefinition
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)
    decision_id = history.last_id
    (history
        .add_activity_task_schedule_failed(
            activity_id='activity-tests.test_dataflow.increment-1',
            decision_id=decision_id,
            activity_type={
                'name': increment.name,
                'version': increment.version
            },
            cause='ACTIVITY_TYPE_DOES_NOT_EXIST'))

    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment)
예제 #43
0
def test_activity_not_found_schedule_failed():
    workflow = TestDefinition
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)
    decision_id = history.last_id
    (history
        .add_activity_task_schedule_failed(
            activity_id='activity-tests.test_dataflow.increment-1',
            decision_id=decision_id,
            activity_type={
                'name': increment.name,
                'version': increment.version
            },
            cause='ACTIVITY_TYPE_DOES_NOT_EXIST'))

    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment)
예제 #44
0
def test_activity_task_timeout():
    workflow = TestDefinition
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)
    decision_id = history.last_id
    (history
        .add_activity_task(
            increment,
            activity_id='activity-tests.data.activities.increment-1',
            decision_id=decision_id,
            last_state='timed_out',
            timeout_type='START_TO_CLOSE'))

    decisions, _ = executor.replay(history)
    # The task timed out and there is no retry.
    assert len(decisions) == 1
    check_task_scheduled_decision(decisions[0], double)
예제 #45
0
def test_workflow_with_child_workflow():
    workflow = TestDefinitionChildWorkflow
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow,
                              input={'args': (1,)})

    # The executor should schedule the execution of a child workflow.
    decisions, _ = executor.replay(history)
    assert len(decisions) == 1
    assert decisions == [{
        'startChildWorkflowExecutionDecisionAttributes': {
            'workflowId': 'workflow-test_workflow-1',
            'taskList': {
                'name': 'test_task_list'
            },
            'executionStartToCloseTimeout': '3600',
            'input': '{"args": [1], "kwargs": {}}',
            'workflowType': {
                'version': 'test_version',
                'name': 'test_workflow'
            },
            'taskStartToCloseTimeout': '300'
        },
        'decisionType': 'StartChildWorkflowExecution'
    }]

    # Let's add the child workflow to the history to simulate its completion.
    (history
        .add_decision_task()
        .add_child_workflow(
            workflow,
            workflow_id='workflow-test_workflow-1',
            task_list=TestWorkflow.task_list,
            input='"{\\"args\\": [1], \\"kwargs\\": {}}"',
            result='4'))

    # Now the child workflow is finished and the executor should complete the
    # workflow.
    decisions, _ = executor.replay(history)
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json.dumps(4))

    assert decisions[0] == workflow_completed
예제 #46
0
def test_workflow_with_child_workflow():
    workflow = ATestDefinitionChildWorkflow
    executor = Executor(DOMAIN, workflow)

    # FIXME the original test only contains args, and check both keys are present.
    # FIXME But their order is unspecified from one execution to the next
    input = {'args': (1, ), 'kwargs': {}}
    history = builder.History(workflow, input=input)

    # The executor should schedule the execution of a child workflow.
    decisions, _ = executor.replay(Response(history=history))
    assert len(decisions) == 1
    assert decisions == [{
        'startChildWorkflowExecutionDecisionAttributes': {
            'workflowId': 'workflow-test_workflow-1',
            'taskList': {
                'name': 'test_task_list'
            },
            'executionStartToCloseTimeout': '3600',
            'input': json_dumps(input),
            'workflowType': {
                'version': 'test_version',
                'name': 'test_workflow'
            },
            'taskStartToCloseTimeout': '300'
        },
        'decisionType': 'StartChildWorkflowExecution'
    }]

    # Let's add the child workflow to the history to simulate its completion.
    (history.add_decision_task().add_child_workflow(
        workflow,
        workflow_id='workflow-test_workflow-1',
        task_list=ATestWorkflow.task_list,
        input='"{\\"args\\": [1], \\"kwargs\\": {}}"',
        result='4'))

    # Now the child workflow is finished and the executor should complete the
    # workflow.
    decisions, _ = executor.replay(Response(history=history))
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json_dumps(4))

    assert decisions[0] == workflow_completed
예제 #47
0
def test_workflow_with_child_workflow():
    workflow = TestDefinitionChildWorkflow
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow,
                              input={'args': (1,)})

    # The executor should schedule the execution of a child workflow.
    decisions, _ = executor.replay(history)
    assert len(decisions) == 1
    assert decisions == [{
        'startChildWorkflowExecutionDecisionAttributes': {
            'workflowId': 'workflow-test_workflow-1',
            'taskList': {
                'name': 'test_task_list'
            },
            'executionStartToCloseTimeout': '3600',
            'input': '{"args": [1], "kwargs": {}}',
            'workflowType': {
                'version': 'test_version',
                'name': 'test_workflow'
            },
            'taskStartToCloseTimeout': '300'
        },
        'decisionType': 'StartChildWorkflowExecution'
    }]

    # Let's add the child workflow to the history to simulate its completion.
    (history
        .add_decision_task()
        .add_child_workflow(
            workflow,
            workflow_id='workflow-test_workflow-1',
            task_list=TestWorkflow.task_list,
            input='"{\\"args\\": [1], \\"kwargs\\": {}}"',
            result='4'))

    # Now the child workflow is finished and the executor should complete the
    # workflow.
    decisions, _ = executor.replay(history)
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json.dumps(4))

    assert decisions[0] == workflow_completed
예제 #48
0
def test_workflow_retry_activity():
    workflow = TestDefinitionRetryActivity
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)

    # There is a single task, hence the executor should schedule it first.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment_retry)

    # Let's add the task in ``failed`` state.
    decision_id = history.last_id
    (history
        .add_activity_task(increment_retry,
                           decision_id=decision_id,
                           last_state='failed',
                           activity_id='activity-tests.test_dataflow.increment_retry-1')
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # As the retry value is one, the executor should retry i.e. schedule the
    # task again.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment_retry)

    # Let's add the task in ``completed`` state.
    decision_id = history.last_id
    (history
        .add_activity_task(increment_retry,
                           decision_id=decision_id,
                           last_state='completed',
                           activity_id='activity-tests.test_dataflow.increment_retry-1',
                           input={'args': 7},
                           result=8)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # Now the task is finished and the executor should complete the workflow.
    decisions, _ = executor.replay(history)
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json.dumps(8))

    assert decisions[0] == workflow_completed
예제 #49
0
def test_workflow_retry_activity():
    workflow = TestDefinitionRetryActivity
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)

    # There is a single task, hence the executor should schedule it first.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment_retry)

    # Let's add the task in ``failed`` state.
    decision_id = history.last_id
    (history
        .add_activity_task(increment_retry,
                           decision_id=decision_id,
                           last_state='failed',
                           activity_id='activity-tests.test_dataflow.increment_retry-1')
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # As the retry value is one, the executor should retry i.e. schedule the
    # task again.
    decisions, _ = executor.replay(history)
    check_task_scheduled_decision(decisions[0], increment_retry)

    # Let's add the task in ``completed`` state.
    decision_id = history.last_id
    (history
        .add_activity_task(increment_retry,
                           decision_id=decision_id,
                           last_state='completed',
                           activity_id='activity-tests.test_dataflow.increment_retry-1',
                           input={'args': 7},
                           result=8)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # Now the task is finished and the executor should complete the workflow.
    decisions, _ = executor.replay(history)
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(result=json.dumps(8))

    assert decisions[0] == workflow_completed
예제 #50
0
def test_workflow_retry_activity_failed_again():
    workflow = ATestDefinitionRetryActivity
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)

    # There is a single task, hence the executor should schedule it first.
    decisions, _ = executor.replay(Response(history=history))
    check_task_scheduled_decision(decisions[0], increment_retry)

    # Let's add the task in ``failed`` state.
    decision_id = history.last_id
    (history.add_activity_task(
        increment_retry,
        decision_id=decision_id,
        last_state='failed',
        activity_id='activity-tests.data.activities.increment_retry-1').
     add_decision_task_scheduled().add_decision_task_started())

    # As the retry value is one, the executor should retry i.e. schedule the
    # task again.
    decisions, _ = executor.replay(Response(history=history))
    check_task_scheduled_decision(decisions[0], increment_retry)

    # Let's add the task in ``failed`` state again.
    decision_id = history.last_id
    (history.add_activity_task(
        increment_retry,
        decision_id=decision_id,
        last_state='failed',
        activity_id='activity-tests.data.activities.increment_retry-1').
     add_decision_task_scheduled().add_decision_task_started())

    # There is no more retry. The executor should set `Future.exception` and
    # complete the workflow as there is no further task.
    decisions, _ = executor.replay(Response(history=history))

    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    # ``a.result`` is ``None`` because it was not set.
    workflow_completed.complete(result=json_dumps(None))

    assert decisions[0] == workflow_completed
예제 #51
0
def test_workflow_start_complete_callback():
    workflow = TestCallBacks

    executor = Executor(DOMAIN, workflow)

    args = [4]
    kwargs = { 'kwarg1': 'bar' }

    result = 5
    history = builder.History(workflow, input={'args': args, 'kwargs': kwargs})

    decisions, _ = executor.replay(history)

    assert executor._workflow.on_start_called
    assert executor._workflow.on_start_args == args
    assert executor._workflow.on_start_kwargs == kwargs

    executor._workflow.on_start_called = False

    decision_id = history.last_id
    scheduled_id = decision_id + 1

    (history
        .add_activity_task(increment,
                           decision_id=decision_id,
                           last_state='completed',
                           activity_id='activity-tests.test_decider.increment-1',
                           input={'args': args[0]},
                           result=result)
        .add_decision_task_scheduled()
        .add_decision_task_started())

    decisions, _ = executor.replay(history)

    # on_start should only be called once
    assert not executor._workflow.on_start_called

    assert executor._workflow.on_complete_called
    assert executor._workflow.on_complete_args == args
    assert executor._workflow.on_complete_kwargs == kwargs
    assert executor._workflow.on_complete_result == result
    assert executor._workflow.on_complete_history._history == history
예제 #52
0
def test_activity_not_found_schedule_failed_already_exists():
    workflow = TestDefinition
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)
    decision_id = history.last_id
    (history
        .add_activity_task_schedule_failed(
            activity_id='activity-tests.data.activities.increment-1',
            decision_id=decision_id,
            activity_type={
                'name': increment.name,
                'version': increment.version
            },
            cause='ACTIVITY_TYPE_DOES_NOT_EXIST'))

    with mock.patch(
            'swf.models.ActivityType.save',
            raise_already_exists(increment)):
        decisions, _ = executor.replay(history)

    check_task_scheduled_decision(decisions[0], increment)
예제 #53
0
def test_activity_task_timeout_raises():
    workflow = TestDefinitionActivityRaisesOnFailure
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)
    decision_id = history.last_id
    (history
        .add_activity_task(
            raise_on_failure,
            activity_id='activity-tests.test_dataflow.raise_on_failure-1',
            decision_id=decision_id,
            last_state='timed_out',
            timeout_type='START_TO_CLOSE'))

    decisions, _ = executor.replay(history)
    workflow_failed = swf.models.decision.WorkflowExecutionDecision()
    workflow_failed.fail(
        reason='Workflow execution error in task '
               'activity-tests.test_dataflow.raise_on_failure: '
               '"TimeoutError(START_TO_CLOSE)"')

    assert decisions[0] == workflow_failed
예제 #54
0
def test_workflow_map():
    workflow = TestDefinitionMap
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow)

    nb_parts = TestDefinitionMap.nb_parts

    # All the futures returned by the map are passed to wait().
    # The executor should then schedule all of them.
    decisions, _ = executor.replay(history)
    for i in xrange(nb_parts):
        check_task_scheduled_decision(decisions[i], increment)

    # Let's add all tasks of the map to the history to simulate their
    # completion.
    decision_id = history.last_id
    for i in xrange(nb_parts):
        history.add_activity_task(
            increment,
            decision_id=decision_id,
            activity_id='activity-tests.test_dataflow.increment-{}'.format(
                i + 1),
            last_state='completed',
            input={'args': i},
            result=i + 1)
    (history
        .add_decision_task_scheduled()
        .add_decision_task_started())

    # All tasks are finished, the executor should complete the workflow.
    decisions, _ = executor.replay(history)
    workflow_completed = swf.models.decision.WorkflowExecutionDecision()
    workflow_completed.complete(
        result=json.dumps([i + 1 for i in xrange(nb_parts)]))

    assert decisions[0] == workflow_completed
예제 #55
0
def test_task_naming():
    workflow = TestTaskNaming
    executor = Executor(DOMAIN, workflow)

    history = builder.History(workflow, input={})

    decisions, _ = executor.replay(history)
    expected = [
        # non idempotent task, should increment
        "activity-tests.data.activities.increment-1",
        # non idempotent task, should increment again
        "activity-tests.data.activities.increment-2",
        # idempotent task, with arg 1
        "activity-tests.data.activities.triple-deb8adb88b687c0df408628aa69b1377",
        # idempotent task, with arg 2
        "activity-tests.data.activities.triple-d269dc325a06c6ad32888f450ee8dd30",
        # idempotent task, with arg 2 too => same task id
        "activity-tests.data.activities.triple-d269dc325a06c6ad32888f450ee8dd30",
        # class-based task, non idempotent
        "activity-tests.data.activities.Tetra-1",
    ]
    for i in range(0, len(expected)):
        decision = decisions[i]['scheduleActivityTaskDecisionAttributes']
        assert decision['activityId'] == expected[i]
예제 #56
0
class TestWorkflowMixin(object):

    def build_history(self, workflow_input):
        domain = Domain("TestDomain")
        self.executor = Executor(domain, self.WORKFLOW)
        self.history = builder.History(self.WORKFLOW, input=workflow_input)

    def replay(self):
        decisions = self.executor.replay(
            Response(history=self.history, execution=None),
            decref_workflow=False)
        return decisions.decisions

    def check_task_scheduled_decision(self, decision, task):
        """
        Asserts that *decision* schedules *task*.
        """
        assert decision['decisionType'] == 'ScheduleActivityTask'

        attributes = decision['scheduleActivityTaskDecisionAttributes']
        assert attributes['activityType']['name'] == task.name

    def add_activity_task_from_decision(self, decision, activity, result=None, last_state="completed"):
        attributes = decision['scheduleActivityTaskDecisionAttributes']
        decision_id = self.history.last_id
        activity_id = attributes["activityId"]
        activity_input = attributes["input"]
        (self.history
            .add_activity_task(
                activity,
                decision_id=decision_id,
                activity_id=activity_id,
                last_state=last_state,
                input=activity_input,
                result=result)
        )
예제 #57
0
 def build_decisions(self, workflow_class):
     self.decider = Decider(self.domain, self.decision_task_list)
     response = self.decider.poll()
     self._decision_token = response.token
     self.executor = Executor(self.domain, workflow_class)
     return self.executor.replay(response)