Skip to content

Commit b36a879

Browse files
author
Codex
committed
Add product security route tests
1 parent f23bf7d commit b36a879

4 files changed

Lines changed: 120 additions & 4 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
python manage.py seed_requirements || true
3636
python manage.py seed_product_security || true
3737
python manage.py check
38-
python manage.py test apps.core apps.reports
38+
python manage.py test apps.core apps.reports apps.product_security
3939
4040
nix-devshell:
4141
runs-on: ubuntu-24.04
@@ -56,7 +56,7 @@ jobs:
5656
cp .env.example .env
5757
mkdir -p static media staticfiles
5858
python manage.py check
59-
python manage.py test apps.core apps.reports
59+
python manage.py test apps.core apps.reports apps.product_security
6060
'
6161
6262
llm-runtime-build:

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ local-check:
1414
. .venv/bin/activate && python manage.py check
1515

1616
local-test:
17-
. .venv/bin/activate && python manage.py test apps.core apps.reports
17+
. .venv/bin/activate && python manage.py test apps.core apps.reports apps.product_security
1818

1919
dev-up:
2020
$(COMPOSE_DEV) up --build

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ make local-check
164164
make local-test
165165
```
166166

167-
`make local-test` deckt aktuell die Basis-Gesundheitschecks und die mandantenbezogenen Report-Views ab.
167+
`make local-test` deckt aktuell die Basis-Gesundheitschecks, mandantenbezogene Report-Views und die Product-Security-Routen ab.
168168

169169
## Support-Matrix
170170

apps/product_security/tests.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
from django.contrib.auth import get_user_model
2+
from django.test import TestCase
3+
from django.urls import reverse
4+
5+
from apps.organizations.models import Tenant
6+
from apps.product_security.models import Product, ProductFamily, ProductSecuritySnapshot
7+
8+
9+
User = get_user_model()
10+
11+
12+
class ProductSecurityViewTests(TestCase):
13+
def setUp(self):
14+
self.tenant_a = Tenant.objects.create(
15+
name="Tenant A",
16+
slug="tenant-a-ps",
17+
country="DE",
18+
develops_digital_products=True,
19+
)
20+
self.tenant_b = Tenant.objects.create(
21+
name="Tenant B",
22+
slug="tenant-b-ps",
23+
country="DE",
24+
develops_digital_products=True,
25+
)
26+
27+
self.user_a = User.objects.create_user(
28+
username="product-security-a",
29+
password="testpass123",
30+
tenant=self.tenant_a,
31+
)
32+
self.user_b = User.objects.create_user(
33+
username="product-security-b",
34+
password="testpass123",
35+
tenant=self.tenant_b,
36+
)
37+
38+
self.family_a = ProductFamily.objects.create(tenant=self.tenant_a, name="Familie A")
39+
self.family_b = ProductFamily.objects.create(tenant=self.tenant_b, name="Familie B")
40+
41+
self.product_a = Product.objects.create(
42+
tenant=self.tenant_a,
43+
family=self.family_a,
44+
name="Produkt A",
45+
has_digital_elements=True,
46+
)
47+
self.product_b = Product.objects.create(
48+
tenant=self.tenant_b,
49+
family=self.family_b,
50+
name="Produkt B",
51+
has_digital_elements=True,
52+
)
53+
54+
self.snapshot_a = ProductSecuritySnapshot.objects.create(
55+
tenant=self.tenant_a,
56+
product=self.product_a,
57+
cra_applicable=True,
58+
cra_readiness_percent=72,
59+
threat_model_coverage_percent=40,
60+
psirt_readiness_percent=55,
61+
open_vulnerability_count=2,
62+
critical_vulnerability_count=1,
63+
summary="Snapshot Tenant A",
64+
)
65+
self.snapshot_b = ProductSecuritySnapshot.objects.create(
66+
tenant=self.tenant_b,
67+
product=self.product_b,
68+
cra_applicable=True,
69+
cra_readiness_percent=33,
70+
summary="Snapshot Tenant B",
71+
)
72+
73+
def test_product_list_only_shows_current_tenant_products_and_snapshots(self):
74+
self.client.force_login(self.user_a)
75+
76+
response = self.client.get(reverse("product_security:list"))
77+
78+
self.assertEqual(response.status_code, 200)
79+
self.assertEqual(list(response.context["products"]), [self.product_a])
80+
self.assertEqual(list(response.context["snapshots"]), [self.snapshot_a])
81+
self.assertContains(response, "Produkt A")
82+
self.assertNotContains(response, "Produkt B")
83+
84+
def test_product_detail_blocks_foreign_tenant_access(self):
85+
self.client.force_login(self.user_a)
86+
87+
response = self.client.get(reverse("product_security:detail", args=[self.product_b.pk]))
88+
89+
self.assertEqual(response.status_code, 404)
90+
91+
def test_product_detail_generates_roadmap_for_current_tenant(self):
92+
self.client.force_login(self.user_a)
93+
94+
response = self.client.get(reverse("product_security:detail", args=[self.product_a.pk]))
95+
96+
self.assertEqual(response.status_code, 200)
97+
roadmap = self.product_a.roadmaps.first()
98+
self.assertIsNotNone(roadmap)
99+
self.assertGreater(roadmap.tasks.count(), 0)
100+
self.assertContains(response, "Product-Security-Roadmap")
101+
102+
def test_product_roadmap_blocks_foreign_tenant_access(self):
103+
self.client.force_login(self.user_a)
104+
105+
response = self.client.get(reverse("product_security:roadmap", args=[self.product_b.pk]))
106+
107+
self.assertEqual(response.status_code, 404)
108+
109+
def test_product_roadmap_renders_generated_tasks(self):
110+
self.client.force_login(self.user_a)
111+
112+
response = self.client.get(reverse("product_security:roadmap", args=[self.product_a.pk]))
113+
114+
self.assertEqual(response.status_code, 200)
115+
self.assertContains(response, "Governance")
116+
self.assertContains(response, "Produkt-Scope, Verantwortlichkeiten und Security Sign-off festigen")

0 commit comments

Comments
 (0)