Coverage for tests/solutions/test_solutions_logic.py: 100%
149 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, MagicMock
3from webapp.solutions.logic import (
4 get_solution_from_backend,
5 get_publisher_solutions,
6 group_solution_drafts,
7 get_solution_categories,
8 map_category_slugs_to_display,
9)
12@patch(
13 "webapp.solutions.logic.SOLUTIONS_API_BASE", "http://localhost:5000/api"
14)
15class TestSolutionsLogic(unittest.TestCase):
16 @patch("webapp.solutions.logic.session")
17 def test_get_solution_from_backend_success(self, mock_session):
18 mock_response = MagicMock()
19 mock_response.status_code = 200
20 mock_response.json.return_value = {
21 "uuid": "123",
22 "name": "Test Solution",
23 }
24 mock_session.get.return_value = mock_response
26 result = get_solution_from_backend("123")
28 self.assertEqual(result, {"uuid": "123", "name": "Test Solution"})
29 mock_session.get.assert_called_once()
31 @patch(
32 "webapp.solutions.logic.flask_session",
33 {"account": {"username": "testuser"}},
34 )
35 @patch("webapp.solutions.logic.make_authenticated_request")
36 @patch("webapp.solutions.logic.session")
37 def test_get_solution_from_backend_prefers_authenticated_solution(
38 self, mock_session, mock_auth_request
39 ):
40 mock_response = MagicMock()
41 mock_response.status_code = 200
42 mock_response.json.return_value = {
43 "hash": "123",
44 "name": "Test Solution",
45 "creator": {"email": "creator@example.com"},
46 }
47 mock_auth_request.return_value = mock_response
49 result = get_solution_from_backend("123", prefer_authenticated=True)
51 self.assertEqual(result["creator"]["email"], "creator@example.com")
52 mock_auth_request.assert_called_once()
53 mock_session.get.assert_not_called()
55 @patch(
56 "webapp.solutions.logic.flask_session",
57 {"account": {"username": "testuser"}},
58 )
59 @patch("webapp.solutions.logic.make_authenticated_request")
60 @patch("webapp.solutions.logic.session")
61 def test_get_solution_from_backend_falls_back_to_public_preview(
62 self, mock_session, mock_auth_request
63 ):
64 mock_auth_request.side_effect = Exception("Auth failed")
65 mock_response = MagicMock()
66 mock_response.status_code = 200
67 mock_response.json.return_value = {
68 "uuid": "123",
69 "name": "Test Solution",
70 }
71 mock_session.get.return_value = mock_response
73 result = get_solution_from_backend("123", prefer_authenticated=True)
75 self.assertEqual(result, {"uuid": "123", "name": "Test Solution"})
76 mock_session.get.assert_called_once()
78 @patch(
79 "webapp.solutions.logic.flask_session",
80 {"account": {"username": "testuser"}},
81 )
82 @patch("webapp.solutions.logic.make_authenticated_request")
83 @patch("webapp.solutions.logic.session")
84 def test_get_solution_from_backend_falls_back_when_auth_hash_not_found(
85 self, mock_session, mock_auth_request
86 ):
87 mock_auth_response = MagicMock()
88 mock_auth_response.status_code = 404
89 mock_auth_response.json.return_value = {"error": "Solution not found"}
90 mock_auth_request.return_value = mock_auth_response
92 mock_response = MagicMock()
93 mock_response.status_code = 200
94 mock_response.json.return_value = {
95 "uuid": "123",
96 "name": "Test Solution",
97 }
98 mock_session.get.return_value = mock_response
100 result = get_solution_from_backend("123", prefer_authenticated=True)
102 self.assertEqual(result, {"uuid": "123", "name": "Test Solution"})
103 mock_session.get.assert_called_once()
105 @patch("webapp.solutions.logic.session")
106 def test_get_solution_from_backend_not_found(self, mock_session):
107 mock_response = MagicMock()
108 mock_response.status_code = 404
109 mock_session.get.return_value = mock_response
111 result = get_solution_from_backend("nonexistent")
113 self.assertIsNone(result)
115 @patch("webapp.solutions.logic.session")
116 def test_get_solution_from_backend_exception(self, mock_session):
117 mock_session.get.side_effect = Exception("Network error")
119 result = get_solution_from_backend("123")
121 self.assertIsNone(result)
123 @patch("webapp.solutions.logic.flask_session", {})
124 @patch("webapp.solutions.logic.session")
125 @patch("webapp.solutions.logic.login")
126 def test_get_publisher_solutions_success(self, mock_login, mock_session):
127 mock_login.return_value = "fake-token"
128 mock_response = MagicMock()
129 mock_response.status_code = 200
130 mock_response.json.return_value = [
131 {"uuid": "solution1", "name": "Solution 1"}
132 ]
133 mock_session.request.return_value = mock_response
135 result = get_publisher_solutions("testuser")
137 self.assertEqual(len(result), 1)
138 self.assertEqual(result[0]["name"], "Solution 1")
139 mock_login.assert_called_once_with("testuser")
140 mock_session.request.assert_called_once()
142 @patch("webapp.solutions.auth.login")
143 def test_get_publisher_solutions_auth_failure(self, mock_login):
144 mock_login.side_effect = Exception("Auth failed")
146 result = get_publisher_solutions("testuser")
148 self.assertEqual(result, [])
150 def test_group_solution_drafts_attaches_draft_to_published(self):
151 published = {"name": "sol", "hash": "pub", "status": "published"}
152 draft = {
153 "name": "sol",
154 "hash": "draft",
155 "status": "draft",
156 "revision": 2,
157 "last_updated": "2026-01-01",
158 }
160 result = group_solution_drafts([published, draft])
162 self.assertEqual(len(result), 1)
163 self.assertEqual(result[0]["hash"], "pub")
164 self.assertEqual(
165 result[0]["draft_update"],
166 {"hash": "draft", "revision": 2, "last_updated": "2026-01-01"},
167 )
169 def test_group_solution_drafts_standalone_draft_without_published(self):
170 draft = {
171 "name": "sol",
172 "hash": "draft",
173 "status": "draft",
174 "revision": 2,
175 }
177 result = group_solution_drafts([draft])
179 self.assertEqual(len(result), 1)
180 self.assertTrue(result[0]["is_draft_update"])
182 def test_group_solution_drafts_rev1_draft_is_untouched(self):
183 draft = {
184 "name": "sol",
185 "hash": "draft",
186 "status": "draft",
187 "revision": 1,
188 }
190 result = group_solution_drafts([draft])
192 self.assertEqual(len(result), 1)
193 self.assertNotIn("is_draft_update", result[0])
194 self.assertNotIn("draft_update", result[0])
196 def test_group_solution_drafts_published_without_draft(self):
197 published = {"name": "sol", "hash": "pub", "status": "published"}
199 result = group_solution_drafts([published])
201 self.assertEqual(len(result), 1)
202 self.assertNotIn("draft_update", result[0])
204 def test_group_solution_drafts_preserves_unrelated_solutions(self):
205 published = {"name": "a", "hash": "a-pub", "status": "published"}
206 draft = {
207 "name": "a",
208 "hash": "a-draft",
209 "status": "draft",
210 "revision": 2,
211 }
212 other = {"name": "b", "hash": "b-pub", "status": "published"}
213 pending = {"name": "c", "hash": "c", "status": "pending_metadata_review"}
215 result = group_solution_drafts([published, draft, other, pending])
217 result_hashes = {solution["hash"] for solution in result}
218 self.assertEqual(result_hashes, {"a-pub", "b-pub", "c"})
219 attached = next(s for s in result if s["hash"] == "a-pub")
220 self.assertEqual(attached["draft_update"]["hash"], "a-draft")
223class TestSolutionCategories(unittest.TestCase):
224 CONFIG = [{"slug": "cloud", "name": "Cloud"}]
226 @patch("webapp.solutions.logic.get_store_categories")
227 def test_get_solution_categories_maps_store_shape(self, mock_store):
228 mock_store.return_value = [
229 {"name": "ai-ml", "display_name": "AI/ML"},
230 {"name": "monitoring", "display_name": "Monitoring"},
231 ]
233 result = get_solution_categories()
235 self.assertEqual(
236 result,
237 [
238 {"slug": "ai-ml", "name": "AI/ML"},
239 {"slug": "monitoring", "name": "Monitoring"},
240 ],
241 )
243 @patch("webapp.solutions.logic.CATEGORIES", CONFIG)
244 @patch("webapp.solutions.logic.get_store_categories")
245 def test_get_solution_categories_falls_back_to_config(self, mock_store):
246 mock_store.return_value = []
248 result = get_solution_categories()
250 self.assertEqual(result, [{"slug": "cloud", "name": "Cloud"}])
252 @patch("webapp.solutions.logic.CATEGORIES", CONFIG)
253 @patch("webapp.solutions.logic.get_store_categories")
254 def test_get_solution_categories_falls_back_on_store_error(
255 self, mock_store
256 ):
257 mock_store.side_effect = Exception("store down")
259 result = get_solution_categories()
261 self.assertEqual(result, [{"slug": "cloud", "name": "Cloud"}])
263 @patch("webapp.solutions.logic.get_store_categories")
264 def test_map_category_slugs_to_display_known_slugs(self, mock_store):
265 mock_store.return_value = [
266 {"name": "ai-ml", "display_name": "AI/ML"},
267 {"name": "monitoring", "display_name": "Monitoring"},
268 ]
270 result = map_category_slugs_to_display(["monitoring", "ai-ml"])
272 self.assertEqual(
273 result,
274 [
275 {"slug": "monitoring", "name": "Monitoring"},
276 {"slug": "ai-ml", "name": "AI/ML"},
277 ],
278 )
280 @patch("webapp.solutions.logic.get_store_categories")
281 def test_map_category_slugs_to_display_unknown_slug_fallback(
282 self, mock_store
283 ):
284 mock_store.return_value = [{"name": "ai-ml", "display_name": "AI/ML"}]
286 result = map_category_slugs_to_display(["made-up-slug"])
288 self.assertEqual(
289 result, [{"slug": "made-up-slug", "name": "Made Up Slug"}]
290 )
292 def test_map_category_slugs_to_display_empty(self):
293 self.assertEqual(map_category_slugs_to_display([]), [])
294 self.assertEqual(map_category_slugs_to_display(None), [])