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  }