diff --git a/axes/tests/test_handlers.py b/axes/tests/test_handlers.py index 838c78a..29e849d 100644 --- a/axes/tests/test_handlers.py +++ b/axes/tests/test_handlers.py @@ -15,9 +15,19 @@ class ProxyHandlerTestCase(TestCase): self.instance = MagicMock() @patch('axes.signals.ProxyHandler.implementation', None) - def test_initialize(self): + @patch('axes.signals.import_string', return_value=AxesHandler) + def test_initialize(self, importer): + self.assertEqual(0, importer.call_count) self.assertIsNone(ProxyHandler.implementation) + ProxyHandler.initialize() + + self.assertEqual(1, importer.call_count) + self.assertIsInstance(ProxyHandler.implementation, AxesHandler) + + ProxyHandler.initialize() + + self.assertEqual(1, importer.call_count) self.assertIsInstance(ProxyHandler.implementation, AxesHandler) @patch('axes.signals.ProxyHandler.implementation') diff --git a/axes/tests/test_logging.py b/axes/tests/test_logging.py new file mode 100644 index 0000000..aa71111 --- /dev/null +++ b/axes/tests/test_logging.py @@ -0,0 +1,43 @@ +from unittest.mock import patch + +from django.test import TestCase, override_settings + +from axes.apps import AppConfig + + +@patch('axes.apps.AppConfig.logging_initialized', False) +@patch('axes.apps.log') +class AppsTestCase(TestCase): + def test_axes_config_log_re_entrant(self, log): + """ + Test that log call count does not increase on repeat calls. + """ + + AppConfig.log() + calls = log.info.call_count + + AppConfig.log() + self.assertTrue( + calls == log.info.call_count and calls > 0, + 'AxesConfig.log needs to be re-entrant', + ) + + @override_settings(AXES_VERBOSE=False) + def test_axes_config_log_not_verbose(self, log): + AppConfig.log() + self.assertFalse(log.info.called) + + @override_settings(AXES_ONLY_USER_FAILURES=True) + def test_axes_config_log_user_only(self, log): + AppConfig.log() + log.info.assert_called_with('AXES: blocking by username only.') + + @override_settings(AXES_ONLY_USER_FAILURES=False) + def test_axes_config_log_ip_only(self, log): + AppConfig.log() + log.info.assert_called_with('AXES: blocking by IP only.') + + @override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True) + def test_axes_config_log_user_ip(self, log): + AppConfig.log() + log.info.assert_called_with('AXES: blocking by combination of username and IP.') diff --git a/axes/tests/test_models.py b/axes/tests/test_models.py index 4c311b1..3709135 100644 --- a/axes/tests/test_models.py +++ b/axes/tests/test_models.py @@ -4,18 +4,33 @@ from django.db.migrations.autodetector import MigrationAutodetector from django.db.migrations.executor import MigrationExecutor from django.db.migrations.state import ProjectState from django.test import TestCase -from django.utils import translation + +from axes.models import AccessAttempt, AccessLog -class MigrationsCheck(TestCase): +class ModelsTestCase(TestCase): def setUp(self): - self.saved_locale = translation.get_language() - translation.deactivate_all() + self.failures_since_start = 42 - def tearDown(self): - if self.saved_locale is not None: - translation.activate(self.saved_locale) + self.access_attempt = AccessAttempt( + failures_since_start=self.failures_since_start, + ) + self.access_log = AccessLog() + def test_access_attempt_str(self): + self.assertIn('Access', str(self.access_attempt)) + + def test_access_attempt_failures(self): + self.assertEqual( + self.access_attempt.failures, + self.failures_since_start, + ) + + def test_access_log_str(self): + self.assertIn('Access', str(self.access_log)) + + +class MigrationsTestCase(TestCase): def test_missing_migrations(self): executor = MigrationExecutor(connection) autodetector = MigrationAutodetector( diff --git a/axes/tests/test_utils.py b/axes/tests/test_utils.py index 3d466bf..7c20b9b 100644 --- a/axes/tests/test_utils.py +++ b/axes/tests/test_utils.py @@ -1,9 +1,11 @@ import datetime +from unittest.mock import patch -from django.http import HttpRequest +from django.http import HttpRequest, JsonResponse, HttpResponseRedirect, HttpResponse from django.test import TestCase, override_settings -from axes.utils import iso8601, is_ipv6, get_client_str, get_client_username +import axes +from axes.utils import iso8601, is_ipv6, get_client_str, get_client_username, get_lockout_response def get_expected_client_str(*args, **kwargs): @@ -11,7 +13,13 @@ def get_expected_client_str(*args, **kwargs): return client_str_template.format(*args, **kwargs) -class UtilsTest(TestCase): +class AxesTestCase(TestCase): + @patch('axes.__version__', 'test') + def test_get_version(self): + self.assertEqual(axes.get_version(), 'test') + + +class UtilsTestCase(TestCase): def test_iso8601(self): """ Test iso8601 correctly translates datetime.timdelta to ISO 8601 formatted duration. @@ -57,6 +65,18 @@ class UtilsTest(TestCase): self.assertEqual(expected, actual) + @override_settings(AXES_VERBOSE=True) + def test_verbose_ip_only_client_details_tuple(self): + username = 'test@example.com' + ip = '127.0.0.1' + user_agent = 'Googlebot/2.1 (+http://www.googlebot.com/bot.html)' + path_info = ('admin', 'login') + + expected = get_expected_client_str(username, ip, user_agent, path_info[0]) + actual = get_client_str(username, ip, user_agent, path_info) + + self.assertEqual(expected, actual) + @override_settings(AXES_VERBOSE=False) def test_non_verbose_ip_only_client_details(self): username = 'test@example.com' @@ -238,3 +258,33 @@ class UtilsTest(TestCase): def test_get_client_username_invalid_callable_too_many_arguments(self): with self.assertRaises(TypeError): actual = get_client_username(HttpRequest(), {}) + + +class LockoutResponseTestCase(TestCase): + def setUp(self): + self.request = HttpRequest() + + @override_settings(AXES_COOLOFF_TIME=42) + def test_get_lockout_response_cool_off(self): + get_lockout_response(request=self.request) + + @override_settings(AXES_LOCKOUT_TEMPLATE='example.html') + @patch('axes.utils.render') + def test_get_lockout_response_lockout_template(self, render): + self.assertFalse(render.called) + get_lockout_response(request=self.request) + self.assertTrue(render.called) + + @override_settings(AXES_LOCKOUT_URL='https://example.com') + def test_get_lockout_response_lockout_url(self): + response = get_lockout_response(request=self.request) + self.assertEqual(type(response), HttpResponseRedirect) + + def test_get_lockout_response_lockout_json(self): + self.request.is_ajax = lambda: True + response = get_lockout_response(request=self.request) + self.assertEqual(type(response), JsonResponse) + + def test_get_lockout_response_lockout_response(self): + response = get_lockout_response(request=self.request) + self.assertEqual(type(response), HttpResponse)