File: test_http_httpx.py

package info (click to toggle)
python-elastic-transport 9.2.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 644 kB
  • sloc: python: 6,673; makefile: 17
file content (169 lines) | stat: -rw-r--r-- 6,027 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#  Licensed to Elasticsearch B.V. under one or more contributor
#  license agreements. See the NOTICE file distributed with
#  this work for additional information regarding copyright
#  ownership. Elasticsearch B.V. licenses this file to you under
#  the Apache License, Version 2.0 (the "License"); you may
#  not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
# 	http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing,
#  software distributed under the License is distributed on an
#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
#  KIND, either express or implied.  See the License for the
#  specific language governing permissions and limitations
#  under the License.

import gzip
import ssl
import warnings

import pytest
import respx

from elastic_transport import HttpxAsyncHttpNode, NodeConfig
from elastic_transport._node._base import DEFAULT_USER_AGENT


def create_node(node_config: NodeConfig):
    return HttpxAsyncHttpNode(node_config)


class TestHttpxAsyncNodeCreation:
    def test_ssl_context(self):
        ssl_context = ssl.create_default_context()
        with warnings.catch_warnings(record=True) as w:
            node = create_node(
                NodeConfig(
                    scheme="https",
                    host="localhost",
                    port=80,
                    ssl_context=ssl_context,
                )
            )
        assert node.client._transport._pool._ssl_context is ssl_context
        assert len(w) == 0

    def test_uses_https_if_verify_certs_is_off(self):
        with warnings.catch_warnings(record=True) as w:
            _ = create_node(NodeConfig("https", "localhost", 443, verify_certs=False))
        assert (
            str(w[0].message)
            == "Connecting to 'https://localhost:443' using TLS with verify_certs=False is insecure"
        )

    def test_no_warn_when_uses_https_if_verify_certs_is_off(self):
        with warnings.catch_warnings(record=True) as w:
            _ = create_node(
                NodeConfig(
                    "https",
                    "localhost",
                    443,
                    verify_certs=False,
                    ssl_show_warn=False,
                )
            )
        assert 0 == len(w)

    def test_ca_certs_with_verify_ssl_false_raises_error(self):
        with pytest.raises(ValueError) as exc:
            create_node(
                NodeConfig(
                    "https",
                    "localhost",
                    443,
                    ca_certs="/ca/certs",
                    verify_certs=False,
                )
            )
            assert (
                str(exc.value) == "You cannot use 'ca_certs' when 'verify_certs=False'"
            )

    def test_path_prefix(self):
        node = create_node(
            NodeConfig(
                "http",
                "localhost",
                9200,
                path_prefix="/test",
            )
        )
        assert node.base_url == "http://localhost:9200/test"
        assert node.client.base_url == "http://localhost:9200/test/"


@pytest.mark.anyio
class TestHttpxAsyncNode:
    @respx.mock
    async def test_simple_request(self):
        node = create_node(NodeConfig(scheme="http", host="localhost", port=80))
        respx.get("http://localhost/index")
        await node.perform_request(
            "GET", "/index", b"hello world", headers={"key": "value"}
        )
        request = respx.calls.last.request
        assert request.content == b"hello world"
        assert {
            "key": "value",
            "connection": "keep-alive",
            "user-agent": DEFAULT_USER_AGENT,
        }.items() <= request.headers.items()

    @respx.mock
    async def test_compression(self):
        node = create_node(
            NodeConfig(scheme="http", host="localhost", port=80, http_compress=True)
        )
        respx.get("http://localhost/index")
        await node.perform_request("GET", "/index", b"hello world")
        request = respx.calls.last.request
        assert gzip.decompress(request.content) == b"hello world"
        assert {"content-encoding": "gzip"}.items() <= request.headers.items()

    @respx.mock
    async def test_default_timeout(self):
        node = create_node(
            NodeConfig(scheme="http", host="localhost", port=80, request_timeout=10)
        )
        respx.get("http://localhost/index")
        await node.perform_request("GET", "/index", b"hello world")
        request = respx.calls.last.request
        assert request.extensions["timeout"]["connect"] == 10

    @respx.mock
    async def test_overwritten_timeout(self):
        node = create_node(
            NodeConfig(scheme="http", host="localhost", port=80, request_timeout=10)
        )
        respx.get("http://localhost/index")
        await node.perform_request("GET", "/index", b"hello world", request_timeout=15)
        request = respx.calls.last.request
        assert request.extensions["timeout"]["connect"] == 15

    @respx.mock
    async def test_merge_headers(self):
        node = create_node(
            NodeConfig("http", "localhost", 80, headers={"h1": "v1", "h2": "v2"})
        )
        respx.get("http://localhost/index")
        await node.perform_request(
            "GET", "/index", b"hello world", headers={"h2": "v2p", "h3": "v3"}
        )
        request = respx.calls.last.request
        assert request.headers["h1"] == "v1"
        assert request.headers["h2"] == "v2p"
        assert request.headers["h3"] == "v3"


def test_ssl_assert_fingerprint(cert_fingerprint, httpbin_secure):
    with pytest.raises(ValueError, match="httpx does not support certificate pinning"):
        HttpxAsyncHttpNode(
            NodeConfig(
                scheme="https",
                host=httpbin_secure.host,
                port=httpbin_secure.port,
                ssl_assert_fingerprint=cert_fingerprint,
            )
        )