import pytest
from simplehttprouter import SimpleHttpRouter, Route


class TestSimpleHttpRouter(object):
    def test_MethodAndPathDefinesARoute(self):
        assert not ("GET", "asdf") == Route("GET", "/asdf")
        assert Route("GET", "/asdf") == Route("GET", "/asdf")
        assert not Route("GET", "/asdf") == Route("POST", "/asdf")
        assert not Route("GET", "/asdf") == Route("GET", "/asdf2")
        assert Route("GET", "/asdf") != Route("POST", "/asdf")
        assert Route("GET", "/asdf") != Route("GET", "/asdf2")

    def test_ReprIsImplementedInTheRoute(self):
        assert "Route(\"GET\", \"/asdf\")" == Route("GET", "/asdf").__repr__()

    def test_StrIsImplementedInTheRoute(self):
        assert "method: GET, path: /asdf" == Route("GET", "/asdf").__str__()

    def test_whenNoRoutesArePassed_NoneIsReturned(self):
        router = SimpleHttpRouter([])
        assert not router.get_route(Route("GET", "/"))

    def test_TheUrlIsNotPartilayMatched(self):
        router = SimpleHttpRouter([Route("GET", "/")])
        assert not router.get_route(Route("GET", "/asdf"))

    def test_whenUrlMatchRoute_HasRouteReturnTheOriginalRoute(self):
        router = SimpleHttpRouter([Route("GET", "/")])
        assert Route("GET", "/") == router.get_route(Route("GET", "/")).route

    def test_theHashOfTheReturnedObjectIsEqualToTheHashOfThePassedRoute(self):
        router = SimpleHttpRouter([Route("GET", "/")])
        assert Route("GET", "/").__hash__() == router.get_route(Route("GET", "/")).route.__hash__()

    @pytest.mark.parametrize(
        "url_pattern, url",
        [
            ("/{id}", "/type/asdf"),
            ("/type/{id}", "/type/asdf"),
            ("/{id}/type", "asdf/type"),
            ("/type/{id}/plan", "/type/asdf/plan"),
            ("/type/{id}/{id2}", "/type/asdf/plan")
        ])
    def test_whenPathHasAParameterTheRouteIsFound(self, url_pattern, url):
        router = SimpleHttpRouter([Route("GET", url_pattern)])

        assert router.get_route(Route("GET", url))

    @pytest.mark.parametrize(
        "url_pattern, url",
        [
            ("/type/{id}/data{id2}", "/type/asdf/dataplan")
        ])
    def test_PathParametersMustBeSeparatedBySlashs(self, url_pattern, url):
        router = SimpleHttpRouter([Route("GET", url_pattern)])

        assert router.get_route(Route("GET", url)) is None

    def test_twoParametersAreNotGroupedAsASingleParameter(self):
        router = SimpleHttpRouter([Route("GET", "/type/{id}/{id2}/asdf")])
        assert not router.get_route(Route("GET", "/type/plan/asdf"))

    def test_theHttpMethodIsUsedToCheckTheRoute(self):
        router = SimpleHttpRouter([Route("GET", "/type/asdf")])
        assert not router.get_route(Route("POST", "/type/asdf"))
        assert router.get_route(Route("GET", "/type/asdf"))

    @pytest.mark.parametrize(
        "url_pattern, url, expected_path_parameters",
        [
            ("/{id}", "/type/asdf", {"id": "type/asdf"}),
            ("/type/{id}", "/type/asdf", {"id": "asdf"}),
            ("/{id}/type", "/asdf/type", {"id": "asdf"}),
            ("/type/{id}/plan", "/type/asdf/plan", {"id": "asdf"}),
            ("/type/{id}/{id2}", "/type/asdf/plan", {"id": "asdf", "id2": "plan"}),
            ("/type/{id}/data/{id2}", "/type/asdf/data/plan", {"id": "asdf", "id2": "plan"}),
            ("/type/data", "/type/data", {})
        ])
    def test_pathParametersAreCorrecltyReturned(self, url_pattern, url, expected_path_parameters):
        router = SimpleHttpRouter([Route("GET", url_pattern)])
        found_route = router.get_route(Route("GET", url))

        assert expected_path_parameters == found_route.path_parameters

    def test_completeRoutesHavePreference(self):
        router = SimpleHttpRouter([
            Route("GET", "/option"),
            Route("GET", "/option/{id}"),
        ])

        assert router.get_route(Route("GET", "/option")).route == Route("GET", "/option")
        assert router.get_route(Route("GET", "/option/")).route == Route("GET", "/option/{id}")

    def test_fixedRouteHasPriorityOverVariableRoute(self):
        router = SimpleHttpRouter([
            Route("GET", "/option"),
            Route("GET", "/option/{id}"),
            Route("GET", "/option/composition")
        ])

        assert router.get_route(Route("GET", "/option/composition")).route == Route("GET", "/option/composition")
        assert router.get_route(Route("GET", "/option/composition2")).route == Route("GET", "/option/{id}")

    def test_multipleFinalRoutesAreSupported(self):
        router = SimpleHttpRouter([
            Route("GET", "/option"),
            Route("GET", "/product"),
            Route("GET", "/combo")
        ])

        assert router.get_route(Route("GET", "/option")).route == Route("GET", "/option")
        assert router.get_route(Route("GET", "/product")).route == Route("GET", "/product")
        assert router.get_route(Route("GET", "/combo")).route == Route("GET", "/combo")

    def test_theLongestRouteIsReturned(self):
        router = SimpleHttpRouter([
            Route("GET", "/option"),
            Route("GET", "/option/{id}/composition/{compId}"),
            Route("GET", "/option/{id}")
        ])

        received = router.get_route(Route("GET", "/option/2/composition/3")).route
        assert received == Route("GET", "/option/{id}/composition/{compId}")

    def test_theLongestRouteIsReturnedEvenWhenThereIsAFixedRoute(self):
        router = SimpleHttpRouter([
            Route("GET", "/option/{id}"),
            Route("GET", "/{type}/{id}/priceList"),
        ])

        received = router.get_route(Route("GET", "/option/2/priceList")).route
        assert received == Route("GET", "/{type}/{id}/priceList")

        received = router.get_route(Route("GET", "/option/2")).route
        assert received == Route("GET", "/option/{id}")
