email_test.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package email
  2. import (
  3. "testing"
  4. "bytes"
  5. "crypto/rand"
  6. "io"
  7. "io/ioutil"
  8. "mime"
  9. "mime/multipart"
  10. "net/mail"
  11. "net/smtp"
  12. "os"
  13. )
  14. func TestEmailTextHtmlAttachment(t *testing.T) {
  15. e := NewEmail()
  16. e.From = "Jordan Wright <test@example.com>"
  17. e.To = []string{"test@example.com"}
  18. e.Bcc = []string{"test_bcc@example.com"}
  19. e.Cc = []string{"test_cc@example.com"}
  20. e.Subject = "Awesome Subject"
  21. e.Text = []byte("Text Body is, of course, supported!\n")
  22. e.HTML = []byte("<h1>Fancy Html is supported, too!</h1>\n")
  23. e.Attach(bytes.NewBufferString("Rad attachement"), "rad.txt", "text/plain; charset=utf-8")
  24. raw, err := e.Bytes()
  25. if err != nil {
  26. t.Fatal("Failed to render message: ", e)
  27. }
  28. msg, err := mail.ReadMessage(bytes.NewBuffer(raw))
  29. if err != nil {
  30. t.Fatal("Could not parse rendered message: ", err)
  31. }
  32. expectedHeaders := map[string]string{
  33. "To": "test@example.com",
  34. "From": "Jordan Wright <test@example.com>",
  35. "Cc": "test_cc@example.com",
  36. "Subject": "Awesome Subject",
  37. }
  38. for header, expected := range expectedHeaders {
  39. if val := msg.Header.Get(header); val != expected {
  40. t.Errorf("Wrong value for message header %s: %v != %v", header, expected, val)
  41. }
  42. }
  43. // Were the right headers set?
  44. ct := msg.Header.Get("Content-type")
  45. mt, params, err := mime.ParseMediaType(ct)
  46. if err != nil {
  47. t.Fatal("Content-type header is invalid: ", ct)
  48. } else if mt != "multipart/mixed" {
  49. t.Fatalf("Content-type expected \"multipart/mixed\", not %v", mt)
  50. }
  51. b := params["boundary"]
  52. if b == "" {
  53. t.Fatalf("Invalid or missing boundary parameter: ", b)
  54. }
  55. if len(params) != 1 {
  56. t.Fatal("Unexpected content-type parameters")
  57. }
  58. // Is the generated message parsable?
  59. mixed := multipart.NewReader(msg.Body, params["boundary"])
  60. text, err := mixed.NextPart()
  61. if err != nil {
  62. t.Fatalf("Could not find text component of email: ", err)
  63. }
  64. // Does the text portion match what we expect?
  65. mt, params, err = mime.ParseMediaType(text.Header.Get("Content-type"))
  66. if err != nil {
  67. t.Fatal("Could not parse message's Content-Type")
  68. } else if mt != "multipart/alternative" {
  69. t.Fatal("Message missing multipart/alternative")
  70. }
  71. mpReader := multipart.NewReader(text, params["boundary"])
  72. part, err := mpReader.NextPart()
  73. if err != nil {
  74. t.Fatal("Could not read plain text component of message: ", err)
  75. }
  76. plainText, err := ioutil.ReadAll(part)
  77. if err != nil {
  78. t.Fatal("Could not read plain text component of message: ", err)
  79. }
  80. if !bytes.Equal(plainText, []byte("Text Body is, of course, supported!\r\n")) {
  81. t.Fatalf("Plain text is broken: %#q", plainText)
  82. }
  83. // Check attachments.
  84. _, err = mixed.NextPart()
  85. if err != nil {
  86. t.Fatalf("Could not find attachemnt compoenent of email: ", err)
  87. }
  88. if _, err = mixed.NextPart(); err != io.EOF {
  89. t.Error("Expected only text and one attachement!")
  90. }
  91. }
  92. func ExampleGmail() {
  93. e := NewEmail()
  94. e.From = "Jordan Wright <test@gmail.com>"
  95. e.To = []string{"test@example.com"}
  96. e.Bcc = []string{"test_bcc@example.com"}
  97. e.Cc = []string{"test_cc@example.com"}
  98. e.Subject = "Awesome Subject"
  99. e.Text = []byte("Text Body is, of course, supported!\n")
  100. e.HTML = []byte("<h1>Fancy Html is supported, too!</h1>\n")
  101. e.Send("smtp.gmail.com:587", smtp.PlainAuth("", e.From, "password123", "smtp.gmail.com"))
  102. }
  103. func ExampleAttach() {
  104. e := NewEmail()
  105. e.AttachFile("test.txt")
  106. }
  107. func Test_base64Wrap(t *testing.T) {
  108. file := "I'm a file long enough to force the function to wrap a\n" +
  109. "couple of lines, but I stop short of the end of one line and\n" +
  110. "have some padding dangling at the end."
  111. encoded := "SSdtIGEgZmlsZSBsb25nIGVub3VnaCB0byBmb3JjZSB0aGUgZnVuY3Rpb24gdG8gd3JhcCBhCmNv\r\n" +
  112. "dXBsZSBvZiBsaW5lcywgYnV0IEkgc3RvcCBzaG9ydCBvZiB0aGUgZW5kIG9mIG9uZSBsaW5lIGFu\r\n" +
  113. "ZApoYXZlIHNvbWUgcGFkZGluZyBkYW5nbGluZyBhdCB0aGUgZW5kLg==\r\n"
  114. var buf bytes.Buffer
  115. base64Wrap(&buf, []byte(file))
  116. if !bytes.Equal(buf.Bytes(), []byte(encoded)) {
  117. t.Fatalf("Encoded file does not match expected: %#q != %#q", string(buf.Bytes()), encoded)
  118. }
  119. }
  120. func Test_quotedPrintEncode(t *testing.T) {
  121. var buf bytes.Buffer
  122. text := []byte("Dear reader!\n\n" +
  123. "This is a test email to try and capture some of the corner cases that exist within\n" +
  124. "the quoted-printable encoding.\n" +
  125. "There are some wacky parts like =, and this input assumes UNIX line breaks so\r\n" +
  126. "it can come out a little weird. Also, we need to support unicode so here's a fish: 🐟\n")
  127. expected := []byte("Dear reader!\r\n\r\n" +
  128. "This is a test email to try and capture some of the corner cases that exist=\r\n" +
  129. " within\r\n" +
  130. "the quoted-printable encoding.\r\n" +
  131. "There are some wacky parts like =3D, and this input assumes UNIX line break=\r\n" +
  132. "s so=0D\r\n" +
  133. "it can come out a little weird. Also, we need to support unicode so here's=\r\n" +
  134. " a fish: =F0=9F=90=9F\r\n")
  135. if err := quotePrintEncode(&buf, text); err != nil {
  136. t.Fatal("quotePrintEncode: ", err)
  137. }
  138. if b := buf.Bytes(); !bytes.Equal(b, expected) {
  139. t.Errorf("quotedPrintEncode generated incorrect results: %#q != %#q", b, expected)
  140. }
  141. }
  142. func Benchmark_quotedPrintEncode(b *testing.B) {
  143. text := []byte("Dear reader!\n\n" +
  144. "This is a test email to try and capture some of the corner cases that exist within\n" +
  145. "the quoted-printable encoding.\n" +
  146. "There are some wacky parts like =, and this input assumes UNIX line breaks so\r\n" +
  147. "it can come out a little weird. Also, we need to support unicode so here's a fish: 🐟\n")
  148. for i := 0; i <= b.N; i++ {
  149. if err := quotePrintEncode(ioutil.Discard, text); err != nil {
  150. panic(err)
  151. }
  152. }
  153. }
  154. func Benchmark_base64Wrap(b *testing.B) {
  155. // Reasonable base case; 128K random bytes
  156. file := make([]byte, 128*1024)
  157. if _, err := rand.Read(file); err != nil {
  158. panic(err)
  159. }
  160. for i := 0; i <= b.N; i++ {
  161. base64Wrap(ioutil.Discard, file)
  162. }
  163. }