Repository: django/channels

Issue:
valid_group_name throwing wrong exception message
`valid_group_name` always throws the message `Group name must be a valid unicode string containing only ASCII alphanumerics, hyphens, or periods.`, even if the actual error is group name being too long. Would be helpful if we throw a different message or include the length limit in the message. 


Discussion:
I agree, it would be helpful, this wouldn't be too hard for someone to write a patch for.
I'll submit a PR shortly. Also, is it documented anywhere why there's a restriction on the length of the names? Thank you!
There's no specific reason, but it's so the names can be more easily portable across different backends. Unlimited length names would be hard to deal with in some places.

Resolving patch:
diff --git a/channels/layers.py b/channels/layers.py
--- a/channels/layers.py
+++ b/channels/layers.py
@@ -98,6 +98,8 @@ class BaseChannelLayer:
     common functionality.
     """
 
+    MAX_NAME_LENGTH = 100
+
     def __init__(self, expiry=60, capacity=100, channel_capacity=None):
         self.expiry = expiry
         self.capacity = capacity
@@ -131,7 +133,7 @@ def get_capacity(self, channel):
         return self.capacity
 
     def match_type_and_length(self, name):
-        if isinstance(name, str) and (len(name) < 100):
+        if isinstance(name, str) and (len(name) < self.MAX_NAME_LENGTH):
             return True
         return False
 
@@ -140,8 +142,10 @@ def match_type_and_length(self, name):
     channel_name_regex = re.compile(r"^[a-zA-Z\d\-_.]+(\![\d\w\-_.]*)?$")
     group_name_regex = re.compile(r"^[a-zA-Z\d\-_.]+$")
     invalid_name_error = (
-        "{} name must be a valid unicode string containing only ASCII "
-        + "alphanumerics, hyphens, underscores, or periods."
+        "{} name must be a valid unicode string "
+        + "with length < {} ".format(MAX_NAME_LENGTH)
+        + "containing only ASCII alphanumerics, hyphens, underscores, or periods, "
+        + "not {}"
     )
 
     def valid_channel_name(self, name, receive=False):
@@ -153,13 +157,13 @@ def valid_channel_name(self, name, receive=False):
                         "Specific channel names in receive() must end at the !"
                     )
                 return True
-        raise TypeError(self.invalid_name_error("Channel"))
+        raise TypeError(self.invalid_name_error.format("Channel", name))
 
     def valid_group_name(self, name):
         if self.match_type_and_length(name):
             if bool(self.group_name_regex.match(name)):
                 return True
-        raise TypeError(self.invalid_name_error("Group"))
+        raise TypeError(self.invalid_name_error.format("Group", name))
 
     def valid_channel_names(self, names, receive=False):
         _non_empty_list = True if names else False


Test patch:
diff --git a/tests/test_layers.py b/tests/test_layers.py
--- a/tests/test_layers.py
+++ b/tests/test_layers.py
@@ -5,7 +5,12 @@
 
 from channels import DEFAULT_CHANNEL_LAYER
 from channels.exceptions import InvalidChannelLayerError
-from channels.layers import InMemoryChannelLayer, channel_layers, get_channel_layer
+from channels.layers import (
+    BaseChannelLayer,
+    InMemoryChannelLayer,
+    channel_layers,
+    get_channel_layer,
+)
 
 
 class TestChannelLayerManager(unittest.TestCase):
@@ -63,3 +68,19 @@ async def test_send_receive():
     message = {"type": "test.message"}
     await layer.send("test.channel", message)
     assert message == await layer.receive("test.channel")
+
+
+@pytest.mark.parametrize(
+    "method",
+    [BaseChannelLayer().valid_channel_name, BaseChannelLayer().valid_group_name],
+)
+@pytest.mark.parametrize(
+    "channel_name,expected_valid",
+    [("¯\\_(ツ)_/¯", False), ("chat", True), ("chat" * 100, False)],
+)
+def test_channel_and_group_name_validation(method, channel_name, expected_valid):
+    if expected_valid:
+        method(channel_name)
+    else:
+        with pytest.raises(TypeError):
+            method(channel_name)