Browse Source

mock net.Conn & example for how to use it

flashmob 8 years ago
parent
commit
ff70fb4778
3 changed files with 192 additions and 1 deletions
  1. 1 1
      mocks/client.go
  2. 101 0
      mocks/conn_mock.go
  3. 90 0
      server_test.go

+ 1 - 1
mocks/client.go

@@ -1,4 +1,4 @@
-package main
+package mocks
 
 import (
 	"fmt"

+ 101 - 0
mocks/conn_mock.go

@@ -0,0 +1,101 @@
+package mocks
+
+import (
+	"io"
+	"net"
+	"time"
+)
+
+// Mocks a net.Conn - server and client sides.
+// See server_test.go for usage examples
+// Taken from https://github.com/jordwest/mock-conn
+// This great answer http://stackoverflow.com/questions/1976950/simulate-a-tcp-connection-in-go
+
+// Addr is a fake network interface which implements the net.Addr interface
+type Addr struct {
+	NetworkString string
+	AddrString    string
+}
+
+func (a Addr) Network() string {
+	return a.NetworkString
+}
+
+func (a Addr) String() string {
+	return a.AddrString
+}
+
+// End is one 'end' of a simulated connection.
+type End struct {
+	Reader *io.PipeReader
+	Writer *io.PipeWriter
+}
+
+func (c End) Close() error {
+	if err := c.Writer.Close(); err != nil {
+		return err
+	}
+	if err := c.Reader.Close(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (e End) Read(data []byte) (n int, err error)  { return e.Reader.Read(data) }
+func (e End) Write(data []byte) (n int, err error) { return e.Writer.Write(data) }
+
+func (e End) LocalAddr() net.Addr {
+	return Addr{
+		NetworkString: "tcp",
+		AddrString:    "127.0.0.1",
+	}
+}
+
+func (e End) RemoteAddr() net.Addr {
+	return Addr{
+		NetworkString: "tcp",
+		AddrString:    "127.0.0.1",
+	}
+}
+
+func (e End) SetDeadline(t time.Time) error      { return nil }
+func (e End) SetReadDeadline(t time.Time) error  { return nil }
+func (e End) SetWriteDeadline(t time.Time) error { return nil }
+
+// MockConn facilitates testing by providing two connected ReadWriteClosers
+// each of which can be used in place of a net.Conn
+type Conn struct {
+	Server *End
+	Client *End
+}
+
+func NewConn() *Conn {
+	// A connection consists of two pipes:
+	// Client      |      Server
+	//   writes   ===>  reads
+	//    reads  <===   writes
+
+	serverRead, clientWrite := io.Pipe()
+	clientRead, serverWrite := io.Pipe()
+
+	return &Conn{
+		Server: &End{
+			Reader: serverRead,
+			Writer: serverWrite,
+		},
+		Client: &End{
+			Reader: clientRead,
+			Writer: clientWrite,
+		},
+	}
+}
+
+func (c *Conn) Close() error {
+	if err := c.Server.Close(); err != nil {
+		return err
+	}
+	if err := c.Client.Close(); err != nil {
+		return err
+	}
+	return nil
+}

+ 90 - 0
server_test.go

@@ -0,0 +1,90 @@
+package guerrilla
+
+import (
+	"testing"
+
+	"bufio"
+	"fmt"
+	"github.com/flashmob/go-guerrilla/backends"
+	"github.com/flashmob/go-guerrilla/mocks"
+	"net/textproto"
+	"strings"
+	"sync"
+)
+
+// getMockServerConfig gets a mock ServerConfig struct used for creating a new server
+func getMockServerConfig() *ServerConfig {
+	sc := &ServerConfig{
+		IsEnabled:       true, // not tested here
+		Hostname:        "saggydimes.test.com",
+		MaxSize:         1024, // smtp message max size
+		PrivateKeyFile:  "./tests/mail.guerrillamail.com.key.pem",
+		PublicKeyFile:   "./tests/mail.guerrillamail.com.cert.pem",
+		Timeout:         5,
+		ListenInterface: "127.0.0.1:2529",
+		StartTLSOn:      true,
+		TLSAlwaysOn:     false,
+		MaxClients:      30, // not tested here
+		LogFile:         "/dev/stdout",
+	}
+	return sc
+}
+
+// getMockServerConn gets a new server using sc. Server will be using a mocked TCP connection
+// using the dummy backend
+// RCP TO command only allows test.com host
+func getMockServerConn(sc *ServerConfig, t *testing.T) (*mocks.Conn, *server) {
+
+	backend, err := backends.New("dummy", backends.BackendConfig{"log_received_mails": true})
+	if err != nil {
+		t.Error("new dummy backend failed because:", err)
+	}
+	server, err := newServer(sc, backend)
+	if err != nil {
+		t.Error("new server failed because:", err)
+	} else {
+		server.setAllowedHosts([]string{"test.com"})
+	}
+	conn := mocks.NewConn()
+	return conn, server
+}
+
+func TestHandleClient(t *testing.T) {
+
+	sc := getMockServerConfig()
+	conn, server := getMockServerConn(sc, t)
+	// call the serve.handleClient() func in a goroutine.
+	client := NewClient(conn.Server, 1)
+	var wg sync.WaitGroup
+	wg.Add(1)
+	go func() {
+		server.handleClient(client)
+		wg.Done()
+	}()
+	// Wait for the greeting from the server
+	r := textproto.NewReader(bufio.NewReader(conn.Client))
+	line, _ := r.ReadLine()
+	fmt.Println(line)
+	w := textproto.NewWriter(bufio.NewWriter(conn.Client))
+	w.PrintfLine("HELO test.test.com")
+	line, _ = r.ReadLine()
+	fmt.Println(line)
+	w.PrintfLine("QUIT")
+	line, _ = r.ReadLine()
+	fmt.Println("line is:", line)
+	expected := "221 Bye"
+	if strings.Index(line, expected) != 0 {
+		t.Error("expected", expected, "but got:", line)
+	}
+	wg.Wait() // wait for handleClient to exit
+}
+
+// TODO
+// - test github issue #44 and #42
+// - test other commands
+
+// also, could test
+// - test allowsHost() and allowsHost()
+// - test isInTransaction() (make sure it returns true after MAIL command, but false after HELO/EHLO/RSET/end of DATA
+// - test to make sure client envelope
+// - perhaps anything else that can be tested in server_test.go