Coverage for tests/store/test_package_has_sboms.py: 99%
106 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-18 22:11 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-18 22:11 +0000
1import unittest
2from unittest.mock import patch, Mock
3from webapp.store.views import package_has_sboms
6class TestPackageHasSboms(unittest.TestCase):
7 """Test cases for the package_has_sboms function."""
9 def test_package_has_sboms_no_revisions(self):
10 """Test that function returns False when revisions is empty or None."""
11 # Test with None
12 result = package_has_sboms(None, "test-package-123")
13 self.assertFalse(result)
15 # Test with empty list
16 result = package_has_sboms([], "test-package-123")
17 self.assertFalse(result)
19 @patch("webapp.store.views.requests.head")
20 @patch("webapp.store.views.device_gateway_sbom.get_endpoint_url")
21 def test_package_has_sboms_success_200(
22 self, mock_get_endpoint_url, mock_head
23 ):
24 """Test that function returns True when HEAD request returns 200."""
25 # Setup mocks
26 mock_get_endpoint_url.return_value = "https://example.com/sbom/path"
27 mock_response = Mock()
28 mock_response.status_code = 200
29 mock_head.return_value = mock_response
31 revisions = ["revision-123"]
32 package_id = "test-package-456"
34 result = package_has_sboms(revisions, package_id)
36 # Assertions
37 self.assertTrue(result)
38 expected_sbom_path = (
39 f"download/sbom_charm_{package_id}_{revisions[0]}.spdx2.3.json"
40 )
41 mock_get_endpoint_url.assert_called_once_with(expected_sbom_path)
42 mock_head.assert_called_once_with("https://example.com/sbom/path")
44 @patch("webapp.store.views.requests.head")
45 @patch("webapp.store.views.device_gateway_sbom.get_endpoint_url")
46 def test_package_has_sboms_success_302(
47 self, mock_get_endpoint_url, mock_head
48 ):
49 """Test that function returns True when HEAD request returns 302."""
50 # Setup mocks
51 mock_get_endpoint_url.return_value = "https://example.com/sbom/path"
52 mock_response = Mock()
53 mock_response.status_code = 302
54 mock_head.return_value = mock_response
56 revisions = ["revision-789"]
57 package_id = "test-package-abc"
59 result = package_has_sboms(revisions, package_id)
61 # Assertions
62 self.assertTrue(result)
63 expected_sbom_path = (
64 f"download/sbom_charm_{package_id}_{revisions[0]}.spdx2.3.json"
65 )
66 mock_get_endpoint_url.assert_called_once_with(expected_sbom_path)
67 mock_head.assert_called_once_with("https://example.com/sbom/path")
69 @patch("webapp.store.views.requests.head")
70 @patch("webapp.store.views.device_gateway_sbom.get_endpoint_url")
71 def test_package_has_sboms_failure_404(
72 self, mock_get_endpoint_url, mock_head
73 ):
74 """Test that function returns False when HEAD request returns 404."""
75 # Setup mocks
76 mock_get_endpoint_url.return_value = "https://example.com/sbom/path"
77 mock_response = Mock()
78 mock_response.status_code = 404
79 mock_head.return_value = mock_response
81 revisions = ["revision-456"]
82 package_id = "test-package-def"
84 result = package_has_sboms(revisions, package_id)
86 # Assertions
87 self.assertFalse(result)
88 expected_sbom_path = (
89 f"download/sbom_charm_{package_id}_{revisions[0]}.spdx2.3.json"
90 )
91 mock_get_endpoint_url.assert_called_once_with(expected_sbom_path)
92 mock_head.assert_called_once_with("https://example.com/sbom/path")
94 @patch("webapp.store.views.requests.head")
95 @patch("webapp.store.views.device_gateway_sbom.get_endpoint_url")
96 def test_package_has_sboms_failure_500(
97 self, mock_get_endpoint_url, mock_head
98 ):
99 """Test that function returns False when HEAD request returns 500."""
100 # Setup mocks
101 mock_get_endpoint_url.return_value = "https://example.com/sbom/path"
102 mock_response = Mock()
103 mock_response.status_code = 500
104 mock_head.return_value = mock_response
106 revisions = ["revision-999"]
107 package_id = "test-package-xyz"
109 result = package_has_sboms(revisions, package_id)
111 # Assertions
112 self.assertFalse(result)
113 expected_sbom_path = (
114 f"download/sbom_charm_{package_id}_{revisions[0]}.spdx2.3.json"
115 )
116 mock_get_endpoint_url.assert_called_once_with(expected_sbom_path)
117 mock_head.assert_called_once_with("https://example.com/sbom/path")
119 @patch("webapp.store.views.requests.head")
120 @patch("webapp.store.views.device_gateway_sbom.get_endpoint_url")
121 def test_package_has_sboms_multiple_revisions_uses_first(
122 self, mock_get_endpoint_url, mock_head
123 ):
124 """Test that function uses only the first revision when multiple"""
125 # Setup mocks
126 mock_get_endpoint_url.return_value = "https://example.com/sbom/path"
127 mock_response = Mock()
128 mock_response.status_code = 200
129 mock_head.return_value = mock_response
131 revisions = ["first-revision", "second-revision", "third-revision"]
132 package_id = "multi-revision-package"
134 result = package_has_sboms(revisions, package_id)
136 # Assertions
137 self.assertTrue(result)
138 # Should use only the first revision
139 expected_sbom_path = (
140 f"download/sbom_charm_{package_id}_first-revision.spdx2.3.json"
141 )
142 mock_get_endpoint_url.assert_called_once_with(expected_sbom_path)
143 mock_head.assert_called_once_with("https://example.com/sbom/path")
145 @patch("webapp.store.views.requests.head")
146 @patch("webapp.store.views.device_gateway_sbom.get_endpoint_url")
147 def test_package_has_sboms_request_exception_returns_false(
148 self, mock_get_endpoint_url, mock_head
149 ):
150 """Test function returns False when requests.head raises exception."""
151 # Setup mocks
152 mock_get_endpoint_url.return_value = "https://example.com/sbom/path"
153 mock_head.side_effect = Exception("Network error")
155 revisions = ["revision-error"]
156 package_id = "error-package"
158 # Function should handle exceptions gracefully and return False
159 with self.assertRaises(Exception):
160 package_has_sboms(revisions, package_id)
162 @patch("webapp.store.views.requests.head")
163 @patch("webapp.store.views.device_gateway_sbom.get_endpoint_url")
164 def test_package_has_sboms_edge_cases(
165 self, mock_get_endpoint_url, mock_head
166 ):
167 """Test edge cases with various status codes."""
168 test_cases = [
169 (100, False), # Continue
170 (201, False), # Created
171 (301, False), # Moved Permanently
172 (400, False), # Bad Request
173 (401, False), # Unauthorized
174 (403, False), # Forbidden
175 (418, False), # I'm a teapot
176 ]
178 for status_code, expected_result in test_cases:
179 with self.subTest(status_code=status_code):
180 # Reset mocks
181 mock_get_endpoint_url.reset_mock()
182 mock_head.reset_mock()
184 # Setup mocks
185 mock_get_endpoint_url.return_value = (
186 "https://example.com/sbom/path"
187 )
188 mock_response = Mock()
189 mock_response.status_code = status_code
190 mock_head.return_value = mock_response
192 revisions = ["test-revision"]
193 package_id = f"test-package-{status_code}"
195 result = package_has_sboms(revisions, package_id)
196 self.assertEqual(result, expected_result)
199if __name__ == "__main__":
200 unittest.main()