Skip to content

Commit 7c8d2b2

Browse files
committed
Add tests
1 parent c701ab7 commit 7c8d2b2

File tree

6 files changed

+210
-0
lines changed

6 files changed

+210
-0
lines changed

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44

55
Adafruit-Blinka
66
Adafruit-Circuitpython-ConnectionManager
7+
requests

tests/files/green_red.png

125 Bytes
Loading

tests/files/green_red.png.license

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SPDX-FileCopyrightText: 2024 Justin Myers
2+
# SPDX-License-Identifier: Unlicense

tests/files/red_green.png

123 Bytes
Loading

tests/files/red_green.png.license

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SPDX-FileCopyrightText: 2024 Justin Myers
2+
# SPDX-License-Identifier: Unlicense

tests/files_test.py

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: Unlicense
4+
5+
""" Post Tests """
6+
# pylint: disable=line-too-long
7+
8+
import re
9+
from unittest import mock
10+
11+
import mocket
12+
import pytest
13+
import requests as python_requests
14+
15+
16+
@pytest.fixture
17+
def log_stream():
18+
return []
19+
20+
21+
@pytest.fixture
22+
def post_url():
23+
return "https://httpbin.org/post"
24+
25+
26+
@pytest.fixture
27+
def request_logging(log_stream):
28+
"""Reset the ConnectionManager, since it's a singlton and will hold data"""
29+
import http.client # pylint: disable=import-outside-toplevel
30+
31+
def httpclient_log(*args):
32+
log_stream.append(args)
33+
34+
http.client.print = httpclient_log
35+
http.client.HTTPConnection.debuglevel = 1
36+
37+
38+
def get_actual_request_data(log_stream):
39+
boundary_pattern = r"(?<=boundary=)(.\w*)"
40+
41+
boundary = ""
42+
actual_request_post = ""
43+
for log in log_stream:
44+
for log_arg in log:
45+
boundary_search = re.findall(boundary_pattern, log_arg)
46+
if boundary_search:
47+
boundary = boundary_search[0]
48+
elif "Content-Disposition" in log_arg:
49+
# this will look like:
50+
# b\'{content}\'
51+
# and escapped characters look like:
52+
# \\r
53+
post_data = log_arg[2:-1]
54+
post_bytes = post_data.encode("utf-8")
55+
post_unescaped = post_bytes.decode("unicode_escape")
56+
actual_request_post = post_unescaped.encode("latin1")
57+
58+
return boundary, actual_request_post
59+
60+
61+
def test_post_files_text( # pylint: disable=unused-argument
62+
sock, requests, log_stream, post_url, request_logging
63+
):
64+
file_data = {
65+
"key_4": (None, "Value 5"),
66+
}
67+
68+
python_requests.post(post_url, files=file_data)
69+
boundary, actual_request_post = get_actual_request_data(log_stream)
70+
71+
requests._build_boundary_string = mock.Mock(return_value=boundary)
72+
requests.post("http://" + mocket.MOCK_HOST_1 + "/post", files=file_data)
73+
74+
sock.connect.assert_called_once_with((mocket.MOCK_POOL_IP, 80))
75+
sock.send.assert_has_calls(
76+
[
77+
mock.call(b"Content-Type"),
78+
mock.call(b": "),
79+
mock.call(f"multipart/form-data; boundary={boundary}".encode()),
80+
mock.call(b"\r\n"),
81+
]
82+
)
83+
sock.send.assert_has_calls(
84+
[
85+
mock.call(b"Content-Length"),
86+
mock.call(b": "),
87+
mock.call(b"131"),
88+
mock.call(b"\r\n"),
89+
]
90+
)
91+
92+
sent = b"".join(sock.sent_data)
93+
assert sent.endswith(actual_request_post)
94+
95+
96+
def test_post_files_file( # pylint: disable=unused-argument
97+
sock, requests, log_stream, post_url, request_logging
98+
):
99+
with open("tests/files/red_green.png", "rb") as file_1:
100+
file_data = {
101+
"file_1": (
102+
"red_green.png",
103+
file_1,
104+
"image/png",
105+
{
106+
"Key_1": "Value 1",
107+
"Key_2": "Value 2",
108+
"Key_3": "Value 3",
109+
},
110+
),
111+
}
112+
113+
python_requests.post(post_url, files=file_data)
114+
boundary, actual_request_post = get_actual_request_data(log_stream)
115+
116+
requests._build_boundary_string = mock.Mock(return_value=boundary)
117+
requests.post("http://" + mocket.MOCK_HOST_1 + "/post", files=file_data)
118+
119+
sock.connect.assert_called_once_with((mocket.MOCK_POOL_IP, 80))
120+
sock.send.assert_has_calls(
121+
[
122+
mock.call(b"Content-Type"),
123+
mock.call(b": "),
124+
mock.call(f"multipart/form-data; boundary={boundary}".encode()),
125+
mock.call(b"\r\n"),
126+
]
127+
)
128+
sock.send.assert_has_calls(
129+
[
130+
mock.call(b"Content-Length"),
131+
mock.call(b": "),
132+
mock.call(b"347"),
133+
mock.call(b"\r\n"),
134+
]
135+
)
136+
sent = b"".join(sock.sent_data)
137+
assert sent.endswith(actual_request_post)
138+
139+
140+
def test_post_files_complex( # pylint: disable=unused-argument
141+
sock, requests, log_stream, post_url, request_logging
142+
):
143+
with open("tests/files/red_green.png", "rb") as file_1, open(
144+
"tests/files/green_red.png", "rb"
145+
) as file_2:
146+
file_data = {
147+
"file_1": (
148+
"red_green.png",
149+
file_1,
150+
"image/png",
151+
{
152+
"Key_1": "Value 1",
153+
"Key_2": "Value 2",
154+
"Key_3": "Value 3",
155+
},
156+
),
157+
"key_4": (None, "Value 5"),
158+
"file_2": (
159+
"green_red.png",
160+
file_2,
161+
"image/png",
162+
),
163+
"key_6": (None, "Value 6"),
164+
}
165+
166+
python_requests.post(post_url, files=file_data)
167+
boundary, actual_request_post = get_actual_request_data(log_stream)
168+
169+
requests._build_boundary_string = mock.Mock(return_value=boundary)
170+
requests.post("http://" + mocket.MOCK_HOST_1 + "/post", files=file_data)
171+
172+
sock.connect.assert_called_once_with((mocket.MOCK_POOL_IP, 80))
173+
sock.send.assert_has_calls(
174+
[
175+
mock.call(b"Content-Type"),
176+
mock.call(b": "),
177+
mock.call(f"multipart/form-data; boundary={boundary}".encode()),
178+
mock.call(b"\r\n"),
179+
]
180+
)
181+
sock.send.assert_has_calls(
182+
[
183+
mock.call(b"Content-Length"),
184+
mock.call(b": "),
185+
mock.call(b"796"),
186+
mock.call(b"\r\n"),
187+
]
188+
)
189+
sent = b"".join(sock.sent_data)
190+
assert sent.endswith(actual_request_post)
191+
192+
193+
def test_post_files_not_binary(requests):
194+
with open("tests/files/red_green.png", "r") as file_1:
195+
file_data = {
196+
"file_1": (
197+
"red_green.png",
198+
file_1,
199+
"image/png",
200+
),
201+
}
202+
203+
with pytest.raises(AttributeError) as context:
204+
requests.post("http://" + mocket.MOCK_HOST_1 + "/post", files=file_data)
205+
assert "Files must be opened in binary mode" in str(context)

0 commit comments

Comments
 (0)