Browse Source

Added test setup for the irc client with a simple mock server.

master
Gisle Aune 6 years ago
parent
commit
bdacfcf111
  1. 107
      client_test.go
  2. 124
      internal/irctest/interaction.go
  3. 56
      internal/irctest/interaction_test.go

107
client_test.go

@ -0,0 +1,107 @@
package irc_test
import (
"context"
"testing"
"git.aiterp.net/gisle/irc"
"git.aiterp.net/gisle/irc/internal/irctest"
)
func TestClientInteraction(t *testing.T) {
client := irc.New(context.Background(), irc.Config{
Nick: "Test",
User: "Tester",
RealName: "...",
Alternatives: []string{"Test2", "Test3", "Test4"},
})
interaction := irctest.Interaction{
Strict: false,
Lines: []irctest.InteractionLine{
{Kind: 'C', Data: "CAP LS 302"},
{Kind: 'C', Data: "NICK Test"},
{Kind: 'C', Data: "USER Tester 8 * :..."},
{Kind: 'S', Data: ":testserver.example.com CAP * LS :multi-prefix userhost-in-names"},
{Kind: 'C', Data: "CAP REQ :multi-prefix userhost-in-names"},
{Kind: 'S', Data: ":testserver.example.com CAP * ACK :multi-prefix userhost-in-names"},
{Kind: 'C', Data: "CAP END"},
{Kind: 'S', Data: ":testserver.example.com 443 * Test :Nick is not available"},
{Kind: 'C', Data: "NICK Test2"},
{Kind: 'S', Data: ":testserver.example.com 443 * Test2 :Nick is not available"},
{Kind: 'C', Data: "NICK Test3"},
{Kind: 'S', Data: ":testserver.example.com 443 * Test3 :Nick is not available"},
{Kind: 'C', Data: "NICK Test4"},
{Kind: 'S', Data: ":testserver.example.com 443 * Test4 :Nick is not available"},
{Kind: 'C', Data: "NICK Test*"},
{Kind: 'S', Data: ":testserver.example.com 001 Test768 :Welcome to the TestServer Internet Relay Chat Network test"},
{Kind: 'C', Data: "WHO Test768*"},
{Kind: 'S', Data: ":testserver.example.com 002 Test768 :Your host is testserver.example.com[testserver.example.com/6667], running version charybdis-4-rc3"},
{Kind: 'S', Data: ":testserver.example.com 003 Test768 :This server was created Fri Nov 25 2016 at 17:28:20 CET"},
{Kind: 'S', Data: ":testserver.example.com 004 Test768 testserver.example.com charybdis-4-rc3 DQRSZagiloswxz CFILNPQbcefgijklmnopqrstvz bkloveqjfI"},
{Kind: 'S', Data: ":testserver.example.com 005 Test768 FNC SAFELIST ELIST=CTU MONITOR=100 WHOX ETRACE KNOCK CHANTYPES=#& EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLNPQcgimnprstz CHANLIMIT=#&:15 :are supported by this server"},
{Kind: 'S', Data: ":testserver.example.com 005 Test768 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=TestServer STATUSMSG=@+ CALLERID=g CASEMAPPING=rfc1459 NICKLEN=30 MAXNICKLEN=31 CHANNELLEN=50 TOPICLEN=390 DEAF=D :are supported by this server"},
{Kind: 'S', Data: ":testserver.example.com 005 Test768 TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,&acjmorsuxz| CLIENTVER=3.0 :are supported by this server"},
{Kind: 'S', Data: ":testserver.example.com 251 Test768 :There are 0 users and 2 invisible on 1 servers"},
{Kind: 'S', Data: ":testserver.example.com 254 Test768 1 :channels formed"},
{Kind: 'S', Data: ":testserver.example.com 255 Test768 :I have 2 clients and 0 servers"},
{Kind: 'S', Data: ":testserver.example.com 265 Test768 2 2 :Current local users 2, max 2"},
{Kind: 'S', Data: ":testserver.example.com 266 Test768 2 2 :Current global users 2, max 2"},
{Kind: 'S', Data: ":testserver.example.com 250 Test768 :Highest connection count: 2 (2 clients) (8 connections received)"},
{Kind: 'S', Data: ":testserver.example.com 352 Test768 * ~Tester testclient.example.com testserver.example.com Test768 H :0 ..."},
{Kind: 'S', Data: ":testserver.example.com 375 Test768 :- testserver.example.com Message of the Day - "},
{Kind: 'S', Data: ":testserver.example.com 372 Test768 :- This server is only for testing irce, not chatting. If you happen"},
{Kind: 'S', Data: ":testserver.example.com 372 Test768 :- to connect to it by accident, please disconnect immediately."},
{Kind: 'S', Data: ":testserver.example.com 372 Test768 :- "},
{Kind: 'S', Data: ":testserver.example.com 372 Test768 :- - #Test :: Test Channel"},
{Kind: 'S', Data: ":testserver.example.com 372 Test768 :- - #Test2 :: Other Test Channel"},
{Kind: 'S', Data: ":testserver.example.com 376 Test768 :End of /MOTD command."},
{Kind: 'S', Data: ":test MODE Test768 :+i"},
{Kind: 'C', Data: "JOIN #Test"},
},
}
addr, err := interaction.Listen()
if err != nil {
t.Fatal("Listen:", err)
}
irc.Handle(func(event *irc.Event, client *irc.Client) {
if event.Name() == "packet.376" {
client.SendQueued("JOIN #Test")
}
})
err = client.Connect(addr, false)
if err != nil {
t.Fatal("Connect:", err)
return
}
interaction.Wait()
fail := interaction.Failure
if fail != nil {
t.Error("Index:", fail.Index)
t.Error("NetErr:", fail.NetErr)
t.Error("Result:", fail.Result)
if fail.Index >= 0 {
t.Error("Line.Kind:", interaction.Lines[fail.Index].Kind)
t.Error("Line.Data:", interaction.Lines[fail.Index].Data)
}
}
if client.Nick() != "Test768" {
t.Errorf("Nick: %#+v != %#+v (Expectation)", client.Nick(), "Test768")
}
if client.User() != "~Tester" {
t.Errorf("User: %#+v != %#+v (Expectation)", client.User(), "~Tester")
}
if client.Host() != "testclient.example.com" {
t.Errorf("Host: %#+v != %#+v (Expectation)", client.Host(), "testclient.example.com")
}
for i, logLine := range interaction.Log {
t.Logf("Log[%d] = %#+v", i, logLine)
}
}

124
internal/irctest/interaction.go

@ -0,0 +1,124 @@
package irctest
import (
"bufio"
"net"
"strings"
"sync"
"time"
)
// An Interaction is a "simulated" server that will trigger the
// client.
type Interaction struct {
wg sync.WaitGroup
Strict bool
Lines []InteractionLine
Log []string
Failure *InteractionFailure
}
// Listen listens for a client in a separate goroutine.
func (interaction *Interaction) Listen() (addr string, err error) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return "", err
}
lines := make([]InteractionLine, len(interaction.Lines))
copy(lines, interaction.Lines)
go func() {
interaction.wg.Add(1)
defer interaction.wg.Done()
conn, err := listener.Accept()
if err != nil {
interaction.Failure = &InteractionFailure{
Index: -1, NetErr: err,
}
return
}
defer conn.Close()
reader := bufio.NewReader(conn)
for i := 0; i < len(lines); i++ {
line := lines[i]
switch line.Kind {
case 'S':
{
_, err := conn.Write(append([]byte(line.Data), '\r', '\n'))
if err != nil {
interaction.Failure = &InteractionFailure{
Index: i, NetErr: err,
}
return
}
}
case 'C':
{
conn.SetReadDeadline(time.Now().Add(time.Second))
input, err := reader.ReadString('\n')
if err != nil {
interaction.Failure = &InteractionFailure{
Index: i, NetErr: err,
}
return
}
input = strings.Replace(input, "\r", "", -1)
input = strings.Replace(input, "\n", "", 1)
match := line.Data
success := false
if strings.HasSuffix(match, "*") {
success = strings.HasPrefix(input, match[:len(match)-1])
} else {
success = match == input
}
interaction.Log = append(interaction.Log, input)
if !success {
if !interaction.Strict {
i--
continue
}
interaction.Failure = &InteractionFailure{
Index: i, Result: input,
}
return
}
}
}
}
}()
return listener.Addr().String(), nil
}
// Wait waits for the setup to be done. It's safe to check
// Failure after that.
func (interaction *Interaction) Wait() {
interaction.wg.Wait()
}
// InteractionFailure signifies a test failure.
type InteractionFailure struct {
Index int
Result string
NetErr error
}
// InteractionLine is part of an interaction, whether it is a line
// that is sent to a client or a line expected from a client.
type InteractionLine struct {
Kind byte
Data string
}

56
internal/irctest/interaction_test.go

@ -0,0 +1,56 @@
package irctest_test
import (
"net"
"testing"
"git.aiterp.net/gisle/irc/internal/irctest"
)
func TestInteraction(t *testing.T) {
interaction := irctest.Interaction{
Lines: []irctest.InteractionLine{
{Kind: 'C', Data: "FIRST MESSAGE"},
{Kind: 'S', Data: "SERVER MESSAGE"},
{Kind: 'C', Data: "SECOND MESSAGE"},
},
}
addr, err := interaction.Listen()
if err != nil {
t.Fatal("Listen:", err)
}
conn, err := net.Dial("tcp", addr)
if err != nil {
t.Fatal("Dial:", err)
}
_, err = conn.Write([]byte("FIRST MESSAGE\r\n"))
if err != nil {
t.Fatal("Write:", err)
}
buffer := make([]byte, 64)
n, err := conn.Read(buffer)
if err != nil {
t.Fatal("Read:", err)
}
if string(buffer[:n]) != "SERVER MESSAGE\r\n" {
t.Fatal("Read not correct:", string(buffer[:n]))
}
_, err = conn.Write([]byte("SECOND MESSAGE\r\n"))
if err != nil {
t.Fatal("Write 2:", err)
}
interaction.Wait()
if interaction.Failure != nil {
t.Error("Index:", interaction.Failure.Index)
t.Error("Result:", interaction.Failure.Result)
t.Error("NetErr:", interaction.Failure.NetErr)
t.FailNow()
}
}
Loading…
Cancel
Save