Compare commits

..

No commits in common. "c28bd7e33193f896d6e88d5d58cb30683483a763" and "e7e0fe03bbce4921675e6ccaccf176cb6a812ead" have entirely different histories.

6 changed files with 1201 additions and 1584 deletions

View file

@ -31,8 +31,8 @@ jobs:
go: ['1.23'] go: ['1.23']
env: env:
PERFORM_ONLINE_TEST: ${{ vars.PERFORM_ONLINE_TEST }} PERFORM_ONLINE_TEST: ${{ vars.PERFORM_ONLINE_TEST }}
PERFORM_UNIX_OPEN_WRITE_TESTS: ${{ vars.PERFORM_UNIX_OPEN_WRITE_TESTS }} PERFORM_UNIX_OPEN_WRITE_TESTS: "true"
PERFORM_SENDMAIL_TESTS: ${{ vars.PERFORM_SENDMAIL_TESTS }} TEST_SENDMAIL: ${{ vars.TEST_SENDMAIL }}
TEST_HOST: ${{ secrets.TEST_HOST }} TEST_HOST: ${{ secrets.TEST_HOST }}
TEST_USER: ${{ secrets.TEST_USER }} TEST_USER: ${{ secrets.TEST_USER }}
TEST_PASS: ${{ secrets.TEST_PASS }} TEST_PASS: ${{ secrets.TEST_PASS }}

View file

@ -3559,7 +3559,6 @@ type serverProps struct {
FailOnQuit bool FailOnQuit bool
FailOnReset bool FailOnReset bool
FailOnSTARTTLS bool FailOnSTARTTLS bool
FailTemp bool
FeatureSet string FeatureSet string
ListenPort int ListenPort int
SSLListener bool SSLListener bool
@ -3721,10 +3720,6 @@ func handleTestServerConnection(connection net.Conn, t *testing.T, props *server
writeLine("500 5.0.0 Error during DATA transmission") writeLine("500 5.0.0 Error during DATA transmission")
break break
} }
if props.FailTemp {
writeLine("451 4.3.0 Error: fail on DATA close")
break
}
writeLine("250 2.0.0 Ok: queued as 1234567890") writeLine("250 2.0.0 Ok: queued as 1234567890")
break break
} }

View file

@ -6,17 +6,17 @@ coverage:
status: status:
project: project:
default: default:
target: 90% target: 85%
threshold: 2% threshold: 5%
base: auto base: auto
if_ci_failed: error if_ci_failed: error
only_pulls: false only_pulls: false
patch: patch:
default: default:
target: 90% target: 80%
base: auto base: auto
if_ci_failed: error if_ci_failed: error
threshold: 2% threshold: 5%
comment: comment:
require_changes: true require_changes: true

4
msg.go
View file

@ -2371,8 +2371,8 @@ func (m *Msg) WriteToSendmailWithContext(ctx context.Context, sendmailPath strin
// - https://datatracker.ietf.org/doc/html/rfc5322 // - https://datatracker.ietf.org/doc/html/rfc5322
func (m *Msg) NewReader() *Reader { func (m *Msg) NewReader() *Reader {
reader := &Reader{} reader := &Reader{}
buffer := bytes.NewBuffer(nil) buffer := bytes.Buffer{}
_, err := m.Write(buffer) _, err := m.Write(&buffer)
if err != nil { if err != nil {
reader.err = fmt.Errorf("failed to write Msg to Reader buffer: %w", err) reader.err = fmt.Errorf("failed to write Msg to Reader buffer: %w", err)
} }

File diff suppressed because it is too large Load diff

View file

@ -9,9 +9,11 @@ package mail
import ( import (
"bytes" "bytes"
"context"
"errors" "errors"
"os" "os"
"testing" "testing"
"time"
) )
func TestMsg_AttachFile_unixOnly(t *testing.T) { func TestMsg_AttachFile_unixOnly(t *testing.T) {
@ -20,7 +22,7 @@ func TestMsg_AttachFile_unixOnly(t *testing.T) {
t.Skipf("PERFORM_UNIX_OPEN_WRITE_TESTS variable is not set. Skipping unix open/write tests") t.Skipf("PERFORM_UNIX_OPEN_WRITE_TESTS variable is not set. Skipping unix open/write tests")
} }
tempFile, err := os.CreateTemp("", "attachfile-open-write-test.*.txt") tempFile, err := os.CreateTemp("testdata/tmp", "attachfile-open-write-test.*.txt")
if err != nil { if err != nil {
t.Fatalf("failed to create temp file: %s", err) t.Fatalf("failed to create temp file: %s", err)
} }
@ -51,76 +53,134 @@ func TestMsg_AttachFile_unixOnly(t *testing.T) {
t.Errorf("expected error to be %s, got: %s", os.ErrPermission, err) t.Errorf("expected error to be %s, got: %s", os.ErrPermission, err)
} }
}) })
} t.Run("AttachFile with fileFromFS fails on copy", func(t *testing.T) {
tempfile, err := os.CreateTemp("testdata/tmp", "attachfile-close-early.*.txt")
func TestMsg_EmbedFile_unixOnly(t *testing.T) {
t.Run("EmbedFile with fileFromFS fails on open", func(t *testing.T) {
if os.Getenv("PERFORM_UNIX_OPEN_WRITE_TESTS") != "true" {
t.Skipf("PERFORM_UNIX_OPEN_WRITE_TESTS variable is not set. Skipping unix open/write tests")
}
tempFile, err := os.CreateTemp("", "embedfile-open-write-test.*.txt")
if err != nil { if err != nil {
t.Fatalf("failed to create temp file: %s", err) t.Fatalf("failed to create temp file: %s", err)
} }
t.Cleanup(func() { t.Cleanup(func() {
if err := os.Remove(tempFile.Name()); err != nil { if err := os.Remove(tempfile.Name()); err != nil {
t.Errorf("failed to remove temp file: %s", err) t.Errorf("failed to remove temp file: %s", err)
} }
}) })
if err = os.Chmod(tempFile.Name(), 0o000); err != nil {
t.Fatalf("failed to chmod temp file: %s", err)
}
message := NewMsg() message := NewMsg()
if message == nil { if message == nil {
t.Fatal("message is nil") t.Fatal("message is nil")
} }
message.EmbedFile(tempFile.Name()) message.AttachFile("testdata/attachment.txt")
embeds := message.GetEmbeds() attachments := message.GetAttachments()
if len(embeds) != 1 { if len(attachments) != 1 {
t.Fatalf("failed to get embeds, expected 1, got: %d", len(embeds)) t.Fatalf("failed to get attachments, expected 1, got: %d", len(attachments))
} }
messageBuf := bytes.NewBuffer(nil) messageBuf, err := os.Open(tempfile.Name())
_, err = embeds[0].Writer(messageBuf) if err != nil {
t.Fatalf("failed to open temp file: %s", err)
}
// We close early to cause an error during io.Copy
if err = messageBuf.Close(); err != nil {
t.Fatalf("failed to close temp file: %s", err)
}
_, err = attachments[0].Writer(messageBuf)
if err == nil { if err == nil {
t.Error("writer func expected to fail, but didn't") t.Error("writer func expected to fail, but didn't")
} }
if !errors.Is(err, os.ErrPermission) {
t.Errorf("expected error to be %s, got: %s", os.ErrPermission, err)
}
}) })
} }
func TestMsg_WriteToFile_unixOnly(t *testing.T) { func TestMsg_AttachReader_unixOnly(t *testing.T) {
t.Run("WriteToFile fails on create", func(t *testing.T) { t.Run("AttachReader with fileFromReader fails on copy", func(t *testing.T) {
if os.Getenv("PERFORM_UNIX_OPEN_WRITE_TESTS") != "true" { tempfile, err := os.CreateTemp("", "attachfile-close-early.*.txt")
t.Skipf("PERFORM_UNIX_OPEN_WRITE_TESTS variable is not set. Skipping unix open/write tests")
}
tempfile, err := os.CreateTemp("", "testmail-create.*.eml")
if err != nil { if err != nil {
t.Fatalf("failed to create temp file: %s", err) t.Fatalf("failed to create temp file: %s", err)
} }
if err = os.Chmod(tempfile.Name(), 0o000); err != nil {
t.Fatalf("failed to chmod temp file: %s", err)
}
t.Cleanup(func() { t.Cleanup(func() {
if err = tempfile.Close(); err != nil { if err := os.Remove(tempfile.Name()); err != nil {
t.Fatalf("failed to close temp file: %s", err) t.Errorf("failed to remove temp file: %s", err)
}
if err = os.Remove(tempfile.Name()); err != nil {
t.Fatalf("failed to remove temp file: %s", err)
} }
}) })
message := testMessage(t) message := NewMsg()
if err = message.WriteToFile(tempfile.Name()); err == nil { if message == nil {
t.Errorf("expected error, got nil") t.Fatal("message is nil")
}
file, err := os.Open("testdata/attachment.txt")
if err != nil {
t.Fatalf("failed to open file: %s", err)
}
t.Cleanup(func() {
if err := file.Close(); err != nil {
t.Errorf("failed to close file: %s", err)
}
})
if err = message.AttachReader("attachment.txt", file); err != nil {
t.Fatalf("failed to attach reader: %s", err)
}
attachments := message.GetAttachments()
if len(attachments) != 1 {
t.Fatalf("failed to get attachments, expected 1, got: %d", len(attachments))
}
messageBuf, err := os.Open(tempfile.Name())
if err != nil {
t.Fatalf("failed to open temp file: %s", err)
}
// We close early to cause an error during io.Copy
if err = messageBuf.Close(); err != nil {
t.Fatalf("failed to close temp file: %s", err)
}
_, err = attachments[0].Writer(messageBuf)
if err == nil {
t.Error("writer func expected to fail, but didn't")
} }
}) })
} }
/* // TestMsg_WriteToSendmailWithContext tests the WriteToSendmailWithContext() method of the Msg
func TestMsg_WriteToSendmailWithContext(t *testing.T) {
if os.Getenv("TEST_SENDMAIL") != "true" {
t.Skipf("TEST_SENDMAIL variable is not set. Skipping sendmail test")
}
tests := []struct {
name string
sp string
sf bool
}{
{"Sendmail path: /dev/null", "/dev/null", true},
{"Sendmail path: /bin/cat", "/bin/cat", true},
{"Sendmail path: /is/invalid", "/is/invalid", true},
{"Sendmail path: /bin/echo", "/bin/echo", false},
}
m := NewMsg()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx, cfn := context.WithTimeout(context.Background(), time.Second*10)
defer cfn()
m.SetBodyString(TypeTextPlain, "Plain")
if err := m.WriteToSendmailWithContext(ctx, tt.sp); err != nil && !tt.sf {
t.Errorf("WriteToSendmailWithCommand() failed: %s", err)
}
m.Reset()
})
}
}
// TestMsg_WriteToSendmail will test the output to the local sendmail command
func TestMsg_WriteToSendmail(t *testing.T) {
if os.Getenv("TEST_SENDMAIL") != "true" {
t.Skipf("TEST_SENDMAIL variable is not set. Skipping sendmail test")
}
_, err := os.Stat(SendmailPath)
if err != nil {
t.Skipf("local sendmail command not found in expected path. Skipping")
}
m := NewMsg()
_ = m.From("Toni Tester <tester@example.com>")
_ = m.To(TestRcpt)
m.SetBodyString(TypeTextPlain, "This is a test")
if err := m.WriteToSendmail(); err != nil {
t.Errorf("WriteToSendmail failed: %s", err)
}
}
func TestMsg_WriteToTempFileFailed(t *testing.T) { func TestMsg_WriteToTempFileFailed(t *testing.T) {
m := NewMsg() m := NewMsg()
_ = m.From("Toni Tester <tester@example.com>") _ = m.From("Toni Tester <tester@example.com>")
@ -142,6 +202,3 @@ func TestMsg_WriteToTempFileFailed(t *testing.T) {
t.Errorf("WriteToTempFile() did not fail as expected") t.Errorf("WriteToTempFile() did not fail as expected")
} }
} }
*/