Mirror of github.com/gissleh/irc
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

334 lines
13 KiB

  1. package irc_test
  2. import (
  3. "context"
  4. "errors"
  5. "testing"
  6. "git.aiterp.net/gisle/irc"
  7. "git.aiterp.net/gisle/irc/handlers"
  8. "git.aiterp.net/gisle/irc/internal/irctest"
  9. )
  10. // Integration test below, brace yourself.
  11. func TestClient(t *testing.T) {
  12. irc.Handle(handlers.Input)
  13. irc.Handle(handlers.MRoleplay)
  14. client := irc.New(context.Background(), irc.Config{
  15. Nick: "Test",
  16. User: "Tester",
  17. RealName: "...",
  18. Alternatives: []string{"Test2", "Test3", "Test4", "Test768"},
  19. SendRate: 1000,
  20. })
  21. t.Logf("Client.ID = %#+v", client.ID())
  22. if client.ID() == "" {
  23. t.Fail()
  24. }
  25. interaction := irctest.Interaction{
  26. Strict: false,
  27. Lines: []irctest.InteractionLine{
  28. {Kind: 'C', Data: "CAP LS 302"},
  29. {Kind: 'C', Data: "NICK Test"},
  30. {Kind: 'C', Data: "USER Tester 8 * :..."},
  31. {Kind: 'S', Data: ":testserver.example.com CAP * LS :multi-prefix chghost userhost-in-names vendorname/custom-stuff echo-message =malformed vendorname/advanced-custom-stuff=things,and,items"},
  32. {Kind: 'C', Data: "CAP REQ :multi-prefix chghost userhost-in-names"},
  33. {Kind: 'S', Data: ":testserver.example.com CAP * ACK :multi-prefix userhost-in-names"},
  34. {Kind: 'C', Data: "CAP END"},
  35. {Callback: func() error {
  36. if !client.CapEnabled("multi-prefix") {
  37. return errors.New("multi-prefix cap should be enabled.")
  38. }
  39. if !client.CapEnabled("userhost-in-names") {
  40. return errors.New("userhost-in-names cap should be enabled.")
  41. }
  42. if client.CapEnabled("echo-message") {
  43. return errors.New("echo-message cap should not be enabled.")
  44. }
  45. if client.CapEnabled("") {
  46. return errors.New("(blank) cap should be enabled.")
  47. }
  48. return nil
  49. }},
  50. {Kind: 'S', Data: ":testserver.example.com 433 * Test :Nick is not available"},
  51. {Kind: 'C', Data: "NICK Test2"},
  52. {Kind: 'S', Data: ":testserver.example.com 433 * Test2 :Nick is not available"},
  53. {Kind: 'C', Data: "NICK Test3"},
  54. {Kind: 'S', Data: ":testserver.example.com 433 * Test3 :Nick is not available"},
  55. {Kind: 'C', Data: "NICK Test4"},
  56. {Kind: 'S', Data: ":testserver.example.com 433 * Test4 :Nick is not available"},
  57. {Kind: 'C', Data: "NICK Test768"},
  58. {Kind: 'S', Data: ":testserver.example.com 001 Test768 :Welcome to the TestServer Internet Relay Chat Network test"},
  59. {Kind: 'C', Data: "WHO Test768*"},
  60. {Kind: 'S', Data: ":testserver.example.com 002 Test768 :Your host is testserver.example.com[testserver.example.com/6667], running version charybdis-4-rc3"},
  61. {Kind: 'S', Data: ":testserver.example.com 003 Test768 :This server was created Fri Nov 25 2016 at 17:28:20 CET"},
  62. {Kind: 'S', Data: ":testserver.example.com 004 Test768 testserver.example.com charybdis-4-rc3 DQRSZagiloswxz CFILNPQbcefgijklmnopqrstvz bkloveqjfI"},
  63. {Kind: 'S', Data: ":testserver.example.com 005 Test768 FNC SAFELIST ELIST=CTU MONITOR=100 WHOX ETRACE KNOCK CHANTYPES=#& EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLNPQcgimnprstz CHANLIMIT=#&:15 :are supported by this server"},
  64. {Kind: 'S', Data: ":testserver.example.com 005 Test768 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=TestServer STATUSMSG=@+ CALLERID=g CASEMAPPING=rfc1459 NICKLEN=30 MAXNICKLEN=31 CHANNELLEN=50 TOPICLEN=390 DEAF=D :are supported by this server"},
  65. {Kind: 'S', Data: ":testserver.example.com 005 Test768 TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,&acjmorsuxz| CLIENTVER=3.0 :are supported by this server"},
  66. {Kind: 'S', Data: ":testserver.example.com 251 Test768 :There are 0 users and 2 invisible on 1 servers"},
  67. {Kind: 'S', Data: ":testserver.example.com 254 Test768 1 :channels formed"},
  68. {Kind: 'S', Data: ":testserver.example.com 255 Test768 :I have 2 clients and 0 servers"},
  69. {Kind: 'S', Data: ":testserver.example.com 265 Test768 2 2 :Current local users 2, max 2"},
  70. {Kind: 'S', Data: ":testserver.example.com 266 Test768 2 2 :Current global users 2, max 2"},
  71. {Kind: 'S', Data: ":testserver.example.com 250 Test768 :Highest connection count: 2 (2 clients) (8 connections received)"},
  72. {Kind: 'S', Data: ":testserver.example.com 375 Test768 :- testserver.example.com Message of the Day - "},
  73. {Kind: 'S', Data: ":testserver.example.com 372 Test768 :- This server is only for testing irce, not chatting. If you happen"},
  74. {Kind: 'S', Data: ":testserver.example.com 372 Test768 :- to connect to it by accident, please disconnect immediately."},
  75. {Kind: 'S', Data: ":testserver.example.com 372 Test768 :- "},
  76. {Kind: 'S', Data: ":testserver.example.com 372 Test768 :- - #Test :: Test Channel"},
  77. {Kind: 'S', Data: ":testserver.example.com 372 Test768 :- - #Test2 :: Other Test Channel"},
  78. {Kind: 'S', Data: ":testserver.example.com 376 Test768 :End of /MOTD command."},
  79. {Kind: 'S', Data: ":testserver.example.com 352 Test768 * ~Tester testclient.example.com testserver.example.com Test768 H :0 ..."},
  80. {Kind: 'S', Data: ":Test768 MODE Test768 :+i"},
  81. {Kind: 'S', Data: "PING :testserver.example.com"}, // Ping/Pong to sync.
  82. {Kind: 'C', Data: "PONG :testserver.example.com"},
  83. {Callback: func() error {
  84. if client.Nick() != "Test768" {
  85. return errors.New("client.Nick shouldn't be " + client.Nick())
  86. }
  87. if client.User() != "~Tester" {
  88. return errors.New("client.User shouldn't be " + client.User())
  89. }
  90. if client.Host() != "testclient.example.com" {
  91. return errors.New("client.Host shouldn't be " + client.Host())
  92. }
  93. return nil
  94. }},
  95. {Callback: func() error {
  96. client.Join("#Test")
  97. return nil
  98. }},
  99. {Kind: 'C', Data: "JOIN #Test"},
  100. {Kind: 'S', Data: ":Test768!~test@127.0.0.1 JOIN #Test *"},
  101. {Kind: 'S', Data: ":testserver.example.com 353 Test768 = #Test :Test768!~test@127.0.0.1 @+Gisle!gisle@gisle.me"},
  102. {Kind: 'S', Data: ":testserver.example.com 366 Test768 #Test :End of /NAMES list."},
  103. {Kind: 'S', Data: ":Gisle!~irce@10.32.0.1 MODE #Test +osv Test768 Test768"},
  104. {Kind: 'S', Data: ":Gisle!~irce@10.32.0.1 MODE #Test +N-s "},
  105. {Kind: 'S', Data: ":Test1234!~test2@172.17.37.1 JOIN #Test Test1234"},
  106. {Kind: 'S', Data: ":Test4321!~test2@172.17.37.1 JOIN #Test Test1234"},
  107. {Kind: 'S', Data: ":Gisle!~irce@10.32.0.1 MODE #Test +v Test1234"},
  108. {Kind: 'S', Data: "PING :testserver.example.com"}, // Ping/Pong to sync.
  109. {Kind: 'C', Data: "PONG :testserver.example.com"},
  110. {Callback: func() error {
  111. channel := client.Channel("#Test")
  112. if channel == nil {
  113. return errors.New("Channel #Test not found")
  114. }
  115. err := irctest.AssertUserlist(t, channel, "@Gisle", "@Test768", "+Test1234", "Test4321")
  116. if err != nil {
  117. return err
  118. }
  119. userTest1234, ok := channel.UserList().User("Test1234")
  120. if !ok {
  121. return errors.New("Test1234 not found")
  122. }
  123. if userTest1234.Account != "Test1234" {
  124. return errors.New("Test1234 did not get account from extended-join")
  125. }
  126. return nil
  127. }},
  128. {Kind: 'S', Data: ":Test1234!~test2@172.17.37.1 NICK Hunter2"},
  129. {Kind: 'S', Data: ":Hunter2!~test2@172.17.37.1 AWAY :Doing stuff"},
  130. {Kind: 'S', Data: ":Gisle!~irce@10.32.0.1 AWAY"},
  131. {Kind: 'S', Data: ":Gisle!~irce@10.32.0.1 PART #Test :Leaving the channel"},
  132. {Kind: 'S', Data: ":Hunter2!~test2@172.17.37.1 CHGHOST test2 some.awesome.virtual.host"},
  133. {Kind: 'S', Data: "@account=Hunter2 :Test4321!~test2@172.17.37.1 PRIVMSG #Test :Hello World."},
  134. {Kind: 'S', Data: "PING :testserver.example.com"}, // Ping/Pong to sync.
  135. {Kind: 'C', Data: "PONG :testserver.example.com"},
  136. {Callback: func() error {
  137. channel := client.Channel("#Test")
  138. if channel == nil {
  139. return errors.New("Channel #Test not found")
  140. }
  141. err := irctest.AssertUserlist(t, channel, "@Test768", "+Hunter2", "Test4321")
  142. if err != nil {
  143. return err
  144. }
  145. _, ok := channel.UserList().User("Test1234")
  146. if ok {
  147. return errors.New("Test1234 is still there")
  148. }
  149. userHunter2, ok := channel.UserList().User("Hunter2")
  150. if !ok {
  151. return errors.New("Test1234 not found")
  152. }
  153. if userHunter2.Account != "Test1234" {
  154. return errors.New("Hunter2 did not persist account post nick change")
  155. }
  156. if !userHunter2.IsAway() {
  157. return errors.New("Hunter2 should be away")
  158. }
  159. if userHunter2.Away != "Doing stuff" {
  160. return errors.New("Hunter2 has the wrong away message: " + userHunter2.Away)
  161. }
  162. if userHunter2.Host != "some.awesome.virtual.host" {
  163. return errors.New("Hunter2 should have changed the host: " + userHunter2.Host)
  164. }
  165. return nil
  166. }},
  167. {Kind: 'S', Data: ":Hunter2!~test2@172.17.37.1 PRIVMSG Test768 :Hello, World"},
  168. {Kind: 'S', Data: "PING :testserver.example.com"}, // Ping/Pong to sync.
  169. {Kind: 'C', Data: "PONG :testserver.example.com"},
  170. {Callback: func() error {
  171. query := client.Query("Hunter2")
  172. if query == nil {
  173. return errors.New("Did not find query")
  174. }
  175. return nil
  176. }},
  177. {Kind: 'S', Data: ":Hunter2!~test2@172.17.37.1 NICK SevenAsterisks"},
  178. {Kind: 'S', Data: "PING :testserver.example.com"}, // Ping/Pong to sync.
  179. {Kind: 'C', Data: "PONG :testserver.example.com"},
  180. {Callback: func() error {
  181. oldQuerry := client.Query("Hunter2")
  182. if oldQuerry != nil {
  183. return errors.New("Did find query by old name")
  184. }
  185. query := client.Query("SevenAsterisks")
  186. if query == nil {
  187. return errors.New("Did not find query by new name")
  188. }
  189. return nil
  190. }},
  191. {Callback: func() error {
  192. client.EmitInput("/invalidcommand stuff and things", nil)
  193. return nil
  194. }},
  195. {Kind: 'C', Data: "INVALIDCOMMAND stuff and things"},
  196. {Kind: 'S', Data: ":testserver.example.com 421 Test768 INVALIDCOMMAND :Unknown command"},
  197. {Callback: func() error {
  198. channel := client.Channel("#Test")
  199. if channel == nil {
  200. return errors.New("Channel #Test not found")
  201. }
  202. client.EmitInput("/me does stuff", channel)
  203. client.EmitInput("/describe #Test describes stuff", channel)
  204. client.EmitInput("/text Hello, World", channel)
  205. client.EmitInput("Hello again", channel)
  206. return nil
  207. }},
  208. {Kind: 'C', Data: "PRIVMSG #Test :\x01ACTION does stuff\x01"},
  209. {Kind: 'C', Data: "PRIVMSG #Test :\x01ACTION describes stuff\x01"},
  210. {Kind: 'C', Data: "PRIVMSG #Test :Hello, World"},
  211. {Kind: 'C', Data: "PRIVMSG #Test :Hello again"},
  212. {Kind: 'S', Data: ":Test768!~test@127.0.0.1 PRIVMSG #Test :\x01ACTION does stuff\x01"},
  213. {Kind: 'S', Data: ":Test768!~test@127.0.0.1 PRIVMSG #Test :\x01ACTION describes stuff\x01"},
  214. {Kind: 'S', Data: ":Test768!~test@127.0.0.1 PRIVMSG #Test :Hello, World"},
  215. {Kind: 'S', Data: ":Test768!~test@127.0.0.1 PRIVMSG #Test :Hello again"},
  216. {Callback: func() error {
  217. channel := client.Channel("#Test")
  218. if channel == nil {
  219. return errors.New("Channel #Test not found")
  220. }
  221. client.EmitInput("/m +N", channel)
  222. client.EmitInput("/npcac Test_NPC stuffs things", channel)
  223. return nil
  224. }},
  225. {Kind: 'C', Data: "MODE #Test +N"},
  226. {Kind: 'C', Data: "NPCA #Test Test_NPC :stuffs things"},
  227. {Kind: 'S', Data: ":Test768!~test@127.0.0.1 MODE #Test +N"},
  228. {Kind: 'S', Data: ":\x1FTest_NPC\x1F!Test768@npc.fakeuser.invalid PRIVMSG #Test :\x01ACTION stuffs things\x01"},
  229. {Callback: func() error {
  230. channel := client.Channel("#Test")
  231. if channel == nil {
  232. return errors.New("Channel #Test not found")
  233. }
  234. client.Describef(channel.Name(), "does stuff with %d things", 42)
  235. client.Sayf(channel.Name(), "Hello, %s", "World")
  236. return nil
  237. }},
  238. {Kind: 'C', Data: "PRIVMSG #Test :\x01ACTION does stuff with 42 things\x01"},
  239. {Kind: 'C', Data: "PRIVMSG #Test :Hello, World"},
  240. {Callback: func() error {
  241. client.Part("#Test")
  242. return nil
  243. }},
  244. {Kind: 'C', Data: "PART #Test"},
  245. },
  246. }
  247. addr, err := interaction.Listen()
  248. if err != nil {
  249. t.Fatal("Listen:", err)
  250. }
  251. if err := client.Disconnect(); err != irc.ErrNoConnection {
  252. t.Errorf("It should fail to disconnect, got: %s", err)
  253. }
  254. err = client.Connect(addr, false)
  255. if err != nil {
  256. t.Fatal("Connect:", err)
  257. return
  258. }
  259. interaction.Wait()
  260. fail := interaction.Failure
  261. if fail != nil {
  262. t.Error("Index:", fail.Index)
  263. t.Error("NetErr:", fail.NetErr)
  264. t.Error("CBErr:", fail.CBErr)
  265. t.Error("Result:", fail.Result)
  266. if fail.Index >= 0 {
  267. t.Error("Line.Kind:", interaction.Lines[fail.Index].Kind)
  268. t.Error("Line.Data:", interaction.Lines[fail.Index].Data)
  269. }
  270. }
  271. for i, logLine := range interaction.Log {
  272. t.Logf("Log[%d] = %#+v", i, logLine)
  273. }
  274. }
  275. // TestParenthesesBug tests that the bugfix causing `((Remove :01 goofs!*))` to be parsed as an empty message. It was
  276. // initially thought to be caused by the parentheses (like a hidden m_roleplay NPC attribution removal), hence the name
  277. // for this function.
  278. func TestParenthesesBug(t *testing.T) {
  279. gotMessage := true
  280. client := irc.New(context.Background(), irc.Config{
  281. Nick: "Stuff",
  282. })
  283. irc.Handle(func(event *irc.Event, client *irc.Client) {
  284. if event.Name() == "packet.privmsg" || event.Nick == "Dante" {
  285. gotMessage = true
  286. if event.Text != "((Remove :01 goofs!*))" {
  287. t.Errorf("Expected: %#+v", "((Remove :01 goofs!*))")
  288. t.Errorf("Result: %#+v", event.Text)
  289. }
  290. }
  291. })
  292. packet, err := irc.ParsePacket("@example/tag=32; :Dante!TheBeans@captain.purple.beans PRIVMSG Stuff :((Remove :01 goofs!*))")
  293. if err != nil {
  294. t.Error("Parse", err)
  295. }
  296. client.EmitSync(context.Background(), packet)
  297. if !gotMessage {
  298. t.Error("Message was not received")
  299. }
  300. }