diff --git a/msg_test.go b/msg_test.go index 62c74ae..117e8e7 100644 --- a/msg_test.go +++ b/msg_test.go @@ -3908,6 +3908,32 @@ func TestMsg_SetBodyWriter(t *testing.T) { t.Errorf("expected message body to be %s, got: %s", "test", messageBuf.String()) } }) + t.Run("SetBodyWriter with nil option", func(t *testing.T) { + message := NewMsg() + if message == nil { + t.Fatal("message is nil") + } + message.SetBodyWriter(TypeTextPlain, writerFunc, nil) + parts := message.GetParts() + if len(parts) != 1 { + t.Fatalf("expected 1 part, got: %d", len(parts)) + } + if parts[0] == nil { + t.Fatal("expected part to be not nil") + } + if parts[0].contentType != TypeTextPlain { + t.Errorf("expected contentType to be %s, got: %s", TypeTextPlain, + parts[0].contentType) + } + messageBuf := bytes.NewBuffer(nil) + _, err := parts[0].writeFunc(messageBuf) + if err != nil { + t.Errorf("writeFunc failed: %s", err) + } + if !strings.EqualFold(messageBuf.String(), "test") { + t.Errorf("expected message body to be %s, got: %s", "test", messageBuf.String()) + } + }) } func TestMsg_SetBodyHTMLTemplate(t *testing.T) { @@ -4622,6 +4648,39 @@ func TestMsg_AttachReader(t *testing.T) { t.Error("writer func expected to fail, but didn't") } }) + // Tests the Msg.AttachReader methods with consecutive calls to Msg.WriteTo to make sure + // the attachments are not lost. + // https://github.com/wneessen/go-mail/issues/110 + t.Run("AttachReader with consecutive writes", func(t *testing.T) { + teststring := "This is a test string" + message := testMessage(t) + if err := message.AttachReader("attachment.txt", bytes.NewBufferString(teststring)); err != nil { + t.Fatalf("failed to attach teststring buffer: %s", err) + } + messageBuffer := bytes.NewBuffer(nil) + altBuffer := bytes.NewBuffer(nil) + // First write + n1, err := message.WriteTo(messageBuffer) + if err != nil { + t.Fatalf("failed to write message to buffer: %s", err) + } + // Second write + n2, err := message.WriteTo(altBuffer) + if err != nil { + t.Fatalf("failed to write message to alternative buffer: %s", err) + } + if n1 != n2 { + t.Errorf("number of written bytes between consecutive writes differ, got: %d and %d", n1, n2) + } + if !strings.Contains(messageBuffer.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") { + t.Errorf("expected string not found in message buffer, want: %s, got: %s", + "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n", messageBuffer.String()) + } + if !strings.Contains(altBuffer.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") { + t.Errorf("expected string not found in alternative message buffer, want: %s, got: %s", + "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n", altBuffer.String()) + } + }) } func TestMsg_AttachReadSeeker(t *testing.T) { @@ -4684,6 +4743,37 @@ func TestMsg_AttachReadSeeker(t *testing.T) { t.Error("writer func expected to fail, but didn't") } }) + // Tests the Msg.AttachReadSeeker methods with consecutive calls to Msg.WriteTo to make sure + // the attachments are not lost. + // https://github.com/wneessen/go-mail/issues/110 + t.Run("AttachReadSeeker with consecutive writes", func(t *testing.T) { + teststring := []byte("This is a test string") + message := testMessage(t) + message.AttachReadSeeker("attachment.txt", bytes.NewReader(teststring)) + messageBuffer := bytes.NewBuffer(nil) + altBuffer := bytes.NewBuffer(nil) + // First write + n1, err := message.WriteTo(messageBuffer) + if err != nil { + t.Fatalf("failed to write message to buffer: %s", err) + } + // Second write + n2, err := message.WriteTo(altBuffer) + if err != nil { + t.Fatalf("failed to write message to alternative buffer: %s", err) + } + if n1 != n2 { + t.Errorf("number of written bytes between consecutive writes differ, got: %d and %d", n1, n2) + } + if !strings.Contains(messageBuffer.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") { + t.Errorf("expected string not found in message buffer, want: %s, got: %s", + "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n", messageBuffer.String()) + } + if !strings.Contains(altBuffer.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") { + t.Errorf("expected string not found in alternative message buffer, want: %s, got: %s", + "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n", altBuffer.String()) + } + }) } func TestMsg_AttachHTMLTemplate(t *testing.T) { @@ -5023,6 +5113,39 @@ func TestMsg_EmbedReader(t *testing.T) { t.Fatalf("expected error, got nil") } }) + // Tests the Msg.EmbedReader methods with consecutive calls to Msg.WriteTo to make sure + // the attachments are not lost. + // https://github.com/wneessen/go-mail/issues/110 + t.Run("EmbedReader with consecutive writes", func(t *testing.T) { + teststring := "This is a test string" + message := testMessage(t) + if err := message.EmbedReader("embed.txt", bytes.NewBufferString(teststring)); err != nil { + t.Fatalf("failed to embed teststring buffer: %s", err) + } + messageBuffer := bytes.NewBuffer(nil) + altBuffer := bytes.NewBuffer(nil) + // First write + n1, err := message.WriteTo(messageBuffer) + if err != nil { + t.Fatalf("failed to write message to buffer: %s", err) + } + // Second write + n2, err := message.WriteTo(altBuffer) + if err != nil { + t.Fatalf("failed to write message to alternative buffer: %s", err) + } + if n1 != n2 { + t.Errorf("number of written bytes between consecutive writes differ, got: %d and %d", n1, n2) + } + if !strings.Contains(messageBuffer.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") { + t.Errorf("expected string not found in message buffer, want: %s, got: %s", + "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n", messageBuffer.String()) + } + if !strings.Contains(altBuffer.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") { + t.Errorf("expected string not found in alternative message buffer, want: %s, got: %s", + "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n", altBuffer.String()) + } + }) } func TestMsg_EmbedReadSeeker(t *testing.T) { @@ -5085,6 +5208,37 @@ func TestMsg_EmbedReadSeeker(t *testing.T) { t.Error("writer func expected to fail, but didn't") } }) + // Tests the Msg.EmbedReadSeeker methods with consecutive calls to Msg.WriteTo to make sure + // the attachments are not lost. + // https://github.com/wneessen/go-mail/issues/110 + t.Run("EmbedReadSeeker with consecutive writes", func(t *testing.T) { + teststring := []byte("This is a test string") + message := testMessage(t) + message.EmbedReadSeeker("embed.txt", bytes.NewReader(teststring)) + messageBuffer := bytes.NewBuffer(nil) + altBuffer := bytes.NewBuffer(nil) + // First write + n1, err := message.WriteTo(messageBuffer) + if err != nil { + t.Fatalf("failed to write message to buffer: %s", err) + } + // Second write + n2, err := message.WriteTo(altBuffer) + if err != nil { + t.Fatalf("failed to write message to alternative buffer: %s", err) + } + if n1 != n2 { + t.Errorf("number of written bytes between consecutive writes differ, got: %d and %d", n1, n2) + } + if !strings.Contains(messageBuffer.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") { + t.Errorf("expected string not found in message buffer, want: %s, got: %s", + "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n", messageBuffer.String()) + } + if !strings.Contains(altBuffer.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") { + t.Errorf("expected string not found in alternative message buffer, want: %s, got: %s", + "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n", altBuffer.String()) + } + }) } func TestMsg_EmbedHTMLTemplate(t *testing.T) { @@ -6077,743 +6231,219 @@ func TestMsg_WriteToTempFile(t *testing.T) { }) } -/* -// TestMsg_hasAlt tests the hasAlt() method of the Msg +func TestMsg_hasAlt(t *testing.T) { + t.Run("message has no alt", func(t *testing.T) { + message := testMessage(t) + if message.hasAlt() { + t.Error("message has no alt, but hasAlt returned true") + } + }) + t.Run("message has alt", func(t *testing.T) { + message := testMessage(t) + message.AddAlternativeString(TypeTextHTML, "this is the alt") + if !message.hasAlt() { + t.Error("message has alt, but hasAlt returned false") + } + }) + t.Run("message has no alt due to deleted part", func(t *testing.T) { + message := testMessage(t) + message.AddAlternativeString(TypeTextHTML, "this is the alt") + if len(message.GetParts()) != 2 { + t.Errorf("expected message to have 2 parts, got: %d", len(message.GetParts())) + } + message.parts[1].isDeleted = true + if message.hasAlt() { + t.Error("message has no alt, but hasAlt returned true") + } + }) + t.Run("message has alt and deleted parts", func(t *testing.T) { + message := testMessage(t) + message.AddAlternativeString(TypeTextHTML, "this is the alt") + message.AddAlternativeString(TypeTextPlain, "this is also a alt") + if len(message.GetParts()) != 3 { + t.Errorf("expected message to have 3 parts, got: %d", len(message.GetParts())) + } + message.parts[1].isDeleted = true + if !message.hasAlt() { + t.Error("message has alt, but hasAlt returned false") + } + }) + t.Run("message has alt but it is pgptype", func(t *testing.T) { + message := testMessage(t) + message.SetPGPType(PGPSignature) + if message.hasAlt() { + t.Error("message has alt but it is a pgpType, hence hasAlt should return false") + } + }) +} - func TestMsg_hasAlt(t *testing.T) { - m := NewMsg() - m.SetBodyString(TypeTextPlain, "Plain") - m.AddAlternativeString(TypeTextHTML, "HTML") - if !m.hasAlt() { - t.Errorf("mail has alternative parts but hasAlt() returned true") +func TestMsg_hasMixed(t *testing.T) { + t.Run("message has no mixed", func(t *testing.T) { + message := testMessage(t) + if message.hasMixed() { + t.Error("message has no mixed, but hasMixed returned true") } - } + }) + t.Run("message with alt has no mixed", func(t *testing.T) { + message := testMessage(t) + message.AddAlternativeString(TypeTextHTML, "this is the alt") + if message.hasMixed() { + t.Error("message has no mixed, but hasMixed returned true") + } + }) + t.Run("message with attachment is mixed", func(t *testing.T) { + message := testMessage(t) + message.AttachFile("testdata/attachment.txt") + if !message.hasMixed() { + t.Error("message with attachment is supposed to be mixed") + } + }) + t.Run("message with embed is not mixed", func(t *testing.T) { + message := testMessage(t) + message.EmbedFile("testdata/embed.txt") + if message.hasMixed() { + t.Error("message with embed is not supposed to be mixed") + } + }) + t.Run("message with attachment and embed is mixed", func(t *testing.T) { + message := testMessage(t) + message.AttachFile("testdata/attachment.txt") + message.EmbedFile("testdata/embed.txt") + if !message.hasMixed() { + t.Error("message with attachment and embed is supposed to be mixed") + } + }) +} -// TestMsg_hasRelated tests the hasRelated() method of the Msg +func TestMsg_hasRelated(t *testing.T) { + t.Run("message has no related", func(t *testing.T) { + message := testMessage(t) + if message.hasRelated() { + t.Error("message has no related, but hasRelated returned true") + } + }) + t.Run("message with alt has no related", func(t *testing.T) { + message := testMessage(t) + message.AddAlternativeString(TypeTextHTML, "this is the alt") + if message.hasRelated() { + t.Error("message has no related, but hasRelated returned true") + } + }) + t.Run("message with attachment is not related", func(t *testing.T) { + message := testMessage(t) + message.AttachFile("testdata/attachment.txt") + if message.hasRelated() { + t.Error("message with attachment is not supposed to be related") + } + }) + t.Run("message with embed is related", func(t *testing.T) { + message := testMessage(t) + message.EmbedFile("testdata/embed.txt") + if !message.hasRelated() { + t.Error("message with embed is supposed to be related") + } + }) + t.Run("message with attachment and embed is related", func(t *testing.T) { + message := testMessage(t) + message.AttachFile("testdata/attachment.txt") + message.EmbedFile("testdata/embed.txt") + if !message.hasRelated() { + t.Error("message with attachment and embed is supposed to be related") + } + }) +} - 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") +func TestMsg_hasPGPType(t *testing.T) { + t.Run("message has no pgpType", func(t *testing.T) { + message := testMessage(t) + if message.hasPGPType() { + t.Error("message has no PGPType, but hasPGPType returned true") } - } + }) + t.Run("message has signature", func(t *testing.T) { + message := testMessage(t) + message.SetPGPType(PGPSignature) + if !message.hasPGPType() { + t.Error("message has signature, but hasPGPType returned false") + } + }) + t.Run("message has encryption", func(t *testing.T) { + message := testMessage(t) + message.SetPGPType(PGPEncrypt) + if !message.hasPGPType() { + t.Error("message has encryption, but hasPGPType returned false") + } + }) + t.Run("message has encryption and signature", func(t *testing.T) { + message := testMessage(t) + message.SetPGPType(PGPEncrypt | PGPSignature) + if !message.hasPGPType() { + t.Error("message has encryption and signature, but hasPGPType returned false") + } + }) + t.Run("message has NoPGP", func(t *testing.T) { + message := testMessage(t) + message.SetPGPType(NoPGP) + if message.hasPGPType() { + t.Error("message has NoPGP, but hasPGPType returned true") + } + }) +} -// TestMsg_hasMixed tests the hasMixed() method of the Msg +func TestMsg_checkUserAgent(t *testing.T) { + t.Run("default user agent should be set", func(t *testing.T) { + message := testMessage(t) + message.checkUserAgent() + checkGenHeader(t, message, HeaderUserAgent, "checkUserAgent", 0, 1, + fmt.Sprintf("go-mail v%s // https://github.com/wneessen/go-mail", VERSION)) + checkGenHeader(t, message, HeaderXMailer, "checkUserAgent", 0, 1, + fmt.Sprintf("go-mail v%s // https://github.com/wneessen/go-mail", VERSION)) + }) + t.Run("noDefaultUserAgent should return empty string", func(t *testing.T) { + message := testMessage(t) + message.noDefaultUserAgent = true + message.checkUserAgent() + if len(message.genHeader[HeaderUserAgent]) != 0 { + t.Error("user agent should be empty") + } + if len(message.genHeader[HeaderXMailer]) != 0 { + t.Error("x-mailer should be empty") + } + }) +} - 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") +func TestMsg_addDefaultHeader(t *testing.T) { + t.Run("empty message should add defaults", func(t *testing.T) { + message := NewMsg() + if message == nil { + t.Fatal("failed to create new message") } - } + if _, ok := message.genHeader[HeaderDate]; ok { + t.Error("empty message should not have date header") + } + if _, ok := message.genHeader[HeaderMessageID]; ok { + t.Error("empty message should not have message-id header") + } + if _, ok := message.genHeader[HeaderMIMEVersion]; ok { + t.Error("empty message should not have mime version header") + } + message.addDefaultHeader() + if _, ok := message.genHeader[HeaderDate]; !ok { + t.Error("message should now have date header") + } + if _, ok := message.genHeader[HeaderMessageID]; !ok { + t.Error("message should now have message-id header") + } + if _, ok := message.genHeader[HeaderMIMEVersion]; !ok { + t.Error("message should now have mime version header") + } + }) +} -// TestMsg_UpdateReader tests the Msg.UpdateReader method - - func TestMsg_UpdateReader(t *testing.T) { - m := NewMsg() - m.Subject("Subject-Run 1") - mr := m.NewReader() - if mr == nil { - t.Errorf("NewReader failed: Reader is nil") - } - wbuf1 := bytes.Buffer{} - _, err := io.Copy(&wbuf1, mr) - if err != nil { - t.Errorf("io.Copy on Reader failed: %s", err) - } - if !strings.Contains(wbuf1.String(), "Subject: Subject-Run 1") { - t.Errorf("io.Copy on Reader failed. Expected to find %q but string in Subject was not found", - "Subject-Run 1") - } - - m.Subject("Subject-Run 2") - m.UpdateReader(mr) - wbuf2 := bytes.Buffer{} - _, err = io.Copy(&wbuf2, mr) - if err != nil { - t.Errorf("2nd io.Copy on Reader failed: %s", err) - } - if !strings.Contains(wbuf2.String(), "Subject: Subject-Run 2") { - t.Errorf("io.Copy on Reader failed. Expected to find %q but string in Subject was not found", - "Subject-Run 2") - } - } - -// TestMsg_GetGenHeader will test the GetGenHeader method of the Msg - - func TestMsg_GetGenHeader(t *testing.T) { - m := NewMsg() - m.Subject("this is a test") - sa := m.GetGenHeader(HeaderSubject) - if len(sa) <= 0 { - t.Errorf("GetGenHeader on subject failed. Got empty slice") - return - } - if sa[0] == "" { - t.Errorf("GetGenHeader on subject failed. Got empty value") - } - if sa[0] != "this is a test" { - t.Errorf("GetGenHeader on subject failed. Expected: %q, got: %q", "this is a test", sa[0]) - } - } - -// TestMsg_GetAddrHeader will test the Msg.GetAddrHeader method - - func TestMsg_GetAddrHeader(t *testing.T) { - m := NewMsg() - if err := m.FromFormat("Toni Sender", "sender@example.com"); err != nil { - t.Errorf("failed to set FROM address: %s", err) - } - if err := m.AddToFormat("Toni To", "to@example.com"); err != nil { - t.Errorf("failed to set TO address: %s", err) - } - if err := m.AddCcFormat("Toni Cc", "cc@example.com"); err != nil { - t.Errorf("failed to set CC address: %s", err) - } - if err := m.AddBccFormat("Toni Bcc", "bcc@example.com"); err != nil { - t.Errorf("failed to set BCC address: %s", err) - } - fh := m.GetAddrHeader(HeaderFrom) - if len(fh) <= 0 { - t.Errorf("GetAddrHeader on FROM failed. Got empty slice") - return - } - if fh[0].String() == "" { - t.Errorf("GetAddrHeader on FROM failed. Got empty value") - } - if fh[0].String() != `"Toni Sender" ` { - t.Errorf("GetAddrHeader on FROM failed. Expected: %q, got: %q", - `"Toni Sender" "`, fh[0].String()) - } - th := m.GetAddrHeader(HeaderTo) - if len(th) <= 0 { - t.Errorf("GetAddrHeader on TO failed. Got empty slice") - return - } - if th[0].String() == "" { - t.Errorf("GetAddrHeader on TO failed. Got empty value") - } - if th[0].String() != `"Toni To" ` { - t.Errorf("GetAddrHeader on TO failed. Expected: %q, got: %q", - `"Toni To" "`, th[0].String()) - } - ch := m.GetAddrHeader(HeaderCc) - if len(ch) <= 0 { - t.Errorf("GetAddrHeader on CC failed. Got empty slice") - return - } - if ch[0].String() == "" { - t.Errorf("GetAddrHeader on CC failed. Got empty value") - } - if ch[0].String() != `"Toni Cc" ` { - t.Errorf("GetAddrHeader on CC failed. Expected: %q, got: %q", - `"Toni Cc" "`, ch[0].String()) - } - bh := m.GetAddrHeader(HeaderBcc) - if len(bh) <= 0 { - t.Errorf("GetAddrHeader on BCC failed. Got empty slice") - return - } - if bh[0].String() == "" { - t.Errorf("GetAddrHeader on BCC failed. Got empty value") - } - if bh[0].String() != `"Toni Bcc" ` { - t.Errorf("GetAddrHeader on BCC failed. Expected: %q, got: %q", - `"Toni Bcc" "`, bh[0].String()) - } - } - -// TestMsg_GetFrom will test the Msg.GetFrom method - - func TestMsg_GetFrom(t *testing.T) { - m := NewMsg() - if err := m.FromFormat("Toni Sender", "sender@example.com"); err != nil { - t.Errorf("failed to set FROM address: %s", err) - } - fh := m.GetFrom() - if len(fh) <= 0 { - t.Errorf("GetFrom failed. Got empty slice") - return - } - if fh[0].String() == "" { - t.Errorf("GetFrom failed. Got empty value") - } - if fh[0].String() != `"Toni Sender" ` { - t.Errorf("GetFrom failed. Expected: %q, got: %q", - `"Toni Sender" "`, fh[0].String()) - } - } - -// TestMsg_GetFromString will test the Msg.GetFromString method - - func TestMsg_GetFromString(t *testing.T) { - m := NewMsg() - if err := m.FromFormat("Toni Sender", "sender@example.com"); err != nil { - t.Errorf("failed to set FROM address: %s", err) - } - fh := m.GetFromString() - if len(fh) <= 0 { - t.Errorf("GetFromString failed. Got empty slice") - return - } - if fh[0] == "" { - t.Errorf("GetFromString failed. Got empty value") - } - if fh[0] != `"Toni Sender" ` { - t.Errorf("GetFromString failed. Expected: %q, got: %q", - `"Toni Sender" "`, fh[0]) - } - } - -// TestMsg_GetTo will test the Msg.GetTo method - - func TestMsg_GetTo(t *testing.T) { - m := NewMsg() - if err := m.AddToFormat("Toni To", "to@example.com"); err != nil { - t.Errorf("failed to set TO address: %s", err) - } - fh := m.GetTo() - if len(fh) <= 0 { - t.Errorf("GetTo failed. Got empty slice") - return - } - if fh[0].String() == "" { - t.Errorf("GetTo failed. Got empty value") - } - if fh[0].String() != `"Toni To" ` { - t.Errorf("GetTo failed. Expected: %q, got: %q", - `"Toni To" "`, fh[0].String()) - } - } - -// TestMsg_GetToString will test the Msg.GetToString method - - func TestMsg_GetToString(t *testing.T) { - m := NewMsg() - if err := m.AddToFormat("Toni To", "to@example.com"); err != nil { - t.Errorf("failed to set TO address: %s", err) - } - fh := m.GetToString() - if len(fh) <= 0 { - t.Errorf("GetToString failed. Got empty slice") - return - } - if fh[0] == "" { - t.Errorf("GetToString failed. Got empty value") - } - if fh[0] != `"Toni To" ` { - t.Errorf("GetToString failed. Expected: %q, got: %q", - `"Toni To" "`, fh[0]) - } - } - -// TestMsg_GetCc will test the Msg.GetCc method - - func TestMsg_GetCc(t *testing.T) { - m := NewMsg() - if err := m.AddCcFormat("Toni Cc", "cc@example.com"); err != nil { - t.Errorf("failed to set TO address: %s", err) - } - fh := m.GetCc() - if len(fh) <= 0 { - t.Errorf("GetCc failed. Got empty slice") - return - } - if fh[0].String() == "" { - t.Errorf("GetCc failed. Got empty value") - } - if fh[0].String() != `"Toni Cc" ` { - t.Errorf("GetCc failed. Expected: %q, got: %q", - `"Toni Cc" "`, fh[0].String()) - } - } - -// TestMsg_GetCcString will test the Msg.GetCcString method - - func TestMsg_GetCcString(t *testing.T) { - m := NewMsg() - if err := m.AddCcFormat("Toni Cc", "cc@example.com"); err != nil { - t.Errorf("failed to set TO address: %s", err) - } - fh := m.GetCcString() - if len(fh) <= 0 { - t.Errorf("GetCcString failed. Got empty slice") - return - } - if fh[0] == "" { - t.Errorf("GetCcString failed. Got empty value") - } - if fh[0] != `"Toni Cc" ` { - t.Errorf("GetCcString failed. Expected: %q, got: %q", - `"Toni Cc" "`, fh[0]) - } - } - -// TestMsg_GetBcc will test the Msg.GetBcc method - - func TestMsg_GetBcc(t *testing.T) { - m := NewMsg() - if err := m.AddBccFormat("Toni Bcc", "bcc@example.com"); err != nil { - t.Errorf("failed to set TO address: %s", err) - } - fh := m.GetBcc() - if len(fh) <= 0 { - t.Errorf("GetBcc failed. Got empty slice") - return - } - if fh[0].String() == "" { - t.Errorf("GetBcc failed. Got empty value") - } - if fh[0].String() != `"Toni Bcc" ` { - t.Errorf("GetBcc failed. Expected: %q, got: %q", - `"Toni Cc" "`, fh[0].String()) - } - } - -// TestMsg_GetBccString will test the Msg.GetBccString method - - func TestMsg_GetBccString(t *testing.T) { - m := NewMsg() - if err := m.AddBccFormat("Toni Bcc", "bcc@example.com"); err != nil { - t.Errorf("failed to set TO address: %s", err) - } - fh := m.GetBccString() - if len(fh) <= 0 { - t.Errorf("GetBccString failed. Got empty slice") - return - } - if fh[0] == "" { - t.Errorf("GetBccString failed. Got empty value") - } - if fh[0] != `"Toni Bcc" ` { - t.Errorf("GetBccString failed. Expected: %q, got: %q", - `"Toni Cc" "`, fh[0]) - } - } - -// TestMsg_GetBoundary will test the Msg.GetBoundary method - - func TestMsg_GetBoundary(t *testing.T) { - b := "random_boundary_string" - m := NewMsg() - if boundary := m.GetBoundary(); boundary != "" { - t.Errorf("GetBoundary failed. Expected empty string, but got: %s", boundary) - } - m = NewMsg(WithBoundary(b)) - if boundary := m.GetBoundary(); boundary != b { - t.Errorf("GetBoundary failed. Expected boundary: %s, got: %s", b, boundary) - } - } - -// TestMsg_AttachEmbedReader_consecutive tests the Msg.AttachReader and Msg.EmbedReader -// methods with consecutive calls to Msg.WriteTo to make sure the attachments are not -// lost (see Github issue #110) - - func TestMsg_AttachEmbedReader_consecutive(t *testing.T) { - ts1 := "This is a test string" - ts2 := "Another test string" - m := NewMsg() - if err := m.AttachReader("attachment.txt", bytes.NewBufferString(ts1)); err != nil { - t.Errorf("AttachReader() failed. Expected no error, got: %s", err.Error()) - return - } - if err := m.EmbedReader("embedded.txt", bytes.NewBufferString(ts2)); err != nil { - t.Errorf("EmbedReader() failed. Expected no error, got: %s", err.Error()) - return - } - obuf1 := &bytes.Buffer{} - obuf2 := &bytes.Buffer{} - _, err := m.WriteTo(obuf1) - if err != nil { - t.Errorf("WriteTo to first output buffer failed: %s", err) - } - _, err = m.WriteTo(obuf2) - if err != nil { - t.Errorf("WriteTo to second output buffer failed: %s", err) - } - if !strings.Contains(obuf1.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") { - t.Errorf("Expected file attachment string not found in first output buffer") - } - if !strings.Contains(obuf2.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") { - t.Errorf("Expected file attachment string not found in second output buffer") - } - if !strings.Contains(obuf1.String(), "QW5vdGhlciB0ZXN0IHN0cmluZw==") { - t.Errorf("Expected embedded file string not found in first output buffer") - } - if !strings.Contains(obuf2.String(), "QW5vdGhlciB0ZXN0IHN0cmluZw==") { - t.Errorf("Expected embded file string not found in second output buffer") - } - } - -// TestMsg_AttachEmbedReadSeeker_consecutive tests the Msg.AttachReadSeeker and -// Msg.EmbedReadSeeker methods with consecutive calls to Msg.WriteTo to make -// sure the attachments are not lost (see Github issue #110) - - func TestMsg_AttachEmbedReadSeeker_consecutive(t *testing.T) { - ts1 := []byte("This is a test string") - ts2 := []byte("Another test string") - m := NewMsg() - m.AttachReadSeeker("attachment.txt", bytes.NewReader(ts1)) - m.EmbedReadSeeker("embedded.txt", bytes.NewReader(ts2)) - obuf1 := &bytes.Buffer{} - obuf2 := &bytes.Buffer{} - _, err := m.WriteTo(obuf1) - if err != nil { - t.Errorf("WriteTo to first output buffer failed: %s", err) - } - _, err = m.WriteTo(obuf2) - if err != nil { - t.Errorf("WriteTo to second output buffer failed: %s", err) - } - if !strings.Contains(obuf1.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") { - t.Errorf("Expected file attachment string not found in first output buffer") - } - if !strings.Contains(obuf2.String(), "VGhpcyBpcyBhIHRlc3Qgc3RyaW5n") { - t.Errorf("Expected file attachment string not found in second output buffer") - } - if !strings.Contains(obuf1.String(), "QW5vdGhlciB0ZXN0IHN0cmluZw==") { - t.Errorf("Expected embedded file string not found in first output buffer") - } - if !strings.Contains(obuf2.String(), "QW5vdGhlciB0ZXN0IHN0cmluZw==") { - t.Errorf("Expected embded file string not found in second output buffer") - } - } - -// TestMsg_AttachReadSeeker tests the Msg.AttachReadSeeker method - - func TestMsg_AttachReadSeeker(t *testing.T) { - m := NewMsg() - ts := []byte("This is a test string") - r := bytes.NewReader(ts) - m.AttachReadSeeker("testfile.txt", r) - if len(m.attachments) != 1 { - t.Errorf("AttachReadSeeker() failed. Number of attachments expected: %d, got: %d", 1, - len(m.attachments)) - return - } - file := m.attachments[0] - if file == nil { - t.Errorf("AttachReadSeeker() failed. Attachment file pointer is nil") - return - } - if file.Name != "testfile.txt" { - t.Errorf("AttachReadSeeker() 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() != string(ts) { - t.Errorf("AttachReadSeeker() failed. Expected string: %q, got: %q", ts, wbuf.String()) - } - } - -// TestMsg_EmbedReadSeeker tests the Msg.EmbedReadSeeker method - - func TestMsg_EmbedReadSeeker(t *testing.T) { - m := NewMsg() - ts := []byte("This is a test string") - r := bytes.NewReader(ts) - m.EmbedReadSeeker("testfile.txt", r) - if len(m.embeds) != 1 { - t.Errorf("EmbedReadSeeker() failed. Number of attachments expected: %d, got: %d", 1, - len(m.embeds)) - return - } - file := m.embeds[0] - if file == nil { - t.Errorf("EmbedReadSeeker() failed. Embedded file pointer is nil") - return - } - if file.Name != "testfile.txt" { - t.Errorf("EmbedReadSeeker() 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() != string(ts) { - t.Errorf("EmbedReadSeeker() failed. Expected string: %q, got: %q", ts, wbuf.String()) - } - } - -// TestMsg_ToFromString tests Msg.ToFromString in different scenarios - - func TestMsg_ToFromString(t *testing.T) { - tests := []struct { - n string - v string - w []*mail.Address - sf bool - }{ - {"valid single address", "test@test.com", []*mail.Address{ - {Name: "", Address: "test@test.com"}, - }, false}, - { - "valid multiple addresses", "test@test.com,test2@example.com", - []*mail.Address{ - {Name: "", Address: "test@test.com"}, - {Name: "", Address: "test2@example.com"}, - }, - false, - }, - { - "valid multiple addresses with space and name", - `test@test.com, "Toni Tester" `, - []*mail.Address{ - {Name: "", Address: "test@test.com"}, - {Name: "Toni Tester", Address: "test2@example.com"}, - }, - false, - }, - { - "invalid and valid multiple addresses", "test@test.com,test2#example.com", nil, - true, - }, - } - - for _, tt := range tests { - t.Run(tt.n, func(t *testing.T) { - m := NewMsg() - if err := m.ToFromString(tt.v); err != nil && !tt.sf { - t.Errorf("Msg.ToFromString failed: %s", err) - return - } - mto := m.GetTo() - if len(mto) != len(tt.w) { - t.Errorf("Msg.ToFromString failed, expected len: %d, got: %d", len(tt.w), - len(mto)) - return - } - for i := range mto { - w := tt.w[i] - g := mto[i] - if w.String() != g.String() { - t.Errorf("Msg.ToFromString failed, expected address: %s, got: %s", - w.String(), g.String()) - } - } - }) - } - } - -// TestMsg_CcFromString tests Msg.CcFromString in different scenarios - - func TestMsg_CcFromString(t *testing.T) { - tests := []struct { - n string - v string - w []*mail.Address - sf bool - }{ - {"valid single address", "test@test.com", []*mail.Address{ - {Name: "", Address: "test@test.com"}, - }, false}, - { - "valid multiple addresses", "test@test.com,test2@example.com", - []*mail.Address{ - {Name: "", Address: "test@test.com"}, - {Name: "", Address: "test2@example.com"}, - }, - false, - }, - { - "valid multiple addresses with space and name", - `test@test.com, "Toni Tester" `, - []*mail.Address{ - {Name: "", Address: "test@test.com"}, - {Name: "Toni Tester", Address: "test2@example.com"}, - }, - false, - }, - { - "invalid and valid multiple addresses", "test@test.com,test2#example.com", nil, - true, - }, - } - - for _, tt := range tests { - t.Run(tt.n, func(t *testing.T) { - m := NewMsg() - if err := m.CcFromString(tt.v); err != nil && !tt.sf { - t.Errorf("Msg.CcFromString failed: %s", err) - return - } - mto := m.GetCc() - if len(mto) != len(tt.w) { - t.Errorf("Msg.CcFromString failed, expected len: %d, got: %d", len(tt.w), - len(mto)) - return - } - for i := range mto { - w := tt.w[i] - g := mto[i] - if w.String() != g.String() { - t.Errorf("Msg.CcFromString failed, expected address: %s, got: %s", - w.String(), g.String()) - } - } - }) - } - } - -// TestMsg_BccFromString tests Msg.BccFromString in different scenarios - - func TestMsg_BccFromString(t *testing.T) { - tests := []struct { - n string - v string - w []*mail.Address - sf bool - }{ - {"valid single address", "test@test.com", []*mail.Address{ - {Name: "", Address: "test@test.com"}, - }, false}, - { - "valid multiple addresses", "test@test.com,test2@example.com", - []*mail.Address{ - {Name: "", Address: "test@test.com"}, - {Name: "", Address: "test2@example.com"}, - }, - false, - }, - { - "valid multiple addresses with space and name", - `test@test.com, "Toni Tester" `, - []*mail.Address{ - {Name: "", Address: "test@test.com"}, - {Name: "Toni Tester", Address: "test2@example.com"}, - }, - false, - }, - { - "invalid and valid multiple addresses", "test@test.com,test2#example.com", nil, - true, - }, - } - - for _, tt := range tests { - t.Run(tt.n, func(t *testing.T) { - m := NewMsg() - if err := m.BccFromString(tt.v); err != nil && !tt.sf { - t.Errorf("Msg.BccFromString failed: %s", err) - return - } - mto := m.GetBcc() - if len(mto) != len(tt.w) { - t.Errorf("Msg.BccFromString failed, expected len: %d, got: %d", len(tt.w), - len(mto)) - return - } - for i := range mto { - w := tt.w[i] - g := mto[i] - if w.String() != g.String() { - t.Errorf("Msg.BccFromString failed, expected address: %s, got: %s", - w.String(), g.String()) - } - } - }) - } - } - -// TestMsg_checkUserAgent tests the checkUserAgent method of the Msg - - func TestMsg_checkUserAgent(t *testing.T) { - tests := []struct { - name string - noDefaultUserAgent bool - genHeader map[Header][]string - wantUserAgent string - sf bool - }{ - { - name: "check default user agent", - noDefaultUserAgent: false, - wantUserAgent: fmt.Sprintf("go-mail v%s // https://github.com/wneessen/go-mail", VERSION), - sf: false, - }, - { - name: "check no default user agent", - noDefaultUserAgent: true, - wantUserAgent: "", - sf: true, - }, - { - name: "check if ua and xm is already set", - noDefaultUserAgent: false, - genHeader: map[Header][]string{ - HeaderUserAgent: {"custom UA"}, - HeaderXMailer: {"custom XM"}, - }, - wantUserAgent: "custom UA", - sf: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - msg := &Msg{ - noDefaultUserAgent: tt.noDefaultUserAgent, - genHeader: tt.genHeader, - } - msg.checkUserAgent() - gotUserAgent := "" - if val, ok := msg.genHeader[HeaderUserAgent]; ok { - gotUserAgent = val[0] // Assuming the first one is the needed value - } - if gotUserAgent != tt.wantUserAgent && !tt.sf { - t.Errorf("UserAgent got = %v, want = %v", gotUserAgent, tt.wantUserAgent) - } - }) - } - } - -// TestNewMsgWithMIMEVersion tests WithMIMEVersion and Msg.SetMIMEVersion - - func TestNewMsgWithNoDefaultUserAgent(t *testing.T) { - m := NewMsg(WithNoDefaultUserAgent()) - if m.noDefaultUserAgent != true { - t.Errorf("WithNoDefaultUserAgent() failed. Expected: %t, got: %t", true, false) - } - } - -// Fuzzing tests - - func FuzzMsg_Subject(f *testing.F) { - f.Add("Testsubject") - f.Add("") - f.Add("This is a longer test subject.") - f.Add("Let's add some umlauts: üäöß") - f.Add("Or even emojis: ☝️💪👍") - f.Fuzz(func(t *testing.T, data string) { - m := NewMsg() - m.Subject(data) - m.Reset() - }) - } - - func FuzzMsg_From(f *testing.F) { - f.Add("Toni Tester ") - f.Add("") - f.Add("mail@server.com") - f.Fuzz(func(t *testing.T, data string) { - m := NewMsg() - if err := m.From(data); err != nil && - !strings.Contains(err.Error(), "failed to parse mail address") { - t.Errorf("failed set set FROM address: %s", err) - } - m.Reset() - }) - } -*/ +// uppercaseMiddleware is a middleware type that transforms the subject to uppercase. type uppercaseMiddleware struct{} +// Handle satisfies the Middleware interface for the uppercaseMiddlware func (mw uppercaseMiddleware) Handle(m *Msg) *Msg { s, ok := m.genHeader[HeaderSubject] if !ok { @@ -6826,12 +6456,15 @@ func (mw uppercaseMiddleware) Handle(m *Msg) *Msg { return m } +// Type satisfies the Middleware interface for the uppercaseMiddlware func (mw uppercaseMiddleware) Type() MiddlewareType { return "uppercase" } +// encodeMiddleware is a middleware type that transforms an "a" in the subject to an "@" type encodeMiddleware struct{} +// Handle satisfies the Middleware interface for the encodeMiddleware func (mw encodeMiddleware) Handle(m *Msg) *Msg { s, ok := m.genHeader[HeaderSubject] if !ok { @@ -6844,6 +6477,7 @@ func (mw encodeMiddleware) Handle(m *Msg) *Msg { return m } +// Type satisfies the Middleware interface for the encodeMiddleware func (mw encodeMiddleware) Type() MiddlewareType { return "encode" } @@ -6927,3 +6561,31 @@ func hasSendmail() bool { } return false } + +// Fuzzing tests +func FuzzMsg_Subject(f *testing.F) { + f.Add("Testsubject") + f.Add("") + f.Add("This is a longer test subject.") + f.Add("Let's add some umlauts: üäöß") + f.Add("Or even emojis: ☝️💪👍") + f.Fuzz(func(t *testing.T, data string) { + m := NewMsg() + m.Subject(data) + m.Reset() + }) +} + +func FuzzMsg_From(f *testing.F) { + f.Add("Toni Tester ") + f.Add("") + f.Add("mail@server.com") + f.Fuzz(func(t *testing.T, data string) { + m := NewMsg() + if err := m.From(data); err != nil && + !strings.Contains(err.Error(), "failed to parse mail address") { + t.Errorf("failed set set FROM address: %s", err) + } + m.Reset() + }) +}