Reform: bring back tests + fix server opts
This commit is contained in:
parent
58c1d27f2c
commit
87a2ab50c1
30
builtin.go
30
builtin.go
@ -7,6 +7,7 @@ package girc
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/araddon/dateparse"
|
||||
@ -123,7 +124,10 @@ func nickCollisionHandler(c *Client, e Event) {
|
||||
return
|
||||
}
|
||||
|
||||
c.Cmd.Nick(c.Config.HandleNickCollide(c.GetNick()))
|
||||
newNick := c.Config.HandleNickCollide(c.GetNick())
|
||||
if newNick != "" {
|
||||
c.Cmd.Nick(newNick)
|
||||
}
|
||||
}
|
||||
|
||||
// handlePING helps respond to ping requests from the server.
|
||||
@ -488,23 +492,25 @@ func handleISUPPORT(c *Client, e Event) {
|
||||
return
|
||||
}
|
||||
|
||||
c.state.Lock()
|
||||
// Skip the first parameter, as it's our nickname, and the last, as it's the doc.
|
||||
for i := 1; i < len(e.Params)-1; i++ {
|
||||
j := strings.IndexByte(e.Params[i], '=')
|
||||
for i := range e.Params {
|
||||
split := strings.Split(e.Params[i], "=")
|
||||
|
||||
if j < 1 || (j+1) == len(e.Params[i]) {
|
||||
opt := c.state.serverOptions[e.Params[i]]
|
||||
opt.Store("")
|
||||
if len(split) != 2 {
|
||||
c.state.serverOptions[e.Params[i]] = &atomic.Value{}
|
||||
c.state.serverOptions[e.Params[i]].Store("")
|
||||
continue
|
||||
}
|
||||
|
||||
name := e.Params[i][0:j]
|
||||
val := e.Params[i][j+1:]
|
||||
opt := c.state.serverOptions[name]
|
||||
opt.Store(val)
|
||||
if len(split[0]) < 1 || len(split[1]) < 1 {
|
||||
c.state.serverOptions[e.Params[i]] = &atomic.Value{}
|
||||
c.state.serverOptions[e.Params[i]].Store("")
|
||||
continue
|
||||
}
|
||||
|
||||
c.state.serverOptions[split[0]] = &atomic.Value{}
|
||||
c.state.serverOptions[split[0]].Store(split[1])
|
||||
}
|
||||
c.state.Unlock()
|
||||
|
||||
c.state.notify(c, UPDATE_GENERAL)
|
||||
}
|
||||
|
19
client.go
19
client.go
@ -19,7 +19,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -199,6 +198,9 @@ type Config struct {
|
||||
// an invalid nickname. For example, if "test" is already in use, or is
|
||||
// blocked by the network/a service, the client will try and use "test_",
|
||||
// then it will attempt "test__", "test___", and so on.
|
||||
//
|
||||
// If HandleNickCollide returns an empty string, the client will not
|
||||
// attempt to fix nickname collisions, and you must handle this yourself.
|
||||
HandleNickCollide func(oldNick string) (newNick string)
|
||||
}
|
||||
|
||||
@ -690,10 +692,9 @@ func (c *Client) IsInChannel(channel string) (in bool) {
|
||||
//
|
||||
func (c *Client) GetServerOption(key string) (result string, ok bool) {
|
||||
c.panicIfNotTracking()
|
||||
var opt atomic.Value
|
||||
|
||||
if opt, ok = c.state.serverOptions[key]; ok {
|
||||
result = opt.Load().(string)
|
||||
if _, ok := c.state.serverOptions[key]; ok {
|
||||
return c.state.serverOptions[key].Load().(string), ok
|
||||
}
|
||||
|
||||
return result, ok
|
||||
@ -725,8 +726,14 @@ func (c *Client) GetAllServerOption() (map[string]string, error) {
|
||||
func (c *Client) NetworkName() (name string) {
|
||||
c.panicIfNotTracking()
|
||||
|
||||
name, _ = c.GetServerOption("NETWORK")
|
||||
if len(name) < 1 {
|
||||
var ok bool
|
||||
|
||||
name, ok = c.GetServerOption("NETWORK")
|
||||
if !ok {
|
||||
return c.IRCd.Network
|
||||
}
|
||||
|
||||
if len(name) < 0 && len(c.IRCd.Network) > 1 {
|
||||
name = c.IRCd.Network
|
||||
}
|
||||
|
||||
|
200
client_test.go
Normal file
200
client_test.go
Normal file
@ -0,0 +1,200 @@
|
||||
// Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use
|
||||
// of this source code is governed by the MIT license that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
package girc
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestDisableTracking(t *testing.T) {
|
||||
client := New(Config{
|
||||
Server: "dummy.int",
|
||||
Port: 6667,
|
||||
Nick: "test",
|
||||
User: "test",
|
||||
Name: "Testing123",
|
||||
})
|
||||
|
||||
if len(client.Handlers.internal) < 1 {
|
||||
t.Fatal("Client.Handlers empty, though just initialized")
|
||||
}
|
||||
|
||||
client.DisableTracking()
|
||||
if _, ok := client.Handlers.internal[CAP]; ok {
|
||||
t.Fatal("Client.Handlers contains capability tracking handlers, though disabled")
|
||||
}
|
||||
|
||||
client.state.Lock()
|
||||
defer client.state.Unlock()
|
||||
|
||||
if client.state.channels != nil {
|
||||
t.Fatal("Client.DisableTracking() called but channel state still exists")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValid(t *testing.T) {
|
||||
conf := Config{
|
||||
Server: "irc.example.com", Port: 6667,
|
||||
Nick: "test", User: "test", Name: "Realname",
|
||||
}
|
||||
|
||||
var err error
|
||||
if err = conf.isValid(); err != nil {
|
||||
t.Fatalf("valid config failed Config.isValid() with: %s", err)
|
||||
}
|
||||
|
||||
conf.Server = ""
|
||||
if err = conf.isValid(); err == nil {
|
||||
t.Fatalf("invalid server passed validation check: %s", err)
|
||||
}
|
||||
conf.Server = "irc.example.com"
|
||||
|
||||
conf.Port = 100000
|
||||
if err = conf.isValid(); err == nil {
|
||||
t.Fatalf("invalid port passed validation check: %s", err)
|
||||
}
|
||||
conf.Port = 0 // Assumes "default".
|
||||
if err = conf.isValid(); err != nil {
|
||||
t.Fatalf("valid default failed validation check: %s", err)
|
||||
}
|
||||
if conf.Port != 6667 {
|
||||
t.Fatal("irc port was not defaulted to 6667")
|
||||
}
|
||||
|
||||
conf.Nick = "invalid nick"
|
||||
if err = conf.isValid(); err == nil {
|
||||
t.Fatalf("invalid nick passed validation check: %s", err)
|
||||
}
|
||||
conf.User = "test"
|
||||
|
||||
conf.User = "invalid user"
|
||||
if err = conf.isValid(); err == nil {
|
||||
t.Fatalf("invalid user passed validation check: %s", err)
|
||||
}
|
||||
conf.User = "test"
|
||||
}
|
||||
|
||||
func TestClientLifetime(t *testing.T) {
|
||||
client := New(Config{
|
||||
Server: "dummy.int",
|
||||
Port: 6667,
|
||||
Nick: "test",
|
||||
User: "test",
|
||||
Name: "Testing123",
|
||||
})
|
||||
|
||||
tm := client.Lifetime()
|
||||
|
||||
if tm < 0 || tm > 2*time.Second {
|
||||
t.Fatalf("Client.Lifetime() = %q, out of bounds", tm)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientUptime(t *testing.T) {
|
||||
c, conn, server := genMockConn()
|
||||
defer conn.Close()
|
||||
defer server.Close()
|
||||
go mockReadBuffer(conn)
|
||||
|
||||
done := make(chan struct{}, 1)
|
||||
c.Handlers.Add(INITIALIZED, func(c *Client, e Event) { close(done) })
|
||||
|
||||
go c.MockConnect(server)
|
||||
defer c.Close()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("Client.Uptime() timed out")
|
||||
}
|
||||
|
||||
uptime, err := c.Uptime()
|
||||
if err != nil {
|
||||
t.Fatalf("Client.Uptime() = %s, wanted time", err)
|
||||
}
|
||||
|
||||
since := time.Since(uptime)
|
||||
connsince, err := c.ConnSince()
|
||||
if err != nil {
|
||||
t.Fatalf("Client.ConnSince() = %s, wanted time", err)
|
||||
}
|
||||
|
||||
if since < 0 || since > 4*time.Second || *connsince < 0 || *connsince > 4*time.Second {
|
||||
t.Fatalf("Client.Uptime() = %q (%q, connsince: %q), out of bounds", uptime, since, connsince)
|
||||
}
|
||||
|
||||
// Verify the time we got from Client.Uptime() and Client.ConnSince() are
|
||||
// within reach of eachother.
|
||||
|
||||
if *connsince-since > 2*time.Second {
|
||||
t.Fatalf("Client.Uptime() (diff) = %q, Client.ConnSince() = %q, differ too much", since, connsince)
|
||||
}
|
||||
|
||||
if !c.IsConnected() {
|
||||
t.Fatal("Client.IsConnected() = false, though mock should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientGet(t *testing.T) {
|
||||
c, conn, server := genMockConn()
|
||||
defer conn.Close()
|
||||
defer server.Close()
|
||||
go mockReadBuffer(conn)
|
||||
|
||||
done := make(chan struct{}, 1)
|
||||
c.Handlers.Add(INITIALIZED, func(c *Client, e Event) { close(done) })
|
||||
|
||||
go c.MockConnect(server)
|
||||
defer c.Close()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("timed out during connect")
|
||||
}
|
||||
|
||||
if nick := c.GetNick(); nick != c.Config.Nick {
|
||||
t.Fatalf("Client.GetNick() = %q though should be %q", nick, c.Config.Nick)
|
||||
}
|
||||
|
||||
if user := c.GetIdent(); user != c.Config.User {
|
||||
t.Fatalf("Client.GetIdent() = %q though should be %q", user, c.Config.User)
|
||||
}
|
||||
|
||||
if !strings.Contains(c.String(), "connected:true") {
|
||||
t.Fatalf("Client.String() == %q, doesn't contain 'connected:true'", c.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientClose(t *testing.T) {
|
||||
c, conn, server := genMockConn()
|
||||
defer server.Close()
|
||||
defer conn.Close()
|
||||
go mockReadBuffer(conn)
|
||||
|
||||
errchan := make(chan error, 1)
|
||||
done := make(chan struct{}, 1)
|
||||
|
||||
c.Handlers.AddBg(CLOSED, func(c *Client, e Event) { close(done) })
|
||||
c.Handlers.AddBg(INITIALIZED, func(c *Client, e Event) { c.Close() })
|
||||
|
||||
go func() { errchan <- c.MockConnect(server) }()
|
||||
|
||||
defer c.Close()
|
||||
|
||||
select {
|
||||
case err := <-errchan:
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
t.Fatalf("connect returned with error when close was invoked: %s", err)
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("Client.Close() timed out")
|
||||
case <-done:
|
||||
}
|
||||
}
|
11
conn.go
11
conn.go
@ -145,6 +145,17 @@ type ErrParseEvent struct {
|
||||
|
||||
func (e ErrParseEvent) Error() string { return "unable to parse event: " + e.Line }
|
||||
|
||||
func (c *ircConn) encode(event *Event) error {
|
||||
if _, err := c.io.Write(event.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.io.Write(endline); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.io.Flush()
|
||||
}
|
||||
|
||||
func (c *ircConn) decode() (event *Event, err error) {
|
||||
line, err := c.io.ReadString(delim)
|
||||
if err != nil {
|
||||
|
120
conn_test.go
Normal file
120
conn_test.go
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use
|
||||
// of this source code is governed by the MIT license that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
package girc
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func mockBuffers() (in *bytes.Buffer, out *bytes.Buffer, irc *ircConn) {
|
||||
in = &bytes.Buffer{}
|
||||
out = &bytes.Buffer{}
|
||||
irc = &ircConn{
|
||||
io: bufio.NewReadWriter(bufio.NewReader(in), bufio.NewWriter(out)),
|
||||
connected: atomic.Value{},
|
||||
}
|
||||
|
||||
return in, out, irc
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
in, _, c := mockBuffers()
|
||||
|
||||
e := mockEvent()
|
||||
|
||||
in.Write(e.Bytes())
|
||||
in.Write(endline)
|
||||
|
||||
event, err := c.decode()
|
||||
if err != nil {
|
||||
t.Fatalf("received error during decode: %s", err)
|
||||
}
|
||||
|
||||
if event.String() != e.String() {
|
||||
t.Fatalf("event returned from decode not the same as mock event. want %#v, got %#v", e, event)
|
||||
}
|
||||
|
||||
// Test a failure.
|
||||
in.WriteString("::abcd\r\n")
|
||||
event, err = c.decode()
|
||||
if err == nil {
|
||||
t.Fatalf("should have failed to parse decoded event. got: %#v", event)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
_, out, c := mockBuffers()
|
||||
|
||||
e := mockEvent()
|
||||
|
||||
err := c.encode(e)
|
||||
if err != nil {
|
||||
t.Fatalf("received error during encode: %s", err)
|
||||
}
|
||||
|
||||
line, err := out.ReadString(delim)
|
||||
if err != nil {
|
||||
t.Fatalf("received error during check for encoded event: %s", err)
|
||||
}
|
||||
|
||||
want := e.String() + "\r\n"
|
||||
|
||||
if want != line {
|
||||
t.Fatalf("encoded line wanted: %q, got: %q", want, line)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func TestRate(t *testing.T) {
|
||||
_, _, c := mockBuffers()
|
||||
c.lastWrite.Store(time.Now())
|
||||
if delay := c.rate(100); delay > time.Second {
|
||||
t.Fatal("first instance of rate is > second")
|
||||
}
|
||||
|
||||
for i := 0; i < 500; i++ {
|
||||
c.rate(200)
|
||||
}
|
||||
|
||||
if delay := c.rate(200); delay > (3 * time.Second) {
|
||||
t.Fatal("rate delay too high")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func genMockConn() (client *Client, clientConn net.Conn, serverConn net.Conn) {
|
||||
client = New(Config{
|
||||
Server: "dummy.int",
|
||||
Port: 6667,
|
||||
Nick: "test",
|
||||
User: "test",
|
||||
Name: "Testing123",
|
||||
})
|
||||
|
||||
conn1, conn2 := net.Pipe()
|
||||
|
||||
return client, conn1, conn2
|
||||
}
|
||||
|
||||
func mockReadBuffer(conn net.Conn) {
|
||||
// Accept all outgoing writes from the client.
|
||||
b := bufio.NewReader(conn)
|
||||
for {
|
||||
conn.SetReadDeadline(time.Now().Add(10 * time.Second))
|
||||
_, err := b.ReadString(byte('\n'))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
4
state.go
4
state.go
@ -32,7 +32,7 @@ type state struct {
|
||||
// serverOptions are the standard capabilities and configurations
|
||||
// supported by the server at connection time. This also includes
|
||||
// RPL_ISUPPORT entries.
|
||||
serverOptions map[string]atomic.Value
|
||||
serverOptions map[string]*atomic.Value
|
||||
// motd is the servers message of the day.
|
||||
motd string
|
||||
|
||||
@ -52,7 +52,7 @@ func (s *state) reset(initial bool) {
|
||||
s.host.Store("")
|
||||
s.channels = make(map[string]*Channel)
|
||||
s.users = make(map[string]*User)
|
||||
s.serverOptions = make(map[string]atomic.Value)
|
||||
s.serverOptions = make(map[string]*atomic.Value)
|
||||
s.enabledCap = make(map[string]map[string]string)
|
||||
s.tmpCap = make(map[string]map[string]string)
|
||||
s.motd = ""
|
||||
|
261
state_test.go
Normal file
261
state_test.go
Normal file
@ -0,0 +1,261 @@
|
||||
// Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use
|
||||
// of this source code is governed by the MIT license that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
package girc
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func debounce(delay time.Duration, done chan bool, f func()) {
|
||||
var init bool
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
init = true
|
||||
case <-time.After(delay):
|
||||
if init {
|
||||
f()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mockConnStartState = `:dummy.int NOTICE * :*** Looking up your hostname...
|
||||
:dummy.int NOTICE * :*** Checking Ident
|
||||
:dummy.int NOTICE * :*** Found your hostname
|
||||
:dummy.int NOTICE * :*** No Ident response
|
||||
:dummy.int 001 fhjones :Welcome to the DUMMY Internet Relay Chat Network fhjones
|
||||
:dummy.int 005 fhjones NETWORK=DummyIRC NICKLEN=20 :are supported by this server
|
||||
:dummy.int 375 fhjones :- dummy.int Message of the Day -
|
||||
:dummy.int 372 fhjones :example motd
|
||||
:dummy.int 376 fhjones :End of /MOTD command.
|
||||
:fhjones!~user@local.int JOIN #channel * :realname
|
||||
:dummy.int 332 fhjones #channel :example topic
|
||||
:dummy.int 353 fhjones = #channel :fhjones!~user@local.int @nick2!nick2@other.int
|
||||
:dummy.int 366 fhjones #channel :End of /NAMES list.
|
||||
:dummy.int 354 fhjones 1 #channel ~user local.int fhjones 0 :realname
|
||||
:dummy.int 354 fhjones 1 #channel nick2 other.int nick2 nick2 :realname2
|
||||
:dummy.int 315 fhjones #channel :End of /WHO list.
|
||||
:fhjones!~user@local.int JOIN #channel2 * :realname
|
||||
:dummy.int 332 fhjones #channel2 :example topic
|
||||
:dummy.int 353 fhjones = #channel2 :fhjones!~user@local.int @nick2!nick2@other.int
|
||||
:dummy.int 366 fhjones #channel2 :End of /NAMES list.
|
||||
:dummy.int 354 fhjones 1 #channel2 ~user local.int fhjones 0 :realname
|
||||
:dummy.int 354 fhjones 1 #channel2 nick2 other.int nick2 nick2 :realname2
|
||||
:dummy.int 315 fhjones #channel2 :End of /WHO list.
|
||||
`
|
||||
|
||||
const mockConnEndState = `:nick2!nick2@other.int QUIT :example reason
|
||||
:fhjones!~user@local.int PART #channel2 :example reason
|
||||
:fhjones!~user@local.int NICK notjones
|
||||
`
|
||||
|
||||
func TestState(t *testing.T) {
|
||||
c, conn, server := genMockConn()
|
||||
defer c.Close()
|
||||
go mockReadBuffer(conn)
|
||||
|
||||
go func() {
|
||||
err := c.MockConnect(server)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
bounceStart := make(chan bool, 1)
|
||||
finishStart := make(chan bool, 1)
|
||||
go debounce(250*time.Millisecond, bounceStart, func() {
|
||||
if motd := c.ServerMOTD(); motd != "example motd" {
|
||||
t.Fatalf("Client.ServerMOTD() returned invalid MOTD: %q", motd)
|
||||
}
|
||||
|
||||
if network := c.NetworkName(); network != "DummyIRC" {
|
||||
t.Fatalf("Client.NetworkName() returned invalid network name: %q", network)
|
||||
}
|
||||
|
||||
if caseExample, ok := c.GetServerOption("NICKLEN"); !ok || caseExample != "20" {
|
||||
t.Fatalf("Client.GetServerOptions returned invalid ISUPPORT variable")
|
||||
}
|
||||
|
||||
users := c.UserList()
|
||||
channels := c.ChannelList()
|
||||
|
||||
if !reflect.DeepEqual(users, []string{"fhjones", "nick2"}) {
|
||||
// This could fail too, if sorting isn't occurring.
|
||||
t.Fatalf("got state users %#v, wanted: %#v", users, []string{"fhjones", "nick2"})
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(channels, []string{"#channel", "#channel2"}) {
|
||||
// This could fail too, if sorting isn't occurring.
|
||||
t.Fatalf("got state channels %#v, wanted: %#v", channels, []string{"#channel", "#channel2"})
|
||||
}
|
||||
|
||||
fullChannels := c.Channels()
|
||||
for i := 0; i < len(fullChannels); i++ {
|
||||
if fullChannels[i].Name != channels[i] {
|
||||
t.Fatalf("fullChannels name doesn't map to same name in ChannelsList: %q :: %#v", fullChannels[i].Name, channels)
|
||||
}
|
||||
}
|
||||
|
||||
fullUsers := c.Users()
|
||||
for i := 0; i < len(fullUsers); i++ {
|
||||
if fullUsers[i].Nick != users[i] {
|
||||
t.Fatalf("fullUsers nick doesn't map to same nick in UsersList: %q :: %#v", fullUsers[i].Nick, users)
|
||||
}
|
||||
}
|
||||
|
||||
ch := c.LookupChannel("#channel")
|
||||
if ch == nil {
|
||||
t.Fatal("Client.LookupChannel returned nil on existing channel")
|
||||
}
|
||||
|
||||
adm := ch.Admins(c)
|
||||
admList := []string{}
|
||||
for i := 0; i < len(adm); i++ {
|
||||
admList = append(admList, adm[i].Nick)
|
||||
}
|
||||
trusted := ch.Trusted(c)
|
||||
trustedList := []string{}
|
||||
for i := 0; i < len(trusted); i++ {
|
||||
trustedList = append(trustedList, trusted[i].Nick)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(admList, []string{"nick2"}) {
|
||||
t.Fatalf("got Channel.Admins() == %#v, wanted %#v", admList, []string{"nick2"})
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(trustedList, []string{"nick2"}) {
|
||||
t.Fatalf("got Channel.Trusted() == %#v, wanted %#v", trustedList, []string{"nick2"})
|
||||
}
|
||||
|
||||
if topic := ch.Topic; topic != "example topic" {
|
||||
t.Fatalf("Channel.Topic == %q, want \"example topic\"", topic)
|
||||
}
|
||||
|
||||
if in := ch.UserIn("fhjones"); !in {
|
||||
t.Fatalf("Channel.UserIn == %t, want %t", in, true)
|
||||
}
|
||||
|
||||
if users := ch.Users(c); len(users) != 2 {
|
||||
t.Fatalf("Channel.Users == %#v, wanted length of 2", users)
|
||||
}
|
||||
|
||||
if h := c.GetHost(); h != "local.int" {
|
||||
t.Fatalf("Client.GetHost() == %q, want local.int", h)
|
||||
}
|
||||
|
||||
if nick := c.GetNick(); nick != "fhjones" {
|
||||
t.Fatalf("Client.GetNick() == %q, want nick", nick)
|
||||
}
|
||||
|
||||
if ident := c.GetIdent(); ident != "~user" {
|
||||
t.Fatalf("Client.GetIdent() == %q, want ~user", ident)
|
||||
}
|
||||
|
||||
user := c.LookupUser("fhjones")
|
||||
if user == nil {
|
||||
t.Fatal("Client.LookupUser() returned nil on existing user")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(user.ChannelList, []string{"#channel", "#channel2"}) {
|
||||
t.Fatalf("User.ChannelList == %#v, wanted %#v", user.ChannelList, []string{"#channel", "#channel2"})
|
||||
}
|
||||
|
||||
if count := len(user.Channels(c)); count != 2 {
|
||||
t.Fatalf("len(User.Channels) == %d, want 2", count)
|
||||
}
|
||||
|
||||
if user.Nick != "fhjones" {
|
||||
t.Fatalf("User.Nick == %q, wanted \"nick\"", user.Nick)
|
||||
}
|
||||
|
||||
if user.Extras.Name != "realname" {
|
||||
t.Fatalf("User.Extras.Name == %q, wanted \"realname\"", user.Extras.Name)
|
||||
}
|
||||
|
||||
if user.Host != "local.int" {
|
||||
t.Fatalf("User.Host == %q, wanted \"local.int\"", user.Host)
|
||||
}
|
||||
|
||||
if user.Ident != "~user" {
|
||||
t.Fatalf("User.Ident == %q, wanted \"~user\"", user.Ident)
|
||||
}
|
||||
|
||||
if !user.InChannel("#channel2") {
|
||||
t.Fatal("User.InChannel() returned false for existing channel")
|
||||
}
|
||||
|
||||
finishStart <- true
|
||||
})
|
||||
|
||||
cuid := c.Handlers.AddBg(UPDATE_STATE, func(c *Client, e Event) {
|
||||
bounceStart <- true
|
||||
})
|
||||
|
||||
conn.SetDeadline(time.Now().Add(5 * time.Second))
|
||||
_, err := conn.Write([]byte(mockConnStartState))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-finishStart:
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("timed out while waiting for state update start")
|
||||
}
|
||||
c.Handlers.Remove(cuid)
|
||||
|
||||
bounceEnd := make(chan bool, 1)
|
||||
finishEnd := make(chan bool, 1)
|
||||
go debounce(250*time.Millisecond, bounceEnd, func() {
|
||||
if !reflect.DeepEqual(c.ChannelList(), []string{"#channel"}) {
|
||||
t.Fatalf("Client.ChannelList() == %#v, wanted %#v", c.ChannelList(), []string{"#channel"})
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(c.UserList(), []string{"notjones"}) {
|
||||
t.Fatalf("Client.UserList() == %#v, wanted %#v", c.UserList(), []string{"notjones"})
|
||||
}
|
||||
|
||||
user := c.LookupUser("notjones")
|
||||
if user == nil {
|
||||
t.Fatal("Client.LookupUser() returned nil for existing user")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(user.ChannelList, []string{"#channel"}) {
|
||||
t.Fatalf("user.ChannelList == %q, wanted %q", user.ChannelList, []string{"#channel"})
|
||||
}
|
||||
|
||||
channel := c.LookupChannel("#channel")
|
||||
if channel == nil {
|
||||
t.Fatal("Client.LookupChannel() returned nil for existing channel")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(channel.UserList, []string{"notjones"}) {
|
||||
t.Fatalf("channel.UserList == %q, wanted %q", channel.UserList, []string{"notjones"})
|
||||
}
|
||||
|
||||
finishEnd <- true
|
||||
})
|
||||
|
||||
cuid = c.Handlers.AddBg(UPDATE_STATE, func(c *Client, e Event) {
|
||||
bounceEnd <- true
|
||||
})
|
||||
|
||||
conn.SetDeadline(time.Now().Add(5 * time.Second))
|
||||
_, err = conn.Write([]byte(mockConnEndState))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-finishEnd:
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("timed out while waiting for state update end")
|
||||
}
|
||||
c.Handlers.Remove(cuid)
|
||||
}
|
Loading…
Reference in New Issue
Block a user