|
3 | 3 | from django.test import Client, TestCase |
4 | 4 | from django.urls import reverse |
5 | 5 |
|
| 6 | +from shared.listeners.automatic_linkage import build_new_links |
| 7 | +from shared.listeners.notify_users import create_package_subscription_notifications |
| 8 | +from shared.models.cve import ( |
| 9 | + AffectedProduct, |
| 10 | + CveRecord, |
| 11 | + Description, |
| 12 | + Metric, |
| 13 | + Organization, |
| 14 | + Version, |
| 15 | +) |
| 16 | +from shared.models.linkage import CVEDerivationClusterProposal |
6 | 17 | from shared.models.nix_evaluation import ( |
7 | 18 | NixChannel, |
8 | 19 | NixDerivation, |
@@ -253,88 +264,53 @@ def test_user_cannot_unsubscribe_from_non_subscribed_package_htmx(self) -> None: |
253 | 264 | self.assertIn("package_subscriptions", response.context) |
254 | 265 | self.assertEqual(response.context["package_subscriptions"], []) |
255 | 266 |
|
256 | | - def test_subscription_center_shows_user_subscriptions(self) -> None: |
257 | | - """Test that the center displays user's current subscriptions""" |
258 | | - # First add some subscriptions via HTMX |
| 267 | + def test_user_receives_notification_for_subscribed_package_suggestion(self) -> None: |
| 268 | + """Test that users receive notifications when suggestions affect their subscribed packages""" |
| 269 | + # User subscribes to firefox package |
259 | 270 | add_url = reverse("webview:subscriptions:add") |
260 | 271 | self.client.post(add_url, {"package_name": "firefox"}, HTTP_HX_REQUEST="true") |
261 | 272 |
|
262 | | - # Add second package |
263 | | - self.client.post(add_url, {"package_name": "chromium"}, HTTP_HX_REQUEST="true") |
264 | | - |
265 | | - # Check subscription center shows both subscriptions |
266 | | - response = self.client.get(reverse("webview:subscriptions:center")) |
267 | | - self.assertEqual(response.status_code, 200) |
268 | | - |
269 | | - # Check context contains both subscriptions |
270 | | - self.assertIn("package_subscriptions", response.context) |
271 | | - subscriptions = response.context["package_subscriptions"] |
272 | | - self.assertIn("firefox", subscriptions) |
273 | | - self.assertIn("chromium", subscriptions) |
274 | | - self.assertEqual(len(subscriptions), 2) |
275 | | - |
276 | | - def test_subscription_center_shows_empty_state(self) -> None: |
277 | | - """Test empty state when user has no subscriptions""" |
278 | | - response = self.client.get(reverse("webview:subscriptions:center")) |
279 | | - self.assertEqual(response.status_code, 200) |
280 | | - |
281 | | - # Check context shows empty subscriptions |
282 | | - self.assertIn("package_subscriptions", response.context) |
283 | | - self.assertEqual(response.context["package_subscriptions"], []) |
284 | | - |
285 | | - def test_subscription_center_requires_login(self) -> None: |
286 | | - """Test that subscription center redirects when not logged in""" |
287 | | - # Logout the user |
288 | | - self.client.logout() |
289 | | - |
290 | | - response = self.client.get(reverse("webview:subscriptions:center")) |
291 | | - self.assertEqual(response.status_code, 302) |
292 | | - self.assertIn("login", response.url) |
293 | | - |
294 | | - # Test add endpoint also requires login |
295 | | - response = self.client.post( |
296 | | - reverse("webview:subscriptions:add"), {"package_name": "firefox"} |
| 273 | + # Create CVE and container - this should trigger automatic linkage and then notifications |
| 274 | + assigner = Organization.objects.create(uuid=1, short_name="test_org") |
| 275 | + cve_record = CveRecord.objects.create( |
| 276 | + cve_id="CVE-2025-0001", |
| 277 | + assigner=assigner, |
297 | 278 | ) |
298 | | - self.assertEqual(response.status_code, 302) |
299 | | - self.assertIn("login", response.url) |
300 | 279 |
|
301 | | - # Test remove endpoint also requires login |
302 | | - response = self.client.post( |
303 | | - reverse("webview:subscriptions:remove"), {"package_name": "firefox"} |
| 280 | + description = Description.objects.create(value="Test firefox vulnerability") |
| 281 | + metric = Metric.objects.create(format="cvssV3_1", raw_cvss_json={}) |
| 282 | + affected_product = AffectedProduct.objects.create(package_name="firefox") |
| 283 | + affected_product.versions.add( |
| 284 | + Version.objects.create(status=Version.Status.AFFECTED, version="120.0") |
304 | 285 | ) |
305 | | - self.assertEqual(response.status_code, 302) |
306 | | - self.assertIn("login", response.url) |
307 | 286 |
|
308 | | - # Test HTMX requests also require login |
309 | | - response = self.client.post( |
310 | | - reverse("webview:subscriptions:add"), |
311 | | - {"package_name": "firefox"}, |
312 | | - HTTP_HX_REQUEST="true", |
| 287 | + container = cve_record.container.create( |
| 288 | + provider=assigner, |
| 289 | + title="Firefox Security Issue", |
313 | 290 | ) |
314 | | - self.assertEqual(response.status_code, 302) |
315 | | - self.assertIn("login", response.url) |
316 | 291 |
|
317 | | - response = self.client.post( |
318 | | - reverse("webview:subscriptions:remove"), |
319 | | - {"package_name": "firefox"}, |
320 | | - HTTP_HX_REQUEST="true", |
321 | | - ) |
322 | | - self.assertEqual(response.status_code, 302) |
323 | | - self.assertIn("login", response.url) |
| 292 | + container.affected.set([affected_product]) |
| 293 | + container.descriptions.set([description]) |
| 294 | + container.metrics.set([metric]) |
324 | 295 |
|
325 | | - def test_user_unsubscribes_from_empty_package_name_fails_htmx(self) -> None: |
326 | | - """Test unsubscription fails for empty package name via HTMX""" |
327 | | - url = reverse("webview:subscriptions:remove") |
328 | | - response = self.client.post(url, {"package_name": ""}, HTTP_HX_REQUEST="true") |
| 296 | + # Trigger the linkage and notification system manually since pgpubsub triggers won't work in tests |
| 297 | + linkage_created = build_new_links(container) |
329 | 298 |
|
330 | | - # Should return 200 with component template for HTMX request |
| 299 | + if linkage_created: |
| 300 | + # Get the created proposal and trigger notifications |
| 301 | + suggestion = CVEDerivationClusterProposal.objects.get(cve=cve_record) |
| 302 | + create_package_subscription_notifications(suggestion) |
| 303 | + |
| 304 | + # Verify notification appears in notification center context |
| 305 | + response = self.client.get(reverse("webview:notifications:center")) |
331 | 306 | self.assertEqual(response.status_code, 200) |
332 | | - self.assertTemplateUsed(response, "subscriptions/components/packages.html") |
333 | 307 |
|
334 | | - # Check that error message is in context |
335 | | - self.assertIn("error_message", response.context) |
336 | | - self.assertIn("required", response.context["error_message"]) |
| 308 | + # Check that notification appears in context |
| 309 | + notifications = response.context["notifications"] |
| 310 | + self.assertEqual(len(notifications), 1) |
337 | 311 |
|
338 | | - # Verify empty subscriptions in context |
339 | | - self.assertIn("package_subscriptions", response.context) |
340 | | - self.assertEqual(response.context["package_subscriptions"], []) |
| 312 | + notification = notifications[0] |
| 313 | + self.assertEqual(notification.user, self.user) |
| 314 | + self.assertIn("firefox", notification.title) |
| 315 | + self.assertIn("CVE-2025-0001", notification.message) |
| 316 | + self.assertFalse(notification.is_read) # Should be unread initially |
0 commit comments