1
+ """Tests for version checking functionality."""
2
+
3
+ import unittest
4
+ from unittest .mock import Mock , patch , MagicMock
5
+ from packaging import version
6
+
7
+ from codeflash .code_utils .version_check import (
8
+ get_latest_version_from_pypi ,
9
+ check_for_newer_minor_version ,
10
+ _version_cache ,
11
+ _cache_duration
12
+ )
13
+
14
+
15
+ class TestVersionCheck (unittest .TestCase ):
16
+ """Test cases for version checking functionality."""
17
+
18
+ def setUp (self ):
19
+ """Reset version cache before each test."""
20
+ _version_cache ["version" ] = None
21
+ _version_cache ["timestamp" ] = 0
22
+
23
+ def tearDown (self ):
24
+ """Clean up after each test."""
25
+ _version_cache ["version" ] = None
26
+ _version_cache ["timestamp" ] = 0
27
+
28
+ @patch ('codeflash.code_utils.version_check.requests.get' )
29
+ def test_get_latest_version_from_pypi_success (self , mock_get ):
30
+ """Test successful version fetch from PyPI."""
31
+ # Mock successful response
32
+ mock_response = Mock ()
33
+ mock_response .status_code = 200
34
+ mock_response .json .return_value = {"info" : {"version" : "1.2.3" }}
35
+ mock_get .return_value = mock_response
36
+
37
+ result = get_latest_version_from_pypi ()
38
+
39
+ self .assertEqual (result , "1.2.3" )
40
+ mock_get .assert_called_once_with (
41
+ "https://pypi.org/pypi/codeflash/json" ,
42
+ timeout = 2
43
+ )
44
+
45
+ @patch ('codeflash.code_utils.version_check.requests.get' )
46
+ def test_get_latest_version_from_pypi_http_error (self , mock_get ):
47
+ """Test handling of HTTP error responses."""
48
+ # Mock HTTP error response
49
+ mock_response = Mock ()
50
+ mock_response .status_code = 404
51
+ mock_get .return_value = mock_response
52
+
53
+ result = get_latest_version_from_pypi ()
54
+
55
+ self .assertIsNone (result )
56
+
57
+ @patch ('codeflash.code_utils.version_check.requests.get' )
58
+ def test_get_latest_version_from_pypi_network_error (self , mock_get ):
59
+ """Test handling of network errors."""
60
+ # Mock network error
61
+ mock_get .side_effect = Exception ("Network error" )
62
+
63
+ result = get_latest_version_from_pypi ()
64
+
65
+ self .assertIsNone (result )
66
+
67
+ @patch ('codeflash.code_utils.version_check.requests.get' )
68
+ def test_get_latest_version_from_pypi_invalid_response (self , mock_get ):
69
+ """Test handling of invalid response format."""
70
+ # Mock invalid response format
71
+ mock_response = Mock ()
72
+ mock_response .status_code = 200
73
+ mock_response .json .return_value = {"invalid" : "format" }
74
+ mock_get .return_value = mock_response
75
+
76
+ result = get_latest_version_from_pypi ()
77
+
78
+ self .assertIsNone (result )
79
+
80
+ @patch ('codeflash.code_utils.version_check.requests.get' )
81
+ def test_get_latest_version_from_pypi_caching (self , mock_get ):
82
+ """Test that version caching works correctly."""
83
+ # Mock successful response
84
+ mock_response = Mock ()
85
+ mock_response .status_code = 200
86
+ mock_response .json .return_value = {"info" : {"version" : "1.2.3" }}
87
+ mock_get .return_value = mock_response
88
+
89
+ # First call should hit the network
90
+ result1 = get_latest_version_from_pypi ()
91
+ self .assertEqual (result1 , "1.2.3" )
92
+ self .assertEqual (mock_get .call_count , 1 )
93
+
94
+ # Second call should use cache
95
+ result2 = get_latest_version_from_pypi ()
96
+ self .assertEqual (result2 , "1.2.3" )
97
+ self .assertEqual (mock_get .call_count , 1 ) # Still only 1 call
98
+
99
+ @patch ('codeflash.code_utils.version_check.requests.get' )
100
+ def test_get_latest_version_from_pypi_cache_expiry (self , mock_get ):
101
+ """Test that cache expires after the specified duration."""
102
+ import time
103
+
104
+ # Mock successful response
105
+ mock_response = Mock ()
106
+ mock_response .status_code = 200
107
+ mock_response .json .return_value = {"info" : {"version" : "1.2.3" }}
108
+ mock_get .return_value = mock_response
109
+
110
+ # First call
111
+ result1 = get_latest_version_from_pypi ()
112
+ self .assertEqual (result1 , "1.2.3" )
113
+
114
+ # Manually expire the cache
115
+ _version_cache ["timestamp" ] = time .time () - _cache_duration - 1
116
+
117
+ # Second call should hit the network again
118
+ result2 = get_latest_version_from_pypi ()
119
+ self .assertEqual (result2 , "1.2.3" )
120
+ self .assertEqual (mock_get .call_count , 2 )
121
+
122
+ @patch ('codeflash.code_utils.version_check.get_latest_version_from_pypi' )
123
+ @patch ('codeflash.code_utils.version_check.console' )
124
+ @patch ('codeflash.code_utils.version_check.__version__' , '1.0.0' )
125
+ def test_check_for_newer_minor_version_newer_available (self , mock_console , mock_get_version ):
126
+ """Test warning message when newer minor version is available."""
127
+ mock_get_version .return_value = "1.1.0"
128
+
129
+ check_for_newer_minor_version ()
130
+
131
+ mock_console .print .assert_called_once ()
132
+ call_args = mock_console .print .call_args [0 ][0 ]
133
+ self .assertIn ("ℹ️ A newer version of Codeflash is available!" , call_args )
134
+ self .assertIn ("Current version: 1.0.0" , call_args )
135
+ self .assertIn ("Latest version: 1.1.0" , call_args )
136
+
137
+ @patch ('codeflash.code_utils.version_check.get_latest_version_from_pypi' )
138
+ @patch ('codeflash.code_utils.version_check.console' )
139
+ @patch ('codeflash.code_utils.version_check.__version__' , '1.0.0' )
140
+ def test_check_for_newer_minor_version_newer_major_available (self , mock_console , mock_get_version ):
141
+ """Test warning message when newer major version is available."""
142
+ mock_get_version .return_value = "2.0.0"
143
+
144
+ check_for_newer_minor_version ()
145
+
146
+ mock_console .print .assert_called_once ()
147
+ call_args = mock_console .print .call_args [0 ][0 ]
148
+ self .assertIn ("ℹ️ A newer version of Codeflash is available!" , call_args )
149
+
150
+ @patch ('codeflash.code_utils.version_check.get_latest_version_from_pypi' )
151
+ @patch ('codeflash.code_utils.version_check.console' )
152
+ @patch ('codeflash.code_utils.version_check.__version__' , '1.1.0' )
153
+ def test_check_for_newer_minor_version_no_newer_available (self , mock_console , mock_get_version ):
154
+ """Test no warning when no newer version is available."""
155
+ mock_get_version .return_value = "1.0.0"
156
+
157
+ check_for_newer_minor_version ()
158
+
159
+ mock_console .print .assert_not_called ()
160
+
161
+ @patch ('codeflash.code_utils.version_check.get_latest_version_from_pypi' )
162
+ @patch ('codeflash.code_utils.version_check.console' )
163
+ @patch ('codeflash.code_utils.version_check.__version__' , '1.0.0' )
164
+ def test_check_for_newer_minor_version_patch_update_ignored (self , mock_console , mock_get_version ):
165
+ """Test that patch updates don't trigger warnings."""
166
+ mock_get_version .return_value = "1.0.1"
167
+
168
+ check_for_newer_minor_version ()
169
+
170
+ mock_console .print .assert_not_called ()
171
+
172
+ @patch ('codeflash.code_utils.version_check.get_latest_version_from_pypi' )
173
+ @patch ('codeflash.code_utils.version_check.console' )
174
+ @patch ('codeflash.code_utils.version_check.__version__' , '1.0.0' )
175
+ def test_check_for_newer_minor_version_same_version (self , mock_console , mock_get_version ):
176
+ """Test no warning when versions are the same."""
177
+ mock_get_version .return_value = "1.0.0"
178
+
179
+ check_for_newer_minor_version ()
180
+
181
+ mock_console .print .assert_not_called ()
182
+
183
+ @patch ('codeflash.code_utils.version_check.get_latest_version_from_pypi' )
184
+ @patch ('codeflash.code_utils.version_check.console' )
185
+ @patch ('codeflash.code_utils.version_check.__version__' , '1.0.0' )
186
+ def test_check_for_newer_minor_version_no_latest_version (self , mock_console , mock_get_version ):
187
+ """Test no warning when latest version cannot be fetched."""
188
+ mock_get_version .return_value = None
189
+
190
+ check_for_newer_minor_version ()
191
+
192
+ mock_console .print .assert_not_called ()
193
+
194
+ @patch ('codeflash.code_utils.version_check.get_latest_version_from_pypi' )
195
+ @patch ('codeflash.code_utils.version_check.console' )
196
+ @patch ('codeflash.code_utils.version_check.__version__' , '1.0.0' )
197
+ def test_check_for_newer_minor_version_invalid_version_format (self , mock_console , mock_get_version ):
198
+ """Test handling of invalid version format."""
199
+ mock_get_version .return_value = "invalid-version"
200
+
201
+ check_for_newer_minor_version ()
202
+
203
+ mock_console .print .assert_not_called ()
204
+
205
+ @patch ('codeflash.code_utils.version_check.get_latest_version_from_pypi' )
206
+ @patch ('codeflash.code_utils.version_check.console' )
207
+ def test_check_for_newer_minor_version_disable_check (self , mock_console , mock_get_version ):
208
+ """Test that version check is skipped when disable_check is True."""
209
+ check_for_newer_minor_version (disable_check = True )
210
+
211
+ mock_get_version .assert_not_called ()
212
+ mock_console .print .assert_not_called ()
213
+
214
+ @patch ('codeflash.code_utils.version_check.get_latest_version_from_pypi' )
215
+ @patch ('codeflash.code_utils.version_check.console' )
216
+ def test_check_for_newer_minor_version_disable_check_false (self , mock_console , mock_get_version ):
217
+ """Test that version check runs when disable_check is False."""
218
+ mock_get_version .return_value = None
219
+
220
+ check_for_newer_minor_version (disable_check = False )
221
+
222
+ mock_get_version .assert_called_once ()
223
+ mock_console .print .assert_not_called ()
224
+
225
+
226
+ if __name__ == '__main__' :
227
+ unittest .main ()
0 commit comments