77
88import six
99from dateutil .tz import tzutc
10+ from parameterized import parameterized
1011from pydantic import BaseModel
1112from pydantic .v1 import BaseModel as BaseModelV1
1213
1718
1819
1920class TestUtils (unittest .TestCase ):
21+ @parameterized .expand (
22+ [
23+ ("naive datetime should be naive" , True ),
24+ ("timezone-aware datetime should not be naive" , False ),
25+ ]
26+ )
27+ def test_is_naive (self , _name : str , expected_naive : bool ):
28+ if expected_naive :
29+ dt = datetime .now () # naive datetime
30+ else :
31+ dt = datetime .now (tz = tzutc ()) # timezone-aware datetime
32+
33+ assert utils .is_naive (dt ) is expected_naive
34+
2035 def test_timezone_utils (self ):
2136 now = datetime .now ()
2237 utcnow = datetime .now (tz = tzutc ())
23- assert utils .is_naive (now ) is True
24- assert utils .is_naive (utcnow ) is False
2538
2639 fixed = utils .guess_timezone (now )
2740 assert utils .is_naive (fixed ) is False
@@ -70,20 +83,24 @@ def test_clean_with_dates(self):
7083 assert dict_with_dates == utils .clean (dict_with_dates )
7184
7285 def test_bytes (self ):
73- if six .PY3 :
74- item = bytes (10 )
75- else :
76- item = bytearray (10 )
77-
86+ item = bytes (10 )
7887 utils .clean (item )
88+ assert utils .clean (item ) == '\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 '
7989
8090 def test_clean_fn (self ):
8191 cleaned = utils .clean ({"fn" : lambda x : x , "number" : 4 })
8292 assert cleaned == {"fn" : None , "number" : 4 }
8393
84- def test_remove_slash (self ):
85- assert "http://posthog.io" == utils .remove_trailing_slash ("http://posthog.io/" )
86- assert "http://posthog.io" == utils .remove_trailing_slash ("http://posthog.io" )
94+ @parameterized .expand (
95+ [
96+ ("http://posthog.io/" , "http://posthog.io" ),
97+ ("http://posthog.io" , "http://posthog.io" ),
98+ ("https://example.com/path/" , "https://example.com/path" ),
99+ ("https://example.com/path" , "https://example.com/path" ),
100+ ]
101+ )
102+ def test_remove_slash (self , input_url , expected_url ):
103+ assert expected_url == utils .remove_trailing_slash (input_url )
87104
88105 def test_clean_pydantic (self ):
89106 class ModelV2 (BaseModel ):
@@ -108,12 +125,16 @@ class NestedModel(BaseModel):
108125 "foo" : {"foo" : "1" , "bar" : 2 , "baz" : "3" }
109126 }
110127
128+ def test_clean_pydantic_like_class (self ) -> None :
111129 class Dummy :
112- def model_dump (self , required_param ) :
113- pass
130+ def model_dump (self , required_param : str ) -> dict :
131+ return {}
114132
115- # Skips a class with a defined non-Pydantic `model_dump` method.
116- assert utils .clean ({"test" : Dummy ()}) == {}
133+ # previously python 2 code would cause an error while cleaning,
134+ # and this entire object would be None, and we would log an error
135+ # let's allow ourselves to clean `Dummy` as None,
136+ # without blatting the `test` key
137+ assert utils .clean ({"test" : Dummy ()}) == {"test" : None }
117138
118139 def test_clean_dataclass (self ):
119140 @dataclass
@@ -155,6 +176,21 @@ class TestDataClass:
155176
156177
157178class TestSizeLimitedDict (unittest .TestCase ):
179+ @parameterized .expand (
180+ [
181+ (5 , 20 ),
182+ (10 , 100 ),
183+ (3 , 15 ),
184+ ]
185+ )
186+ def test_size_limited_dict_with_different_sizes (self , size , iterations ):
187+ values = utils .SizeLimitedDict (size , lambda _ : - 1 )
188+
189+ for i in range (iterations ):
190+ values [i ] = i
191+ assert values [i ] == i
192+ assert len (values ) == min (i + 1 , size )
193+
158194 def test_size_limited_dict (self ):
159195 size = 10
160196 values = utils .SizeLimitedDict (size , lambda _ : - 1 )
0 commit comments