mirror of https://github.com/progval/irctest.git
Compare commits
14 Commits
fc058c7262
...
5dd6f6fb07
Author | SHA1 | Date | |
---|---|---|---|
Val Lorentz | 5dd6f6fb07 | ||
Val Lorentz | df626de5ed | ||
Val Lorentz | 79223d35f1 | ||
Shivaram Lingamneni | 723991c7ec | ||
Valentin Lorentz | 1bc8741479 | ||
Val Lorentz | 9f8e712776 | ||
Val Lorentz | a1f8fcac49 | ||
Valentin Lorentz | d3c919e0f5 | ||
Val Lorentz | ce51dddc15 | ||
Val Lorentz | 7f9b4b315f | ||
Val Lorentz | 9d43a002c2 | ||
Val Lorentz | ea66a8f9a4 | ||
Valentin Lorentz | 473db1cc5b | ||
Valentin Lorentz | e4bca8a401 |
|
@ -228,7 +228,7 @@ jobs:
|
|||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: ngircd
|
||||
ref: 0714466af88d71d6c395629cd7fb624b099507d4
|
||||
ref: 3e3f6cbeceefd9357b53b27c2386bb39306ab353
|
||||
repository: ngircd/ngircd
|
||||
- name: Build ngircd
|
||||
run: |
|
||||
|
@ -1106,7 +1106,7 @@ jobs:
|
|||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: sable
|
||||
ref: fe337a036c3ab5f8548e2578b65568e628f4c32f
|
||||
ref: b9deaa930c49f2939d9a584bedbfc3236da0d707
|
||||
repository: Libera-Chat/sable
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
|
|
2
Makefile
2
Makefile
|
@ -93,7 +93,7 @@ SABLE_SELECTORS := \
|
|||
and not arbitrary_client_tags \
|
||||
and not react_tag \
|
||||
and not private_chathistory \
|
||||
and not whowas and not list and not lusers and not userhost and not time and not info \
|
||||
and not list and not lusers and not time and not info \
|
||||
$(EXTRA_SELECTORS)
|
||||
|
||||
SOLANUM_SELECTORS := \
|
||||
|
|
|
@ -72,6 +72,7 @@ TEMPLATE_CONFIG = """
|
|||
<module name="monitor">
|
||||
<module name="m_muteban"> # for testing mute extbans
|
||||
<module name="namesx"> # For multi-prefix
|
||||
<module name="noctcp">
|
||||
<module name="sasl">
|
||||
<module name="uhnames"> # For userhost-in-names
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ TEMPLATE_CONFIG = """
|
|||
|
||||
[Options]
|
||||
MorePrivacy = no # by default, always replies to WHOWAS with ERR_WASNOSUCHNICK
|
||||
PAM = no
|
||||
|
||||
[Operator]
|
||||
Name = operuser
|
||||
|
|
|
@ -116,20 +116,7 @@ NETWORK_CONFIG_CONFIG = """
|
|||
],
|
||||
|
||||
"alias_users": [
|
||||
{
|
||||
"nick": "ChanServ",
|
||||
"user": "ChanServ",
|
||||
"host": "services.",
|
||||
"realname": "Channel services compatibility layer",
|
||||
"command_alias": "CS"
|
||||
},
|
||||
{
|
||||
"nick": "NickServ",
|
||||
"user": "NickServ",
|
||||
"host": "services.",
|
||||
"realname": "Account services compatibility layer",
|
||||
"command_alias": "NS"
|
||||
}
|
||||
%(services_alias_users)s
|
||||
],
|
||||
|
||||
"default_roles": {
|
||||
|
@ -160,6 +147,23 @@ NETWORK_CONFIG_CONFIG = """
|
|||
}
|
||||
"""
|
||||
|
||||
SERVICES_ALIAS_USERS = """
|
||||
{
|
||||
"nick": "ChanServ",
|
||||
"user": "ChanServ",
|
||||
"host": "services.",
|
||||
"realname": "Channel services compatibility layer",
|
||||
"command_alias": "CS"
|
||||
},
|
||||
{
|
||||
"nick": "NickServ",
|
||||
"user": "NickServ",
|
||||
"host": "services.",
|
||||
"realname": "Account services compatibility layer",
|
||||
"command_alias": "NS"
|
||||
}
|
||||
"""
|
||||
|
||||
SERVER_CONFIG = """
|
||||
{
|
||||
"server_id": 1,
|
||||
|
@ -374,6 +378,7 @@ class SableController(BaseServerController, DirectoryBasedController):
|
|||
.strip(),
|
||||
services_management_hostname=services_management_hostname,
|
||||
services_management_port=services_management_port,
|
||||
services_alias_users=SERVICES_ALIAS_USERS if run_services else "",
|
||||
)
|
||||
|
||||
with self.open_file("configs/network.conf") as fd:
|
||||
|
|
|
@ -245,8 +245,19 @@ def build_test_table(
|
|||
# TODO: only hash test parameter
|
||||
row_anchor = md5sum(row_anchor)
|
||||
|
||||
doc = docstring(
|
||||
getattr(getattr(module, class_name), test_name.split("[")[0])
|
||||
)
|
||||
row = HTML.tr(
|
||||
HTML.th(HTML.a(test_name, href=f"#{row_anchor}"), class_="test-name"),
|
||||
HTML.th(
|
||||
HTML.details(
|
||||
HTML.summary(HTML.a(test_name, href=f"#{row_anchor}")),
|
||||
doc,
|
||||
)
|
||||
if doc
|
||||
else HTML.a(test_name, href=f"#{row_anchor}"),
|
||||
class_="test-name",
|
||||
),
|
||||
id=row_anchor,
|
||||
)
|
||||
rows.append(row)
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
"""
|
||||
Handles ambiguities of RFCs.
|
||||
"""
|
||||
|
||||
from typing import List
|
||||
|
||||
|
||||
def normalize_namreply_params(params: List[str]) -> List[str]:
|
||||
# So… RFC 2812 says:
|
||||
# "( "=" / "*" / "@" ) <channel>
|
||||
# :[ "@" / "+" ] <nick> *( " " [ "@" / "+" ] <nick> )
|
||||
# but spaces seem to be missing (eg. before the colon), so we
|
||||
# don't know if there should be one before the <channel> and its
|
||||
# prefix.
|
||||
# So let's normalize this to “with space”, and strip spaces at the
|
||||
# end of the nick list.
|
||||
params = list(params) # copy the list
|
||||
if len(params) == 3:
|
||||
assert params[1][0] in "=*@", params
|
||||
params.insert(1, params[1][0])
|
||||
params[2] = params[2][1:]
|
||||
params[3] = params[3].rstrip()
|
||||
return params
|
|
@ -15,7 +15,7 @@ TAG_ESCAPE = [
|
|||
unescape_tag_value = MultipleReplacer(dict(map(lambda x: (x[1], x[0]), TAG_ESCAPE)))
|
||||
|
||||
# TODO: validate host
|
||||
tag_key_validator = re.compile(r"\+?(\S+/)?[a-zA-Z0-9-]+")
|
||||
tag_key_validator = re.compile(r"^\+?(\S+/)?[a-zA-Z0-9-]+$")
|
||||
|
||||
|
||||
def parse_tags(s: str) -> Dict[str, Optional[str]]:
|
||||
|
|
|
@ -106,15 +106,15 @@ def match_string(got: Optional[str], expected: Union[str, Operator, None]) -> bo
|
|||
elif isinstance(expected, _AnyStr) and got is not None:
|
||||
return True
|
||||
elif isinstance(expected, StrRe):
|
||||
if got is None or not re.match(expected.regexp, got):
|
||||
if got is None or not re.match(expected.regexp + "$", got):
|
||||
return False
|
||||
elif isinstance(expected, OptStrRe):
|
||||
if got is None:
|
||||
return True
|
||||
if not re.match(expected.regexp, got):
|
||||
if not re.match(expected.regexp + "$", got):
|
||||
return False
|
||||
elif isinstance(expected, NotStrRe):
|
||||
if got is None or re.match(expected.regexp, got):
|
||||
if got is None or re.match(expected.regexp + "$", got):
|
||||
return False
|
||||
elif isinstance(expected, InsensitiveStr):
|
||||
if got is None or got.lower() != expected.string.lower():
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
from irctest import cases, runner
|
||||
from irctest.numerics import ERR_CANNOTSENDTOCHAN
|
||||
from irctest.patma import ANYSTR
|
||||
|
||||
|
||||
class NoctcpModeTestCase(cases.BaseServerTestCase):
|
||||
@cases.mark_specifications("Modern")
|
||||
def testNoctcpMode(self):
|
||||
"""
|
||||
"This mode is used in almost all IRC software today. The standard mode letter
|
||||
used for it is `"+C"`.
|
||||
When this mode is set, should not send [CTCP](/ctcp.html) messages, except
|
||||
CTCP Action (also known as `/me`) to the channel.
|
||||
When blocking a message because of this mode, servers SHOULD use
|
||||
ERR_CANNOTSENDTOCHAN"
|
||||
-- TODO add link
|
||||
"""
|
||||
self.connectClient("chanop")
|
||||
|
||||
if "C" not in self.server_support.get("CHANMODES", ""):
|
||||
raise runner.NotImplementedByController("+C (noctcp) channel mode")
|
||||
|
||||
# Both users join:
|
||||
|
||||
self.sendLine(1, "JOIN #chan")
|
||||
self.getMessages(1) # synchronize
|
||||
|
||||
self.connectClient("user")
|
||||
self.sendLine(2, "JOIN #chan")
|
||||
self.getMessages(2)
|
||||
self.getMessages(1)
|
||||
|
||||
# Send ACTION and PING, both should go through:
|
||||
|
||||
self.sendLine(2, "PRIVMSG #chan :\x01ACTION is testing\x01")
|
||||
self.sendLine(2, "PRIVMSG #chan :\x01PING 12345\x01")
|
||||
self.assertEqual(self.getMessages(2), [])
|
||||
|
||||
self.assertEqual(
|
||||
[(m.command, m.params[1]) for m in self.getMessages(1)],
|
||||
[
|
||||
("PRIVMSG", "\x01ACTION is testing\x01"),
|
||||
("PRIVMSG", "\x01PING 12345\x01"),
|
||||
],
|
||||
)
|
||||
|
||||
# Set mode +C:
|
||||
|
||||
self.sendLine(1, "MODE #chan +C")
|
||||
self.getMessages(1)
|
||||
self.getMessages(2)
|
||||
|
||||
# Send ACTION and PING, only ACTION should go through:
|
||||
|
||||
self.sendLine(2, "PRIVMSG #chan :\x01ACTION is testing\x01")
|
||||
self.assertEqual(self.getMessages(2), [])
|
||||
self.sendLine(2, "PRIVMSG #chan :\x01PING 12345\x01")
|
||||
self.assertMessageMatch(
|
||||
self.getMessage(2),
|
||||
command=ERR_CANNOTSENDTOCHAN,
|
||||
params=["user", "#chan", ANYSTR],
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[(m.command, m.params[1]) for m in self.getMessages(1)],
|
||||
[
|
||||
("PRIVMSG", "\x01ACTION is testing\x01"),
|
||||
],
|
||||
)
|
|
@ -56,7 +56,8 @@ class IsupportTestCase(cases.BaseServerTestCase):
|
|||
return
|
||||
|
||||
m = re.match(
|
||||
r"\((?P<modes>[a-zA-Z]+)\)(?P<prefixes>\S+)", self.server_support["PREFIX"]
|
||||
r"^\((?P<modes>[a-zA-Z]+)\)(?P<prefixes>\S+)$",
|
||||
self.server_support["PREFIX"],
|
||||
)
|
||||
self.assertTrue(
|
||||
m,
|
||||
|
@ -117,5 +118,5 @@ class IsupportTestCase(cases.BaseServerTestCase):
|
|||
parts = self.server_support["TARGMAX"].split(",")
|
||||
for part in parts:
|
||||
self.assertTrue(
|
||||
re.match("[A-Z]+:[0-9]*", part), "Invalid TARGMAX key:value: %r", part
|
||||
re.match("^[A-Z]+:[0-9]*$", part), "Invalid TARGMAX key:value: %r", part
|
||||
)
|
||||
|
|
|
@ -6,7 +6,6 @@ The JOIN command (`RFC 1459
|
|||
"""
|
||||
|
||||
from irctest import cases, runner
|
||||
from irctest.irc_utils import ambiguities
|
||||
from irctest.numerics import (
|
||||
ERR_BADCHANMASK,
|
||||
ERR_FORBIDDENCHANNEL,
|
||||
|
@ -61,6 +60,7 @@ class JoinTestCase(cases.BaseServerTestCase):
|
|||
),
|
||||
)
|
||||
|
||||
@cases.xfailIfSoftware(["Bahamut", "irc2"], "trailing space on RPL_NAMREPLY")
|
||||
@cases.mark_specifications("RFC2812")
|
||||
def testJoinNamreply(self):
|
||||
"""“353 RPL_NAMREPLY
|
||||
|
@ -75,33 +75,23 @@ class JoinTestCase(cases.BaseServerTestCase):
|
|||
|
||||
for m in self.getMessages(1):
|
||||
if m.command == "353":
|
||||
self.assertIn(
|
||||
len(m.params),
|
||||
(3, 4),
|
||||
m,
|
||||
fail_msg="RPL_NAM_REPLY with number of arguments "
|
||||
"<3 or >4: {msg}",
|
||||
self.assertMessageMatch(
|
||||
m, params=["foo", StrRe(r"[=\*@]"), "#chan", StrRe("[@+]?foo")]
|
||||
)
|
||||
params = ambiguities.normalize_namreply_params(m.params)
|
||||
self.assertIn(
|
||||
params[1],
|
||||
"=*@",
|
||||
|
||||
self.connectClient("bar")
|
||||
self.sendLine(2, "JOIN #chan")
|
||||
|
||||
for m in self.getMessages(2):
|
||||
if m.command == "353":
|
||||
self.assertMessageMatch(
|
||||
m,
|
||||
fail_msg="Bad channel prefix: {item} not in {list}: {msg}",
|
||||
)
|
||||
self.assertEqual(
|
||||
params[2],
|
||||
"#chan",
|
||||
m,
|
||||
fail_msg="Bad channel name: {got} instead of " "{expects}: {msg}",
|
||||
)
|
||||
self.assertIn(
|
||||
params[3],
|
||||
{"foo", "@foo", "+foo"},
|
||||
m,
|
||||
fail_msg="Bad user list: should contain only user "
|
||||
'"foo" with an optional "+" or "@" prefix, but got: '
|
||||
"{msg}",
|
||||
params=[
|
||||
"bar",
|
||||
StrRe(r"[=\*@]"),
|
||||
"#chan",
|
||||
StrRe("([@+]?foo bar|bar [@+]?foo)"),
|
||||
],
|
||||
)
|
||||
|
||||
def testJoinTwice(self):
|
||||
|
@ -115,34 +105,8 @@ class JoinTestCase(cases.BaseServerTestCase):
|
|||
# if the join is successful, or has an error among the given set.
|
||||
for m in self.getMessages(1):
|
||||
if m.command == "353":
|
||||
self.assertIn(
|
||||
len(m.params),
|
||||
(3, 4),
|
||||
m,
|
||||
fail_msg="RPL_NAM_REPLY with number of arguments "
|
||||
"<3 or >4: {msg}",
|
||||
)
|
||||
params = ambiguities.normalize_namreply_params(m.params)
|
||||
self.assertIn(
|
||||
params[1],
|
||||
"=*@",
|
||||
m,
|
||||
fail_msg="Bad channel prefix: {item} not in {list}: {msg}",
|
||||
)
|
||||
self.assertEqual(
|
||||
params[2],
|
||||
"#chan",
|
||||
m,
|
||||
fail_msg="Bad channel name: {got} instead of " "{expects}: {msg}",
|
||||
)
|
||||
self.assertIn(
|
||||
params[3],
|
||||
{"foo", "@foo", "+foo"},
|
||||
m,
|
||||
fail_msg='Bad user list after user "foo" joined twice '
|
||||
"the same channel: should contain only user "
|
||||
'"foo" with an optional "+" or "@" prefix, but got: '
|
||||
"{msg}",
|
||||
self.assertMessageMatch(
|
||||
m, params=["foo", StrRe(r"[=\*@]"), "#chan", StrRe("[@+]?foo")]
|
||||
)
|
||||
|
||||
def testJoinPartiallyInvalid(self):
|
||||
|
|
|
@ -24,11 +24,6 @@ class MultiPrefixTestCase(cases.BaseServerTestCase):
|
|||
|
||||
self.sendLine(1, "NAMES #chan")
|
||||
reply = self.getMessage(1)
|
||||
self.assertMessageMatch(
|
||||
reply,
|
||||
command="353",
|
||||
fail_msg="Expected NAMES response (353) with @+foo, got: {msg}",
|
||||
)
|
||||
self.assertMessageMatch(
|
||||
reply,
|
||||
command="353",
|
||||
|
@ -47,9 +42,57 @@ class MultiPrefixTestCase(cases.BaseServerTestCase):
|
|||
8,
|
||||
"Expected WHO response (352) with 8 params, got: {msg}".format(msg=msg),
|
||||
)
|
||||
self.assertTrue(
|
||||
"@+" in msg.params[6],
|
||||
self.assertIn(
|
||||
"@+",
|
||||
msg.params[6],
|
||||
'Expected WHO response (352) with "@+" in param 7, got: {msg}'.format(
|
||||
msg=msg
|
||||
),
|
||||
)
|
||||
|
||||
@cases.xfailIfSoftware(
|
||||
["irc2", "Bahamut"], "irc2 and Bahamut send a trailing space"
|
||||
)
|
||||
def testNoMultiPrefix(self):
|
||||
"""When not requested, only the highest prefix should be sent"""
|
||||
self.connectClient("foo")
|
||||
self.joinChannel(1, "#chan")
|
||||
self.sendLine(1, "MODE #chan +v foo")
|
||||
self.getMessages(1)
|
||||
|
||||
# TODO(dan): Make sure +v is voice
|
||||
|
||||
self.sendLine(1, "NAMES #chan")
|
||||
reply = self.getMessage(1)
|
||||
self.assertMessageMatch(
|
||||
reply,
|
||||
command="353",
|
||||
params=["foo", ANYSTR, "#chan", "@foo"],
|
||||
fail_msg="Expected NAMES response (353) with @foo, got: {msg}",
|
||||
)
|
||||
self.getMessages(1)
|
||||
|
||||
self.sendLine(1, "WHO #chan")
|
||||
msg = self.getMessage(1)
|
||||
self.assertEqual(
|
||||
msg.command, "352", msg, fail_msg="Expected WHO response (352), got: {msg}"
|
||||
)
|
||||
self.assertGreaterEqual(
|
||||
len(msg.params),
|
||||
8,
|
||||
"Expected WHO response (352) with 8 params, got: {msg}".format(msg=msg),
|
||||
)
|
||||
self.assertIn(
|
||||
"@",
|
||||
msg.params[6],
|
||||
'Expected WHO response (352) with "@" in param 7, got: {msg}'.format(
|
||||
msg=msg
|
||||
),
|
||||
)
|
||||
self.assertNotIn(
|
||||
"+",
|
||||
msg.params[6],
|
||||
'Expected WHO response (352) with no "+" in param 7, got: {msg}'.format(
|
||||
msg=msg
|
||||
),
|
||||
)
|
||||
|
|
|
@ -11,7 +11,7 @@ from irctest.patma import ANYSTR, StrRe
|
|||
|
||||
|
||||
class NamesTestCase(cases.BaseServerTestCase):
|
||||
def _testNames(self, symbol):
|
||||
def _testNames(self, symbol: bool, allow_trailing_space: bool):
|
||||
self.connectClient("nick1")
|
||||
self.sendLine(1, "JOIN #chan")
|
||||
self.getMessages(1)
|
||||
|
@ -31,7 +31,10 @@ class NamesTestCase(cases.BaseServerTestCase):
|
|||
"nick1",
|
||||
*(["="] if symbol else []),
|
||||
"#chan",
|
||||
StrRe("(nick2 @nick1|@nick1 nick2)"),
|
||||
StrRe(
|
||||
"(nick2 @nick1|@nick1 nick2)"
|
||||
+ (" ?" if allow_trailing_space else "")
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -44,20 +47,59 @@ class NamesTestCase(cases.BaseServerTestCase):
|
|||
@cases.mark_specifications("RFC1459", deprecated=True)
|
||||
def testNames1459(self):
|
||||
"""
|
||||
https://modern.ircdocs.horse/#names-message
|
||||
https://datatracker.ietf.org/doc/html/rfc1459#section-4.2.5
|
||||
https://datatracker.ietf.org/doc/html/rfc2812#section-3.2.5
|
||||
"""
|
||||
self._testNames(symbol=False)
|
||||
self._testNames(symbol=False, allow_trailing_space=True)
|
||||
|
||||
@cases.mark_specifications("RFC1459", "RFC2812", "Modern")
|
||||
@cases.mark_specifications("RFC2812", "Modern")
|
||||
def testNames2812(self):
|
||||
"""
|
||||
https://modern.ircdocs.horse/#names-message
|
||||
https://datatracker.ietf.org/doc/html/rfc1459#section-4.2.5
|
||||
https://datatracker.ietf.org/doc/html/rfc2812#section-3.2.5
|
||||
"""
|
||||
self._testNames(symbol=True)
|
||||
self._testNames(symbol=True, allow_trailing_space=True)
|
||||
|
||||
@cases.mark_specifications("Modern")
|
||||
@cases.xfailIfSoftware(
|
||||
["Bahamut", "irc2"], "Bahamut and irc2 send a trailing space in RPL_NAMREPLY"
|
||||
)
|
||||
def testNamesModern(self):
|
||||
"""
|
||||
https://modern.ircdocs.horse/#names-message
|
||||
"""
|
||||
self._testNames(symbol=True, allow_trailing_space=False)
|
||||
|
||||
@cases.mark_specifications("RFC2812", "Modern")
|
||||
def testNames2812Secret(self):
|
||||
"""The symbol sent for a secret channel is `@` instead of `=`:
|
||||
https://datatracker.ietf.org/doc/html/rfc2812#section-3.2.5
|
||||
https://modern.ircdocs.horse/#rplnamreply-353
|
||||
"""
|
||||
self.connectClient("nick1")
|
||||
self.sendLine(1, "JOIN #chan")
|
||||
# enable secret channel mode
|
||||
self.sendLine(1, "MODE #chan +s")
|
||||
self.getMessages(1)
|
||||
self.sendLine(1, "NAMES #chan")
|
||||
messages = self.getMessages(1)
|
||||
self.assertMessageMatch(
|
||||
messages[0],
|
||||
command=RPL_NAMREPLY,
|
||||
params=["nick1", "@", "#chan", StrRe("@nick1 ?")],
|
||||
)
|
||||
self.assertMessageMatch(
|
||||
messages[1],
|
||||
command=RPL_ENDOFNAMES,
|
||||
params=["nick1", "#chan", ANYSTR],
|
||||
)
|
||||
|
||||
self.connectClient("nick2")
|
||||
self.sendLine(2, "JOIN #chan")
|
||||
namreplies = [msg for msg in self.getMessages(2) if msg.command == RPL_NAMREPLY]
|
||||
self.assertNotEqual(len(namreplies), 0)
|
||||
for msg in namreplies:
|
||||
self.assertMessageMatch(
|
||||
msg, command=RPL_NAMREPLY, params=["nick2", "@", "#chan", ANYSTR]
|
||||
)
|
||||
|
||||
def _testNamesMultipleChannels(self, symbol):
|
||||
self.connectClient("nick1")
|
||||
|
|
|
@ -57,8 +57,16 @@ class Utf8TestCase(cases.BaseServerTestCase):
|
|||
self.sendLine(2, "NICK bar")
|
||||
self.clients[2].conn.sendall(b"USER username * * :i\xe8rc\xe9\r\n")
|
||||
|
||||
d = self.clients[2].conn.recv(1024)
|
||||
if b"FAIL " in d or b"468 " in d: # ERR_INVALIDUSERNAME
|
||||
d = b""
|
||||
while True:
|
||||
try:
|
||||
buf = self.clients[2].conn.recv(1024)
|
||||
except TimeoutError:
|
||||
break
|
||||
if d and not buf:
|
||||
break
|
||||
d += buf
|
||||
if b"FAIL " in d or b"ERROR " in d or b"468 " in d: # ERR_INVALIDUSERNAME
|
||||
return # nothing more to test
|
||||
self.assertIn(b"001 ", d)
|
||||
|
||||
|
@ -72,14 +80,21 @@ class Utf8TestCase(cases.BaseServerTestCase):
|
|||
|
||||
self.addClient()
|
||||
self.sendLine(2, "NICK bar")
|
||||
self.sendLine(2, "USER 😊😊😊😊😊😊😊😊😊😊 * * :realname")
|
||||
m = self.getRegistrationMessage(2)
|
||||
if m.command in ("FAIL", "468"): # ERR_INVALIDUSERNAME
|
||||
self.clients[2].conn.sendall(b"USER \xe8rc\xe9 * * :readlname\r\n")
|
||||
|
||||
d = b""
|
||||
while True:
|
||||
try:
|
||||
buf = self.clients[2].conn.recv(1024)
|
||||
except TimeoutError:
|
||||
break
|
||||
if d and not buf:
|
||||
break
|
||||
d += buf
|
||||
if b"FAIL " in d or b"ERROR " in d or b"468 " in d: # ERR_INVALIDUSERNAME
|
||||
return # nothing more to test
|
||||
self.assertMessageMatch(
|
||||
m,
|
||||
command="001",
|
||||
)
|
||||
self.assertIn(b"001 ", d)
|
||||
|
||||
self.sendLine(2, "WHOIS bar")
|
||||
self.getMessages(2)
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ class BaseWhoTestCase:
|
|||
class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
||||
@cases.mark_specifications("Modern")
|
||||
def testWhoStar(self):
|
||||
if self.controller.software_name in ("Bahamut", "Sable"):
|
||||
if self.controller.software_name in ("Bahamut",):
|
||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||
|
||||
self._init()
|
||||
|
@ -118,7 +118,7 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
|||
)
|
||||
@cases.mark_specifications("Modern")
|
||||
def testWhoNick(self, mask):
|
||||
if "*" in mask and self.controller.software_name in ("Bahamut", "Sable"):
|
||||
if "*" in mask and self.controller.software_name in ("Bahamut",):
|
||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||
|
||||
self._init()
|
||||
|
@ -148,7 +148,7 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
|||
ids=["username", "realname-mask", "hostname"],
|
||||
)
|
||||
def testWhoUsernameRealName(self, mask):
|
||||
if "*" in mask and self.controller.software_name in ("Bahamut", "Sable"):
|
||||
if "*" in mask and self.controller.software_name in ("Bahamut",):
|
||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||
|
||||
self._init()
|
||||
|
@ -201,7 +201,7 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
|||
)
|
||||
@cases.mark_specifications("Modern")
|
||||
def testWhoNickAway(self, mask):
|
||||
if "*" in mask and self.controller.software_name in ("Bahamut", "Sable"):
|
||||
if "*" in mask and self.controller.software_name in ("Bahamut",):
|
||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||
|
||||
self._init()
|
||||
|
@ -235,7 +235,7 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
|||
)
|
||||
@cases.mark_specifications("Modern")
|
||||
def testWhoNickOper(self, mask):
|
||||
if "*" in mask and self.controller.software_name in ("Bahamut", "Sable"):
|
||||
if "*" in mask and self.controller.software_name in ("Bahamut",):
|
||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||
|
||||
self._init()
|
||||
|
@ -274,7 +274,7 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
|||
)
|
||||
@cases.mark_specifications("Modern")
|
||||
def testWhoNickAwayAndOper(self, mask):
|
||||
if "*" in mask and self.controller.software_name in ("Bahamut", "Sable"):
|
||||
if "*" in mask and self.controller.software_name in ("Bahamut",):
|
||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||
|
||||
self._init()
|
||||
|
@ -308,7 +308,7 @@ class WhoTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
|||
@pytest.mark.parametrize("mask", ["#chan", "#CHAN"], ids=["exact", "casefolded"])
|
||||
@cases.mark_specifications("Modern")
|
||||
def testWhoChan(self, mask):
|
||||
if "*" in mask and self.controller.software_name in ("Bahamut", "Sable"):
|
||||
if "*" in mask and self.controller.software_name in ("Bahamut",):
|
||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||
|
||||
self._init()
|
||||
|
@ -632,7 +632,7 @@ class WhoServicesTestCase(BaseWhoTestCase, cases.BaseServerTestCase):
|
|||
class WhoInvisibleTestCase(cases.BaseServerTestCase):
|
||||
@cases.mark_specifications("Modern")
|
||||
def testWhoInvisible(self):
|
||||
if self.controller.software_name in ("Bahamut", "Sable"):
|
||||
if self.controller.software_name in ("Bahamut",):
|
||||
raise runner.OptionalExtensionNotSupported("WHO mask")
|
||||
|
||||
self.connectClient("evan", name="evan")
|
||||
|
|
|
@ -97,7 +97,9 @@ class _WhoisTestMixin(cases.BaseServerTestCase):
|
|||
params=[
|
||||
"nick1",
|
||||
"nick2",
|
||||
StrRe("(@#chan1 @#chan2|@#chan2 @#chan1)"),
|
||||
# trailing space was required by the RFCs, and Modern explicitly
|
||||
# allows it
|
||||
StrRe("(@#chan1 @#chan2|@#chan2 @#chan1) ?"),
|
||||
],
|
||||
)
|
||||
elif m.command == RPL_WHOISSPECIAL:
|
||||
|
|
|
@ -154,6 +154,9 @@ class WhowasTestCase(cases.BaseServerTestCase):
|
|||
except ConnectionClosed:
|
||||
pass
|
||||
|
||||
if self.controller.software_name == "Sable":
|
||||
time.sleep(1) # may take a little while to record the historical user
|
||||
|
||||
self.sendLine(1, whowas_command)
|
||||
|
||||
messages = self.getMessages(1)
|
||||
|
|
|
@ -230,7 +230,7 @@ software:
|
|||
name: ngircd
|
||||
repository: ngircd/ngircd
|
||||
refs:
|
||||
stable: 0714466af88d71d6c395629cd7fb624b099507d4 # two years ahead of rel-26.1
|
||||
stable: 3e3f6cbeceefd9357b53b27c2386bb39306ab353 # three years ahead of rel-26.1
|
||||
release: null
|
||||
devel: master
|
||||
devel_release: null
|
||||
|
@ -249,7 +249,7 @@ software:
|
|||
name: Sable
|
||||
repository: Libera-Chat/sable
|
||||
refs:
|
||||
stable: fe337a036c3ab5f8548e2578b65568e628f4c32f
|
||||
stable: b9deaa930c49f2939d9a584bedbfc3236da0d707
|
||||
release: null
|
||||
devel: master
|
||||
devel_release: null
|
||||
|
|
Loading…
Reference in New Issue