diff --git a/Makefile b/Makefile index ad60aba..9978892 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ ANOPE_SELECTORS := \ # buffering tests cannot pass because of issues with UTF-8 handling: https://github.com/DALnet/bahamut/issues/196 # mask tests in test_who.py fail because they are not implemented. +# some HelpTestCase::*[HELP] tests fail because Bahamut forwards /HELP to HelpServ (but not /HELPOP) BAHAMUT_SELECTORS := \ not Ergo \ and not deprecated \ @@ -21,6 +22,7 @@ BAHAMUT_SELECTORS := \ and not buffering \ and not (testWho and not whois and mask) \ and not testWhoStar \ + and (not HelpTestCase or HELPOP) \ $(EXTRA_SELECTORS) # testQuitErrors is very flaky @@ -67,6 +69,7 @@ INSPIRCD_SELECTORS := \ # testKeyValidation[empty] fails because ircu2 returns ERR_NEEDMOREPARAMS on empty keys: https://github.com/UndernetIRC/ircu2/issues/13 # testKickDefaultComment fails because it uses the nick of the kickee rather than the kicker. # testEmptyRealname fails because it uses a default value instead of ERR_NEEDMOREPARAMS. +# HelpTestCase fails because it returns NOTICEs instead of numerics IRCU2_SELECTORS := \ not Ergo \ and not deprecated \ @@ -78,6 +81,7 @@ IRCU2_SELECTORS := \ and not (testKeyValidation and empty) \ and not testKickDefaultComment \ and not testEmptyRealname \ + and not HelpTestCase \ $(EXTRA_SELECTORS) # same justification as ircu2 @@ -94,6 +98,7 @@ SNIRCD_SELECTORS := \ # testListEmpty and testListOne fails because irc2 deprecated LIST # testKickDefaultComment fails because it uses the nick of the kickee rather than the kicker. # testWallopsPrivileges fails because it ignores the command instead of replying ERR_UNKNOWNCOMMAND +# HelpTestCase fails because it returns NOTICEs instead of numerics IRC2_SELECTORS := \ not Ergo \ and not deprecated \ @@ -101,6 +106,7 @@ IRC2_SELECTORS := \ and not testListEmpty and not testListOne \ and not testKickDefaultComment \ and not testWallopsPrivileges \ + and not HelpTestCase \ $(EXTRA_SELECTORS) MAMMON_SELECTORS := \ @@ -113,6 +119,7 @@ MAMMON_SELECTORS := \ # testStarNick: wat # testEmptyRealname fails because it uses a default value instead of ERR_NEEDMOREPARAMS. # chathistory tests fail because they need nicks longer than 9 chars +# HelpTestCase::*[HELP] fails because it returns NOTICEs instead of numerics NGIRCD_SELECTORS := \ not Ergo \ and not deprecated \ @@ -121,6 +128,7 @@ NGIRCD_SELECTORS := \ and not testStarNick \ and not testEmptyRealname \ and not chathistory \ + and (not HelpTestCase or HELPOP) \ $(EXTRA_SELECTORS) # testInviteUnoppedModern is the only strict test that Plexus4 fails @@ -163,6 +171,7 @@ SOPEL_SELECTORS := \ # testChathistory[BETWEEN] fails: https://bugs.unrealircd.org/view.php?id=5952 # testChathistory[AROUND] fails: https://bugs.unrealircd.org/view.php?id=5953 # testWhoAllOpers fails because Unreal skips results when the mask is too broad +# HELP and HELPOP tests fail because Unreal uses custom numerics https://github.com/unrealircd/unrealircd/pull/184 UNREALIRCD_SELECTORS := \ not Ergo \ and not deprecated \ @@ -177,6 +186,7 @@ UNREALIRCD_SELECTORS := \ and not private_chathistory \ and not (testChathistory and (between or around)) \ and not testWhoAllOpers \ + and not HelpTestCase \ $(EXTRA_SELECTORS) .PHONY: all flakes bahamut charybdis ergo inspircd ircu2 snircd irc2 mammon limnoria sopel solanum unrealircd diff --git a/irctest/controllers/inspircd.py b/irctest/controllers/inspircd.py index 5184241..1cd40ba 100644 --- a/irctest/controllers/inspircd.py +++ b/irctest/controllers/inspircd.py @@ -76,6 +76,11 @@ TEMPLATE_CONFIG = """ # For multi-prefix +# HELP/HELPOP + # for the HELP alias + + + # Misc: diff --git a/irctest/controllers/unrealircd.py b/irctest/controllers/unrealircd.py index 62af954..5e8b103 100644 --- a/irctest/controllers/unrealircd.py +++ b/irctest/controllers/unrealircd.py @@ -15,6 +15,7 @@ TEMPLATE_CONFIG = """ include "modules.default.conf"; include "operclass.default.conf"; {extras} +include "help/help.conf"; me {{ name "My.Little.Server"; diff --git a/irctest/runner.py b/irctest/runner.py index f102841..065a133 100644 --- a/irctest/runner.py +++ b/irctest/runner.py @@ -14,6 +14,11 @@ class ImplementationChoice(unittest.SkipTest): ) +class OptionalCommandNotSupported(unittest.SkipTest): + def __str__(self) -> str: + return "Unsupported command: {}".format(self.args[0]) + + class OptionalExtensionNotSupported(unittest.SkipTest): def __str__(self) -> str: return "Unsupported extension: {}".format(self.args[0]) diff --git a/irctest/server_tests/help.py b/irctest/server_tests/help.py new file mode 100644 index 0000000..c5acc65 --- /dev/null +++ b/irctest/server_tests/help.py @@ -0,0 +1,98 @@ +""" +The HELP and HELPOP command. +""" + +import re + +import pytest + +from irctest import cases, runner +from irctest.numerics import ( + ERR_HELPNOTFOUND, + ERR_UNKNOWNCOMMAND, + RPL_ENDOFHELP, + RPL_HELPSTART, + RPL_HELPTXT, +) +from irctest.patma import ANYSTR, StrRe + + +class HelpTestCase(cases.BaseServerTestCase): + def _assertValidHelp(self, messages, subject): + if subject != ANYSTR: + subject = StrRe("(?i)" + re.escape(subject)) + + self.assertMessageMatch( + messages[0], + command=RPL_HELPSTART, + params=["nick", subject, ANYSTR], + fail_msg=f"Expected {RPL_HELPSTART} (RPL_HELPSTART), got: {{msg}}", + ) + + self.assertMessageMatch( + messages[-1], + command=RPL_ENDOFHELP, + params=["nick", subject, ANYSTR], + fail_msg=f"Expected {RPL_ENDOFHELP} (RPL_ENDOFHELP), got: {{msg}}", + ) + + for i in range(1, len(messages) - 1): + self.assertMessageMatch( + messages[i], + command=RPL_HELPTXT, + params=["nick", subject, ANYSTR], + fail_msg=f"Expected {RPL_HELPTXT} (RPL_HELPTXT), got: {{msg}}", + ) + + @pytest.mark.parametrize("command", ["HELP", "HELPOP"]) + @cases.mark_specifications("Modern") + def testHelpNoArg(self, command): + self.connectClient("nick") + self.sendLine(1, f"{command}") + + messages = self.getMessages(1) + + if messages[0].command == ERR_UNKNOWNCOMMAND: + raise runner.OptionalCommandNotSupported(command) + + self._assertValidHelp(messages, ANYSTR) + + @pytest.mark.parametrize("command", ["HELP", "HELPOP"]) + @cases.mark_specifications("Modern") + def testHelpPrivmsg(self, command): + self.connectClient("nick") + self.sendLine(1, f"{command} PRIVMSG") + messages = self.getMessages(1) + + if messages[0].command == ERR_UNKNOWNCOMMAND: + raise runner.OptionalCommandNotSupported(command) + + self._assertValidHelp(messages, "PRIVMSG") + + @pytest.mark.parametrize("command", ["HELP", "HELPOP"]) + @cases.mark_specifications("Modern") + def testHelpUnknownSubject(self, command): + self.connectClient("nick") + self.sendLine(1, f"{command} THISISNOTACOMMAND") + messages = self.getMessages(1) + + if messages[0].command == ERR_UNKNOWNCOMMAND: + raise runner.OptionalCommandNotSupported(command) + + if messages[0].command == ERR_HELPNOTFOUND: + # Inspircd, Hybrid et al + self.assertEqual(len(messages), 1) + self.assertMessageMatch( + messages[0], + command=ERR_HELPNOTFOUND, + params=[ + "nick", + StrRe( + "(?i)THISISNOTACOMMAND" + ), # case-insensitive, for Hybrid and Plexus4 (but not Chary et al) + ANYSTR, + ], + ) + else: + # Unrealircd + self._assertValidHelp(messages, ANYSTR)