Best Testcontainers-go code snippet using wait.Host
client_test.go
Source:client_test.go
1package twitch2import (3 "bufio"4 "context"5 "crypto/tls"6 "fmt"7 "net"8 "net/textproto"9 "reflect"10 "sort"11 "strconv"12 "strings"13 "sync"14 "sync/atomic"15 "testing"16 "time"17)18var startPortMutex sync.Mutex19var startPort = 1000020func newPort() (r int) {21 startPortMutex.Lock()22 r = startPort23 startPort++24 startPortMutex.Unlock()25 return26}27func closeOnConnect(c chan struct{}) func(conn net.Conn) {28 return func(conn net.Conn) {29 close(c)30 }31}32func waitWithTimeout(c chan struct{}) bool {33 select {34 case <-c:35 return true36 case <-time.After(time.Second * 3):37 return false38 }39}40func closeOnPassReceived(pass *string, c chan struct{}) func(message string) {41 return func(message string) {42 if strings.HasPrefix(message, "PASS") {43 *pass = message44 close(c)45 }46 }47}48func nothingOnConnect(conn net.Conn) {49}50func nothingOnMessage(message string) {51}52func clientCloseOnConnect(c chan struct{}) func() {53 return func() {54 close(c)55 }56}57func postMessageOnConnect(message string) func(conn net.Conn) {58 return func(conn net.Conn) {59 fmt.Fprintf(conn, "%s\r\n", message)60 }61}62func postMessagesOnConnect(messages []string) func(conn net.Conn) {63 return func(conn net.Conn) {64 for _, message := range messages {65 fmt.Fprintf(conn, "%s\r\n", message)66 }67 }68}69func newTestClient(host string) *Client {70 client := NewClient("justinfan123123", "oauth:123123132")71 client.IrcAddress = host72 return client73}74func newAnonymousTestClient(host string) *Client {75 client := NewAnonymousClient()76 client.IrcAddress = host77 return client78}79func connectAndEnsureGoodDisconnect(t *testing.T, client *Client) chan struct{} {80 c := make(chan struct{})81 go func() {82 err := client.Connect()83 assertErrorsEqual(t, ErrClientDisconnected, err)84 close(c)85 }()86 return c87}88func handleTestConnection(t *testing.T, onConnect func(net.Conn), onMessage func(string), listener net.Listener, wg *sync.WaitGroup) {89 conn, err := listener.Accept()90 if err != nil {91 t.Error(err)92 }93 defer func() {94 time.Sleep(100 * time.Millisecond)95 conn.Close()96 wg.Done()97 }()98 reader := bufio.NewReader(conn)99 tp := textproto.NewReader(reader)100 for {101 message, err := tp.ReadLine()102 if err != nil {103 return104 }105 message = strings.Replace(message, "\r\n", "", 1)106 if strings.HasPrefix(message, "NICK") {107 fmt.Fprintf(conn, ":tmi.twitch.tv 001 justinfan123123 :Welcome, GLHF!\r\n")108 onConnect(conn)109 continue110 }111 if strings.HasPrefix(message, "PASS") {112 pass := strings.Split(message, " ")[1]113 if !strings.HasPrefix(pass, "oauth:") {114 fmt.Fprintf(conn, ":tmi.twitch.tv NOTICE * :Improperly formatted auth\r\n")115 return116 } else if pass == "oauth:wrong" {117 fmt.Fprintf(conn, ":tmi.twitch.tv NOTICE * :Login authentication failed\r\n")118 return119 }120 }121 onMessage(message)122 }123}124type testServer struct {125 host string126 stopped chan struct{}127}128func startServer(t *testing.T, onConnect func(net.Conn), onMessage func(string)) string {129 s := startServer2(t, onConnect, onMessage)130 return s.host131}132func startServer2(t *testing.T, onConnect func(net.Conn), onMessage func(string)) *testServer {133 s := &testServer{134 host: "127.0.0.1:" + strconv.Itoa(newPort()),135 stopped: make(chan struct{}),136 }137 cert, err := tls.LoadX509KeyPair("test_resources/server.crt", "test_resources/server.key")138 if err != nil {139 t.Fatal(err)140 }141 config := &tls.Config{142 MinVersion: tls.VersionTLS12,143 Certificates: []tls.Certificate{cert},144 }145 listener, err := tls.Listen("tcp", s.host, config)146 if err != nil {147 t.Fatal(err)148 }149 wg := sync.WaitGroup{}150 wg.Add(1)151 go handleTestConnection(t, onConnect, onMessage, listener, &wg)152 go func() {153 wg.Wait()154 listener.Close()155 close(s.stopped)156 }()157 return s158}159func startServerMultiConns(t *testing.T, numConns int, onConnect func(net.Conn), onMessage func(string)) string {160 host := "127.0.0.1:" + strconv.Itoa(newPort())161 cert, err := tls.LoadX509KeyPair("test_resources/server.crt", "test_resources/server.key")162 if err != nil {163 t.Fatal(err)164 }165 config := &tls.Config{166 Certificates: []tls.Certificate{cert},167 }168 listener, err := tls.Listen("tcp", host, config)169 if err != nil {170 t.Fatal(err)171 }172 wg := sync.WaitGroup{}173 wg.Add(numConns)174 for i := 0; i < numConns; i++ {175 go handleTestConnection(t, onConnect, onMessage, listener, &wg)176 }177 go func() {178 wg.Wait()179 listener.Close()180 }()181 return host182}183func startServerMultiConnsNoTLS(t *testing.T, numConns int, onConnect func(net.Conn), onMessage func(string)) string {184 host := "127.0.0.1:" + strconv.Itoa(newPort())185 listener, err := net.Listen("tcp", host)186 if err != nil {187 t.Fatal(err)188 }189 wg := sync.WaitGroup{}190 wg.Add(numConns)191 for i := 0; i < numConns; i++ {192 go handleTestConnection(t, onConnect, onMessage, listener, &wg)193 }194 go func() {195 wg.Wait()196 listener.Close()197 }()198 return host199}200func startNoTLSServer(t *testing.T, onConnect func(net.Conn), onMessage func(string)) string {201 host := "127.0.0.1:" + strconv.Itoa(newPort())202 listener, err := net.Listen("tcp", host)203 if err != nil {204 t.Fatal(err)205 }206 wg := sync.WaitGroup{}207 wg.Add(1)208 go handleTestConnection(t, onConnect, onMessage, listener, &wg)209 go func() {210 wg.Wait()211 listener.Close()212 }()213 return host214}215func TestCanConnectAndAuthenticateWithoutTLS(t *testing.T) {216 t.Parallel()217 const oauthCode = "oauth:123123132"218 wait := make(chan struct{})219 var received string220 host := startNoTLSServer(t, nothingOnConnect, func(message string) {221 if strings.HasPrefix(message, "PASS") {222 received = message223 close(wait)224 }225 })226 client := NewClient("justinfan123123", oauthCode)227 client.TLS = false228 client.IrcAddress = host229 client.PongTimeout = time.Second * 30230 go client.Connect()231 select {232 case <-wait:233 case <-time.After(time.Second * 3):234 t.Fatal("no oauth read")235 }236 assertStringsEqual(t, "PASS "+oauthCode, received)237}238func TestCanChangeOauthToken(t *testing.T) {239 t.Parallel()240 const oauthCode = "oauth:123123132"241 wait := make(chan bool)242 var received string243 host := startNoTLSServer(t, nothingOnConnect, func(message string) {244 if strings.HasPrefix(message, "PASS") {245 received = message246 wait <- true247 }248 })249 client := NewClient("justinfan123123", "wrongoauthcodelol")250 client.TLS = false251 client.IrcAddress = host252 client.SetIRCToken(oauthCode)253 go client.Connect()254 select {255 case <-wait:256 case <-time.After(time.Second * 3):257 t.Fatal("no oauth read")258 }259 assertStringsEqual(t, "PASS "+oauthCode, received)260}261func TestCanAddSetupCmd(t *testing.T) {262 t.Parallel()263 const oauthCode = "oauth:123123132"264 const setupCmd = "LOGIN kkonabot"265 wait := make(chan bool)266 var received string267 host := startNoTLSServer(t, nothingOnConnect, func(message string) {268 if strings.HasPrefix(message, "LOGIN") {269 received = message270 wait <- true271 }272 })273 client := NewClient("justinfan123123", oauthCode)274 client.TLS = false275 client.IrcAddress = host276 client.SetupCmd = setupCmd277 go client.Connect()278 select {279 case <-wait:280 case <-time.After(time.Second * 3):281 t.Fatal("no oauth read")282 }283 assertStringsEqual(t, setupCmd, received)284}285func TestCanCreateClient(t *testing.T) {286 t.Parallel()287 client := NewClient("justinfan123123", "oauth:1123123")288 if reflect.TypeOf(client) != reflect.TypeOf(&Client{}) {289 t.Error("client is not of type Client")290 }291}292func TestCanConnectAndAuthenticate(t *testing.T) {293 const oauthCode = "oauth:123123132"294 wait := make(chan struct{})295 var received string296 host := startServer(t, nothingOnConnect, func(message string) {297 if strings.HasPrefix(message, "PASS") {298 received = message299 close(wait)300 }301 })302 client := newTestClient(host)303 client.PongTimeout = time.Second * 30304 connectAndEnsureGoodDisconnect(t, client)305 defer client.Disconnect()306 select {307 case <-wait:308 case <-time.After(time.Second * 3):309 t.Fatal("no oauth read")310 }311 assertStringsEqual(t, "PASS "+oauthCode, received)312}313// This test is meant to be a blueprint for a test that needs the flow completely from server start to server stop314func TestFullConnectAndDisconnect(t *testing.T) {315 const oauthCode = "oauth:123123132"316 waitPass := make(chan struct{})317 waitServerConnect := make(chan struct{})318 waitClientConnect := make(chan struct{})319 var received string320 server := startServer2(t, closeOnConnect(waitServerConnect), closeOnPassReceived(&received, waitPass))321 client := newTestClient(server.host)322 client.OnConnect(clientCloseOnConnect(waitClientConnect))323 clientDisconnected := connectAndEnsureGoodDisconnect(t, client)324 // Wait for correct password to be read in server325 if !waitWithTimeout(waitPass) {326 t.Fatal("no oauth read")327 }328 assertStringsEqual(t, "PASS "+oauthCode, received)329 // Wait for server to acknowledge connection330 if !waitWithTimeout(waitServerConnect) {331 t.Fatal("no successful connection")332 }333 // Wait for client to acknowledge connection334 if !waitWithTimeout(waitClientConnect) {335 t.Fatal("no successful connection")336 }337 // Disconnect client from server338 err := client.Disconnect()339 if err != nil {340 t.Error("Error during disconnect:" + err.Error())341 }342 // Wait for client to be fully disconnected343 <-clientDisconnected344 // Wait for server to be fully disconnected345 <-server.stopped346}347func TestCanConnectAndAuthenticateAnonymous(t *testing.T) {348 const oauthCode = "oauth:59301"349 waitPass := make(chan struct{})350 waitServerConnect := make(chan struct{})351 waitClientConnect := make(chan struct{})352 var received string353 server := startServer2(t, closeOnConnect(waitServerConnect), closeOnPassReceived(&received, waitPass))354 client := newAnonymousTestClient(server.host)355 client.OnConnect(clientCloseOnConnect(waitClientConnect))356 clientDisconnected := connectAndEnsureGoodDisconnect(t, client)357 // Wait for server to acknowledge connection358 if !waitWithTimeout(waitServerConnect) {359 t.Fatal("no successful connection")360 }361 // Wait for client to acknowledge connection362 if !waitWithTimeout(waitClientConnect) {363 t.Fatal("no successful connection")364 }365 // Wait to receive password366 select {367 case <-waitPass:368 case <-time.After(time.Second * 3):369 t.Fatal("no oauth read")370 }371 assertStringsEqual(t, "PASS "+oauthCode, received)372 // Disconnect client from server373 err := client.Disconnect()374 if err != nil {375 t.Error("Error during disconnect:" + err.Error())376 }377 // Wait for client to be fully disconnected378 <-clientDisconnected379 // Wait for server to be fully disconnected380 <-server.stopped381}382func TestCanDisconnect(t *testing.T) {383 t.Parallel()384 wait := make(chan struct{})385 host := startServer(t, nothingOnConnect, nothingOnMessage)386 client := newTestClient(host)387 client.OnConnect(func() {388 close(wait)389 })390 go client.Connect()391 // wait for server to start392 select {393 case <-wait:394 case <-time.After(time.Second * 3):395 t.Fatal("OnConnect did not fire")396 }397 if err := client.Disconnect(); err != nil {398 t.Fatalf("couldn't disconnect: %s", err.Error())399 }400}401func TestCanNotDisconnectOnClosedConnection(t *testing.T) {402 t.Parallel()403 client := NewClient("justinfan123123", "oauth:123123132")404 err := client.Disconnect()405 assertErrorsEqual(t, ErrConnectionIsNotOpen, err)406}407func TestCanReceivePRIVMSGMessage(t *testing.T) {408 t.Parallel()409 testMessage := "@badges=subscriber/6,premium/1;color=#FF0000;display-name=Redflamingo13;emotes=;id=2a31a9df-d6ff-4840-b211-a2547c7e656e;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1490382457309;turbo=0;user-id=78424343;user-type= :redflamingo13!redflamingo13@redflamingo13.tmi.twitch.tv PRIVMSG #pajlada :Thrashh5, FeelsWayTooAmazingMan kinda"410 wait := make(chan struct{})411 var received string412 host := startServer(t, postMessageOnConnect(testMessage), nothingOnMessage)413 client := newTestClient(host)414 client.OnPrivateMessage(func(message PrivateMessage) {415 received = message.Message416 assertMessageTypesEqual(t, PRIVMSG, message.GetType())417 close(wait)418 })419 go client.Connect()420 // wait for server to start421 select {422 case <-wait:423 case <-time.After(time.Second * 3):424 t.Fatal("no message sent")425 }426 assertStringsEqual(t, "Thrashh5, FeelsWayTooAmazingMan kinda", received)427}428func TestCanReceiveWHISPERMessage(t *testing.T) {429 t.Parallel()430 testMessage := "@badges=;color=#00FF7F;display-name=Danielps1;emotes=;message-id=20;thread-id=32591953_77829817;turbo=0;user-id=32591953;user-type= :danielps1!danielps1@danielps1.tmi.twitch.tv WHISPER gempir :i like memes"431 wait := make(chan struct{})432 var received string433 host := startServer(t, postMessageOnConnect(testMessage), nothingOnMessage)434 client := newTestClient(host)435 client.OnWhisperMessage(func(message WhisperMessage) {436 received = message.Message437 assertMessageTypesEqual(t, WHISPER, message.GetType())438 close(wait)439 })440 go client.Connect()441 // wait for server to start442 select {443 case <-wait:444 case <-time.After(time.Second * 3):445 t.Fatal("no message sent")446 }447 assertStringsEqual(t, "i like memes", received)448}449func TestCanReceiveCLEARCHATMessage(t *testing.T) {450 t.Parallel()451 testMessage := `@ban-duration=1;ban-reason=testing\sxd;room-id=11148817;target-user-id=40910607 :tmi.twitch.tv CLEARCHAT #pajlada :ampzyh`452 wait := make(chan struct{})453 var received int454 host := startServer(t, postMessageOnConnect(testMessage), nothingOnMessage)455 client := newTestClient(host)456 client.OnClearChatMessage(func(message ClearChatMessage) {457 received = message.BanDuration458 assertMessageTypesEqual(t, CLEARCHAT, message.GetType())459 close(wait)460 })461 go client.Connect()462 // wait for server to start463 select {464 case <-wait:465 case <-time.After(time.Second * 3):466 t.Fatal("no message sent")467 }468 assertIntsEqual(t, 1, received)469}470func TestCanReceiveCLEARMSGMessage(t *testing.T) {471 t.Parallel()472 testMessage := `@login=ronni;target-msg-id=abc-123-def :tmi.twitch.tv CLEARMSG #dallas :HeyGuys`473 wait := make(chan struct{})474 var received string475 host := startServer(t, postMessageOnConnect(testMessage), nothingOnMessage)476 client := newTestClient(host)477 client.OnClearMessage(func(message ClearMessage) {478 received = message.Login479 assertMessageTypesEqual(t, CLEARMSG, message.GetType())480 close(wait)481 })482 go client.Connect()483 // wait for server to start484 select {485 case <-wait:486 case <-time.After(time.Second * 3):487 t.Fatal("no message sent")488 }489 assertStringsEqual(t, "ronni", received)490}491func TestCanReceiveROOMSTATEMessage(t *testing.T) {492 t.Parallel()493 testMessage := `@slow=10 :tmi.twitch.tv ROOMSTATE #gempir`494 wait := make(chan struct{})495 var received string496 host := startServer(t, postMessageOnConnect(testMessage), nothingOnMessage)497 client := newTestClient(host)498 client.OnRoomStateMessage(func(message RoomStateMessage) {499 received = message.Tags["slow"]500 assertMessageTypesEqual(t, ROOMSTATE, message.GetType())501 close(wait)502 })503 go client.Connect()504 // wait for server to start505 select {506 case <-wait:507 case <-time.After(time.Second * 3):508 t.Fatal("no message sent")509 }510 assertStringsEqual(t, "10", received)511}512func TestCanReceiveUSERNOTICEMessage(t *testing.T) {513 t.Parallel()514 testMessage := `@badges=subscriber/12,premium/1;color=#5F9EA0;display-name=blahh;emotes=;id=9154ac04-c9ad-46d5-97ad-15d2dbf244f0;login=deliquid;mod=0;msg-id=resub;msg-param-months=16;msg-param-sub-plan-name=Channel\sSubscription\s(NOTHING);msg-param-sub-plan=Prime;room-id=23161357;subscriber=1;system-msg=blahh\sjust\ssubscribed\swith\sTwitch\sPrime.\sblahh\ssubscribed\sfor\s16\smonths\sin\sa\srow!;tmi-sent-ts=1517165351175;turbo=0;user-id=1234567890;user-type= :tmi.twitch.tv USERNOTICE #nothing`515 wait := make(chan struct{})516 var received string517 host := startServer(t, postMessageOnConnect(testMessage), nothingOnMessage)518 client := newTestClient(host)519 client.OnUserNoticeMessage(func(message UserNoticeMessage) {520 received = message.Tags["msg-param-months"]521 assertMessageTypesEqual(t, USERNOTICE, message.GetType())522 close(wait)523 })524 go client.Connect()525 select {526 case <-wait:527 case <-time.After(time.Second * 3):528 t.Fatal("no message sent")529 }530 assertStringsEqual(t, "16", received)531}532func TestCanReceiveUSERNOTICEMessageResub(t *testing.T) {533 t.Parallel()534 testMessage := `@badges=moderator/1,subscriber/24;color=#1FD2FF;display-name=Karl_Kons;emotes=28087:0-6;flags=;id=7c95beea-a7ac-4c10-9e0a-d7dbf163c038;login=karl_kons;mod=1;msg-id=resub;msg-param-months=34;msg-param-sub-plan-name=look\sat\sthose\sshitty\semotes,\srip\s$5\sLUL;msg-param-sub-plan=1000;room-id=11148817;subscriber=1;system-msg=Karl_Kons\sjust\ssubscribed\swith\sa\sTier\s1\ssub.\sKarl_Kons\ssubscribed\sfor\s34\smonths\sin\sa\srow!;tmi-sent-ts=1540140252828;turbo=0;user-id=68706331;user-type=mod :tmi.twitch.tv USERNOTICE #pajlada :WutFace`535 wait := make(chan struct{})536 var received string537 host := startServer(t, postMessageOnConnect(testMessage), nothingOnMessage)538 client := newTestClient(host)539 client.OnUserNoticeMessage(func(message UserNoticeMessage) {540 received = message.Tags["msg-param-months"]541 close(wait)542 })543 go client.Connect()544 select {545 case <-wait:546 case <-time.After(time.Second * 3):547 t.Fatal("no message sent")548 }549 assertStringsEqual(t, "34", received)550}551func checkNoticeMessage(t *testing.T, testMessage string, requirements map[string]string) {552 received := map[string]string{}553 wait := make(chan struct{})554 host := startServer(t, postMessageOnConnect(testMessage), nothingOnMessage)555 client := newTestClient(host)556 client.OnNoticeMessage(func(message NoticeMessage) {557 received["msg-id"] = message.Tags["msg-id"]558 received["channel"] = message.Channel559 received["text"] = message.Message560 received["raw"] = message.Raw561 assertMessageTypesEqual(t, NOTICE, message.GetType())562 close(wait)563 })564 go client.Connect()565 select {566 case <-wait:567 case <-time.After(time.Second * 3):568 t.Fatal("no message sent")569 }570 assertStringsEqual(t, testMessage, received["raw"])571 for key, requirement := range requirements {572 assertStringsEqual(t, requirement, received[key])573 }574}575func TestCanReceiveNOTICEMessage(t *testing.T) {576 t.Parallel()577 testMessage := `@msg-id=host_on :tmi.twitch.tv NOTICE #pajlada :Now hosting KKona.`578 checkNoticeMessage(t, testMessage, map[string]string{579 "msg-id": "host_on",580 "channel": "pajlada",581 "text": "Now hosting KKona.",582 })583}584func TestCanReceiveNOTICEMessageTimeout(t *testing.T) {585 t.Parallel()586 testMessage := `@msg-id=timeout_success :tmi.twitch.tv NOTICE #forsen :thedl0rd has been timed out for 8 minutes 11 seconds.`587 checkNoticeMessage(t, testMessage, map[string]string{588 "msg-id": "timeout_success",589 "channel": "forsen",590 "text": "thedl0rd has been timed out for 8 minutes 11 seconds.",591 })592}593func TestCanReceiveUSERStateMessage(t *testing.T) {594 t.Parallel()595 testMessage := `@badges=moderator/1;color=;display-name=blahh;emote-sets=0;mod=1;subscriber=0;user-type=mod :tmi.twitch.tv USERSTATE #nothing`596 wait := make(chan struct{})597 var received string598 host := startServer(t, postMessageOnConnect(testMessage), nothingOnMessage)599 client := newTestClient(host)600 client.OnUserStateMessage(func(message UserStateMessage) {601 received = message.Tags["mod"]602 assertMessageTypesEqual(t, USERSTATE, message.GetType())603 close(wait)604 })605 go client.Connect()606 select {607 case <-wait:608 case <-time.After(time.Second * 3):609 t.Fatal("no message sent")610 }611 assertStringsEqual(t, "1", received)612}613func TestCanReceiveGlobalUserStateMessage(t *testing.T) {614 t.Parallel()615 testMessage := `@badge-info=;badges=;color=#00FF7F;display-name=gempbot;emote-sets=0,14417,300206298,300374282,300548762;user-id=99659894;user-type= :tmi.twitch.tv GLOBALUSERSTATE`616 wait := make(chan struct{})617 var received string618 host := startServer(t, postMessageOnConnect(testMessage), nothingOnMessage)619 client := newTestClient(host)620 client.OnGlobalUserStateMessage(func(message GlobalUserStateMessage) {621 received = message.Tags["user-id"]622 assertMessageTypesEqual(t, GLOBALUSERSTATE, message.GetType())623 close(wait)624 })625 //nolint626 go client.Connect()627 select {628 case <-wait:629 case <-time.After(time.Second * 3):630 t.Fatal("no message sent")631 }632 assertStringsEqual(t, "99659894", received)633}634func TestCanReceiveJOINMessage(t *testing.T) {635 t.Parallel()636 testMessage := `:username123!username123@username123.tmi.twitch.tv JOIN #mychannel`637 wait := make(chan struct{})638 var received UserJoinMessage639 host := startServer(t, postMessageOnConnect(testMessage), nothingOnMessage)640 client := newTestClient(host)641 client.OnUserJoinMessage(func(message UserJoinMessage) {642 received = message643 close(wait)644 })645 go client.Connect()646 // wait for server to start647 select {648 case <-wait:649 case <-time.After(time.Second * 3):650 t.Fatal("no message sent")651 }652 assertStringsEqual(t, "username123", received.User)653 assertStringsEqual(t, "mychannel", received.Channel)654 assertMessageTypesEqual(t, JOIN, received.GetType())655}656func TestReceiveJOINMessageWithSelfJOIN(t *testing.T) {657 t.Parallel()658 testMessages := []string{659 `:justinfan123123!justinfan123123@justinfan123123.tmi.twitch.tv JOIN #mychannel`,660 `:username123!username123@username123.tmi.twitch.tv JOIN #mychannel`,661 }662 var receivedOther UserJoinMessage663 var receivedSelf UserJoinMessage664 host := startServer(t, postMessagesOnConnect(testMessages), nothingOnMessage)665 client := newTestClient(host)666 wg := new(sync.WaitGroup)667 wg.Add(2)668 client.OnUserJoinMessage(func(message UserJoinMessage) {669 receivedOther = message670 wg.Done()671 })672 client.OnSelfJoinMessage(func(message UserJoinMessage) {673 receivedSelf = message674 wg.Done()675 })676 go client.Connect()677 // hack with ctx makes it possible to use it in select statement below678 ctx, cancel := context.WithCancel(context.Background())679 go func() {680 wg.Wait()681 cancel()682 }()683 // wait for server to start684 select {685 case <-ctx.Done():686 case <-time.After(time.Second * 3):687 t.Fatal("no message sent")688 }689 assertStringsEqual(t, "username123", receivedOther.User)690 assertStringsEqual(t, "mychannel", receivedOther.Channel)691 assertMessageTypesEqual(t, JOIN, receivedOther.GetType())692 assertStringsEqual(t, "justinfan123123", receivedSelf.User)693 assertStringsEqual(t, "mychannel", receivedSelf.Channel)694 assertMessageTypesEqual(t, JOIN, receivedSelf.GetType())695}696func TestCanReceivePARTMessage(t *testing.T) {697 t.Parallel()698 testMessage := `:username123!username123@username123.tmi.twitch.tv PART #mychannel`699 wait := make(chan struct{})700 var received UserPartMessage701 host := startServer(t, postMessageOnConnect(testMessage), nothingOnMessage)702 client := newTestClient(host)703 client.OnUserPartMessage(func(message UserPartMessage) {704 received = message705 close(wait)706 })707 go client.Connect()708 // wait for server to start709 select {710 case <-wait:711 case <-time.After(time.Second * 3):712 t.Fatal("no message sent")713 }714 assertStringsEqual(t, "username123", received.User)715 assertStringsEqual(t, "mychannel", received.Channel)716 assertMessageTypesEqual(t, PART, received.GetType())717}718func TestReceivePARTMessageWithSelfPART(t *testing.T) {719 t.Parallel()720 testMessages := []string{721 `:justinfan123123!justinfan123123@justinfan123123.tmi.twitch.tv PART #mychannel`,722 `:username123!username123@username123.tmi.twitch.tv PART #mychannel`,723 }724 var receivedOther UserPartMessage725 var receivedSelf UserPartMessage726 host := startServer(t, postMessagesOnConnect(testMessages), nothingOnMessage)727 client := newTestClient(host)728 wg := new(sync.WaitGroup)729 wg.Add(2)730 client.OnUserPartMessage(func(message UserPartMessage) {731 receivedOther = message732 wg.Done()733 })734 client.OnSelfPartMessage(func(message UserPartMessage) {735 receivedSelf = message736 wg.Done()737 })738 go client.Connect()739 // hack with ctx makes it possible to use it in select statement below740 ctx, cancel := context.WithCancel(context.Background())741 go func() {742 wg.Wait()743 cancel()744 }()745 // wait for server to start746 select {747 case <-ctx.Done():748 case <-time.After(time.Second * 3):749 t.Fatal("no message sent")750 }751 assertStringsEqual(t, "username123", receivedOther.User)752 assertStringsEqual(t, "mychannel", receivedOther.Channel)753 assertMessageTypesEqual(t, PART, receivedOther.GetType())754 assertStringsEqual(t, "justinfan123123", receivedSelf.User)755 assertStringsEqual(t, "mychannel", receivedSelf.Channel)756 assertMessageTypesEqual(t, PART, receivedSelf.GetType())757}758func TestCanReceiveUNSETMessage(t *testing.T) {759 t.Parallel()760 testMessage := `@badges=moderator/1,subscriber/24;color=#1FD2FF;display-name=Karl_Kons;emotes=28087:0-6;flags=;id=7c95beea-a7ac-4c10-9e0a-d7dbf163c038;login=karl_kons;mod=1;msg-id=resub;msg-param-months=34;msg-param-sub-plan-name=look\sat\sthose\sshitty\semotes,\srip\s$5\sLUL;msg-param-sub-plan=1000;room-id=11148817;subscriber=1;system-msg=Karl_Kons\sjust\ssubscribed\swith\sa\sTier\s1\ssub.\sKarl_Kons\ssubscribed\sfor\s34\smonths\sin\sa\srow!;tmi-sent-ts=1540140252828;turbo=0;user-id=68706331;user-type=mod :tmi.twitch.tv MALFORMEDMESSAGETYPETHISWILLBEUNSET #pajlada :WutFace`761 wait := make(chan struct{})762 var received RawMessage763 host := startServer(t, postMessageOnConnect(testMessage), nothingOnMessage)764 client := newTestClient(host)765 client.OnUnsetMessage(func(rawMessage RawMessage) {766 if rawMessage.RawType == "MALFORMEDMESSAGETYPETHISWILLBEUNSET" {767 received = rawMessage768 close(wait)769 }770 })771 go client.Connect()772 select {773 case <-wait:774 case <-time.After(time.Second * 3):775 t.Fatal("no message sent")776 }777 assertStringsEqual(t, testMessage, received.Raw)778 assertMessageTypesEqual(t, UNSET, received.GetType())779}780func TestCanHandleRECONNECTMessage(t *testing.T) {781 t.Parallel()782 const testMessage = ":tmi.twitch.tv RECONNECT"783 wait := make(chan bool)784 var received ReconnectMessage785 var connCount int32786 host := startServerMultiConns(t, 2, func(conn net.Conn) {787 atomic.AddInt32(&connCount, 1)788 wait <- true789 time.AfterFunc(100*time.Millisecond, func() {790 fmt.Fprintf(conn, "%s\r\n", testMessage)791 })792 }, nothingOnMessage)793 client := newTestClient(host)794 client.OnReconnectMessage(func(msg ReconnectMessage) {795 received = msg796 })797 go client.Connect()798 // wait for server to start799 select {800 case <-wait:801 case <-time.After(time.Second * 3):802 t.Fatal("no message sent")803 }804 assertInt32sEqual(t, 1, atomic.LoadInt32(&connCount))805 select {806 case <-wait:807 case <-time.After(time.Second * 3):808 t.Fatal("no message sent")809 }810 assertInt32sEqual(t, 2, atomic.LoadInt32(&connCount))811 assertMessageTypesEqual(t, RECONNECT, received.GetType())812}813func TestCanSayMessage(t *testing.T) {814 t.Parallel()815 const testMessage = "Do not go gentle into that good night."816 waitEnd := make(chan struct{})817 var received string818 host := startServer(t, nothingOnConnect, func(message string) {819 if strings.HasPrefix(message, "PRIVMSG") {820 received = message821 close(waitEnd)822 }823 })824 client := newTestClient(host)825 client.OnConnect(func() {826 client.Say("gempir", testMessage)827 })828 go client.Connect()829 // wait for server to receive message830 select {831 case <-waitEnd:832 case <-time.After(time.Second * 3):833 t.Fatal("no privmsg received")834 }835 assertStringsEqual(t, "PRIVMSG #gempir :"+testMessage, received)836}837func TestCanReplyMessage(t *testing.T) {838 t.Parallel()839 testMessage := "Do not go gentle into that good night."840 testParentMessageId := "b34ccfc7-4977-403a-8a94-33c6bac34fb8"841 waitEnd := make(chan struct{})842 var received string843 host := startServer(t, nothingOnConnect, func(message string) {844 if strings.HasPrefix(message, "@reply") {845 received = message846 close(waitEnd)847 }848 })849 client := newTestClient(host)850 client.OnConnect(func() {851 client.Reply("gempir", testParentMessageId, testMessage)852 })853 go client.Connect()854 // wait for server to receive message855 select {856 case <-waitEnd:857 case <-time.After(time.Second * 3):858 t.Fatal("no privmsg received")859 }860 assertStringsEqual(t, "@reply-parent-msg-id="+testParentMessageId+" PRIVMSG #gempir :"+testMessage, received)861}862func TestCanWhisperMessage(t *testing.T) {863 t.Parallel()864 testMessage := "Do not go gentle into that good night."865 waitEnd := make(chan struct{})866 var receivedMsg string867 host := startServer(t, nothingOnConnect, func(message string) {868 if strings.HasPrefix(message, "PRIVMSG") {869 receivedMsg = message870 close(waitEnd)871 }872 })873 client := newTestClient(host)874 go client.Connect()875 client.Whisper("gempir", testMessage)876 // wait for server to receive message877 select {878 case <-waitEnd:879 case <-time.After(time.Second * 3):880 t.Fatal("no privmsg received")881 }882 assertStringsEqual(t, "PRIVMSG #justinfan123123 :/w gempir "+testMessage, receivedMsg)883}884func TestCanJoinChannel(t *testing.T) {885 t.Parallel()886 waitEnd := make(chan struct{})887 var receivedMsg string888 host := startServer(t, nothingOnConnect, func(message string) {889 if strings.HasPrefix(message, "JOIN") {890 receivedMsg = message891 close(waitEnd)892 }893 })894 client := newTestClient(host)895 client.Join("gempiR")896 go client.Connect()897 // wait for server to receive message898 select {899 case <-waitEnd:900 case <-time.After(time.Second * 3):901 t.Fatal("no join message received")902 }903 assertStringsEqual(t, "JOIN #gempir", receivedMsg)904}905func TestCanRunFollowersOn(t *testing.T) {906 t.Parallel()907 waitEnd := make(chan struct{})908 var received string909 host := startServer(t, nothingOnConnect, func(message string) {910 if strings.HasPrefix(message, "PRIVMSG") {911 received = message912 close(waitEnd)913 }914 })915 client := newTestClient(host)916 client.OnConnect(func() {917 client.FollowersOn("gempiR", "30m")918 })919 go client.Connect() //nolint920 // wait for server to receive message921 select {922 case <-waitEnd:923 case <-time.After(time.Second * 3):924 t.Fatal("no privmsg received")925 }926 assertStringsEqual(t, "PRIVMSG #gempir :/followers 30m", received)927}928func TestCanRunFollowersOff(t *testing.T) {929 t.Parallel()930 waitEnd := make(chan struct{})931 var received string932 host := startServer(t, nothingOnConnect, func(message string) {933 if strings.HasPrefix(message, "PRIVMSG") {934 received = message935 close(waitEnd)936 }937 })938 client := newTestClient(host)939 client.OnConnect(func() {940 client.FollowersOff("gempiR")941 })942 go client.Connect() //nolint943 // wait for server to receive message944 select {945 case <-waitEnd:946 case <-time.After(time.Second * 3):947 t.Fatal("no privmsg received")948 }949 assertStringsEqual(t, "PRIVMSG #gempir :/followersoff", received)950}951func TestCanRunBanUser(t *testing.T) {952 t.Parallel()953 const testuser = "testuser123"954 const testreason = "Test Ban"955 waitEnd := make(chan struct{})956 var received string957 host := startServer(t, nothingOnConnect, func(message string) {958 if strings.HasPrefix(message, "PRIVMSG") {959 received = message960 close(waitEnd)961 }962 })963 client := newTestClient(host)964 client.OnConnect(func() {965 client.Ban("gempiR", testuser, testreason)966 })967 go client.Connect() //nolint968 // wait for server to receive message969 select {970 case <-waitEnd:971 case <-time.After(time.Second * 3):972 t.Fatal("no ban received for "+testuser+" with reason: "+testreason, received)973 }974}975func TestCanRunDeleteMessage(t *testing.T) {976 t.Parallel()977 const testmsgid = "c9c2d88c-74a5-4c12-8342-2e515357738c"978 waitEnd := make(chan struct{})979 var received string980 host := startServer(t, nothingOnConnect, func(message string) {981 if strings.HasPrefix(message, "PRIVMSG") {982 received = message983 close(waitEnd)984 }985 })986 client := newTestClient(host)987 client.OnConnect(func() {988 client.DeleteMessage("gempiR", testmsgid)989 })990 go client.Connect() //nolint991 // wait for server to receive message992 select {993 case <-waitEnd:994 case <-time.After(time.Second * 3):995 t.Fatal("no privmsg received")996 }997 assertStringsEqual(t, "PRIVMSG #gempir :/delete "+testmsgid, received)998}999func TestCanJoinChannelAfterConnection(t *testing.T) {1000 t.Parallel()1001 waitEnd := make(chan struct{})1002 var receivedMsg string1003 host := startServer(t, nothingOnConnect, func(message string) {1004 if strings.HasPrefix(message, "JOIN") {1005 receivedMsg = message1006 close(waitEnd)1007 }1008 })1009 client := newTestClient(host)1010 go client.Connect()1011 // wait for the connection to go active1012 for !client.connActive.get() {1013 time.Sleep(time.Millisecond * 2)1014 }1015 client.Join("gempir")1016 // wait for server to receive message1017 select {1018 case <-waitEnd:1019 case <-time.After(time.Second * 3):1020 t.Fatal("no join message received")1021 }1022 assertStringsEqual(t, "JOIN #gempir", receivedMsg)1023}1024func TestCanRespectDefaultJoinRateLimits(t *testing.T) {1025 t.Parallel()1026 waitEnd := make(chan struct{})1027 var joinMessages []timedTestMessage1028 targetJoinCount := 251029 host := startServer(t, nothingOnConnect, func(message string) {1030 if strings.HasPrefix(message, "JOIN ") {1031 joinMessages = append(joinMessages, timedTestMessage{message, time.Now()})1032 if len(joinMessages) == targetJoinCount {1033 close(waitEnd)1034 }1035 }1036 })1037 client := newTestClient(host)1038 client.PongTimeout = time.Second * 301039 client.SetJoinRateLimiter(CreateDefaultRateLimiter())1040 go client.Connect() //nolint1041 // wait for the connection to go active1042 for !client.connActive.get() {1043 time.Sleep(time.Millisecond * 2)1044 }1045 // send enough messages to ensure we hit the rate limit1046 for i := 1; i <= targetJoinCount; i++ {1047 client.Join(fmt.Sprintf("gempir%d", i))1048 }1049 // wait for server to receive message1050 select {1051 case <-waitEnd:1052 case <-time.After(time.Second * 30):1053 t.Fatal("didn't receive all messages in time")1054 }1055 assertJoinRateLimitRespected(t, client.joinRateLimiter.GetLimit(), joinMessages)1056}1057func TestCanRespectBulkDefaultJoinRateLimits(t *testing.T) {1058 t.Parallel()1059 waitEnd := make(chan struct{})1060 var joinMessages []timedTestMessage1061 targetJoinCount := 501062 host := startServer(t, nothingOnConnect, func(message string) {1063 if strings.HasPrefix(message, "JOIN ") {1064 splits := strings.Split(message, ",")1065 for _, split := range splits {1066 joinMessages = append(joinMessages, timedTestMessage{split, time.Now()})1067 }1068 if len(joinMessages) == targetJoinCount {1069 close(waitEnd)1070 }1071 }1072 })1073 client := newTestClient(host)1074 client.PongTimeout = time.Second * 601075 client.SetJoinRateLimiter(CreateDefaultRateLimiter())1076 go client.Connect() //nolint1077 // wait for the connection to go active1078 for !client.connActive.get() {1079 time.Sleep(time.Millisecond * 2)1080 }1081 perBulk := 251082 // send enough messages to ensure we hit the rate limit1083 for i := 1; i <= targetJoinCount; {1084 channels := []string{}1085 for j := i; j < i+perBulk; j++ {1086 channels = append(channels, fmt.Sprintf("gempir%d", j))1087 }1088 client.Join(channels...)1089 i += perBulk1090 }1091 // wait for server to receive message1092 select {1093 case <-waitEnd:1094 case <-time.After(time.Second * 60):1095 t.Fatal("didn't receive all messages in time")1096 }1097 assertJoinRateLimitRespected(t, client.joinRateLimiter.GetLimit(), joinMessages)1098}1099func TestCanRespectVerifiedJoinRateLimits(t *testing.T) {1100 t.Parallel()1101 waitEnd := make(chan struct{})1102 var joinMessages []timedTestMessage1103 targetJoinCount := 30001104 host := startServer(t, nothingOnConnect, func(message string) {1105 if strings.HasPrefix(message, "JOIN ") {1106 joinMessages = append(joinMessages, timedTestMessage{message, time.Now()})1107 if len(joinMessages) == targetJoinCount {1108 close(waitEnd)1109 }1110 }1111 })1112 client := newTestClient(host)1113 client.PongTimeout = time.Second * 301114 client.SetJoinRateLimiter(CreateVerifiedRateLimiter())1115 go client.Connect() //nolint1116 // wait for the connection to go active1117 for !client.connActive.get() {1118 time.Sleep(time.Millisecond * 2)1119 }1120 // send enough messages to ensure we hit the rate limit1121 for i := 1; i <= targetJoinCount; i++ {1122 client.Join(fmt.Sprintf("gempir%d", i))1123 }1124 // wait for server to receive message1125 select {1126 case <-waitEnd:1127 case <-time.After(time.Second * 30):1128 t.Fatal("didn't receive all messages in time")1129 }1130 assertJoinRateLimitRespected(t, client.joinRateLimiter.GetLimit(), joinMessages)1131}1132func TestCanIgnoreJoinRateLimits(t *testing.T) {1133 t.Parallel()1134 waitEnd := make(chan struct{})1135 var messages []timedTestMessage1136 targetJoinCount := 3000 // this breaks when above 700, why? the fuck?1137 host := startServer(t, nothingOnConnect, func(message string) {1138 if strings.HasPrefix(message, "JOIN ") {1139 messages = append(messages, timedTestMessage{message, time.Now()})1140 if len(messages) == targetJoinCount {1141 close(waitEnd)1142 }1143 }1144 })1145 client := newTestClient(host)1146 client.PongTimeout = time.Second * 301147 client.SetJoinRateLimiter(CreateUnlimitedRateLimiter())1148 go client.Connect() //nolint1149 // wait for the connection to go active1150 for !client.connActive.get() {1151 time.Sleep(time.Millisecond * 2)1152 }1153 // send enough messages to ensure we hit the rate limit1154 for i := 1; i <= targetJoinCount; i++ {1155 client.Join(fmt.Sprintf("gempir%d", i))1156 }1157 // wait for server to receive message1158 select {1159 case <-waitEnd:1160 case <-time.After(time.Second * 10):1161 t.Fatal("didn't receive all messages in time")1162 }1163 lastMessageTime := messages[len(messages)-1].time1164 firstMessageTime := messages[0].time1165 assertTrue(t, lastMessageTime.Sub(firstMessageTime).Seconds() <= 10, fmt.Sprintf("join ratelimit not skipped last message time: %s, first message time: %s", lastMessageTime, firstMessageTime))1166}1167func TestCanDepartChannel(t *testing.T) {1168 t.Parallel()1169 waitEnd := make(chan struct{})1170 var receivedMsg string1171 host := startServer(t, nothingOnConnect, func(message string) {1172 if strings.HasPrefix(message, "PART") {1173 receivedMsg = message1174 close(waitEnd)1175 }1176 })1177 client := newTestClient(host)1178 go client.Connect()1179 // wait for the connection to go active1180 for !client.connActive.get() {1181 time.Sleep(time.Millisecond * 2)1182 }1183 client.Depart("gempir")1184 // wait for server to receive message1185 select {1186 case <-waitEnd:1187 case <-time.After(time.Second * 3):1188 t.Fatal("no depart message received")1189 }1190 assertStringsEqual(t, "PART #gempir", receivedMsg)1191}1192func TestCanGetUserlist(t *testing.T) {1193 t.Parallel()1194 expectedNames := []string{"username1", "username2"}1195 testMessages := []string{1196 `:justinfan123123.tmi.twitch.tv 353 justinfan123123 = #channel123 :username1 username2`,1197 `@badges=subscriber/6,premium/1;color=#FF0000;display-name=Redflamingo13;emotes=;id=2a31a9df-d6ff-4840-b211-a2547c7e656e;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1490382457309;turbo=0;user-id=78424343;user-type= :redflamingo13!redflamingo13@redflamingo13.tmi.twitch.tv PRIVMSG #anythingbutchannel123 :ok go now`,1198 }1199 waitEnd := make(chan struct{})1200 host := startServer(t, postMessagesOnConnect(testMessages), nothingOnMessage)1201 client := newTestClient(host)1202 var received NamesMessage1203 client.OnNamesMessage(func(message NamesMessage) {1204 received = message1205 })1206 client.Join("channel123")1207 client.OnPrivateMessage(func(message PrivateMessage) {1208 if message.Message == "ok go now" {1209 // test a valid channel1210 got, err := client.Userlist("channel123")1211 if err != nil {1212 t.Fatal("error not nil for client.Userlist")1213 }1214 sort.Strings(got)1215 assertStringSlicesEqual(t, expectedNames, got)1216 // test an unknown channel1217 got, err = client.Userlist("random_channel123")1218 if err == nil || got != nil {1219 t.Fatal("error expected on unknown channel for client.Userlist")1220 }1221 close(waitEnd)1222 }1223 })1224 go client.Connect()1225 // wait for the connection to go active1226 for !client.connActive.get() {1227 time.Sleep(time.Millisecond * 5)1228 }1229 // wait for server to receive message1230 select {1231 case <-waitEnd:1232 case <-time.After(time.Second * 3):1233 t.Fatal("no userlist received")1234 }1235 assertStringsEqual(t, "channel123", received.Channel)1236 assertStringSlicesEqual(t, expectedNames, received.Users)1237 assertMessageTypesEqual(t, NAMES, received.GetType())1238}1239func TestDepartNegatesJoinIfNotConnected(t *testing.T) {1240 t.Parallel()1241 waitErrorPart := make(chan struct{})1242 waitErrorJoin := make(chan struct{})1243 host := startServer(t, nothingOnConnect, func(message string) {1244 if strings.HasPrefix(message, "PART") {1245 close(waitErrorPart)1246 }1247 if strings.HasPrefix(message, "JOIN") {1248 close(waitErrorJoin)1249 }1250 })1251 client := newTestClient(host)1252 client.Join("gempir")1253 client.Depart("gempir")1254 go client.Connect()1255 // wait for the connection to go active1256 for !client.connActive.get() {1257 time.Sleep(time.Millisecond * 2)1258 }1259 // wait for server to receive message1260 select {1261 case <-waitErrorPart:1262 t.Fatal("erroneously received part message")1263 case <-waitErrorJoin:1264 t.Fatal("erroneously received join message")1265 case <-time.After(time.Millisecond * 100):1266 }1267}1268func TestCanRespondToPING1(t *testing.T) {1269 t.Parallel()1270 testMessage := `PING`1271 expectedMessage := `PONG`1272 waitEnd := make(chan struct{})1273 host := startServer(t, postMessageOnConnect(testMessage), func(message string) {1274 // On message received1275 if message == expectedMessage {1276 close(waitEnd)1277 }1278 })1279 client := newTestClient(host)1280 go client.Connect()1281 // wait for server to receive message1282 select {1283 case <-waitEnd:1284 case <-time.After(time.Second * 3):1285 t.Fatal("no pong message received")1286 }1287}1288func TestCanRespondToPING2(t *testing.T) {1289 t.Parallel()1290 testMessage := `:tmi.twitch.tv PING`1291 expectedMessage := `PONG`1292 waitEnd := make(chan struct{})1293 host := startServer(t, postMessageOnConnect(testMessage), func(message string) {1294 // On message received1295 if message == expectedMessage {1296 close(waitEnd)1297 }1298 })1299 client := newTestClient(host)1300 go client.Connect()1301 // wait for server to receive message1302 select {1303 case <-waitEnd:1304 case <-time.After(time.Second * 3):1305 t.Fatal("no pong message received")1306 }1307}1308func TestCanAttachToPingMessageCallback(t *testing.T) {1309 t.Parallel()1310 testMessage := `:tmi.twitch.tv PING`1311 wait := make(chan struct{})1312 host := startServer(t, postMessageOnConnect(testMessage), nothingOnMessage)1313 client := newTestClient(host)1314 client.OnPingMessage(func(msg PingMessage) {1315 close(wait)1316 })1317 go client.Connect()1318 // wait for server to receive message1319 select {1320 case <-wait:1321 case <-time.After(time.Second * 3):1322 t.Fatal("no ping message received")1323 }1324}1325func TestCanPong(t *testing.T) {1326 t.Parallel()1327 testMessage := `PING :hello`1328 expectedMessage := `PONG :hello`1329 waitEnd := make(chan struct{})1330 host := startServer(t, postMessageOnConnect(testMessage), func(message string) {1331 // On message received1332 if message == expectedMessage {1333 close(waitEnd)1334 }1335 })1336 client := newTestClient(host)1337 go client.Connect()1338 // wait for server to receive message1339 select {1340 case <-waitEnd:1341 case <-time.After(time.Second * 3):1342 t.Fatal("no pong message received")1343 }1344}1345func TestCanNotDialInvalidAddress(t *testing.T) {1346 t.Parallel()1347 client := NewClient("justinfan123123", "oauth:123123132")1348 client.IrcAddress = "127.0.0.1:123123123123"1349 err := client.Connect()1350 if !strings.Contains(err.Error(), "invalid port") {1351 t.Fatal("wrong Connect() error: " + err.Error())1352 }1353}1354func TestCanNotUseImproperlyFormattedOauthPENIS(t *testing.T) {1355 t.Parallel()1356 host := startServer(t, nothingOnConnect, nothingOnMessage)1357 client := NewClient("justinfan123123", "imrpproperlyformattedoauth")1358 client.IrcAddress = host1359 err := client.Connect()1360 if err != ErrLoginAuthenticationFailed {1361 t.Fatal("wrong Connect() error: " + err.Error())1362 }1363}1364func TestCanNotUseWrongOauthPENIS123(t *testing.T) {1365 t.Parallel()1366 host := startServer(t, nothingOnConnect, nothingOnMessage)1367 client := NewClient("justinfan123123", "oauth:wrong")1368 client.IrcAddress = host1369 err := client.Connect()1370 if err != ErrLoginAuthenticationFailed {1371 t.Fatal("wrong Connect() error: " + err.Error())1372 }1373}1374func TestCanConnectToTwitch(t *testing.T) {1375 t.Parallel()1376 client := NewClient("justinfan123123", "oauth:123123132")1377 client.OnConnect(func() {1378 client.Disconnect()1379 })1380 err := client.Connect()1381 assertErrorsEqual(t, ErrClientDisconnected, err)1382}1383func TestCanConnectToTwitchWithoutTLS(t *testing.T) {1384 t.Parallel()1385 client := NewClient("justinfan123123", "oauth:123123132")1386 client.TLS = false1387 wait := make(chan struct{})1388 client.OnConnect(func() {1389 client.Disconnect()1390 })1391 go func() {1392 err := client.Connect()1393 assertErrorsEqual(t, ErrClientDisconnected, err)1394 close(wait)1395 }()1396 select {1397 case <-wait:1398 case <-time.After(time.Second * 3):1399 t.Fatal("Did not establish a connection")1400 }1401}1402func TestCanHandleInvalidNick(t *testing.T) {1403 t.Parallel()1404 client := NewClient("", "")1405 client.TLS = false1406 wait := make(chan struct{})1407 client.OnConnect(func() {1408 t.Fatal("A connection should not be able to be established with an invalid nick")1409 })1410 go func() {1411 err := client.Connect()1412 assertErrorsEqual(t, ErrLoginAuthenticationFailed, err)1413 close(wait)1414 }()1415 select {1416 case <-wait:1417 case <-time.After(time.Second * 3):1418 t.Fatal("Did not establish a connection")1419 }1420}1421func TestLocalSendingPingsReceivedPong(t *testing.T) {1422 t.Parallel()1423 const idlePingInterval = 300 * time.Millisecond1424 wait := make(chan bool)1425 var conn net.Conn1426 host := startServer(t, func(c net.Conn) {1427 conn = c1428 }, func(message string) {1429 if message == pingMessage {1430 // Send an emulated pong1431 fmt.Fprintf(conn, formatPong(strings.Split(message, " :")[1])+"\r\n")1432 wait <- true1433 }1434 })1435 client := newTestClient(host)1436 client.IdlePingInterval = idlePingInterval1437 go client.Connect()1438 select {1439 case <-wait:1440 case <-time.After(time.Second * 3):1441 t.Fatal("Did not establish a connection")1442 }1443 client.Disconnect()1444}1445func TestLocalCanReconnectAfterNoPongResponse(t *testing.T) {1446 t.Parallel()1447 const idlePingInterval = 300 * time.Millisecond1448 const pongTimeout = 300 * time.Millisecond1449 wait := make(chan bool)1450 var connCount int321451 host := startServerMultiConns(t, 3, func(conn net.Conn) {1452 atomic.AddInt32(&connCount, 1)1453 wait <- true1454 }, nothingOnMessage)1455 client := newTestClient(host)1456 client.IdlePingInterval = idlePingInterval1457 client.PongTimeout = pongTimeout1458 go client.Connect()1459 select {1460 case <-wait:1461 case <-time.After(time.Second * 3):1462 t.Fatal("Did not establish a connection")1463 }1464 assertInt32sEqual(t, 1, atomic.LoadInt32(&connCount))1465 // Wait for reconnect based on lack of ping response1466 select {1467 case <-wait:1468 case <-time.After(time.Second * 3):1469 t.Fatal("Did not establish a connection")1470 }1471 assertInt32sEqual(t, 2, atomic.LoadInt32(&connCount))1472 // Wait for another reconnect based on lack of ping response1473 select {1474 case <-wait:1475 case <-time.After(time.Second * 3):1476 t.Fatal("Did not establish a connection")1477 }1478 assertInt32sEqual(t, 3, atomic.LoadInt32(&connCount))1479}1480func TestLocalSendingPingsReceivedPongAlsoDisconnect(t *testing.T) {1481 t.Parallel()1482 const idlePingInterval = 300 * time.Millisecond1483 wait := make(chan bool)1484 var conn net.Conn1485 host := startServer(t, func(c net.Conn) {1486 conn = c1487 }, func(message string) {1488 if message == pingMessage {1489 // Send an emulated pong1490 fmt.Fprintf(conn, formatPong(strings.Split(message, " :")[1])+"\r\n")1491 conn.Close()1492 wait <- true1493 }1494 })1495 client := newTestClient(host)1496 client.IdlePingInterval = idlePingInterval1497 go client.Connect()1498 select {1499 case <-wait:1500 case <-time.After(time.Second * 3):1501 t.Fatal("Did not establish a connection")1502 }1503 client.Disconnect()1504}1505func TestPinger(t *testing.T) {1506 t.Parallel()1507 const idlePingInterval = 300 * time.Millisecond1508 wait := make(chan bool)1509 var conn net.Conn1510 var pingpongMutex sync.Mutex1511 var pingsSent int1512 var pongsReceived int1513 host := startServer(t, func(c net.Conn) {1514 conn = c1515 }, func(message string) {1516 if message == pingMessage {1517 // Send an emulated pong1518 fmt.Fprintf(conn, formatPong(strings.Split(message, " :")[1])+"\r\n")1519 wait <- true1520 }1521 })1522 client := newTestClient(host)1523 client.IdlePingInterval = idlePingInterval1524 client.OnPingSent(func() {1525 pingpongMutex.Lock()1526 pingsSent++1527 pingpongMutex.Unlock()1528 })1529 client.OnPongMessage(func(msg PongMessage) {1530 pingpongMutex.Lock()1531 pongsReceived++1532 pingpongMutex.Unlock()1533 })1534 go client.Connect()1535 select {1536 case <-wait:1537 case <-time.After(time.Second * 3):1538 t.Fatal("Did not establish a connection")1539 }1540 wait = make(chan bool)1541 // Ping has been sent by server1542 go func() {1543 for {1544 <-time.After(5 * time.Millisecond)1545 pingpongMutex.Lock()1546 if pingsSent == pongsReceived {1547 wait <- pingsSent == 11548 pingpongMutex.Unlock()1549 return1550 }1551 pingpongMutex.Unlock()1552 }1553 }()1554 select {1555 case res := <-wait:1556 assertTrue(t, res, "did not send a ping??????")1557 case <-time.After(time.Second * 3):1558 t.Fatal("Did not receive a pong")1559 }1560 client.Disconnect()1561}1562func TestCanAttachToPongMessageCallback(t *testing.T) {1563 t.Parallel()1564 pongMessage := `:tmi.twitch.tv PONG tmi.twitch.tv :go-twitch-irc`1565 wait := make(chan struct{})1566 host := startServer(t, postMessageOnConnect(pongMessage), nothingOnMessage)1567 client := newTestClient(host)1568 var received string1569 client.OnPongMessage(func(msg PongMessage) {1570 received = msg.Message1571 close(wait)1572 })1573 go client.Connect()1574 select {1575 case <-wait:1576 case <-time.After(time.Second * 3):1577 t.Fatal("Did not establish a connection")1578 }1579 client.Disconnect()1580 assertStringsEqual(t, "go-twitch-irc", received)1581}1582type createJoinMessageResult struct {1583 messages []string1584 joined []string1585}1586func TestCreateJoinMessagesCreatesMessages(t *testing.T) {1587 cases := []struct {1588 channels []string1589 expected createJoinMessageResult1590 }{1591 {1592 channels: nil,1593 expected: createJoinMessageResult{1594 messages: []string{},1595 joined: []string{},1596 },1597 },1598 {1599 channels: []string{},1600 expected: createJoinMessageResult{1601 messages: []string{},1602 joined: []string{},1603 },1604 },1605 {1606 channels: []string{"pajlada", "forsen"},1607 expected: createJoinMessageResult{1608 messages: []string{"JOIN #pajlada,#forsen"},1609 joined: []string{"pajlada", "forsen"},1610 },1611 },1612 {1613 channels: []string{1614 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1615 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1616 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1617 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1618 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1619 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1620 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1621 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1622 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1623 "aaaaaaaaa",1624 },1625 expected: createJoinMessageResult{1626 messages: []string{"JOIN #aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaa"},1627 joined: []string{1628 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1629 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1630 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1631 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1632 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1633 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1634 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1635 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1636 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1637 "aaaaaaaaa",1638 },1639 },1640 },1641 {1642 channels: []string{1643 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1644 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1645 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1646 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1647 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1648 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1649 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1650 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1651 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1652 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",1653 },1654 expected: createJoinMessageResult{1655 messages: []string{1656 "JOIN #aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1657 "JOIN #bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",1658 },1659 joined: []string{1660 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1661 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1662 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1663 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1664 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1665 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1666 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1667 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1668 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",1669 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",1670 },1671 },1672 },1673 }1674 client := NewAnonymousClient()1675 client.channels = make(map[string]bool)1676 for _, test := range cases {1677 messages, joined := client.createJoinMessages(test.channels...)1678 assertStringSlicesEqual(t, test.expected.messages, messages)1679 assertStringSlicesEqual(t, test.expected.joined, joined)1680 }1681}1682func TestCreateJoinMessageReturnsLowercase(t *testing.T) {1683 channels := []string{"PAJLADA", "FORSEN"}1684 joined := make(map[string]bool)1685 expected := []string{"JOIN #pajlada,#forsen"}1686 expectedJoined := []string{"pajlada", "forsen"}1687 client := NewAnonymousClient()1688 client.channels = joined1689 actual, actualJoined := client.createJoinMessages(channels...)1690 assertStringSlicesEqual(t, expected, actual)1691 assertStringSlicesEqual(t, expectedJoined, actualJoined)1692}1693func TestCreateJoinMessageSkipsJoinedChannels(t *testing.T) {1694 channels := []string{"pajlada", "forsen", "nymn"}1695 joined := map[string]bool{1696 "pajlada": true,1697 "forsen": false,1698 }1699 expected := []string{"forsen", "nymn"}1700 client := NewAnonymousClient()1701 client.channels = joined1702 _, actual := client.createJoinMessages(channels...)1703 assertStringSlicesEqual(t, expected, actual)1704}1705func TestRejoinOnReconnect(t *testing.T) {1706 t.Parallel()1707 waitEnd := make(chan struct{})1708 var receivedMsg string1709 host := startServerMultiConns(t, 2, nothingOnConnect, func(message string) {1710 if strings.HasPrefix(message, "JOIN") {1711 receivedMsg = message1712 close(waitEnd)1713 }1714 })1715 client := newTestClient(host)1716 client.Join("gempiR")1717 clientDisconnected := connectAndEnsureGoodDisconnect(t, client)1718 // wait for server to receive message1719 select {1720 case <-waitEnd:1721 case <-time.After(time.Second * 3):1722 t.Fatal("no join message received")1723 }1724 // Server received first JOIN message1725 assertStringsEqual(t, "JOIN #gempir", receivedMsg)1726 receivedMsg = ""1727 // Manually disconnect1728 client.Disconnect()1729 <-clientDisconnected1730 waitEnd = make(chan struct{})1731 // Manually reconnect1732 go client.Connect()1733 select {1734 case <-waitEnd:1735 case <-time.After(time.Second * 3):1736 t.Fatal("no join message received 2")1737 }1738 // Server received second JOIN message1739 assertStringsEqual(t, "JOIN #gempir", receivedMsg)1740}1741func TestCapabilities(t *testing.T) {1742 type testTable struct {1743 name string1744 in []string1745 expected string1746 }1747 var tests = []testTable{1748 {1749 "Default Capabilities (not modifying)",1750 nil,1751 "CAP REQ :" + strings.Join([]string{TagsCapability, CommandsCapability}, " "),1752 },1753 {1754 "Modified Capabilities",1755 []string{CommandsCapability, MembershipCapability},1756 "CAP REQ :" + strings.Join([]string{CommandsCapability, MembershipCapability}, " "),1757 },1758 }1759 for _, tt := range tests {1760 func(tt testTable) {1761 t.Run(tt.name, func(t *testing.T) {1762 t.Parallel()1763 waitRecv := make(chan struct{})1764 waitServerConnect := make(chan struct{})1765 waitClientConnect := make(chan struct{})1766 var received string1767 server := startServer2(t, closeOnConnect(waitServerConnect), func(message string) {1768 if strings.HasPrefix(message, "CAP REQ") {1769 received = message1770 close(waitRecv)1771 }1772 })1773 client := newTestClient(server.host)1774 if tt.in != nil {1775 client.Capabilities = tt.in1776 }1777 client.OnConnect(clientCloseOnConnect(waitClientConnect))1778 clientDisconnected := connectAndEnsureGoodDisconnect(t, client)1779 // Wait for correct password to be read in server1780 if !waitWithTimeout(waitRecv) {1781 t.Fatal("no oauth read")1782 }1783 assertStringsEqual(t, tt.expected, received)1784 // Wait for server to acknowledge connection1785 if !waitWithTimeout(waitServerConnect) {1786 t.Fatal("no successful connection")1787 }1788 // Wait for client to acknowledge connection1789 if !waitWithTimeout(waitClientConnect) {1790 t.Fatal("no successful connection")1791 }1792 // Disconnect client from server1793 err := client.Disconnect()1794 if err != nil {1795 t.Error("Error during disconnect:" + err.Error())1796 }1797 // Wait for client to be fully disconnected1798 <-clientDisconnected1799 // Wait for server to be fully disconnected1800 <-server.stopped1801 })1802 }(tt)1803 }1804}1805func TestEmptyCapabilities(t *testing.T) {1806 type testTable struct {1807 name string1808 in []string1809 }1810 var tests = []testTable{1811 {"nil", nil},1812 {"Empty list", []string{}},1813 }1814 for _, tt := range tests {1815 func(tt testTable) {1816 t.Run(tt.name, func(t *testing.T) {1817 t.Parallel()1818 // we will modify the clients caps to only send commands and membership1819 receivedCapabilities := false1820 waitRecv := make(chan struct{})1821 waitServerConnect := make(chan struct{})1822 waitClientConnect := make(chan struct{})1823 server := startServer2(t, closeOnConnect(waitServerConnect), func(message string) {1824 if strings.HasPrefix(message, "CAP REQ") {1825 receivedCapabilities = true1826 } else if strings.HasPrefix(message, "PASS") {1827 close(waitRecv)1828 }1829 })1830 client := newTestClient(server.host)1831 client.Capabilities = tt.in1832 client.OnConnect(clientCloseOnConnect(waitClientConnect))1833 clientDisconnected := connectAndEnsureGoodDisconnect(t, client)1834 // Wait for correct password to be read in server1835 if !waitWithTimeout(waitRecv) {1836 t.Fatal("no oauth read")1837 }1838 assertFalse(t, receivedCapabilities, "We should NOT have received caps since we sent an empty list of caps")1839 // Wait for server to acknowledge connection1840 if !waitWithTimeout(waitServerConnect) {1841 t.Fatal("no successful connection")1842 }1843 // Wait for client to acknowledge connection1844 if !waitWithTimeout(waitClientConnect) {1845 t.Fatal("no successful connection")1846 }1847 // Disconnect client from server1848 err := client.Disconnect()1849 if err != nil {1850 t.Error("Error during disconnect:" + err.Error())1851 }1852 // Wait for client to be fully disconnected1853 <-clientDisconnected1854 // Wait for server to be fully disconnected1855 <-server.stopped1856 })1857 }(tt)1858 }1859}...
scanutils.go
Source:scanutils.go
...18 End int19}20type host struct {21 IPv4 string `json:"ipv4"`22 Hostname string `json:"hostname"`23 PrettyName string `json:"pretty_name"`24 OpenPorts []int `json:"open_ports"`25 mutex sync.Mutex26 lock *semaphore.Weighted27}28type CIDR struct {29 Range string `json:"range"`30 Hosts []*host `json:"hosts"`31}32// Helper function to validate IPv433func ValidIPv4(host string) bool {34 parts := strings.Split(host, ".")35 if len(parts) < 4 {36 return false37 }38 for _, x := range parts {39 if i, err := strconv.Atoi(x); err == nil {40 if i < 0 || i > 255 {41 return false42 }43 } else {44 return false45 }46 }47 return true48}49// Validates that a new host can be created based on hostName50func NewHost(hostName string) (*host, error) {51 mtx := sync.Mutex{}52 if ValidIPv4(hostName) {53 return &host{54 IPv4: hostName,55 PrettyName: hostName,56 mutex: mtx,57 lock: semaphore.NewWeighted(100), // yeah i hardcoded don't @me58 }, nil59 } else {60 // Try and lookup the hostname61 ips, err := net.LookupIP(hostName)62 if err != nil {63 return nil, err64 }65 hostStr := fmt.Sprintf("%s (%s)", ips[0].String(), hostName)66 return &host{67 IPv4: ips[0].String(),68 Hostname: hostName,69 PrettyName: hostStr,70 mutex: mtx,71 lock: semaphore.NewWeighted(100),72 }, nil73 }74}75func NewCIDR(cidrStr string) (*CIDR, error) {76 ip, ipnet, err := net.ParseCIDR(cidrStr)77 var hosts []*host78 // Maybe single IP given?79 if err != nil {80 hostInst, err := NewHost(cidrStr)81 // Failed to parse the single ip. Fail out.82 if err != nil {83 return nil, err84 }85 hosts = append(hosts, hostInst)86 } else {87 var ips []string88 for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {89 ips = append(ips, ip.String())90 }91 // remove network address and broadcast address92 for i := 1; i < len(ips)-1; i++ {93 hostInst, err := NewHost(ips[i])94 if err != nil {95 return nil, err96 }97 hosts = append(hosts, hostInst)98 }99 }100 return &CIDR{101 Range: cidrStr,102 Hosts: hosts,103 }, nil104}105// http://play.golang.org/p/m8TNTtygK0106func inc(ip net.IP) {107 for j := len(ip) - 1; j >= 0; j-- {108 ip[j]++109 if ip[j] > 0 {110 break111 }112 }113}114// Scan a single port!115//export116func (server *host) ScanPort(port int, timeout time.Duration) {117 target := fmt.Sprintf("%s:%d", server.IPv4, port)118 conn, err := net.DialTimeout("tcp", target, timeout)119 if conn != nil {120 conn.Close()121 }122 if err != nil {123 if strings.Contains(err.Error(), "too many open files") || strings.Contains(err.Error(), "temporarily unavailable") {124 time.Sleep(timeout)125 server.ScanPort(port, timeout)126 }127 return128 }129 server.mutex.Lock()130 server.OpenPorts = append(server.OpenPorts, port)131 server.mutex.Unlock()132}133// Scan a sequential range of ports134func (server *host) ScanPortRange(pr PortRange, timeout time.Duration) {135 wg := sync.WaitGroup{}136 for port := pr.Start; port <= pr.End; port++ {137 server.lock.Acquire(context.TODO(), 1)138 wg.Add(1)139 go func(port int) {140 defer server.lock.Release(1)141 defer wg.Done()142 server.ScanPort(port, timeout)143 }(port)144 }145 wg.Wait()146}147// Scan a smattering of ports based on the slice.148func (server *host) ScanPortRanges(portList []PortRange, waitTime time.Duration) {149 // maybe start threading scan here150 // lim := Ulimit() / 2151 for i := 0; i < len(portList); i++ {152 server.ScanPortRange(portList[i], waitTime)153 }154}155func (cidrRange *CIDR) ScanHosts(portList []PortRange, waitTime time.Duration, job *structs.Job) {156 wg := sync.WaitGroup{}157 for i := 0; i < len(cidrRange.Hosts); i++ {158 if *job.Stop > 0 {159 break160 }161 server := cidrRange.Hosts[i]162 wg.Add(1)163 go func(server *host, portList []PortRange, waitTime time.Duration) {164 defer wg.Done()165 server.ScanPortRanges(portList, waitTime)166 }(server, portList, waitTime)167 }168 wg.Wait()169}170func (server *host) FormatOpenPorts() string {171 if len(server.OpenPorts) == 0 {172 return ""173 }174 result := ""175 result += fmt.Sprintf("Scan results for %s:\n", server.PrettyName)176 totalWhiteSpace := 6177 for i := 0; i < len(server.OpenPorts); i++ {178 result += fmt.Sprintf("\t%d%sopen\n", server.OpenPorts[i], strings.Repeat(" ", totalWhiteSpace-len(strconv.Itoa(server.OpenPorts[i]))))179 }180 result += fmt.Sprint("\n")181 return result182}183func (server *host) GreppableString() string {184 if len(server.OpenPorts) == 0 {185 return ""186 }187 totalWhiteSpace := 45 // arbitrary amt188 padding := totalWhiteSpace - len(server.PrettyName)189 if padding < 1 {190 padding = 1191 }192 portString := "("193 for i := 0; i < len(server.OpenPorts); i++ {194 addStr := fmt.Sprintf("%d/open", server.OpenPorts[i])195 if i != (len(server.OpenPorts) - 1) {196 addStr += ", "197 }198 portString += addStr199 }200 portString += ")"201 line := fmt.Sprintf("%s%s%s", server.PrettyName, strings.Repeat(" ", padding), portString)202 return line203}204func (cidrRange *CIDR) FormatOpenPorts() string {205 results := ""206 for i := 0; i < len(cidrRange.Hosts); i++ {207 results += cidrRange.Hosts[i].FormatOpenPorts()208 }209 return results210}...
swsnmp.go
Source:swsnmp.go
1package main2import (3 "bufio"4 "flag"5 "fmt"6 "golang.org/x/gosnmp"7 "os"8 "regexp"9 "strings"10 "sync"11 "time"12)13const (14 sysDescr = "1.3.6.1.2.1.1.1.0"15 sysName = "1.3.6.1.2.1.1.5.0"16)17var swVendor = map[string]*regexp.Regexp{18 "H3C": regexp.MustCompile("H3C"),19 "HUAWEI": regexp.MustCompile("Huawei"),20 "NEXUS": regexp.MustCompile("Cisco NX"),21 "CISCO": regexp.MustCompile("Cisco"),22 "RUIJIE": regexp.MustCompile("Ruijie"),23 "JUNIPER": regexp.MustCompile("Juniper"),24}25func getVendor(host string, community string, port uint16) (version string) {26 conn := &gosnmp.GoSNMP{27 Target: host,28 Port: port,29 Community: community,30 Version: gosnmp.Version2c,31 Timeout: time.Duration(3) * time.Second,32 }33 err := conn.Connect()34 if err != nil {35 return "ConnectFailed"36 }37 defer conn.Conn.Close()38 _resp, err := conn.Get([]string{sysDescr})39 if err != nil {40 return "Unkown"41 }42 resp := _resp.Variables[0]43 switch resp.Type {44 case gosnmp.OctetString:45 sysdescr := string(resp.Value.([]byte))46 for k, v := range swVendor {47 if v.MatchString(sysdescr) {48 return k49 }50 }51 default:52 return "Unkown"53 }54 return "Unkown"55}56func getSysname(host string, community string, port uint16) (sysname string) {57 conn := &gosnmp.GoSNMP{58 Target: host,59 Port: port,60 Community: community,61 Version: gosnmp.Version2c,62 Timeout: time.Duration(3) * time.Second,63 }64 err := conn.Connect()65 if err != nil {66 return "ConnectFailed"67 }68 defer conn.Conn.Close()69 _resp, err := conn.Get([]string{sysName})70 if err != nil {71 return "Unkown"72 }73 resp := _resp.Variables[0]74 switch resp.Type {75 case gosnmp.OctetString:76 sysname = string(resp.Value.([]byte))77 return sysname78 default:79 return "Unkown"80 }81 return "Unkown"82}83type Args struct {84 hostfile string85 community string86 verdor bool87 sysname bool88 inorder bool89}90func readlines(filename string) ([]string, error) {91 file, err := os.Open(filename)92 if err != nil {93 return nil, err94 }95 reader := bufio.NewReader(file)96 var lines []string97 for {98 line, err := reader.ReadString('\n')99 if err != nil {100 break101 }102 lines = append(lines, strings.TrimSpace(line))103 }104 return lines, nil105}106var args = Args{}107func initflag() {108 flag.StringVar(&args.hostfile, "f", "", `Target hosts list file, one ip on a separate line, for example:109'10.10.10.10'110'12.12.12.12'.`)111 flag.StringVar(&args.community, "c", "360buy", `Snmp community.`)112 flag.BoolVar(&args.verdor, "vendor", false, "Retrieve switch's vendor.")113 flag.BoolVar(&args.sysname, "sysname", false, `Retrieve switch's sysname.`)114 flag.BoolVar(&args.inorder, "order", false, `Print tagert respone in same order with hostfile.`)115 flag.Parse()116}117func main() {118 initflag()119 if args.hostfile == "" {120 fmt.Println("Traget hostfile is expected but got none. See help docs.")121 os.Exit(0)122 }123 if args.verdor && args.sysname {124 fmt.Println("Only one type retrieve is spported.")125 os.Exit(0)126 }127 if !args.verdor && !args.sysname {128 fmt.Println("You should specify the retrieve type. See help docs.")129 os.Exit(0)130 }131 hosts, err := readlines(args.hostfile)132 if err != nil {133 fmt.Printf("Open file failed, %v\n", err)134 }135 maxThread := 500136 threadchan := make(chan struct{}, maxThread)137 wait := sync.WaitGroup{}138 if args.verdor {139 if args.inorder {140 //respchan := make(chan map[string]string, len(hosts))141 resps := make(map[string]string)142 for _, host := range hosts {143 wait.Add(1)144 resps[host] = ""145 go func(host string) {146 threadchan <- struct{}{}147 resps[host] = getVendor(host, args.community, 161)148 <-threadchan149 wait.Done()150 }(host)151 }152 wait.Wait()153 for k, v := range resps {154 fmt.Printf("%s\t%s\n", k, v)155 }156 } else {157 for _, host := range hosts {158 wait.Add(1)159 go func(host string) {160 threadchan <- struct{}{}161 fmt.Printf("%s\t%s\n", host, getVendor(host, args.community, 161))162 <-threadchan163 wait.Done()164 }(host)165 }166 wait.Wait()167 }168 }169 if args.sysname {170 if args.inorder {171 resps := make(map[string]string)172 for _, host := range hosts {173 wait.Add(1)174 resps[host] = ""175 go func(host string) {176 threadchan <- struct{}{}177 resps[host] = getSysname(host, args.community, 161)178 <-threadchan179 wait.Done()180 }(host)181 }182 wait.Wait()183 for k, v := range resps {184 fmt.Printf("%s\t%s\n", k, v)185 }186 } else {187 for _, host := range hosts {188 wait.Add(1)189 go func(host string) {190 threadchan <- struct{}{}191 fmt.Printf("%s\t%s\n", host, getSysname(host, args.community, 161))192 <-threadchan193 wait.Done()194 }(host)195 }196 wait.Wait()197 }198 }199}...
Host
Using AI Code Generation
1import (2func main() {3 c1 := make(chan string)4 c2 := make(chan string)5 go func() {6 time.Sleep(time.Second * 1)7 }()8 go func() {9 time.Sleep(time.Second * 2)10 }()11 for i := 0; i < 2; i++ {12 select {13 fmt.Println("received", msg1)14 fmt.Println("received", msg2)15 }16 }17}18import (19func main() {20 tick := time.Tick(time.Second)21 boom := time.After(time.Second * 5)22 for {23 select {24 fmt.Println("tick.")25 fmt.Println("BOOM!")26 fmt.Println(" .")27 time.Sleep(time.Sec
Host
Using AI Code Generation
1import (2func main() {3 c1 := make(chan string)4 c2 := make(chan string)5 go func() {6 time.Sleep(time.Second * 1)7 }()8 go func() {9 time.Sleep(time.Second * 2)10 }()11 for i := 0; i < 2; i++ {12 select {13 fmt.Println("received", msg1)14 fmt.Println("received", msg2)15 }16 }17}18select {19 statement(s);20 statement(s);21 statement(s);22}23import (24func main() {25 c1 := make(chan string, 1)26 c2 := make(chan string, 1)27 go func() {28 time.Sleep(time.Second * 1)29 }()30 go func() {31 time.Sleep(time.Second * 2)32 }()33 for i := 0; i < 2; i++ {34 select {35 fmt.Println("received", msg1)36 fmt.Println("received", msg2)37 fmt.Println("no communication")38 }39 }40}41import (42func main() {
Host
Using AI Code Generation
1import (2func main() {3 waitGroup.Add(1)4 go func() {5 defer waitGroup.Done()6 fmt.Println("Hello")7 }()8 waitGroup.Wait()9 fmt.Println("Done")10}11import (12func main() {13 waitGroup.Add(1)14 go func() {15 defer waitGroup.Done()16 fmt.Println("Hello")17 }()18 waitGroup.Wait()19 fmt.Println("Done")20}21import (22func main() {23 waitGroup.Add(1)24 go func() {25 defer waitGroup.Done()26 fmt.Println("Hello")27 }()28 waitGroup.Wait()29 fmt.Println("Done")30}31import (32func main() {33 waitGroup.Add(1)34 go func() {35 defer waitGroup.Done()36 fmt.Println("Hello")37 }()38 waitGroup.Wait()39 fmt.Println("Done")40}41import (42func main() {43 waitGroup.Add(1)44 go func() {45 defer waitGroup.Done()46 fmt.Println("Hello")47 }()48 waitGroup.Wait()49 fmt.Println("Done")50}51import (52func main() {53 waitGroup.Add(1)54 go func() {55 defer waitGroup.Done()56 fmt.Println("Hello")57 }()58 waitGroup.Wait()59 fmt.Println("Done")60}61import (62func main() {
Host
Using AI Code Generation
1import (2func main() {3 waitGroup.Add(2)4 go func() {5 defer waitGroup.Done()6 time.Sleep(time.Second)7 fmt.Println("1st goroutine finished")8 }()9 go func() {10 defer waitGroup.Done()11 time.Sleep(2 * time.Second)12 fmt.Println("2nd goroutine finished")13 }()14 waitGroup.Wait()15 fmt.Println("All goroutines finished.")16}
Host
Using AI Code Generation
1import (2func main() {3 c := make(chan string)4 go func() {5 time.Sleep(5 * time.Second)6 }()7 fmt.Println("Waiting to receive")8 fmt.Println(s)9}10import (11func main() {12 wg.Add(2)13 go func() {14 fmt.Println("Hello")15 wg.Done()16 }()17 go func() {18 fmt.Println("World")19 wg.Done()20 }()21 wg.Wait()22}
Host
Using AI Code Generation
1import (2func main() {3 w.Add(1)4 go func() {5 fmt.Println("Hello")6 w.Done()7 }()8 w.Wait()9 fmt.Println("World")10}11import (12func main() {13 w.Add(2)14 go func() {15 fmt.Println("Hello")16 w.Done()17 }()18 go func() {19 fmt.Println("World")20 w.Done()21 }()22 w.Wait()23}24import (25func main() {26 w.Add(2)27 go func() {28 fmt.Println("Hello")29 w.Done()30 }()31 go func() {32 fmt.Println("World")33 w.Done()34 }()35 w.Wait()36}37import (38func main() {39 w.Add(2)40 go func() {41 fmt.Println("Hello")42 w.Done()43 }()44 go func() {45 fmt.Println("World")46 w.Done()47 }()48 w.Wait()49}50import (51func main() {52 w.Add(2)53 go func() {54 fmt.Println("Hello")55 w.Done()56 }()57 go func() {58 fmt.Println("World")59 w.Done()60 }()61 w.Wait()62}63import (64func main() {65 w.Add(2)66 go func() {67 fmt.Println("Hello")68 w.Done()69 }()70 go func() {71 fmt.Println("World")72 w.Done()73 }()74 w.Wait()75}
Host
Using AI Code Generation
1import (2func main() {3 fmt.Println("hello")4 wait := new(Wait)5 wait.Host()6}7import (8type Wait struct {9}10func (wait *Wait) Host() {11 fmt.Println("I am waiting")12 time.Sleep(3 * time.Second)13 fmt.Println("I am done waiting")14}15import (16func main() {17 fmt.Println("hello")18 wait := new(Wait)19 wait.Host()20}21import (22type Wait struct {23}24func (wait *Wait) Host() {25 fmt.Println("I am waiting")26 time.Sleep(3 * time.Second)27 fmt.Println("I am done waiting")28}
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!