From d3956b694e9df1ddeea02bd02c50ef0b344d5d8c Mon Sep 17 00:00:00 2001 From: Rinat Sabitov Date: Tue, 14 Apr 2026 18:26:26 +0200 Subject: [PATCH] feat(core): extract url checker and support multiple api endpoints --- kc-compat.py | 14 ++++++++---- test_kc_compat.py | 56 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/kc-compat.py b/kc-compat.py index 3f78c4d..15f9956 100644 --- a/kc-compat.py +++ b/kc-compat.py @@ -89,20 +89,26 @@ def is_distro_supported(distro_name): return distro_name in SUPPORTED_DISTROS -def is_compat(): - url = 'http://patches.kernelcare.com/' + get_kernel_hash() + '/version' +def _check_url(url): try: urlopen(url) return True except HTTPError as e: if e.code == 404: return False - else: - raise + raise except URLError: raise +def is_compat(): + base = 'http://patches.kernelcare.com/' + get_kernel_hash() + for endpoint in ('/version', '/latest.v2', '/latest.v3'): + if _check_url(base + endpoint): + return True + return False + + def myprint(silent, message): if not silent: print(message) diff --git a/test_kc_compat.py b/test_kc_compat.py index 0eb5e84..f067e29 100644 --- a/test_kc_compat.py +++ b/test_kc_compat.py @@ -85,19 +85,69 @@ def test_is_distro_supported(self): assert kc_compat.is_distro_supported('debian') == False +class TestCheckUrl: + @patch.object(kc_compat, 'urlopen') + def test_check_url_success(self, mock_urlopen): + mock_urlopen.return_value = MagicMock() + assert kc_compat._check_url('http://example.com') == True + + @patch.object(kc_compat, 'urlopen') + def test_check_url_404(self, mock_urlopen): + mock_urlopen.side_effect = HTTPError(None, 404, 'Not Found', None, None) + assert kc_compat._check_url('http://example.com') == False + + @patch.object(kc_compat, 'urlopen') + def test_check_url_500_raises(self, mock_urlopen): + mock_urlopen.side_effect = HTTPError(None, 500, 'Server Error', None, None) + with pytest.raises(HTTPError): + kc_compat._check_url('http://example.com') + + @patch.object(kc_compat, 'urlopen') + def test_check_url_url_error_raises(self, mock_urlopen): + mock_urlopen.side_effect = URLError('Connection refused') + with pytest.raises(URLError): + kc_compat._check_url('http://example.com') + + class TestIsCompat: + BASE = 'http://patches.kernelcare.com/abcdef123456' + @patch.object(kc_compat, 'get_kernel_hash', return_value='abcdef123456') @patch.object(kc_compat, 'urlopen') - def test_is_compat_success(self, mock_urlopen, mock_hash): + def test_is_compat_version_hit(self, mock_urlopen, mock_hash): mock_urlopen.return_value = MagicMock() assert kc_compat.is_compat() == True - mock_urlopen.assert_called_once_with('http://patches.kernelcare.com/abcdef123456/version') + mock_urlopen.assert_called_once_with(self.BASE + '/version') + + @patch.object(kc_compat, 'get_kernel_hash', return_value='abcdef123456') + @patch.object(kc_compat, 'urlopen') + def test_is_compat_fallback_to_v2(self, mock_urlopen, mock_hash): + mock_urlopen.side_effect = [ + HTTPError(None, 404, 'Not Found', None, None), + MagicMock(), + ] + assert kc_compat.is_compat() == True + assert mock_urlopen.call_count == 2 + mock_urlopen.assert_called_with(self.BASE + '/latest.v2') + + @patch.object(kc_compat, 'get_kernel_hash', return_value='abcdef123456') + @patch.object(kc_compat, 'urlopen') + def test_is_compat_fallback_to_v3(self, mock_urlopen, mock_hash): + mock_urlopen.side_effect = [ + HTTPError(None, 404, 'Not Found', None, None), + HTTPError(None, 404, 'Not Found', None, None), + MagicMock(), + ] + assert kc_compat.is_compat() == True + assert mock_urlopen.call_count == 3 + mock_urlopen.assert_called_with(self.BASE + '/latest.v3') @patch.object(kc_compat, 'get_kernel_hash', return_value='abcdef123456') @patch.object(kc_compat, 'urlopen') - def test_is_compat_404_error_returns_false(self, mock_urlopen, mock_hash): + def test_is_compat_all_404_returns_false(self, mock_urlopen, mock_hash): mock_urlopen.side_effect = HTTPError(None, 404, 'Not Found', None, None) assert kc_compat.is_compat() == False + assert mock_urlopen.call_count == 3 @patch.object(kc_compat, 'get_kernel_hash', return_value='abcdef123456') @patch.object(kc_compat, 'urlopen')