diff --git a/irctest/server_tests/names.py b/irctest/server_tests/names.py index 5a79743..801fcac 100644 --- a/irctest/server_tests/names.py +++ b/irctest/server_tests/names.py @@ -5,12 +5,114 @@ The NAMES command (`RFC 1459 `Modern `__) """ -from irctest import cases -from irctest.numerics import RPL_ENDOFNAMES -from irctest.patma import ANYSTR - +from irctest import cases, runner +from irctest.numerics import RPL_ENDOFNAMES, RPL_NAMREPLY +from irctest.patma import ANYSTR, StrRe class NamesTestCase(cases.BaseServerTestCase): + def _testNames(self, symbol): + self.connectClient("nick1") + self.sendLine(1, "JOIN #chan") + self.getMessages(1) + self.connectClient("nick2") + self.sendLine(2, "JOIN #chan") + self.getMessages(2) + self.getMessages(1) + + self.sendLine(1, "NAMES #chan") + + # TODO: It is technically allowed to have one line for each; + # but noone does that. + self.assertMessageMatch( + self.getMessage(1), + command=RPL_NAMREPLY, + params=[ + "nick1", + *(["="] if symbol else []), + "#chan", + StrRe("(nick2 @nick1|@nick1 nick2)"), + ], + ) + + self.assertMessageMatch( + self.getMessage(1), + command=RPL_ENDOFNAMES, + params=["nick1", "#chan", ANYSTR], + ) + + @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) + + @cases.mark_specifications("RFC1459", "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) + + def _testNamesMultipleChannels(self, symbol): + self.connectClient("nick1") + + targmax = dict( + item.split(":", 1) + for item in self.server_support.get("TARGMAX", "").split(",") + if item + ) + if targmax.get("NAMES", "1") == "1": + raise runner.NotImplementedByController("Multi-target NAMES") + + self.sendLine(1, "JOIN #chan1") + self.sendLine(1, "JOIN #chan2") + self.getMessages(1) + + self.sendLine(1, "NAMES #chan1,#chan2") + + # TODO: order is unspecified + self.assertMessageMatch( + self.getMessage(1), + command=RPL_NAMREPLY, + params=["nick1", *(["="] if symbol else []), "#chan1", "@nick1"], + ) + self.assertMessageMatch( + self.getMessage(1), + command=RPL_NAMREPLY, + params=["nick1", *(["="] if symbol else []), "#chan2", "@nick1"], + ) + + self.assertMessageMatch( + self.getMessage(1), + command=RPL_ENDOFNAMES, + params=["nick1", "#chan1,#chan2", ANYSTR], + ) + + @cases.mark_isupport("TARGMAX") + @cases.mark_specifications("RFC1459", deprecated=True) + def testNamesMultipleChannels1459(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._testNamesMultipleChannels(symbol=False) + + @cases.mark_isupport("TARGMAX") + @cases.mark_specifications("RFC1459", "RFC2812", "Modern") + def testNamesMultipleChannels2812(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._testNamesMultipleChannels(symbol=True) + @cases.mark_specifications("RFC1459", "RFC2812", "Modern") def testNamesInvalidChannel(self): """ @@ -54,3 +156,101 @@ class NamesTestCase(cases.BaseServerTestCase): command=RPL_ENDOFNAMES, params=["foo", "#nonexisting", ANYSTR], ) + + def _testNamesNoArgumentPublic(self, symbol): + self.connectClient("nick1") + self.getMessages(1) + self.sendLine(1, "JOIN #chan1") + self.connectClient("nick2") + self.sendLine(2, "JOIN #chan2") + self.sendLine(2, "MODE #chan2 -sp") + self.getMessages(1) + self.getMessages(2) + + self.sendLine(1, "NAMES") + + # TODO: order is unspecified + self.assertMessageMatch( + self.getMessage(1), + command=RPL_NAMREPLY, + params=["nick1", *(["="] if symbol else []), "#chan1", "@nick1"], + ) + self.assertMessageMatch( + self.getMessage(1), + command=RPL_NAMREPLY, + params=["nick1", *(["="] if symbol else []), "#chan2", "@nick2"], + ) + + self.assertMessageMatch( + self.getMessage(1), + command=RPL_ENDOFNAMES, + params=["nick1", ANYSTR, ANYSTR], + ) + + @cases.mark_specifications("RFC1459", deprecated=True) + def testNamesNoArgumentPublic1459(self): + """ + "If no parameter is given, a list of all channels and their + occupants is returned." + -- https://datatracker.ietf.org/doc/html/rfc1459#section-4.2.5 + -- https://datatracker.ietf.org/doc/html/rfc2812#section-3.2.5 + """ + self._testNamesNoArgumentPublic(symbol=False) + + @cases.mark_specifications("RFC2812", deprecated=True) + def testNamesNoArgumentPublic2812(self): + """ + "If no parameter is given, a list of all channels and their + occupants is returned." + -- https://datatracker.ietf.org/doc/html/rfc1459#section-4.2.5 + -- https://datatracker.ietf.org/doc/html/rfc2812#section-3.2.5 + """ + self._testNamesNoArgumentPublic(symbol=True) + + def _testNamesNoArgumentPrivate(self, symbol): + self.connectClient("nick1") + self.getMessages(1) + self.sendLine(1, "JOIN #chan1") + self.connectClient("nick2") + self.sendLine(2, "JOIN #chan2") + self.sendLine(2, "MODE #chan2 +sp") + self.getMessages(1) + self.getMessages(2) + + self.sendLine(1, "NAMES") + + self.assertMessageMatch( + self.getMessage(1), + command=RPL_NAMREPLY, + params=["nick1", *(["="] if symbol else []), "#chan1", "@nick1"], + ) + + self.assertMessageMatch( + self.getMessage(1), + command=RPL_ENDOFNAMES, + params=["nick1", ANYSTR, ANYSTR], + ) + + @cases.mark_specifications("RFC1459", deprecated=True) + def testNamesNoArgumentPrivate1459(self): + """ + "If no parameter is given, a list of all channels and their + occupants is returned. At the end of this list, a list of users who + are visible but either not on any channel or not on a visible channel + are listed as being on `channel' "*"." + -- https://datatracker.ietf.org/doc/html/rfc1459#section-4.2.5 + -- https://datatracker.ietf.org/doc/html/rfc2812#section-3.2.5 + """ + self._testNamesNoArgumentPrivate(symbol=False) + + @cases.mark_specifications("RFC2812", deprecated=True) + def testNamesNoArgumentPrivate2812(self): + """ + "If no parameter is given, a list of all channels and their + occupants is returned. At the end of this list, a list of users who + are visible but either not on any channel or not on a visible channel + are listed as being on `channel' "*"." + -- https://datatracker.ietf.org/doc/html/rfc1459#section-4.2.5 + -- https://datatracker.ietf.org/doc/html/rfc2812#section-3.2.5 + """ + self._testNamesNoArgumentPrivate(symbol=True)