1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-17 17:47:03 +00:00

Refactor SMTP handler_test log collection (#289)

* smtp: allow logger to be passed into startSession
* smtp: remove logbuf and teardown from handler_test
* smtp: handler_test log output to testing.T
This commit is contained in:
James Hillyerd
2022-08-24 21:42:53 -07:00
committed by GitHub
parent eae4926b23
commit 9dbffa88de
3 changed files with 36 additions and 73 deletions

View File

@@ -15,7 +15,6 @@ import (
"github.com/inbucket/inbucket/pkg/policy" "github.com/inbucket/inbucket/pkg/policy"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log"
) )
// State tracks the current mode of our SMTP state machine. // State tracks the current mode of our SMTP state machine.
@@ -144,8 +143,8 @@ func (s *Session) String() string {
* 4. If bad cmd, respond error * 4. If bad cmd, respond error
* 5. Goto 2 * 5. Goto 2
*/ */
func (s *Server) startSession(id int, conn net.Conn) { func (s *Server) startSession(id int, conn net.Conn, logger zerolog.Logger) {
logger := log.Hook(logHook{}).With(). logger = logger.Hook(logHook{}).With().
Str("module", "smtp"). Str("module", "smtp").
Str("remote", conn.RemoteAddr().String()). Str("remote", conn.RemoteAddr().String()).
Int("session", id).Logger() Int("session", id).Logger()

View File

@@ -1,14 +1,11 @@
package smtp package smtp
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"log"
"net" "net"
"net/textproto" "net/textproto"
"os"
"testing" "testing"
"time" "time"
@@ -17,6 +14,7 @@ import (
"github.com/inbucket/inbucket/pkg/policy" "github.com/inbucket/inbucket/pkg/policy"
"github.com/inbucket/inbucket/pkg/storage" "github.com/inbucket/inbucket/pkg/storage"
"github.com/inbucket/inbucket/pkg/test" "github.com/inbucket/inbucket/pkg/test"
"github.com/rs/zerolog"
) )
type scriptStep struct { type scriptStep struct {
@@ -27,8 +25,8 @@ type scriptStep struct {
// Test commands in GREET state // Test commands in GREET state
func TestGreetState(t *testing.T) { func TestGreetState(t *testing.T) {
ds := test.NewStore() ds := test.NewStore()
server, logbuf, teardown := setupSMTPServer(ds) server := setupSMTPServer(ds)
defer teardown() defer server.Drain() // Required to prevent test logging data race.
// Test out some mangled HELOs // Test out some mangled HELOs
script := []scriptStep{ script := []scriptStep{
@@ -77,19 +75,13 @@ func TestGreetState(t *testing.T) {
t.Error(err) t.Error(err)
} }
if t.Failed() {
// Wait for handler to finish logging
time.Sleep(2 * time.Second)
// Dump buffered log data if there was a failure
_, _ = io.Copy(os.Stderr, logbuf)
}
} }
// Test commands in READY state // Test commands in READY state
func TestEmptyEnvelope(t *testing.T) { func TestEmptyEnvelope(t *testing.T) {
ds := test.NewStore() ds := test.NewStore()
server, logbuf, teardown := setupSMTPServer(ds) server := setupSMTPServer(ds)
defer teardown() defer server.Drain()
// Test out some empty envelope without blanks // Test out some empty envelope without blanks
script := []scriptStep{ script := []scriptStep{
@@ -97,8 +89,6 @@ func TestEmptyEnvelope(t *testing.T) {
{"MAIL FROM:<>", 250}, {"MAIL FROM:<>", 250},
} }
if err := playSession(t, server, script); err != nil { if err := playSession(t, server, script); err != nil {
// Dump buffered log data if there was a failure
_, _ = io.Copy(os.Stderr, logbuf)
t.Error(err) t.Error(err)
} }
@@ -108,8 +98,6 @@ func TestEmptyEnvelope(t *testing.T) {
{"MAIL FROM: <>", 250}, {"MAIL FROM: <>", 250},
} }
if err := playSession(t, server, script); err != nil { if err := playSession(t, server, script); err != nil {
// Dump buffered log data if there was a failure
_, _ = io.Copy(os.Stderr, logbuf)
t.Error(err) t.Error(err)
} }
} }
@@ -117,8 +105,8 @@ func TestEmptyEnvelope(t *testing.T) {
// Test AUTH // Test AUTH
func TestAuth(t *testing.T) { func TestAuth(t *testing.T) {
ds := test.NewStore() ds := test.NewStore()
server, logbuf, teardown := setupSMTPServer(ds) server := setupSMTPServer(ds)
defer teardown() defer server.Drain()
// PLAIN AUTH // PLAIN AUTH
script := []scriptStep{ script := []scriptStep{
@@ -149,20 +137,13 @@ func TestAuth(t *testing.T) {
if err := playSession(t, server, script); err != nil { if err := playSession(t, server, script); err != nil {
t.Error(err) t.Error(err)
} }
if t.Failed() {
// Wait for handler to finish logging
time.Sleep(2 * time.Second)
// Dump buffered log data if there was a failure
_, _ = io.Copy(os.Stderr, logbuf)
}
} }
// Test commands in READY state // Test commands in READY state
func TestReadyState(t *testing.T) { func TestReadyState(t *testing.T) {
ds := test.NewStore() ds := test.NewStore()
server, logbuf, teardown := setupSMTPServer(ds) server := setupSMTPServer(ds)
defer teardown() defer server.Drain()
// Test out some mangled READY commands // Test out some mangled READY commands
script := []scriptStep{ script := []scriptStep{
@@ -221,20 +202,13 @@ func TestReadyState(t *testing.T) {
if err := playSession(t, server, script); err != nil { if err := playSession(t, server, script); err != nil {
t.Error(err) t.Error(err)
} }
if t.Failed() {
// Wait for handler to finish logging
time.Sleep(2 * time.Second)
// Dump buffered log data if there was a failure
_, _ = io.Copy(os.Stderr, logbuf)
}
} }
// Test commands in MAIL state // Test commands in MAIL state
func TestMailState(t *testing.T) { func TestMailState(t *testing.T) {
mds := test.NewStore() mds := test.NewStore()
server, logbuf, teardown := setupSMTPServer(mds) server := setupSMTPServer(mds)
defer teardown() defer server.Drain()
// Test out some mangled READY commands // Test out some mangled READY commands
script := []scriptStep{ script := []scriptStep{
@@ -335,23 +309,16 @@ func TestMailState(t *testing.T) {
if err := playSession(t, server, script); err != nil { if err := playSession(t, server, script); err != nil {
t.Error(err) t.Error(err)
} }
if t.Failed() {
// Wait for handler to finish logging
time.Sleep(2 * time.Second)
// Dump buffered log data if there was a failure
_, _ = io.Copy(os.Stderr, logbuf)
}
} }
// Test commands in DATA state // Test commands in DATA state
func TestDataState(t *testing.T) { func TestDataState(t *testing.T) {
mds := test.NewStore() mds := test.NewStore()
server, logbuf, teardown := setupSMTPServer(mds) server := setupSMTPServer(mds)
defer teardown() defer server.Drain()
var script []scriptStep var script []scriptStep
pipe := setupSMTPSession(server) pipe := setupSMTPSession(t, server)
c := textproto.NewConn(pipe) c := textproto.NewConn(pipe)
if code, _, err := c.ReadCodeLine(220); err != nil { if code, _, err := c.ReadCodeLine(220); err != nil {
@@ -366,6 +333,7 @@ func TestDataState(t *testing.T) {
if err := playScriptAgainst(t, c, script); err != nil { if err := playScriptAgainst(t, c, script); err != nil {
t.Error(err) t.Error(err)
} }
// Send a message // Send a message
body := `To: u1@gmail.com body := `To: u1@gmail.com
From: john@gmail.com From: john@gmail.com
@@ -379,9 +347,11 @@ Hi!
if code, _, err := c.ReadCodeLine(250); err != nil { if code, _, err := c.ReadCodeLine(250); err != nil {
t.Errorf("Expected a 250 greeting, got %v", code) t.Errorf("Expected a 250 greeting, got %v", code)
} }
_, _ = c.Cmd("QUIT")
_, _, _ = c.ReadCodeLine(221)
// Test with no useful headers. // Test with no useful headers.
pipe = setupSMTPSession(server) pipe = setupSMTPSession(t, server)
c = textproto.NewConn(pipe) c = textproto.NewConn(pipe)
if code, _, err := c.ReadCodeLine(220); err != nil { if code, _, err := c.ReadCodeLine(220); err != nil {
t.Errorf("Expected a 220 greeting, got %v", code) t.Errorf("Expected a 220 greeting, got %v", code)
@@ -395,6 +365,7 @@ Hi!
if err := playScriptAgainst(t, c, script); err != nil { if err := playScriptAgainst(t, c, script); err != nil {
t.Error(err) t.Error(err)
} }
// Send a message // Send a message
body = `X-Useless-Header: true body = `X-Useless-Header: true
@@ -406,18 +377,13 @@ Hi! Can you still deliver this?
if code, _, err := c.ReadCodeLine(250); err != nil { if code, _, err := c.ReadCodeLine(250); err != nil {
t.Errorf("Expected a 250 greeting, got %v", code) t.Errorf("Expected a 250 greeting, got %v", code)
} }
_, _ = c.Cmd("QUIT")
if t.Failed() { _, _, _ = c.ReadCodeLine(221)
// Wait for handler to finish logging
time.Sleep(2 * time.Second)
// Dump buffered log data if there was a failure
_, _ = io.Copy(os.Stderr, logbuf)
}
} }
// playSession creates a new session, reads the greeting and then plays the script // playSession creates a new session, reads the greeting and then plays the script
func playSession(t *testing.T, server *Server, script []scriptStep) error { func playSession(t *testing.T, server *Server, script []scriptStep) error {
pipe := setupSMTPSession(server) pipe := setupSMTPSession(t, server)
c := textproto.NewConn(pipe) c := textproto.NewConn(pipe)
if code, _, err := c.ReadCodeLine(220); err != nil { if code, _, err := c.ReadCodeLine(220); err != nil {
@@ -467,7 +433,7 @@ func (m *mockConn) SetDeadline(t time.Time) error { return nil }
func (m *mockConn) SetReadDeadline(t time.Time) error { return nil } func (m *mockConn) SetReadDeadline(t time.Time) error { return nil }
func (m *mockConn) SetWriteDeadline(t time.Time) error { return nil } func (m *mockConn) SetWriteDeadline(t time.Time) error { return nil }
func setupSMTPServer(ds storage.Store) (s *Server, buf *bytes.Buffer, teardown func()) { func setupSMTPServer(ds storage.Store) *Server {
cfg := &config.Root{ cfg := &config.Root{
MailboxNaming: config.FullNaming, MailboxNaming: config.FullNaming,
SMTP: config.SMTP{ SMTP: config.SMTP{
@@ -480,26 +446,24 @@ func setupSMTPServer(ds storage.Store) (s *Server, buf *bytes.Buffer, teardown f
Timeout: 5, Timeout: 5,
}, },
} }
// Capture log output.
buf = new(bytes.Buffer)
log.SetOutput(buf)
// Create a server, don't start it. // Create a server, don't start it.
// TODO Remove teardown.
teardown = func() {}
addrPolicy := &policy.Addressing{Config: cfg} addrPolicy := &policy.Addressing{Config: cfg}
manager := &message.StoreManager{Store: ds} manager := &message.StoreManager{Store: ds}
s = NewServer(cfg.SMTP, manager, addrPolicy)
return s, buf, teardown return NewServer(cfg.SMTP, manager, addrPolicy)
} }
var sessionNum int var sessionNum int
func setupSMTPSession(server *Server) net.Conn { func setupSMTPSession(t *testing.T, server *Server) net.Conn {
// Pair of pipes to communicate. logger := zerolog.New(zerolog.NewTestWriter(t))
serverConn, clientConn := net.Pipe() serverConn, clientConn := net.Pipe()
// Start the session. // Start the session.
server.wg.Add(1) server.wg.Add(1)
sessionNum++ sessionNum++
go server.startSession(sessionNum, &mockConn{serverConn}) go server.startSession(sessionNum, &mockConn{serverConn}, logger)
return clientConn return clientConn
} }

View File

@@ -170,7 +170,7 @@ func (s *Server) serve(ctx context.Context) {
tempDelay = 0 tempDelay = 0
expConnectsTotal.Add(1) expConnectsTotal.Add(1)
s.wg.Add(1) s.wg.Add(1)
go s.startSession(sessionID, conn) go s.startSession(sessionID, conn, log.Logger)
} }
} }
} }