Skip to content

Commit 23d0f56

Browse files
Use locally hosted services for tests (#246)
* Use a locally hosted httpbin for tests * use a www.elastic.co instead of httpbin.org for SSL tests * use pytest-httpbin instead of a local service * use pytest-httpbin also for SSL tests * fix inaccurate comment
1 parent da5f360 commit 23d0f56

File tree

9 files changed

+120
-101
lines changed

9 files changed

+120
-101
lines changed

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"pytest-cov",
6161
"pytest-mock",
6262
"pytest-asyncio",
63+
"pytest-httpbin",
6364
"pytest-httpserver",
6465
"trustme",
6566
"requests",

tests/async_/test_async_transport.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,21 @@
4646

4747

4848
@pytest.mark.asyncio
49-
async def test_async_transport_httpbin(httpbin_node_config):
49+
async def test_async_transport_httpbin(httpbin_node_config, httpbin):
5050
t = AsyncTransport([httpbin_node_config], meta_header=False)
5151
resp, data = await t.perform_request("GET", "/anything?key=value")
5252

5353
assert resp.status == 200
5454
assert data["method"] == "GET"
55-
assert data["url"] == "https://httpbin.org/anything?key=value"
55+
assert data["url"] == f"{httpbin.url}/anything?key=value"
5656
assert data["args"] == {"key": "value"}
5757

5858
data["headers"].pop("X-Amzn-Trace-Id", None)
59-
assert data["headers"] == {"User-Agent": DEFAULT_USER_AGENT, "Host": "httpbin.org"}
59+
assert data["headers"] == {
60+
"User-Agent": DEFAULT_USER_AGENT,
61+
"Connection": "keep-alive",
62+
"Host": f"{httpbin.host}:{httpbin.port}",
63+
}
6064

6165

6266
@pytest.mark.skipif(

tests/async_/test_httpbin.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828

2929
@pytest.mark.asyncio
30-
async def test_simple_request(httpbin_node_config):
30+
async def test_simple_request(httpbin_node_config, httpbin):
3131
t = AsyncTransport([httpbin_node_config])
3232

3333
resp, data = await t.perform_request(
@@ -38,7 +38,7 @@ async def test_simple_request(httpbin_node_config):
3838
)
3939
assert resp.status == 200
4040
assert data["method"] == "GET"
41-
assert data["url"] == "https://httpbin.org/anything?key[]=1&key[]=2&q1&q2="
41+
assert data["url"] == f"{httpbin.url}/anything?key[]=1&key[]=2&q1&q2="
4242

4343
# httpbin makes no-value query params into ''
4444
assert data["args"] == {
@@ -53,13 +53,14 @@ async def test_simple_request(httpbin_node_config):
5353
"Content-Type": "application/json",
5454
"Content-Length": "15",
5555
"Custom": "headeR",
56-
"Host": "httpbin.org",
56+
"Connection": "keep-alive",
57+
"Host": f"{httpbin.host}:{httpbin.port}",
5758
}
5859
assert all(v == data["headers"][k] for k, v in request_headers.items())
5960

6061

6162
@pytest.mark.asyncio
62-
async def test_node(httpbin_node_config):
63+
async def test_node(httpbin_node_config, httpbin):
6364
def new_node(**kwargs):
6465
return AiohttpHttpNode(dataclasses.replace(httpbin_node_config, **kwargs))
6566

@@ -69,11 +70,12 @@ def new_node(**kwargs):
6970
parsed = parse_httpbin(data)
7071
assert parsed == {
7172
"headers": {
72-
"Host": "httpbin.org",
73+
"Connection": "keep-alive",
74+
"Host": f"{httpbin.host}:{httpbin.port}",
7375
"User-Agent": DEFAULT_USER_AGENT,
7476
},
7577
"method": "GET",
76-
"url": "https://httpbin.org/anything",
78+
"url": f"{httpbin.url}/anything",
7779
}
7880

7981
node = new_node(http_compress=True)
@@ -83,11 +85,12 @@ def new_node(**kwargs):
8385
assert parsed == {
8486
"headers": {
8587
"Accept-Encoding": "gzip",
86-
"Host": "httpbin.org",
88+
"Connection": "keep-alive",
89+
"Host": f"{httpbin.host}:{httpbin.port}",
8790
"User-Agent": DEFAULT_USER_AGENT,
8891
},
8992
"method": "GET",
90-
"url": "https://httpbin.org/anything",
93+
"url": f"{httpbin.url}/anything",
9194
}
9295

9396
resp, data = await node.perform_request("GET", "/anything", body=b"hello, world!")
@@ -99,11 +102,12 @@ def new_node(**kwargs):
99102
"Content-Encoding": "gzip",
100103
"Content-Type": "application/octet-stream",
101104
"Content-Length": "33",
102-
"Host": "httpbin.org",
105+
"Connection": "keep-alive",
106+
"Host": f"{httpbin.host}:{httpbin.port}",
103107
"User-Agent": DEFAULT_USER_AGENT,
104108
},
105109
"method": "GET",
106-
"url": "https://httpbin.org/anything",
110+
"url": f"{httpbin.url}/anything",
107111
}
108112

109113
resp, data = await node.perform_request(
@@ -120,9 +124,10 @@ def new_node(**kwargs):
120124
"Content-Encoding": "gzip",
121125
"Content-Length": "36",
122126
"Content-Type": "application/json",
123-
"Host": "httpbin.org",
127+
"Connection": "keep-alive",
128+
"Host": f"{httpbin.host}:{httpbin.port}",
124129
"User-Agent": DEFAULT_USER_AGENT,
125130
},
126131
"method": "POST",
127-
"url": "https://httpbin.org/anything",
132+
"url": f"{httpbin.url}/anything",
128133
}

tests/conftest.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,32 +66,33 @@ async def perform_request(self, *args, **kwargs):
6666
return NodeApiResponse(meta, self.body)
6767

6868

69-
@pytest.fixture(scope="session", params=[True, False])
70-
def httpbin_cert_fingerprint(request) -> str:
71-
"""Gets the SHA256 fingerprint of the certificate for 'httpbin.org'"""
72-
sock = socket.create_connection(("httpbin.org", 443))
69+
@pytest.fixture(
70+
scope="session", params=["short-lower", "short-upper", "long-lower", "long-upper"]
71+
)
72+
def cert_fingerprint(request, httpbin_secure) -> str:
73+
"""Gets the SHA256 fingerprint of the certificate for the secure httpbin"""
74+
sock = socket.create_connection((httpbin_secure.host, httpbin_secure.port))
7375
ctx = ssl.create_default_context()
7476
ctx.check_hostname = False
7577
ctx.verify_mode = ssl.CERT_NONE
7678
sock = ctx.wrap_socket(sock)
7779
digest = hashlib.sha256(sock.getpeercert(binary_form=True)).hexdigest()
7880
assert len(digest) == 64
7981
sock.close()
80-
if request.param:
82+
if "upper" in request.param:
83+
digest = digest.upper()
84+
else:
85+
digest = digest.lower()
86+
if "short" in request.param:
8187
return digest
8288
else:
8389
return ":".join([digest[i : i + 2] for i in range(0, len(digest), 2)])
8490

8591

8692
@pytest.fixture(scope="session")
87-
def httpbin_node_config() -> NodeConfig:
88-
try:
89-
sock = socket.create_connection(("httpbin.org", 443))
90-
except Exception as e:
91-
pytest.skip(f"Couldn't connect to httpbin.org, internet not connected? {e}")
92-
sock.close()
93+
def httpbin_node_config(httpbin) -> NodeConfig:
9394
return NodeConfig(
94-
"https", "httpbin.org", 443, verify_certs=False, ssl_show_warn=False
95+
"http", httpbin.host, httpbin.port, verify_certs=False, ssl_show_warn=False
9596
)
9697

9798

tests/node/test_http_aiohttp.py

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -290,14 +290,14 @@ async def test_head_workaround(self, aiohttp_fixed_head_bug):
290290

291291

292292
@pytest.mark.asyncio
293-
async def test_ssl_assert_fingerprint(httpbin_cert_fingerprint):
293+
async def test_ssl_assert_fingerprint(cert_fingerprint, httpbin_secure):
294294
with warnings.catch_warnings(record=True) as w:
295295
node = AiohttpHttpNode(
296296
NodeConfig(
297297
scheme="https",
298-
host="httpbin.org",
299-
port=443,
300-
ssl_assert_fingerprint=httpbin_cert_fingerprint,
298+
host=httpbin_secure.host,
299+
port=httpbin_secure.port,
300+
ssl_assert_fingerprint=cert_fingerprint,
301301
)
302302
)
303303
resp, _ = await node.perform_request("GET", "/")
@@ -307,23 +307,29 @@ async def test_ssl_assert_fingerprint(httpbin_cert_fingerprint):
307307

308308

309309
@pytest.mark.asyncio
310-
async def test_default_headers():
311-
node = AiohttpHttpNode(NodeConfig(scheme="https", host="httpbin.org", port=443))
310+
async def test_default_headers(httpbin):
311+
node = AiohttpHttpNode(
312+
NodeConfig(scheme="http", host=httpbin.host, port=httpbin.port)
313+
)
312314
resp, data = await node.perform_request("GET", "/anything")
313315

314316
assert resp.status == 200
315317
headers = json.loads(data)["headers"]
316318
headers.pop("X-Amzn-Trace-Id", None)
317-
assert headers == {"Host": "httpbin.org", "User-Agent": DEFAULT_USER_AGENT}
319+
assert headers == {
320+
"Connection": "keep-alive",
321+
"Host": f"{httpbin.host}:{httpbin.port}",
322+
"User-Agent": DEFAULT_USER_AGENT,
323+
}
318324

319325

320326
@pytest.mark.asyncio
321-
async def test_custom_headers():
327+
async def test_custom_headers(httpbin):
322328
node = AiohttpHttpNode(
323329
NodeConfig(
324-
scheme="https",
325-
host="httpbin.org",
326-
port=443,
330+
scheme="http",
331+
host=httpbin.host,
332+
port=httpbin.port,
327333
headers={"accept-encoding": "gzip", "Content-Type": "application/json"},
328334
)
329335
)
@@ -341,19 +347,20 @@ async def test_custom_headers():
341347
headers.pop("X-Amzn-Trace-Id", None)
342348
assert headers == {
343349
"Accept-Encoding": "gzip",
350+
"Connection": "keep-alive",
344351
"Content-Type": "application/x-ndjson",
345-
"Host": "httpbin.org",
352+
"Host": f"{httpbin.host}:{httpbin.port}",
346353
"User-Agent": "custom-agent/1.2.3",
347354
}
348355

349356

350357
@pytest.mark.asyncio
351-
async def test_custom_user_agent():
358+
async def test_custom_user_agent(httpbin):
352359
node = AiohttpHttpNode(
353360
NodeConfig(
354-
scheme="https",
355-
host="httpbin.org",
356-
port=443,
361+
scheme="http",
362+
host=httpbin.host,
363+
port=httpbin.port,
357364
headers={
358365
"accept-encoding": "gzip",
359366
"Content-Type": "application/json",
@@ -371,8 +378,9 @@ async def test_custom_user_agent():
371378
headers.pop("X-Amzn-Trace-Id", None)
372379
assert headers == {
373380
"Accept-Encoding": "gzip",
381+
"Connection": "keep-alive",
374382
"Content-Type": "application/json",
375-
"Host": "httpbin.org",
383+
"Host": f"{httpbin.host}:{httpbin.port}",
376384
"User-Agent": "custom-agent/1.2.3",
377385
}
378386

@@ -383,9 +391,11 @@ def test_repr():
383391

384392

385393
@pytest.mark.asyncio
386-
async def test_head():
394+
async def test_head(httpbin):
387395
node = AiohttpHttpNode(
388-
NodeConfig(scheme="https", host="httpbin.org", port=443, http_compress=True)
396+
NodeConfig(
397+
scheme="http", host=httpbin.host, port=httpbin.port, http_compress=True
398+
)
389399
)
390400
resp, data = await node.perform_request("HEAD", "/anything")
391401

tests/node/test_http_httpx.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,13 @@ async def test_merge_headers(self):
145145
assert request.headers["h3"] == "v3"
146146

147147

148-
def test_ssl_assert_fingerprint(httpbin_cert_fingerprint):
148+
def test_ssl_assert_fingerprint(cert_fingerprint, httpbin_secure):
149149
with pytest.raises(ValueError, match="httpx does not support certificate pinning"):
150150
HttpxAsyncHttpNode(
151151
NodeConfig(
152152
scheme="https",
153-
host="httpbin.org",
154-
port=443,
155-
ssl_assert_fingerprint=httpbin_cert_fingerprint,
153+
host=httpbin_secure.host,
154+
port=httpbin_secure.port,
155+
ssl_assert_fingerprint=cert_fingerprint,
156156
)
157157
)

tests/node/test_urllib3_chain_certs.py

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@
3030

3131
@requires_ssl_assert_fingerprint_in_chain
3232
@pytest.mark.parametrize("node_cls", [Urllib3HttpNode, RequestsHttpNode])
33-
def test_ssl_assert_fingerprint_invalid_length(node_cls):
33+
def test_ssl_assert_fingerprint_invalid_length(node_cls, httpbin_secure):
3434
with pytest.raises(ValueError) as e:
3535
node_cls(
3636
NodeConfig(
3737
"https",
38-
"httpbin.org",
39-
443,
38+
httpbin_secure.host,
39+
httpbin_secure.port,
4040
ssl_assert_fingerprint="0000",
4141
)
4242
)
@@ -49,22 +49,14 @@ def test_ssl_assert_fingerprint_invalid_length(node_cls):
4949

5050
@requires_ssl_assert_fingerprint_in_chain
5151
@pytest.mark.parametrize("node_cls", [Urllib3HttpNode, RequestsHttpNode])
52-
@pytest.mark.parametrize(
53-
"ssl_assert_fingerprint",
54-
[
55-
"8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e",
56-
"8e:cd:e6:88:4f:3d:87:b1:12:5b:a3:1a:c3:fc:b1:3d:70:16:de:7f:57:cc:90:4f:e1:cb:97:c6:ae:98:19:6e",
57-
"8ECDE6884F3D87B1125BA31AC3FCB13D7016DE7F57CC904FE1CB97C6AE98196E",
58-
],
59-
)
60-
def test_assert_fingerprint_in_cert_chain(node_cls, ssl_assert_fingerprint):
52+
def test_assert_fingerprint_in_cert_chain(node_cls, cert_fingerprint, httpbin_secure):
6153
with warnings.catch_warnings(record=True) as w:
6254
node = node_cls(
6355
NodeConfig(
6456
"https",
65-
"httpbin.org",
66-
443,
67-
ssl_assert_fingerprint=ssl_assert_fingerprint,
57+
httpbin_secure.host,
58+
httpbin_secure.port,
59+
ssl_assert_fingerprint=cert_fingerprint,
6860
)
6961
)
7062
meta, _ = node.perform_request("GET", "/")
@@ -75,11 +67,13 @@ def test_assert_fingerprint_in_cert_chain(node_cls, ssl_assert_fingerprint):
7567

7668
@requires_ssl_assert_fingerprint_in_chain
7769
@pytest.mark.parametrize("node_cls", [Urllib3HttpNode, RequestsHttpNode])
78-
def test_assert_fingerprint_in_cert_chain_failure(node_cls):
70+
def test_assert_fingerprint_in_cert_chain_failure(
71+
node_cls, httpbin_secure, cert_fingerprint
72+
):
7973
node = node_cls(
8074
NodeConfig(
8175
"https",
82-
"httpbin.org",
76+
"www.elastic.co",
8377
443,
8478
ssl_assert_fingerprint="0" * 64,
8579
)
@@ -95,5 +89,5 @@ def test_assert_fingerprint_in_cert_chain_failure(node_cls):
9589
'Expected "0000000000000000000000000000000000000000000000000000000000000000",'
9690
in err
9791
)
98-
# This is the root CA for httpbin.org with a leading comma to denote more than one cert was listed.
99-
assert ', "8ecde6884f3d87b1125ba31ac3fcb13d7016de7f57cc904fe1cb97c6ae98196e"' in err
92+
# This is the root CA for www.elastic.co with a leading comma to denote more than one cert was listed.
93+
assert ', "cbb522d7b7f127ad6a0113865bdf1cd4102e7d0759af635a7cf4720dc963c53b"' in err

0 commit comments

Comments
 (0)