Пример #1
0
def test_service_get_command_conf_events(story):
    chain = deque(
        [Service('service'),
         Command('cmd'),
         Event('foo'),
         Command('bar')])
    story.app.services = {
        'service': {
            'configuration': {
                'actions': {
                    'cmd': {
                        'events': {
                            'foo': {
                                'output': {
                                    'actions': {
                                        'bar': {
                                            'a': 'b'
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    assert Services.get_command_conf(story, chain) == {'a': 'b'}
Пример #2
0
async def test_services_execute_external_http(patch, story, async_mock):
    line = {
        Line.service: 'cups',
        Line.command: 'print',
        Line.method: 'execute'
    }

    story.app.services = {
        'cups': {
            ServiceConstants.config: {
                'actions': {
                    'print': {
                        'http': {}
                    }
                }
            }
        }
    }

    patch.object(Services, 'execute_http', new=async_mock())
    patch.object(Services, 'start_container', new=async_mock())

    ret = await Services.execute_external(story, line)
    Services.execute_http.mock.assert_called_with(
        story, line,
        deque([Service(name='cups'), Command(name='print')]),
        {'http': {}})
    assert ret == await Services.execute_http()
    Services.start_container.mock.assert_called()
Пример #3
0
async def test_execute_inline(patch, story, command, simulate_finished,
                              bin_content):
    # Not a valid combination.
    if bin_content and command != 'write':
        return

    chain = deque([Service('http'), Event('server'), Command(command)])
    req = MagicMock()
    req._finished = simulate_finished

    def is_finished():
        return req._finished

    req.is_finished = is_finished
    io_loop = MagicMock()
    story.context = {
        ContextConstants.server_request: req,
        ContextConstants.server_io_loop: io_loop
    }

    command_conf = {
        'arguments': {
            'content': {
                'type': 'string',
                'in': 'responseBody',
                'required': True
            }
        }
    }

    if bin_content:
        patch.object(story, 'argument_by_name', return_value=b'bin world!')
    else:
        patch.object(story, 'argument_by_name', return_value='hello world!')

    expected_body = {'command': command, 'data': {'content': 'hello world!'}}

    line = {}

    if simulate_finished:
        with pytest.raises(StoryscriptError):
            await Services.execute_inline(story, line, chain, command_conf)
        return
    else:
        await Services.execute_inline(story, line, chain, command_conf)

    if bin_content:
        req.write.assert_called_with(b'bin world!')
    else:
        req.write.assert_called_with(
            json.dumps(expected_body, cls=HttpDataEncoder) + '\n')

    if command == 'finish' or bin_content:
        io_loop.add_callback.assert_called_with(req.finish)
    else:
        io_loop.add_callback.assert_not_called()
Пример #4
0
def test_service_get_command_conf_simple(story):
    chain = deque([Service('service'), Command('cmd')])
    story.app.services = {
        'service': {
            'configuration': {
                'actions': {
                    'cmd': {'x': 'y'}
                }
            }
        }
    }
    assert Services.get_command_conf(story, chain) == {'x': 'y'}
Пример #5
0
async def test_start_services(patch, app, async_mock, magic, reusable,
                              internal):
    app.stories = {
        'a.story': {
            'tree': {
                '1': {
                    'method': 'execute',
                    'next': '2'
                },
                '2': {
                    'method': 'execute',
                    'next': '3'
                },
                '3': {
                    'method': 'not_execute'
                }
            },
            'entrypoint': '1'
        }
    }
    chain = deque()
    chain.append(Service(name='cold_service'))
    chain.append(Command(name='cold_command'))

    start_container_result = magic()

    patch.object(Services, 'resolve_chain', return_value=chain)
    patch.object(Services, 'is_internal', return_value=internal)
    patch.object(Services,
                 'start_container',
                 return_value=start_container_result)
    patch.object(Containers, 'is_service_reusable', return_value=reusable)
    patch.object(asyncio, 'wait', new=async_mock(return_value=([], [])))

    await app.start_services()

    tasks = [start_container_result]
    if not reusable:
        tasks = [start_container_result, start_container_result]

    if not internal:
        asyncio.wait.mock.assert_called_with(tasks)
Пример #6
0
async def test_start_services_completed_exc(patch, app, async_mock, magic):
    app.stories = {
        'a': {
            'tree': {
                '1': {
                    'method': 'execute'
                }
            },
            'entrypoint': '1'
        }
    }

    patch.object(Containers, 'is_service_reusable', return_value=True)
    patch.object(Services, 'is_internal', return_value=False)
    chain = deque()
    chain.append(Service(name='foo'))
    chain.append(Command(name='foo'))
    patch.object(Services, 'resolve_chain', return_value=chain)
    task = magic()
    task.exception.return_value = Exception()
    patch.object(asyncio, 'wait', new=async_mock(return_value=([task], [])))
    with pytest.raises(Exception):
        await app.start_services()
Пример #7
0
async def test_services_execute_http(patch, story, async_mock, absolute_url,
                                     location, method, service_output):
    if location == 'formBody' and method == 'GET':
        return  # Invalid case.

    chain = deque([Service(name='service'), Command(name='cmd')])
    patch.object(Containers, 'get_hostname',
                 new=async_mock(return_value='container_host'))

    patch.object(uuid, 'uuid4')

    patch.object(ServiceOutputValidator, 'raise_if_invalid')

    command_conf = {
        'http': {
            'method': method.lower(),
            'port': 2771,
            'path': '/invoke'
        },
        'arguments': {
            'foo': {
                'in': location
            }
        }
    }

    if absolute_url:
        command_conf['http']['url'] = 'https://extcoolfunctions.com/invoke'
        del command_conf['http']['port']
        del command_conf['http']['path']

    if service_output is not None:
        command_conf['output'] = service_output

    if location is None:
        del command_conf['arguments']

    if location == 'formBody':
        command_conf['http']['contentType'] = 'multipart/form-data'

    patch.object(story, 'argument_by_name', return_value='bar')

    if location == 'path':
        if absolute_url:
            command_conf['http']['url'] = 'https://extcoolfunctions.com' \
                                          '/invoke/{foo}'
            expected_url = 'https://extcoolfunctions.com/invoke/bar'
        else:
            command_conf['http']['path'] = '/invoke/{foo}'
            expected_url = 'http://container_host:2771/invoke/bar'
    elif location == 'query':
        if absolute_url:
            expected_url = 'https://extcoolfunctions.com/invoke?foo=bar'
        else:
            expected_url = 'http://container_host:2771/invoke?foo=bar'
    else:  # requestBody
        if absolute_url:
            expected_url = 'https://extcoolfunctions.com/invoke'
        else:
            expected_url = 'http://container_host:2771/invoke'

    expected_kwargs = {
        'method': method,
        'headers': {}
    }

    if location == 'header':
        expected_kwargs['headers']['foo'] = 'bar'

    if method == 'POST':
        expected_kwargs['headers']['Content-Type'] = 'application/json; ' \
                                                     'charset=utf-8'

        if location == 'requestBody':
            expected_kwargs['body'] = '{"foo": "bar"}'
        elif location == 'formBody':
            expected_kwargs['headers']['Content-Type'] = \
                f'multipart/form-data; boundary={uuid.uuid4().hex}'
        else:
            expected_kwargs['body'] = '{}'

    line = {
        'ln': '1'
    }

    patch.init(AsyncHTTPClient)
    client = AsyncHTTPClient()
    response = HTTPResponse(HTTPRequest(url=expected_url), 200,
                            buffer=StringIO('{"foo": "\U0001f44d"}'),
                            headers={'Content-Type': 'application/json'})

    patch.object(HttpUtils, 'fetch_with_retry',
                 new=async_mock(return_value=response))

    if location == 'invalid_loc' or \
            (location == 'requestBody' and method == 'GET'):
        with pytest.raises(StoryscriptError):
            await Services.execute_http(story, line, chain, command_conf)
        return
    else:
        ret = await Services.execute_http(story, line, chain, command_conf)

    assert ret == {'foo': '\U0001f44d'}

    if location == 'formBody':
        call = HttpUtils.fetch_with_retry.mock.mock_calls[0][1]
        assert call[0] == 3
        assert call[1] == story.logger
        assert call[2] == expected_url
        assert call[3] == client

        # Since we can't mock the partial, we must inspect it.
        actual_body_producer = call[4].pop('body_producer')
        assert call[4] == expected_kwargs

        assert actual_body_producer.func == Services._multipart_producer
    else:
        HttpUtils.fetch_with_retry.mock.assert_called_with(
            3, story.logger, expected_url, client, expected_kwargs)

    if service_output is not None:
        ServiceOutputValidator.raise_if_invalid.assert_called_with(
            command_conf['output'], ret, chain)
    else:
        ServiceOutputValidator.raise_if_invalid.assert_not_called()

    # Additionally, test for other scenarios.
    response = HTTPResponse(HTTPRequest(url=expected_url), 200,
                            buffer=StringIO('foo'), headers={})

    patch.object(HttpUtils, 'fetch_with_retry',
                 new=async_mock(return_value=response))

    ret = await Services.execute_http(story, line, chain, command_conf)
    assert ret == 'foo'

    response = HTTPResponse(HTTPRequest(url=expected_url), 500)

    patch.object(HttpUtils, 'fetch_with_retry',
                 new=async_mock(return_value=response))

    with pytest.raises(StoryscriptError):
        await Services.execute_http(story, line, chain, command_conf)
Пример #8
0
def test_resolve_chain(story):
    """
    The story tested here is:
    alpine echo as client
        when client foo as echo_helper
            alpine echo
                echo_helper sonar  # This isn't possible, but OK.
            echo_helper sonar

    """
    story.app.services = {
        'alpine': {}
    }

    story.tree = {
        '1': {
            Line.method: 'execute',
            Line.service: 'alpine',
            Line.command: 'echo',
            Line.enter: '2',
            Line.output: ['client']
        },
        '2': {
            Line.method: 'when',
            Line.service: 'client',
            Line.command: 'foo',
            Line.parent: '1',
            Line.output: ['echo_helper']
        },
        '3': {
            Line.method: 'execute',
            Line.service: 'alpine',
            Line.command: 'echo',
            Line.parent: '2',
            Line.enter: '4'
        },
        '4': {
            Line.method: 'execute',
            Line.service: 'echo_helper',
            Line.command: 'sonar',
            Line.parent: '3'
        },
        '5': {
            Line.method: 'execute',
            Line.service: 'echo_helper',
            Line.command: 'sonar',
            Line.parent: '2'
        }
    }

    assert Services.resolve_chain(story, story.tree['1']) \
        == deque([Service(name='alpine'), Command(name='echo')])

    assert Services.resolve_chain(story, story.tree['2']) \
        == deque([Service(name='alpine'),
                  Command(name='echo'), Event(name='foo')])

    assert Services.resolve_chain(story, story.tree['3']) \
        == deque([Service(name='alpine'), Command(name='echo')])

    assert Services.resolve_chain(story, story.tree['4']) \
        == deque([Service(name='alpine'), Command(name='echo'),
                  Event(name='foo'), Command(name='sonar')])

    assert Services.resolve_chain(story, story.tree['5']) \
        == deque([Service(name='alpine'), Command(name='echo'),
                  Event(name='foo'), Command(name='sonar')])