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

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) 

10 

11 

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 

25 

26 result = get_solution_from_backend("123") 

27 

28 self.assertEqual(result, {"uuid": "123", "name": "Test Solution"}) 

29 mock_session.get.assert_called_once() 

30 

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 

48 

49 result = get_solution_from_backend("123", prefer_authenticated=True) 

50 

51 self.assertEqual(result["creator"]["email"], "creator@example.com") 

52 mock_auth_request.assert_called_once() 

53 mock_session.get.assert_not_called() 

54 

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 

72 

73 result = get_solution_from_backend("123", prefer_authenticated=True) 

74 

75 self.assertEqual(result, {"uuid": "123", "name": "Test Solution"}) 

76 mock_session.get.assert_called_once() 

77 

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 

91 

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 

99 

100 result = get_solution_from_backend("123", prefer_authenticated=True) 

101 

102 self.assertEqual(result, {"uuid": "123", "name": "Test Solution"}) 

103 mock_session.get.assert_called_once() 

104 

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 

110 

111 result = get_solution_from_backend("nonexistent") 

112 

113 self.assertIsNone(result) 

114 

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") 

118 

119 result = get_solution_from_backend("123") 

120 

121 self.assertIsNone(result) 

122 

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 

134 

135 result = get_publisher_solutions("testuser") 

136 

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() 

141 

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") 

145 

146 result = get_publisher_solutions("testuser") 

147 

148 self.assertEqual(result, []) 

149 

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 } 

159 

160 result = group_solution_drafts([published, draft]) 

161 

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 ) 

168 

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 } 

176 

177 result = group_solution_drafts([draft]) 

178 

179 self.assertEqual(len(result), 1) 

180 self.assertTrue(result[0]["is_draft_update"]) 

181 

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 } 

189 

190 result = group_solution_drafts([draft]) 

191 

192 self.assertEqual(len(result), 1) 

193 self.assertNotIn("is_draft_update", result[0]) 

194 self.assertNotIn("draft_update", result[0]) 

195 

196 def test_group_solution_drafts_published_without_draft(self): 

197 published = {"name": "sol", "hash": "pub", "status": "published"} 

198 

199 result = group_solution_drafts([published]) 

200 

201 self.assertEqual(len(result), 1) 

202 self.assertNotIn("draft_update", result[0]) 

203 

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"} 

214 

215 result = group_solution_drafts([published, draft, other, pending]) 

216 

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") 

221 

222 

223class TestSolutionCategories(unittest.TestCase): 

224 CONFIG = [{"slug": "cloud", "name": "Cloud"}] 

225 

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 ] 

232 

233 result = get_solution_categories() 

234 

235 self.assertEqual( 

236 result, 

237 [ 

238 {"slug": "ai-ml", "name": "AI/ML"}, 

239 {"slug": "monitoring", "name": "Monitoring"}, 

240 ], 

241 ) 

242 

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 = [] 

247 

248 result = get_solution_categories() 

249 

250 self.assertEqual(result, [{"slug": "cloud", "name": "Cloud"}]) 

251 

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") 

258 

259 result = get_solution_categories() 

260 

261 self.assertEqual(result, [{"slug": "cloud", "name": "Cloud"}]) 

262 

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 ] 

269 

270 result = map_category_slugs_to_display(["monitoring", "ai-ml"]) 

271 

272 self.assertEqual( 

273 result, 

274 [ 

275 {"slug": "monitoring", "name": "Monitoring"}, 

276 {"slug": "ai-ml", "name": "AI/ML"}, 

277 ], 

278 ) 

279 

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"}] 

285 

286 result = map_category_slugs_to_display(["made-up-slug"]) 

287 

288 self.assertEqual( 

289 result, [{"slug": "made-up-slug", "name": "Made Up Slug"}] 

290 ) 

291 

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), []) 

295