Skip to content

Commit 16dad0c

Browse files
author
Two Dev
committed
update: pytest for unit tests, bug fixes.
1 parent 6ac9a87 commit 16dad0c

36 files changed

+616
-81
lines changed

.coveragerc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[run]
2+
omit = tests/*

.editorconfig

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# http://editorconfig.org
2+
3+
root = true
4+
5+
[*]
6+
charset = utf-8
7+
end_of_line = lf
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true
10+
11+
[*.{py,rst,ini}]
12+
indent_style = space
13+
indent_size = 4
14+
15+
[*.{html,css,scss,json,yml,xml}]
16+
indent_style = space
17+
indent_size = 2
18+
19+
[*.md]
20+
trim_trailing_whitespace = false
21+
22+
[Makefile]
23+
indent_style = tab

.github/workflows/ci.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
strategy:
1414
max-parallel: 4
1515
matrix:
16-
python-version: ['3.9', '3.10', '3.11']
16+
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
1717
steps:
1818
- uses: actions/checkout@v4
1919
- name: Set up Python ${{ matrix.python-version }}
@@ -24,11 +24,15 @@ jobs:
2424
- name: Install Dependencies
2525
run: |
2626
python -m pip install --upgrade pip
27-
pip install -r requirements.txt
27+
pip install -r requirements-dev.txt
2828
2929
- name: Run pre-commit
3030
uses: pre-commit/[email protected]
3131

32+
- name: Run tests
33+
run: |
34+
python -m pytest tests
35+
3236
deploy:
3337
needs: build
3438
runs-on: ubuntu-latest

CHANGELOG.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,35 @@
11
Release History
22
===============
33

4+
1.0.3 (2024-12-11)
5+
-------------------
6+
**Improvements:**
7+
8+
- Add unit tests.
9+
- Improve document.
10+
11+
**Bugfixes:**
12+
13+
- Fix timeout.
14+
- Fix missing port redirection.
15+
16+
17+
1.0.3 (2024-12-05)
18+
-------------------
19+
**Improvements**
20+
21+
- improve document.
22+
23+
**Bugfixes**
24+
25+
- Fix multipart encoders, cross share auth.
26+
27+
1.0.2 (2024-12-05)
28+
-------------------
29+
**Improvements**
30+
- Download specific TLS library versions.
31+
- Add a document.
32+
433
1.0.1 (2024-12-04)
534
-------------------
6-
* First release
35+
- First release

Makefile

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
.PHONY: docs
22
init:
3-
python -m pip install -r requirements.txt
3+
python -m pip install -r requirements-dev.txt
4+
5+
test:
6+
tox -p
7+
rm -rf *.egg-info
48

59
test-readme:
610
python setup.py check --restructuredtext --strict && ([ $$? -eq 0 ] && echo "README.rst and CHANGELOG.md ok") || echo "Invalid markup in README.md or CHANGELOG.md!"
@@ -10,11 +14,22 @@ lint:
1014
python -m isort tls_requests
1115
python -m flake8 tls_requests
1216

17+
coverage:
18+
python -m pytest --cov-config .coveragerc --verbose --cov-report term --cov-report xml --cov=tls_requests tests
19+
20+
docs:
21+
mkdocs serve
22+
23+
publish-test-pypi:
24+
python -m pip install -r requirements-dev.txt
25+
python -m pip install 'twine>=6.0.1'
26+
python setup.py sdist bdist_wheel
27+
twine upload --repository testpypi dist/*
28+
rm -rf build dist .egg *.egg-info
29+
1330
publish-pypi:
31+
python -m pip install -r requirements-dev.txt
1432
python -m pip install 'twine>=6.0.1'
1533
python setup.py sdist bdist_wheel
1634
twine upload dist/*
17-
rm -rf build dist .egg wrapper_tls_requests.egg-info
18-
19-
docs:
20-
mkdocs serve
35+
rm -rf build dist .egg *.egg-info

docs/advanced/hooks.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ client.hooks = {
100100
Best Practices
101101
--------------
102102

103-
1. **Access Content**: Use `.read()` or `await read()` in asynchronous contexts to access `response.content` before returning it.
103+
1. **Access Content**: Use `.read()` or `await .aread()` in asynchronous contexts to access `response.content` before returning it.
104104
2. **Always Use Lists:** Hooks must be registered as **lists of callables**, even if you are adding only one function.
105105
3. **Combine Hooks:** You can register multiple hooks for the same event type to handle various concerns, such as logging and error handling.
106106
4. **Order Matters:** Hooks are executed in the order they are registered.

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[build-system]
22
requires = ['setuptools>=40.8.0']
33
build-backend = 'setuptools.build_meta'
4+
5+
[tool.pytest.ini_options]
6+
asyncio_mode = "auto"

requirements-dev.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
-r requirements.txt
2+
3+
# Documentation
4+
mkdocs==1.6.1
5+
mkautodoc==0.2.0
6+
mkdocs-material==9.5.39
7+
8+
# Packaging
9+
setuptools~=75.3.0
10+
twine~=6.0.1
11+
12+
# Tests & Linting
13+
pre-commit==3.7.0
14+
black==24.3.0
15+
coverage[toml]==7.6.1
16+
pre-commit==3.7.0
17+
isort==5.13.2
18+
mypy==1.11.2
19+
pytest==8.3.3
20+
pytest-asyncio==0.24.0
21+
pytest-cov==6.0.0
22+
pytest_httpserver==1.1.0
23+
Werkzeug==3.1.3
24+
tox==4.23.2

requirements.txt

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,5 @@
11
# Base
22
chardet~=5.2.0
3-
requests~=2.32.3
3+
requests>=2.28.0
44
tqdm~=4.67.1
5-
6-
# Documentation
7-
mkdocs==1.6.1
8-
mkautodoc==0.2.0
9-
mkdocs-material==9.5.39
10-
11-
# Packaging
12-
setuptools~=75.3.0
13-
twine~=6.0.1
14-
15-
# Tests & Linting
16-
pre-commit==3.7.0
17-
black==24.3.0
18-
flake8==7.0.0
19-
coverage[toml]==7.6.1
20-
pre-commit==3.7.0
21-
isort==5.13.2
22-
mypy==1.11.2
23-
pytest==8.3.3
5+
idna~=3.10

setup.cfg

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ classifiers =
2121
Programming Language :: Python :: 3.10
2222
Programming Language :: Python :: 3.11
2323
Programming Language :: Python :: 3.12
24+
Programming Language :: Python :: 3.13
2425
Programming Language :: Python :: 3 :: Only
2526
Programming Language :: Python :: Implementation :: CPython
2627
Programming Language :: Python :: Implementation :: PyPy
@@ -29,7 +30,7 @@ classifiers =
2930

3031
project_urls =
3132
Changelog = https://github.com/thewebscraping/tls-requests/blob/main/CHANGELOG.md
32-
Documentation = https://github.com/thewebscraping/tls-requests
33+
Documentation = https://thewebscraping.github.io/tls-requests/
3334
Source = https://github.com/thewebscraping/tls-requests
3435
Homepage = https://github.com/thewebscraping/tls-requests
3536

tests/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pytest_plugins = ['pytest_httpserver', 'pytest_asyncio']

tests/files/__init__.py

Whitespace-only changes.

tests/files/coingecko.png

36.4 KB
Loading

tests/test_api.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import tls_requests
2+
3+
RESPONSE_BYTE = b"Hello World!"
4+
RESPONSE_TEXT = "Hello World!"
5+
6+
7+
def assert_response(response):
8+
assert response.status_code, 200
9+
assert response.reason, "OK"
10+
assert response.text, RESPONSE_TEXT
11+
assert response.content, RESPONSE_BYTE
12+
13+
14+
def make_request(request_fn, httpserver, is_assert_response: bool = True):
15+
httpserver.expect_request("/api").respond_with_data(RESPONSE_BYTE)
16+
response = request_fn(httpserver.url_for('/api'))
17+
if is_assert_response:
18+
assert_response(response)
19+
20+
return response
21+
22+
23+
def test_get(httpserver):
24+
make_request(tls_requests.get, httpserver)
25+
26+
27+
def test_post(httpserver):
28+
make_request(tls_requests.post, httpserver)
29+
30+
31+
def test_put(httpserver):
32+
make_request(tls_requests.put, httpserver)
33+
34+
35+
def test_patch(httpserver):
36+
make_request(tls_requests.patch, httpserver)
37+
38+
39+
def test_delete(httpserver):
40+
make_request(tls_requests.delete, httpserver)
41+
42+
43+
def test_options(httpserver):
44+
make_request(tls_requests.options, httpserver)
45+
46+
47+
def test_head(httpserver):
48+
response = make_request(tls_requests.head, httpserver, False)
49+
assert response.status_code == 200
50+
assert response.reason == "OK"
51+
assert response.text == ""
52+
assert response.content == b""

tests/test_auth.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
from base64 import b64encode
2+
3+
import pytest
4+
5+
import tls_requests
6+
7+
auth = ("user", "pass")
8+
AUTH_TOKEN = "Basic %s" % b64encode(b":".join([s.encode() for s in auth])).decode()
9+
AUTH_HEADERS = {"authorization": AUTH_TOKEN}
10+
AUTH_FUNCTION_KEY = "x-authorization"
11+
AUTH_FUNCTION_VALUE = "123456"
12+
AUTH_FUNCTION_HEADERS = {AUTH_FUNCTION_KEY: AUTH_FUNCTION_VALUE}
13+
14+
15+
def auth_function(request):
16+
request.headers.update(AUTH_FUNCTION_HEADERS)
17+
18+
19+
@pytest.fixture
20+
def auth_url(httpserver):
21+
return httpserver.url_for('/auth')
22+
23+
24+
@pytest.fixture
25+
def http_auth_function(httpserver):
26+
httpserver.expect_request("/auth", headers=AUTH_FUNCTION_HEADERS).respond_with_data()
27+
return httpserver
28+
29+
30+
@pytest.fixture
31+
def http_auth(httpserver):
32+
httpserver.expect_request("/auth", headers=AUTH_HEADERS).respond_with_data()
33+
return httpserver
34+
35+
36+
def test_auth(http_auth, auth_url):
37+
response = tls_requests.get(auth_url, auth=auth)
38+
assert response.status_code == 200
39+
assert response.request.headers["Authorization"] == AUTH_TOKEN
40+
41+
42+
def test_auth_function(http_auth_function, auth_url):
43+
response = tls_requests.get(auth_url, auth=auth_function)
44+
assert response.status_code == 200
45+
assert response.request.headers[AUTH_FUNCTION_KEY] == AUTH_FUNCTION_VALUE
46+
47+
48+
def test_client_auth(http_auth, auth_url):
49+
with tls_requests.Client(auth=auth) as client:
50+
response = client.get(auth_url)
51+
52+
assert response.status_code == 200
53+
assert bool(response.closed == client.closed) is True
54+
assert response.request.headers["Authorization"] == AUTH_TOKEN
55+
56+
57+
def test_client_auth_cross_sharing(http_auth, auth_url):
58+
with tls_requests.Client(auth=('1', '2')) as client:
59+
response = client.get(auth_url, auth=auth)
60+
61+
assert response.status_code == 200
62+
assert bool(response.closed == client.closed) is True
63+
assert response.request.headers["Authorization"] == AUTH_TOKEN
64+
65+
66+
def test_client_auth_function_cross_sharing(http_auth_function, auth_url):
67+
with tls_requests.Client(auth=auth) as client:
68+
response = client.get(auth_url, auth=auth_function)
69+
70+
assert response.status_code == 200
71+
assert bool(response.closed == client.closed) is True
72+
assert response.request.headers[AUTH_FUNCTION_KEY] == AUTH_FUNCTION_VALUE
73+
74+
75+
@pytest.mark.asyncio
76+
async def test_async_auth(http_auth, auth_url):
77+
async with tls_requests.AsyncClient(auth=auth) as client:
78+
response = await client.get(auth_url)
79+
80+
assert response.status_code == 200
81+
assert bool(response.closed == client.closed) is True
82+
assert response.request.headers["Authorization"] == AUTH_TOKEN
83+
84+
85+
@pytest.mark.asyncio
86+
async def test_async_auth_function(http_auth_function, auth_url):
87+
async with tls_requests.AsyncClient(auth=auth_function) as client:
88+
response = await client.get(auth_url)
89+
90+
assert response.status_code == 200
91+
assert bool(response.closed == client.closed) is True
92+
assert response.request.headers[AUTH_FUNCTION_KEY] == AUTH_FUNCTION_VALUE
93+
94+
95+
@pytest.mark.asyncio
96+
async def test_async_auth_function_cross_sharing(http_auth_function, auth_url):
97+
async with tls_requests.AsyncClient(auth=auth) as client:
98+
response = await client.get(auth_url, auth=auth_function)
99+
100+
assert response.status_code == 200
101+
assert bool(response.closed == client.closed) is True
102+
assert response.request.headers[AUTH_FUNCTION_KEY] == AUTH_FUNCTION_VALUE

0 commit comments

Comments
 (0)