diff --git a/allianceauth/authentication/admin.py b/allianceauth/authentication/admin.py index a3df70b2..dac057d8 100644 --- a/allianceauth/authentication/admin.py +++ b/allianceauth/authentication/admin.py @@ -3,7 +3,7 @@ from django.conf import settings from django.contrib import admin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.models import User as BaseUser, \ - Permission as BasePermission + Permission as BasePermission, Group from django.db.models import Q, F from allianceauth.services.hooks import ServicesHook from django.db.models.signals import pre_save, post_save, pre_delete, \ @@ -113,7 +113,7 @@ def user_profile_pic(obj): user_obj.profile.main_character.portrait_url(size=32) ) else: - return '' + return None user_profile_pic.short_description = '' @@ -131,13 +131,19 @@ def user_username(obj): args=(obj.pk,) ) user_obj = obj.user if hasattr(obj, 'user') else obj - return format_html( - '{}
{}', - link, - user_obj.username, - user_obj.profile.main_character.character_name \ - if user_obj.profile.main_character else '' - ) + if user_obj.profile.main_character: + return format_html( + '{}
{}', + link, + user_obj.username, + user_obj.profile.main_character.character_name + ) + else: + return format_html( + '{}', + link, + user_obj.username, + ) user_username.short_description = 'user / main' user_username.admin_order_field = 'username' @@ -150,20 +156,18 @@ def user_main_organization(obj): To be used for all user based admin lists """ user_obj = obj.user if hasattr(obj, 'user') else obj - if user_obj.profile.main_character: + if not user_obj.profile.main_character: + result = None + else: corporation = user_obj.profile.main_character.corporation_name - else: - corporation = '' - if (user_obj.profile.main_character - and user_obj.profile.main_character.alliance_id - ): - alliance = user_obj.profile.main_character.alliance_name - else: - alliance = '' - return format_html('{}
{}', - corporation, - alliance - ) + if user_obj.profile.main_character.alliance_id: + result = format_html('{}
{}', + corporation, + user_obj.profile.main_character.alliance_name + ) + else: + result = corporation + return result user_main_organization.short_description = 'Corporation / Alliance (Main)' user_main_organization.admin_order_field = \ @@ -177,7 +181,7 @@ class MainCorporationsFilter(admin.SimpleListFilter): To be used for all user based admin lists """ title = 'corporation' - parameter_name = 'main_corporations' + parameter_name = 'main_corporation_id__exact' def lookups(self, request, model_admin): qs = EveCharacter.objects\ @@ -210,7 +214,7 @@ class MainAllianceFilter(admin.SimpleListFilter): To be used for all user based admin lists """ title = 'alliance' - parameter_name = 'main_alliances' + parameter_name = 'main_alliance_id__exact' def lookups(self, request, model_admin): qs = EveCharacter.objects\ @@ -251,7 +255,7 @@ class UserAdmin(BaseUserAdmin): class RealGroupsFilter(admin.SimpleListFilter): """Custom filter to get groups w/o Autogroups""" title = 'group' - parameter_name = 'real_groups' + parameter_name = 'group_id__exact' def lookups(self, request, model_admin): qs = Group.objects.all().order_by(Lower('name')) @@ -267,7 +271,6 @@ class UserAdmin(BaseUserAdmin): else: return queryset.filter(groups__pk=self.value()) - def update_main_character_model(self, request, queryset): tasks_count = 0 for obj in queryset: @@ -283,7 +286,6 @@ class UserAdmin(BaseUserAdmin): update_main_character_model.short_description = \ 'Update main character model from ESI' - def get_actions(self, request): actions = super(BaseUserAdmin, self).get_actions(request) @@ -313,21 +315,22 @@ class UserAdmin(BaseUserAdmin): ) return actions - def _list_2_html_w_tooltips(self, my_items: list, max_items: int) -> str: """converts list of strings into HTML with cutoff and tooltip""" items_truncated_str = ', '.join(my_items[:max_items]) - if len(my_items) <= max_items: - return items_truncated_str + if not my_items: + result = None + elif len(my_items) <= max_items: + result = items_truncated_str else: - items_truncated_str += ' (...)' + items_truncated_str += ', (...)' items_all_str = ', '.join(my_items) - return format_html( + result = format_html( '{}', items_all_str, items_truncated_str ) - + return result inlines = BaseUserAdmin.inlines + [UserProfileInline] @@ -380,12 +383,11 @@ class UserAdmin(BaseUserAdmin): def _state(self, obj): - return obj.profile.state + return obj.profile.state.name _state.short_description = 'state' _state.admin_order_field = 'profile__state' - def _groups(self, obj): if not _has_auto_groups: my_groups = [x.name for x in obj.groups.order_by('name')] @@ -404,20 +406,17 @@ class UserAdmin(BaseUserAdmin): _groups.short_description = 'groups' - def _role(self, obj): if obj.is_superuser: role = 'Superuser' elif obj.is_staff: role = 'Staff' else: - role = 'User' - + role = 'User' return role _role.short_description = 'role' - def has_change_permission(self, request, obj=None): return request.user.has_perm('auth.change_user') diff --git a/allianceauth/authentication/tests/test_admin.py b/allianceauth/authentication/tests/test_admin.py new file mode 100644 index 00000000..961ff3c9 --- /dev/null +++ b/allianceauth/authentication/tests/test_admin.py @@ -0,0 +1,384 @@ +from unittest.mock import patch + +from django.test import TestCase, RequestFactory +from django.contrib import admin +from django.contrib.admin.sites import AdminSite +from django.contrib.auth.models import User as BaseUser, Group + +from allianceauth.authentication.models import CharacterOwnership, State +from allianceauth.eveonline.autogroups.models import AutogroupsConfig +from allianceauth.eveonline.models import ( + EveCharacter, EveCorporationInfo, EveAllianceInfo +) + +from ..admin import ( + BaseUserAdmin, + MainCorporationsFilter, + MainAllianceFilter, + User, + UserAdmin, + user_main_organization, + user_profile_pic, + user_username, +) + + +MODULE_PATH = 'allianceauth.authentication.admin' + + +class MockRequest(object): + + def __init__(self, user=None): + self.user = user + + +class TestUserAdmin(TestCase): + + def setUp(self): + self.factory = RequestFactory() + self.modeladmin = UserAdmin( + model=User, admin_site=AdminSite() + ) + # groups + self.group_1 = Group.objects.create( + name='Group 1' + ) + self.group_2 = Group.objects.create( + name='Group 2' + ) + + # user 1 - corp and alliance, normal user + self.character_1 = EveCharacter.objects.create( + character_id='1001', + character_name='Bruce Wayne', + corporation_id='2001', + corporation_name='Wayne Technologies', + corporation_ticker='WT', + alliance_id='3001', + alliance_name='Wayne Enterprises', + alliance_ticker='WE', + ) + self.character_1a = EveCharacter.objects.create( + character_id='1002', + character_name='Batman', + corporation_id='2001', + corporation_name='Wayne Technologies', + corporation_ticker='WT', + alliance_id='3001', + alliance_name='Wayne Enterprises', + alliance_ticker='WE', + ) + alliance = EveAllianceInfo.objects.create( + alliance_id='3001', + alliance_name='Wayne Enterprises', + alliance_ticker='WE', + executor_corp_id='2001' + ) + EveCorporationInfo.objects.create( + corporation_id='2001', + corporation_name='Wayne Technologies', + corporation_ticker='WT', + member_count=42, + alliance=alliance + ) + self.user_1 = User.objects.create_user( + self.character_1.character_name.replace(' ', '_'), + 'abc@example.com', + 'password' + ) + CharacterOwnership.objects.create( + character=self.character_1, + owner_hash='x1' + self.character_1.character_name, + user=self.user_1 + ) + CharacterOwnership.objects.create( + character=self.character_1a, + owner_hash='x1' + self.character_1a.character_name, + user=self.user_1 + ) + self.user_1.profile.main_character = self.character_1 + self.user_1.profile.save() + self.user_1.groups.add(self.group_1) + + # user 2 - corp only, staff + self.character_2 = EveCharacter.objects.create( + character_id=1003, + character_name='Clark Kent', + corporation_id=2002, + corporation_name='Daily Planet', + corporation_ticker='DP', + alliance_id=None + ) + EveCorporationInfo.objects.create( + corporation_id=2002, + corporation_name='Daily Plane', + corporation_ticker='DP', + member_count=99, + alliance=None + ) + self.user_2 = User.objects.create_user( + self.character_2.character_name.replace(' ', '_'), + 'abc@example.com', + 'password' + ) + CharacterOwnership.objects.create( + character=self.character_2, + owner_hash='x1' + self.character_2.character_name, + user=self.user_2 + ) + self.user_2.profile.main_character = self.character_2 + self.user_2.profile.save() + self.user_2.groups.add(self.group_2) + self.user_2.is_staff = True + self.user_2.save() + + # user 3 - no main, no group, superuser + self.character_3 = EveCharacter.objects.create( + character_id=1101, + character_name='Lex Luthor', + corporation_id=2101, + corporation_name='Lex Corp', + corporation_ticker='LC', + alliance_id=None + ) + EveCorporationInfo.objects.create( + corporation_id=2101, + corporation_name='Lex Corp', + corporation_ticker='LC', + member_count=666, + alliance=None + ) + EveAllianceInfo.objects.create( + alliance_id='3101', + alliance_name='Lex World Domination', + alliance_ticker='LWD', + executor_corp_id='' + ) + self.user_3 = User.objects.create_user( + self.character_3.character_name.replace(' ', '_'), + 'abc@example.com', + 'password' + ) + CharacterOwnership.objects.create( + character=self.character_3, + owner_hash='x1' + self.character_3.character_name, + user=self.user_3 + ) + self.user_3.is_superuser = True + self.user_3.save() + + # create autogroups for corps and alliances + autogroups_config = AutogroupsConfig( + corp_groups = True, + alliance_groups = True + ) + autogroups_config.save() + for state in State.objects.all(): + autogroups_config.states.add(state) + autogroups_config.update_corp_group_membership(self.user_1) + + # column rendering + + def test_user_profile_pic_1(self): + expected = ('') + self.assertEqual(user_profile_pic(self.user_1), expected) + + def test_user_profile_pic_3(self): + self.assertIsNone(user_profile_pic(self.user_3)) + + def test_user_username_1(self): + expected = ( + '' + 'Bruce_Wayne
Bruce Wayne'.format(self.user_1.pk) + ) + self.assertEqual(user_username(self.user_1), expected) + + def test_user_username_3(self): + expected = ( + '' + 'Lex_Luthor'.format(self.user_3.pk) + ) + self.assertEqual(user_username(self.user_3), expected) + + def test_user_main_organization_1(self): + expected = 'Wayne Technologies
Wayne Enterprises' + self.assertEqual(user_main_organization(self.user_1), expected) + + def test_user_main_organization_2(self): + expected = 'Daily Planet' + self.assertEqual(user_main_organization(self.user_2), expected) + + def test_user_main_organization_3(self): + expected = None + self.assertEqual(user_main_organization(self.user_3), expected) + + def test_characters_1(self): + expected = 'Batman, Bruce Wayne' + result = self.modeladmin._characters(self.user_1) + self.assertEqual(result, expected) + + def test_characters_2(self): + expected = 'Clark Kent' + result = self.modeladmin._characters(self.user_2) + self.assertEqual(result, expected) + + def test_characters_3(self): + expected = 'Lex Luthor' + result = self.modeladmin._characters(self.user_3) + self.assertEqual(result, expected) + + def test_groups_1(self): + expected = 'Group 1' + result = self.modeladmin._groups(self.user_1) + self.assertEqual(result, expected) + + def test_groups_2(self): + expected = 'Group 2' + result = self.modeladmin._groups(self.user_2) + self.assertEqual(result, expected) + + def test_groups_3(self): + result = self.modeladmin._groups(self.user_3) + self.assertIsNone(result) + + def test_state(self): + expected = 'Guest' + result = self.modeladmin._state(self.user_1) + self.assertEqual(result, expected) + + def test_role_1(self): + expected = 'User' + result = self.modeladmin._role(self.user_1) + self.assertEqual(result, expected) + + def test_role_2(self): + expected = 'Staff' + result = self.modeladmin._role(self.user_2) + self.assertEqual(result, expected) + + def test_role_3(self): + expected = 'Superuser' + result = self.modeladmin._role(self.user_3) + self.assertEqual(result, expected) + + def test_list_2_html_w_tooltips_no_cutoff(self): + items = ['one', 'two', 'three'] + expected = 'one, two, three' + result = self.modeladmin._list_2_html_w_tooltips(items, 5) + self.assertEqual(expected, result) + + def test_list_2_html_w_tooltips_w_cutoff(self): + items = ['one', 'two', 'three'] + expected = ('one, two, (...)') + result = self.modeladmin._list_2_html_w_tooltips(items, 2) + self.assertEqual(expected, result) + + def test_list_2_html_w_tooltips_empty_list(self): + items = [] + expected = None + result = self.modeladmin._list_2_html_w_tooltips(items, 5) + self.assertEqual(expected, result) + + # actions + + @patch(MODULE_PATH + '.UserAdmin.message_user', auto_spec=True) + @patch(MODULE_PATH + '.update_character') + def test_action_update_main_character_model( + self, mock_task, mock_message_user + ): + users_qs = User.objects.filter(pk__in=[self.user_1.pk, self.user_2.pk]) + self.modeladmin.update_main_character_model( + MockRequest(self.user_1), users_qs + ) + self.assertEqual(mock_task.delay.call_count, 2) + self.assertTrue(mock_message_user.called) + + # filters + + def test_filter_real_groups(self): + + class UserAdminTest(BaseUserAdmin): + list_filter = (UserAdmin.RealGroupsFilter,) + + my_modeladmin = UserAdminTest(User, AdminSite()) + + # Make sure the lookups are correct + request = self.factory.get('/') + request.user = self.user_1 + changelist = my_modeladmin.get_changelist_instance(request) + filters = changelist.get_filters(request) + filterspec = filters[0][0] + expected = [ + (self.group_1.pk, self.group_1.name), + (self.group_2.pk, self.group_2.name), + ] + self.assertEqual(filterspec.lookup_choices, expected) + + # Make sure the correct queryset is returned + request = self.factory.get('/', {'group_id__exact': self.group_1.pk}) + request.user = self.user_1 + changelist = my_modeladmin.get_changelist_instance(request) + queryset = changelist.get_queryset(request) + expected = User.objects.filter(groups__in=[self.group_1]) + self.assertSetEqual(set(queryset), set(expected)) + + def test_filter_main_corporations(self): + + class UserAdminTest(BaseUserAdmin): + list_filter = (MainCorporationsFilter,) + + my_modeladmin = UserAdminTest(User, AdminSite()) + + # Make sure the lookups are correct + request = self.factory.get('/') + request.user = self.user_1 + changelist = my_modeladmin.get_changelist_instance(request) + filters = changelist.get_filters(request) + filterspec = filters[0][0] + expected = [ + ('2002', 'Daily Planet'), + ('2001', 'Wayne Technologies'), + ] + self.assertEqual(filterspec.lookup_choices, expected) + + # Make sure the correct queryset is returned + request = self.factory.get( + '/', + {'main_corporation_id__exact': self.character_1.corporation_id} + ) + request.user = self.user_1 + changelist = my_modeladmin.get_changelist_instance(request) + queryset = changelist.get_queryset(request) + expected = [self.user_1] + self.assertSetEqual(set(queryset), set(expected)) + + def test_filter_main_alliances(self): + + class UserAdminTest(BaseUserAdmin): + list_filter = (MainAllianceFilter,) + + my_modeladmin = UserAdminTest(User, AdminSite()) + + # Make sure the lookups are correct + request = self.factory.get('/') + request.user = self.user_1 + changelist = my_modeladmin.get_changelist_instance(request) + filters = changelist.get_filters(request) + filterspec = filters[0][0] + expected = [ + ('3001', 'Wayne Enterprises'), + ] + self.assertEqual(filterspec.lookup_choices, expected) + + # Make sure the correct queryset is returned + request = self.factory.get( + '/', + {'main_alliance_id__exact': self.character_1.alliance_id} + ) + request.user = self.user_1 + changelist = my_modeladmin.get_changelist_instance(request) + queryset = changelist.get_queryset(request) + expected = [self.user_1] + self.assertSetEqual(set(queryset), set(expected)) \ No newline at end of file