From 1d08bd96cb089a0881ffec2ee0e9c7f4adf5493d Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Thu, 15 Aug 2019 18:24:06 +0200 Subject: Adding position, role hierarchy comparisons to Role model; top_role to User model --- .../migrations/0039_add_position_field_to_role.py | 18 +++ pydis_site/apps/api/models/bot/role.py | 10 ++ pydis_site/apps/api/models/bot/user.py | 4 + pydis_site/apps/api/serializers.py | 2 +- pydis_site/apps/api/tests/test_models.py | 3 +- pydis_site/apps/api/tests/test_roles.py | 159 +++++++++++++++++++++ pydis_site/apps/api/tests/test_users.py | 30 +++- pydis_site/apps/api/viewsets/bot/role.py | 21 ++- 8 files changed, 239 insertions(+), 8 deletions(-) create mode 100644 pydis_site/apps/api/migrations/0039_add_position_field_to_role.py create mode 100644 pydis_site/apps/api/tests/test_roles.py (limited to 'pydis_site') diff --git a/pydis_site/apps/api/migrations/0039_add_position_field_to_role.py b/pydis_site/apps/api/migrations/0039_add_position_field_to_role.py new file mode 100644 index 00000000..0b8b0db3 --- /dev/null +++ b/pydis_site/apps/api/migrations/0039_add_position_field_to_role.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.3 on 2019-08-15 11:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0038_merge_20190719_1817'), + ] + + operations = [ + migrations.AddField( + model_name='role', + name='position', + field=models.IntegerField(default=-1, help_text='The position of the role in the role hierarchy of the Discord Guild.'), + ), + ] diff --git a/pydis_site/apps/api/models/bot/role.py b/pydis_site/apps/api/models/bot/role.py index 34e74009..777168b8 100644 --- a/pydis_site/apps/api/models/bot/role.py +++ b/pydis_site/apps/api/models/bot/role.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models @@ -43,7 +45,15 @@ class Role(ModelReprMixin, models.Model): ), help_text="The integer value of the permission bitset of this role from Discord." ) + position = models.IntegerField( + default=-1, + help_text="The position of the role in the role hierarchy of the Discord Guild." + ) def __str__(self): """Returns the name of the current role, for display purposes.""" return self.name + + def __lt__(self, other: Role): + """Compares the roles based on their position in the role hierarchy of the guild.""" + return self.position < other.position diff --git a/pydis_site/apps/api/models/bot/user.py b/pydis_site/apps/api/models/bot/user.py index d4deb630..1c566989 100644 --- a/pydis_site/apps/api/models/bot/user.py +++ b/pydis_site/apps/api/models/bot/user.py @@ -51,3 +51,7 @@ class User(ModelReprMixin, models.Model): def __str__(self): """Returns the name and discriminator for the current user, for display purposes.""" return f"{self.name}#{self.discriminator}" + + @property + def top_role(self) -> Role: + return max(self.roles.all()) diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index 51ad9276..38275c82 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -195,7 +195,7 @@ class RoleSerializer(ModelSerializer): """Metadata defined for the Django REST Framework.""" model = Role - fields = ('id', 'name', 'colour', 'permissions') + fields = ('id', 'name', 'colour', 'permissions', 'position') class TagSerializer(ModelSerializer): diff --git a/pydis_site/apps/api/tests/test_models.py b/pydis_site/apps/api/tests/test_models.py index cfc8464e..2120b056 100644 --- a/pydis_site/apps/api/tests/test_models.py +++ b/pydis_site/apps/api/tests/test_models.py @@ -51,7 +51,8 @@ class StringDunderMethodTests(SimpleTestCase): OffTopicChannelName(name='bob-the-builders-playground'), Role( id=5, name='test role', - colour=0x5, permissions=0 + colour=0x5, permissions=0, + position=10, ), Message( id=45, diff --git a/pydis_site/apps/api/tests/test_roles.py b/pydis_site/apps/api/tests/test_roles.py new file mode 100644 index 00000000..882083bb --- /dev/null +++ b/pydis_site/apps/api/tests/test_roles.py @@ -0,0 +1,159 @@ +from django_hosts.resolvers import reverse + +from .base import APISubdomainTestCase +from ..models import Role + + +class CreationTests(APISubdomainTestCase): + @classmethod + def setUpTestData(cls): # noqa + cls.admins_role = Role.objects.create( + id=1, + name="Admins", + colour=1, + permissions=1, + position=4, + ) + cls.developers_role = Role.objects.create( + id=4, + name="Helpers", + colour=4, + permissions=4, + position=1, + ) + cls.everyone_role = Role.objects.create( + id=5, + name="@everyone", + colour=5, + permissions=5, + position=0, + ) + + def _validate_roledict(self, role_dict: dict) -> None: + """Helper method to validate a dict representing a role.""" + self.assertIsInstance(role_dict, dict) + self.assertEqual(len(role_dict), 5) + attributes = ('id', 'name', 'colour', 'permissions', 'position') + self.assertTrue(all(attribute in role_dict for attribute in attributes)) + + def test_role_ordering(self): + """Tests the __lt__ comparisons based on role position in the hierarchy.""" + self.assertTrue(self.everyone_role < self.developers_role) + self.assertFalse(self.developers_role > self.admins_role) + + top_role = max([self.developers_role, self.admins_role, self.everyone_role]) + self.assertIs(top_role, self.admins_role) + + def test_role_list(self): + """Tests the GET list-view and validates the contents.""" + url = reverse('bot:role-list', host='api') + + response = self.client.get(url) + self.assertContains(response, text="id", count=3, status_code=200) + + roles = response.json() + self.assertIsInstance(roles, list) + self.assertEqual(len(roles), 3) + + for role in roles: + self._validate_roledict(role) + + def test_role_get_detail_success(self): + """Tests GET detail view of an existing role.""" + url = reverse('bot:role-detail', host='api', args=(self.admins_role.id, )) + response = self.client.get(url) + self.assertContains(response, text="id", count=1, status_code=200) + + role = response.json() + self._validate_roledict(role) + + admins_role = Role.objects.get(id=role["id"]) + self.assertEqual(admins_role.name, role["name"]) + self.assertEqual(admins_role.colour, role["colour"]) + self.assertEqual(admins_role.permissions, role["permissions"]) + self.assertEqual(admins_role.position, role["position"]) + + def test_role_post_201(self): + """Tests creation of a role with a valid request.""" + url = reverse('bot:role-list', host='api') + data = { + "id": 1234567890, + "name": "Role Creation Test", + "permissions": 0b01010010101, + "colour": 1, + "position": 10, + } + response = self.client.post(url, data=data) + self.assertEqual(response.status_code, 201) + + def test_role_post_invalid_request_body(self): + """Tests creation of a role with an invalid request body.""" + url = reverse('bot:role-list', host='api') + data = { + "name": "Role Creation Test", + "permissions": 0b01010010101, + "colour": 1, + "position": 10, + } + response = self.client.post(url, data=data) + self.assertEqual(response.status_code, 400) + self.assertJSONEqual(response.content, '{"id": ["This field is required."]}') + + def test_role_put_200(self): + """Tests PUT role request with valid request body.""" + url = reverse('bot:role-detail', host='api', args=(self.admins_role.id,)) + data = { + "id": 123454321, + "name": "Role Put Alteration Test", + "permissions": 255, + "colour": 999, + "position": 20, + } + + response = self.client.put(url, data=data) + self.assertEqual(response.status_code, 200) + + admins_role = Role.objects.get(id=data["id"]) + self.assertEqual(admins_role.name, data["name"]) + self.assertEqual(admins_role.permissions, data["permissions"]) + self.assertEqual(admins_role.colour, data["colour"]) + self.assertEqual(admins_role.position, data["position"]) + + def test_role_put_invalid_request_body(self): + """Tests PUT role request with invalid request body.""" + url = reverse('bot:role-detail', host='api', args=(self.admins_role.id,)) + data = { + "name": "Role Put Alteration Test", + "permissions": 255, + "colour": 999, + "position": 20, + } + response = self.client.put(url, data=data) + self.assertEqual(response.status_code, 400) + + def test_role_patch_200(self): + """Tests PATCH role request with valid request body.""" + url = reverse('bot:role-detail', host='api', args=(self.admins_role.id,)) + data = { + "name": "Owners" + } + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 200) + + admins_role = Role.objects.get(id=self.admins_role.id) + self.assertEqual(admins_role.name, data["name"]) + + def test_role_delete_200(self): + """Tests DELETE requests for existing role.""" + url = reverse('bot:role-detail', host='api', args=(self.admins_role.id,)) + response = self.client.delete(url) + self.assertEqual(response.status_code, 204) + + def test_role_detail_404_all_methods(self): + """Tests detail view with non-existing ID.""" + url = reverse('bot:role-detail', host='api', args=(20190815,)) + + for method in ('get', 'put', 'patch', 'delete'): + response = getattr(self.client, method)(url) + self.assertEqual(response.status_code, 404) + self.assertJSONEqual(response.content, '{"detail": "Not found."}') diff --git a/pydis_site/apps/api/tests/test_users.py b/pydis_site/apps/api/tests/test_users.py index 90bc3d30..44ce481e 100644 --- a/pydis_site/apps/api/tests/test_users.py +++ b/pydis_site/apps/api/tests/test_users.py @@ -41,8 +41,31 @@ class CreationTests(APISubdomainTestCase): id=5, name="Test role pls ignore", colour=2, - permissions=0b01010010101 + permissions=0b01010010101, + position=1 ) + cls.role_bottom = Role.objects.create( + id=6, + name="Low test role", + colour=2, + permissions=0b01010010101, + position=0, + ) + cls.role_top = Role.objects.create( + id=7, + name="High test role", + colour=2, + permissions=0b01010010101, + position=10, + ) + cls.role_test_user = User.objects.create( + id=1, + avatar_hash="coolavatarhash", + name="Test User", + discriminator=1111, + in_guild=True, + ) + cls.role_test_user.roles.add(cls.role_bottom, cls.role_top) def test_accepts_valid_data(self): url = reverse('bot:user-list', host='api') @@ -119,3 +142,8 @@ class CreationTests(APISubdomainTestCase): response = self.client.post(url, data=data) self.assertEqual(response.status_code, 400) + + def test_correct_top_role_property(self): + """Tests if the top_role property returns the correct role.""" + self.assertIsInstance(self.role_test_user.top_role, Role) + self.assertEqual(self.role_test_user.top_role.id, self.role_top.id) diff --git a/pydis_site/apps/api/viewsets/bot/role.py b/pydis_site/apps/api/viewsets/bot/role.py index 213f0a19..1f82208d 100644 --- a/pydis_site/apps/api/viewsets/bot/role.py +++ b/pydis_site/apps/api/viewsets/bot/role.py @@ -20,7 +20,8 @@ class RoleViewSet(ModelViewSet): ... 'id': 267628507062992896, ... 'name': "Admins", ... 'colour': 1337, - ... 'permissions': 8 + ... 'permissions': 8, + ... 'position': 1 ... } ... ] @@ -35,7 +36,8 @@ class RoleViewSet(ModelViewSet): ... 'id': 267628507062992896, ... 'name': "Admins", ... 'colour': 1337, - ... 'permissions': 8 + ... 'permissions': 8, + ... 'position': 1 ... } #### Status codes @@ -51,6 +53,7 @@ class RoleViewSet(ModelViewSet): ... 'name': str, ... 'colour': int, ... 'permissions': int, + ... 'position': 1, ... } #### Status codes @@ -66,24 +69,32 @@ class RoleViewSet(ModelViewSet): ... 'id': int, ... 'name': str, ... 'colour': int, - ... 'permissions': int + ... 'permissions': int, + ... 'position': 1, ... } #### Status codes - 200: returned on success - 400: if the request body was invalid + - 404: if a role with the given `snowflake` does not exist ### PATCH /bot/roles/ Update the role with the given `snowflake`. - All fields in the request body are required. + #### Request body >>> { ... 'id': int, ... 'name': str, ... 'colour': int, - ... 'permissions': int + ... 'permissions': int, + ... 'position': 1, ... } + #### Status codes + - 200: returned on success + - 400: if the request body was invalid + - 404: if a role with the given `snowflake` does not exist + ### DELETE /bot/roles/ Deletes the role with the given `snowflake`. -- cgit v1.2.3 From 98633ecd4f6914365f8c51678d97415672b2359f Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Thu, 15 Aug 2019 18:25:30 +0200 Subject: Adding missing docstring in public method --- pydis_site/apps/api/models/bot/user.py | 1 + 1 file changed, 1 insertion(+) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/models/bot/user.py b/pydis_site/apps/api/models/bot/user.py index 1c566989..8049d319 100644 --- a/pydis_site/apps/api/models/bot/user.py +++ b/pydis_site/apps/api/models/bot/user.py @@ -54,4 +54,5 @@ class User(ModelReprMixin, models.Model): @property def top_role(self) -> Role: + """Attribute that returns the user's top role.""" return max(self.roles.all()) -- cgit v1.2.3 From f86a8143256575c204f218b7a4b3e6ad7203052d Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Thu, 15 Aug 2019 20:31:19 +0200 Subject: Making User.top_role test only query once --- pydis_site/apps/api/tests/test_users.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/tests/test_users.py b/pydis_site/apps/api/tests/test_users.py index 44ce481e..1afba40d 100644 --- a/pydis_site/apps/api/tests/test_users.py +++ b/pydis_site/apps/api/tests/test_users.py @@ -145,5 +145,6 @@ class CreationTests(APISubdomainTestCase): def test_correct_top_role_property(self): """Tests if the top_role property returns the correct role.""" - self.assertIsInstance(self.role_test_user.top_role, Role) - self.assertEqual(self.role_test_user.top_role.id, self.role_top.id) + top_role = self.role_test_user.top_role + self.assertIsInstance(top_role, Role) + self.assertEqual(top_role.id, self.role_top.id) -- cgit v1.2.3 From 24bc7ced26a3d6aa8bd039f0b2cd95a6b5c85d5c Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Thu, 22 Aug 2019 20:54:26 +0200 Subject: Fixing top_role bug for users without roles & adding appropriate test --- pydis_site/apps/api/models/bot/user.py | 9 +++- pydis_site/apps/api/tests/test_users.py | 74 ++++++++++++++++++++++----------- 2 files changed, 58 insertions(+), 25 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/models/bot/user.py b/pydis_site/apps/api/models/bot/user.py index 8049d319..00c24d3d 100644 --- a/pydis_site/apps/api/models/bot/user.py +++ b/pydis_site/apps/api/models/bot/user.py @@ -54,5 +54,12 @@ class User(ModelReprMixin, models.Model): @property def top_role(self) -> Role: - """Attribute that returns the user's top role.""" + """ + Attribute that returns the user's top role. + + This will fall back to the Developers role if the user does not have any roles. + """ + roles = self.roles.all() + if not roles: + return Role.objects.get(name="Developers") return max(self.roles.all()) diff --git a/pydis_site/apps/api/tests/test_users.py b/pydis_site/apps/api/tests/test_users.py index 1afba40d..bbdd3ff4 100644 --- a/pydis_site/apps/api/tests/test_users.py +++ b/pydis_site/apps/api/tests/test_users.py @@ -44,28 +44,6 @@ class CreationTests(APISubdomainTestCase): permissions=0b01010010101, position=1 ) - cls.role_bottom = Role.objects.create( - id=6, - name="Low test role", - colour=2, - permissions=0b01010010101, - position=0, - ) - cls.role_top = Role.objects.create( - id=7, - name="High test role", - colour=2, - permissions=0b01010010101, - position=10, - ) - cls.role_test_user = User.objects.create( - id=1, - avatar_hash="coolavatarhash", - name="Test User", - discriminator=1111, - in_guild=True, - ) - cls.role_test_user.roles.add(cls.role_bottom, cls.role_top) def test_accepts_valid_data(self): url = reverse('bot:user-list', host='api') @@ -143,8 +121,56 @@ class CreationTests(APISubdomainTestCase): response = self.client.post(url, data=data) self.assertEqual(response.status_code, 400) - def test_correct_top_role_property(self): + +class UserModelTests(APISubdomainTestCase): + @classmethod + def setUpTestData(cls): # noqa + cls.role_top = Role.objects.create( + id=777, + name="High test role", + colour=2, + permissions=0b01010010101, + position=10, + ) + cls.role_bottom = Role.objects.create( + id=888, + name="Low test role", + colour=2, + permissions=0b01010010101, + position=1, + ) + cls.developers_role = Role.objects.create( + id=1234567, + name="Developers", + colour=1234, + permissions=0b01010010101, + position=2, + ) + cls.user_with_roles = User.objects.create( + id=1, + avatar_hash="coolavatarhash", + name="Test User with two roles", + discriminator=1111, + in_guild=True, + ) + cls.user_with_roles.roles.add(cls.role_bottom, cls.role_top) + + cls.user_without_roles = User.objects.create( + id=2, + avatar_hash="coolavatarhash", + name="Test User without roles", + discriminator=2222, + in_guild=True, + ) + + def test_correct_top_role_property_user_with_roles(self): """Tests if the top_role property returns the correct role.""" - top_role = self.role_test_user.top_role + top_role = self.user_with_roles.top_role self.assertIsInstance(top_role, Role) self.assertEqual(top_role.id, self.role_top.id) + + def test_correct_top_role_property_user_without_roles(self): + """Tests if the top_role property returns the correct role.""" + top_role = self.user_without_roles.top_role + self.assertIsInstance(top_role, Role) + self.assertEqual(top_role.id, self.developers_role.id) -- cgit v1.2.3 From 9a5f8547b2fe5764738dcf89ed1e35b401da2c16 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Thu, 22 Aug 2019 21:08:32 +0200 Subject: Removing default value for 'position' field of Role model --- pydis_site/apps/api/migrations/0039_add_position_field_to_role.py | 2 +- pydis_site/apps/api/models/bot/role.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/migrations/0039_add_position_field_to_role.py b/pydis_site/apps/api/migrations/0039_add_position_field_to_role.py index 0b8b0db3..2aa51f80 100644 --- a/pydis_site/apps/api/migrations/0039_add_position_field_to_role.py +++ b/pydis_site/apps/api/migrations/0039_add_position_field_to_role.py @@ -13,6 +13,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='role', name='position', - field=models.IntegerField(default=-1, help_text='The position of the role in the role hierarchy of the Discord Guild.'), + field=models.IntegerField(help_text='The position of the role in the role hierarchy of the Discord Guild.'), ), ] diff --git a/pydis_site/apps/api/models/bot/role.py b/pydis_site/apps/api/models/bot/role.py index 777168b8..836d9f27 100644 --- a/pydis_site/apps/api/models/bot/role.py +++ b/pydis_site/apps/api/models/bot/role.py @@ -46,7 +46,6 @@ class Role(ModelReprMixin, models.Model): help_text="The integer value of the permission bitset of this role from Discord." ) position = models.IntegerField( - default=-1, help_text="The position of the role in the role hierarchy of the Discord Guild." ) -- cgit v1.2.3 From 4c6c1f9cefe5f6989181716fbf92e7ce8150d013 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Thu, 22 Aug 2019 21:20:24 +0200 Subject: First adding default value, then removing it to make it required. --- pydis_site/apps/api/migrations/0039_add_position_field_to_role.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/migrations/0039_add_position_field_to_role.py b/pydis_site/apps/api/migrations/0039_add_position_field_to_role.py index 2aa51f80..b6b27ff2 100644 --- a/pydis_site/apps/api/migrations/0039_add_position_field_to_role.py +++ b/pydis_site/apps/api/migrations/0039_add_position_field_to_role.py @@ -13,6 +13,11 @@ class Migration(migrations.Migration): migrations.AddField( model_name='role', name='position', - field=models.IntegerField(help_text='The position of the role in the role hierarchy of the Discord Guild.'), + field=models.IntegerField(default=-1, help_text='The position of the role in the role hierarchy of the Discord Guild.'), ), + migrations.AlterField( + model_name='role', + name='position', + field=models.IntegerField(help_text='The position of the role in the role hierarchy of the Discord Guild.'), + ) ] -- cgit v1.2.3 From 861f272ccf76abfe317b96827e4632b2657d5d69 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Fri, 23 Aug 2019 13:26:37 +0200 Subject: Making the comparison operators for Role act like those for d.py Role objects --- pydis_site/apps/api/models/bot/role.py | 15 +++++++++--- pydis_site/apps/api/tests/test_roles.py | 42 +++++++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 8 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/models/bot/role.py b/pydis_site/apps/api/models/bot/role.py index 836d9f27..58bbf8b4 100644 --- a/pydis_site/apps/api/models/bot/role.py +++ b/pydis_site/apps/api/models/bot/role.py @@ -7,7 +7,12 @@ from pydis_site.apps.api.models.utils import ModelReprMixin class Role(ModelReprMixin, models.Model): - """A role on our Discord server.""" + """ + A role on our Discord server. + + The comparison operators <, <=, >, >=, ==, != act the same as they do with Role-objects of the + discord.py library, see https://discordpy.readthedocs.io/en/latest/api.html#discord.Role + """ id = models.BigIntegerField( primary_key=True, @@ -49,10 +54,14 @@ class Role(ModelReprMixin, models.Model): help_text="The position of the role in the role hierarchy of the Discord Guild." ) - def __str__(self): + def __str__(self) -> str: """Returns the name of the current role, for display purposes.""" return self.name - def __lt__(self, other: Role): + def __lt__(self, other: Role) -> bool: """Compares the roles based on their position in the role hierarchy of the guild.""" return self.position < other.position + + def __le__(self, other: Role) -> bool: + """Compares the roles based on their position in the role hierarchy of the guild.""" + return self.position <= other.position diff --git a/pydis_site/apps/api/tests/test_roles.py b/pydis_site/apps/api/tests/test_roles.py index 882083bb..0a6cea9e 100644 --- a/pydis_site/apps/api/tests/test_roles.py +++ b/pydis_site/apps/api/tests/test_roles.py @@ -28,6 +28,13 @@ class CreationTests(APISubdomainTestCase): permissions=5, position=0, ) + cls.lowest_position_duplicate = Role.objects.create( + id=6, + name="lowest position duplicate", + colour=6, + permissions=6, + position=0, + ) def _validate_roledict(self, role_dict: dict) -> None: """Helper method to validate a dict representing a role.""" @@ -36,24 +43,49 @@ class CreationTests(APISubdomainTestCase): attributes = ('id', 'name', 'colour', 'permissions', 'position') self.assertTrue(all(attribute in role_dict for attribute in attributes)) - def test_role_ordering(self): + def test_role_ordering_lt(self): """Tests the __lt__ comparisons based on role position in the hierarchy.""" self.assertTrue(self.everyone_role < self.developers_role) self.assertFalse(self.developers_role > self.admins_role) - top_role = max([self.developers_role, self.admins_role, self.everyone_role]) - self.assertIs(top_role, self.admins_role) + def test_role_ordering_le(self): + """Tests the __le__ comparisons based on role position in the hierarchy.""" + self.assertTrue(self.everyone_role <= self.developers_role) + self.assertTrue(self.everyone_role <= self.lowest_position_duplicate) + self.assertTrue(self.everyone_role >= self.lowest_position_duplicate) + self.assertTrue(self.developers_role >= self.everyone_role) + + self.assertFalse(self.developers_role >= self.admins_role) + self.assertFalse(self.developers_role <= self.everyone_role) + + def test_role_min_max_ordering(self): + """Tests the `min` and `max` operations based on the role hierarchy.""" + top_role_no_duplicates = max([self.developers_role, self.admins_role, self.everyone_role]) + self.assertIs(top_role_no_duplicates, self.admins_role) + + top_role_duplicates = max([self.developers_role, self.admins_role, self.admins_role]) + self.assertIs(top_role_duplicates, self.admins_role) + + bottom_role_no_duplicates = min( + [self.developers_role, self.admins_role, self.everyone_role] + ) + self.assertIs(bottom_role_no_duplicates, self.everyone_role) + + bottom_role_duplicates = min( + [self.lowest_position_duplicate, self.admins_role, self.everyone_role] + ) + self.assertIs(bottom_role_duplicates, self.lowest_position_duplicate) def test_role_list(self): """Tests the GET list-view and validates the contents.""" url = reverse('bot:role-list', host='api') response = self.client.get(url) - self.assertContains(response, text="id", count=3, status_code=200) + self.assertContains(response, text="id", count=4, status_code=200) roles = response.json() self.assertIsInstance(roles, list) - self.assertEqual(len(roles), 3) + self.assertEqual(len(roles), 4) for role in roles: self._validate_roledict(role) -- cgit v1.2.3