Compare commits

...

11 Commits

Author SHA1 Message Date
Val Lorentz b69fad7c42
Merge d7bc01c656 into f4a01cfe49 2024-04-14 20:00:19 +00:00
Val Lorentz d7bc01c656 and on irc2 2024-04-14 22:00:14 +02:00
Val Lorentz 038d1fff07 Mark testNamesModern as failing on Bahamut 2024-04-14 21:45:22 +02:00
Val Lorentz bd72b514d0 Merge branch 'master' into re-match 2024-04-14 21:08:41 +02:00
Val Lorentz f4a01cfe49
Enable CAP tests for Sable (#267)
It now implements userhost-in-names and multi-prefix, which these tests depend on
2024-04-14 21:07:29 +02:00
Val Lorentz e6dfb87759
testMonitorForbidsMasks: Allow ERR_ERRONEUSNICKNAME reply (#266)
This is returned by Sable
2024-04-14 20:01:28 +02:00
Val Lorentz 2ae612c68f
Makefile: Add selectors in preparation for Sable adding message-tags support (#264)
Some tests Sable would fail are currently disabled only because Sable does not
support message-tags; but it probably will in the near future.
2024-04-13 14:41:45 +02:00
Val Lorentz d908699674
chathistory: Skip assertions based on MSGREFTYPES (#263)
This will be useful to test Sable, which does not support CHATHISTORY
with msgid= yet
2024-04-13 14:41:13 +02:00
Sadie Powell 61ae4bcf9e
Relink the modules directory as well as the lib directory. (#260) 2024-04-04 17:47:48 +02:00
Sadie Powell 0c5c91368a
Pass --nopid to Anope. (#259) 2024-03-21 21:04:13 +01:00
Shivaram Lingamneni c0e6ca4dde
add a test for WHOIS on nonexistent users (#258)
* add a test for WHOIS on nonexistent users

* skip test in Sable for now
2024-03-19 10:30:44 -04:00
11 changed files with 250 additions and 173 deletions

View File

@ -1106,7 +1106,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
path: sable path: sable
ref: dcf8b53cac54f460b86861908d36d67969cf1eb2 ref: fe337a036c3ab5f8548e2578b65568e628f4c32f
repository: Libera-Chat/sable repository: Libera-Chat/sable
- name: Install rust toolchain - name: Install rust toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1

View File

@ -83,10 +83,16 @@ LIMNORIA_SELECTORS := \
(foo or not foo) \ (foo or not foo) \
$(EXTRA_SELECTORS) $(EXTRA_SELECTORS)
# Tests marked with arbitrary_client_tags or react_tag can't pass because Sable does not support client tags yet
# Tests marked with private_chathistory can't pass because Sable does not implement CHATHISTORY for DMs
SABLE_SELECTORS := \ SABLE_SELECTORS := \
not Ergo \ not Ergo \
and not deprecated \ and not deprecated \
and not strict \ and not strict \
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 whowas and not list and not lusers and not userhost and not time and not info \
$(EXTRA_SELECTORS) $(EXTRA_SELECTORS)

View File

@ -160,6 +160,7 @@ class _IrcTestCase(Generic[TController]):
def messageDiffers( def messageDiffers(
self, self,
msg: Message, msg: Message,
command: Union[str, None, patma.Operator] = None,
params: Optional[List[Union[str, None, patma.Operator]]] = None, params: Optional[List[Union[str, None, patma.Operator]]] = None,
target: Optional[str] = None, target: Optional[str] = None,
tags: Optional[ tags: Optional[
@ -186,6 +187,14 @@ class _IrcTestCase(Generic[TController]):
msg=msg, msg=msg,
) )
if command is not None and not patma.match_string(msg.command, command):
fail_msg = (
fail_msg or "expected command to match {expects}, got {got}: {msg}"
)
return fail_msg.format(
*extra_format, got=msg.command, expects=command, msg=msg
)
if prefix is not None and not patma.match_string(msg.prefix, prefix): if prefix is not None and not patma.match_string(msg.prefix, prefix):
fail_msg = ( fail_msg = (
fail_msg or "expected prefix to match {expects}, got {got}: {msg}" fail_msg or "expected prefix to match {expects}, got {got}: {msg}"
@ -214,7 +223,7 @@ class _IrcTestCase(Generic[TController]):
or "expected nick to be {expects}, got {got} instead: {msg}" or "expected nick to be {expects}, got {got} instead: {msg}"
) )
return fail_msg.format( return fail_msg.format(
*extra_format, got=got_nick, expects=nick, param=key, msg=msg *extra_format, got=got_nick, expects=nick, msg=msg
) )
return None return None

View File

@ -132,14 +132,16 @@ class AnopeController(BaseServicesController, DirectoryBasedController):
# Config and code need to be in the same directory, *obviously* # Config and code need to be in the same directory, *obviously*
(self.directory / "lib").symlink_to(Path(services_path).parent.parent / "lib") (self.directory / "lib").symlink_to(Path(services_path).parent.parent / "lib")
(self.directory / "modules").symlink_to(
Path(services_path).parent.parent / "modules"
)
self.proc = subprocess.Popen( self.proc = subprocess.Popen(
[ [
"anope", "anope",
"-n", # don't fork "--config=services.conf", # can't be an absolute path in 2.0
"--config=services.conf", # can't be an absolute path "--nofork", # don't fork
# "--logdir", "--nopid", # don't write a pid
# f"/tmp/services-{server_port}.log",
], ],
cwd=self.directory, cwd=self.directory,
# stdout=subprocess.DEVNULL, # stdout=subprocess.DEVNULL,

View File

@ -173,7 +173,7 @@ MESSAGE_SPECS: List[Tuple[Dict, List[str], List[str], List[str]]] = [
], ],
# and they each error with: # and they each error with:
[ [
"expected command to be PRIVMSG, got PRIVMG", "expected command to match PRIVMSG, got PRIVMG",
"expected tags to match {'tag1': 'bar', RemainingKeys(ANYSTR): ANYOPTSTR}, got {'tag1': 'value1'}", "expected tags to match {'tag1': 'bar', RemainingKeys(ANYSTR): ANYOPTSTR}, got {'tag1': 'value1'}",
"expected params to match ['#chan', 'hello'], got ['#chan', 'hello2']", "expected params to match ['#chan', 'hello'], got ['#chan', 'hello2']",
"expected params to match ['#chan', 'hello'], got ['#chan2', 'hello']", "expected params to match ['#chan', 'hello'], got ['#chan2', 'hello']",
@ -206,7 +206,7 @@ MESSAGE_SPECS: List[Tuple[Dict, List[str], List[str], List[str]]] = [
], ],
# and they each error with: # and they each error with:
[ [
"expected command to be PRIVMSG, got PRIVMG", "expected command to match PRIVMSG, got PRIVMG",
"expected tags to match {StrRe(r'tag[12]'): 'bar', RemainingKeys(ANYSTR): ANYOPTSTR}, got {'tag1': 'value1'}", "expected tags to match {StrRe(r'tag[12]'): 'bar', RemainingKeys(ANYSTR): ANYOPTSTR}, got {'tag1': 'value1'}",
"expected params to match ['#chan', 'hello'], got ['#chan', 'hello2']", "expected params to match ['#chan', 'hello'], got ['#chan', 'hello2']",
"expected params to match ['#chan', 'hello'], got ['#chan2', 'hello']", "expected params to match ['#chan', 'hello'], got ['#chan2', 'hello']",
@ -235,7 +235,7 @@ MESSAGE_SPECS: List[Tuple[Dict, List[str], List[str], List[str]]] = [
], ],
# and they each error with: # and they each error with:
[ [
"expected command to be PRIVMSG, got PRIVMG", "expected command to match PRIVMSG, got PRIVMG",
"expected tags to match {'tag1': 'bar', RemainingKeys(NotStrRe(r'tag2')): ANYOPTSTR}, got {'tag1': 'value1'}", "expected tags to match {'tag1': 'bar', RemainingKeys(NotStrRe(r'tag2')): ANYOPTSTR}, got {'tag1': 'value1'}",
"expected tags to match {'tag1': 'bar', RemainingKeys(NotStrRe(r'tag2')): ANYOPTSTR}, got {'tag1': 'bar', 'tag2': ''}", "expected tags to match {'tag1': 'bar', RemainingKeys(NotStrRe(r'tag2')): ANYOPTSTR}, got {'tag1': 'bar', 'tag2': ''}",
"expected tags to match {'tag1': 'bar', RemainingKeys(NotStrRe(r'tag2')): ANYOPTSTR}, got {'tag1': 'bar', 'tag2': 'baz'}", "expected tags to match {'tag1': 'bar', RemainingKeys(NotStrRe(r'tag2')): ANYOPTSTR}, got {'tag1': 'bar', 'tag2': 'baz'}",
@ -345,7 +345,7 @@ MESSAGE_SPECS: List[Tuple[Dict, List[str], List[str], List[str]]] = [
], ],
# and they each error with: # and they each error with:
[ [
"expected command to be PING, got PONG" "expected command to match PING, got PONG"
] ]
), ),
] ]

View File

@ -56,10 +56,6 @@ class CapTestCase(cases.BaseServerTestCase):
) )
@cases.mark_specifications("IRCv3") @cases.mark_specifications("IRCv3")
@cases.xfailIfSoftware(
["Sable"],
"does not support multi-prefix",
)
def testReqOne(self): def testReqOne(self):
"""Tests requesting a single capability""" """Tests requesting a single capability"""
self.addClient(1) self.addClient(1)
@ -93,7 +89,7 @@ class CapTestCase(cases.BaseServerTestCase):
@cases.mark_specifications("IRCv3") @cases.mark_specifications("IRCv3")
@cases.xfailIfSoftware( @cases.xfailIfSoftware(
["ngIRCd", "Sable"], ["ngIRCd"],
"does not support userhost-in-names", "does not support userhost-in-names",
) )
def testReqTwo(self): def testReqTwo(self):
@ -135,7 +131,7 @@ class CapTestCase(cases.BaseServerTestCase):
@cases.mark_specifications("IRCv3") @cases.mark_specifications("IRCv3")
@cases.xfailIfSoftware( @cases.xfailIfSoftware(
["ngIRCd", "Sable"], ["ngIRCd"],
"does not support userhost-in-names", "does not support userhost-in-names",
) )
def testReqOneThenOne(self): def testReqOneThenOne(self):
@ -187,7 +183,7 @@ class CapTestCase(cases.BaseServerTestCase):
@cases.mark_specifications("IRCv3") @cases.mark_specifications("IRCv3")
@cases.xfailIfSoftware( @cases.xfailIfSoftware(
["ngIRCd", "Sable"], ["ngIRCd"],
"does not support userhost-in-names", "does not support userhost-in-names",
) )
def testReqPostRegistration(self): def testReqPostRegistration(self):

View File

@ -58,6 +58,16 @@ class ChathistoryTestCase(cases.BaseServerTestCase):
def config() -> cases.TestCaseControllerConfig: def config() -> cases.TestCaseControllerConfig:
return cases.TestCaseControllerConfig(chathistory=True) return cases.TestCaseControllerConfig(chathistory=True)
def _supports_msgid(self):
return "msgid" in self.server_support.get(
"MSGREFTYPES", "msgid,timestamp"
).split(",")
def _supports_timestamp(self):
return "timestamp" in self.server_support.get(
"MSGREFTYPES", "msgid,timestamp"
).split(",")
@skip_ngircd @skip_ngircd
def testInvalidTargets(self): def testInvalidTargets(self):
bar, pw = random_name("bar"), random_name("pw") bar, pw = random_name("bar"), random_name("pw")
@ -460,172 +470,195 @@ class ChathistoryTestCase(cases.BaseServerTestCase):
result = self.validate_chathistory_batch(self.getMessages(user), chname) result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[-1:], result) self.assertEqual(echo_messages[-1:], result)
self.sendLine( if self._supports_msgid():
user, self.sendLine(
"CHATHISTORY LATEST %s msgid=%s %d" user,
% (chname, echo_messages[4].msgid, INCLUSIVE_LIMIT), "CHATHISTORY LATEST %s msgid=%s %d"
) % (chname, echo_messages[4].msgid, INCLUSIVE_LIMIT),
result = self.validate_chathistory_batch(self.getMessages(user), chname) )
self.assertEqual(echo_messages[5:], result) result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[5:], result)
self.sendLine( if self._supports_timestamp():
user, self.sendLine(
"CHATHISTORY LATEST %s timestamp=%s %d" user,
% (chname, echo_messages[4].time, INCLUSIVE_LIMIT), "CHATHISTORY LATEST %s timestamp=%s %d"
) % (chname, echo_messages[4].time, INCLUSIVE_LIMIT),
result = self.validate_chathistory_batch(self.getMessages(user), chname) )
self.assertEqual(echo_messages[5:], result) result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[5:], result)
def _validate_chathistory_BEFORE(self, echo_messages, user, chname): def _validate_chathistory_BEFORE(self, echo_messages, user, chname):
INCLUSIVE_LIMIT = len(echo_messages) * 2 INCLUSIVE_LIMIT = len(echo_messages) * 2
self.sendLine( if self._supports_msgid():
user, self.sendLine(
"CHATHISTORY BEFORE %s msgid=%s %d" user,
% (chname, echo_messages[6].msgid, INCLUSIVE_LIMIT), "CHATHISTORY BEFORE %s msgid=%s %d"
) % (chname, echo_messages[6].msgid, INCLUSIVE_LIMIT),
result = self.validate_chathistory_batch(self.getMessages(user), chname) )
self.assertEqual(echo_messages[:6], result) result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[:6], result)
self.sendLine( if self._supports_timestamp():
user, self.sendLine(
"CHATHISTORY BEFORE %s timestamp=%s %d" user,
% (chname, echo_messages[6].time, INCLUSIVE_LIMIT), "CHATHISTORY BEFORE %s timestamp=%s %d"
) % (chname, echo_messages[6].time, INCLUSIVE_LIMIT),
result = self.validate_chathistory_batch(self.getMessages(user), chname) )
self.assertEqual(echo_messages[:6], result) result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[:6], result)
self.sendLine( self.sendLine(
user, user,
"CHATHISTORY BEFORE %s timestamp=%s %d" "CHATHISTORY BEFORE %s timestamp=%s %d"
% (chname, echo_messages[6].time, 2), % (chname, echo_messages[6].time, 2),
) )
result = self.validate_chathistory_batch(self.getMessages(user), chname) result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[4:6], result) self.assertEqual(echo_messages[4:6], result)
def _validate_chathistory_AFTER(self, echo_messages, user, chname): def _validate_chathistory_AFTER(self, echo_messages, user, chname):
INCLUSIVE_LIMIT = len(echo_messages) * 2 INCLUSIVE_LIMIT = len(echo_messages) * 2
self.sendLine( if self._supports_msgid():
user, self.sendLine(
"CHATHISTORY AFTER %s msgid=%s %d" user,
% (chname, echo_messages[3].msgid, INCLUSIVE_LIMIT), "CHATHISTORY AFTER %s msgid=%s %d"
) % (chname, echo_messages[3].msgid, INCLUSIVE_LIMIT),
result = self.validate_chathistory_batch(self.getMessages(user), chname) )
self.assertEqual(echo_messages[4:], result) result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[4:], result)
self.sendLine( if self._supports_timestamp():
user, self.sendLine(
"CHATHISTORY AFTER %s timestamp=%s %d" user,
% (chname, echo_messages[3].time, INCLUSIVE_LIMIT), "CHATHISTORY AFTER %s timestamp=%s %d"
) % (chname, echo_messages[3].time, INCLUSIVE_LIMIT),
result = self.validate_chathistory_batch(self.getMessages(user), chname) )
self.assertEqual(echo_messages[4:], result) result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[4:], result)
self.sendLine( self.sendLine(
user, user,
"CHATHISTORY AFTER %s timestamp=%s %d" % (chname, echo_messages[3].time, 3), "CHATHISTORY AFTER %s timestamp=%s %d"
) % (chname, echo_messages[3].time, 3),
result = self.validate_chathistory_batch(self.getMessages(user), chname) )
self.assertEqual(echo_messages[4:7], result) result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[4:7], result)
def _validate_chathistory_BETWEEN(self, echo_messages, user, chname): def _validate_chathistory_BETWEEN(self, echo_messages, user, chname):
INCLUSIVE_LIMIT = len(echo_messages) * 2 INCLUSIVE_LIMIT = len(echo_messages) * 2
# BETWEEN forwards and backwards if self._supports_msgid():
self.sendLine( # BETWEEN forwards and backwards
user, self.sendLine(
"CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d" user,
% ( "CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d"
chname, % (
echo_messages[0].msgid, chname,
echo_messages[-1].msgid, echo_messages[0].msgid,
INCLUSIVE_LIMIT, echo_messages[-1].msgid,
), INCLUSIVE_LIMIT,
) ),
result = self.validate_chathistory_batch(self.getMessages(user), chname) )
self.assertEqual(echo_messages[1:-1], result) result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:-1], result)
self.sendLine( self.sendLine(
user, user,
"CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d" "CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d"
% ( % (
chname, chname,
echo_messages[-1].msgid, echo_messages[-1].msgid,
echo_messages[0].msgid, echo_messages[0].msgid,
INCLUSIVE_LIMIT, INCLUSIVE_LIMIT,
), ),
) )
result = self.validate_chathistory_batch(self.getMessages(user), chname) result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:-1], result) self.assertEqual(echo_messages[1:-1], result)
# BETWEEN forwards and backwards with a limit, should get # BETWEEN forwards and backwards with a limit, should get
# different results this time # different results this time
self.sendLine( self.sendLine(
user, user,
"CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d" "CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d"
% (chname, echo_messages[0].msgid, echo_messages[-1].msgid, 3), % (chname, echo_messages[0].msgid, echo_messages[-1].msgid, 3),
) )
result = self.validate_chathistory_batch(self.getMessages(user), chname) result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:4], result) self.assertEqual(echo_messages[1:4], result)
self.sendLine( self.sendLine(
user, user,
"CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d" "CHATHISTORY BETWEEN %s msgid=%s msgid=%s %d"
% (chname, echo_messages[-1].msgid, echo_messages[0].msgid, 3), % (chname, echo_messages[-1].msgid, echo_messages[0].msgid, 3),
) )
result = self.validate_chathistory_batch(self.getMessages(user), chname) result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[-4:-1], result) self.assertEqual(echo_messages[-4:-1], result)
# same stuff again but with timestamps if self._supports_timestamp():
self.sendLine( # same stuff again but with timestamps
user, self.sendLine(
"CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d" user,
% (chname, echo_messages[0].time, echo_messages[-1].time, INCLUSIVE_LIMIT), "CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d"
) % (
result = self.validate_chathistory_batch(self.getMessages(user), chname) chname,
self.assertEqual(echo_messages[1:-1], result) echo_messages[0].time,
self.sendLine( echo_messages[-1].time,
user, INCLUSIVE_LIMIT,
"CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d" ),
% (chname, echo_messages[-1].time, echo_messages[0].time, INCLUSIVE_LIMIT), )
) result = self.validate_chathistory_batch(self.getMessages(user), chname)
result = self.validate_chathistory_batch(self.getMessages(user), chname) self.assertEqual(echo_messages[1:-1], result)
self.assertEqual(echo_messages[1:-1], result) self.sendLine(
self.sendLine( user,
user, "CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d"
"CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d" % (
% (chname, echo_messages[0].time, echo_messages[-1].time, 3), chname,
) echo_messages[-1].time,
result = self.validate_chathistory_batch(self.getMessages(user), chname) echo_messages[0].time,
self.assertEqual(echo_messages[1:4], result) INCLUSIVE_LIMIT,
self.sendLine( ),
user, )
"CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d" result = self.validate_chathistory_batch(self.getMessages(user), chname)
% (chname, echo_messages[-1].time, echo_messages[0].time, 3), self.assertEqual(echo_messages[1:-1], result)
) self.sendLine(
result = self.validate_chathistory_batch(self.getMessages(user), chname) user,
self.assertEqual(echo_messages[-4:-1], result) "CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d"
% (chname, echo_messages[0].time, echo_messages[-1].time, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[1:4], result)
self.sendLine(
user,
"CHATHISTORY BETWEEN %s timestamp=%s timestamp=%s %d"
% (chname, echo_messages[-1].time, echo_messages[0].time, 3),
)
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[-4:-1], result)
def _validate_chathistory_AROUND(self, echo_messages, user, chname): def _validate_chathistory_AROUND(self, echo_messages, user, chname):
self.sendLine( if self._supports_msgid():
user, self.sendLine(
"CHATHISTORY AROUND %s msgid=%s %d" % (chname, echo_messages[7].msgid, 1), user,
) "CHATHISTORY AROUND %s msgid=%s %d"
result = self.validate_chathistory_batch(self.getMessages(user), chname) % (chname, echo_messages[7].msgid, 1),
self.assertEqual([echo_messages[7]], result) )
result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual([echo_messages[7]], result)
self.sendLine( self.sendLine(
user, user,
"CHATHISTORY AROUND %s msgid=%s %d" % (chname, echo_messages[7].msgid, 3), "CHATHISTORY AROUND %s msgid=%s %d"
) % (chname, echo_messages[7].msgid, 3),
result = self.validate_chathistory_batch(self.getMessages(user), chname) )
self.assertEqual(echo_messages[6:9], result) result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertEqual(echo_messages[6:9], result)
self.sendLine( if self._supports_timestamp():
user, self.sendLine(
"CHATHISTORY AROUND %s timestamp=%s %d" user,
% (chname, echo_messages[7].time, 3), "CHATHISTORY AROUND %s timestamp=%s %d"
) % (chname, echo_messages[7].time, 3),
result = self.validate_chathistory_batch(self.getMessages(user), chname) )
self.assertIn(echo_messages[7], result) result = self.validate_chathistory_batch(self.getMessages(user), chname)
self.assertIn(echo_messages[7], result)
@pytest.mark.arbitrary_client_tags @pytest.mark.arbitrary_client_tags
@skip_ngircd @skip_ngircd

View File

@ -8,6 +8,7 @@ import pytest
from irctest import cases, runner from irctest import cases, runner
from irctest.client_mock import NoMessageException from irctest.client_mock import NoMessageException
from irctest.numerics import ( from irctest.numerics import (
ERR_ERRONEUSNICKNAME,
RPL_ENDOFMONLIST, RPL_ENDOFMONLIST,
RPL_MONLIST, RPL_MONLIST,
RPL_MONOFFLINE, RPL_MONOFFLINE,
@ -190,14 +191,15 @@ class MonitorTestCase(_BaseMonitorTestCase):
self.check_server_support() self.check_server_support()
self.sendLine(1, "MONITOR + *!username@localhost") self.sendLine(1, "MONITOR + *!username@localhost")
self.sendLine(1, "MONITOR + *!username@127.0.0.1") self.sendLine(1, "MONITOR + *!username@127.0.0.1")
expected_command = StrRe(f"({RPL_MONOFFLINE}|{ERR_ERRONEUSNICKNAME})")
try: try:
m = self.getMessage(1) m = self.getMessage(1)
self.assertMessageMatch(m, command="731") self.assertMessageMatch(m, command=expected_command)
except NoMessageException: except NoMessageException:
pass pass
else: else:
m = self.getMessage(1) m = self.getMessage(1)
self.assertMessageMatch(m, command="731") self.assertMessageMatch(m, command=expected_command)
self.connectClient("bar") self.connectClient("bar")
try: try:
m = self.getMessage(1) m = self.getMessage(1)

View File

@ -11,7 +11,7 @@ from irctest.patma import ANYSTR, StrRe
class NamesTestCase(cases.BaseServerTestCase): class NamesTestCase(cases.BaseServerTestCase):
def _testNames(self, symbol): def _testNames(self, symbol: bool, allow_trailing_space: bool):
self.connectClient("nick1") self.connectClient("nick1")
self.sendLine(1, "JOIN #chan") self.sendLine(1, "JOIN #chan")
self.getMessages(1) self.getMessages(1)
@ -31,7 +31,10 @@ class NamesTestCase(cases.BaseServerTestCase):
"nick1", "nick1",
*(["="] if symbol else []), *(["="] if symbol else []),
"#chan", "#chan",
StrRe("(nick2 @nick1|@nick1 nick2)"), StrRe(
"(nick2 @nick1|@nick1 nick2)"
+ (" ?" if allow_trailing_space else "")
),
], ],
) )
@ -44,20 +47,26 @@ class NamesTestCase(cases.BaseServerTestCase):
@cases.mark_specifications("RFC1459", deprecated=True) @cases.mark_specifications("RFC1459", deprecated=True)
def testNames1459(self): 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/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): 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 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)
def _testNamesMultipleChannels(self, symbol): def _testNamesMultipleChannels(self, symbol):
self.connectClient("nick1") self.connectClient("nick1")

View File

@ -8,6 +8,7 @@ import pytest
from irctest import cases from irctest import cases
from irctest.numerics import ( from irctest.numerics import (
ERR_NOSUCHNICK,
RPL_AWAY, RPL_AWAY,
RPL_ENDOFWHOIS, RPL_ENDOFWHOIS,
RPL_WHOISACCOUNT, RPL_WHOISACCOUNT,
@ -219,6 +220,25 @@ class WhoisTestCase(_WhoisTestMixin, cases.BaseServerTestCase):
whois_user.params[3], [nick, username, "~" + username, realname] whois_user.params[3], [nick, username, "~" + username, realname]
) )
@cases.mark_specifications("RFC2812")
@cases.xfailIfSoftware(["Sable"], "https://github.com/Libera-Chat/sable/issues/101")
def testWhoisMissingUser(self):
"""Test WHOIS on a nonexistent nickname."""
self.connectClient("qux", name="qux")
self.sendLine("qux", "WHOIS bar")
messages = self.getMessages("qux")
self.assertEqual(len(messages), 2)
self.assertMessageMatch(
messages[0],
command=ERR_NOSUCHNICK,
params=["qux", "bar", ANYSTR],
)
self.assertMessageMatch(
messages[1],
command=RPL_ENDOFWHOIS,
params=["qux", "bar", ANYSTR],
)
@pytest.mark.parametrize( @pytest.mark.parametrize(
"away,oper", "away,oper",
[(False, False), (True, False), (False, True)], [(False, False), (True, False), (False, True)],

View File

@ -249,7 +249,7 @@ software:
name: Sable name: Sable
repository: Libera-Chat/sable repository: Libera-Chat/sable
refs: refs:
stable: dcf8b53cac54f460b86861908d36d67969cf1eb2 stable: fe337a036c3ab5f8548e2578b65568e628f4c32f
release: null release: null
devel: master devel: master
devel_release: null devel_release: null