Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Golang smtpmock v2.3.3 #193

Merged
merged 6 commits into from
Nov 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/.goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version: 2

builds:
- id: "smtpmock-build"
dir: cmd
dir: cmd/smtpmock
binary: smtpmock
env:
- CGO_ENABLED=0
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.3.3] - 2024-11-17

### Fixed

- Fixed issue with [invalid name with email address parsing](https://github.com/mocktools/go-smtp-mock/issues/153) for `MAIL FROM` and `RCPT TO` commands
- Fixed flaky tests

### Updated

- Updated `cmd` namespace
- Updated `goreleaser` config

## [2.3.2] - 2024-11-14

### Fixed
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
22 changes: 11 additions & 11 deletions consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,20 @@ const (
// Regex patterns
availableCmdsRegexPattern = `(?i)helo|ehlo|mail from:|rcpt to:|data|rset|noop|quit`
domainRegexPattern = `(?i)([\p{L}0-9]+([\-.]{1}[\p{L}0-9]+)*\.\p{L}{2,63}|localhost)`
emailRegexPattern = `(?i)<?([\p{L}0-9][\p{L}0-9\-.]*[\p{L}0-9]+@` + domainRegexPattern + `)>?`
emailRegexPattern = `(?i)(?:[\p{L}\p{N}\s]*?<?)*?([a-zA-Z0-9][-a-zA-Z0-9.]*[a-zA-Z0-9]@` + domainRegexPattern + `)>*`
ipAddressRegexPattern = `(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}`
addressLiteralRegexPattern = `|\[` + ipAddressRegexPattern + `\]`

validHeloCmdsRegexPattern = `(?i)helo|ehlo`
validMailfromCmdRegexPattern = `(?i)mail from:`
validRcpttoCmdRegexPattern = `(?i)rcpt to:`
validDataCmdRegexPattern = `\A(?i)data\z`
validRsetCmdRegexPattern = `\A(?i)rset\z`
validNoopCmdRegexPattern = `\A(?i)noop\z`
validQuitCmdRegexPattern = `\A(?i)quit\z`
validHeloComplexCmdRegexPattern = `\A(` + validHeloCmdsRegexPattern + `) (` + domainRegexPattern + `|` + ipAddressRegexPattern + addressLiteralRegexPattern + `)\z`
validMailromComplexCmdRegexPattern = `\A(` + validMailfromCmdRegexPattern + `) ?(` + emailRegexPattern + `)\z`
validRcpttoComplexCmdRegexPattern = `\A(` + validRcpttoCmdRegexPattern + `) ?(` + emailRegexPattern + `)\z`
validHeloCmdsRegexPattern = `(?i)helo|ehlo`
validMailfromCmdRegexPattern = `(?i)mail from:`
validRcpttoCmdRegexPattern = `(?i)rcpt to:`
validDataCmdRegexPattern = `\A(?i)data\z`
validRsetCmdRegexPattern = `\A(?i)rset\z`
validNoopCmdRegexPattern = `\A(?i)noop\z`
validQuitCmdRegexPattern = `\A(?i)quit\z`
validHeloComplexCmdRegexPattern = `\A(` + validHeloCmdsRegexPattern + `) (` + domainRegexPattern + `|` + ipAddressRegexPattern + addressLiteralRegexPattern + `)\z`
validMailfromComplexCmdRegexPattern = `\A(` + validMailfromCmdRegexPattern + `)\s*` + emailRegexPattern + `\z`
validRcpttoComplexCmdRegexPattern = `\A(` + validRcpttoCmdRegexPattern + `)\s*` + emailRegexPattern + `\z`

// Helpers
emptyString = ""
Expand Down
4 changes: 2 additions & 2 deletions handler_mailfrom.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (handler *handlerMailfrom) isInvalidCmdSequence(request string) bool {
// Invalid MAILFROM command argument predicate. Returns true and writes result for case when
// MAILFROM command argument is invalid, otherwise returns false
func (handler *handlerMailfrom) isInvalidCmdArg(request string) bool {
if !matchRegex(request, validMailromComplexCmdRegexPattern) {
if !matchRegex(request, validMailfromComplexCmdRegexPattern) {
return handler.writeResult(false, request, handler.configuration.msgInvalidCmdMailfromArg)
}

Expand All @@ -71,7 +71,7 @@ func (handler *handlerMailfrom) isInvalidCmdArg(request string) bool {

// Returns email from MAILFROM request
func (handler *handlerMailfrom) mailfromEmail(request string) string {
return regexCaptureGroup(request, validMailromComplexCmdRegexPattern, 3)
return regexCaptureGroup(request, validMailfromComplexCmdRegexPattern, 2)
}

// Custom behavior for MAILFROM email. Returns true and writes result for case when
Expand Down
18 changes: 13 additions & 5 deletions handler_mailfrom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,20 +226,28 @@ func TestHandlerMaifromIsInvalidCmdArg(t *testing.T) {
}

func TestHandlerMailfromMailfromEmail(t *testing.T) {
handler := new(handlerMailfrom)
validEmail, handler := "user@example.com", new(handlerMailfrom)

t.Run("when request includes valid email address without <> sign", func(t *testing.T) {
validEmail := "user@example.com"

assert.Equal(t, validEmail, handler.mailfromEmail("MAIL FROM: "+validEmail))
})

t.Run("when request includes valid email address with <> sign", func(t *testing.T) {
validEmail := "user@example.com"

assert.Equal(t, validEmail, handler.mailfromEmail("MAIL FROM: "+"<"+validEmail+">"))
})

t.Run("when request includes valid email address without <> sign, with name", func(t *testing.T) {
assert.Equal(t, validEmail, handler.mailfromEmail("MAIL FROM: John Doe <"+validEmail+">"))
})

t.Run("when request includes valid email address with <> sign, name with space", func(t *testing.T) {
assert.Equal(t, validEmail, handler.mailfromEmail("MAIL FROM: "+"<John Doe <"+validEmail+">>"))
})

t.Run("when request includes valid email address with <> sign and name without space", func(t *testing.T) {
assert.Equal(t, validEmail, handler.mailfromEmail("MAIL FROM: "+"<JohnDoe<"+validEmail+">>"))
})

t.Run("when request includes invalid email address", func(t *testing.T) {
invalidEmail := "user@invalid"

Expand Down
2 changes: 1 addition & 1 deletion handler_rcptto.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func (handler *handlerRcptto) isInvalidCmdArg(request string) bool {

// Returns email from RCPTTO request
func (handler *handlerRcptto) rcpttoEmail(request string) string {
return regexCaptureGroup(request, validRcpttoComplexCmdRegexPattern, 3)
return regexCaptureGroup(request, validRcpttoComplexCmdRegexPattern, 2)
}

// Custom behavior for RCPTTO email. Returns true and writes result for case when
Expand Down
18 changes: 13 additions & 5 deletions handler_rcptto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,20 +326,28 @@ func TestHandlerRcpttoIsInvalidCmdArg(t *testing.T) {
}

func TestHandlerRcpttoRcpttoEmail(t *testing.T) {
handler := new(handlerRcptto)
validEmail, handler := "user@example.com", new(handlerRcptto)

t.Run("when request includes valid email address without <> sign", func(t *testing.T) {
validEmail := "user@example.com"

assert.Equal(t, validEmail, handler.rcpttoEmail("RCPT TO: "+validEmail))
})

t.Run("when request includes valid email address with <> sign", func(t *testing.T) {
validEmail := "user@example.com"

assert.Equal(t, validEmail, handler.rcpttoEmail("RCPT TO: "+"<"+validEmail+">"))
})

t.Run("when request includes valid email address without <> sign, with name", func(t *testing.T) {
assert.Equal(t, validEmail, handler.rcpttoEmail("RCPT TO: John Doe <"+validEmail+">"))
})

t.Run("when request includes valid email address with <> sign, name with space", func(t *testing.T) {
assert.Equal(t, validEmail, handler.rcpttoEmail("RCPT TO: "+"<John Doe <"+validEmail+">>"))
})

t.Run("when request includes valid email address with <> sign and name without space", func(t *testing.T) {
assert.Equal(t, validEmail, handler.rcpttoEmail("RCPT TO: "+"<JohnDoe<"+validEmail+">>"))
})

t.Run("when request includes invalid email address", func(t *testing.T) {
invalidEmail := "user@invalid"

Expand Down
10 changes: 8 additions & 2 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package smtpmock
import (
"fmt"
"regexp"
"time"
)

// Regex builder
Expand All @@ -20,8 +21,8 @@ func matchRegex(strContext, regexPattern string) bool {
return regex.MatchString(strContext)
}

// Returns string by regex pattern capture group index. For cases when regex not matched or
// capture group not found returns empty string
// Returns string by regex pattern capture group index.
// For cases when regex not matched or capture group not found returns empty string
func regexCaptureGroup(str string, regexPattern string, captureGroup int) (capturedString string) {
var regex *regexp.Regexp
defer func() { _ = recover() }()
Expand All @@ -47,3 +48,8 @@ func isIncluded(slice []string, target string) bool {
func serverWithPortNumber(server string, portNumber int) string {
return fmt.Sprintf("%s:%d", server, portNumber)
}

// Sleeps for the given duration in milliseconds
func sleepMilliseconds(duration int) {
time.Sleep(time.Duration(duration) * time.Millisecond)
}
8 changes: 5 additions & 3 deletions smtpmock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package smtpmock
import (
"fmt"
"testing"
"time"

"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -154,9 +153,12 @@ func TestNew(t *testing.T) {
assert.False(t, server.isStarted())

assert.NoError(t, server.Start())
sleepMilliseconds(100)
assert.True(t, server.isStarted())
_ = runSuccessfulSMTPSession(configuration.hostAddress, server.PortNumber(), true, 0)
sleepMilliseconds(100)
_ = server.Stop()
sleepMilliseconds(100)

assert.Equal(t, 2, len(server.Messages()))
assert.NotNil(t, server.quit)
Expand Down Expand Up @@ -221,11 +223,11 @@ func TestServerMessagesRaceCondition(t *testing.T) {
}()

// ensure that server.MessagesAndPurge() doesn't touch messages from active SMTP-session
time.Sleep(5 * time.Millisecond)
sleepMilliseconds(5)
assert.Empty(t, server.MessagesAndPurge())

// ensure that messages appears after SMTP-session
time.Sleep(100 * time.Millisecond)
sleepMilliseconds(100)
assert.Len(t, server.Messages(), 1)

if err := server.Stop(); err != nil {
Expand Down