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.

475 lines
17 KiB

  1. package irc_test
  2. import (
  3. "context"
  4. "errors"
  5. "github.com/gissleh/irc/handlers"
  6. "testing"
  7. "github.com/gissleh/irc"
  8. "github.com/gissleh/irc/internal/irctest"
  9. )
  10. // Integration test below, brace yourself.
  11. func TestClient(t *testing.T) {
  12. client := irc.New(context.Background(), irc.Config{
  13. Nick: "Test",
  14. User: "Tester",
  15. RealName: "...",
  16. Alternatives: []string{"Test2", "Test3", "Test4", "Test768"},
  17. SendRate: 1000,
  18. AutoJoinInvites: true,
  19. })
  20. logger := irctest.EventLog{}
  21. client.AddHandler(handlers.Input)
  22. client.AddHandler(handlers.MRoleplay)
  23. client.AddHandler(handlers.CTCP)
  24. client.AddHandler(logger.Handler)
  25. t.Logf("Client.ID = %#+v", client.ID())
  26. if client.ID() == "" {
  27. t.Fail()
  28. }
  29. interaction := irctest.Interaction{
  30. Strict: false,
  31. Lines: []irctest.InteractionLine{
  32. {Client: "CAP LS 302"},
  33. {Client: "NICK Test"},
  34. {Client: "USER Tester 8 * :..."},
  35. {Server: ":testserver.example.com NOTICE * :*** Checking your bits..."},
  36. {Server: ":testserver.example.com CAP * LS :multi-prefix chghost userhost-in-names vendorname/custom-stuff echo-message =malformed vendorname/advanced-custom-stuff=things,and,items"},
  37. {Server: ":testserver.example.com 433 * Test :Nick is not available"},
  38. {Client: "CAP REQ :multi-prefix chghost userhost-in-names echo-message"},
  39. {Server: ":testserver.example.com CAP * ACK :multi-prefix userhost-in-names"},
  40. {Client: "CAP END"},
  41. {Callback: func() error {
  42. if !client.CapEnabled("multi-prefix") {
  43. return errors.New("multi-prefix cap should be enabled.")
  44. }
  45. if !client.CapEnabled("userhost-in-names") {
  46. return errors.New("userhost-in-names cap should be enabled.")
  47. }
  48. if client.CapEnabled("echo-message") {
  49. return errors.New("echo-message cap should not be enabled.")
  50. }
  51. if client.CapEnabled("") {
  52. return errors.New("(blank) cap should be enabled.")
  53. }
  54. return nil
  55. }},
  56. {Client: "NICK Test2"},
  57. {Server: ":testserver.example.com 433 * Test2 :Nick is not available"},
  58. {Client: "NICK Test3"},
  59. {Server: ":testserver.example.com 433 * Test3 :Nick is not available"},
  60. {Client: "NICK Test4"},
  61. {Server: ":testserver.example.com 433 * Test4 :Nick is not available"},
  62. {Client: "NICK Test768"},
  63. {Server: ":testserver.example.com 001 Test768 :Welcome to the TestServer Internet Relay Chat Network test"},
  64. {Client: "WHO Test768*"},
  65. {Server: ":testserver.example.com 002 Test768 :Your host is testserver.example.com[testserver.example.com/6667], running version charybdis-4-rc3"},
  66. {Server: ":testserver.example.com 003 Test768 :This server was created Fri Nov 25 2016 at 17:28:20 CET"},
  67. {Server: ":testserver.example.com 004 Test768 testserver.example.com charybdis-4-rc3 DQRSZagiloswxz CFILNPQbcefgijklmnopqrstvz bkloveqjfI"},
  68. {Server: ":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"},
  69. {Server: ":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"},
  70. {Server: ":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"},
  71. {Server: ":testserver.example.com 251 Test768 :There are 0 users and 2 invisible on 1 servers"},
  72. {Server: ":testserver.example.com 254 Test768 1 :channels formed"},
  73. {Server: ":testserver.example.com 255 Test768 :I have 2 clients and 0 servers"},
  74. {Server: ":testserver.example.com 265 Test768 2 2 :Current local users 2, max 2"},
  75. {Server: ":testserver.example.com 266 Test768 2 2 :Current global users 2, max 2"},
  76. {Server: ":testserver.example.com 250 Test768 :Highest connection count: 2 (2 clients) (8 connections received)"},
  77. {Server: ":testserver.example.com 375 Test768 :- testserver.example.com Message of the Day - "},
  78. {Server: ":testserver.example.com 372 Test768 :- This server is only for testing irce, not chatting. If you happen"},
  79. {Server: ":testserver.example.com 372 Test768 :- to connect to it by accident, please disconnect immediately."},
  80. {Server: ":testserver.example.com 372 Test768 :- "},
  81. {Server: ":testserver.example.com 372 Test768 :- - #Test :: Test Channel"},
  82. {Server: ":testserver.example.com 372 Test768 :- - #Test2 :: Other Test Channel"},
  83. {Server: ":testserver.example.com 376 Test768 :End of /MOTD command."},
  84. {Server: ":testserver.example.com 352 Test768 * ~Tester testclient.example.com testserver.example.com Test768 H :0 ..."},
  85. {Server: ":Test768 MODE Test768 :+i"},
  86. {Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
  87. {Client: "PONG :testserver.example.com"},
  88. {Callback: func() error {
  89. if client.Nick() != "Test768" {
  90. return errors.New("client.Nick shouldn't be " + client.Nick())
  91. }
  92. if client.User() != "~Tester" {
  93. return errors.New("client.User shouldn't be " + client.User())
  94. }
  95. if client.Host() != "testclient.example.com" {
  96. return errors.New("client.Host shouldn't be " + client.Host())
  97. }
  98. return nil
  99. }},
  100. {Callback: func() error {
  101. client.Join("#Test")
  102. return nil
  103. }},
  104. {Client: "JOIN #Test"},
  105. {Server: ":Test768!~Tester@127.0.0.1 JOIN #Test *"},
  106. {Server: ":testserver.example.com 353 Test768 = #Test :Test768!~Tester@127.0.0.1 @+Gisle!irce@10.32.0.1"},
  107. {Server: ":testserver.example.com 366 Test768 #Test :End of /NAMES list."},
  108. {Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
  109. {Client: "PONG :testserver.example.com"},
  110. {Callback: func() error {
  111. if client.Channel("#Test") == nil {
  112. return errors.New("Channel #Test not found")
  113. }
  114. return nil
  115. }},
  116. {Server: ":Gisle!~irce@10.32.0.1 MODE #Test +osv Test768 Test768"},
  117. {Server: ":Gisle!~irce@10.32.0.1 MODE #Test +N-s "},
  118. {Server: ":Test1234!~test2@172.17.37.1 JOIN #Test Test1234"},
  119. {Server: ":Test4321!~test2@172.17.37.1 JOIN #Test Test1234"},
  120. {Server: ":Gisle!~irce@10.32.0.1 MODE #Test +v Test1234"},
  121. {Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
  122. {Client: "PONG :testserver.example.com"},
  123. {Callback: func() error {
  124. channel := client.Channel("#Test")
  125. if channel == nil {
  126. return errors.New("Channel #Test not found")
  127. }
  128. err := irctest.AssertUserlist(t, channel, "@Gisle", "@Test768", "+Test1234", "Test4321")
  129. if err != nil {
  130. return err
  131. }
  132. userTest1234, ok := channel.UserList().User("Test1234")
  133. if !ok {
  134. return errors.New("Test1234 not found")
  135. }
  136. if userTest1234.Account != "Test1234" {
  137. return errors.New("Test1234 did not get account from extended-join")
  138. }
  139. return nil
  140. }},
  141. {Server: ":Test1234!~test2@172.17.37.1 NICK Hunter2"},
  142. {Server: ":Hunter2!~test2@172.17.37.1 AWAY :Doing stuff"},
  143. {Server: ":Gisle!~irce@10.32.0.1 AWAY"},
  144. {Server: ":Gisle!~irce@10.32.0.1 PART #Test :Leaving the channel"},
  145. {Server: ":Hunter2!~test2@172.17.37.1 CHGHOST test2 some.awesome.virtual.host"},
  146. {Server: "@account=Hunter2 :Test4321!~test2@172.17.37.1 PRIVMSG #Test :Hello World."},
  147. {Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
  148. {Client: "PONG :testserver.example.com"},
  149. {Callback: func() error {
  150. channel := client.Channel("#Test")
  151. if channel == nil {
  152. return errors.New("Channel #Test not found")
  153. }
  154. err := irctest.AssertUserlist(t, channel, "@Test768", "+Hunter2", "Test4321")
  155. if err != nil {
  156. return err
  157. }
  158. _, ok := channel.UserList().User("Test1234")
  159. if ok {
  160. return errors.New("Test1234 is still there")
  161. }
  162. userHunter2, ok := channel.UserList().User("Hunter2")
  163. if !ok {
  164. return errors.New("Test1234 not found")
  165. }
  166. if userHunter2.Account != "Test1234" {
  167. return errors.New("Hunter2 did not persist account post nick change")
  168. }
  169. if !userHunter2.IsAway() {
  170. return errors.New("Hunter2 should be away")
  171. }
  172. if userHunter2.Away != "Doing stuff" {
  173. return errors.New("Hunter2 has the wrong away message: " + userHunter2.Away)
  174. }
  175. if userHunter2.Host != "some.awesome.virtual.host" {
  176. return errors.New("Hunter2 should have changed the host: " + userHunter2.Host)
  177. }
  178. event := logger.Last("packet", "PRIVMSG")
  179. if event == nil {
  180. return errors.New("did not find last channel message")
  181. }
  182. if event.ChannelTarget() == nil {
  183. return errors.New("event lacks channel target")
  184. }
  185. return nil
  186. }},
  187. {Server: ":Hunter2!~test2@172.17.37.1 PRIVMSG Test768 :Hello, World"},
  188. {Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
  189. {Client: "PONG :testserver.example.com"},
  190. {Callback: func() error {
  191. query := client.Query("Hunter2")
  192. if query == nil {
  193. return errors.New("Did not find query")
  194. }
  195. event := logger.Last("packet", "PRIVMSG")
  196. if event == nil {
  197. return errors.New("did not find last query message")
  198. }
  199. if event.QueryTarget() == nil {
  200. return errors.New("event lacks query target")
  201. }
  202. return nil
  203. }},
  204. {Server: ":Hunter2!~test2@172.17.37.1 NICK SevenAsterisks"},
  205. {Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
  206. {Client: "PONG :testserver.example.com"},
  207. {Callback: func() error {
  208. oldQuery := client.Query("Hunter2")
  209. if oldQuery != nil {
  210. return errors.New("Did find query by old name")
  211. }
  212. query := client.Query("SevenAsterisks")
  213. if query == nil {
  214. return errors.New("Did not find query by new name")
  215. }
  216. return nil
  217. }},
  218. {Callback: func() error {
  219. client.EmitInput("/invalidcommand stuff and things", nil)
  220. return nil
  221. }},
  222. {Client: "INVALIDCOMMAND stuff and things"},
  223. {Server: ":testserver.example.com 421 Test768 INVALIDCOMMAND :Unknown command"},
  224. {Callback: func() error {
  225. client.Say("SevenAsterisks", "hi!")
  226. return nil
  227. }},
  228. {Client: "PRIVMSG SevenAsterisks :hi!"},
  229. {Server: ":Test768!~Tester@127.0.0.1 PRIVMSG SevenAsterisks :hi!"},
  230. {Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
  231. {Client: "PONG :testserver.example.com"},
  232. {Callback: func() error {
  233. event := logger.Last("packet", "PRIVMSG")
  234. if event == nil {
  235. return errors.New("did not find last query message")
  236. }
  237. if event.QueryTarget() == nil {
  238. return errors.New("event lacks query target")
  239. }
  240. if event.QueryTarget().Name() != "SevenAsterisks" {
  241. return errors.New("incorrect query target")
  242. }
  243. if event.Nick != client.Nick() {
  244. return errors.New("incorrect client nick")
  245. }
  246. if event.Arg(0) != "SevenAsterisks" {
  247. return errors.New("incorrect client nick")
  248. }
  249. return nil
  250. }},
  251. {Callback: func() error {
  252. client.EmitInput("/invalidcommand stuff and things", nil)
  253. return nil
  254. }},
  255. {Callback: func() error {
  256. channel := client.Channel("#Test")
  257. if channel == nil {
  258. return errors.New("Channel #Test not found")
  259. }
  260. client.EmitInput("/me does stuff", channel)
  261. client.EmitInput("/describe #Test describes stuff", channel)
  262. client.EmitInput("/text Hello, World", channel)
  263. client.EmitInput("Hello again", channel)
  264. return nil
  265. }},
  266. {Client: "PRIVMSG #Test :\x01ACTION does stuff\x01"},
  267. {Client: "PRIVMSG #Test :\x01ACTION describes stuff\x01"},
  268. {Client: "PRIVMSG #Test :Hello, World"},
  269. {Client: "PRIVMSG #Test :Hello again"},
  270. {Server: ":Test768!~Tester@127.0.0.1 PRIVMSG #Test :\x01ACTION does stuff\x01"},
  271. {Server: ":Test768!~Tester@127.0.0.1 PRIVMSG #Test :\x01ACTION describes stuff\x01"},
  272. {Server: ":Test768!~Tester@127.0.0.1 PRIVMSG #Test :Hello, World"},
  273. {Server: ":Test768!~Tester@127.0.0.1 PRIVMSG #Test :Hello again"},
  274. {Callback: func() error {
  275. channel := client.Channel("#Test")
  276. if channel == nil {
  277. return errors.New("Channel #Test not found")
  278. }
  279. client.EmitInput("/m +N", channel)
  280. client.EmitInput("/npcac Test_NPC stuffs things", channel)
  281. return nil
  282. }},
  283. {Client: "MODE #Test +N"},
  284. {Client: "NPCA #Test Test_NPC :stuffs things"},
  285. {Server: ":Test768!~Tester@127.0.0.1 MODE #Test +N"},
  286. {Server: ":\x1FTest_NPC\x1F!Test768@npc.fakeuser.invalid PRIVMSG #Test :\x01ACTION stuffs things\x01"},
  287. {Callback: func() error {
  288. channel := client.Channel("#Test")
  289. if channel == nil {
  290. return errors.New("Channel #Test not found")
  291. }
  292. client.Describef(channel.Name(), "does stuff with %d things", 42)
  293. client.Sayf(channel.Name(), "Hello, %s", "World")
  294. return nil
  295. }},
  296. {Client: "PRIVMSG #Test :\x01ACTION does stuff with 42 things\x01"},
  297. {Client: "PRIVMSG #Test :Hello, World"},
  298. {Callback: func() error {
  299. channel := client.Channel("#Test")
  300. if channel == nil {
  301. return errors.New("#Test doesn't exist")
  302. }
  303. _, err := client.RemoveTarget(channel)
  304. return err
  305. }},
  306. {Client: "PART #Test"},
  307. {Server: ":Test768!~Tester@127.0.0.1 PART #Test"},
  308. {Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
  309. {Client: "PONG :testserver.example.com"},
  310. {Callback: func() error {
  311. if client.Channel("#Test") != nil {
  312. return errors.New("#Test is still there.")
  313. }
  314. return nil
  315. }},
  316. {Callback: func() error {
  317. client.Join("#Test2")
  318. return nil
  319. }},
  320. {Client: "JOIN #Test2"},
  321. {Server: ":Test768!~Tester@127.0.0.1 JOIN #Test2 *"},
  322. {Server: ":testserver.example.com 353 Test768 = #Test2 :Test768!~Tester@127.0.0.1 +DoomedUser!doom@example.com @+ZealousMod!zeal@example.com"},
  323. {Server: ":testserver.example.com 366 Test768 #Test2 :End of /NAMES list."},
  324. {Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
  325. {Client: "PONG :testserver.example.com"},
  326. {Callback: func() error {
  327. channel := client.Channel("#Test2")
  328. if channel == nil {
  329. return errors.New("Channel #Test2 not found")
  330. }
  331. return irctest.AssertUserlist(t, channel, "@ZealousMod", "+DoomedUser", "Test768")
  332. }},
  333. {Server: ":ZealousMod!zeal@example.com KICK #Test2 DoomedUser :Kickety kick"},
  334. {Server: "PING :testserver.example.com sync"}, // Ping/Pong to sync.
  335. {Client: "PONG :testserver.example.com sync"},
  336. {Callback: func() error {
  337. channel := client.Channel("#Test2")
  338. if channel == nil {
  339. return errors.New("Channel #Test2 not found")
  340. }
  341. return irctest.AssertUserlist(t, channel, "@ZealousMod", "Test768")
  342. }},
  343. {Server: ":ZealousMod!zeal@example.com KICK #Test2 Test768 :Kickety kick"},
  344. {Server: "PING :testserver.example.com sync"}, // Ping/Pong to sync.
  345. {Client: "PONG :testserver.example.com sync"},
  346. {Callback: func() error {
  347. if client.Channel("#Test2") != nil {
  348. return errors.New("#Test2 is still there.")
  349. }
  350. return nil
  351. }},
  352. {Server: ":testserver.example.com CAP Test768 NEW :invite-notify"},
  353. {Client: "CAP REQ :invite-notify"},
  354. {Server: ":testserver.example.com CAP Test768 ACK :invite-notify"},
  355. {Server: ":ZealousMod!zeal@example.com INVITE Test768 #Test2"},
  356. {Client: "JOIN #Test2"},
  357. {Server: ":Test768!~Tester@127.0.0.1 JOIN #Test2 *"},
  358. {Server: ":testserver.example.com 353 Test768 = #Test2 :Test768!~Tester@127.0.0.1 @+ZealousMod!zeal@example.com"},
  359. {Server: ":testserver.example.com 366 Test768 #Test2 :End of /NAMES list."},
  360. {Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
  361. {Client: "PONG :testserver.example.com"},
  362. {Callback: func() error {
  363. if client.Channel("#Test2") == nil {
  364. return errors.New("#Test2 is not there.")
  365. }
  366. return nil
  367. }},
  368. {Server: ":ZealousMod!zeal@example.com INVITE Test768 #Test2"},
  369. {Server: ":ZealousMod!zeal@example.com INVITE DoomedUser #test768-do-not-join"},
  370. {Server: "PING :testserver.example.com"}, // Ping/Pong to sync.
  371. {Client: "PONG :testserver.example.com"},
  372. },
  373. }
  374. addr, err := interaction.Listen()
  375. if err != nil {
  376. t.Fatal("Listen:", err)
  377. }
  378. if err := client.Disconnect(false); err != irc.ErrNoConnection {
  379. t.Errorf("It should fail to disconnect, got: %s", err)
  380. }
  381. err = client.Connect(addr, false)
  382. if err != nil {
  383. t.Fatal("Connect:", err)
  384. return
  385. }
  386. interaction.Wait()
  387. fail := interaction.Failure
  388. if fail != nil {
  389. t.Error("Index:", fail.Index)
  390. t.Error("NetErr:", fail.NetErr)
  391. t.Error("CBErr:", fail.CBErr)
  392. t.Error("Result:", fail.Result)
  393. if fail.Index >= 0 {
  394. if interaction.Lines[fail.Index].Server != "" {
  395. t.Error("Line.Server:", interaction.Lines[fail.Index].Server)
  396. }
  397. if interaction.Lines[fail.Index].Client != "" {
  398. t.Error("Line.Client:", interaction.Lines[fail.Index].Client)
  399. }
  400. }
  401. }
  402. for i, logLine := range interaction.Log {
  403. t.Logf("Log[%d] = %#+v", i, logLine)
  404. }
  405. }
  406. // TestParenthesesBug tests that the bugfix causing `((Remove :01 goofs!*))` to be parsed as an empty message. It was
  407. // initially thought to be caused by the parentheses (like a hidden m_roleplay NPC attribution removal), hence the name
  408. // for this function.
  409. func TestParenthesesBug(t *testing.T) {
  410. gotMessage := true
  411. client := irc.New(context.Background(), irc.Config{
  412. Nick: "Stuff",
  413. })
  414. client.AddHandler(func(event *irc.Event, client *irc.Client) {
  415. if event.Name() == "packet.privmsg" || event.Nick == "Beans" {
  416. gotMessage = true
  417. if event.Text != "((Remove :01 goofs!*))" {
  418. t.Errorf("Expected: %#+v", "((Remove :01 goofs!*))")
  419. t.Errorf("Result: %#+v", event.Text)
  420. }
  421. }
  422. })
  423. packet, err := irc.ParsePacket("@example/tag=32; :Beans!beans@beans.example.com PRIVMSG Stuff :((Remove :01 goofs!*))")
  424. if err != nil {
  425. t.Error("Parse", err)
  426. }
  427. client.EmitSync(context.Background(), packet)
  428. if !gotMessage {
  429. t.Error("Message was not received")
  430. }
  431. }