Repository: Cog-Creators/Red-DiscordBot

Issue:
modlog.create_case() should raise exception not fail silently when passing invalid action_type
Current behaviour is to `return None` when an invalid `action_type` is passed.

```py  
 # redbot/core/modlog.py
case_type = await get_casetype(action_type, guild)
    if case_type is None:
        return
```
[Quick link](https://github.com/Cog-Creators/Red-DiscordBot/blob/V3/develop/redbot/core/modlog.py#L750)


Discussion:
Not sure I agree with that, but changing that would be breaking so there's plenty of time for that to be discussed.
While it could be a tiny bit better (easier to detect a problem in your code and more Pythonic perhaps) if this raised an exception, it returning `None` is documented quite clearly and there's only one case it can return it in so I don't think it warrants possibly breaking someone by it just to make the API a bit cleaner.
> While it could be a tiny bit better (easier to detect a problem in your code and more Pythonic perhaps) if this raised an exception, it returning `None` is documented quite clearly and there's only one case it can return it in so I don't think it warrants possibly breaking someone by it just to make the API a bit cleaner.

This comment of mine doesn't make a lot of sense 🤔 I think I thought that this issue is referring to `modlog.get_casetype()`, not `modlog.create_case()`... So ignore my previous comment as I think that this DOES make sense to do as the breakage is minimal and I doubt that anyone would intentionally pass invalid action type to `create_case()`.

With that out of the way... This should be quite simple, it just requires raising `ValueError` rather than returning. PRs are welcome.

Resolving patch:
diff --git a/redbot/core/modlog.py b/redbot/core/modlog.py
--- a/redbot/core/modlog.py
+++ b/redbot/core/modlog.py
@@ -1020,18 +1020,23 @@ async def create_case(
 
     Raises
     ------
+    ValueError
+        If the action type is not a valid action type.
+    RuntimeError
+        If user is the bot itself.
     TypeError
         If ``channel`` is of type `discord.PartialMessageable`.
     """
     case_type = await get_casetype(action_type, guild)
     if case_type is None:
-        return
+        raise ValueError(f"{action_type} is not a valid action type.")
 
     if not await case_type.is_enabled():
         return
 
-    if user == bot.user:
-        return
+    user_id = user if isinstance(user, int) else user.id
+    if user_id == bot.user.id:
+        raise RuntimeError("The bot itself can not be the target of a modlog entry.")
 
     if isinstance(channel, discord.PartialMessageable):
         raise TypeError("Can't use PartialMessageable as the channel for a modlog case.")


Test patch:
diff --git a/tests/cogs/test_mod.py b/tests/cogs/test_mod.py
--- a/tests/cogs/test_mod.py
+++ b/tests/cogs/test_mod.py
@@ -1,3 +1,4 @@
+from collections import namedtuple
 import pytest
 
 from redbot.pytest.mod import *
@@ -9,7 +10,7 @@ async def test_modlog_register_casetype(mod):
     assert casetype is not None
 
 
-async def test_modlog_case_create(mod, ctx, member_factory):
+async def test_modlog_case_create(mod, ctx, monkeypatch, member_factory, empty_user):
     from datetime import datetime, timezone
 
     # Run casetype register test to register casetype in this test too
@@ -22,6 +23,10 @@ async def test_modlog_case_create(mod, ctx, member_factory):
     moderator = ctx.author
     reason = "Test 12345"
     created_at = datetime.now(timezone.utc)
+    # mod.create_case needs bot to have a user object. Without a connection to discord it would be none by default.
+    # due to the implementation of bot.user we need to set the user as part of bot._connection
+    mock_connection = namedtuple("Connection", "user")
+    monkeypatch.setattr(bot, "_connection", mock_connection(empty_user))
     case = await mod.create_case(bot, guild, created_at, case_type, usr, moderator, reason)
     assert case is not None
     assert case.user == usr