2015-06-30 17:55:03 +08:00
|
|
|
package gomail
|
|
|
|
|
|
|
|
import (
|
2015-07-03 05:08:18 +08:00
|
|
|
"bytes"
|
2015-06-30 17:55:03 +08:00
|
|
|
"crypto/tls"
|
|
|
|
"io"
|
|
|
|
"net"
|
|
|
|
"net/smtp"
|
|
|
|
"reflect"
|
|
|
|
"testing"
|
2016-04-12 05:23:03 +08:00
|
|
|
"time"
|
2015-06-30 17:55:03 +08:00
|
|
|
)
|
|
|
|
|
2015-06-30 22:41:55 +08:00
|
|
|
const (
|
2015-06-30 17:55:03 +08:00
|
|
|
testPort = 587
|
|
|
|
testSSLPort = 465
|
2015-06-30 22:41:55 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2016-03-07 02:17:01 +08:00
|
|
|
testConn = &net.TCPConn{}
|
2015-06-30 17:55:03 +08:00
|
|
|
testTLSConn = &tls.Conn{}
|
|
|
|
testConfig = &tls.Config{InsecureSkipVerify: true}
|
2016-03-07 02:17:01 +08:00
|
|
|
testAuth = smtp.PlainAuth("", testUser, testPwd, testHost)
|
2015-06-30 17:55:03 +08:00
|
|
|
)
|
|
|
|
|
2015-06-30 23:08:26 +08:00
|
|
|
func TestDialer(t *testing.T) {
|
2016-03-07 02:17:01 +08:00
|
|
|
d := NewDialer(testHost, testPort, "user", "pwd")
|
2015-06-30 17:55:03 +08:00
|
|
|
testSendMail(t, d, []string{
|
|
|
|
"Extension STARTTLS",
|
|
|
|
"StartTLS",
|
|
|
|
"Extension AUTH",
|
|
|
|
"Auth",
|
|
|
|
"Mail " + testFrom,
|
|
|
|
"Rcpt " + testTo1,
|
|
|
|
"Rcpt " + testTo2,
|
|
|
|
"Data",
|
|
|
|
"Write message",
|
|
|
|
"Close writer",
|
|
|
|
"Quit",
|
|
|
|
"Close",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-06-30 23:08:26 +08:00
|
|
|
func TestDialerSSL(t *testing.T) {
|
2016-03-07 02:17:01 +08:00
|
|
|
d := NewDialer(testHost, testSSLPort, "user", "pwd")
|
2015-06-30 17:55:03 +08:00
|
|
|
testSendMail(t, d, []string{
|
|
|
|
"Extension AUTH",
|
|
|
|
"Auth",
|
|
|
|
"Mail " + testFrom,
|
|
|
|
"Rcpt " + testTo1,
|
|
|
|
"Rcpt " + testTo2,
|
|
|
|
"Data",
|
|
|
|
"Write message",
|
|
|
|
"Close writer",
|
|
|
|
"Quit",
|
|
|
|
"Close",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-06-30 23:08:26 +08:00
|
|
|
func TestDialerConfig(t *testing.T) {
|
2016-03-07 02:17:01 +08:00
|
|
|
d := NewDialer(testHost, testPort, "user", "pwd")
|
2015-10-21 13:07:33 +08:00
|
|
|
d.LocalName = "test"
|
2015-06-30 17:55:03 +08:00
|
|
|
d.TLSConfig = testConfig
|
|
|
|
testSendMail(t, d, []string{
|
2015-10-21 13:07:33 +08:00
|
|
|
"Hello test",
|
2015-06-30 17:55:03 +08:00
|
|
|
"Extension STARTTLS",
|
|
|
|
"StartTLS",
|
|
|
|
"Extension AUTH",
|
|
|
|
"Auth",
|
|
|
|
"Mail " + testFrom,
|
|
|
|
"Rcpt " + testTo1,
|
|
|
|
"Rcpt " + testTo2,
|
|
|
|
"Data",
|
|
|
|
"Write message",
|
|
|
|
"Close writer",
|
|
|
|
"Quit",
|
|
|
|
"Close",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-06-30 23:08:26 +08:00
|
|
|
func TestDialerSSLConfig(t *testing.T) {
|
2016-03-07 02:17:01 +08:00
|
|
|
d := NewDialer(testHost, testSSLPort, "user", "pwd")
|
2015-10-21 13:07:33 +08:00
|
|
|
d.LocalName = "test"
|
2015-06-30 17:55:03 +08:00
|
|
|
d.TLSConfig = testConfig
|
|
|
|
testSendMail(t, d, []string{
|
2015-10-21 13:07:33 +08:00
|
|
|
"Hello test",
|
2015-06-30 17:55:03 +08:00
|
|
|
"Extension AUTH",
|
|
|
|
"Auth",
|
|
|
|
"Mail " + testFrom,
|
|
|
|
"Rcpt " + testTo1,
|
|
|
|
"Rcpt " + testTo2,
|
|
|
|
"Data",
|
|
|
|
"Write message",
|
|
|
|
"Close writer",
|
|
|
|
"Quit",
|
|
|
|
"Close",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-06-30 23:08:26 +08:00
|
|
|
func TestDialerNoAuth(t *testing.T) {
|
|
|
|
d := &Dialer{
|
2015-06-30 17:55:03 +08:00
|
|
|
Host: testHost,
|
|
|
|
Port: testPort,
|
|
|
|
}
|
|
|
|
testSendMail(t, d, []string{
|
|
|
|
"Extension STARTTLS",
|
|
|
|
"StartTLS",
|
|
|
|
"Mail " + testFrom,
|
|
|
|
"Rcpt " + testTo1,
|
|
|
|
"Rcpt " + testTo2,
|
|
|
|
"Data",
|
|
|
|
"Write message",
|
|
|
|
"Close writer",
|
|
|
|
"Quit",
|
|
|
|
"Close",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-03-14 20:43:57 +08:00
|
|
|
func TestDialerTimeout(t *testing.T) {
|
|
|
|
d := &Dialer{
|
|
|
|
Host: testHost,
|
|
|
|
Port: testPort,
|
|
|
|
}
|
|
|
|
testSendMailTimeout(t, d, []string{
|
|
|
|
"Extension STARTTLS",
|
|
|
|
"StartTLS",
|
|
|
|
"Mail " + testFrom,
|
|
|
|
"Extension STARTTLS",
|
|
|
|
"StartTLS",
|
|
|
|
"Mail " + testFrom,
|
|
|
|
"Rcpt " + testTo1,
|
|
|
|
"Rcpt " + testTo2,
|
|
|
|
"Data",
|
|
|
|
"Write message",
|
|
|
|
"Close writer",
|
|
|
|
"Quit",
|
|
|
|
"Close",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-06-30 17:55:03 +08:00
|
|
|
type mockClient struct {
|
2016-03-14 20:43:57 +08:00
|
|
|
t *testing.T
|
|
|
|
i int
|
|
|
|
want []string
|
|
|
|
addr string
|
|
|
|
config *tls.Config
|
|
|
|
timeout bool
|
2015-06-30 17:55:03 +08:00
|
|
|
}
|
|
|
|
|
2015-10-21 13:07:33 +08:00
|
|
|
func (c *mockClient) Hello(localName string) error {
|
|
|
|
c.do("Hello " + localName)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-06-30 17:55:03 +08:00
|
|
|
func (c *mockClient) Extension(ext string) (bool, string) {
|
|
|
|
c.do("Extension " + ext)
|
|
|
|
return true, ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) StartTLS(config *tls.Config) error {
|
|
|
|
assertConfig(c.t, config, c.config)
|
|
|
|
c.do("StartTLS")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) Auth(a smtp.Auth) error {
|
2016-03-07 02:17:01 +08:00
|
|
|
if !reflect.DeepEqual(a, testAuth) {
|
|
|
|
c.t.Errorf("Invalid auth, got %#v, want %#v", a, testAuth)
|
|
|
|
}
|
2015-06-30 17:55:03 +08:00
|
|
|
c.do("Auth")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) Mail(from string) error {
|
|
|
|
c.do("Mail " + from)
|
2016-03-14 20:43:57 +08:00
|
|
|
if c.timeout {
|
|
|
|
c.timeout = false
|
|
|
|
return io.EOF
|
|
|
|
}
|
2015-06-30 17:55:03 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) Rcpt(to string) error {
|
|
|
|
c.do("Rcpt " + to)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) Data() (io.WriteCloser, error) {
|
|
|
|
c.do("Data")
|
|
|
|
return &mockWriter{c: c, want: testMsg}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) Quit() error {
|
|
|
|
c.do("Quit")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) Close() error {
|
|
|
|
c.do("Close")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockClient) do(cmd string) {
|
|
|
|
if c.i >= len(c.want) {
|
|
|
|
c.t.Fatalf("Invalid command %q", cmd)
|
|
|
|
}
|
|
|
|
|
|
|
|
if cmd != c.want[c.i] {
|
|
|
|
c.t.Fatalf("Invalid command, got %q, want %q", cmd, c.want[c.i])
|
|
|
|
}
|
|
|
|
c.i++
|
|
|
|
}
|
|
|
|
|
|
|
|
type mockWriter struct {
|
|
|
|
want string
|
|
|
|
c *mockClient
|
2015-07-03 05:08:18 +08:00
|
|
|
buf bytes.Buffer
|
2015-06-30 17:55:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w *mockWriter) Write(p []byte) (int, error) {
|
2015-07-03 05:08:18 +08:00
|
|
|
if w.buf.Len() == 0 {
|
|
|
|
w.c.do("Write message")
|
|
|
|
}
|
|
|
|
w.buf.Write(p)
|
2015-06-30 17:55:03 +08:00
|
|
|
return len(p), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *mockWriter) Close() error {
|
2015-07-03 05:08:18 +08:00
|
|
|
compareBodies(w.c.t, w.buf.String(), w.want)
|
2015-06-30 17:55:03 +08:00
|
|
|
w.c.do("Close writer")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-06-30 23:08:26 +08:00
|
|
|
func testSendMail(t *testing.T, d *Dialer, want []string) {
|
2016-03-14 20:43:57 +08:00
|
|
|
doTestSendMail(t, d, want, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testSendMailTimeout(t *testing.T, d *Dialer, want []string) {
|
|
|
|
doTestSendMail(t, d, want, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func doTestSendMail(t *testing.T, d *Dialer, want []string, timeout bool) {
|
2015-06-30 17:55:03 +08:00
|
|
|
testClient := &mockClient{
|
2016-03-14 20:43:57 +08:00
|
|
|
t: t,
|
|
|
|
want: want,
|
|
|
|
addr: addr(d.Host, d.Port),
|
|
|
|
config: d.TLSConfig,
|
|
|
|
timeout: timeout,
|
2015-06-30 17:55:03 +08:00
|
|
|
}
|
|
|
|
|
2016-04-12 05:23:03 +08:00
|
|
|
netDialTimeout = func(network, address string, d time.Duration) (net.Conn, error) {
|
2015-06-30 17:55:03 +08:00
|
|
|
if network != "tcp" {
|
|
|
|
t.Errorf("Invalid network, got %q, want tcp", network)
|
|
|
|
}
|
2016-03-07 02:17:01 +08:00
|
|
|
if address != testClient.addr {
|
|
|
|
t.Errorf("Invalid address, got %q, want %q",
|
|
|
|
address, testClient.addr)
|
|
|
|
}
|
|
|
|
return testConn, nil
|
2015-06-30 17:55:03 +08:00
|
|
|
}
|
|
|
|
|
2016-03-07 02:17:01 +08:00
|
|
|
tlsClient = func(conn net.Conn, config *tls.Config) *tls.Conn {
|
|
|
|
if conn != testConn {
|
|
|
|
t.Errorf("Invalid conn, got %#v, want %#v", conn, testConn)
|
2015-06-30 17:55:03 +08:00
|
|
|
}
|
2016-03-07 02:17:01 +08:00
|
|
|
assertConfig(t, config, testClient.config)
|
|
|
|
return testTLSConn
|
|
|
|
}
|
|
|
|
|
|
|
|
smtpNewClient = func(conn net.Conn, host string) (smtpClient, error) {
|
2015-06-30 17:55:03 +08:00
|
|
|
if host != testHost {
|
|
|
|
t.Errorf("Invalid host, got %q, want %q", host, testHost)
|
|
|
|
}
|
|
|
|
return testClient, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := d.DialAndSend(getTestMessage()); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func assertConfig(t *testing.T, got, want *tls.Config) {
|
|
|
|
if want == nil {
|
|
|
|
want = &tls.Config{ServerName: testHost}
|
|
|
|
}
|
|
|
|
if got.ServerName != want.ServerName {
|
|
|
|
t.Errorf("Invalid field ServerName in config, got %q, want %q", got.ServerName, want.ServerName)
|
|
|
|
}
|
|
|
|
if got.InsecureSkipVerify != want.InsecureSkipVerify {
|
|
|
|
t.Errorf("Invalid field InsecureSkipVerify in config, got %v, want %v", got.InsecureSkipVerify, want.InsecureSkipVerify)
|
|
|
|
}
|
|
|
|
}
|