Test refactoring

This commit is contained in:
Mickael Remond 2017-10-05 20:11:28 -04:00
parent 2579c84481
commit 228cb14491
No known key found for this signature in database
GPG Key ID: E6F6045D79965AA3
2 changed files with 94 additions and 90 deletions

View File

@ -16,8 +16,8 @@ const (
func TestClient_Connect(t *testing.T) { func TestClient_Connect(t *testing.T) {
// Setup Mock server // Setup Mock server
mock := XMPPServerMock{} mock := ServerMock{}
mock.Start(t, handlerConnackSuccess) mock.Start(t, testXMPPAddress, handlerConnectSuccess)
// Test / Check result // Test / Check result
options := Options{Address: testXMPPAddress, Jid: "test@localhost", Password: "test"} options := Options{Address: testXMPPAddress, Jid: "test@localhost", Password: "test"}
@ -28,13 +28,10 @@ func TestClient_Connect(t *testing.T) {
t.Errorf("connect create XMPP client: %s", err) t.Errorf("connect create XMPP client: %s", err)
} }
var session *Session if _, err = client.Connect(); err != nil {
if session, err = client.Connect(); err != nil {
t.Errorf("XMPP connection failed: %s", err) t.Errorf("XMPP connection failed: %s", err)
} }
fmt.Println("Stream opened, we have streamID = ", session.StreamId)
mock.Stop() mock.Stop()
} }
@ -43,30 +40,20 @@ func TestClient_Connect(t *testing.T) {
const serverStreamOpen = "<?xml version='1.0'?><stream:stream to='%s' id='%s' xmlns='%s' xmlns:stream='%s' version='1.0'>" const serverStreamOpen = "<?xml version='1.0'?><stream:stream to='%s' id='%s' xmlns='%s' xmlns:stream='%s' version='1.0'>"
func handlerConnackSuccess(t *testing.T, c net.Conn) { func handlerConnectSuccess(t *testing.T, c net.Conn) {
decoder := xml.NewDecoder(c) decoder := xml.NewDecoder(c)
checkOpenStream(t, decoder) checkOpenStream(t, c, decoder)
if _, err := fmt.Fprintf(c, serverStreamOpen, "localhost", "streamid1", NSClient, NSStream); err != nil {
t.Errorf("cannot write server stream open: %s", err)
}
fmt.Println("Sent stream Open")
sendStreamFeatures(t, c, decoder) sendStreamFeatures(t, c, decoder)
fmt.Println("Sent stream feature")
readAuth(t, decoder) readAuth(t, decoder)
fmt.Fprintln(c, "<success xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"/>") fmt.Fprintln(c, "<success xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"/>")
checkOpenStream(t, decoder) checkOpenStream(t, c, decoder)
if _, err := fmt.Fprintf(c, serverStreamOpen, "localhost", "streamid1", NSClient, NSStream); err != nil {
t.Errorf("cannot write server stream open: %s", err)
}
sendBindFeature(t, c, decoder) sendBindFeature(t, c, decoder)
bind(t, c, decoder) bind(t, c, decoder)
session(t, c, decoder)
} }
func checkOpenStream(t *testing.T, decoder *xml.Decoder) { func checkOpenStream(t *testing.T, c net.Conn, decoder *xml.Decoder) {
for { for {
var token xml.Token var token xml.Token
token, err := decoder.Token() token, err := decoder.Token()
@ -79,11 +66,12 @@ func checkOpenStream(t *testing.T, decoder *xml.Decoder) {
case xml.StartElement: case xml.StartElement:
if elem.Name.Space != NSStream || elem.Name.Local != "stream" { if elem.Name.Space != NSStream || elem.Name.Local != "stream" {
err = errors.New("xmpp: expected <stream> but got <" + elem.Name.Local + "> in " + elem.Name.Space) err = errors.New("xmpp: expected <stream> but got <" + elem.Name.Local + "> in " + elem.Name.Space)
}
fmt.Printf("Received: %v\n", elem.Name.Local)
return return
case xml.ProcInst: }
fmt.Printf("Received: %v\n", elem.Inst) if _, err := fmt.Fprintf(c, serverStreamOpen, "localhost", "streamid1", NSClient, NSStream); err != nil {
t.Errorf("cannot write server stream open: %s", err)
}
return
} }
} }
} }
@ -112,7 +100,6 @@ func readAuth(t *testing.T, decoder *xml.Decoder) string {
nv = &auth{} nv = &auth{}
// Decode element into pointer storage // Decode element into pointer storage
if err = decoder.DecodeElement(nv, &se); err != nil { if err = decoder.DecodeElement(nv, &se); err != nil {
fmt.Println(err)
t.Errorf("cannot decode auth: %s", err) t.Errorf("cannot decode auth: %s", err)
return "" return ""
} }
@ -144,7 +131,6 @@ func bind(t *testing.T, c net.Conn, decoder *xml.Decoder) {
iq := &ClientIQ{} iq := &ClientIQ{}
// Decode element into pointer storage // Decode element into pointer storage
if err = decoder.DecodeElement(&iq, &se); err != nil { if err = decoder.DecodeElement(&iq, &se); err != nil {
fmt.Println(err)
t.Errorf("cannot decode bind iq: %s", err) t.Errorf("cannot decode bind iq: %s", err)
return return
} }
@ -160,68 +146,3 @@ func bind(t *testing.T, c net.Conn, decoder *xml.Decoder) {
</iq>` </iq>`
fmt.Fprintf(c, result, iq.Id, "test@localhost/test") // TODO use real JID fmt.Fprintf(c, result, iq.Id, "test@localhost/test") // TODO use real JID
} }
func session(t *testing.T, c net.Conn, decoder *xml.Decoder) {
}
type testHandler func(t *testing.T, conn net.Conn)
type XMPPServerMock struct {
t *testing.T
handler testHandler
listener net.Listener
connections []net.Conn
done chan struct{}
}
func (mock *XMPPServerMock) Start(t *testing.T, handler testHandler) {
mock.t = t
mock.handler = handler
if err := mock.init(); err != nil {
return
}
go mock.loop()
}
func (mock *XMPPServerMock) Stop() {
close(mock.done)
if mock.listener != nil {
mock.listener.Close()
}
// Close all existing connections
for _, c := range mock.connections {
c.Close()
}
}
func (mock *XMPPServerMock) init() error {
mock.done = make(chan struct{})
l, err := net.Listen("tcp", testXMPPAddress)
if err != nil {
mock.t.Errorf("TCPServerMock cannot listen on address: %q", testXMPPAddress)
return err
}
mock.listener = l
return nil
}
func (mock *XMPPServerMock) loop() {
listener := mock.listener
for {
conn, err := listener.Accept()
if err != nil {
select {
case <-mock.done:
return
default:
mock.t.Error("TCPServerMock accept error:", err.Error())
}
return
}
mock.connections = append(mock.connections, conn)
// TODO Create and pass a context to cancel the handler if they are still around = avoid possible leak on complex handlers
go mock.handler(mock.t, conn)
}
}

83
xmpp/tcp_server_mock.go Normal file
View File

@ -0,0 +1,83 @@
package xmpp
import (
"net"
"testing"
)
//=============================================================================
// TCP Server Mock
// ClientHandler is passed by the test client to provide custom behaviour to
// the TCP server mock. This allows customizing the server behaviour to allow
// testing clients under various scenarii.
type ClientHandler func(t *testing.T, conn net.Conn)
// ServerMock is a simple TCP server that can be use to mock basic server
// behaviour to test clients.
type ServerMock struct {
t *testing.T
handler ClientHandler
listener net.Listener
connections []net.Conn
done chan struct{}
}
// Start launches the mock TCP server, listening to an actual address / port.
func (mock *ServerMock) Start(t *testing.T, addr string, handler ClientHandler) {
mock.t = t
mock.handler = handler
if err := mock.init(addr); err != nil {
return
}
go mock.loop()
}
func (mock *ServerMock) Stop() {
close(mock.done)
if mock.listener != nil {
mock.listener.Close()
}
// Close all existing connections
for _, c := range mock.connections {
c.Close()
}
}
//=============================================================================
// Mock Server internals
// init starts listener on the provided address.
func (mock *ServerMock) init(addr string) error {
mock.done = make(chan struct{})
l, err := net.Listen("tcp", addr)
if err != nil {
mock.t.Errorf("TCPServerMock cannot listen on address: %q", addr)
return err
}
mock.listener = l
return nil
}
// loop accepts connections and creates a go routine per connection.
// The go routine is running the client handler, that is used to provide the
// real TCP server behaviour.
func (mock *ServerMock) loop() {
listener := mock.listener
for {
conn, err := listener.Accept()
if err != nil {
select {
case <-mock.done:
return
default:
mock.t.Error("TCPServerMock accept error:", err.Error())
}
return
}
mock.connections = append(mock.connections, conn)
// TODO Create and pass a context to cancel the handler if they are still around = avoid possible leak on complex handlers
go mock.handler(mock.t, conn)
}
}