mirror of
https://github.com/wneessen/go-mail.git
synced 2024-11-22 13:50:49 +01:00
Switched write functions to return the number of bytes. Due to failing tests in Msg.Write()
This commit is contained in:
parent
bf755cd944
commit
709b4e6b91
5 changed files with 208 additions and 28 deletions
2
file.go
2
file.go
|
@ -12,7 +12,7 @@ type FileOption func(*File)
|
||||||
type File struct {
|
type File struct {
|
||||||
Name string
|
Name string
|
||||||
Header textproto.MIMEHeader
|
Header textproto.MIMEHeader
|
||||||
Writer func(w io.Writer) error
|
Writer func(w io.Writer) (int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithFileName sets the filename of the File
|
// WithFileName sets the filename of the File
|
||||||
|
|
36
msg.go
36
msg.go
|
@ -389,15 +389,15 @@ func (m *Msg) GetRecipients() ([]string, error) {
|
||||||
// SetBodyString sets the body of the message.
|
// SetBodyString sets the body of the message.
|
||||||
func (m *Msg) SetBodyString(ct ContentType, b string, o ...PartOption) {
|
func (m *Msg) SetBodyString(ct ContentType, b string, o ...PartOption) {
|
||||||
buf := bytes.NewBufferString(b)
|
buf := bytes.NewBufferString(b)
|
||||||
w := func(w io.Writer) error {
|
w := func(w io.Writer) (int64, error) {
|
||||||
_, err := io.Copy(w, buf)
|
nb, err := io.Copy(w, buf)
|
||||||
return err
|
return nb, err
|
||||||
}
|
}
|
||||||
m.SetBodyWriter(ct, w, o...)
|
m.SetBodyWriter(ct, w, o...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBodyWriter sets the body of the message.
|
// SetBodyWriter sets the body of the message.
|
||||||
func (m *Msg) SetBodyWriter(ct ContentType, w func(io.Writer) error, o ...PartOption) {
|
func (m *Msg) SetBodyWriter(ct ContentType, w func(io.Writer) (int64, error), o ...PartOption) {
|
||||||
p := m.newPart(ct, o...)
|
p := m.newPart(ct, o...)
|
||||||
p.w = w
|
p.w = w
|
||||||
m.parts = []*Part{p}
|
m.parts = []*Part{p}
|
||||||
|
@ -406,15 +406,15 @@ func (m *Msg) SetBodyWriter(ct ContentType, w func(io.Writer) error, o ...PartOp
|
||||||
// AddAlternativeString sets the alternative body of the message.
|
// AddAlternativeString sets the alternative body of the message.
|
||||||
func (m *Msg) AddAlternativeString(ct ContentType, b string, o ...PartOption) {
|
func (m *Msg) AddAlternativeString(ct ContentType, b string, o ...PartOption) {
|
||||||
buf := bytes.NewBufferString(b)
|
buf := bytes.NewBufferString(b)
|
||||||
w := func(w io.Writer) error {
|
w := func(w io.Writer) (int64, error) {
|
||||||
_, err := io.Copy(w, buf)
|
nb, err := io.Copy(w, buf)
|
||||||
return err
|
return nb, err
|
||||||
}
|
}
|
||||||
m.AddAlternativeWriter(ct, w, o...)
|
m.AddAlternativeWriter(ct, w, o...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddAlternativeWriter sets the body of the message.
|
// AddAlternativeWriter sets the body of the message.
|
||||||
func (m *Msg) AddAlternativeWriter(ct ContentType, w func(io.Writer) error, o ...PartOption) {
|
func (m *Msg) AddAlternativeWriter(ct ContentType, w func(io.Writer) (int64, error), o ...PartOption) {
|
||||||
p := m.newPart(ct, o...)
|
p := m.newPart(ct, o...)
|
||||||
p.w = w
|
p.w = w
|
||||||
m.parts = append(m.parts, p)
|
m.parts = append(m.parts, p)
|
||||||
|
@ -601,16 +601,17 @@ func fileFromFS(n string) *File {
|
||||||
return &File{
|
return &File{
|
||||||
Name: filepath.Base(n),
|
Name: filepath.Base(n),
|
||||||
Header: make(map[string][]string),
|
Header: make(map[string][]string),
|
||||||
Writer: func(w io.Writer) error {
|
Writer: func(w io.Writer) (int64, error) {
|
||||||
h, err := os.Open(n)
|
h, err := os.Open(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return 0, err
|
||||||
}
|
}
|
||||||
if _, err := io.Copy(w, h); err != nil {
|
nb, err := io.Copy(w, h)
|
||||||
|
if err != nil {
|
||||||
_ = h.Close()
|
_ = h.Close()
|
||||||
return fmt.Errorf("failed to copy file to io.Writer: %w", err)
|
return nb, fmt.Errorf("failed to copy file to io.Writer: %w", err)
|
||||||
}
|
}
|
||||||
return h.Close()
|
return nb, h.Close()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -620,11 +621,12 @@ func fileFromReader(n string, r io.Reader) *File {
|
||||||
return &File{
|
return &File{
|
||||||
Name: filepath.Base(n),
|
Name: filepath.Base(n),
|
||||||
Header: make(map[string][]string),
|
Header: make(map[string][]string),
|
||||||
Writer: func(w io.Writer) error {
|
Writer: func(w io.Writer) (int64, error) {
|
||||||
if _, err := io.Copy(w, r); err != nil {
|
nb, err := io.Copy(w, r)
|
||||||
return err
|
if err != nil {
|
||||||
|
return nb, err
|
||||||
}
|
}
|
||||||
return nil
|
return nb, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
187
msg_test.go
187
msg_test.go
|
@ -1,6 +1,7 @@
|
||||||
package mail
|
package mail
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -855,7 +856,7 @@ func TestMsg_SetBodyString(t *testing.T) {
|
||||||
}
|
}
|
||||||
part := m.parts[0]
|
part := m.parts[0]
|
||||||
res := bytes.Buffer{}
|
res := bytes.Buffer{}
|
||||||
if err := part.w(&res); err != nil && !tt.sf {
|
if _, err := part.w(&res); err != nil && !tt.sf {
|
||||||
t.Errorf("WriteFunc of part failed: %s", err)
|
t.Errorf("WriteFunc of part failed: %s", err)
|
||||||
}
|
}
|
||||||
if res.String() != tt.want {
|
if res.String() != tt.want {
|
||||||
|
@ -890,7 +891,7 @@ func TestMsg_AddAlternativeString(t *testing.T) {
|
||||||
}
|
}
|
||||||
apart := m.parts[1]
|
apart := m.parts[1]
|
||||||
res := bytes.Buffer{}
|
res := bytes.Buffer{}
|
||||||
if err := apart.w(&res); err != nil && !tt.sf {
|
if _, err := apart.w(&res); err != nil && !tt.sf {
|
||||||
t.Errorf("WriteFunc of part failed: %s", err)
|
t.Errorf("WriteFunc of part failed: %s", err)
|
||||||
}
|
}
|
||||||
if res.String() != tt.want {
|
if res.String() != tt.want {
|
||||||
|
@ -932,7 +933,7 @@ func TestMsg_AttachFile(t *testing.T) {
|
||||||
file.Name)
|
file.Name)
|
||||||
}
|
}
|
||||||
buf := bytes.Buffer{}
|
buf := bytes.Buffer{}
|
||||||
if err := file.Writer(&buf); err != nil {
|
if _, err := file.Writer(&buf); err != nil {
|
||||||
t.Errorf("failed to execute WriterFunc: %s", err)
|
t.Errorf("failed to execute WriterFunc: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -956,11 +957,185 @@ func TestMsg_AttachFileBrokenFunc(t *testing.T) {
|
||||||
t.Errorf("AttachFile() failed. Attachment file pointer is nil")
|
t.Errorf("AttachFile() failed. Attachment file pointer is nil")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
file.Writer = func(io.Writer) error {
|
file.Writer = func(io.Writer) (int64, error) {
|
||||||
return fmt.Errorf("failing intentionally")
|
return 0, fmt.Errorf("failing intentionally")
|
||||||
}
|
}
|
||||||
buf := bytes.Buffer{}
|
buf := bytes.Buffer{}
|
||||||
if err := file.Writer(&buf); err == nil {
|
if _, err := file.Writer(&buf); err == nil {
|
||||||
t.Errorf("execute WriterFunc did not fail, but was expected to fail")
|
t.Errorf("execute WriterFunc did not fail, but was expected to fail")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestMsg_AttachReader tests the Msg.AttachReader method
|
||||||
|
func TestMsg_AttachReader(t *testing.T) {
|
||||||
|
m := NewMsg()
|
||||||
|
ts := "This is a test string"
|
||||||
|
rbuf := bytes.Buffer{}
|
||||||
|
rbuf.WriteString(ts)
|
||||||
|
r := bufio.NewReader(&rbuf)
|
||||||
|
m.AttachReader("testfile.txt", r)
|
||||||
|
if len(m.attachments) != 1 {
|
||||||
|
t.Errorf("AttachReader() failed. Number of attachments expected: %d, got: %d", 1,
|
||||||
|
len(m.attachments))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file := m.attachments[0]
|
||||||
|
if file == nil {
|
||||||
|
t.Errorf("AttachReader() failed. Attachment file pointer is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if file.Name != "testfile.txt" {
|
||||||
|
t.Errorf("AttachReader() failed. Expected file name: %s, got: %s", "testfile.txt",
|
||||||
|
file.Name)
|
||||||
|
}
|
||||||
|
wbuf := bytes.Buffer{}
|
||||||
|
if _, err := file.Writer(&wbuf); err != nil {
|
||||||
|
t.Errorf("execute WriterFunc failed: %s", err)
|
||||||
|
}
|
||||||
|
if wbuf.String() != ts {
|
||||||
|
t.Errorf("AttachReader() failed. Expected string: %q, got: %q", ts, wbuf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMsg_EmbedFile tests the Msg.EmbedFile and the WithFilename FileOption method
|
||||||
|
func TestMsg_EmbedFile(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
file string
|
||||||
|
fn string
|
||||||
|
sf bool
|
||||||
|
}{
|
||||||
|
{"File: README.md", "README.md", "README.md", false},
|
||||||
|
{"File: doc.go", "doc.go", "foo.go", false},
|
||||||
|
{"File: nonexisting", "", "invalid.file", true},
|
||||||
|
}
|
||||||
|
m := NewMsg()
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
m.EmbedFile(tt.file, WithFileName(tt.fn), nil)
|
||||||
|
if len(m.embeds) != 1 && !tt.sf {
|
||||||
|
t.Errorf("EmbedFile() failed. Number of embeds expected: %d, got: %d", 1,
|
||||||
|
len(m.embeds))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !tt.sf {
|
||||||
|
file := m.embeds[0]
|
||||||
|
if file == nil {
|
||||||
|
t.Errorf("EmbedFile() failed. Embedded file pointer is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if file.Name != tt.fn {
|
||||||
|
t.Errorf("EmbedFile() failed. Filename of embeds expected: %s, got: %s", tt.fn,
|
||||||
|
file.Name)
|
||||||
|
}
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
if _, err := file.Writer(&buf); err != nil {
|
||||||
|
t.Errorf("failed to execute WriterFunc: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.Reset()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMsg_EmbedFileBrokenFunc tests WriterFunc of the Msg.EmbedFile method
|
||||||
|
func TestMsg_EmbedFileBrokenFunc(t *testing.T) {
|
||||||
|
m := NewMsg()
|
||||||
|
m.EmbedFile("README.md")
|
||||||
|
if len(m.embeds) != 1 {
|
||||||
|
t.Errorf("EmbedFile() failed. Number of embeds expected: %d, got: %d", 1,
|
||||||
|
len(m.embeds))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file := m.embeds[0]
|
||||||
|
if file == nil {
|
||||||
|
t.Errorf("EmbedFile() failed. Embedded file pointer is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file.Writer = func(io.Writer) (int64, error) {
|
||||||
|
return 0, fmt.Errorf("failing intentionally")
|
||||||
|
}
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
if _, err := file.Writer(&buf); err == nil {
|
||||||
|
t.Errorf("execute WriterFunc did not fail, but was expected to fail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMsg_EmbedReader tests the Msg.EmbedReader method
|
||||||
|
func TestMsg_EmbedReader(t *testing.T) {
|
||||||
|
m := NewMsg()
|
||||||
|
ts := "This is a test string"
|
||||||
|
rbuf := bytes.Buffer{}
|
||||||
|
rbuf.WriteString(ts)
|
||||||
|
r := bufio.NewReader(&rbuf)
|
||||||
|
m.EmbedReader("testfile.txt", r)
|
||||||
|
if len(m.embeds) != 1 {
|
||||||
|
t.Errorf("EmbedReader() failed. Number of embeds expected: %d, got: %d", 1,
|
||||||
|
len(m.embeds))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file := m.embeds[0]
|
||||||
|
if file == nil {
|
||||||
|
t.Errorf("EmbedReader() failed. Embedded file pointer is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if file.Name != "testfile.txt" {
|
||||||
|
t.Errorf("EmbedReader() failed. Expected file name: %s, got: %s", "testfile.txt",
|
||||||
|
file.Name)
|
||||||
|
}
|
||||||
|
wbuf := bytes.Buffer{}
|
||||||
|
if _, err := file.Writer(&wbuf); err != nil {
|
||||||
|
t.Errorf("execute WriterFunc failed: %s", err)
|
||||||
|
}
|
||||||
|
if wbuf.String() != ts {
|
||||||
|
t.Errorf("EmbedReader() failed. Expected string: %q, got: %q", ts, wbuf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMsg_hasAlt tests the hasAlt() method of the Msg
|
||||||
|
func TestMsg_hasAlt(t *testing.T) {
|
||||||
|
m := NewMsg()
|
||||||
|
m.SetBodyString(TypeTextPlain, "Plain")
|
||||||
|
m.AddAlternativeString(TypeTextHTML, "<b>HTML</b>")
|
||||||
|
if !m.hasAlt() {
|
||||||
|
t.Errorf("mail has alternative parts but hasAlt() returned true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMsg_hasRelated tests the hasRelated() method of the Msg
|
||||||
|
func TestMsg_hasRelated(t *testing.T) {
|
||||||
|
m := NewMsg()
|
||||||
|
m.SetBodyString(TypeTextPlain, "Plain")
|
||||||
|
m.EmbedFile("README.md")
|
||||||
|
if !m.hasRelated() {
|
||||||
|
t.Errorf("mail has related parts but hasRelated() returned true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMsg_hasMixed tests the hasMixed() method of the Msg
|
||||||
|
func TestMsg_hasMixed(t *testing.T) {
|
||||||
|
m := NewMsg()
|
||||||
|
m.SetBodyString(TypeTextPlain, "Plain")
|
||||||
|
m.AttachFile("README.md")
|
||||||
|
if !m.hasMixed() {
|
||||||
|
t.Errorf("mail has mixed parts but hasMixed() returned true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMsg_Write tests the Write() method of the Msg
|
||||||
|
func TestMsg_Write(t *testing.T) {
|
||||||
|
m := NewMsg()
|
||||||
|
m.SetBodyString(TypeTextPlain, "Plain")
|
||||||
|
wbuf := bytes.Buffer{}
|
||||||
|
n, err := m.Write(&wbuf)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Write() failed: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n != int64(wbuf.Len()) {
|
||||||
|
t.Errorf("Write() failed: expected written byte length: %d, got: %d", n, wbuf.Len())
|
||||||
|
fmt.Printf("%s", wbuf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -241,9 +241,10 @@ func (mw *msgWriter) writeHeader(k Header, v ...string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeBody writes an io.Reader into an io.Writer using provided Encoding
|
// writeBody writes an io.Reader into an io.Writer using provided Encoding
|
||||||
func (mw *msgWriter) writeBody(f func(io.Writer) error, e Encoding) {
|
func (mw *msgWriter) writeBody(f func(io.Writer) (int64, error), e Encoding) {
|
||||||
var w io.Writer
|
var w io.Writer
|
||||||
var ew io.WriteCloser
|
var ew io.WriteCloser
|
||||||
|
var n int64
|
||||||
if mw.d == 0 {
|
if mw.d == 0 {
|
||||||
w = mw.w
|
w = mw.w
|
||||||
}
|
}
|
||||||
|
@ -257,12 +258,14 @@ func (mw *msgWriter) writeBody(f func(io.Writer) error, e Encoding) {
|
||||||
case EncodingB64:
|
case EncodingB64:
|
||||||
ew = base64.NewEncoder(base64.StdEncoding, w)
|
ew = base64.NewEncoder(base64.StdEncoding, w)
|
||||||
case NoEncoding:
|
case NoEncoding:
|
||||||
mw.err = f(w)
|
n, mw.err = f(w)
|
||||||
|
mw.n += n
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
ew = quotedprintable.NewWriter(w)
|
ew = quotedprintable.NewWriter(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
mw.err = f(ew)
|
n, mw.err = f(ew)
|
||||||
|
mw.n += int64(n)
|
||||||
mw.err = ew.Close()
|
mw.err = ew.Close()
|
||||||
}
|
}
|
||||||
|
|
2
part.go
2
part.go
|
@ -9,7 +9,7 @@ type PartOption func(*Part)
|
||||||
type Part struct {
|
type Part struct {
|
||||||
ctype ContentType
|
ctype ContentType
|
||||||
enc Encoding
|
enc Encoding
|
||||||
w func(io.Writer) error
|
w func(io.Writer) (int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetEncoding creates a new mime.WordEncoder based on the encoding setting of the message
|
// SetEncoding creates a new mime.WordEncoder based on the encoding setting of the message
|
||||||
|
|
Loading…
Reference in a new issue