Add session tests

This commit is contained in:
Ian Kent 2014-11-23 12:43:33 +00:00
parent fe4748f583
commit cb5e79debb
4 changed files with 125 additions and 16 deletions

View file

@ -1,6 +1,7 @@
package config package config
import ( import (
"github.com/ian-kent/Go-MailHog/mailhog/data"
"github.com/ian-kent/Go-MailHog/mailhog/storage" "github.com/ian-kent/Go-MailHog/mailhog/storage"
) )
@ -22,7 +23,7 @@ type Config struct {
MongoUri string MongoUri string
MongoDb string MongoDb string
MongoColl string MongoColl string
MessageChan chan interface{} MessageChan chan *data.Message
Storage storage.Storage Storage storage.Storage
Assets func(asset string) ([]byte, error) Assets func(asset string) ([]byte, error)
} }

View file

@ -7,26 +7,27 @@ import (
"log" "log"
"strings" "strings"
"github.com/ian-kent/Go-MailHog/mailhog/config"
"github.com/ian-kent/Go-MailHog/mailhog/data" "github.com/ian-kent/Go-MailHog/mailhog/data"
"github.com/ian-kent/Go-MailHog/mailhog/smtp/protocol" "github.com/ian-kent/Go-MailHog/mailhog/smtp/protocol"
"github.com/ian-kent/Go-MailHog/mailhog/storage"
) )
// Session represents a SMTP session using net.TCPConn // Session represents a SMTP session using net.TCPConn
type Session struct { type Session struct {
conn io.ReadWriteCloser conn io.ReadWriteCloser
proto *protocol.Protocol proto *protocol.Protocol
conf *config.Config storage storage.Storage
messageChan chan *data.Message
remoteAddress string remoteAddress string
isTLS bool isTLS bool
line string line string
} }
// Accept starts a new SMTP session using io.ReadWriteCloser // Accept starts a new SMTP session using io.ReadWriteCloser
func Accept(remoteAddress string, conn io.ReadWriteCloser, conf *config.Config) { func Accept(remoteAddress string, conn io.ReadWriteCloser, storage storage.Storage, messageChan chan *data.Message, hostname string) {
proto := protocol.NewProtocol() proto := protocol.NewProtocol()
proto.Hostname = conf.Hostname proto.Hostname = hostname
session := &Session{conn, proto, conf, remoteAddress, false, ""} session := &Session{conn, proto, storage, messageChan, remoteAddress, false, ""}
proto.LogHandler = session.logf proto.LogHandler = session.logf
proto.MessageReceivedHandler = session.acceptMessage proto.MessageReceivedHandler = session.acceptMessage
proto.ValidateSenderHandler = session.validateSender proto.ValidateSenderHandler = session.validateSender
@ -54,8 +55,8 @@ func (c *Session) validateSender(from string) bool {
func (c *Session) acceptMessage(msg *data.Message) (id string, err error) { func (c *Session) acceptMessage(msg *data.Message) (id string, err error) {
c.logf("Storing message %s", msg.ID) c.logf("Storing message %s", msg.ID)
id, err = c.conf.Storage.Store(msg) id, err = c.storage.Store(msg)
c.conf.MessageChan <- msg c.messageChan <- msg
return return
} }
@ -72,8 +73,10 @@ func (c *Session) Read() bool {
if n == 0 { if n == 0 {
c.logf("Connection closed by remote host\n") c.logf("Connection closed by remote host\n")
io.Closer(c.conn).Close() // not sure this is necessary?
return false return false
} }
if err != nil { if err != nil {
c.logf("Error reading from socket: %s\n", err) c.logf("Error reading from socket: %s\n", err)
return false return false
@ -86,13 +89,15 @@ func (c *Session) Read() bool {
c.line += text c.line += text
line, reply := c.proto.Parse(c.line) for strings.Contains(c.line, "\n") {
c.line = line line, reply := c.proto.Parse(c.line)
c.line = line
if reply != nil { if reply != nil {
c.Write(reply) c.Write(reply)
if reply.Status == 221 { if reply.Status == 221 {
io.Closer(c.conn).Close() io.Closer(c.conn).Close()
}
} }
} }

View file

@ -1,11 +1,107 @@
package server package server
import ( import (
"errors"
"sync"
"testing" "testing"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
"github.com/ian-kent/Go-MailHog/mailhog/data"
"github.com/ian-kent/Go-MailHog/mailhog/storage"
) )
type fakeRw struct {
_read func(p []byte) (n int, err error)
_write func(p []byte) (n int, err error)
_close func() error
}
func (rw *fakeRw) Read(p []byte) (n int, err error) {
if rw._read != nil {
return rw._read(p)
}
return 0, nil
}
func (rw *fakeRw) Close() error {
if rw._close != nil {
return rw._close()
}
return nil
}
func (rw *fakeRw) Write(p []byte) (n int, err error) {
if rw._write != nil {
return rw._write(p)
}
return len(p), nil
}
func TestAccept(t *testing.T) {
Convey("Accept should handle a connection", t, func() {
frw := &fakeRw{}
mChan := make(chan *data.Message)
Accept("1.1.1.1:11111", frw, storage.CreateInMemory(), mChan, "localhost")
})
}
func TestSocketError(t *testing.T) {
Convey("Socket errors should return from Accept", t, func() {
frw := &fakeRw{
_read: func(p []byte) (n int, err error) {
return -1, errors.New("OINK")
},
}
mChan := make(chan *data.Message)
Accept("1.1.1.1:11111", frw, storage.CreateInMemory(), mChan, "localhost")
})
}
func TestAcceptMessage(t *testing.T) {
Convey("acceptMessage should be called", t, func() {
mbuf := "EHLO localhost\nMAIL FROM:<test>\nRCPT TO:<test>\nDATA\nHi.\r\n.\r\nQUIT\n"
var rbuf []byte
frw := &fakeRw{
_read: func(p []byte) (n int, err error) {
if len(p) >= len(mbuf) {
ba := []byte(mbuf)
mbuf = ""
for i, b := range ba {
p[i] = b
}
return len(ba), nil
}
ba := []byte(mbuf[0:len(p)])
mbuf = mbuf[len(p):]
for i, b := range ba {
p[i] = b
}
return len(ba), nil
},
_write: func(p []byte) (n int, err error) {
rbuf = append(rbuf, p...)
return len(p), nil
},
_close: func() error {
return nil
},
}
mChan := make(chan *data.Message)
var wg sync.WaitGroup
wg.Add(1)
handlerCalled := false
go func() {
handlerCalled = true
m := <-mChan
So(m, ShouldNotBeNil)
wg.Done()
}()
Accept("1.1.1.1:11111", frw, storage.CreateInMemory(), mChan, "localhost")
wg.Wait()
So(handlerCalled, ShouldBeTrue)
})
}
func TestValidateAuthentication(t *testing.T) { func TestValidateAuthentication(t *testing.T) {
Convey("validateAuthentication is always successful", t, func() { Convey("validateAuthentication is always successful", t, func() {
c := &Session{} c := &Session{}

11
main.go
View file

@ -7,6 +7,7 @@ import (
"os" "os"
"github.com/ian-kent/Go-MailHog/mailhog/config" "github.com/ian-kent/Go-MailHog/mailhog/config"
"github.com/ian-kent/Go-MailHog/mailhog/data"
mhhttp "github.com/ian-kent/Go-MailHog/mailhog/http" mhhttp "github.com/ian-kent/Go-MailHog/mailhog/http"
"github.com/ian-kent/Go-MailHog/mailhog/http/api" "github.com/ian-kent/Go-MailHog/mailhog/http/api"
smtp "github.com/ian-kent/Go-MailHog/mailhog/smtp/server" smtp "github.com/ian-kent/Go-MailHog/mailhog/smtp/server"
@ -42,7 +43,7 @@ func configure() {
MongoDb: mongodb, MongoDb: mongodb,
MongoColl: mongocoll, MongoColl: mongocoll,
Assets: Asset, Assets: Asset,
MessageChan: make(chan interface{}), MessageChan: make(chan *data.Message),
} }
if storage_type == "mongodb" { if storage_type == "mongodb" {
@ -123,6 +124,12 @@ func smtp_listen() *net.TCPListener {
} }
defer conn.Close() defer conn.Close()
go smtp.Accept(conn.(*net.TCPConn).RemoteAddr().String(), io.ReadWriteCloser(conn), conf) go smtp.Accept(
conn.(*net.TCPConn).RemoteAddr().String(),
io.ReadWriteCloser(conn),
conf.Storage,
conf.MessageChan,
conf.Hostname,
)
} }
} }