email_test.go 5.7 KB

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