Example #1
0
async def test_external_pass_through(client):  # pragma: nocover
    with respx.mock:
        # Mock pass-through call
        url = "https://httpbin.org/post"
        route = respx.post(url).respond(content=b"").pass_through()

        # Mock a non-matching callback pattern pre-reading request data
        def callback(req):
            req.read()  # TODO: Make this not needed, might affect pass-through
            assert req.content == b'{"foo": "bar"}'
            return None

        respx.add(callback)

        # Make external pass-through call
        assert route.call_count == 0
        response = await client.post(url, json={"foo": "bar"})

        assert response.content is not None
        assert len(response.content) > 0
        assert "Content-Length" in response.headers
        assert int(response.headers["Content-Length"]) > 0
        assert response.json()["json"] == {"foo": "bar"}

        assert respx.calls.last.request.url == url
        assert respx.calls.last.response is None

        # TODO: Routed and recorded twice; AsyncConnectionPool + AsyncHTTPConnection
        assert route.call_count == 2
        assert respx.calls.call_count == 2
Example #2
0
async def test_external_pass_through(client):  # pragma: nocover
    with respx.mock:
        # Mock pass-through call
        url = "https://httpbin.org/post"
        respx.get(url, content=b"", pass_through=True)

        # Mock a non-matching callback pattern pre-reading request data
        def callback(req, res):
            req.read()  # TODO: Make this not needed, might affect pass-through
            assert req.content == b'{"foo": "bar"}'
            return None

        respx.add(callback)

        # Make external pass-through call
        response = await client.post(url, json={"foo": "bar"})

        assert response.content is not None
        assert len(response.content) > 0
        assert "Content-Length" in response.headers
        assert int(response.headers["Content-Length"]) > 0
        assert response.json()["json"] == {"foo": "bar"}

        _, resp = respx.calls[-1]
        await resp.aread()  # Read async pass-through response
        assert resp.content == b"", "Should be 0, stream already read by real Response!"
        assert "Content-Length" in resp.headers
        assert int(resp.headers["Content-Length"]) > 0
Example #3
0
async def test_start_stop(client):
    url = "https://foo.bar/"
    request = respx.add("GET", url, status_code=202)
    assert respx.stats.call_count == 0

    try:
        respx.start()
        response = await client.get(url)
        assert request.called is True
        assert response.status_code == 202
        assert response.text == ""
        assert respx.stats.call_count == 1

        respx.stop(clear=False, reset=False)
        assert len(respx.mock.patterns) == 1
        assert respx.stats.call_count == 1
        assert request.called is True

        respx.reset()
        assert len(respx.mock.patterns) == 1
        assert respx.stats.call_count == 0
        assert request.called is False

        respx.clear()
        assert len(respx.mock.patterns) == 0

    except Exception:  # pragma: nocover
        respx.stop()  # Cleanup global state on error, to not affect other tests
        raise
Example #4
0
def test_add():
    with respx.mock:
        route = Route(method="GET", url="https://foo.bar/")
        respx.add(route, name="foobar")

        response = httpx.get("https://foo.bar/")
        assert response.status_code == 200
        assert respx.routes["foobar"].called

        with pytest.raises(TypeError):
            respx.add(route, status_code=418)  # pragma: nocover

        with pytest.raises(ValueError):
            respx.add("GET")  # pragma: nocover

        with pytest.raises(NotImplementedError):
            route.name = "spam"

        with pytest.raises(NotImplementedError):
            route.pattern &= M(params={"foo": "bar"})
Example #5
0
def test_restore(rt, app, client, mstorage):
    # pylint: disable=too-many-statements
    # Create fake backup (not pretty but sufficient?)
    storage = mstorage.get_storage(rt.storage_name)
    storage.upload_json(BACKUP_NAME, BACKUP_MANIFEST)
    nodes = app.state.coordinator_config.nodes
    with respx.mock:
        for i, node in enumerate(nodes):
            respx.post(f"{node.url}/unlock?locker=x&ttl=0",
                       content={"locked": False})
            # Failure point 1: Lock fails
            respx.post(f"{node.url}/lock?locker=x&ttl=60",
                       content={"locked": rt.fail_at != 1})

            if i == 0:
                # Failure point 2: download call fails
                url = f"{node.url}/download"

                def match_download(request, response, *, _url=url):
                    if request.method != "POST" or _url != str(request.url):
                        return None
                    if rt.fail_at == 2:
                        return None
                    if json.loads(
                            request.read())["storage"] != storage.storage_name:
                        return None
                    if json.loads(request.read())["root_globs"] != ["*"]:
                        return None
                    return response

                result_url = f"{node.url}/download/result"

                respx.add(match_download,
                          content={
                              "op_id": 42,
                              "status_url": result_url
                          })

                # Failure point 3: download result call fails
                respx.get(result_url,
                          content={
                              "progress": {
                                  "handled": 10,
                                  "failed": 0,
                                  "total": 10,
                                  "final": True
                              },
                          },
                          status_code=200 if rt.fail_at != 3 else 500)
            else:
                url = f"{node.url}/clear"

                def match_clear(request, response, *, _url=url):
                    if request.method != "POST" or _url != str(request.url):
                        return None
                    if rt.fail_at == 4:
                        return None
                    if json.loads(request.read())["root_globs"] != ["*"]:
                        return None
                    return response

                result_url = f"{node.url}/clear/result"

                respx.add(match_clear,
                          content={
                              "op_id": 42,
                              "status_url": result_url
                          })

                # Failure point 5: clear result call fails
                respx.get(result_url,
                          content={
                              "progress": {
                                  "final": True
                              },
                          },
                          status_code=200 if rt.fail_at != 5 else 500)

        req = {}
        if rt.storage_name:
            req["storage"] = rt.storage_name
        if rt.partial:
            req["partial_restore_nodes"] = [{
                "node_index": 0,
                "backup_index": 0
            }]
        response = client.post("/restore", json=req)
        if rt.fail_at == 1:
            # Cluster lock failure is immediate
            assert response.status_code == 409, response.json()
            assert app.state.coordinator_state.op_info.op_id == 0
            return
        assert response.status_code == 200, response.json()

        response = client.get(response.json()["status_url"])
        assert response.status_code == 200, response.json()
        if rt.fail_at:
            assert response.json().get("state") == "fail"
            assert response.json().get("progress") is not None
            assert response.json().get("progress")["final"]
        else:
            assert response.json().get("state") == "done"
            assert response.json().get("progress") is not None
            assert response.json().get("progress")["final"]
        if rt.fail_at == 5 or rt.fail_at is None:
            assert response.json().get("progress")["handled"] == 10
            assert response.json().get("progress")["failed"] == 0
            assert response.json().get("progress")["total"] == 10
        else:
            assert response.json().get("progress")["handled"] == 0
            assert response.json().get("progress")["failed"] == 0
            assert response.json().get("progress")["total"] == 0

        assert app.state.coordinator_state.op_info.op_id == 1
Example #6
0
async def test_deprecated_apis():
    with respx.mock:
        url = "https://foo.bar/"

        # Response kwargs among request kwargs
        with warnings.catch_warnings(record=True) as w:
            respx.get(url, status_code=201)
            respx.get(url, headers={})
            respx.get(url, content_type="foo/bar")
            respx.get(url, content="")
            respx.get(url, text="")
            respx.get(url, html="")
            respx.get(url, json={})
            respx.get(url, pass_through=True)
            assert len(w) == 8

        # Add route by http method string
        with warnings.catch_warnings(record=True) as w:
            respx.add("GET", url)
            assert len(w) == 1

        # Alias and aliases
        with warnings.catch_warnings(record=True) as w:
            request_pattern = respx.get(url, alias="index")
            name = request_pattern.alias
            aliased_pattern = respx.mock.aliases["index"]
            assert aliased_pattern is request_pattern
            assert name == request_pattern.name
            assert len(w) == 3

        # RequestPattern
        with warnings.catch_warnings(record=True) as w:
            callback = lambda req, res: res  # pragma: nocover
            request_pattern = RequestPattern(callback)
            assert request_pattern.has_side_effect

            request_pattern = RequestPattern("GET",
                                             "https://foo.bar/",
                                             pass_through=True)
            assert request_pattern.is_pass_through
            assert len(w) == 2

        # ResponseTemplate
        with warnings.catch_warnings(record=True) as w:
            request = httpx.Request("GET", "https://foo.bar/")

            callback = lambda request: ResponseTemplate(201)
            request_pattern = RequestPattern(callback,
                                             response=ResponseTemplate(444))
            assert request_pattern.resolve(request).status_code == 201

            request_pattern = RequestPattern("GET",
                                             response=ResponseTemplate(444))
            assert request_pattern.resolve(request).status_code == 444

            assert len(w) == 5

        # Mixing callback and response details
        with pytest.raises(NotImplementedError):
            callback = lambda request: ResponseTemplate(201)  # pragma: nocover
            respx.Router().add(callback, status_code=201)

        # Async callback
        with pytest.raises(NotImplementedError):

            async def callback(request):
                return None  # pragma: nocover

            mock_response = MockResponse(content=callback)
            request = httpx.Request("GET", "http://foo.bar/")
            mock_response.as_response(request)