github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/services/mailservice/mail_test.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package mailservice 5 6 import ( 7 "bytes" 8 "context" 9 "io" 10 "io/ioutil" 11 "net" 12 "net/mail" 13 "net/smtp" 14 "os" 15 "strings" 16 "testing" 17 "time" 18 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 ) 22 23 func getConfig() *SMTPConfig { 24 server := os.Getenv("MM_EMAILSETTINGS_SMTPSERVER") 25 if server == "" { 26 server = "localhost" 27 } 28 port := os.Getenv("MM_EMAILSETTINGS_SMTPPORT") 29 if port == "" { 30 port = "10025" 31 } 32 33 return &SMTPConfig{ 34 ConnectionSecurity: "", 35 SkipServerCertificateVerification: false, 36 Hostname: "localhost", 37 ServerName: server, 38 Server: server, 39 Port: port, 40 ServerTimeout: 10, 41 Username: "", 42 Password: "", 43 EnableSMTPAuth: false, 44 SendEmailNotifications: true, 45 FeedbackName: "", 46 FeedbackEmail: "test@example.com", 47 ReplyToAddress: "test@example.com", 48 } 49 } 50 51 func TestMailConnectionFromConfig(t *testing.T) { 52 cfg := getConfig() 53 54 conn, err := ConnectToSMTPServer(cfg) 55 require.NoError(t, err, "Should connect to the SMTP Server %v", err) 56 57 _, err = NewSMTPClient(context.Background(), conn, cfg) 58 59 require.NoError(t, err, "Should get new SMTP client") 60 61 cfg.Server = "wrongServer" 62 cfg.Port = "553" 63 64 _, err = ConnectToSMTPServer(cfg) 65 66 require.Error(t, err, "Should not connect to the SMTP Server") 67 } 68 69 func TestMailConnectionAdvanced(t *testing.T) { 70 cfg := getConfig() 71 72 conn, err := ConnectToSMTPServerAdvanced(cfg) 73 require.NoError(t, err, "Should connect to the SMTP Server") 74 defer conn.Close() 75 76 _, err2 := NewSMTPClientAdvanced(context.Background(), conn, cfg) 77 require.NoError(t, err2, "Should get new SMTP client") 78 79 l, err3 := net.Listen("tcp", "localhost:") // emulate nc -l <random-port> 80 require.NoError(t, err3, "Should've open a network socket and listen") 81 defer l.Close() 82 cfg.Server = strings.Split(l.Addr().String(), ":")[0] 83 cfg.Port = strings.Split(l.Addr().String(), ":")[1] 84 cfg.ServerTimeout = 1 85 86 conn2, err := ConnectToSMTPServerAdvanced(cfg) 87 require.NoError(t, err, "Should connect to the SMTP Server") 88 defer conn2.Close() 89 90 ctx := context.Background() 91 ctx, cancel := context.WithTimeout(ctx, time.Second) 92 defer cancel() 93 94 _, err4 := NewSMTPClientAdvanced( 95 ctx, 96 conn2, 97 cfg, 98 ) 99 require.Error(t, err4, "Should get a timeout get while creating a new SMTP client") 100 assert.Contains(t, err4.Error(), "unable to connect to the SMTP server") 101 102 cfg.Server = "wrongServer" 103 cfg.Port = "553" 104 105 _, err5 := ConnectToSMTPServerAdvanced(cfg) 106 require.Error(t, err5, "Should not connect to the SMTP Server") 107 } 108 109 func TestSendMailUsingConfig(t *testing.T) { 110 cfg := getConfig() 111 112 var emailTo = "test@example.com" 113 var emailSubject = "Testing this email" 114 var emailBody = "This is a test from autobot" 115 var emailCC = "test@example.com" 116 117 //Delete all the messages before check the sample email 118 DeleteMailBox(emailTo) 119 120 err2 := SendMailUsingConfig(emailTo, emailSubject, emailBody, cfg, true, emailCC) 121 require.NoError(t, err2, "Should connect to the SMTP Server") 122 123 //Check if the email was send to the right email address 124 var resultsMailbox JSONMessageHeaderInbucket 125 err3 := RetryInbucket(5, func() error { 126 var err error 127 resultsMailbox, err = GetMailBox(emailTo) 128 return err 129 }) 130 if err3 != nil { 131 t.Log(err3) 132 t.Log("No email was received, maybe due load on the server. Skipping this verification") 133 } else { 134 if len(resultsMailbox) > 0 { 135 require.Contains(t, resultsMailbox[0].To[0], emailTo, "Wrong To: recipient") 136 resultsEmail, err := GetMessageFromMailbox(emailTo, resultsMailbox[0].ID) 137 require.NoError(t, err, "Could not get message from mailbox") 138 require.Contains(t, emailBody, resultsEmail.Body.Text, "Wrong received message %s", resultsEmail.Body.Text) 139 } 140 } 141 } 142 143 func TestSendMailWithEmbeddedFilesUsingConfig(t *testing.T) { 144 cfg := getConfig() 145 146 var emailTo = "test@example.com" 147 var emailSubject = "Testing this email" 148 var emailBody = "This is a test from autobot" 149 var emailCC = "test@example.com" 150 151 //Delete all the messages before check the sample email 152 DeleteMailBox(emailTo) 153 154 embeddedFiles := map[string]io.Reader{ 155 "test1.png": bytes.NewReader([]byte("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")), 156 "test2.png": bytes.NewReader([]byte("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")), 157 } 158 err2 := SendMailWithEmbeddedFilesUsingConfig(emailTo, emailSubject, emailBody, embeddedFiles, cfg, true, emailCC) 159 require.NoError(t, err2, "Should connect to the SMTP Server") 160 161 //Check if the email was send to the right email address 162 var resultsMailbox JSONMessageHeaderInbucket 163 err3 := RetryInbucket(5, func() error { 164 var err error 165 resultsMailbox, err = GetMailBox(emailTo) 166 return err 167 }) 168 if err3 != nil { 169 t.Log(err3) 170 t.Log("No email was received, maybe due load on the server. Skipping this verification") 171 } else { 172 if len(resultsMailbox) > 0 { 173 require.Contains(t, resultsMailbox[0].To[0], emailTo, "Wrong To: recipient") 174 resultsEmail, err := GetMessageFromMailbox(emailTo, resultsMailbox[0].ID) 175 require.NoError(t, err, "Could not get message from mailbox") 176 require.Contains(t, emailBody, resultsEmail.Body.Text, "Wrong received message %s", resultsEmail.Body.Text) 177 // Usign the message size because the inbucket API doesn't return embedded attachments through the API 178 require.Greater(t, resultsEmail.Size, 1500, "the file size should be more because the embedded attachemtns") 179 } 180 } 181 } 182 183 func TestSendMailUsingConfigAdvanced(t *testing.T) { 184 cfg := getConfig() 185 186 //Delete all the messages before check the sample email 187 DeleteMailBox("test2@example.com") 188 189 // create two files with the same name that will both be attached to the email 190 file1, err := ioutil.TempFile("", "*") 191 require.NoError(t, err) 192 defer os.Remove(file1.Name()) 193 file1.Write([]byte("hello world")) 194 file1.Close() 195 file2, err := ioutil.TempFile("", "*") 196 197 require.NoError(t, err) 198 defer os.Remove(file2.Name()) 199 file2.Write([]byte("foo bar")) 200 file2.Close() 201 202 embeddedFiles := map[string]io.Reader{ 203 "test": bytes.NewReader([]byte("test data")), 204 } 205 206 headers := make(map[string]string) 207 headers["TestHeader"] = "TestValue" 208 209 mail := mailData{ 210 mimeTo: "test@example.com", 211 smtpTo: "test2@example.com", 212 from: mail.Address{Name: "Nobody", Address: "nobody@mattermost.com"}, 213 replyTo: mail.Address{Name: "ReplyTo", Address: "reply_to@mattermost.com"}, 214 subject: "Testing this email", 215 htmlBody: "This is a test from autobot", 216 embeddedFiles: embeddedFiles, 217 mimeHeaders: headers, 218 } 219 220 err = sendMailUsingConfigAdvanced(mail, cfg, true) 221 require.NoError(t, err, "Should connect to the STMP Server: %v", err) 222 223 //Check if the email was send to the right email address 224 var resultsMailbox JSONMessageHeaderInbucket 225 err = RetryInbucket(5, func() error { 226 var mailErr error 227 resultsMailbox, mailErr = GetMailBox(mail.smtpTo) 228 return mailErr 229 }) 230 require.NoError(t, err, "No emails found for address %s. error: %v", mail.smtpTo, err) 231 require.NotEqual(t, len(resultsMailbox), 0) 232 233 require.Contains(t, resultsMailbox[0].To[0], mail.mimeTo, "Wrong To recipient") 234 235 resultsEmail, err := GetMessageFromMailbox(mail.smtpTo, resultsMailbox[0].ID) 236 require.NoError(t, err) 237 238 require.Contains(t, mail.htmlBody, resultsEmail.Body.Text, "Wrong received message") 239 240 // 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 241 assert.Equal(t, mail.mimeTo, resultsEmail.Header["To"][0]) 242 243 // verify that the MIME from address is correct - unfortunately, we can't verify the SMTP from address 244 assert.Equal(t, mail.from.String(), resultsEmail.Header["From"][0]) 245 246 // check that the custom mime headers came through - header case seems to get mutated 247 assert.Equal(t, "TestValue", resultsEmail.Header["Testheader"][0]) 248 } 249 250 func TestAuthMethods(t *testing.T) { 251 auth := &authChooser{ 252 config: &SMTPConfig{ 253 Username: "test", 254 Password: "fakepass", 255 ServerName: "fakeserver", 256 Server: "fakeserver", 257 Port: "25", 258 }, 259 } 260 tests := []struct { 261 desc string 262 server *smtp.ServerInfo 263 err string 264 }{ 265 { 266 desc: "auth PLAIN success", 267 server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"PLAIN"}, TLS: true}, 268 }, 269 { 270 desc: "auth PLAIN unencrypted connection fail", 271 server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"PLAIN"}, TLS: false}, 272 err: "unencrypted connection", 273 }, 274 { 275 desc: "auth PLAIN wrong host name", 276 server: &smtp.ServerInfo{Name: "wrongServer:999", Auth: []string{"PLAIN"}, TLS: true}, 277 err: "wrong host name", 278 }, 279 { 280 desc: "auth LOGIN success", 281 server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"LOGIN"}, TLS: true}, 282 }, 283 { 284 desc: "auth LOGIN unencrypted connection fail", 285 server: &smtp.ServerInfo{Name: "wrongServer:999", Auth: []string{"LOGIN"}, TLS: true}, 286 err: "wrong host name", 287 }, 288 { 289 desc: "auth LOGIN wrong host name", 290 server: &smtp.ServerInfo{Name: "fakeserver:25", Auth: []string{"LOGIN"}, TLS: false}, 291 err: "unencrypted connection", 292 }, 293 } 294 295 for i, test := range tests { 296 t.Run(test.desc, func(t *testing.T) { 297 _, _, err := auth.Start(test.server) 298 got := "" 299 if err != nil { 300 got = err.Error() 301 } 302 assert.True(t, got == test.err, "%d. got error = %q; want %q", i, got, test.err) 303 }) 304 } 305 } 306 307 type mockMailer struct { 308 data []byte 309 } 310 311 func (m *mockMailer) Mail(string) error { return nil } 312 func (m *mockMailer) Rcpt(string) error { return nil } 313 func (m *mockMailer) Data() (io.WriteCloser, error) { return m, nil } 314 func (m *mockMailer) Write(p []byte) (int, error) { 315 m.data = append(m.data, p...) 316 return len(p), nil 317 } 318 func (m *mockMailer) Close() error { return nil } 319 320 func TestSendMail(t *testing.T) { 321 dir, err := ioutil.TempDir(".", "mail-test-") 322 require.NoError(t, err) 323 defer os.RemoveAll(dir) 324 mocm := &mockMailer{} 325 326 testCases := map[string]struct { 327 replyTo mail.Address 328 contains string 329 notContains string 330 }{ 331 "adds reply-to header": { 332 mail.Address{Address: "foo@test.com"}, 333 "\r\nReply-To: <foo@test.com>\r\n", 334 "", 335 }, 336 "doesn't add reply-to header": { 337 mail.Address{}, 338 "", 339 "\r\nReply-To:", 340 }, 341 } 342 343 for testName, tc := range testCases { 344 t.Run(testName, func(t *testing.T) { 345 mail := mailData{"", "", mail.Address{}, "", tc.replyTo, "", "", nil, nil} 346 err = SendMail(mocm, mail, time.Now()) 347 require.NoError(t, err) 348 if tc.contains != "" { 349 require.Contains(t, string(mocm.data), tc.contains) 350 } 351 if tc.notContains != "" { 352 require.NotContains(t, string(mocm.data), tc.notContains) 353 } 354 mocm.data = []byte{} 355 }) 356 } 357 }