test_ircsupport.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Tests for L{twisted.words.im.ircsupport}.
  5. """
  6. from twisted.test.proto_helpers import StringTransport
  7. from twisted.words.im.basechat import ChatUI, Conversation, GroupConversation
  8. from twisted.words.im.ircsupport import IRCAccount, IRCProto
  9. from twisted.words.im.locals import OfflineError
  10. from twisted.words.test.test_irc import IRCTestCase
  11. class StubConversation(Conversation):
  12. def show(self):
  13. pass
  14. def showMessage(self, message, metadata):
  15. self.message = message
  16. self.metadata = metadata
  17. class StubGroupConversation(GroupConversation):
  18. def setTopic(self, topic, nickname):
  19. self.topic = topic
  20. self.topicSetBy = nickname
  21. def show(self):
  22. pass
  23. def showGroupMessage(self, sender, text, metadata=None):
  24. self.metadata = metadata
  25. self.text = text
  26. self.metadata = metadata
  27. class StubChatUI(ChatUI):
  28. def getConversation(self, group, Class=StubConversation, stayHidden=0):
  29. return ChatUI.getGroupConversation(self, group, Class, stayHidden)
  30. def getGroupConversation(self, group, Class=StubGroupConversation, stayHidden=0):
  31. return ChatUI.getGroupConversation(self, group, Class, stayHidden)
  32. class IRCProtoTests(IRCTestCase):
  33. """
  34. Tests for L{IRCProto}.
  35. """
  36. def setUp(self):
  37. self.account = IRCAccount(
  38. "Some account", False, "alice", None, "example.com", 6667)
  39. self.proto = IRCProto(self.account, StubChatUI(), None)
  40. self.transport = StringTransport()
  41. def test_login(self):
  42. """
  43. When L{IRCProto} is connected to a transport, it sends I{NICK} and
  44. I{USER} commands with the username from the account object.
  45. """
  46. self.proto.makeConnection(self.transport)
  47. self.assertEqualBufferValue(
  48. self.transport.value(),
  49. "NICK alice\r\n"
  50. "USER alice foo bar :Twisted-IM user\r\n")
  51. def test_authenticate(self):
  52. """
  53. If created with an account with a password, L{IRCProto} sends a
  54. I{PASS} command before the I{NICK} and I{USER} commands.
  55. """
  56. self.account.password = "secret"
  57. self.proto.makeConnection(self.transport)
  58. self.assertEqualBufferValue(
  59. self.transport.value(),
  60. "PASS secret\r\n"
  61. "NICK alice\r\n"
  62. "USER alice foo bar :Twisted-IM user\r\n")
  63. def test_channels(self):
  64. """
  65. If created with an account with a list of channels, L{IRCProto}
  66. joins each of those channels after registering.
  67. """
  68. self.account.channels = ['#foo', '#bar']
  69. self.proto.makeConnection(self.transport)
  70. self.assertEqualBufferValue(
  71. self.transport.value(),
  72. "NICK alice\r\n"
  73. "USER alice foo bar :Twisted-IM user\r\n"
  74. "JOIN #foo\r\n"
  75. "JOIN #bar\r\n")
  76. def test_isupport(self):
  77. """
  78. L{IRCProto} can interpret I{ISUPPORT} (I{005}) messages from the server
  79. and reflect their information in its C{supported} attribute.
  80. """
  81. self.proto.makeConnection(self.transport)
  82. self.proto.dataReceived(
  83. ":irc.example.com 005 alice MODES=4 CHANLIMIT=#:20\r\n")
  84. self.assertEqual(4, self.proto.supported.getFeature("MODES"))
  85. def test_nick(self):
  86. """
  87. IRC NICK command changes the nickname of a user.
  88. """
  89. self.proto.makeConnection(self.transport)
  90. self.proto.dataReceived(":alice JOIN #group1\r\n")
  91. self.proto.dataReceived(":alice1 JOIN #group1\r\n")
  92. self.proto.dataReceived(":alice1 NICK newnick\r\n")
  93. self.proto.dataReceived(":alice3 NICK newnick3\r\n")
  94. self.assertIn("newnick", self.proto._ingroups)
  95. self.assertNotIn("alice1", self.proto._ingroups)
  96. def test_part(self):
  97. """
  98. IRC PART command removes a user from an IRC channel.
  99. """
  100. self.proto.makeConnection(self.transport)
  101. self.proto.dataReceived(":alice1 JOIN #group1\r\n")
  102. self.assertIn("group1", self.proto._ingroups["alice1"])
  103. self.assertNotIn("group2", self.proto._ingroups["alice1"])
  104. self.proto.dataReceived(":alice PART #group1\r\n")
  105. self.proto.dataReceived(":alice1 PART #group1\r\n")
  106. self.proto.dataReceived(":alice1 PART #group2\r\n")
  107. self.assertNotIn("group1", self.proto._ingroups["alice1"])
  108. self.assertNotIn("group2", self.proto._ingroups["alice1"])
  109. def test_quit(self):
  110. """
  111. IRC QUIT command removes a user from all IRC channels.
  112. """
  113. self.proto.makeConnection(self.transport)
  114. self.proto.dataReceived(":alice1 JOIN #group1\r\n")
  115. self.assertIn("group1", self.proto._ingroups["alice1"])
  116. self.assertNotIn("group2", self.proto._ingroups["alice1"])
  117. self.proto.dataReceived(":alice1 JOIN #group3\r\n")
  118. self.assertIn("group3", self.proto._ingroups["alice1"])
  119. self.proto.dataReceived(":alice1 QUIT\r\n")
  120. self.assertTrue(len(self.proto._ingroups["alice1"]) == 0)
  121. self.proto.dataReceived(":alice3 QUIT\r\n")
  122. def test_topic(self):
  123. """
  124. IRC TOPIC command changes the topic of an IRC channel.
  125. """
  126. self.proto.makeConnection(self.transport)
  127. self.proto.dataReceived(":alice1 JOIN #group1\r\n")
  128. self.proto.dataReceived(":alice1 TOPIC #group1 newtopic\r\n")
  129. groupConversation = self.proto.getGroupConversation("group1")
  130. self.assertEqual(groupConversation.topic, "newtopic")
  131. self.assertEqual(groupConversation.topicSetBy, "alice1")
  132. def test_privmsg(self):
  133. """
  134. PRIVMSG sends a private message to a user or channel.
  135. """
  136. self.proto.makeConnection(self.transport)
  137. self.proto.dataReceived(":alice1 PRIVMSG t2 test_message_1\r\n")
  138. conversation = self.proto.chat.getConversation(
  139. self.proto.getPerson("alice1"))
  140. self.assertEqual(conversation.message, "test_message_1")
  141. self.proto.dataReceived(":alice1 PRIVMSG #group1 test_message_2\r\n")
  142. groupConversation = self.proto.getGroupConversation("group1")
  143. self.assertEqual(groupConversation.text, "test_message_2")
  144. self.proto.setNick("alice")
  145. self.proto.dataReceived(":alice PRIVMSG #foo test_message_3\r\n")
  146. groupConversation = self.proto.getGroupConversation("foo")
  147. self.assertFalse(hasattr(groupConversation, "text"))
  148. conversation = self.proto.chat.getConversation(
  149. self.proto.getPerson("alice"))
  150. self.assertFalse(hasattr(conversation, "message"))
  151. def test_action(self):
  152. """
  153. CTCP ACTION to a user or channel.
  154. """
  155. self.proto.makeConnection(self.transport)
  156. self.proto.dataReceived(":alice1 PRIVMSG alice1 :\01ACTION smiles\01\r\n")
  157. conversation = self.proto.chat.getConversation(
  158. self.proto.getPerson("alice1"))
  159. self.assertEqual(conversation.message, "smiles")
  160. self.proto.dataReceived(":alice1 PRIVMSG #group1 :\01ACTION laughs\01\r\n")
  161. groupConversation = self.proto.getGroupConversation("group1")
  162. self.assertEqual(groupConversation.text, "laughs")
  163. self.proto.setNick("alice")
  164. self.proto.dataReceived(":alice PRIVMSG #group1 :\01ACTION cries\01\r\n")
  165. groupConversation = self.proto.getGroupConversation("foo")
  166. self.assertFalse(hasattr(groupConversation, "text"))
  167. conversation = self.proto.chat.getConversation(
  168. self.proto.getPerson("alice"))
  169. self.assertFalse(hasattr(conversation, "message"))
  170. def test_rplNamreply(self):
  171. """
  172. RPL_NAMREPLY server response (353) lists all the users in a channel.
  173. RPL_ENDOFNAMES server response (363) is sent at the end of RPL_NAMREPLY
  174. to indicate that there are no more names.
  175. """
  176. self.proto.makeConnection(self.transport)
  177. self.proto.dataReceived(
  178. ":example.com 353 z3p = #bnl :pSwede Dan- SkOyg @MrOp +MrPlus\r\n")
  179. expectedInGroups = {'Dan-': ['bnl'],
  180. 'pSwede': ['bnl'],
  181. 'SkOyg': ['bnl'],
  182. 'MrOp': ['bnl'],
  183. 'MrPlus': ['bnl']}
  184. expectedNamReplies = {
  185. 'bnl': ['pSwede', 'Dan-', 'SkOyg', 'MrOp', 'MrPlus']}
  186. self.assertEqual(expectedInGroups, self.proto._ingroups)
  187. self.assertEqual(expectedNamReplies, self.proto._namreplies)
  188. self.proto.dataReceived(
  189. ":example.com 366 alice #bnl :End of /NAMES list\r\n")
  190. self.assertEqual({}, self.proto._namreplies)
  191. groupConversation = self.proto.getGroupConversation("bnl")
  192. self.assertEqual(expectedNamReplies['bnl'], groupConversation.members)
  193. def test_rplTopic(self):
  194. """
  195. RPL_TOPIC server response (332) is sent when a channel's topic is changed
  196. """
  197. self.proto.makeConnection(self.transport)
  198. self.proto.dataReceived(
  199. ":example.com 332 alice, #foo :Some random topic\r\n")
  200. self.assertEqual("Some random topic", self.proto._topics["foo"])
  201. def test_sendMessage(self):
  202. """
  203. L{IRCPerson.sendMessage}
  204. """
  205. self.proto.makeConnection(self.transport)
  206. person = self.proto.getPerson("alice")
  207. self.assertRaises(OfflineError, person.sendMessage, "Some message")
  208. person.account.client = self.proto
  209. self.transport.clear()
  210. person.sendMessage("Some message 2")
  211. self.assertEqual(self.transport.io.getvalue(),
  212. b'PRIVMSG alice :Some message 2\r\n')
  213. self.transport.clear()
  214. person.sendMessage("smiles", {"style": "emote"})
  215. self.assertEqual(self.transport.io.getvalue(),
  216. b'PRIVMSG alice :\x01ACTION smiles\x01\r\n')
  217. def test_sendGroupMessage(self):
  218. """
  219. L{IRCGroup.sendGroupMessage}
  220. """
  221. self.proto.makeConnection(self.transport)
  222. group = self.proto.chat.getGroup("#foo", self.proto)
  223. self.assertRaises(OfflineError, group.sendGroupMessage, "Some message")
  224. group.account.client = self.proto
  225. self.transport.clear()
  226. group.sendGroupMessage("Some message 2")
  227. self.assertEqual(self.transport.io.getvalue(),
  228. b'PRIVMSG #foo :Some message 2\r\n')
  229. self.transport.clear()
  230. group.sendGroupMessage("smiles", {"style": "emote"})
  231. self.assertEqual(self.transport.io.getvalue(),
  232. b'PRIVMSG #foo :\x01ACTION smiles\x01\r\n')