Refactor senderror_test.go for improved test clarity

Consolidated multiple duplicate test cases into grouped sub-tests with clear names. This enhances readability and maintainability, ensures proper test isolation, and removes redundant code.
This commit is contained in:
Winni Neessen 2024-11-01 15:58:11 +01:00
parent 25b7f81e3b
commit e37dd39654
Signed by: wneessen
GPG key ID: 385AC9889632126E

View file

@ -6,17 +6,17 @@ package mail
import ( import (
"errors" "errors"
"fmt"
"strings" "strings"
"testing" "testing"
) )
// TestSendError_Error tests the SendError and SendErrReason error handling methods // TestSendError_Error tests the SendError and SendErrReason error handling methods
func TestSendError_Error(t *testing.T) { func TestSendError_Error(t *testing.T) {
tl := []struct { t.Run("TestSendError_Error with various reasons", func(t *testing.T) {
n string tests := []struct {
r SendErrReason name string
te bool reason SendErrReason
isTemp bool
}{ }{
{"ErrGetSender/temp", ErrGetSender, true}, {"ErrGetSender/temp", ErrGetSender, true},
{"ErrGetSender/perm", ErrGetSender, false}, {"ErrGetSender/perm", ErrGetSender, false},
@ -43,126 +43,173 @@ func TestSendError_Error(t *testing.T) {
{"Unknown/temp", 9999, true}, {"Unknown/temp", 9999, true},
{"Unknown/perm", 9999, false}, {"Unknown/perm", 9999, false},
} }
for _, tt := range tests {
for _, tt := range tl { t.Run(tt.name, func(t *testing.T) {
t.Run(tt.n, func(t *testing.T) { err := returnSendError(tt.reason, tt.isTemp)
if err := returnSendError(tt.r, tt.te); err != nil { if err == nil {
exp := &SendError{Reason: tt.r, isTemp: tt.te} t.Fatalf("error expected, got nil")
if !errors.Is(err, exp) {
t.Errorf("error mismatch, expected: %s (temp: %t), got: %s (temp: %t)", tt.r, tt.te,
exp.Error(), exp.isTemp)
} }
if !strings.Contains(fmt.Sprintf("%s", err), tt.r.String()) { want := &SendError{Reason: tt.reason, isTemp: tt.isTemp}
if !errors.Is(err, want) {
t.Errorf("error mismatch, expected: %s (temp: %t), got: %s (temp: %t)",
tt.reason, tt.isTemp, want.Error(), want.isTemp)
}
if !strings.Contains(err.Error(), tt.reason.String()) {
t.Errorf("error string mismatch, expected: %s, got: %s", t.Errorf("error string mismatch, expected: %s, got: %s",
tt.r.String(), fmt.Sprintf("%s", err)) tt.reason.String(), err.Error())
}
} }
}) })
} }
})
t.Run("TestSendError_Error with multiple errors", func(t *testing.T) {
message := testMessage(t)
err := &SendError{
affectedMsg: message,
errlist: []error{ErrNoRcptAddresses, ErrNoFromAddress},
rcpt: []string{"<toni.tester@domain.tld>", "<tina.tester@domain.tld>"},
Reason: ErrAmbiguous,
}
if !strings.Contains(err.Error(), "ambiguous reason, check Msg.SendError for message specific reasons") {
t.Errorf("error string mismatch, expected: ambiguous reason, check Msg.SendError for message "+
"specific reasons, got: %s", err.Error())
}
if !strings.Contains(err.Error(), "no recipient addresses set, no FROM address set") {
t.Errorf("error string mismatch, expected: no recipient addresses set, no FROM address set, got: %s",
err.Error())
}
if !strings.Contains(err.Error(), "affected recipient(s): <toni.tester@domain.tld>, "+
"<tina.tester@domain.tld>") {
t.Errorf("error string mismatch, expected: affected recipient(s): <toni.tester@domain.tld>, "+
"<tina.tester@domain.tld>, got: %s", err.Error())
}
})
}
func TestSendError_Is(t *testing.T) {
t.Run("TestSendError_Is errors match", func(t *testing.T) {
err1 := returnSendError(ErrAmbiguous, false)
err2 := returnSendError(ErrAmbiguous, false)
if !errors.Is(err1, err2) {
t.Error("error mismatch, expected ErrAmbiguous to be equal to ErrAmbiguous")
}
})
t.Run("TestSendError_Is errors mismatch", func(t *testing.T) {
err1 := returnSendError(ErrAmbiguous, false)
err2 := returnSendError(ErrSMTPMailFrom, false)
if errors.Is(err1, err2) {
t.Error("error mismatch, ErrAmbiguous should not be equal to ErrSMTPMailFrom")
}
})
t.Run("TestSendError_Is on nil", func(t *testing.T) {
var err *SendError
if err.Is(ErrNoFromAddress) {
t.Error("expected false on nil-senderror")
}
})
} }
func TestSendError_IsTemp(t *testing.T) { func TestSendError_IsTemp(t *testing.T) {
var se *SendError t.Run("TestSendError_IsTemp is true", func(t *testing.T) {
err1 := returnSendError(ErrAmbiguous, true) err := returnSendError(ErrAmbiguous, true)
if !errors.As(err1, &se) { if err == nil {
t.Errorf("error mismatch, expected error to be of type *SendError") t.Fatalf("error expected, got nil")
return
} }
if errors.As(err1, &se) && !se.IsTemp() { var sendErr *SendError
t.Errorf("error mismatch, expected temporary error") if !errors.As(err, &sendErr) {
return t.Fatal("error expected to be of type *SendError")
} }
err2 := returnSendError(ErrAmbiguous, false) if !sendErr.IsTemp() {
if !errors.As(err2, &se) { t.Errorf("expected temporary error, got: temperr: %t", sendErr.IsTemp())
t.Errorf("error mismatch, expected error to be of type *SendError")
return
} }
if errors.As(err2, &se) && se.IsTemp() { })
t.Errorf("error mismatch, expected non-temporary error") t.Run("TestSendError_IsTemp is false", func(t *testing.T) {
return err := returnSendError(ErrAmbiguous, false)
if err == nil {
t.Fatalf("error expected, got nil")
} }
} var sendErr *SendError
if !errors.As(err, &sendErr) {
func TestSendError_IsTempNil(t *testing.T) { t.Fatal("error expected to be of type *SendError")
}
if sendErr.IsTemp() {
t.Errorf("expected permanent error, got: temperr: %t", sendErr.IsTemp())
}
})
t.Run("TestSendError_IsTemp is nil", func(t *testing.T) {
var se *SendError var se *SendError
if se.IsTemp() { if se.IsTemp() {
t.Error("expected false on nil-senderror") t.Error("expected false on nil-senderror")
} }
})
} }
func TestSendError_MessageID(t *testing.T) { func TestSendError_MessageID(t *testing.T) {
var se *SendError t.Run("TestSendError_MessageID message ID is set", func(t *testing.T) {
var sendErr *SendError
err := returnSendError(ErrAmbiguous, false) err := returnSendError(ErrAmbiguous, false)
if !errors.As(err, &se) { if !errors.As(err, &sendErr) {
t.Errorf("error mismatch, expected error to be of type *SendError") t.Fatal("error mismatch, expected error to be of type *SendError")
return
} }
if errors.As(err, &se) { if sendErr.MessageID() == "" {
if se.MessageID() == "" { t.Error("sendError expected message-id, but got empty string")
t.Errorf("sendError expected message-id, but got empty string")
} }
if !strings.EqualFold(se.MessageID(), "<this.is.a.message.id>") { if !strings.EqualFold(sendErr.MessageID(), "<this.is.a.message.id>") {
t.Errorf("sendError message-id expected: %s, but got: %s", "<this.is.a.message.id>", t.Errorf("sendError message-id expected: %s, but got: %s", "<this.is.a.message.id>",
se.MessageID()) sendErr.MessageID())
} }
})
t.Run("TestSendError_MessageID message ID is not set", func(t *testing.T) {
var sendErr *SendError
message := testMessage(t)
err := &SendError{
affectedMsg: message,
errlist: []error{ErrNoRcptAddresses},
rcpt: []string{"<toni.tester@domain.tld>", "<tina.tester@domain.tld>"},
Reason: ErrAmbiguous,
} }
} if !errors.As(err, &sendErr) {
t.Fatal("error mismatch, expected error to be of type *SendError")
func TestSendError_MessageIDNil(t *testing.T) {
var se *SendError
if se.MessageID() != "" {
t.Error("expected empty string on nil-senderror")
} }
if sendErr.MessageID() != "" {
t.Errorf("sendError expected empty message-id, got: %s", sendErr.MessageID())
}
})
} }
func TestSendError_Msg(t *testing.T) { func TestSendError_Msg(t *testing.T) {
var se *SendError t.Run("TestSendError_Msg message is set", func(t *testing.T) {
var sendErr *SendError
err := returnSendError(ErrAmbiguous, false) err := returnSendError(ErrAmbiguous, false)
if !errors.As(err, &se) { if !errors.As(err, &sendErr) {
t.Errorf("error mismatch, expected error to be of type *SendError") t.Fatal("error mismatch, expected error to be of type *SendError")
return
} }
if errors.As(err, &se) { msg := sendErr.Msg()
if se.Msg() == nil { if msg == nil {
t.Errorf("sendError expected msg pointer, but got nil") t.Fatalf("sendError expected msg pointer, but got nil")
} }
from := se.Msg().GetFromString() from := msg.GetFromString()
if len(from) == 0 { if len(from) == 0 {
t.Errorf("sendError expected msg from, but got empty string") t.Fatal("sendError expected msg from, but got empty string")
return
} }
if !strings.EqualFold(from[0], "<toni.tester@domain.tld>") { if !strings.EqualFold(from[0], "<toni.tester@domain.tld>") {
t.Errorf("sendError message from expected: %s, but got: %s", "<toni.tester@domain.tld>", t.Errorf("sendError message from expected: %s, but got: %s", "<toni.tester@domain.tld>",
from[0]) from[0])
} }
} })
} t.Run("TestSendError_Msg message is not set", func(t *testing.T) {
var sendErr *SendError
func TestSendError_MsgNil(t *testing.T) {
var se *SendError
if se.Msg() != nil {
t.Error("expected nil on nil-senderror")
}
}
func TestSendError_IsFail(t *testing.T) {
err1 := returnSendError(ErrAmbiguous, false)
err2 := returnSendError(ErrSMTPMailFrom, false)
if errors.Is(err1, err2) {
t.Errorf("error mismatch, ErrAmbiguous should not be equal to ErrSMTPMailFrom")
}
}
func TestSendError_ErrorMulti(t *testing.T) {
expected := `ambiguous reason, check Msg.SendError for message specific reasons, ` +
`affected recipient(s): <email1@domain.tld>, <email2@domain.tld>`
err := &SendError{ err := &SendError{
Reason: ErrAmbiguous, isTemp: false, affectedMsg: nil, errlist: []error{ErrNoRcptAddresses},
rcpt: []string{"<email1@domain.tld>", "<email2@domain.tld>"}, rcpt: []string{"<toni.tester@domain.tld>", "<tina.tester@domain.tld>"},
Reason: ErrAmbiguous,
} }
if err.Error() != expected { if !errors.As(err, &sendErr) {
t.Errorf("error mismatch, expected: %s, got: %s", expected, err.Error()) t.Fatal("error mismatch, expected error to be of type *SendError")
} }
if sendErr.Msg() != nil {
t.Errorf("sendError expected nil msg pointer, got: %v", sendErr.Msg())
}
})
} }
// returnSendError is a helper method to retunr a SendError with a specific reason // returnSendError is a helper method to retunr a SendError with a specific reason
@ -173,6 +220,5 @@ func returnSendError(r SendErrReason, t bool) error {
message.Subject("This is the subject") message.Subject("This is the subject")
message.SetBodyString(TypeTextPlain, "This is the message body") message.SetBodyString(TypeTextPlain, "This is the message body")
message.SetMessageIDWithValue("this.is.a.message.id") message.SetMessageIDWithValue("this.is.a.message.id")
return &SendError{Reason: r, isTemp: t, affectedMsg: message} return &SendError{Reason: r, isTemp: t, affectedMsg: message}
} }