// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors // // SPDX-License-Identifier: MIT //go:build linux || freebsd // +build linux freebsd package mail import ( "bytes" "context" "errors" "os" "testing" "time" ) func TestMsg_AttachFile_unixOnly(t *testing.T) { t.Run("AttachFile 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("testdata/tmp", "attachfile-open-write-test.*.txt") if err != nil { t.Fatalf("failed to create temp file: %s", err) } t.Cleanup(func() { if err := os.Remove(tempFile.Name()); err != nil { 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() if message == nil { t.Fatal("message is nil") } message.AttachFile(tempFile.Name()) attachments := message.GetAttachments() if len(attachments) != 1 { t.Fatalf("failed to get attachments, expected 1, got: %d", len(attachments)) } messageBuf := bytes.NewBuffer(nil) _, err = attachments[0].Writer(messageBuf) if err == nil { 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) } }) t.Run("AttachFile with fileFromFS fails on copy", func(t *testing.T) { tempfile, err := os.CreateTemp("testdata/tmp", "attachfile-close-early.*.txt") if err != nil { t.Fatalf("failed to create temp file: %s", err) } t.Cleanup(func() { if err := os.Remove(tempfile.Name()); err != nil { t.Errorf("failed to remove temp file: %s", err) } }) message := NewMsg() if message == nil { t.Fatal("message is nil") } message.AttachFile("testdata/attachment.txt") 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") } }) } func TestMsg_AttachReader_unixOnly(t *testing.T) { t.Run("AttachReader with fileFromReader fails on copy", func(t *testing.T) { tempfile, err := os.CreateTemp("", "attachfile-close-early.*.txt") if err != nil { t.Fatalf("failed to create temp file: %s", err) } t.Cleanup(func() { if err := os.Remove(tempfile.Name()); err != nil { t.Errorf("failed to remove temp file: %s", err) } }) message := NewMsg() if message == 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") } }) } func TestMsg_AttachReadSeeker_unixOnly(t *testing.T) { t.Run("AttachReadSeeker with fileFromReadSeeker fails on copy", func(t *testing.T) { tempfile, err := os.CreateTemp("", "attachfile-close-early.*.txt") if err != nil { t.Fatalf("failed to create temp file: %s", err) } t.Cleanup(func() { if err := os.Remove(tempfile.Name()); err != nil { t.Errorf("failed to remove temp file: %s", err) } }) message := NewMsg() if message == 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) } }) message.AttachReadSeeker("attachment.txt", file) 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") } }) } 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("testdata/tmp", "embedfile-open-write-test.*.txt") if err != nil { t.Fatalf("failed to create temp file: %s", err) } t.Cleanup(func() { if err := os.Remove(tempFile.Name()); err != nil { 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() if message == nil { t.Fatal("message is nil") } message.EmbedFile(tempFile.Name()) embeds := message.GetEmbeds() if len(embeds) != 1 { t.Fatalf("failed to get embeds, expected 1, got: %d", len(embeds)) } messageBuf := bytes.NewBuffer(nil) _, err = embeds[0].Writer(messageBuf) if err == nil { 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) } }) t.Run("EmbedFile with fileFromFS fails on copy", func(t *testing.T) { tempfile, err := os.CreateTemp("testdata/tmp", "embedfile-close-early.*.txt") if err != nil { t.Fatalf("failed to create temp file: %s", err) } t.Cleanup(func() { if err := os.Remove(tempfile.Name()); err != nil { t.Errorf("failed to remove temp file: %s", err) } }) message := NewMsg() if message == nil { t.Fatal("message is nil") } message.EmbedFile("testdata/embed.txt") emebeds := message.GetEmbeds() if len(emebeds) != 1 { t.Fatalf("failed to get emebeds, expected 1, got: %d", len(emebeds)) } 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 = emebeds[0].Writer(messageBuf) if err == nil { t.Error("writer func expected to fail, but didn't") } }) } func TestMsg_EmbedReader_unixOnly(t *testing.T) { t.Run("EmbedReader with fileFromReader fails on copy", func(t *testing.T) { tempfile, err := os.CreateTemp("", "embedfile-close-early.*.txt") if err != nil { t.Fatalf("failed to create temp file: %s", err) } t.Cleanup(func() { if err := os.Remove(tempfile.Name()); err != nil { t.Errorf("failed to remove temp file: %s", err) } }) message := NewMsg() if message == nil { t.Fatal("message is nil") } file, err := os.Open("testdata/embed.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.EmbedReader("embed.txt", file); err != nil { t.Fatalf("failed to embed reader: %s", err) } embeds := message.GetEmbeds() if len(embeds) != 1 { t.Fatalf("failed to get embeds, expected 1, got: %d", len(embeds)) } 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 = embeds[0].Writer(messageBuf) if err == nil { t.Error("writer func expected to fail, but didn't") } }) t.Run("EmbedReader with fileFromReader on closed reader", func(t *testing.T) { tempfile, err := os.CreateTemp("", "embedfile-close-early.*.txt") if err != nil { t.Fatalf("failed to create temp file: %s", err) } if err = tempfile.Close(); err != nil { t.Fatalf("failed to close temp file: %s", err) } t.Cleanup(func() { if err := os.Remove(tempfile.Name()); err != nil { t.Errorf("failed to remove temp file: %s", err) } }) message := NewMsg() if message == nil { t.Fatal("message is nil") } if err = message.EmbedReader("embed.txt", tempfile); err == nil { t.Fatalf("expected error, got nil") } }) } // 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 ") _ = 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) { m := NewMsg() _ = m.From("Toni Tester ") _ = m.To("Ellenor Tester ") m.SetBodyString(TypeTextPlain, "This is a test") curTmpDir := os.Getenv("TMPDIR") defer func() { if err := os.Setenv("TMPDIR", curTmpDir); err != nil { t.Errorf("failed to set TMPDIR environment variable: %s", err) } }() if err := os.Setenv("TMPDIR", "/invalid/directory/that/does/not/exist"); err != nil { t.Errorf("failed to set TMPDIR environment variable: %s", err) } _, err := m.WriteToTempFile() if err == nil { t.Errorf("WriteToTempFile() did not fail as expected") } }