aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site
diff options
context:
space:
mode:
authorGravatar mbaruh <[email protected]>2023-01-27 22:20:10 +0200
committerGravatar mbaruh <[email protected]>2023-01-27 22:20:10 +0200
commit3862e051407061186609dbeaab23ec53aeca2f94 (patch)
treec524db4d80458d87a44eadd7d55a8832c7accf93 /pydis_site
parentFix filter serializers validation to account for filterlist settings (diff)
Update tests
Diffstat (limited to 'pydis_site')
-rw-r--r--pydis_site/apps/api/models/__init__.py1
-rw-r--r--pydis_site/apps/api/tests/test_filters.py309
-rw-r--r--pydis_site/apps/api/tests/test_models.py3
3 files changed, 170 insertions, 143 deletions
diff --git a/pydis_site/apps/api/models/__init__.py b/pydis_site/apps/api/models/__init__.py
index 580c95a0..fee4c8d5 100644
--- a/pydis_site/apps/api/models/__init__.py
+++ b/pydis_site/apps/api/models/__init__.py
@@ -6,7 +6,6 @@ from .bot import (
BumpedThread,
DocumentationLink,
DeletedMessage,
- FilterList,
Infraction,
Message,
MessageDeletionContext,
diff --git a/pydis_site/apps/api/tests/test_filters.py b/pydis_site/apps/api/tests/test_filters.py
index f3afdaeb..cae78cd6 100644
--- a/pydis_site/apps/api/tests/test_filters.py
+++ b/pydis_site/apps/api/tests/test_filters.py
@@ -1,19 +1,13 @@
import contextlib
from dataclasses import dataclass
+from datetime import timedelta
from typing import Any, Dict, Tuple, Type
from django.db.models import Model
-from django_hosts import reverse
+from django.urls import reverse
-from pydis_site.apps.api.models.bot.filters import ( # noqa: I101 - Preserving the filter order
- FilterList,
- FilterSettings,
- FilterAction,
- ChannelRange,
- Filter,
- FilterOverride
-)
-from pydis_site.apps.api.tests.base import APISubdomainTestCase
+from pydis_site.apps.api.models.bot.filters import FilterList, Filter
+from pydis_site.apps.api.tests.base import AuthenticatedAPITestCase
@dataclass()
@@ -21,99 +15,76 @@ class TestSequence:
model: Type[Model]
route: str
object: Dict[str, Any]
- ignored_fields: Tuple[str] = ()
+ ignored_fields: Tuple[str, ...] = ()
def url(self, detail: bool = False) -> str:
- return reverse(f'bot:{self.route}-{"detail" if detail else "list"}', host='api')
+ return reverse(f'api:bot:{self.route}-{"detail" if detail else "list"}')
-FK_FIELDS: Dict[Type[Model], Tuple[str]] = {
- FilterList: ("default_settings",),
- FilterSettings: ("default_action", "default_range"),
- FilterAction: (),
- ChannelRange: (),
+FK_FIELDS: Dict[Type[Model], Tuple[str, ...]] = {
+ FilterList: (),
Filter: ("filter_list",),
- FilterOverride: ("filter_action", "filter_range")
}
def get_test_sequences() -> Dict[str, TestSequence]:
+ filter_list1_deny_dict = {
+ "name": "testname",
+ "list_type": 0,
+ "guild_pings": [],
+ "filter_dm": True,
+ "dm_pings": [],
+ "remove_context": False,
+ "bypass_roles": [],
+ "enabled": True,
+ "dm_content": "",
+ "dm_embed": "",
+ "infraction_type": "NONE",
+ "infraction_reason": "",
+ "infraction_duration": timedelta(seconds=0),
+ "infraction_channel": 0,
+ "disabled_channels": [],
+ "disabled_categories": [],
+ "enabled_channels": [],
+ "enabled_categories": [],
+ "send_alert": True
+ }
+ filter_list1_allow_dict = filter_list1_deny_dict.copy()
+ filter_list1_allow_dict["list_type"] = 1
+ filter_list1_allow = FilterList(**filter_list1_allow_dict)
+
return {
- "filter_list": TestSequence(
+ "filter_list1": TestSequence(
FilterList,
"filterlist",
- {
- "name": "testname",
- "list_type": 0,
- "default_settings": FilterSettings(
- ping_type=[],
- filter_dm=False,
- dm_ping_type=[],
- remove_context=False,
- bypass_roles=[],
- enabled=False,
- default_action=FilterAction(
- dm_content=None,
- infraction_type=None,
- infraction_reason="",
- infraction_duration=None
- ),
- default_range=ChannelRange(
- disallowed_channels=[],
- disallowed_categories=[],
- allowed_channels=[],
- allowed_categories=[],
- default=False
- )
- )
- },
- ignored_fields=("filters",)
+ filter_list1_deny_dict,
+ ignored_fields=("filters", "created_at", "updated_at")
),
- "filter_settings": TestSequence(
- FilterSettings,
- "filtersettings",
+ "filter_list2": TestSequence(
+ FilterList,
+ "filterlist",
{
- "ping_type": ["onduty"],
- "filter_dm": True,
- "dm_ping_type": ["123456"],
+ "name": "testname2",
+ "list_type": 1,
+ "guild_pings": ["Moderators"],
+ "filter_dm": False,
+ "dm_pings": ["here"],
"remove_context": True,
- "bypass_roles": [123456],
- "enabled": True,
- "default_action": FilterAction(
- dm_content=None,
- infraction_type=None,
- infraction_reason="",
- infraction_duration=None
- ),
- "default_range": ChannelRange(
- disallowed_channels=[],
- disallowed_categories=[],
- allowed_channels=[],
- allowed_categories=[],
- default=False
- )
- }
- ),
- "filter_action": TestSequence(
- FilterAction,
- "filteraction",
- {
- "dm_content": "This is a DM message.",
- "infraction_type": "Mute",
- "infraction_reason": "Too long beard",
- "infraction_duration": "1 02:03:00"
- }
- ),
- "channel_range": TestSequence(
- ChannelRange,
- "channelrange",
- {
- "disallowed_channels": [1234],
- "disallowed_categories": [5678],
- "allowed_channels": [9101],
- "allowed_categories": [1121],
- "default": True
- }
+ "bypass_roles": ["123456"],
+ "enabled": False,
+ "dm_content": "testing testing",
+ "dm_embed": "one two three",
+ "infraction_type": "MUTE",
+ "infraction_reason": "stop testing",
+ "infraction_duration": timedelta(seconds=10.5),
+ "infraction_channel": 123,
+ "disabled_channels": ["python-general"],
+ "disabled_categories": ["CODE JAM"],
+ "enabled_channels": ["mighty-mice"],
+ "enabled_categories": ["Lobby"],
+ "send_alert": False
+ },
+ ignored_fields=("filters", "created_at", "updated_at")
),
"filter": TestSequence(
Filter,
@@ -121,58 +92,35 @@ def get_test_sequences() -> Dict[str, TestSequence]:
{
"content": "bad word",
"description": "This is a really bad word.",
- "additional_field": None,
- "override": None,
- "filter_list": FilterList(
- name="testname",
- list_type=0,
- default_settings=FilterSettings(
- ping_type=[],
- filter_dm=False,
- dm_ping_type=[],
- remove_context=False,
- bypass_roles=[],
- enabled=False,
- default_action=FilterAction(
- dm_content=None,
- infraction_type=None,
- infraction_reason="",
- infraction_duration=None
- ),
- default_range=ChannelRange(
- disallowed_channels=[],
- disallowed_categories=[],
- allowed_channels=[],
- allowed_categories=[],
- default=False
- )
- )
- )
- }
+ "additional_field": "{'hi': 'there'}",
+ "guild_pings": None,
+ "filter_dm": None,
+ "dm_pings": None,
+ "remove_context": None,
+ "bypass_roles": None,
+ "enabled": None,
+ "dm_content": None,
+ "dm_embed": None,
+ "infraction_type": None,
+ "infraction_reason": None,
+ "infraction_duration": None,
+ "infraction_channel": None,
+ "disabled_channels": None,
+ "disabled_categories": None,
+ "enabled_channels": None,
+ "enabled_categories": None,
+ "send_alert": None,
+ "filter_list": filter_list1_allow
+ },
+ ignored_fields=("created_at", "updated_at")
),
- "filter_override": TestSequence(
- FilterOverride,
- "filteroverride",
- {
- "ping_type": ["everyone"],
- "filter_dm": False,
- "dm_ping_type": ["here"],
- "remove_context": False,
- "bypass_roles": [9876],
- "enabled": True,
- "filter_action": None,
- "filter_range": None
- }
- )
}
def save_nested_objects(object_: Model, save_root: bool = True) -> None:
- for field in FK_FIELDS[object_.__class__]:
+ for field in FK_FIELDS.get(object_.__class__, ()):
value = getattr(object_, field)
-
- if value is not None:
- save_nested_objects(value)
+ save_nested_objects(value)
if save_root:
object_.save()
@@ -182,6 +130,8 @@ def clean_test_json(json: dict) -> dict:
for key, value in json.items():
if isinstance(value, Model):
json[key] = value.id
+ elif isinstance(value, timedelta):
+ json[key] = str(value.total_seconds())
return json
@@ -194,7 +144,22 @@ def clean_api_json(json: dict, sequence: TestSequence) -> dict:
return json
-class GenericFilterTest(APISubdomainTestCase):
+def flatten_settings(json: dict) -> dict:
+ settings = json.pop("settings", {})
+ flattened_settings = {}
+ for entry, value in settings.items():
+ if isinstance(value, dict):
+ flattened_settings.update(value)
+ else:
+ flattened_settings[entry] = value
+
+ json.update(flattened_settings)
+
+ return json
+
+
+class GenericFilterTests(AuthenticatedAPITestCase):
+
def test_cannot_read_unauthenticated(self) -> None:
for name, sequence in get_test_sequences().items():
with self.subTest(name=name):
@@ -222,7 +187,7 @@ class GenericFilterTest(APISubdomainTestCase):
response = self.client.get(sequence.url())
self.assertDictEqual(
clean_test_json(sequence.object),
- clean_api_json(response.json()[0], sequence)
+ clean_api_json(flatten_settings(response.json()[0]), sequence)
)
def test_fetch_by_id(self) -> None:
@@ -236,7 +201,7 @@ class GenericFilterTest(APISubdomainTestCase):
response = self.client.get(f"{sequence.url()}/{saved.id}")
self.assertDictEqual(
clean_test_json(sequence.object),
- clean_api_json(response.json(), sequence)
+ clean_api_json(flatten_settings(response.json()), sequence)
)
def test_fetch_non_existing(self) -> None:
@@ -259,14 +224,15 @@ class GenericFilterTest(APISubdomainTestCase):
self.assertEqual(response.status_code, 201)
self.assertDictEqual(
- clean_api_json(response.json(), sequence),
+ clean_api_json(flatten_settings(response.json()), sequence),
clean_test_json(sequence.object)
)
def test_creation_missing_field(self) -> None:
for name, sequence in get_test_sequences().items():
with self.subTest(name=name):
- save_nested_objects(sequence.model(**sequence.object), False)
+ saved = sequence.model(**sequence.object)
+ save_nested_objects(saved)
data = clean_test_json(sequence.object.copy())
for field in sequence.model._meta.get_fields():
@@ -296,3 +262,68 @@ class GenericFilterTest(APISubdomainTestCase):
response = self.client.delete(f"{sequence.url()}/42")
self.assertEqual(response.status_code, 404)
+
+
+class FilterValidationTests(AuthenticatedAPITestCase):
+
+ def test_filter_validation(self) -> None:
+ test_sequences = get_test_sequences()
+ base_filter = test_sequences["filter"]
+ base_filter_list = test_sequences["filter_list1"]
+ cases = (
+ ({"infraction_reason": "hi"}, {}, 400), ({"infraction_duration": timedelta(seconds=10)}, {}, 400),
+ ({"infraction_reason": "hi"}, {"infraction_type": "NOTE"}, 200),
+ ({"infraction_duration": timedelta(seconds=10)}, {"infraction_type": "MUTE"}, 200),
+ ({"enabled_channels": ["admins"]}, {}, 200), ({"disabled_channels": ["123"]}, {}, 200),
+ ({"enabled_categories": ["CODE JAM"]}, {}, 200), ({"disabled_categories": ["CODE JAM"]}, {}, 200),
+ ({"enabled_channels": ["admins"], "disabled_channels": ["123", "admins"]}, {}, 400),
+ ({"enabled_categories": ["admins"], "disabled_categories": ["123", "admins"]}, {}, 400),
+ ({"enabled_channels": ["admins"]}, {"disabled_channels": ["123", "admins"]}, 400),
+ ({"enabled_categories": ["admins"]}, {"disabled_categories": ["123", "admins"]}, 400),
+ )
+
+ for filter_settings, filter_list_settings, response_code in cases:
+ with self.subTest(f_settings=filter_settings, fl_settings=filter_list_settings, response=response_code):
+ base_filter.model.objects.all().delete()
+ base_filter_list.model.objects.all().delete()
+
+ case_filter_dict = base_filter.object.copy()
+ case_fl_dict = base_filter_list.object.copy()
+ case_fl_dict.update(filter_list_settings)
+
+ case_fl = base_filter_list.model(**case_fl_dict)
+ case_filter_dict["filter_list"] = case_fl
+ case_filter = base_filter.model(**case_filter_dict)
+ save_nested_objects(case_filter)
+
+ filter_settings["filter_list"] = case_fl
+ response = self.client.patch(
+ f"{base_filter.url()}/{case_filter.id}", data=clean_test_json(filter_settings)
+ )
+ self.assertEqual(response.status_code, response_code)
+
+ def test_filter_list_validation(self) -> None:
+ test_sequences = get_test_sequences()
+ base_filter_list = test_sequences["filter_list1"]
+ cases = (
+ ({"infraction_reason": "hi"}, 400), ({"infraction_duration": timedelta(seconds=10)}, 400),
+ ({"infraction_reason": "hi", "infraction_type": "NOTE"}, 200),
+ ({"infraction_duration": timedelta(seconds=10), "infraction_type": "MUTE"}, 200),
+ ({"enabled_channels": ["admins"]}, 200), ({"disabled_channels": ["123"]}, 200),
+ ({"enabled_categories": ["CODE JAM"]}, 200), ({"disabled_categories": ["CODE JAM"]}, 200),
+ ({"enabled_channels": ["admins"], "disabled_channels": ["123", "admins"]}, 400),
+ ({"enabled_categories": ["admins"], "disabled_categories": ["123", "admins"]}, 400),
+ )
+
+ for filter_list_settings, response_code in cases:
+ with self.subTest(fl_settings=filter_list_settings, response=response_code):
+ base_filter_list.model.objects.all().delete()
+
+ case_fl_dict = base_filter_list.object.copy()
+ case_fl = base_filter_list.model(**case_fl_dict)
+ save_nested_objects(case_fl)
+
+ response = self.client.patch(
+ f"{base_filter_list.url()}/{case_fl.id}", data=clean_test_json(filter_list_settings)
+ )
+ self.assertEqual(response.status_code, response_code)
diff --git a/pydis_site/apps/api/tests/test_models.py b/pydis_site/apps/api/tests/test_models.py
index b9b14a84..25d771cc 100644
--- a/pydis_site/apps/api/tests/test_models.py
+++ b/pydis_site/apps/api/tests/test_models.py
@@ -8,7 +8,6 @@ from pydis_site.apps.api.models import (
DocumentationLink,
Filter,
FilterList,
- FilterSettings,
Infraction,
MessageDeletionContext,
Nomination,
@@ -110,13 +109,11 @@ class StringDunderMethodTests(SimpleTestCase):
FilterList(
name="forbidden_duckies",
list_type=0,
- default_settings=FilterSettings()
),
Filter(
content="ducky_nsfw",
description="This ducky is totally inappropriate!",
additional_field=None,
- override=None
),
OffensiveMessage(
id=602951077675139072,