mirror of
https://github.com/wneessen/go-mail.git
synced 2024-11-15 02:12:55 +01:00
Add echo buffer to SMTP server tests
Refactored SMTP server tests to use an echo buffer for capturing responses. This allows for better validation of command responses without relying on error messages. Additionally, added a new test case to validate a full SendMail transaction with TLS and authentication.
This commit is contained in:
parent
c3252626e3
commit
c6da393676
1 changed files with 115 additions and 59 deletions
|
@ -2215,11 +2215,12 @@ func TestClient_Mail(t *testing.T) {
|
||||||
PortAdder.Add(1)
|
PortAdder.Add(1)
|
||||||
serverPort := int(TestServerPortBase + PortAdder.Load())
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
featureSet := "250-8BITMIME\r\n250 STARTTLS"
|
featureSet := "250-8BITMIME\r\n250 STARTTLS"
|
||||||
|
echoBuffer := bytes.NewBuffer(nil)
|
||||||
go func() {
|
go func() {
|
||||||
if err := simpleSMTPServer(ctx, t, &serverProps{
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
EchoCommandAsError: "MAIL FROM:",
|
EchoBuffer: echoBuffer,
|
||||||
FeatureSet: featureSet,
|
FeatureSet: featureSet,
|
||||||
ListenPort: serverPort,
|
ListenPort: serverPort,
|
||||||
},
|
},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
t.Errorf("failed to start test server: %s", err)
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
@ -2237,13 +2238,13 @@ func TestClient_Mail(t *testing.T) {
|
||||||
t.Errorf("failed to close client: %s", err)
|
t.Errorf("failed to close client: %s", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if err = client.Mail("valid-from@domain.tld"); err == nil {
|
if err = client.Mail("valid-from@domain.tld"); err != nil {
|
||||||
t.Error("server should echo the command as error but didn't")
|
t.Errorf("failed to set mail from address: %s", err)
|
||||||
}
|
}
|
||||||
sent := strings.Replace(err.Error(), "500 ", "", -1)
|
|
||||||
expected := "MAIL FROM:<valid-from@domain.tld> BODY=8BITMIME"
|
expected := "MAIL FROM:<valid-from@domain.tld> BODY=8BITMIME"
|
||||||
if !strings.EqualFold(sent, expected) {
|
resp := strings.Split(echoBuffer.String(), "\r\n")
|
||||||
t.Errorf("expected mail from command to be %q, but sent %q", expected, sent)
|
if !strings.EqualFold(resp[5], expected) {
|
||||||
|
t.Errorf("expected mail from command to be %q, but sent %q", expected, resp[5])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
t.Run("from address and server supports SMTPUTF8", func(t *testing.T) {
|
t.Run("from address and server supports SMTPUTF8", func(t *testing.T) {
|
||||||
|
@ -2252,11 +2253,12 @@ func TestClient_Mail(t *testing.T) {
|
||||||
PortAdder.Add(1)
|
PortAdder.Add(1)
|
||||||
serverPort := int(TestServerPortBase + PortAdder.Load())
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
featureSet := "250-SMTPUTF8\r\n250 STARTTLS"
|
featureSet := "250-SMTPUTF8\r\n250 STARTTLS"
|
||||||
|
echoBuffer := bytes.NewBuffer(nil)
|
||||||
go func() {
|
go func() {
|
||||||
if err := simpleSMTPServer(ctx, t, &serverProps{
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
EchoCommandAsError: "MAIL FROM:",
|
EchoBuffer: echoBuffer,
|
||||||
FeatureSet: featureSet,
|
FeatureSet: featureSet,
|
||||||
ListenPort: serverPort,
|
ListenPort: serverPort,
|
||||||
},
|
},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
t.Errorf("failed to start test server: %s", err)
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
@ -2274,13 +2276,13 @@ func TestClient_Mail(t *testing.T) {
|
||||||
t.Errorf("failed to close client: %s", err)
|
t.Errorf("failed to close client: %s", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if err = client.Mail("valid-from@domain.tld"); err == nil {
|
if err = client.Mail("valid-from@domain.tld"); err != nil {
|
||||||
t.Error("server should echo the command as error but didn't")
|
t.Errorf("failed to set mail from address: %s", err)
|
||||||
}
|
}
|
||||||
sent := strings.Replace(err.Error(), "500 ", "", -1)
|
|
||||||
expected := "MAIL FROM:<valid-from@domain.tld> SMTPUTF8"
|
expected := "MAIL FROM:<valid-from@domain.tld> SMTPUTF8"
|
||||||
if !strings.EqualFold(sent, expected) {
|
resp := strings.Split(echoBuffer.String(), "\r\n")
|
||||||
t.Errorf("expected mail from command to be %q, but sent %q", expected, sent)
|
if !strings.EqualFold(resp[5], expected) {
|
||||||
|
t.Errorf("expected mail from command to be %q, but sent %q", expected, resp[5])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
t.Run("from address and server supports DSN", func(t *testing.T) {
|
t.Run("from address and server supports DSN", func(t *testing.T) {
|
||||||
|
@ -2289,11 +2291,12 @@ func TestClient_Mail(t *testing.T) {
|
||||||
PortAdder.Add(1)
|
PortAdder.Add(1)
|
||||||
serverPort := int(TestServerPortBase + PortAdder.Load())
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
featureSet := "250-DSN\r\n250 STARTTLS"
|
featureSet := "250-DSN\r\n250 STARTTLS"
|
||||||
|
echoBuffer := bytes.NewBuffer(nil)
|
||||||
go func() {
|
go func() {
|
||||||
if err := simpleSMTPServer(ctx, t, &serverProps{
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
EchoCommandAsError: "MAIL FROM:",
|
EchoBuffer: echoBuffer,
|
||||||
FeatureSet: featureSet,
|
FeatureSet: featureSet,
|
||||||
ListenPort: serverPort,
|
ListenPort: serverPort,
|
||||||
},
|
},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
t.Errorf("failed to start test server: %s", err)
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
@ -2315,10 +2318,10 @@ func TestClient_Mail(t *testing.T) {
|
||||||
if err = client.Mail("valid-from@domain.tld"); err == nil {
|
if err = client.Mail("valid-from@domain.tld"); err == nil {
|
||||||
t.Error("server should echo the command as error but didn't")
|
t.Error("server should echo the command as error but didn't")
|
||||||
}
|
}
|
||||||
sent := strings.Replace(err.Error(), "500 ", "", -1)
|
|
||||||
expected := "MAIL FROM:<valid-from@domain.tld> RET=FULL"
|
expected := "MAIL FROM:<valid-from@domain.tld> RET=FULL"
|
||||||
if !strings.EqualFold(sent, expected) {
|
resp := strings.Split(echoBuffer.String(), "\r\n")
|
||||||
t.Errorf("expected mail from command to be %q, but sent %q", expected, sent)
|
if !strings.EqualFold(resp[5], expected) {
|
||||||
|
t.Errorf("expected mail from command to be %q, but sent %q", expected, resp[5])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
t.Run("from address and server supports DSN, SMTPUTF8 and 8BITMIME", func(t *testing.T) {
|
t.Run("from address and server supports DSN, SMTPUTF8 and 8BITMIME", func(t *testing.T) {
|
||||||
|
@ -2327,11 +2330,12 @@ func TestClient_Mail(t *testing.T) {
|
||||||
PortAdder.Add(1)
|
PortAdder.Add(1)
|
||||||
serverPort := int(TestServerPortBase + PortAdder.Load())
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
featureSet := "250-DSN\r\n250-8BITMIME\r\n250-SMTPUTF8\r\n250 STARTTLS"
|
featureSet := "250-DSN\r\n250-8BITMIME\r\n250-SMTPUTF8\r\n250 STARTTLS"
|
||||||
|
echoBuffer := bytes.NewBuffer(nil)
|
||||||
go func() {
|
go func() {
|
||||||
if err := simpleSMTPServer(ctx, t, &serverProps{
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
EchoCommandAsError: "MAIL FROM:",
|
EchoBuffer: echoBuffer,
|
||||||
FeatureSet: featureSet,
|
FeatureSet: featureSet,
|
||||||
ListenPort: serverPort,
|
ListenPort: serverPort,
|
||||||
},
|
},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
t.Errorf("failed to start test server: %s", err)
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
@ -2353,10 +2357,10 @@ func TestClient_Mail(t *testing.T) {
|
||||||
if err = client.Mail("valid-from@domain.tld"); err == nil {
|
if err = client.Mail("valid-from@domain.tld"); err == nil {
|
||||||
t.Error("server should echo the command as error but didn't")
|
t.Error("server should echo the command as error but didn't")
|
||||||
}
|
}
|
||||||
sent := strings.Replace(err.Error(), "500 ", "", -1)
|
|
||||||
expected := "MAIL FROM:<valid-from@domain.tld> BODY=8BITMIME SMTPUTF8 RET=FULL"
|
expected := "MAIL FROM:<valid-from@domain.tld> BODY=8BITMIME SMTPUTF8 RET=FULL"
|
||||||
if !strings.EqualFold(sent, expected) {
|
resp := strings.Split(echoBuffer.String(), "\r\n")
|
||||||
t.Errorf("expected mail from command to be %q, but sent %q", expected, sent)
|
if !strings.EqualFold(resp[7], expected) {
|
||||||
|
t.Errorf("expected mail from command to be %q, but sent %q", expected, resp[7])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2428,11 +2432,12 @@ func TestClient_Rcpt(t *testing.T) {
|
||||||
PortAdder.Add(1)
|
PortAdder.Add(1)
|
||||||
serverPort := int(TestServerPortBase + PortAdder.Load())
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
featureSet := "250-DSN\r\n250 STARTTLS"
|
featureSet := "250-DSN\r\n250 STARTTLS"
|
||||||
|
echoBuffer := bytes.NewBuffer(nil)
|
||||||
go func() {
|
go func() {
|
||||||
if err := simpleSMTPServer(ctx, t, &serverProps{
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
EchoCommandAsError: "RCPT TO:",
|
EchoBuffer: echoBuffer,
|
||||||
FeatureSet: featureSet,
|
FeatureSet: featureSet,
|
||||||
ListenPort: serverPort,
|
ListenPort: serverPort,
|
||||||
},
|
},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
t.Errorf("failed to start test server: %s", err)
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
@ -2456,10 +2461,10 @@ func TestClient_Rcpt(t *testing.T) {
|
||||||
if err = client.Rcpt("valid-to@domain.tld"); err == nil {
|
if err = client.Rcpt("valid-to@domain.tld"); err == nil {
|
||||||
t.Error("recpient address with newlines should fail")
|
t.Error("recpient address with newlines should fail")
|
||||||
}
|
}
|
||||||
sent := strings.Replace(err.Error(), "500 ", "", -1)
|
|
||||||
expected := "RCPT TO:<valid-to@domain.tld> NOTIFY=SUCCESS"
|
expected := "RCPT TO:<valid-to@domain.tld> NOTIFY=SUCCESS"
|
||||||
if !strings.EqualFold(sent, expected) {
|
resp := strings.Split(echoBuffer.String(), "\r\n")
|
||||||
t.Errorf("expected rcpt to command to be %q, but sent %q", expected, sent)
|
if !strings.EqualFold(resp[5], expected) {
|
||||||
|
t.Errorf("expected rcpt to command to be %q, but sent %q", expected, resp[5])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2537,6 +2542,45 @@ func TestClient_Data(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSendMail(t *testing.T) {
|
||||||
|
t.Run("full SendMail transaction with TLS and auth", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250-AUTH LOGIN\r\n250-DSN\r\n250 STARTTLS"
|
||||||
|
echoBuffer := bytes.NewBuffer(nil)
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
EchoBuffer: echoBuffer,
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
addr := fmt.Sprintf("%s:%d", TestServerAddr, serverPort)
|
||||||
|
testHookStartTLS = func(config *tls.Config) {
|
||||||
|
testConfig := getTLSConfig(t)
|
||||||
|
config.ServerName = testConfig.ServerName
|
||||||
|
config.RootCAs = testConfig.RootCAs
|
||||||
|
config.Certificates = testConfig.Certificates
|
||||||
|
}
|
||||||
|
auth := LoginAuth("username", "password", TestServerAddr, false)
|
||||||
|
if err := SendMail(addr, auth, "valid-from@domain.tld", []string{"valid-to@domain.tld"},
|
||||||
|
[]byte("test message")); err != nil {
|
||||||
|
t.Fatalf("failed to send mail: %s", err)
|
||||||
|
}
|
||||||
|
resp := strings.Split(echoBuffer.String(), "\r\n")
|
||||||
|
for i, line := range resp {
|
||||||
|
t.Logf("response line %d: %q", i, line)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
func TestBasic(t *testing.T) {
|
func TestBasic(t *testing.T) {
|
||||||
server := strings.Join(strings.Split(basicServer, "\n"), "\r\n")
|
server := strings.Join(strings.Split(basicServer, "\n"), "\r\n")
|
||||||
|
@ -4055,28 +4099,28 @@ func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "
|
||||||
|
|
||||||
// serverProps represents the configuration properties for the SMTP server.
|
// serverProps represents the configuration properties for the SMTP server.
|
||||||
type serverProps struct {
|
type serverProps struct {
|
||||||
EchoCommandAsError string
|
EchoBuffer io.Writer
|
||||||
FailOnAuth bool
|
FailOnAuth bool
|
||||||
FailOnDataInit bool
|
FailOnDataInit bool
|
||||||
FailOnDataClose bool
|
FailOnDataClose bool
|
||||||
FailOnDial bool
|
FailOnDial bool
|
||||||
FailOnEhlo bool
|
FailOnEhlo bool
|
||||||
FailOnHelo bool
|
FailOnHelo bool
|
||||||
FailOnMailFrom bool
|
FailOnMailFrom bool
|
||||||
FailOnNoop bool
|
FailOnNoop bool
|
||||||
FailOnQuit bool
|
FailOnQuit bool
|
||||||
FailOnReset bool
|
FailOnReset bool
|
||||||
FailOnSTARTTLS bool
|
FailOnSTARTTLS bool
|
||||||
FailTemp bool
|
FailTemp bool
|
||||||
FeatureSet string
|
FeatureSet string
|
||||||
ListenPort int
|
ListenPort int
|
||||||
HashFunc func() hash.Hash
|
HashFunc func() hash.Hash
|
||||||
IsSCRAMPlus bool
|
IsSCRAMPlus bool
|
||||||
IsTLS bool
|
IsTLS bool
|
||||||
SupportDSN bool
|
SupportDSN bool
|
||||||
SSLListener bool
|
SSLListener bool
|
||||||
TestSCRAM bool
|
TestSCRAM bool
|
||||||
VRFYUserUnknown bool
|
VRFYUserUnknown bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// simpleSMTPServer starts a simple TCP server that resonds to SMTP commands.
|
// simpleSMTPServer starts a simple TCP server that resonds to SMTP commands.
|
||||||
|
@ -4151,6 +4195,11 @@ func handleTestServerConnection(connection net.Conn, t *testing.T, props *server
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Logf("failed to write line: %s", err)
|
t.Logf("failed to write line: %s", err)
|
||||||
}
|
}
|
||||||
|
if props.EchoBuffer != nil {
|
||||||
|
if _, err := props.EchoBuffer.Write([]byte(data + "\r\n")); err != nil {
|
||||||
|
t.Errorf("failed write to echo buffer: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
_ = writer.Flush()
|
_ = writer.Flush()
|
||||||
}
|
}
|
||||||
writeOK := func() {
|
writeOK := func() {
|
||||||
|
@ -4171,13 +4220,15 @@ func handleTestServerConnection(connection net.Conn, t *testing.T, props *server
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
time.Sleep(time.Millisecond)
|
time.Sleep(time.Millisecond)
|
||||||
|
if props.EchoBuffer != nil {
|
||||||
|
if _, err = props.EchoBuffer.Write([]byte(data)); err != nil {
|
||||||
|
t.Errorf("failed write to echo buffer: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var datastring string
|
var datastring string
|
||||||
data = strings.TrimSpace(data)
|
data = strings.TrimSpace(data)
|
||||||
switch {
|
switch {
|
||||||
case props.EchoCommandAsError != "" && strings.HasPrefix(data, props.EchoCommandAsError):
|
|
||||||
writeLine("500 " + data)
|
|
||||||
break
|
|
||||||
case strings.HasPrefix(data, "HELO"):
|
case strings.HasPrefix(data, "HELO"):
|
||||||
if len(strings.Split(data, " ")) != 2 {
|
if len(strings.Split(data, " ")) != 2 {
|
||||||
writeLine("501 Syntax: HELO hostname")
|
writeLine("501 Syntax: HELO hostname")
|
||||||
|
@ -4260,6 +4311,11 @@ func handleTestServerConnection(connection net.Conn, t *testing.T, props *server
|
||||||
t.Logf("failed to read data from connection: %s", derr)
|
t.Logf("failed to read data from connection: %s", derr)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if props.EchoBuffer != nil {
|
||||||
|
if _, err = props.EchoBuffer.Write([]byte(ddata)); err != nil {
|
||||||
|
t.Errorf("failed write to echo buffer: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
ddata = strings.TrimSpace(ddata)
|
ddata = strings.TrimSpace(ddata)
|
||||||
if ddata == "." {
|
if ddata == "." {
|
||||||
if props.FailOnDataClose {
|
if props.FailOnDataClose {
|
||||||
|
|
Loading…
Reference in a new issue