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+
206+
207+ if __name__ == '__main__' :
208+ unittest .main ()
0 commit comments