From 12695385e82981279f7d0bea8e7d88adcee11346 Mon Sep 17 00:00:00 2001 From: Winni Neessen Date: Wed, 23 Oct 2024 17:55:31 +0200 Subject: [PATCH] Refactor SMTP server test setup to use serverProps struct Consolidate server configuration properties into a new serverProps struct for better code clarity and future extensibility. This change involves updating simpleSMTPServer and test cases to use the new struct, allowing more control over server behavior during tests. --- client_test.go | 118 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 93 insertions(+), 25 deletions(-) diff --git a/client_test.go b/client_test.go index 4c7ceb3..e288262 100644 --- a/client_test.go +++ b/client_test.go @@ -1100,7 +1100,7 @@ func TestClient_SetDebugLog(t *testing.T) { serverPort := int(TestServerPortBase + PortAdder.Load()) featureSet := "250-AUTH PLAIN\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8" go func() { - if err := simpleSMTPServer(ctx, featureSet, false, serverPort); err != nil { + if err := simpleSMTPServer(ctx, &serverProps{FeatureSet: featureSet, ListenPort: serverPort}); err != nil { t.Errorf("failed to start test server: %s", err) return } @@ -1482,20 +1482,23 @@ func TestClient_SetLogAuthData(t *testing.T) { } func TestClient_Close(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - PortAdder.Add(1) - serverPort := int(TestServerPortBase + PortAdder.Load()) - featureSet := "250-AUTH PLAIN\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8" - go func() { - if err := simpleSMTPServer(ctx, featureSet, false, serverPort); err != nil { - t.Errorf("failed to start test server: %s", err) - return - } - }() - time.Sleep(time.Millisecond * 300) - t.Run("connect and close the Client", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + PortAdder.Add(1) + serverPort := int(TestServerPortBase + PortAdder.Load()) + featureSet := "250-AUTH PLAIN\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8" + go func() { + if err := simpleSMTPServer(ctx, &serverProps{ + FeatureSet: featureSet, + ListenPort: serverPort, + }); err != nil { + t.Errorf("failed to start test server: %s", err) + return + } + }() + time.Sleep(time.Millisecond * 300) + ctxDial, cancelDial := context.WithTimeout(ctx, time.Millisecond*500) t.Cleanup(cancelDial) @@ -1514,6 +1517,22 @@ func TestClient_Close(t *testing.T) { } }) t.Run("connect and double close the Client", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + PortAdder.Add(1) + serverPort := int(TestServerPortBase + PortAdder.Load()) + featureSet := "250-AUTH PLAIN\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8" + go func() { + if err := simpleSMTPServer(ctx, &serverProps{ + FeatureSet: featureSet, + ListenPort: serverPort, + }); err != nil { + t.Errorf("failed to start test server: %s", err) + return + } + }() + time.Sleep(time.Millisecond * 300) + ctxDial, cancelDial := context.WithTimeout(ctx, time.Millisecond*500) t.Cleanup(cancelDial) @@ -1534,6 +1553,41 @@ func TestClient_Close(t *testing.T) { t.Errorf("failed to close the client: %s", err) } }) + t.Run("test server will let close fail", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + PortAdder.Add(1) + serverPort := int(TestServerPortBase + PortAdder.Load()) + featureSet := "250-AUTH PLAIN\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8" + go func() { + if err := simpleSMTPServer(ctx, &serverProps{ + FailOnQuit: true, + FeatureSet: featureSet, + ListenPort: serverPort, + }); err != nil { + t.Errorf("failed to start test server: %s", err) + return + } + }() + time.Sleep(time.Millisecond * 300) + + ctxDial, cancelDial := context.WithTimeout(ctx, time.Millisecond*500) + t.Cleanup(cancelDial) + + client, err := NewClient(DefaultHost, WithPort(serverPort), WithTLSPolicy(NoTLS)) + if err != nil { + t.Fatalf("failed to create new client: %s", err) + } + if err = client.DialWithContext(ctxDial); err != nil { + t.Fatalf("failed to connect to the test server: %s", err) + } + if !client.smtpClient.HasConnection() { + t.Fatalf("client has no connection") + } + if err = client.Close(); err == nil { + t.Errorf("close was supposed to fail, but didn't") + } + }) } func TestClient_DialWithContext(t *testing.T) { @@ -1543,7 +1597,7 @@ func TestClient_DialWithContext(t *testing.T) { serverPort := int(TestServerPortBase + PortAdder.Load()) featureSet := "250-AUTH PLAIN\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8" go func() { - if err := simpleSMTPServer(ctx, featureSet, false, serverPort); err != nil { + if err := simpleSMTPServer(ctx, &serverProps{FeatureSet: featureSet, ListenPort: serverPort}); err != nil { t.Errorf("failed to start test server: %s", err) return } @@ -3630,11 +3684,21 @@ func parseJSONLog(t *testing.T, buf *bytes.Buffer) logData { return logdata } +type serverProps struct { + FailOnReset bool + FailOnQuit bool + FeatureSet string + ListenPort int +} + // simpleSMTPServer starts a simple TCP server that resonds to SMTP commands. // The provided featureSet represents in what the server responds to EHLO command // failReset controls if a RSET succeeds -func simpleSMTPServer(ctx context.Context, featureSet string, failReset bool, port int) error { - listener, err := net.Listen(TestServerProto, fmt.Sprintf("%s:%d", TestServerAddr, port)) +func simpleSMTPServer(ctx context.Context, props *serverProps) error { + if props == nil { + return fmt.Errorf("no server properties provided") + } + listener, err := net.Listen(TestServerProto, fmt.Sprintf("%s:%d", TestServerAddr, props.ListenPort)) if err != nil { return fmt.Errorf("unable to listen on %s://%s: %w", TestServerProto, TestServerAddr, err) } @@ -3659,12 +3723,12 @@ func simpleSMTPServer(ctx context.Context, featureSet string, failReset bool, po } return fmt.Errorf("unable to accept connection: %w", err) } - handleTestServerConnection(connection, featureSet, failReset) + handleTestServerConnection(connection, props) } } } -func handleTestServerConnection(connection net.Conn, featureSet string, failReset bool) { +func handleTestServerConnection(connection net.Conn, props *serverProps) { defer func() { if err := connection.Close(); err != nil { fmt.Printf("unable to close connection: %s\n", err) @@ -3698,7 +3762,7 @@ func handleTestServerConnection(connection net.Conn, featureSet string, failRese fmt.Printf("expected EHLO, got %q", data) return } - if err = writeLine("250-localhost.localdomain\r\n" + featureSet); err != nil { + if err = writeLine("250-localhost.localdomain\r\n" + props.FeatureSet); err != nil { return } @@ -3748,19 +3812,19 @@ func handleTestServerConnection(connection net.Conn, featureSet string, failRese var username, password string userResp := "VXNlcm5hbWU6" passResp := "UGFzc3dvcmQ6" - if strings.Contains(featureSet, "250-X-MOX-LOGIN") { + if strings.Contains(props.FeatureSet, "250-X-MOX-LOGIN") { userResp = "" passResp = "UGFzc3dvcmQ=" } - if strings.Contains(featureSet, "250-X-NULLBYTE-LOGIN") { + if strings.Contains(props.FeatureSet, "250-X-NULLBYTE-LOGIN") { userResp = "VXNlciBuYW1lAA==" passResp = "UGFzc3dvcmQA" } - if strings.Contains(featureSet, "250-X-BOGUS-LOGIN") { + if strings.Contains(props.FeatureSet, "250-X-BOGUS-LOGIN") { userResp = "Qm9ndXM=" passResp = "Qm9ndXM=" } - if strings.Contains(featureSet, "250-X-EMPTY-LOGIN") { + if strings.Contains(props.FeatureSet, "250-X-EMPTY-LOGIN") { userResp = "" passResp = "" } @@ -3816,12 +3880,16 @@ func handleTestServerConnection(connection net.Conn, featureSet string, failRese strings.EqualFold(data, "vrfy"): writeOK() case strings.EqualFold(data, "rset"): - if failReset { + if props.FailOnReset { _ = writeLine("500 5.1.2 Error: reset failed") break } writeOK() case strings.EqualFold(data, "quit"): + if props.FailOnQuit { + _ = writeLine("500 5.1.2 Error: quit failed") + break + } _ = writeLine("221 2.0.0 Bye") default: _ = writeLine("500 5.5.2 Error: bad syntax")