github.com/xzl8028/xenia-server@v0.0.0-20190809101854-18450a97da63/services/mailservice/mail_test.go (about) 1 // Copyright (c) 2017-present Xenia, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package mailservice 5 6 import ( 7 "bytes" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "os" 12 "strings" 13 "testing" 14 "time" 15 16 "net/mail" 17 "net/smtp" 18 19 "github.com/xzl8028/xenia-server/config" 20 "github.com/xzl8028/xenia-server/model" 21 "github.com/xzl8028/xenia-server/services/filesstore" 22 "github.com/xzl8028/xenia-server/utils" 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 ) 26 27 func TestMailConnectionFromConfig(t *testing.T) { 28 fs, err := config.NewFileStore("config.json", false) 29 require.Nil(t, err) 30 31 cfg := fs.Get() 32 33 if conn, err := ConnectToSMTPServer(cfg); err != nil { 34 t.Log(err) 35 t.Fatal("Should connect to the STMP Server") 36 } else { 37 if _, err1 := NewSMTPClient(conn, cfg); err1 != nil { 38 t.Log(err) 39 t.Fatal("Should get new smtp client") 40 } 41 } 42 43 *cfg.EmailSettings.SMTPServer = "wrongServer" 44 *cfg.EmailSettings.SMTPPort = "553" 45 46 if _, err := ConnectToSMTPServer(cfg); err == nil { 47 t.Log(err) 48 t.Fatal("Should not to the STMP Server") 49 } 50 } 51 52 func TestMailConnectionAdvanced(t *testing.T) { 53 fs, err := config.NewFileStore("config.json", false) 54 require.Nil(t, err) 55 56 cfg := fs.Get() 57 58 if conn, err := ConnectToSMTPServerAdvanced( 59 &SmtpConnectionInfo{ 60 ConnectionSecurity: *cfg.EmailSettings.ConnectionSecurity, 61 SkipCertVerification: *cfg.EmailSettings.SkipServerCertificateVerification, 62 SmtpServerName: *cfg.EmailSettings.SMTPServer, 63 SmtpServerHost: *cfg.EmailSettings.SMTPServer, 64 SmtpPort: *cfg.EmailSettings.SMTPPort, 65 }, 66 ); err != nil { 67 t.Log(err) 68 t.Fatal("Should connect to the STMP Server") 69 } else { 70 if _, err1 := NewSMTPClientAdvanced( 71 conn, 72 utils.GetHostnameFromSiteURL(*cfg.ServiceSettings.SiteURL), 73 &SmtpConnectionInfo{ 74 ConnectionSecurity: *cfg.EmailSettings.ConnectionSecurity, 75 SkipCertVerification: *cfg.EmailSettings.SkipServerCertificateVerification, 76 SmtpServerName: *cfg.EmailSettings.SMTPServer, 77 SmtpServerHost: *cfg.EmailSettings.SMTPServer, 78 SmtpPort: *cfg.EmailSettings.SMTPPort, 79 Auth: *cfg.EmailSettings.EnableSMTPAuth, 80 SmtpUsername: *cfg.EmailSettings.SMTPUsername, 81 SmtpPassword: *cfg.EmailSettings.SMTPPassword, 82 }, 83 ); err1 != nil { 84 t.Log(err) 85 t.Fatal("Should get new smtp client") 86 } 87 } 88 89 if _, err := ConnectToSMTPServerAdvanced( 90 &SmtpConnectionInfo{ 91 ConnectionSecurity: *cfg.EmailSettings.ConnectionSecurity, 92 SkipCertVerification: *cfg.EmailSettings.SkipServerCertificateVerification, 93 SmtpServerName: "wrongServer", 94 SmtpServerHost: "wrongServer", 95 SmtpPort: "553", 96 }, 97 ); err == nil { 98 t.Log(err) 99 t.Fatal("Should not to the STMP Server") 100 } 101 102 } 103 104 func TestSendMailUsingConfig(t *testing.T) { 105 utils.T = utils.GetUserTranslations("en") 106 107 fs, err := config.NewFileStore("config.json", false) 108 require.Nil(t, err) 109 110 cfg := fs.Get() 111 112 var emailTo = "test@example.com" 113 var emailSubject = "Testing this email" 114 var emailBody = "This is a test from autobot" 115 116 //Delete all the messages before check the sample email 117 DeleteMailBox(emailTo) 118 119 if err := SendMailUsingConfig(emailTo, emailSubject, emailBody, cfg, true); err != nil { 120 t.Log(err) 121 t.Fatal("Should connect to the STMP Server") 122 } else { 123 //Check if the email was send to the right email address 124 var resultsMailbox JSONMessageHeaderInbucket 125 err := RetryInbucket(5, func() error { 126 var err error 127 resultsMailbox, err = GetMailBox(emailTo) 128 return err 129 }) 130 if err != nil { 131 t.Log(err) 132 t.Log("No email was received, maybe due load on the server. Disabling this verification") 133 } 134 if err == nil && len(resultsMailbox) > 0 { 135 if !strings.ContainsAny(resultsMailbox[0].To[0], emailTo) { 136 t.Fatal("Wrong To recipient") 137 } else { 138 if resultsEmail, err := GetMessageFromMailbox(emailTo, resultsMailbox[0].ID); err == nil { 139 if !strings.Contains(resultsEmail.Body.Text, emailBody) { 140 t.Log(resultsEmail.Body.Text) 141 t.Fatal("Received message") 142 } 143 } 144 } 145 } 146 } 147 } 148 149 func TestSendMailUsingConfigAdvanced(t *testing.T) { 150 utils.T = utils.GetUserTranslations("en") 151 152 fs, err := config.NewFileStore("config.json", false) 153 require.Nil(t, err) 154 155 cfg := fs.Get() 156 157 var mimeTo = "test@example.com" 158 var smtpTo = "test2@example.com" 159 var from = mail.Address{Name: "Nobody", Address: "nobody@xenia.com"} 160 var replyTo = mail.Address{Name: "ReplyTo", Address: "reply_to@xenia.com"} 161 var emailSubject = "Testing this email" 162 var emailBody = "This is a test from autobot" 163 164 //Delete all the messages before check the sample email 165 DeleteMailBox(smtpTo) 166 167 fileBackend, err := filesstore.NewFileBackend(&cfg.FileSettings, true) 168 assert.Nil(t, err) 169 170 // create two files with the same name that will both be attached to the email 171 fileName := "file.txt" 172 filePath1 := fmt.Sprintf("test1/%s", fileName) 173 filePath2 := fmt.Sprintf("test2/%s", fileName) 174 fileContents1 := []byte("hello world") 175 fileContents2 := []byte("foo bar") 176 _, err = fileBackend.WriteFile(bytes.NewReader(fileContents1), filePath1) 177 assert.Nil(t, err) 178 _, err = fileBackend.WriteFile(bytes.NewReader(fileContents2), filePath2) 179 assert.Nil(t, err) 180 defer fileBackend.RemoveFile(filePath1) 181 defer fileBackend.RemoveFile(filePath2) 182 183 attachments := make([]*model.FileInfo, 2) 184 attachments[0] = &model.FileInfo{ 185 Name: fileName, 186 Path: filePath1, 187 } 188 attachments[1] = &model.FileInfo{ 189 Name: fileName, 190 Path: filePath2, 191 } 192 193 headers := make(map[string]string) 194 headers["TestHeader"] = "TestValue" 195 196 if err := SendMailUsingConfigAdvanced(mimeTo, smtpTo, from, replyTo, emailSubject, emailBody, attachments, headers, cfg, true); err != nil { 197 t.Log(err) 198 t.Fatal("Should connect to the STMP Server") 199 } else { 200 //Check if the email was send to the right email address 201 var resultsMailbox JSONMessageHeaderInbucket 202 err := RetryInbucket(5, func() error { 203 var err error 204 resultsMailbox, err = GetMailBox(smtpTo) 205 return err 206 }) 207 if err != nil { 208 t.Log(err) 209 t.Fatal("No emails found for address " + smtpTo) 210 } 211 if err == nil && len(resultsMailbox) > 0 { 212 if !strings.ContainsAny(resultsMailbox[0].To[0], smtpTo) { 213 t.Fatal("Wrong To recipient") 214 } else { 215 if resultsEmail, err := GetMessageFromMailbox(smtpTo, resultsMailbox[0].ID); err == nil { 216 if !strings.Contains(resultsEmail.Body.Text, emailBody) { 217 t.Log(resultsEmail.Body.Text) 218 t.Fatal("Received message") 219 } 220 221 // verify that the To header of the email message is set to the MIME recipient, even though we got it out of the SMTP recipient's email inbox 222 assert.Equal(t, mimeTo, resultsEmail.Header["To"][0]) 223 224 // verify that the MIME from address is correct - unfortunately, we can't verify the SMTP from address 225 assert.Equal(t, from.String(), resultsEmail.Header["From"][0]) 226 227 // check that the custom mime headers came through - header case seems to get mutated 228 assert.Equal(t, "TestValue", resultsEmail.Header["Testheader"][0]) 229 230 // ensure that the attachments were successfully sent 231 assert.Len(t, resultsEmail.Attachments, 2) 232 assert.Equal(t, fileName, resultsEmail.Attachments[0].Filename) 233 assert.Equal(t, fileName, resultsEmail.Attachments[1].Filename) 234 attachment1 := string(resultsEmail.Attachments[0].Bytes) 235 attachment2 := string(resultsEmail.Attachments[1].Bytes) 236 if attachment1 == string(fileContents1) { 237 assert.Equal(t, attachment2, string(fileContents2)) 238 } else if attachment1 == string(fileContents2) { 239 assert.Equal(t, attachment2, string(fileContents1)) 240 } else { 241 assert.Fail(t, "Unrecognized attachment contents") 242 } 243 } 244 } 245 } 246 } 247 } 248 249 func TestAuthMethods(t *testing.T) { 250 auth := &authChooser{ 251 connectionInfo: &SmtpConnectionInfo{ 252 SmtpUsername: "test", 253 SmtpPassword: "fakepass", 254 SmtpServerName: "fakeserver", 255 SmtpServerHost: "fakeserver", 256 SmtpPort: "25", 257 }, 258 } 259 tests := []struct { 260 desc string 261 server *smtp.ServerInfo 262 err string 263 }{ 264 { 265 desc: "auth PLAIN success", 266 server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"PLAIN"}, TLS: true}, 267 }, 268 { 269 desc: "auth PLAIN unencrypted connection fail", 270 server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"PLAIN"}, TLS: false}, 271 err: "unencrypted connection", 272 }, 273 { 274 desc: "auth PLAIN wrong host name", 275 server: &smtp.ServerInfo{Name: "wrongServer:999", Auth: []string{"PLAIN"}, TLS: true}, 276 err: "wrong host name", 277 }, 278 { 279 desc: "auth LOGIN success", 280 server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"LOGIN"}, TLS: true}, 281 }, 282 { 283 desc: "auth LOGIN unencrypted connection fail", 284 server: &smtp.ServerInfo{Name: "wrongServer:999", Auth: []string{"LOGIN"}, TLS: true}, 285 err: "wrong host name", 286 }, 287 { 288 desc: "auth LOGIN wrong host name", 289 server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"LOGIN"}, TLS: false}, 290 err: "unencrypted connection", 291 }, 292 } 293 294 for i, test := range tests { 295 t.Run(test.desc, func(t *testing.T) { 296 _, _, err := auth.Start(test.server) 297 got := "" 298 if err != nil { 299 got = err.Error() 300 } 301 if got != test.err { 302 t.Errorf("%d. got error = %q; want %q", i, got, test.err) 303 } 304 }) 305 } 306 } 307 308 type mockMailer struct { 309 data []byte 310 } 311 312 func (m *mockMailer) Mail(string) error { return nil } 313 func (m *mockMailer) Rcpt(string) error { return nil } 314 func (m *mockMailer) Data() (io.WriteCloser, error) { return m, nil } 315 func (m *mockMailer) Write(p []byte) (int, error) { 316 m.data = append(m.data, p...) 317 return len(p), nil 318 } 319 func (m *mockMailer) Close() error { return nil } 320 321 func TestSendMail(t *testing.T) { 322 dir, err := ioutil.TempDir(".", "mail-test-") 323 require.Nil(t, err) 324 defer os.RemoveAll(dir) 325 settings := model.FileSettings{ 326 DriverName: model.NewString(model.IMAGE_DRIVER_LOCAL), 327 Directory: &dir, 328 } 329 mockBackend, appErr := filesstore.NewFileBackend(&settings, true) 330 require.Nil(t, appErr) 331 mocm := &mockMailer{} 332 333 testCases := map[string]struct { 334 replyTo mail.Address 335 contains string 336 notContains string 337 }{ 338 "adds reply-to header": { 339 mail.Address{Address: "foo@test.com"}, 340 "\r\nReply-To: <foo@test.com>\r\n", 341 "", 342 }, 343 "doesn't add reply-to header": { 344 mail.Address{}, 345 "", 346 "\r\nReply-To:", 347 }, 348 } 349 350 for testName, tc := range testCases { 351 t.Run(testName, func(t *testing.T) { 352 appErr = SendMail(mocm, "", "", mail.Address{}, tc.replyTo, "", "", nil, nil, mockBackend, time.Now()) 353 require.Nil(t, appErr) 354 if len(tc.contains) > 0 { 355 require.Contains(t, string(mocm.data), tc.contains) 356 } 357 if len(tc.notContains) > 0 { 358 require.NotContains(t, string(mocm.data), tc.notContains) 359 } 360 mocm.data = []byte{} 361 }) 362 } 363 }