예제 #1
0
def test_raises_for_invalid_feed_name_in_lookup():
    with assert_config_error(
            "Invalid feed name at "
            "state_machines.the_workflow.states.0.exit_condition: key "
            "feeds.nope.has_option_b references unknown feed 'nope' (configured "
            "feeds are: 'jacquard')", ):
        load_config(yaml_data('invalid_feed_name_in_exit_condition'))
예제 #2
0
def test_raises_for_invalid_top_level_context_name_in_path():
    with assert_config_error(
            "Invalid context lookup at "
            "state_machines.the_workflow.states.0.next.path: key "
            "jacquard.has_option_b must start with one of 'feeds', 'history' or "
            "'metadata'.", ):
        load_config(yaml_data('invalid_top_level_context_name_in_path'))
예제 #3
0
def test_trivial_config():
    data = yaml_data('trivial')
    expected = Config(
        state_machines={
            'example':
            StateMachine(
                name='example',
                feeds=[],
                webhooks=[],
                states=[
                    Gate(
                        name='start',
                        triggers=[],
                        next_states=NoNextStates(),
                        exit_condition=ExitConditionProgram('false'),
                    ),
                ],
            ),
        },
        database=DatabaseConfig(
            host='localhost',
            port=5432,
            name='routemaster',
            username='******',
            password='',
        ),
        logging_plugins=[],
    )
    with reset_environment():
        assert load_config(data) == expected
예제 #4
0
def test_example_config_loads():
    """
    Test that the example.yaml in this repo can be loaded.

    This ensures that the example file itself is valid and is not intended as a
    test of the system.
    """

    repo_root = Path(__file__).parent.parent.parent.parent
    example_yaml = repo_root / 'example.yaml'

    assert example_yaml.exists(
    ), "Example file is missing! (is this test set up correctly?)"

    example_config = load_config(
        layer_loader.load_files(
            [example_yaml],
            loader=yaml_load,
        ), )

    # Some basic assertions that we got the right thing loaded
    assert list(example_config.state_machines.keys()) == ['user_lifecycle']

    user_lifecycle_machine = example_config.state_machines['user_lifecycle']

    jacquard_feed = FeedConfig('jacquard', 'http://localhost:1212/<label>')
    assert user_lifecycle_machine.feeds == [jacquard_feed]

    assert len(user_lifecycle_machine.states) == 10
    assert user_lifecycle_machine.states[0].name == 'start'
    assert user_lifecycle_machine.states[9].name == 'end'
예제 #5
0
def app_from_config(config_path):
    """
    Create an `App` instance with a session from a given config path.

    By default, will use the example.yaml file.
    """
    config = load_config(
        layer_loader.load_files(
            [config_path],
            loader=yaml_load,
        ), )

    class InteractiveApp(App):
        """
        App for use in interactive shell only.

        Provides a global database session.
        """
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self._session = self._sessionmaker()

        @property
        def session(self):
            """Return the database session."""
            return self._session

    return InteractiveApp(config)
예제 #6
0
def main(ctx, config_files):
    """Shared entrypoint configuration."""
    logging.getLogger('schedule').setLevel(logging.CRITICAL)

    config_data = layer_loader.load_files(
        config_files,
        loader=yaml_load,
    )

    try:
        config = load_config(config_data)
    except ConfigError:
        logger.exception("Configuration Error")
        click.get_current_context().exit(1)

    ctx.obj = App(config)
    _validate_config(ctx.obj)
예제 #7
0
def test_example_config_is_valid(app):
    """
    Test that the example.yaml in this repo is valid.

    This ensures that the example file itself is valid and is not intended as a
    test of the system.
    """

    repo_root = Path(__file__).parent.parent.parent
    example_yaml = repo_root / 'example.yaml'

    assert example_yaml.exists(
    ), "Example file is missing! (is this test set up correctly?)"

    example_config = load_config(
        layer_loader.load_files(
            [example_yaml],
            loader=yaml_load,
        ), )

    # quick check that we've loaded the config we expect
    assert list(example_config.state_machines.keys()) == ['user_lifecycle']

    validate_config(app, example_config)
예제 #8
0
def test_realistic_config():
    data = yaml_data('realistic')
    expected = Config(
        state_machines={
            'example':
            StateMachine(
                name='example',
                feeds=[
                    FeedConfig(name='data_feed',
                               url='http://localhost/<label>'),
                ],
                webhooks=[
                    Webhook(
                        match=re.compile('.+\\.example\\.com'),
                        headers={
                            'x-api-key':
                            'Rahfew7eed1ierae0moa2sho3ieB1et3ohhum0Ei',
                        },
                    ),
                ],
                states=[
                    Gate(
                        name='start',
                        triggers=[
                            SystemTimeTrigger(time=datetime.time(18, 30)),
                            TimezoneAwareTrigger(
                                time=datetime.time(12, 25),
                                timezone='Europe/London',
                            ),
                            MetadataTimezoneAwareTrigger(
                                time=datetime.time(13, 37),
                                timezone_metadata_path=['timezone'],
                            ),
                            MetadataTrigger(metadata_path='foo.bar'),
                            IntervalTrigger(
                                interval=datetime.timedelta(hours=1), ),
                            OnEntryTrigger(),
                        ],
                        next_states=ConstantNextState(state='stage2'),
                        exit_condition=ExitConditionProgram('true'),
                    ),
                    Gate(
                        name='stage2',
                        triggers=[],
                        next_states=ContextNextStates(
                            path='metadata.foo.bar',
                            destinations=[
                                ContextNextStatesOption(
                                    state='stage3',
                                    value='1',
                                ),
                                ContextNextStatesOption(
                                    state='stage3',
                                    value='2',
                                ),
                            ],
                            default='end',
                        ),
                        exit_condition=ExitConditionProgram(
                            'metadata.foo.bar is defined', ),
                    ),
                    Action(
                        name='stage3',
                        webhook='https://localhost/hook',
                        next_states=ConstantNextState(state='end'),
                    ),
                    Gate(
                        name='end',
                        triggers=[],
                        exit_condition=ExitConditionProgram('false'),
                        next_states=NoNextStates(),
                    ),
                ],
            ),
        },
        database=DatabaseConfig(
            host='localhost',
            port=5432,
            name='routemaster',
            username='******',
            password='',
        ),
        logging_plugins=[
            LoggingPluginConfig(
                dotted_path='routemaster_prometheus:PrometheusLogger',
                kwargs={'prometheus_gateway': 'localhost'},
            ),
            LoggingPluginConfig(
                dotted_path='routemaster_sentry:SentryLogger',
                kwargs={'raven_dsn': 'nai8ioca4zeeb2ahgh4V'},
            ),
        ],
    )
    with reset_environment():
        assert load_config(data) == expected
예제 #9
0
def test_multiple_feeds_same_name_invalid():
    with assert_config_error(
            "FeedConfigs must have unique names at state_machines.example.feeds"
    ):
        load_config(yaml_data('multiple_feeds_same_name_invalid'))
예제 #10
0
def test_raises_for_unparseable_database_port_in_environment_variable():
    with mock.patch.dict(os.environ, {'DB_PORT': 'not an int'}):
        with assert_config_error(
                "Could not parse DB_PORT as an integer: 'not an int'."):
            load_config(yaml_data('realistic'))
예제 #11
0
def test_raises_for_nested_kwargs_in_logging_plugin_config():
    with assert_config_error("Could not validate config file against schema."):
        load_config(yaml_data('nested_kwargs_logging_plugin_invalid'))
예제 #12
0
def test_raises_for_invalid_interval_format_in_trigger():
    with assert_config_error("Could not validate config file against schema."):
        load_config(yaml_data('trigger_interval_format_invalid'))
예제 #13
0
def test_raises_for_neither_constant_no_context_next_states():
    with assert_config_error("Could not validate config file against schema."):
        load_config(yaml_data('next_states_not_constant_or_context_invalid'))
예제 #14
0
def test_raises_for_neither_time_nor_context_trigger():
    with assert_config_error("Could not validate config file against schema."):
        load_config(yaml_data('not_time_or_context_invalid'))
예제 #15
0
def test_raises_for_no_state_machines():
    with assert_config_error("Could not validate config file against schema."):
        load_config(yaml_data('no_state_machines_invalid'))
예제 #16
0
def test_raises_for_neither_action_nor_gate_state():
    with assert_config_error("Could not validate config file against schema."):
        load_config(yaml_data('not_action_or_gate_invalid'))