-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add various channel mode tests (#276)
- Loading branch information
Showing
5 changed files
with
219 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,9 @@ | |
[Operator] | ||
Name = operuser | ||
Password = operpassword | ||
[Limits] | ||
MaxNickLength = 32 # defaults to 9 | ||
""" | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
from irctest import cases | ||
from irctest.numerics import RPL_CHANNELCREATED, RPL_CHANNELMODEIS | ||
from irctest.patma import ANYSTR, ListRemainder, StrRe | ||
|
||
|
||
class RplChannelModeIsTestCase(cases.BaseServerTestCase): | ||
@cases.mark_specifications("Modern") | ||
def testChannelModeIs(self): | ||
"""Test RPL_CHANNELMODEIS and RPL_CHANNELCREATED as responses to | ||
`MODE #channel`: | ||
<https://modern.ircdocs.horse/#rplcreationtime-329> | ||
<https://modern.ircdocs.horse/#rplchannelmodeis-324> | ||
""" | ||
expected_numerics = {RPL_CHANNELMODEIS, RPL_CHANNELCREATED} | ||
if self.controller.software_name in ("irc2", "Sable"): | ||
# irc2 and Sable don't use timestamps for conflict resolution, | ||
# consequently they don't store the channel creation timestamp | ||
# and don't send RPL_CHANNELCREATED | ||
expected_numerics = {RPL_CHANNELMODEIS} | ||
|
||
self.connectClient("chanop", name="chanop") | ||
self.joinChannel("chanop", "#chan") | ||
# i, n, and t are specified by RFC1459; some of them may be on by default, | ||
# but after this, at least those three should be enabled: | ||
self.sendLine("chanop", "MODE #chan +int") | ||
self.getMessages("chanop") | ||
|
||
self.sendLine("chanop", "MODE #chan") | ||
messages = self.getMessages("chanop") | ||
self.assertEqual(expected_numerics, {msg.command for msg in messages}) | ||
for message in messages: | ||
if message.command == RPL_CHANNELMODEIS: | ||
# the final parameters are the mode string (e.g. `+int`), | ||
# and then optionally any mode parameters (in case the ircd | ||
# lists a mode that takes a parameter) | ||
self.assertMessageMatch( | ||
message, | ||
command=RPL_CHANNELMODEIS, | ||
params=["chanop", "#chan", ListRemainder(ANYSTR, min_length=1)], | ||
) | ||
final_param = message.params[2] | ||
self.assertEqual(final_param[0], "+") | ||
enabled_modes = list(final_param[1:]) | ||
break | ||
|
||
self.assertLessEqual({"i", "n", "t"}, set(enabled_modes)) | ||
|
||
# remove all the modes listed by RPL_CHANNELMODEIS | ||
self.sendLine("chanop", f"MODE #chan -{''.join(enabled_modes)}") | ||
response = self.getMessage("chanop") | ||
# we should get something like: MODE #chan -int | ||
self.assertMessageMatch( | ||
response, command="MODE", params=["#chan", StrRe("^-.*")] | ||
) | ||
self.assertEqual(set(response.params[1][1:]), set(enabled_modes)) | ||
|
||
self.sendLine("chanop", "MODE #chan") | ||
messages = self.getMessages("chanop") | ||
self.assertEqual(expected_numerics, {msg.command for msg in messages}) | ||
# all modes have been disabled; the correct representation of this is `+` | ||
for message in messages: | ||
if message.command == RPL_CHANNELMODEIS: | ||
self.assertMessageMatch( | ||
message, | ||
command=RPL_CHANNELMODEIS, | ||
params=["chanop", "#chan", "+"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
from irctest import cases | ||
from irctest.numerics import ( | ||
ERR_CHANOPRIVSNEEDED, | ||
ERR_NOSUCHCHANNEL, | ||
ERR_NOSUCHNICK, | ||
ERR_NOTONCHANNEL, | ||
ERR_USERNOTINCHANNEL, | ||
) | ||
|
||
|
||
class ChannelOperatorModeTestCase(cases.BaseServerTestCase): | ||
"""Test various error and success cases around the channel operator mode: | ||
<https://modern.ircdocs.horse/#channel-operators> | ||
<https://modern.ircdocs.horse/#mode-message> | ||
""" | ||
|
||
def setupNicks(self): | ||
"""Set up a standard set of three nicknames and two channels | ||
for testing channel-user MODE interactions.""" | ||
# first nick to join the channel is privileged: | ||
self.connectClient("chanop", name="chanop") | ||
self.joinChannel("chanop", "#chan") | ||
|
||
self.connectClient("unprivileged", name="unprivileged") | ||
self.joinChannel("unprivileged", "#chan") | ||
self.getMessages("chanop") | ||
|
||
self.connectClient("unrelated", name="unrelated") | ||
self.joinChannel("unrelated", "#unrelated") | ||
self.joinChannel("unprivileged", "#unrelated") | ||
self.getMessages("unrelated") | ||
|
||
@cases.mark_specifications("Modern") | ||
@cases.xfailIfSoftware(["irc2"], "broken in irc2") | ||
def testChannelOperatorModeSenderPrivsNeeded(self): | ||
"""Test that +o from a channel member without the necessary privileges | ||
fails as expected.""" | ||
self.setupNicks() | ||
# sender is a channel member but without the necessary privileges: | ||
self.sendLine("unprivileged", "MODE #chan +o unprivileged") | ||
messages = self.getMessages("unprivileged") | ||
self.assertEqual(len(messages), 1) | ||
self.assertMessageMatch(messages[0], command=ERR_CHANOPRIVSNEEDED) | ||
|
||
@cases.mark_specifications("Modern") | ||
def testChannelOperatorModeTargetNotInChannel(self): | ||
"""Test that +o targeting a user not present in the channel fails | ||
as expected.""" | ||
self.setupNicks() | ||
# sender is a chanop, but target nick is not in the channel: | ||
self.sendLine("chanop", "MODE #chan +o unrelated") | ||
messages = self.getMessages("chanop") | ||
self.assertEqual(len(messages), 1) | ||
self.assertMessageMatch(messages[0], command=ERR_USERNOTINCHANNEL) | ||
|
||
@cases.mark_specifications("Modern") | ||
def testChannelOperatorModeTargetDoesNotExist(self): | ||
"""Test that +o targeting a nonexistent nick fails as expected.""" | ||
self.setupNicks() | ||
# sender is a chanop, but target nick does not exist: | ||
self.sendLine("chanop", "MODE #chan +o nobody") | ||
messages = self.getMessages("chanop") | ||
# ERR_NOSUCHNICK is typical, Bahamut additionally sends ERR_USERNOTINCHANNEL | ||
if self.controller.software_name != "Bahamut": | ||
self.assertEqual(len(messages), 1) | ||
self.assertMessageMatch(messages[0], command=ERR_NOSUCHNICK) | ||
else: | ||
self.assertLessEqual(len(messages), 2) | ||
commands = {message.command for message in messages} | ||
self.assertLessEqual({ERR_NOSUCHNICK}, commands) | ||
self.assertLessEqual(commands, {ERR_NOSUCHNICK, ERR_USERNOTINCHANNEL}) | ||
|
||
@cases.mark_specifications("Modern") | ||
def testChannelOperatorModeChannelDoesNotExist(self): | ||
"""Test that +o targeting a nonexistent channel fails as expected.""" | ||
self.setupNicks() | ||
# target channel does not exist, but target nick does: | ||
self.sendLine("chanop", "MODE #nonexistentchan +o chanop") | ||
messages = self.getMessages("chanop") | ||
self.assertEqual(len(messages), 1) | ||
# Modern: "If <target> is a channel that does not exist on the network, | ||
# the ERR_NOSUCHCHANNEL (403) numeric is returned." | ||
# However, Unreal and ngircd send 401 ERR_NOSUCHNICK here instead: | ||
if self.controller.software_name not in ("UnrealIRCd", "ngIRCd"): | ||
self.assertEqual(messages[0].command, ERR_NOSUCHCHANNEL) | ||
else: | ||
self.assertIn(messages[0].command, [ERR_NOSUCHCHANNEL, ERR_NOSUCHNICK]) | ||
|
||
@cases.mark_specifications("Modern") | ||
def testChannelOperatorModeChannelAndTargetDoNotExist(self): | ||
"""Test that +o targeting a nonexistent channel and nickname | ||
fails as expected.""" | ||
self.setupNicks() | ||
# neither target channel nor target nick exist: | ||
self.sendLine("chanop", "MODE #nonexistentchan +o nobody") | ||
messages = self.getMessages("chanop") | ||
self.assertEqual(len(messages), 1) | ||
self.assertIn( | ||
messages[0].command, | ||
[ERR_NOSUCHCHANNEL, ERR_NOTONCHANNEL, ERR_NOSUCHNICK, ERR_USERNOTINCHANNEL], | ||
) | ||
|
||
@cases.mark_specifications("Modern") | ||
def testChannelOperatorModeSenderNonMember(self): | ||
"""Test that +o where the sender is not a channel member | ||
fails as expected.""" | ||
self.setupNicks() | ||
# sender is not a channel member, target nick exists and is a channel member: | ||
self.sendLine("chanop", "MODE #unrelated +o unprivileged") | ||
messages = self.getMessages("chanop") | ||
self.assertEqual(len(messages), 1) | ||
self.assertIn(messages[0].command, [ERR_NOTONCHANNEL, ERR_CHANOPRIVSNEEDED]) | ||
|
||
@cases.mark_specifications("Modern") | ||
def testChannelOperatorModeSenderAndTargetNonMembers(self): | ||
"""Test that +o where neither the sender nor the target is a channel | ||
member fails as expected.""" | ||
self.setupNicks() | ||
# sender is not a channel member, target nick exists but is not a channel member: | ||
self.sendLine("chanop", "MODE #unrelated +o chanop") | ||
messages = self.getMessages("chanop") | ||
self.assertEqual(len(messages), 1) | ||
self.assertIn( | ||
messages[0].command, | ||
[ERR_NOTONCHANNEL, ERR_CHANOPRIVSNEEDED, ERR_USERNOTINCHANNEL], | ||
) | ||
|
||
@cases.mark_specifications("Modern") | ||
def testChannelOperatorModeSuccess(self): | ||
"""Tests a successful grant of +o in a channel.""" | ||
self.setupNicks() | ||
|
||
self.sendLine("chanop", "MODE #chan +o unprivileged") | ||
messages = self.getMessages("chanop") | ||
self.assertEqual(len(messages), 1) | ||
self.assertMessageMatch( | ||
messages[0], | ||
command="MODE", | ||
params=["#chan", "+o", "unprivileged"], | ||
) | ||
messages = self.getMessages("unprivileged") | ||
self.assertEqual(len(messages), 1) | ||
self.assertMessageMatch( | ||
messages[0], | ||
command="MODE", | ||
params=["#chan", "+o", "unprivileged"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters