Add the first test for SMTP connections, based on the exchange in RFC 5321.
[mailpopbox.git] / smtp / conn_test.go
1 package smtp
2
3 import (
4 "net"
5 "net/textproto"
6 "path/filepath"
7 "runtime"
8 "strings"
9 "testing"
10 )
11
12 func ok(t testing.TB, err error) {
13 if err != nil {
14 _, file, line, _ := runtime.Caller(1)
15 t.Errorf("[%s:%d] unexpected error: %v", filepath.Base(file), line, err)
16 }
17 }
18
19 func readCodeLine(t testing.TB, conn *textproto.Conn, code int) string {
20 _, message, err := conn.ReadCodeLine(code)
21 ok(t, err)
22 return message
23 }
24
25 // runServer creates a TCP socket, runs a listening server, and returns the connection.
26 // The server exits when the Conn is closed.
27 func runServer(t *testing.T, server Server) net.Listener {
28 l, err := net.Listen("tcp", "localhost:0")
29 if err != nil {
30 t.Fatal(err)
31 return nil
32 }
33
34 go func() {
35 for {
36 conn, err := l.Accept()
37 if err != nil {
38 return
39 }
40 go AcceptConnection(conn, server)
41 }
42 }()
43
44 return l
45 }
46
47 type testServer struct {
48 EmptyServerCallbacks
49 }
50
51 func (s *testServer) Name() string {
52 return "Test-Server"
53 }
54
55 func createClient(t *testing.T, addr net.Addr) *textproto.Conn {
56 conn, err := textproto.Dial(addr.Network(), addr.String())
57 if err != nil {
58 t.Fatal(err)
59 return nil
60 }
61 return conn
62 }
63
64 // RFC 5321 ยง D.1
65 func TestScenarioTypical(t *testing.T) {
66 s := testServer{}
67 l := runServer(t, &s)
68 defer l.Close()
69
70 conn := createClient(t, l.Addr())
71
72 message := readCodeLine(t, conn, 220)
73 if !strings.HasPrefix(message, s.Name()) {
74 t.Errorf("Greeting does not have server name, got %q", message)
75 }
76
77 greet := "greeting.TestScenarioTypical"
78 ok(t, conn.PrintfLine("EHLO "+greet))
79
80 _, message, err := conn.ReadResponse(250)
81 ok(t, err)
82 if !strings.Contains(message, greet) {
83 t.Errorf("EHLO response does not contain greeting, got %q", message)
84 }
85
86 ok(t, conn.PrintfLine("MAIL FROM:<Smith@bar.com>"))
87 readCodeLine(t, conn, 250)
88
89 ok(t, conn.PrintfLine("RCPT TO:<Jones@foo.com>"))
90 readCodeLine(t, conn, 250)
91
92 ok(t, conn.PrintfLine("RCPT TO:<Green@foo.com>"))
93 readCodeLine(t, conn, 250) // TODO: make this 55o by rejecting Green
94
95 ok(t, conn.PrintfLine("RCPT TO:<Brown@foo.com>"))
96 readCodeLine(t, conn, 250)
97
98 ok(t, conn.PrintfLine("DATA"))
99 readCodeLine(t, conn, 354)
100
101 ok(t, conn.PrintfLine("Blah blah blah..."))
102 ok(t, conn.PrintfLine("...etc. etc. etc."))
103 ok(t, conn.PrintfLine("."))
104 readCodeLine(t, conn, 250)
105
106 ok(t, conn.PrintfLine("QUIT"))
107 readCodeLine(t, conn, 221)
108 }