|
| 1 | +""" |
| 2 | +Tests for ISUPPORT limit enforcement (`Modern |
| 3 | +<https://modern.ircdocs.horse/#rplisupport-parameters>`__) |
| 4 | +""" |
| 5 | + |
| 6 | +import pytest |
| 7 | + |
| 8 | +from irctest import cases, runner |
| 9 | +from irctest.numerics import ( |
| 10 | + ERR_ERRONEUSNICKNAME, |
| 11 | + ERR_NOSUCHCHANNEL, |
| 12 | + ERR_TOOMANYCHANNELS, |
| 13 | +) |
| 14 | +from irctest.patma import ANYSTR |
| 15 | + |
| 16 | + |
| 17 | +class IsupportLimitTestCase(cases.BaseServerTestCase): |
| 18 | + @cases.mark_specifications("Modern") |
| 19 | + @cases.mark_isupport("CHANLIMIT") |
| 20 | + @pytest.mark.parametrize("prefix", ["#", "&"]) |
| 21 | + def testChanlimit(self, prefix): |
| 22 | + """ |
| 23 | + "CHANLIMIT=<prefixes>:[limit]{,<prefixes>:[limit]}" |
| 24 | + -- https://modern.ircdocs.horse/#chanlimit-parameter |
| 25 | + """ |
| 26 | + self.connectClient("foo") |
| 27 | + |
| 28 | + if "CHANLIMIT" not in self.server_support: |
| 29 | + raise runner.IsupportTokenNotSupported("CHANLIMIT") |
| 30 | + |
| 31 | + pairs = [ |
| 32 | + part.split(":", 1) for part in self.server_support["CHANLIMIT"].split(",") |
| 33 | + ] |
| 34 | + limits = {pair[0]: int(pair[1]) if pair[1] else None for pair in pairs} |
| 35 | + |
| 36 | + self.assertTrue(limits, "CHANLIMIT is empty") |
| 37 | + |
| 38 | + limit = limits.get(prefix) |
| 39 | + if limit is None: |
| 40 | + raise runner.ImplementationChoice(f"No limit for {prefix} channels") |
| 41 | + |
| 42 | + # Join up to the limit |
| 43 | + for i in range(limit): |
| 44 | + self.sendLine(1, f"JOIN {prefix}chan{i}") |
| 45 | + self.assertMessageMatch( |
| 46 | + self.getMessage(1), |
| 47 | + command="JOIN", |
| 48 | + params=[f"{prefix}chan{i}"], |
| 49 | + fail_msg=f"Failed to join channel {i + 1}/{limit}", |
| 50 | + ) |
| 51 | + self.getMessages(1) # clear any remaining messages |
| 52 | + |
| 53 | + # Try to join one more - should fail |
| 54 | + self.sendLine(1, f"JOIN {prefix}chan") |
| 55 | + self.assertMessageMatch( |
| 56 | + self.getMessage(1), |
| 57 | + command=ERR_TOOMANYCHANNELS, |
| 58 | + params=["foo", f"{prefix}chan", ANYSTR], |
| 59 | + ) |
| 60 | + |
| 61 | + @cases.mark_specifications("Modern") |
| 62 | + @cases.mark_isupport("CHANNELLEN") |
| 63 | + @pytest.mark.parametrize("prefix", ["#", "&"]) |
| 64 | + def testChannellen(self, prefix): |
| 65 | + """ |
| 66 | + "CHANNELLEN=<number> |
| 67 | + The CHANNELLEN parameter specifies the maximum length of a channel name that a client may join." |
| 68 | + -- https://modern.ircdocs.horse/#channellen-parameter |
| 69 | + """ |
| 70 | + self.connectClient("foo") |
| 71 | + |
| 72 | + if "CHANNELLEN" not in self.server_support: |
| 73 | + raise runner.IsupportTokenNotSupported("CHANNELLEN") |
| 74 | + |
| 75 | + chantypes = self.server_support.get("CHANTYPES", "#") |
| 76 | + if prefix not in chantypes: |
| 77 | + raise runner.NotImplementedByController( |
| 78 | + f"Server does not support {prefix} channels" |
| 79 | + ) |
| 80 | + |
| 81 | + channellen = int(self.server_support["CHANNELLEN"]) |
| 82 | + |
| 83 | + # Try a channel name at exactly the limit |
| 84 | + valid_chan = prefix + "a" * (channellen - 1) |
| 85 | + self.sendLine(1, f"JOIN {valid_chan}") |
| 86 | + self.assertMessageMatch(self.getMessage(1), command="JOIN", params=[valid_chan]) |
| 87 | + |
| 88 | + # Try a channel name longer than the limit |
| 89 | + self.getMessages(1) # clear |
| 90 | + invalid_chan = prefix + "b" * channellen |
| 91 | + self.sendLine(1, f"JOIN {invalid_chan}") |
| 92 | + self.assertMessageMatch( |
| 93 | + self.getMessage(1), |
| 94 | + command=ERR_NOSUCHCHANNEL, |
| 95 | + params=["foo", invalid_chan, ANYSTR], |
| 96 | + ) |
| 97 | + |
| 98 | + @cases.mark_specifications("Modern") |
| 99 | + @cases.mark_isupport("NICKLEN") |
| 100 | + def testNicklen(self): |
| 101 | + """ |
| 102 | + "NICKLEN=<number> |
| 103 | + "The NICKLEN parameter indicates the maximum length of a nickname that a client may set. |
| 104 | + Clients on the network MAY have longer nicks than this. |
| 105 | + The value MUST be specified and MUST be a positive integer. |
| 106 | + 30 or 31 are typical values for this parameter advertised by servers today." |
| 107 | + -- https://modern.ircdocs.horse/#nicklen-parameter |
| 108 | + """ |
| 109 | + self.connectClient("foo") |
| 110 | + |
| 111 | + if "NICKLEN" not in self.server_support: |
| 112 | + raise runner.IsupportTokenNotSupported("NICKLEN") |
| 113 | + |
| 114 | + nicklen = int(self.server_support["NICKLEN"]) |
| 115 | + |
| 116 | + # Try a nick at exactly the limit |
| 117 | + valid_nick = "a" * nicklen |
| 118 | + self.sendLine(1, f"NICK {valid_nick}") |
| 119 | + self.assertMessageMatch(self.getMessage(1), command="NICK", params=[valid_nick]) |
| 120 | + |
| 121 | + # Try a nick longer than the limit |
| 122 | + invalid_nick = "b" * (nicklen + 5) |
| 123 | + self.sendLine(1, f"NICK {invalid_nick}") |
| 124 | + self.assertMessageMatch( |
| 125 | + self.getMessage(1), |
| 126 | + command=ERR_ERRONEUSNICKNAME, |
| 127 | + params=[valid_nick, invalid_nick, ANYSTR], |
| 128 | + ) |
0 commit comments