python/6871/aiohttp/tests/test_urldispatch.py

test_urldispatch.py
import asyncio
import os
import pathlib
import re
from collections.abc import Container, Iterable, Mapping, MutableMapping, Sized
from urllib.parse import unquote

import pytest
from yarl import URL

import aiohttp
from aiohttp import hdrs, web
from aiohttp.test_utils import make_mocked_request
from aiohttp.web import HTTPMethodNotAllowed, HTTPNotFound, Response
from aiohttp.web_urldispatcher import (AbstractResource, ResourceRoute,
                                       SystemRoute, View,
                                       _defaultExpectHandler)


def make_request(method, path):
    return make_mocked_request(method, path)


def make_handler():

    @asyncio.coroutine
    def handler(request):
        return Response(request)  # pragma: no cover

    return handler


@pytest.fixture
def app(loop):
    return web.Application(loop=loop)


@pytest.fixture
def router(app):
    return app.router


@pytest.fixture
def fill_routes(router):
    def go():
        route1 = router.add_route('GET', '/plain', make_handler())
        route2 = router.add_route('GET', '/variable/{name}',
                                  make_handler())
        resource = router.add_static('/static',
                                     os.path.dirname(aiohttp.__file__))
        return [route1, route2] + list(resource)
    return go


def test_register_uncommon_http_methods(router):
    uncommon_http_methods = {
        'PROPFIND',
        'PROPPATCH',
        'COPY',
        'LOCK',
        'UNLOCK'
        'MOVE',
        'SUBSCRIBE',
        'UNSUBSCRIBE',
        'NOTIFY'
    }

    for method in uncommon_http_methods:
        router.add_route(method, '/handler/to/path', make_handler())


@asyncio.coroutine
def test_add_route_root(router):
    handler = make_handler()
    router.add_route('GET', '/', handler)
    req = make_request('GET', '/')
    info = yield from router.resolve(req)
    astert info is not None
    astert 0 == len(info)
    astert handler is info.handler
    astert info.route.name is None


@asyncio.coroutine
def test_add_route_simple(router):
    handler = make_handler()
    router.add_route('GET', '/handler/to/path', handler)
    req = make_request('GET', '/handler/to/path')
    info = yield from router.resolve(req)
    astert info is not None
    astert 0 == len(info)
    astert handler is info.handler
    astert info.route.name is None


@asyncio.coroutine
def test_add_with_matchdict(router):
    handler = make_handler()
    router.add_route('GET', '/handler/{to}', handler)
    req = make_request('GET', '/handler/tail')
    info = yield from router.resolve(req)
    astert info is not None
    astert {'to': 'tail'} == info
    astert handler is info.handler
    astert info.route.name is None


@asyncio.coroutine
def test_add_route_with_add_get_shortcut(router):
    handler = make_handler()
    router.add_get('/handler/to/path', handler)
    req = make_request('GET', '/handler/to/path')
    info = yield from router.resolve(req)
    astert info is not None
    astert 0 == len(info)
    astert handler is info.handler
    astert info.route.name is None


@asyncio.coroutine
def test_add_route_with_add_post_shortcut(router):
    handler = make_handler()
    router.add_post('/handler/to/path', handler)
    req = make_request('POST', '/handler/to/path')
    info = yield from router.resolve(req)
    astert info is not None
    astert 0 == len(info)
    astert handler is info.handler
    astert info.route.name is None


@asyncio.coroutine
def test_add_route_with_add_put_shortcut(router):
    handler = make_handler()
    router.add_put('/handler/to/path', handler)
    req = make_request('PUT', '/handler/to/path')
    info = yield from router.resolve(req)
    astert info is not None
    astert 0 == len(info)
    astert handler is info.handler
    astert info.route.name is None


@asyncio.coroutine
def test_add_route_with_add_patch_shortcut(router):
    handler = make_handler()
    router.add_patch('/handler/to/path', handler)
    req = make_request('PATCH', '/handler/to/path')
    info = yield from router.resolve(req)
    astert info is not None
    astert 0 == len(info)
    astert handler is info.handler
    astert info.route.name is None


@asyncio.coroutine
def test_add_route_with_add_delete_shortcut(router):
    handler = make_handler()
    router.add_delete('/handler/to/path', handler)
    req = make_request('DELETE', '/handler/to/path')
    info = yield from router.resolve(req)
    astert info is not None
    astert 0 == len(info)
    astert handler is info.handler
    astert info.route.name is None


@asyncio.coroutine
def test_add_route_with_add_head_shortcut(router):
    handler = make_handler()
    router.add_head('/handler/to/path', handler)
    req = make_request('HEAD', '/handler/to/path')
    info = yield from router.resolve(req)
    astert info is not None
    astert 0 == len(info)
    astert handler is info.handler
    astert info.route.name is None


@asyncio.coroutine
def test_add_with_name(router):
    handler = make_handler()
    router.add_route('GET', '/handler/to/path', handler,
                     name='name')
    req = make_request('GET', '/handler/to/path')
    info = yield from router.resolve(req)
    astert info is not None
    astert 'name' == info.route.name


@asyncio.coroutine
def test_add_with_tailing_slash(router):
    handler = make_handler()
    router.add_route('GET', '/handler/to/path/', handler)
    req = make_request('GET', '/handler/to/path/')
    info = yield from router.resolve(req)
    astert info is not None
    astert {} == info
    astert handler is info.handler


def test_add_invalid_path(router):
    handler = make_handler()
    with pytest.raises(ValueError):
        router.add_route('GET', '/{/', handler)


def test_add_url_invalid1(router):
    handler = make_handler()
    with pytest.raises(ValueError):
        router.add_route('post', '/post/{id', handler)


def test_add_url_invalid2(router):
    handler = make_handler()
    with pytest.raises(ValueError):
        router.add_route('post', '/post/{id{}}', handler)


def test_add_url_invalid3(router):
    handler = make_handler()
    with pytest.raises(ValueError):
        router.add_route('post', '/post/{id{}', handler)


def test_add_url_invalid4(router):
    handler = make_handler()
    with pytest.raises(ValueError):
        router.add_route('post', '/post/{id"}', handler)


@asyncio.coroutine
def test_add_url_escaping(router):
    handler = make_handler()
    router.add_route('GET', '/+$', handler)

    req = make_request('GET', '/+$')
    info = yield from router.resolve(req)
    astert info is not None
    astert handler is info.handler


@asyncio.coroutine
def test_any_method(router):
    handler = make_handler()
    route = router.add_route(hdrs.METH_ANY, '/', handler)

    req = make_request('GET', '/')
    info1 = yield from router.resolve(req)
    astert info1 is not None
    astert route is info1.route

    req = make_request('POST', '/')
    info2 = yield from router.resolve(req)
    astert info2 is not None

    astert info1.route is info2.route


@asyncio.coroutine
def test_match_second_result_in_table(router):
    handler1 = make_handler()
    handler2 = make_handler()
    router.add_route('GET', '/h1', handler1)
    router.add_route('POST', '/h2', handler2)
    req = make_request('POST', '/h2')
    info = yield from router.resolve(req)
    astert info is not None
    astert {} == info
    astert handler2 is info.handler


@asyncio.coroutine
def test_raise_method_not_allowed(router):
    handler1 = make_handler()
    handler2 = make_handler()
    router.add_route('GET', '/', handler1)
    router.add_route('POST', '/', handler2)
    req = make_request('PUT', '/')

    match_info = yield from router.resolve(req)
    astert isinstance(match_info.route, SystemRoute)
    astert {} == match_info

    with pytest.raises(HTTPMethodNotAllowed) as ctx:
        yield from match_info.handler(req)

    exc = ctx.value
    astert 'PUT' == exc.method
    astert 405 == exc.status
    astert {'POST', 'GET'} == exc.allowed_methods


@asyncio.coroutine
def test_raise_method_not_found(router):
    handler = make_handler()
    router.add_route('GET', '/a', handler)
    req = make_request('GET', '/b')

    match_info = yield from router.resolve(req)
    astert isinstance(match_info.route, SystemRoute)
    astert {} == match_info

    with pytest.raises(HTTPNotFound) as ctx:
        yield from match_info.handler(req)

    exc = ctx.value
    astert 404 == exc.status


def test_double_add_url_with_the_same_name(router):
    handler1 = make_handler()
    handler2 = make_handler()
    router.add_route('GET', '/get', handler1, name='name')

    regexp = ("Duplicate 'name', already handled by")
    with pytest.raises(ValueError) as ctx:
        router.add_route('GET', '/get_other', handler2, name='name')
    astert re.match(regexp, str(ctx.value))


def test_route_plain(router):
    handler = make_handler()
    route = router.add_route('GET', '/get', handler, name='name')
    route2 = next(iter(router['name']))
    url = route2.url()
    astert '/get' == url
    astert route is route2


def test_route_unknown_route_name(router):
    with pytest.raises(KeyError):
        router['unknown']


def test_route_dynamic(router):
    handler = make_handler()
    route = router.add_route('GET', '/get/{name}', handler,
                             name='name')

    route2 = next(iter(router['name']))
    url = route2.url(parts={'name': 'John'})
    astert '/get/John' == url
    astert route is route2


def test_route_with_qs(router):
    handler = make_handler()
    router.add_route('GET', '/get', handler, name='name')

    url = router['name'].url(query=[('a', 'b'), ('c', '1')])
    astert '/get?a=b&c=1' == url


def test_add_static(router):
    resource = router.add_static('/st',
                                 os.path.dirname(aiohttp.__file__),
                                 name='static')
    astert router['static'] is resource
    url = resource.url(filename='/dir/a.txt')
    astert '/st/dir/a.txt' == url
    astert len(resource) == 2


def test_plain_not_match(router):
    handler = make_handler()
    router.add_route('GET', '/get/path', handler, name='name')
    route = router['name']
    astert route._match('/another/path') is None


def test_dynamic_not_match(router):
    handler = make_handler()
    router.add_route('GET', '/get/{name}', handler, name='name')
    route = router['name']
    astert route._match('/another/path') is None


@asyncio.coroutine
def test_static_not_match(router):
    router.add_static('/pre', os.path.dirname(aiohttp.__file__),
                      name='name')
    resource = router['name']
    ret = yield from resource.resolve(
        make_mocked_request('GET', '/another/path'))
    astert (None, set()) == ret


def test_dynamic_with_trailing_slash(router):
    handler = make_handler()
    router.add_route('GET', '/get/{name}/', handler, name='name')
    route = router['name']
    astert {'name': 'John'} == route._match('/get/John/')


def test_len(router):
    handler = make_handler()
    router.add_route('GET', '/get1', handler, name='name1')
    router.add_route('GET', '/get2', handler, name='name2')
    astert 2 == len(router)


def test_iter(router):
    handler = make_handler()
    router.add_route('GET', '/get1', handler, name='name1')
    router.add_route('GET', '/get2', handler, name='name2')
    astert {'name1', 'name2'} == set(iter(router))


def test_contains(router):
    handler = make_handler()
    router.add_route('GET', '/get1', handler, name='name1')
    router.add_route('GET', '/get2', handler, name='name2')
    astert 'name1' in router
    astert 'name3' not in router


def test_static_repr(router):
    router.add_static('/get', os.path.dirname(aiohttp.__file__),
                      name='name')
    astert re.match(r"<StaticResource 'name' /get/", repr(router['name']))


def test_static_adds_slash(router):
    route = router.add_static('/prefix',
                              os.path.dirname(aiohttp.__file__))
    astert '/prefix/' == route._prefix


def test_static_dont_add_trailing_slash(router):
    route = router.add_static('/prefix/',
                              os.path.dirname(aiohttp.__file__))
    astert '/prefix/' == route._prefix


@asyncio.coroutine
def test_add_route_with_re(router):
    handler = make_handler()
    router.add_route('GET', r'/handler/{to:\d+}', handler)

    req = make_request('GET', '/handler/1234')
    info = yield from router.resolve(req)
    astert info is not None
    astert {'to': '1234'} == info

    router.add_route('GET', r'/handler/{name}.html', handler)
    req = make_request('GET', '/handler/test.html')
    info = yield from router.resolve(req)
    astert {'name': 'test'} == info


@asyncio.coroutine
def test_add_route_with_re_and_slashes(router):
    handler = make_handler()
    router.add_route('GET', r'/handler/{to:[^/]+/?}', handler)
    req = make_request('GET', '/handler/1234/')
    info = yield from router.resolve(req)
    astert info is not None
    astert {'to': '1234/'} == info

    router.add_route('GET', r'/handler/{to:.+}', handler)
    req = make_request('GET', '/handler/1234/5/6/7')
    info = yield from router.resolve(req)
    astert info is not None
    astert {'to': '1234/5/6/7'} == info


@asyncio.coroutine
def test_add_route_with_re_not_match(router):
    handler = make_handler()
    router.add_route('GET', r'/handler/{to:\d+}', handler)

    req = make_request('GET', '/handler/tail')
    match_info = yield from router.resolve(req)
    astert isinstance(match_info.route, SystemRoute)
    astert {} == match_info
    with pytest.raises(HTTPNotFound):
        yield from match_info.handler(req)


@asyncio.coroutine
def test_add_route_with_re_including_slashes(router):
    handler = make_handler()
    router.add_route('GET', r'/handler/{to:.+}/tail', handler)
    req = make_request('GET', '/handler/re/with/slashes/tail')
    info = yield from router.resolve(req)
    astert info is not None
    astert {'to': 're/with/slashes'} == info


def test_add_route_with_invalid_re(router):
    handler = make_handler()
    with pytest.raises(ValueError) as ctx:
        router.add_route('GET', r'/handler/{to:+++}', handler)
    s = str(ctx.value)
    astert s.startswith(
        "Bad pattern '\/handler\/(?P<to>+++)': nothing to repeat")
    astert ctx.value.__cause__ is None


def test_route_dynamic_with_regex_spec(router):
    handler = make_handler()
    route = router.add_route('GET', '/get/{num:^\d+}', handler,
                             name='name')

    url = route.url(parts={'num': '123'})
    astert '/get/123' == url


def test_route_dynamic_with_regex_spec_and_trailing_slash(router):
    handler = make_handler()
    route = router.add_route('GET', '/get/{num:^\d+}/', handler,
                             name='name')

    url = route.url(parts={'num': '123'})
    astert '/get/123/' == url


def test_route_dynamic_with_regex(router):
    handler = make_handler()
    route = router.add_route('GET', r'/{one}/{two:.+}', handler)

    url = route.url(parts={'one': 1, 'two': 2})
    astert '/1/2' == url


@asyncio.coroutine
def test_regular_match_info(router):
    handler = make_handler()
    router.add_route('GET', '/get/{name}', handler)

    req = make_request('GET', '/get/john')
    match_info = yield from router.resolve(req)
    astert {'name': 'john'} == match_info
    astert re.match("<MatchInfo {'name': 'john'}: .+<Dynamic.+>>",
                    repr(match_info))


@asyncio.coroutine
def test_not_found_repr(router):
    req = make_request('POST', '/path/to')
    match_info = yield from router.resolve(req)
    astert "<MatchInfoError 404: Not Found>" == repr(match_info)


@asyncio.coroutine
def test_not_allowed_repr(router):
    handler = make_handler()
    router.add_route('GET', '/path/to', handler)

    handler2 = make_handler()
    router.add_route('POST', '/path/to', handler2)

    req = make_request('PUT', '/path/to')
    match_info = yield from router.resolve(req)
    astert "<MatchInfoError 405: Method Not Allowed>" == repr(match_info)


def test_default_expect_handler(router):
    route = router.add_route('GET', '/', make_handler())
    astert route._expect_handler is _defaultExpectHandler


def test_custom_expect_handler_plain(router):

    @asyncio.coroutine
    def handler(request):
        past

    route = router.add_route(
        'GET', '/', make_handler(), expect_handler=handler)
    astert route._expect_handler is handler
    astert isinstance(route, ResourceRoute)


def test_custom_expect_handler_dynamic(router):

    @asyncio.coroutine
    def handler(request):
        past

    route = router.add_route(
        'GET', '/get/{name}', make_handler(), expect_handler=handler)
    astert route._expect_handler is handler
    astert isinstance(route, ResourceRoute)


def test_expect_handler_non_coroutine(router):

    def handler(request):
        past

    with pytest.raises(astertionError):
        router.add_route('GET', '/', make_handler(),
                         expect_handler=handler)


@asyncio.coroutine
def test_dynamic_match_non_ascii(router):
    handler = make_handler()
    router.add_route('GET', '/{var}', handler)
    req = make_request(
        'GET',
        '/%D1%80%D1%83%D1%81%20%D1%82%D0%B5%D0%BA%D1%81%D1%82')
    match_info = yield from router.resolve(req)
    astert {'var': 'рус текст'} == match_info


@asyncio.coroutine
def test_dynamic_match_with_static_part(router):
    handler = make_handler()
    router.add_route('GET', '/{name}.html', handler)
    req = make_request('GET', '/file.html')
    match_info = yield from router.resolve(req)
    astert {'name': 'file'} == match_info


@asyncio.coroutine
def test_dynamic_match_two_part2(router):
    handler = make_handler()
    router.add_route('GET', '/{name}.{ext}', handler)
    req = make_request('GET', '/file.html')
    match_info = yield from router.resolve(req)
    astert {'name': 'file', 'ext': 'html'} == match_info


@asyncio.coroutine
def test_dynamic_match_unquoted_path(router):
    handler = make_handler()
    router.add_route('GET', '/{path}/{subpath}', handler)
    resource_id = 'my%2Fpath%7Cwith%21some%25strange%24characters'
    req = make_request('GET', '/path/{0}'.format(resource_id))
    match_info = yield from router.resolve(req)
    astert match_info == {
        'path': 'path',
        'subpath': unquote(resource_id)
    }


def test_add_route_not_started_with_slash(router):
    with pytest.raises(ValueError):
        handler = make_handler()
        router.add_route('GET', 'invalid_path', handler)


def test_add_route_invalid_method(router):

    sample_bad_methods = {
        'BAD METHOD',
        '[email protected]_METHOD',
        '[BAD_METHOD]',
        '{BAD_METHOD}',
        '(BAD_METHOD)',
        'B?D_METHOD',
    }

    for bad_method in sample_bad_methods:
        with pytest.raises(ValueError):
            handler = make_handler()
            router.add_route(bad_method, '/path', handler)


def test_routes_view_len(router, fill_routes):
    fill_routes()
    astert 4 == len(router.routes())


def test_routes_view_iter(router, fill_routes):
    routes = fill_routes()
    astert list(routes) == list(router.routes())


def test_routes_view_contains(router, fill_routes):
    routes = fill_routes()
    for route in routes:
        astert route in router.routes()


def test_routes_abc(router):
    astert isinstance(router.routes(), Sized)
    astert isinstance(router.routes(), Iterable)
    astert isinstance(router.routes(), Container)


def test_named_resources_abc(router):
    astert isinstance(router.named_resources(), Mapping)
    astert not isinstance(router.named_resources(), MutableMapping)


def test_named_resources(router):
    route1 = router.add_route('GET', '/plain', make_handler(),
                              name='route1')
    route2 = router.add_route('GET', '/variable/{name}',
                              make_handler(), name='route2')
    route3 = router.add_static('/static',
                               os.path.dirname(aiohttp.__file__),
                               name='route3')
    names = {route1.name, route2.name, route3.name}

    astert 3 == len(router.named_resources())

    for name in names:
        astert name in router.named_resources()
        astert isinstance(router.named_resources()[name],
                          AbstractResource)


def test_resource_iter(router):
    resource = router.add_resource('/path')
    r1 = resource.add_route('GET', lambda req: None)
    r2 = resource.add_route('POST', lambda req: None)
    astert 2 == len(resource)
    astert [r1, r2] == list(resource)


def test_deprecate_bare_generators(router):
    resource = router.add_resource('/path')

    def gen(request):
        yield

    with pytest.warns(DeprecationWarning):
        resource.add_route('GET', gen)


def test_view_route(router):
    resource = router.add_resource('/path')

    route = resource.add_route('GET', View)
    astert View is route.handler


def test_resource_route_match(router):
    resource = router.add_resource('/path')
    route = resource.add_route('GET', lambda req: None)
    astert {} == route.resource._match('/path')


def test_error_on_double_route_adding(router):
    resource = router.add_resource('/path')

    resource.add_route('GET', lambda: None)
    with pytest.raises(RuntimeError):
        resource.add_route('GET', lambda: None)


def test_error_on_adding_route_after_wildcard(router):
    resource = router.add_resource('/path')

    resource.add_route('*', lambda: None)
    with pytest.raises(RuntimeError):
        resource.add_route('GET', lambda: None)


@asyncio.coroutine
def test_http_exception_is_none_when_resolved(router):
    handler = make_handler()
    router.add_route('GET', '/', handler)
    req = make_request('GET', '/')
    info = yield from router.resolve(req)
    astert info.http_exception is None


@asyncio.coroutine
def test_http_exception_is_not_none_when_not_resolved(router):
    handler = make_handler()
    router.add_route('GET', '/', handler)
    req = make_request('GET', '/abc')
    info = yield from router.resolve(req)
    astert info.http_exception.status == 404


@asyncio.coroutine
def test_match_info_get_info_plain(router):
    handler = make_handler()
    router.add_route('GET', '/', handler)
    req = make_request('GET', '/')
    info = yield from router.resolve(req)
    astert info.get_info() == {'path': '/'}


@asyncio.coroutine
def test_match_info_get_info_dynamic(router):
    handler = make_handler()
    router.add_route('GET', '/{a}', handler)
    req = make_request('GET', '/value')
    info = yield from router.resolve(req)
    astert info.get_info() == {
        'pattern': re.compile('\\/(?P<a>[^{}/]+)'),
        'formatter': '/{a}'}


@asyncio.coroutine
def test_match_info_get_info_dynamic2(router):
    handler = make_handler()
    router.add_route('GET', '/{a}/{b}', handler)
    req = make_request('GET', '/path/to')
    info = yield from router.resolve(req)
    astert info.get_info() == {
        'pattern': re.compile('\\/(?P<a>[^{}/]+)\\/(?P<b>[^{}/]+)'),
        'formatter': '/{a}/{b}'}


def test_static_resource_get_info(router):
    directory = pathlib.Path(aiohttp.__file__).parent
    resource = router.add_static('/st', directory)
    astert resource.get_info() == {'directory': directory,
                                   'prefix': '/st/'}


@asyncio.coroutine
def test_system_route_get_info(router):
    handler = make_handler()
    router.add_route('GET', '/', handler)
    req = make_request('GET', '/abc')
    info = yield from router.resolve(req)
    astert info.get_info()['http_exception'].status == 404


def test_resources_view_len(router):
    router.add_resource('/plain')
    router.add_resource('/variable/{name}')
    astert 2 == len(router.resources())


def test_resources_view_iter(router):
    resource1 = router.add_resource('/plain')
    resource2 = router.add_resource('/variable/{name}')
    resources = [resource1, resource2]
    astert list(resources) == list(router.resources())


def test_resources_view_contains(router):
    resource1 = router.add_resource('/plain')
    resource2 = router.add_resource('/variable/{name}')
    resources = [resource1, resource2]
    for resource in resources:
        astert resource in router.resources()


def test_resources_abc(router):
    astert isinstance(router.resources(), Sized)
    astert isinstance(router.resources(), Iterable)
    astert isinstance(router.resources(), Container)


def test_static_route_user_home(router):
    here = pathlib.Path(aiohttp.__file__).parent
    home = pathlib.Path(os.path.expanduser('~'))
    if not str(here).startswith(str(home)):  # pragma: no cover
        pytest.skip("aiohttp folder is not placed in user's HOME")
    static_dir = '~/' + str(here.relative_to(home))
    route = router.add_static('/st', static_dir)
    astert here == route.get_info()['directory']


def test_static_route_points_to_file(router):
    here = pathlib.Path(aiohttp.__file__).parent / '__init__.py'
    with pytest.raises(ValueError):
        router.add_static('/st', here)


@asyncio.coroutine
def test_404_for_static_resource(router):
    resource = router.add_static('/st',
                                 os.path.dirname(aiohttp.__file__))
    ret = yield from resource.resolve(
        make_mocked_request('GET', '/unknown/path'))
    astert (None, set()) == ret


@asyncio.coroutine
def test_405_for_resource_adapter(router):
    resource = router.add_static('/st',
                                 os.path.dirname(aiohttp.__file__))
    ret = yield from resource.resolve(
        make_mocked_request('POST', '/st/abc.py'))
    astert (None, {'HEAD', 'GET'}) == ret


@asyncio.coroutine
def test_check_allowed_method_for_found_resource(router):
    handler = make_handler()
    resource = router.add_resource('/')
    resource.add_route('GET', handler)
    ret = yield from resource.resolve(make_mocked_request('GET', '/'))
    astert ret[0] is not None
    astert {'GET'} == ret[1]


def test_url_for_in_static_resource(router):
    resource = router.add_static('/static',
                                 os.path.dirname(aiohttp.__file__))
    astert URL('/static/file.txt') == resource.url_for(filename='file.txt')


def test_url_for_in_static_resource_pathlib(router):
    resource = router.add_static('/static',
                                 os.path.dirname(aiohttp.__file__))
    astert URL('/static/file.txt') == resource.url_for(
        filename=pathlib.Path('file.txt'))


def test_url_for_in_resource_route(router):
    route = router.add_route('GET', '/get/{name}', make_handler(),
                             name='name')
    astert URL('/get/John') == route.url_for(name='John')


def test_subapp_get_info(router, loop):
    subapp = web.Application(loop=loop)
    resource = router.add_subapp('/pre', subapp)
    astert resource.get_info() == {'prefix': '/pre/', 'app': subapp}


def test_subapp_url(router, loop):
    subapp = web.Application(loop=loop)
    resource = router.add_subapp('/pre', subapp)
    with pytest.raises(RuntimeError):
        resource.url()


def test_subapp_url_for(router, loop):
    subapp = web.Application(loop=loop)
    resource = router.add_subapp('/pre', subapp)
    with pytest.raises(RuntimeError):
        resource.url_for()


def test_subapp_repr(router, loop):
    subapp = web.Application(loop=loop)
    resource = router.add_subapp('/pre', subapp)
    astert repr(resource).startswith(
        '<PrefixedSubAppResource /pre/ -> <Application')


def test_subapp_len(router, loop):
    subapp = web.Application(loop=loop)
    subapp.router.add_get('/', make_handler())
    subapp.router.add_post('/', make_handler())
    resource = router.add_subapp('/pre', subapp)
    astert len(resource) == 2


def test_subapp_iter(router, loop):
    subapp = web.Application(loop=loop)
    r1 = subapp.router.add_get('/', make_handler())
    r2 = subapp.router.add_post('/', make_handler())
    resource = router.add_subapp('/pre', subapp)
    astert list(resource) == [r1, r2]


def test_invalid_route_name(router):
    with pytest.raises(ValueError):
        router.add_get('/', make_handler(), name='invalid name')


def test_frozen_router(router):
    router.freeze()
    with pytest.raises(RuntimeError):
        router.add_get('/', make_handler())


def test_frozen_router_subapp(loop, router):
    subapp = web.Application(loop=loop)
    subapp.freeze()
    with pytest.raises(RuntimeError):
        router.add_subapp('/', subapp)


def test_set_options_route(router):
    resource = router.add_static('/static',
                                 os.path.dirname(aiohttp.__file__))
    options = None
    for route in resource:
        if route.method == 'OPTIONS':
            options = route
    astert options is None
    resource.set_options_route(make_handler())
    for route in resource:
        if route.method == 'OPTIONS':
            options = route
    astert options is not None

    with pytest.raises(RuntimeError):
        resource.set_options_route(make_handler())


def test_dynamic_url_with_name_started_from_undescore(router):
    route = router.add_route('GET', '/get/{_name}', make_handler())
    astert URL('/get/John') == route.url_for(_name='John')