diff --git a/pyproject.toml b/pyproject.toml index b38a000..99e8c9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,8 @@ markers = [ "models_str_method: mark test to check __str__ methods of models", "models_code_field: mark test to check code fields in models", "models_code_field_unique: mark test to check code fields are unique", + "settings_admin_permission: mark test to check admin permissions", + "settings_checks: mark test to check settings configuration", ] norecursedirs = [ "migrations", diff --git a/tests/settings/__init__.py b/tests/settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/settings/test_admin_permissions.py b/tests/settings/test_admin_permissions.py new file mode 100644 index 0000000..b088e24 --- /dev/null +++ b/tests/settings/test_admin_permissions.py @@ -0,0 +1,109 @@ +from django.contrib.admin.sites import AdminSite +from django.contrib.auth.models import User +from django.http import HttpRequest +from tests.constants import PYTHON_VERSION, PYTHON_VERSION_REASON +from iranian_cities.conf import sage_iranian_cities_settings +from typing import List, Type, Dict, Optional +from iranian_cities.mixins.dynamic_permission import IranianCitiesAdminReadOnlyEnabled, DynamicInlineAdmin +from iranian_cities.admin import ( + Province, County, District, RuralDistrict, + CityInline, VillageInline, CountyInline, DistrictInline +) +import pytest +import sys + + +pytestmark = [ + pytest.mark.django_db, + pytest.mark.settings_admin_permission, + pytest.mark.skipif(sys.version_info < PYTHON_VERSION, reason=PYTHON_VERSION_REASON) +] + + +class TestAdminPermission: + """Test IranianCities Admin Permission.""" + def test_iranian_cities_admin_permissions(self) -> None: + """ + Test the IranianCitiesAdminReadOnlyEnabled mixin to verify that permission + checks are correctly based on settings. + + This test verifies that add, delete, and change permissions are granted + or denied based on the configuration settings. + + Returns + ------- + None + """ + settings_instance = IranianCitiesAdminReadOnlyEnabled() + request = HttpRequest() + + # Set configuration settings + sage_iranian_cities_settings.IRANIAN_CITIES_ADMIN_ADD_READONLY_ENABLED = True + sage_iranian_cities_settings.IRANIAN_CITIES_ADMIN_DELETE_READONLY_ENABLED = False + sage_iranian_cities_settings.IRANIAN_CITIES_ADMIN_CHANGE_READONLY_ENABLED = True + + assert settings_instance.has_add_permission(request) is True + assert settings_instance.has_delete_permission(request) is False + assert settings_instance.has_change_permission(request) is True + + def test_dynamic_inline_admin(self) -> None: + """ + Test the DynamicInlineAdmin mixin to ensure that the correct inlines + are returned based on settings. + + This test checks that the appropriate inline model admins are returned + for different models when the inline settings are enabled. + + Returns + ------- + None + """ + settings_instance = DynamicInlineAdmin() + + # Mock the models and inlines + sage_iranian_cities_settings.IRANIAN_CITIES_ADMIN_INLINE_ENABLED = True + + assert settings_instance.get_dynamic_inlines(Province) == [CountyInline] + assert settings_instance.get_dynamic_inlines(County) == [DistrictInline] + assert settings_instance.get_dynamic_inlines(District) == [CityInline] + assert settings_instance.get_dynamic_inlines(RuralDistrict) == [VillageInline] + assert settings_instance.get_dynamic_inlines(object) == [] + + def test_get_inlines_method(self, settings) -> None: + """ + Test the `get_inlines` method in a custom admin model admin class. + + This test verifies that the `get_inlines` method returns the correct + inlines based on the model provided, using the dynamic inline admin settings. + + Parameters + ---------- + settings : pytest fixture + The Django settings fixture used to configure the test environment. + + Returns + ------- + None + """ + + request = HttpRequest() + request.user = User.objects.create_superuser('admin', 'admin@example.com', 'password') + + class CustomModelAdmin(DynamicInlineAdmin): + def __init__(self, model: Optional[Type], admin_site: Optional[AdminSite]) -> None: + self.model = model + self.admin_site = admin_site + + model_admins: Dict[Type, List[Type]] = { + Province: [CountyInline], + County: [DistrictInline], + District: [CityInline], + RuralDistrict: [VillageInline] + } + + for model, expected_inlines in model_admins.items(): + model_admin = CustomModelAdmin(model=model, admin_site=AdminSite()) + + inlines = model_admin.get_inlines(request) + + assert inlines == expected_inlines \ No newline at end of file diff --git a/tests/settings/test_checks.py b/tests/settings/test_checks.py new file mode 100644 index 0000000..67ffee9 --- /dev/null +++ b/tests/settings/test_checks.py @@ -0,0 +1,105 @@ +from typing import List +from iranian_cities.checks import check_iranian_cities_config +from iranian_cities.exc import IranianCitiesConfigurationError +from iranian_cities.conf import SageIranianCitiesSettings +from tests.constants import PYTHON_VERSION, PYTHON_VERSION_REASON +import pytest +import sys + +pytestmark = [ + pytest.mark.settings_checks, + pytest.mark.skipif(sys.version_info < PYTHON_VERSION, reason=PYTHON_VERSION_REASON) +] + +class TestIranianCitiesConfig: + """Test IranianCities configuration and settings.""" + def test_check_iranian_cities_config_correct_settings(self, settings) -> None: + """ + Test the Iranian Cities configuration checker with correct settings. + + This test verifies that no errors are returned when all required settings + are correctly configured. + + Parameters + ---------- + settings : pytest.Settings + The Django settings fixture used to configure the test environment. + """ + settings.IRANIAN_CITIES_ADMIN_ADD_READONLY_ENABLED = True + settings.IRANIAN_CITIES_ADMIN_DELETE_READONLY_ENABLED = True + settings.IRANIAN_CITIES_ADMIN_CHANGE_READONLY_ENABLED = True + settings.IRANIAN_CITIES_ADMIN_INLINE_ENABLED = True + + errors: List[Exception] = check_iranian_cities_config({}) + assert len(errors) == 0 + + def test_sage_iranian_cities_settings(self, settings) -> None: + """ + Test the SageIranianCitiesSettings class with correct settings. + + This test verifies that the settings class reads and applies the correct + settings values. + + Parameters + ---------- + settings : pytest.Settings + The Django settings fixture used to configure the test environment. + """ + settings.IRANIAN_CITIES_ADMIN_ADD_READONLY_ENABLED = True + settings.IRANIAN_CITIES_ADMIN_DELETE_READONLY_ENABLED = True + settings.IRANIAN_CITIES_ADMIN_CHANGE_READONLY_ENABLED = True + settings.IRANIAN_CITIES_ADMIN_INLINE_ENABLED = True + + settings_instance = SageIranianCitiesSettings() + + assert settings_instance.IRANIAN_CITIES_ADMIN_ADD_READONLY_ENABLED is True + assert settings_instance.IRANIAN_CITIES_ADMIN_DELETE_READONLY_ENABLED is True + assert settings_instance.IRANIAN_CITIES_ADMIN_CHANGE_READONLY_ENABLED is True + assert settings_instance.IRANIAN_CITIES_ADMIN_INLINE_ENABLED is True + + def test_check_iranian_cities_config_invalid_type_again(self, settings) -> None: + """ + Test the Iranian Cities configuration checker with invalid type settings. + + This test ensures that an IranianCitiesConfigurationError is raised when + settings are of the wrong type. + + Parameters + ---------- + settings : pytest.Settings + The Django settings fixture used to configure the test environment. + """ + settings.IRANIAN_CITIES_ADMIN_ADD_READONLY_ENABLED = "true" + settings.IRANIAN_CITIES_ADMIN_DELETE_READONLY_ENABLED = True + settings.IRANIAN_CITIES_ADMIN_CHANGE_READONLY_ENABLED = True + settings.IRANIAN_CITIES_ADMIN_INLINE_ENABLED = True + + with pytest.raises(IranianCitiesConfigurationError): + SageIranianCitiesSettings() + + def test_sage_iranian_cities_settings_missing_configs(self, settings) -> None: + """ + Test the SageIranianCitiesSettings class with missing configuration values. + + This test verifies that an IranianCitiesConfigurationError is raised when + required settings are missing or set to None. + + Parameters + ---------- + settings : pytest.Settings + The Django settings fixture used to configure the test environment. + """ + settings.IRANIAN_CITIES_ADMIN_ADD_READONLY_ENABLED = None + settings.IRANIAN_CITIES_ADMIN_DELETE_READONLY_ENABLED = None + settings.IRANIAN_CITIES_ADMIN_CHANGE_READONLY_ENABLED = None + settings.IRANIAN_CITIES_ADMIN_INLINE_ENABLED = None + + with pytest.raises(IranianCitiesConfigurationError) as excinfo: + SageIranianCitiesSettings() + + error_message: str = str(excinfo.value) + + assert excinfo.value.code == "E4001" + assert excinfo.value.section_code == "CFG" + + assert "must be of type boolean" in error_message \ No newline at end of file