Compare commits
10 Commits
5a1df7345d
...
simplify
| Author | SHA1 | Date | |
|---|---|---|---|
| 4504e8a2a5 | |||
| 502dd85efb | |||
| 9e6e2b5a03 | |||
| c492b7bda6 | |||
| 0d7e63d3a2 | |||
| 0866e604bc | |||
| 753c00bc45 | |||
| 74d2f15d6a | |||
| 615908e569 | |||
| 55d467ee58 |
@@ -60,7 +60,7 @@ jobs:
|
||||
chmod +x /usr/local/bin/yq
|
||||
yq --version
|
||||
|
||||
- name: Read ${{ matrix.description }} image from deployment (old vs new)
|
||||
- name: Read ${{ matrix.description }} image from deployment
|
||||
id: img
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
@@ -540,5 +540,5 @@ digraph {
|
||||
Bei Fragen oder Problemen mit Diagrammen:
|
||||
1. Code auf https://kroki.io/ testen
|
||||
2. Syntax-Dokumentation des jeweiligen Diagrammtyps konsultieren
|
||||
3. Diagramm-Cache leeren: `python manage.py clear_diagram_cache`
|
||||
3. (Nur mit Shell-Zugriff auf Kubernetes-Pod möglich): Diagramm-Cache leeren: `python manage.py clear_diagram_cache`
|
||||
4. Bei technischen Problemen: Information Security Management BIT kontaktieren
|
||||
|
||||
@@ -151,6 +151,24 @@ LOGIN_URL = 'login'
|
||||
LOGIN_REDIRECT_URL = '/'
|
||||
LOGOUT_REDIRECT_URL = 'login'
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
"OPTIONS": {
|
||||
"min_length": 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||
},
|
||||
]
|
||||
|
||||
#LOGGING = {
|
||||
# "version": 1,
|
||||
# "handlers" :{
|
||||
|
||||
@@ -18,14 +18,14 @@ spec:
|
||||
fsGroupChangePolicy: "OnRootMismatch"
|
||||
initContainers:
|
||||
- name: loader
|
||||
image: git.baumann.gr/adebaumann/vui-data-loader:0.10
|
||||
image: git.baumann.gr/adebaumann/vui-data-loader:0.11
|
||||
command: [ "sh","-c","cp -n preload/preload.sqlite3 /data/db.sqlite3; chown -R 999:999 /data; ls -la /data; sleep 10; exit 0" ]
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
containers:
|
||||
- name: web
|
||||
image: git.baumann.gr/adebaumann/vui:0.968
|
||||
image: git.baumann.gr/adebaumann/vui:0.973
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
|
||||
Binary file not shown.
BIN
data/db.sqlite3
BIN
data/db.sqlite3
Binary file not shown.
@@ -94,9 +94,17 @@ class EinleitungInline(NestedStackedInline):
|
||||
|
||||
class VorgabeForm(forms.ModelForm):
|
||||
referenzen = TreeNodeMultipleChoiceField(queryset=Referenz.objects.all(), required=False)
|
||||
|
||||
class Meta:
|
||||
model = Vorgabe
|
||||
fields = '__all__'
|
||||
|
||||
def clean_thema(self):
|
||||
"""Validate that thema is provided."""
|
||||
thema = self.cleaned_data.get('thema')
|
||||
if not thema:
|
||||
raise forms.ValidationError('Thema ist ein Pflichtfeld. Bitte wählen Sie ein Thema aus.')
|
||||
return thema
|
||||
|
||||
class VorgabeInline(SortableInlineAdminMixin, NestedStackedInline):
|
||||
model = Vorgabe
|
||||
|
||||
@@ -71,6 +71,7 @@ class Command(BaseCommand):
|
||||
"name": name,
|
||||
"gueltigkeit_von": options["gueltigkeit_von"],
|
||||
"gueltigkeit_bis": options["gueltigkeit_bis"],
|
||||
"aktiv":False,
|
||||
},
|
||||
)
|
||||
if created:
|
||||
@@ -319,6 +320,7 @@ class Command(BaseCommand):
|
||||
thema=thema,
|
||||
titel=v["titel"],
|
||||
gueltigkeit_von=timezone.now().date(),
|
||||
order=0,
|
||||
)
|
||||
|
||||
# Stichworte
|
||||
|
||||
@@ -169,6 +169,12 @@ class Vorgabe(models.Model):
|
||||
"""
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
# Check that thema is provided
|
||||
if not self.thema_id:
|
||||
raise ValidationError({
|
||||
'thema': 'Thema ist ein Pflichtfeld. Bitte wählen Sie ein Thema aus.'
|
||||
})
|
||||
|
||||
# Check for conflicts with existing Vorgaben
|
||||
conflicts = self.find_conflicts()
|
||||
if conflicts:
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
{% if standard.history == True %}
|
||||
<div class="alert alert-warning" role="alert">
|
||||
{% if standard.is_future %}
|
||||
<strong>Zukünftige Version vom {{ standard.check_date }}</strong>
|
||||
{% else %}
|
||||
<strong>Historische Version vom {{ standard.check_date }}</strong>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
@@ -493,6 +493,38 @@ class ViewsTestCase(TestCase):
|
||||
url = reverse('standard_history', kwargs={'nummer': 'R01234'})
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_standard_history_past_date_shows_historische(self):
|
||||
"""Test that past dates show 'Historische Version'"""
|
||||
past_date = (date.today() - timedelta(days=30)).strftime('%Y-%m-%d')
|
||||
url = f'/dokumente/R01234/history/{past_date}/'
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, 'Historische Version vom')
|
||||
self.assertNotContains(response, 'Zukünftige Version vom')
|
||||
# Verify is_future flag is False
|
||||
self.assertFalse(response.context['standard'].is_future)
|
||||
|
||||
def test_standard_history_future_date_shows_zukuenftige(self):
|
||||
"""Test that future dates show 'Zukünftige Version'"""
|
||||
future_date = (date.today() + timedelta(days=30)).strftime('%Y-%m-%d')
|
||||
url = f'/dokumente/R01234/history/{future_date}/'
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, 'Zukünftige Version vom')
|
||||
self.assertNotContains(response, 'Historische Version vom')
|
||||
# Verify is_future flag is True
|
||||
self.assertTrue(response.context['standard'].is_future)
|
||||
|
||||
def test_standard_detail_current_has_no_version_label(self):
|
||||
"""Test that current view (no history) has no version label"""
|
||||
url = reverse('standard_detail', kwargs={'nummer': 'R01234'})
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertNotContains(response, 'Historische Version vom')
|
||||
self.assertNotContains(response, 'Zukünftige Version vom')
|
||||
# Verify history flag is False
|
||||
self.assertFalse(response.context['standard'].history)
|
||||
|
||||
|
||||
class URLPatternsTest(TestCase):
|
||||
@@ -761,6 +793,134 @@ class VorgabeSanityCheckTest(TestCase):
|
||||
self.assertIn("intersecting validity periods", report)
|
||||
|
||||
|
||||
class VorgabeThemaValidationTest(TestCase):
|
||||
"""Test cases for Vorgabe Thema validation"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data for Thema validation tests"""
|
||||
self.dokumententyp = Dokumententyp.objects.create(
|
||||
name="Standard IT-Sicherheit",
|
||||
verantwortliche_ve="SR-SUR-SEC"
|
||||
)
|
||||
self.dokument = Dokument.objects.create(
|
||||
nummer="R0066",
|
||||
dokumententyp=self.dokumententyp,
|
||||
name="IT Security Standard",
|
||||
aktiv=True
|
||||
)
|
||||
self.thema = Thema.objects.create(name="Organisation")
|
||||
|
||||
def test_vorgabe_with_thema_passes_validation(self):
|
||||
"""Test that Vorgabe with a valid Thema passes clean() validation"""
|
||||
vorgabe = Vorgabe(
|
||||
order=1,
|
||||
nummer=1,
|
||||
dokument=self.dokument,
|
||||
thema=self.thema,
|
||||
titel="Test Vorgabe",
|
||||
gueltigkeit_von=date.today()
|
||||
)
|
||||
# Should not raise any exception
|
||||
try:
|
||||
vorgabe.clean()
|
||||
except Exception as e:
|
||||
self.fail(f"clean() raised {e} unexpectedly!")
|
||||
|
||||
def test_vorgabe_without_thema_fails_validation(self):
|
||||
"""Test that Vorgabe without Thema fails clean() validation"""
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
vorgabe = Vorgabe(
|
||||
order=1,
|
||||
nummer=1,
|
||||
dokument=self.dokument,
|
||||
thema=None, # No Thema
|
||||
titel="Test Vorgabe",
|
||||
gueltigkeit_von=date.today()
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError) as context:
|
||||
vorgabe.clean()
|
||||
|
||||
# Check that the error message is about thema
|
||||
self.assertIn('thema', context.exception.message_dict)
|
||||
self.assertIn('Thema ist ein Pflichtfeld', str(context.exception))
|
||||
|
||||
def test_vorgabe_form_with_thema_is_valid(self):
|
||||
"""Test that VorgabeForm with Thema is valid"""
|
||||
from dokumente.admin import VorgabeForm
|
||||
|
||||
form_data = {
|
||||
'order': 1,
|
||||
'nummer': 1,
|
||||
'dokument': self.dokument.pk,
|
||||
'thema': self.thema.pk,
|
||||
'titel': 'Test Vorgabe',
|
||||
'gueltigkeit_von': date.today(),
|
||||
}
|
||||
form = VorgabeForm(data=form_data)
|
||||
self.assertTrue(form.is_valid(), f"Form errors: {form.errors}")
|
||||
|
||||
def test_vorgabe_form_without_thema_is_invalid(self):
|
||||
"""Test that VorgabeForm without Thema is invalid"""
|
||||
from dokumente.admin import VorgabeForm
|
||||
|
||||
form_data = {
|
||||
'order': 1,
|
||||
'nummer': 1,
|
||||
'dokument': self.dokument.pk,
|
||||
'thema': '', # Empty/missing Thema
|
||||
'titel': 'Test Vorgabe',
|
||||
'gueltigkeit_von': date.today(),
|
||||
}
|
||||
form = VorgabeForm(data=form_data)
|
||||
self.assertFalse(form.is_valid())
|
||||
self.assertIn('thema', form.errors)
|
||||
|
||||
def test_vorgabe_form_thema_error_message_is_german(self):
|
||||
"""Test that VorgabeForm shows German error message for missing Thema"""
|
||||
from dokumente.admin import VorgabeForm
|
||||
|
||||
form_data = {
|
||||
'order': 1,
|
||||
'nummer': 1,
|
||||
'dokument': self.dokument.pk,
|
||||
'thema': '', # Empty/missing Thema
|
||||
'titel': 'Test Vorgabe',
|
||||
'gueltigkeit_von': date.today(),
|
||||
}
|
||||
form = VorgabeForm(data=form_data)
|
||||
form.is_valid()
|
||||
|
||||
# Check that the error message is in German
|
||||
thema_errors = form.errors.get('thema', [])
|
||||
error_messages = ' '.join(thema_errors)
|
||||
self.assertTrue(
|
||||
'Pflichtfeld' in error_messages or 'pflichtfeld' in error_messages.lower(),
|
||||
f"Expected German error message about Pflichtfeld, got: {thema_errors}"
|
||||
)
|
||||
|
||||
def test_vorgabe_model_clean_error_message_is_german(self):
|
||||
"""Test that Vorgabe.clean() shows German error message for missing Thema"""
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
vorgabe = Vorgabe(
|
||||
order=1,
|
||||
nummer=1,
|
||||
dokument=self.dokument,
|
||||
thema=None,
|
||||
titel="Test Vorgabe",
|
||||
gueltigkeit_von=date.today()
|
||||
)
|
||||
|
||||
with self.assertRaises(ValidationError) as context:
|
||||
vorgabe.clean()
|
||||
|
||||
# Check error message is in German
|
||||
error_str = str(context.exception)
|
||||
self.assertIn('Thema ist ein Pflichtfeld', error_str)
|
||||
|
||||
|
||||
class SanityCheckManagementCommandTest(TestCase):
|
||||
"""Test cases for sanity_check_vorgaben management command"""
|
||||
|
||||
|
||||
@@ -29,9 +29,11 @@ def standard_detail(request, nummer,check_date=""):
|
||||
if check_date:
|
||||
check_date = calendar.parseDT(check_date)[0].date()
|
||||
standard.history = True
|
||||
standard.is_future = check_date > date.today()
|
||||
else:
|
||||
check_date = date.today()
|
||||
standard.history = False
|
||||
standard.is_future = False
|
||||
standard.check_date=check_date
|
||||
vorgaben = list(standard.vorgaben.order_by("thema","nummer").select_related("thema","dokument")) # convert queryset to list so we can attach attributes
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-sm-6 text-right">
|
||||
<p class="text-muted">Version {{ version|default:"0.968" }}</p>
|
||||
<p class="text-muted">Version {{ version|default:"0.973" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ certifi==2025.8.3
|
||||
charset-normalizer==3.4.3
|
||||
curtsies==0.4.3
|
||||
cwcwidth==0.1.10
|
||||
Django==5.2.8
|
||||
Django==5.2.9
|
||||
django-admin-sortable2==2.2.8
|
||||
django-js-asset==3.1.2
|
||||
django-mptt==0.17.0
|
||||
@@ -30,7 +30,7 @@ pyxdg==0.28
|
||||
requests==2.32.5
|
||||
six==1.17.0
|
||||
sqlparse==0.5.3
|
||||
urllib3==2.5.0
|
||||
urllib3==2.6.0
|
||||
wcwidth==0.2.13
|
||||
bleach==6.1.0
|
||||
coverage==7.6.1
|
||||
|
||||
Reference in New Issue
Block a user