From 9518e2f4c10501fdaf60077874c362cd0c1d3c0c Mon Sep 17 00:00:00 2001 From: Jens Kleineheismann Date: Tue, 26 May 2026 15:21:01 +0200 Subject: [PATCH] Improved tests --- .coveragerc | 1 + dav_auth/tests/test_validators.py | 48 ++++++++++++++++++++----------- dav_auth/tests/test_views.py | 38 +++++++++++++++++++++--- 3 files changed, 67 insertions(+), 20 deletions(-) diff --git a/.coveragerc b/.coveragerc index af2ad41..da96129 100644 --- a/.coveragerc +++ b/.coveragerc @@ -6,6 +6,7 @@ source = dav_registration omit = dav_base/tests/generic.py + dav_base/tests/utils.py dav_auth/tests/generic.py dav_events/tests/generic.py dav_registration/tests/generic.py diff --git a/dav_auth/tests/test_validators.py b/dav_auth/tests/test_validators.py index 6de1f77..c4680f4 100644 --- a/dav_auth/tests/test_validators.py +++ b/dav_auth/tests/test_validators.py @@ -26,12 +26,9 @@ class PasswordScoreValidatorTestCase(SimpleTestCase): validator = PasswordScoreValidator(min_classes=0) for password in passwords: - try: + with self.assertRaises(ValidationError, msg=password) as cm: validator.validate(password) - except ValidationError as e: - self.assertEqual('too_little_score', e.code) - else: - self.fail('%s: no validation error was raised' % password) + self.assertEqual('too_little_score', cm.exception.code) def test_too_few_classes(self): passwords = [ @@ -49,12 +46,9 @@ class PasswordScoreValidatorTestCase(SimpleTestCase): validator = PasswordScoreValidator(min_score=0, min_classes=4) for password in passwords: - try: + with self.assertRaises(ValidationError, msg=password) as cm: validator.validate(password) - except ValidationError as e: - self.assertEqual('too_few_classes', e.code) - else: - self.fail('%s: no validation error was raised' % password) + self.assertEqual('too_few_classes', cm.exception.code) def test_valid(self): passwords = [ @@ -75,8 +69,15 @@ class PasswordScoreValidatorTestCase(SimpleTestCase): for password in passwords: try: validator.validate(password) - except ValidationError as e: - self.fail(e) + except ValidationError as e: # pragma: no cover + self.fail('%s: %s' % (password, e)) + + def test_help_text(self): + validator = PasswordScoreValidator() + help_text = validator.get_help_text() + self.assertIn('The password must get a minimum score of 18 points.', help_text) + self.assertIn('Also the password must contain characters from 2 different character classes' + ' (i.e. lower, upper, digits, others).', help_text) class CustomWordlistPasswordValidatorTestCase(SimpleTestCase): @@ -114,7 +115,7 @@ class CustomWordlistPasswordValidatorTestCase(SimpleTestCase): for error in errors: self.assertIn(error, expected_errors) else: - self.fail('%s: no validation error was raised' % password) + self.fail('%s: no validation error was raised' % password) # pragma: no cover def test_valid(self): passwords = [ @@ -128,9 +129,15 @@ class CustomWordlistPasswordValidatorTestCase(SimpleTestCase): for password in passwords: try: validator.validate(password) - except ValidationError as e: + except ValidationError as e: # pragma: no cover self.fail(e) + def test_help_text(self): + validator = CustomWordlistPasswordValidator() + help_text = validator.get_help_text() + self.assertIn('The password must not contain some specific words,', help_text) + self.assertIn('All words are matched case insensitive.', help_text) + class CharacterClassPasswordValidatorTestCase(SimpleTestCase): def setUp(self): @@ -215,7 +222,7 @@ class CharacterClassPasswordValidatorTestCase(SimpleTestCase): for error in errors: self.assertIn(error, expected_errors) else: - self.fail('%s: no validation error was raised' % password) + self.fail('%s: no validation error was raised' % password) # pragma: no cover def test_valid(self): valid_passwords = ['abCD12+-'] @@ -223,5 +230,14 @@ class CharacterClassPasswordValidatorTestCase(SimpleTestCase): for password in valid_passwords: try: validator.validate(password) - except ValidationError as e: + except ValidationError as e: # pragma: no cover self.fail(e) + + def test_help_text(self): + validator = self.validator + help_text = validator.get_help_text() + self.assertIn('The password must contain at least 2 characters from a-z.', help_text) + self.assertIn('The password must contain at least 2 characters from A-Z.', help_text) + self.assertIn('The password must contain at least 2 digits from 0-9.', help_text) + self.assertIn('The password must contain at least 2 non alpha numeric characters.', help_text) + diff --git a/dav_auth/tests/test_views.py b/dav_auth/tests/test_views.py index a086189..e540672 100644 --- a/dav_auth/tests/test_views.py +++ b/dav_auth/tests/test_views.py @@ -10,7 +10,8 @@ from django.urls import reverse from ..forms import LoginForm, SetPasswordForm, CreateAndSendPasswordForm TEST_USERNAME = 'root@localhost' -TEST_PASSWORD = 'me||ön 21ABll' +TEST_STRONG_PASSWORD = 'me||ön 21ABll' +TEST_WEAK_PASSWORD = 'mellon' TEST_EMAIL = TEST_USERNAME @@ -31,16 +32,21 @@ class ViewsTestCase(TestCase): # Some messages cls.wrong_credentials_message = gettext('Benutzername oder Passwort falsch.') + cls.login_message = gettext('Benutzer angemeldet: %(username)s') cls.logout_message = gettext('Benutzer abgemeldet.') cls.set_password_message = gettext('Passwort gespeichert.') + cls.weak_password_warning_message = 'Dein Passwort entspricht nicht mehr den aktuellen Passwortrichtlinien.' def setUp(self): super().setUp() # Need a test user self.test_username = TEST_USERNAME - self.test_password = TEST_PASSWORD + self.test_password = TEST_STRONG_PASSWORD + self.test_email = TEST_EMAIL model = get_user_model() - self.user = model.objects.create_user(username=TEST_USERNAME, password=TEST_PASSWORD, email=TEST_EMAIL) + self.user = model.objects.create_user(username=self.test_username, + password=self.test_password, + email=self.test_email) def test_integrated_login_get(self): response = self.client.get(self.login_url) @@ -74,7 +80,7 @@ class ViewsTestCase(TestCase): def test_integrated_login_succeed(self): username = self.user.username - message = gettext('Benutzer angemeldet: %(username)s') % {'username': username} + message = self.login_message % {'username': username} response = self.client.post(self.login_url, {'username': username, 'password': self.test_password}) self.assertEqual(response.status_code, 302) @@ -82,6 +88,30 @@ class ViewsTestCase(TestCase): response = self.client.get(response.url) self.assertContains(response, message) + self.assertNotContains(response, self.weak_password_warning_message) + + self.assertTrue(response.context['user'].is_authenticated, 'Login failed') + + def test_integrated_login_weak_password(self): + username = self.user.username + password = TEST_WEAK_PASSWORD + + message = self.login_message % {'username': username} + + user_model = get_user_model() + user = user_model.objects.get(username=username) + user.set_password(password) + user.save() + + with self.assertLogs('dav_auth.views', level='WARNING') as cm: + response = self.client.post(self.login_url, {'username': username, 'password': password}) + self.assertStartsWith(cm.output[0], 'WARNING:dav_auth.views:Weak password') + self.assertEqual(response.status_code, 302) + self.assertEqual(response.url, self.login_redirect_url) + + response = self.client.get(response.url) + self.assertContains(response, message) + self.assertContains(response, self.weak_password_warning_message) self.assertTrue(response.context['user'].is_authenticated, 'Login failed')