def test_get_input_json_str():
    builder = ContextBuilder('test_function_context')
    builder.input_ = json.dumps({'city': 'Seattle'})
    context = DurableOrchestrationContext.from_json(builder.to_json_string())

    result = context.get_input()

    assert 'Seattle' == result['city']
def add_hello_failed_events(
        context_builder: ContextBuilder, id_: int, reason: str, details: str):
    context_builder.add_task_scheduled_event(name='Hello', id_=id_)
    context_builder.add_orchestrator_completed_event()
    context_builder.add_orchestrator_started_event()
    context_builder.add_task_failed_event(
        id_=id_, reason=reason, details=details)
def add_completed_event(context_builder: ContextBuilder, id_: int, name: str,
                        result):
    context_builder.add_task_scheduled_event(name=name, id_=id_)
    context_builder.add_orchestrator_completed_event()
    context_builder.add_orchestrator_started_event()
    context_builder.add_task_completed_event(id_=id_,
                                             result=json.dumps(result))
Exemple #4
0
def add_failed_http_events(context_builder: ContextBuilder, id_: int,
                           reason: str, details: str):
    context_builder.add_task_scheduled_event(name=HTTP_ACTION_NAME, id_=id_)
    context_builder.add_orchestrator_completed_event()
    context_builder.add_orchestrator_started_event()
    context_builder.add_task_failed_event(id_=id_,
                                          reason=reason,
                                          details=details)
def add_hello_suborch_completed_events(context_builder: ContextBuilder,
                                       id_: int, result: str):
    context_builder.add_sub_orchestrator_started_event(
        name="HelloSubOrchestrator", id_=id_, input_="")
    context_builder.add_orchestrator_completed_event()
    context_builder.add_orchestrator_started_event()
    context_builder.add_sub_orchestrator_completed_event(result=result,
                                                         id_=id_)
Exemple #6
0
def test_failed_parrot_value():
    failed_reason = 'Reasons'
    failed_details = 'Stuff and Things'
    activity_count = 5
    context_builder = ContextBuilder('test_fan_out_fan_in_function')
    add_completed_event(context_builder, 0, 'GetActivityCount', activity_count)
    add_completed_task_set_events(context_builder, 1, 'ParrotValue', activity_count,
                                  2, failed_reason, failed_details)

    try:
        result = get_orchestration_state_result(
            context_builder, generator_function)
        # we expected an exception
        assert False
    except Exception as e:
        error_label = "\n\n$OutOfProcData$:"
        error_str = str(e)

        expected_state = base_expected_state(error=f'{failed_reason} \n {failed_details}')
        add_single_action(expected_state, function_name='GetActivityCount', input_=None)
        add_multi_actions(expected_state, function_name='ParrotValue', volume=activity_count)

        error_msg = f'{failed_reason} \n {failed_details}'
        expected_state._error = error_msg
        state_str = expected_state.to_json_string()
        
        expected_error_str = f"{error_msg}{error_label}{state_str}"
        assert expected_error_str == error_str
Exemple #7
0
def test_failed_state():
    failed_reason = 'Reasons'
    failed_details = 'Stuff and Things'
    context_builder = ContextBuilder('test_simple_function')
    add_failed_http_events(context_builder, 0, failed_reason, failed_details)

    try:
        result = get_orchestration_state_result(context_builder,
                                                simple_get_generator_function)
        # We expected an exception
        assert False
    except Exception as e:
        error_label = "\n\n$OutOfProcData$:"
        error_str = str(e)

        expected_state = base_expected_state()
        request = get_request()
        add_http_action(expected_state, request)

        error_msg = f'{failed_reason} \n {failed_details}'
        expected_state._error = error_msg
        state_str = expected_state.to_json_string()

        expected_error_str = f"{error_msg}{error_label}{state_str}"
        assert expected_error_str == error_str
def test_failed_tokyo_hit_max_attempts():
    failed_reason = 'Reasons'
    failed_details = 'Stuff and Things'
    context_builder = ContextBuilder('test_simple_function')
    add_hello_failed_events(context_builder, 0, failed_reason, failed_details)
    add_retry_timer_events(context_builder, 1)
    add_hello_failed_events(context_builder, 2, failed_reason, failed_details)
    add_retry_timer_events(context_builder, 3)
    add_hello_failed_events(context_builder, 4, failed_reason, failed_details)
    add_retry_timer_events(context_builder, 5)

    try:
        result = get_orchestration_state_result(context_builder,
                                                generator_function)
        # expected an exception
        assert False
    except Exception as e:
        error_label = "\n\n$OutOfProcData$:"
        error_str = str(e)

        expected_state = base_expected_state()
        add_hello_action(expected_state, 'Tokyo')

        error_msg = f'{failed_reason} \n {failed_details}'
        expected_state._error = error_msg
        state_str = expected_state.to_json_string()

        expected_error_str = f"{error_msg}{error_label}{state_str}"
        assert expected_error_str == error_str
Exemple #9
0
def test_tokyo_and_seattle_and_london_state_all_failed():
    failed_reason = 'Reasons'
    failed_details = 'Stuff and Things'
    context_builder = ContextBuilder('test_simple_function')
    add_hello_suborch_failed_events(context_builder, 0, failed_reason,
                                    failed_details)
    add_retry_timer_events(context_builder, 1)
    add_hello_suborch_failed_events(context_builder, 2, failed_reason,
                                    failed_details)
    add_retry_timer_events(context_builder, 3)
    add_hello_suborch_failed_events(context_builder, 4, failed_reason,
                                    failed_details)
    add_retry_timer_events(context_builder, 5)

    result = get_orchestration_state_result(context_builder,
                                            generator_function)

    expected_state = base_expected_state()
    add_hello_suborch_action(expected_state, 'Tokyo')
    expected_state._error = f'{failed_reason} \n {failed_details}'
    expected = expected_state.to_json()
    expected_state._is_done = True

    #assert_valid_schema(result)
    assert_orchestration_state_equals(expected, result)
def test_timers_comparison_with_relaxed_precision():
    """Test if that two `datetime` different but equivalent
    serializations of timer deadlines are found to be equivalent.

    The Durable Extension may sometimes drop redundant zeroes on
    a datetime object. For instance, the date
        2020-07-23T21:56:54.936700Z
    may get transformed into
        2020-07-23T21:56:54.9367Z
    This test ensures that dropping redundant zeroes does not affect
    our ability to recognize that a timer has been fired.
    """

    # equivalent to 2020-07-23T21:56:54.936700Z
    relaxed_timestamp = "2020-07-23T21:56:54.9367Z"
    fire_at = datetime.strptime(relaxed_timestamp, DATETIME_STRING_FORMAT)

    context_builder = ContextBuilder("relaxed precision")
    add_timer_fired_events(context_builder, 0, relaxed_timestamp)

    result = get_orchestration_state_result(context_builder,
                                            generator_function)

    expected_state = base_expected_state(output='Done!')
    add_timer_action(expected_state, fire_at)

    expected_state._is_done = True
    expected = expected_state.to_json()

    #assert_valid_schema(result)
    # TODO: getting the following error when validating the schema
    # "Additional properties are not allowed ('fireAt', 'isCanceled' were unexpected)">
    assert_orchestration_state_equals(expected, result)
def test_show_me_the_sum_success():
    activity_count = 5
    sum_ = 0
    for i in range(activity_count):
        sum_ += i
    sum_results = f"Well that's nice {sum_}!"
    context_builder = ContextBuilder('test_fan_out_fan_in_function')
    add_completed_event(context_builder, 0, 'GetActivityCount', activity_count)
    add_completed_task_set_events(context_builder, 1, 'ParrotValue',
                                  activity_count)
    add_completed_event(context_builder, activity_count + 1, 'ShowMeTheSum',
                        sum_results)

    result = get_orchestration_state_result(context_builder,
                                            generator_function)

    expected_state = base_expected_state(sum_results)
    add_single_action(expected_state,
                      function_name='GetActivityCount',
                      input_=None)
    add_multi_actions(expected_state,
                      function_name='ParrotValue',
                      volume=activity_count)
    results = []
    for i in range(activity_count):
        results.append(i)
    add_single_action(expected_state,
                      function_name='ShowMeTheSum',
                      input_=results)
    expected_state._is_done = True
    expected = expected_state.to_json()

    assert_valid_schema(result)
    assert_orchestration_state_equals(expected, result)
def test_failed_parrot_value():
    failed_reason = 'Reasons'
    failed_details = 'Stuff and Things'
    activity_count = 5
    context_builder = ContextBuilder('test_fan_out_fan_in_function')
    add_completed_event(context_builder, 0, 'GetActivityCount', activity_count)
    add_completed_task_set_events(context_builder, 1, 'ParrotValue',
                                  activity_count, 2, failed_reason,
                                  failed_details)

    result = get_orchestration_state_result(context_builder,
                                            generator_function)

    expected_state = base_expected_state(
        error=f'{failed_reason} \n {failed_details}')
    add_single_action(expected_state,
                      function_name='GetActivityCount',
                      input_=None)
    add_multi_actions(expected_state,
                      function_name='ParrotValue',
                      volume=activity_count)
    expected = expected_state.to_json()

    assert_valid_schema(result)
    assert_orchestration_state_equals(expected, result)
Exemple #13
0
def test_tokyo_and_seattle_and_london_with_serialization_state():
    """Tests the sequential function pattern with custom object serialization.
    
    This simple test validates that a sequential function pattern returns
    the expected state when the input to activities is a user-provided
    serializable class.
    """
    context_builder = ContextBuilder('test_simple_function')
    add_hello_completed_events(context_builder, 0, "\"Hello Tokyo!\"")
    add_hello_completed_events(context_builder, 1, "\"Hello Seattle!\"")
    add_hello_completed_events(context_builder, 2, "\"Hello London!\"")

    result = get_orchestration_state_result(
        context_builder, generator_function_with_serialization)

    expected_state = base_expected_state(
        ['Hello Tokyo!', 'Hello Seattle!', 'Hello London!'])
    add_hello_action(expected_state, SerializableClass("Tokyo"))
    add_hello_action(expected_state, SerializableClass("Seattle"))
    add_hello_action(expected_state, SerializableClass("London"))
    expected_state._is_done = True
    expected = expected_state.to_json()

    assert_valid_schema(result)
    assert_orchestration_state_equals(expected, result)
Exemple #14
0
def test_is_replaying_initial_value():

    context_builder = ContextBuilder("")
    result = get_orchestration_property(
        context_builder, generator_function, "durable_context")

    assert result.is_replaying == False
Exemple #15
0
def test_tokyo_and_seattle_and_london_state_partial_failure():
    failed_reason = 'Reasons'
    failed_details = 'Stuff and Things'
    context_builder = ContextBuilder('test_simple_function')
    add_hello_suborch_completed_events(context_builder, 0, "\"Hello Tokyo!\"")
    add_hello_suborch_failed_events(context_builder, 1, failed_reason,
                                    failed_details)
    add_retry_timer_events(context_builder, 3)
    add_hello_suborch_completed_events(context_builder, 4,
                                       "\"Hello Seattle!\"")
    add_hello_suborch_completed_events(context_builder, 5, "\"Hello London!\"")

    result = get_orchestration_state_result(context_builder,
                                            generator_function)

    expected_state = base_expected_state(
        ['Hello Tokyo!', 'Hello Seattle!', 'Hello London!'])
    add_hello_suborch_action(expected_state, 'Tokyo')
    add_hello_suborch_action(expected_state, 'Seattle')
    add_hello_suborch_action(expected_state, 'London')
    expected_state._is_done = True
    expected = expected_state.to_json()

    #assert_valid_schema(result)
    assert_orchestration_state_equals(expected, result)
Exemple #16
0
def test_user_code_raises_exception():
    context_builder = ContextBuilder('test_simple_function')
    add_hello_completed_events(context_builder, 0, "\"Hello Tokyo!\"")
    add_hello_completed_events(context_builder, 1, "\"Hello Seattle!\"")
    add_hello_completed_events(context_builder, 2, "\"Hello London!\"")

    try:
        result = get_orchestration_state_result(context_builder,
                                                generator_function_rasing_ex)
        # expected an exception
        assert False
    except Exception as e:
        error_label = "\n\n$OutOfProcData$:"
        error_str = str(e)

        expected_state = base_expected_state()
        add_hello_action(expected_state, 'Tokyo')
        add_hello_action(expected_state, 'Seattle')
        add_hello_action(expected_state, 'London')
        error_msg = 'Oops!'
        expected_state._error = error_msg
        state_str = expected_state.to_json_string()

        expected_error_str = f"{error_msg}{error_label}{state_str}"
        assert expected_error_str == error_str
Exemple #17
0
def add_hello_suborch_failed_events(context_builder: ContextBuilder, id_: int,
                                    reason: str, details: str):
    context_builder.add_sub_orchestrator_started_event(
        name="HelloSubOrchestrator", id_=id_, input_="")
    context_builder.add_orchestrator_completed_event()
    context_builder.add_orchestrator_started_event()
    context_builder.add_sub_orchestrator_failed_event(id_=id_,
                                                      reason=reason,
                                                      details=details)
def test_added_function_context_args():
    context_builder = ContextBuilder('test_function_context')

    additional_attributes = {
        "attrib1": 1,
        "attrib2": "two",
        "attrib3": {
            "randomDictionary": "random"
        }
    }

    context_as_string = context_builder.to_json_string(**additional_attributes)

    durable_context = DurableOrchestrationContext.from_json(context_as_string)

    assert durable_context.function_context is not None
    for key in additional_attributes:
        assert additional_attributes[key] == getattr(
            durable_context.function_context, key)
def test_event_raised_return_completed_task():
    timestamp = datetime.now()
    json_input = '{"test":"somecontent"}'
    expected_action = WaitForExternalEventAction("A")
    context_builder = ContextBuilder('test_simple_function')
    context_builder.add_event_raised_event(name="A",
                                           input_=json_input,
                                           timestamp=timestamp,
                                           id_=1)

    returned_task = wait_for_external_event_task(
        context_builder.history_events, "A")
    expected_task = Task(is_completed=True,
                         is_faulted=False,
                         action=expected_action,
                         result=json.loads(json_input),
                         timestamp=timestamp.replace(tzinfo=tzutc()),
                         id_=1)

    assert_tasks_equal(expected_task, returned_task)
def test_event_not_raised_return_incompleted_task():
    context_builder = ContextBuilder('test_simple_function')
    expected_action = WaitForExternalEventAction("A")

    returned_task = wait_for_external_event_task(
        context_builder.history_events, "A")
    expected_task = Task(is_completed=False,
                         is_faulted=False,
                         action=expected_action)

    assert_tasks_equal(expected_task, returned_task)
Exemple #21
0
def test_initial_call():
    context_builder = ContextBuilder('test_fan_out_fan_in_function')

    result = get_orchestration_state_result(
        context_builder, generator_function)

    expected_state = base_expected_state()
    add_single_action(expected_state, function_name='GetActivityCount', input_=None)
    expected = expected_state.to_json()

    assert_valid_schema(result)
    assert_orchestration_state_equals(expected, result)
Exemple #22
0
    def _complete_event(context: ContextBuilder,
                        id_counter: int) -> Tuple[ContextBuilder, int]:
        """Add event / task completions to the context.

        Parameters
        ----------
        context: ContextBuilder
            Orchestration context mock, to which we'll add the event completion events
        id_counter: int
            The current event counter

        Returns
        -------
        Tuple[ContextBuilder, int]
            The updated context, the updated id_counter
        """
        for id_, city in zip(scheduled_ids, CITIES):
            result = f"\"{RESULT_PREFIX}{city}\""
            context.add_task_completed_event(id_=id_, result=result)
            id_counter += 1
        return context, id_counter
Exemple #23
0
def test_initial_orchestration_state():
    context_builder = ContextBuilder('test_simple_function')

    result = get_orchestration_state_result(context_builder,
                                            generator_function)

    expected_state = base_expected_state()
    add_hello_action(expected_state, 'Tokyo')
    expected = expected_state.to_json()

    assert_valid_schema(result)
    assert_orchestration_state_equals(expected, result)
Exemple #24
0
def test_call_entity_sent():
    context_builder = ContextBuilder('test_simple_function')

    entityId = df.EntityId("Counter", "myCounter")
    result = get_orchestration_state_result(
        context_builder, generator_function_call_entity)

    expected_state = base_expected_state()
    add_call_entity_action(expected_state, entityId, "add", 3)
    expected = expected_state.to_json()

    #assert_valid_schema(result)
    assert_orchestration_state_equals(expected, result)
Exemple #25
0
def test_is_replaying_one_replayed_event():

    timestamp = "2020-07-23T21:56:54.9367Z"
    fire_at = datetime.strptime(timestamp, DATETIME_STRING_FORMAT) + timedelta(seconds=30)
    fire_at_str = fire_at.strftime(DATETIME_STRING_FORMAT)

    context_builder = ContextBuilder("")
    add_timer_fired_events(context_builder, 0, fire_at_str, is_played=True)

    result = get_orchestration_property(
        context_builder, generator_function, "durable_context")

    assert result.is_replaying == True
Exemple #26
0
    def _fire_timer(context: ContextBuilder, id_counter: int,
                    deadlines: List[datetime]) -> Tuple[ContextBuilder, int]:
        """Add timer fired events to the context.

        Parameters
        ----------
        context: ContextBuilder
            Orchestration context mock, to which we'll add the event completion events
        id_counter: int
            The current event counter
        deadlines: List[datetime]
            List of dates at which to fire the timers

        Returns
        -------
        Tuple[ContextBuilder, int]:
            The updated context, the updated id_counter
        """
        for id_, fire_at in deadlines:
            context.add_timer_fired_event(id_=id_, fire_at=fire_at)
            id_counter += 1
        return context, id_counter
Exemple #27
0
    def _fail_events(context: ContextBuilder,
                     id_counter: int) -> Tuple[ContextBuilder, int]:
        """Add event failed to the context.

        Parameters
        ----------
        context: ContextBuilder
            Orchestration context mock, to which we'll add the event completion events
        id_counter: int
            The current event counter

        Returns
        -------
        Tuple[ContextBuilder, int]:
            The updated context, the updated id_counter
        """
        context.add_orchestrator_started_event()
        for id_ in scheduled_ids:
            context.add_task_failed_event(id_=id_,
                                          reason=REASONS,
                                          details=DETAILS)
            id_counter += 1
        return context, id_counter
Exemple #28
0
def test_initial_post_state():
    context_builder = ContextBuilder('test_simple_function')

    result = get_orchestration_state_result(context_builder,
                                            complete_generator_function)

    expected_state = base_expected_state()
    request = post_request()
    add_http_action(expected_state, request)
    expected = expected_state.to_json()

    assert_valid_schema(result)
    assert_orchestration_state_equals(expected, result)
    validate_result_http_request(result)
Exemple #29
0
def test_tokyo_state():
    context_builder = ContextBuilder('test_simple_function')
    add_hello_completed_events(context_builder, 0, "\"Hello Tokyo!\"")

    result = get_orchestration_state_result(context_builder,
                                            generator_function)

    expected_state = base_expected_state()
    add_hello_action(expected_state, 'Tokyo')
    add_hello_action(expected_state, 'Seattle')
    expected = expected_state.to_json()

    assert_valid_schema(result)
    assert_orchestration_state_equals(expected, result)
Exemple #30
0
    def _schedule_events(
            context: ContextBuilder,
            id_counter: int) -> Tuple[ContextBuilder, int, List[int]]:
        """Add scheduled events to the context.

        Parameters
        ----------
        context: ContextBuilder
            Orchestration context mock, to which we'll add the event completion events
        id_counter: int
            The current event counter

        Returns
        -------
        Tuple[ContextBuilder, int, List[int]]:
            The updated context, the updated counter, a list of event IDs for each scheduled event
        """
        scheduled_ids: List[int] = []
        for id_ in range(num_activities):
            scheduled_ids.append(id_)
            context.add_task_scheduled_event(name='Hello', id_=id_)
            id_counter += 1
        return context, id_counter, scheduled_ids