Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

respx_mock doesn't handle // in the path-section of URLs. #273

Open
Skeen opened this issue Aug 2, 2024 · 3 comments
Open

respx_mock doesn't handle // in the path-section of URLs. #273

Skeen opened this issue Aug 2, 2024 · 3 comments

Comments

@Skeen
Copy link

Skeen commented Aug 2, 2024

Hi,

I just stumbled upon the issue described in the title, which can be reproduced with the following pytest file.

import httpx
import pytest
from pydantic import AnyHttpUrl
from respx import MockRouter

@pytest.mark.parametrize(
    "url",
    [
        "http://localhost",  # OK
        "http://localhost/",  # OK
        "http://localhost//",  # Fails
        "http://localhost///",  # Fails
        "http://localhost/%2F",  # Fails
        "http://localhost/%2F/",  # Fails
        "http://localhost/%2F%2F",  # Fails
    ],
)
async def test_respx_targeting(respx_mock: MockRouter, url: AnyHttpUrl) -> None:
    route = respx_mock.get(url=url).respond(status_code=200)
    result = httpx.get(url)
    assert result.status_code == 200
    assert route.called

The fails all take the form of:

respx.models.AllMockedAssertionError: RESPX: <Request('GET', 'http://localhost//')> not mocked!

Having // in the path sections of URLs is valid according to RFC 3986 (see section '3: Syntax Components' and section '3.3. Path'), Stack Overflow seems to concur.

@Skeen Skeen changed the title respx_mock does not seem to handle // in the path-section of URLs. respx_mock doesn't // in the path-section of URLs. Aug 2, 2024
@Skeen Skeen changed the title respx_mock doesn't // in the path-section of URLs. respx_mock doesn't handle // in the path-section of URLs. Aug 2, 2024
@Skeen
Copy link
Author

Skeen commented Aug 2, 2024

I noticed the issue as I am using hypothesis to run property-based tests on my code, in particular using their provisional.urls strategy.

The above parametrized tests can instead be run under hypothesis using the following code:

import httpx
from hypothesis import HealthCheck
from hypothesis import given
from hypothesis import settings
from hypothesis.provisional import urls
from hypothesis.strategies import register_type_strategy
from pydantic import AnyHttpUrl
from respx import MockRouter

register_type_strategy(AnyHttpUrl, urls())


@given(...)
@settings(suppress_health_check=[HealthCheck.function_scoped_fixture])
async def test_respx_targeting(respx_mock: MockRouter, url: AnyHttpUrl) -> None:
    # Reset the function scoped fixture as it is used with hypothesis
    respx_mock.reset()

    route = respx_mock.get(url=url).respond(status_code=200)
    result = httpx.get(url)
    assert result.status_code == 200
    assert route.called

To ensure that the property holds for "all" urls.

@lundberg
Copy link
Owner

lundberg commented Sep 2, 2024

Thanks for reporting @Skeen. I've narrowed down the problem to the Path pattern here.

Both urljoin("/", "///") and httpx.URL("///") turns e.g. /// into a single / path 🤔

@lundberg
Copy link
Owner

lundberg commented Sep 2, 2024

Looked a bit more, and regarding urljoin, we'll need to change to a simple conditional slash prepend.

Regarding httpx.URL("///"), this unfortunately is treated as first two first // as scheme/host delimiter, and a single / path 😅 . Could be fixed by simply adding that, e.g. httpx.URL("scheme://host" + path).path

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants