Skip to content

Commit 063752f

Browse files
committed
add error case tests
1 parent 6e17ae2 commit 063752f

1 file changed

Lines changed: 176 additions & 0 deletions

File tree

tests/test_errors.py

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
"""Tests for error conditions in msgpack-stream to increase coverage."""
2+
3+
import io
4+
from datetime import datetime
5+
from unittest import mock
6+
7+
import pytest
8+
9+
from msgpack_stream import ExtType, pack_stream, unpack_stream
10+
11+
12+
def test_int_too_large_negative():
13+
"""Test that extremely large negative integers raise ValueError."""
14+
stream = io.BytesIO()
15+
# Larger than max int64
16+
obj = -(2**63 + 1)
17+
with pytest.raises(ValueError, match="int too large"):
18+
pack_stream(stream, obj)
19+
20+
21+
def test_uint_too_large():
22+
"""Test that extremely large unsigned integers raise ValueError."""
23+
stream = io.BytesIO()
24+
# Larger than max uint64
25+
obj = 2**64
26+
with pytest.raises(ValueError, match="uint too large"):
27+
pack_stream(stream, obj)
28+
29+
30+
def test_str_too_large():
31+
"""Test that strings exceeding max str32 length raise ValueError."""
32+
stream = io.BytesIO()
33+
test_str = "test"
34+
35+
with mock.patch("msgpack_stream._msgpack.len", return_value=2**32) as patch:
36+
with pytest.raises(ValueError, match="str too large"):
37+
pack_stream(stream, test_str)
38+
patch.assert_called_once()
39+
40+
41+
def test_bin_too_large():
42+
"""Test that bytes exceeding max bin32 length raise ValueError."""
43+
stream = io.BytesIO()
44+
test_bytes = b"test"
45+
46+
with mock.patch("msgpack_stream._msgpack.len", return_value=2**32) as patch:
47+
with pytest.raises(ValueError, match="bin too large"):
48+
pack_stream(stream, test_bytes)
49+
patch.assert_called_once()
50+
51+
52+
def test_map_too_large():
53+
"""Test that dicts exceeding max map32 length raise ValueError."""
54+
stream = io.BytesIO()
55+
test_dict = {"key": "value"}
56+
57+
with mock.patch("msgpack_stream._msgpack.len", return_value=2**32) as patch:
58+
with pytest.raises(ValueError, match="map too large"):
59+
pack_stream(stream, test_dict)
60+
61+
patch.assert_called_once()
62+
63+
64+
def test_array_too_large():
65+
"""Test that lists exceeding max array32 length raise ValueError."""
66+
stream = io.BytesIO()
67+
test_list = [1, 2, 3]
68+
69+
with mock.patch("msgpack_stream._msgpack.len", return_value=2**32) as patch:
70+
with pytest.raises(ValueError, match="array too large"):
71+
pack_stream(stream, test_list)
72+
73+
patch.assert_called_once()
74+
75+
76+
def test_naive_datetime():
77+
"""Test that naive datetime objects raise ValueError."""
78+
stream = io.BytesIO()
79+
dt = datetime(2023, 11, 19, 12, 0, 0) # No timezone
80+
with pytest.raises(ValueError, match="timezone-aware"):
81+
pack_stream(stream, dt)
82+
83+
84+
def test_ext_too_large():
85+
"""Test that ExtType data exceeding max ext32 length raises ValueError."""
86+
stream = io.BytesIO()
87+
test_data = b"test"
88+
test_ext = ExtType(code=42, data=test_data)
89+
90+
with mock.patch("msgpack_stream._msgpack.len", return_value=2**32) as patch:
91+
with pytest.raises(ValueError, match="ext too large"):
92+
pack_stream(stream, test_ext)
93+
94+
patch.assert_called_once()
95+
96+
97+
def test_unsupported_type():
98+
"""Test that unsupported types raise TypeError."""
99+
stream = io.BytesIO()
100+
101+
# Test with other unsupported types
102+
with pytest.raises(TypeError, match="unsupported type"):
103+
pack_stream(stream, set([1, 2, 3]))
104+
105+
with pytest.raises(TypeError, match="unsupported type"):
106+
pack_stream(stream, frozenset([1, 2, 3]))
107+
108+
with pytest.raises(TypeError, match="unsupported type"):
109+
pack_stream(stream, complex(1, 2))
110+
111+
112+
def test_invalid_first_byte_0xc1():
113+
"""Test that 0xC1 (never used) raises ValueError."""
114+
stream = io.BytesIO(b"\xc1")
115+
with pytest.raises(ValueError, match="invalid first byte"):
116+
unpack_stream(stream)
117+
118+
119+
def test_invalid_timestamp96_length():
120+
"""Test that timestamp96 with wrong length raises ValueError."""
121+
stream = io.BytesIO()
122+
# ext8 with code -1 (timestamp) but length != 4, 8, or 12
123+
# Format: 0xC7 (ext8), length, code (-1 = 0xFF), data
124+
stream.write(b"\xc7\x0d\xff" + b"\x00" * 13) # 13 bytes instead of 12
125+
stream.seek(0)
126+
with pytest.raises(ValueError, match="invalid timestamp length"):
127+
unpack_stream(stream)
128+
129+
130+
def test_invalid_fixext_timestamp_length():
131+
"""Test that fixext timestamp with invalid length raises ValueError."""
132+
stream = io.BytesIO()
133+
# fixext2 with timestamp code should fail (only 4, 8 allowed for fixext)
134+
# Format: 0xD5 (fixext2), code (-1 = 0xFF), data
135+
stream.write(b"\xd5\xff\x00\x00")
136+
stream.seek(0)
137+
with pytest.raises(ValueError, match="invalid timestamp length"):
138+
unpack_stream(stream)
139+
140+
141+
def test_unsupported_reserved_extension_ext8():
142+
"""Test that reserved extensions other than timestamp raise NotImplementedError."""
143+
stream = io.BytesIO()
144+
# ext8 with code -2 (reserved but not timestamp)
145+
# Format: 0xC7 (ext8), length, code (-2 = 0xFE), data
146+
stream.write(b"\xc7\x04\xfe" + b"\x00" * 4)
147+
stream.seek(0)
148+
with pytest.raises(NotImplementedError, match="unsupported reserved extension"):
149+
unpack_stream(stream)
150+
151+
152+
def test_unsupported_reserved_extension_fixext():
153+
"""Test that fixext with reserved code (not timestamp) raises NotImplementedError."""
154+
stream = io.BytesIO()
155+
# fixext1 with code -2 (reserved but not timestamp)
156+
# Format: 0xD4 (fixext1), code (-2 = 0xFE), data
157+
stream.write(b"\xd4\xfe\x00")
158+
stream.seek(0)
159+
with pytest.raises(NotImplementedError, match="unsupported reserved extension"):
160+
unpack_stream(stream)
161+
162+
163+
def test_invalid_first_byte():
164+
"""Test that 0xC1 (never used) raises ValueError."""
165+
for i in range(2**8):
166+
if i == 0xC1:
167+
continue
168+
stream = io.BytesIO(bytes([i]))
169+
try:
170+
unpack_stream(stream)
171+
except ValueError:
172+
raise
173+
except Exception:
174+
pass
175+
else:
176+
pass

0 commit comments

Comments
 (0)