From 4aed513154efaf4b9698983fcd6c05bc4d3dd221 Mon Sep 17 00:00:00 2001 From: Vee Zhang Date: Wed, 30 Mar 2022 13:36:25 +0800 Subject: [PATCH] add mail pkg --- go.mod | 4 +++ go.sum | 29 +++++++++++++++ mail/mail.go | 53 ++++++++++++++++++++++++++++ mail/mail_mock.go | 90 +++++++++++++++++++++++++++++++++++++++++++++++ mail/mail_test.go | 79 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 255 insertions(+) create mode 100644 mail/mail.go create mode 100644 mail/mail_mock.go create mode 100644 mail/mail_test.go diff --git a/go.mod b/go.mod index e16a768..6a8c748 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,11 @@ go 1.17 require ( github.com/go-resty/resty/v2 v2.7.0 + github.com/golang/mock v1.6.0 github.com/pkg/errors v0.9.1 + github.com/prashantv/gostub v1.1.0 github.com/stretchr/testify v1.7.1 + gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df ) require ( @@ -13,6 +16,7 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect + gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index ffe3a6e..2d6f101 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -14,24 +16,51 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/mail/mail.go b/mail/mail.go new file mode 100644 index 0000000..c9f75b2 --- /dev/null +++ b/mail/mail.go @@ -0,0 +1,53 @@ +//go:generate mockgen -package mail -destination mail_mock.go -source mail.go Client +package mail + +import ( + "gopkg.in/gomail.v2" +) + +const ( + ContentTypePlain = "text/plain" + ContentTypeHTML = "text/html" +) + +// Stubbed out for tests. +var newDialerSender = func(config Config) dialerSender { + return gomail.NewDialer(config.Host, config.Port, config.User, config.Password) +} + +type ( + Client interface { + Send(to []string, subject, contentType, body string) error + } + + Config struct { + Host string + Port int + User string + Password string + } + + defaultClient struct { + config Config + } + + dialerSender interface { + DialAndSend(m ...*gomail.Message) error + } +) + +func NewClient(config Config) Client { + return &defaultClient{ + config: config, + } +} + +func (c *defaultClient) Send(to []string, subject, contentType, body string) error { + m := gomail.NewMessage() + m.SetHeader("From", c.config.User) + m.SetHeader("To", to...) + m.SetHeader("Subject", subject) + m.SetBody(contentType, body) + + return newDialerSender(c.config).DialAndSend(m) +} diff --git a/mail/mail_mock.go b/mail/mail_mock.go new file mode 100644 index 0000000..230a384 --- /dev/null +++ b/mail/mail_mock.go @@ -0,0 +1,90 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: mail.go + +// Package mail is a generated GoMock package. +package mail + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + gomail "gopkg.in/gomail.v2" +) + +// MockClient is a mock of Client interface. +type MockClient struct { + ctrl *gomock.Controller + recorder *MockClientMockRecorder +} + +// MockClientMockRecorder is the mock recorder for MockClient. +type MockClientMockRecorder struct { + mock *MockClient +} + +// NewMockClient creates a new mock instance. +func NewMockClient(ctrl *gomock.Controller) *MockClient { + mock := &MockClient{ctrl: ctrl} + mock.recorder = &MockClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockClient) EXPECT() *MockClientMockRecorder { + return m.recorder +} + +// Send mocks base method. +func (m *MockClient) Send(to []string, subject, contentType, body string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Send", to, subject, contentType, body) + ret0, _ := ret[0].(error) + return ret0 +} + +// Send indicates an expected call of Send. +func (mr *MockClientMockRecorder) Send(to, subject, contentType, body interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockClient)(nil).Send), to, subject, contentType, body) +} + +// MockdialerSender is a mock of dialerSender interface. +type MockdialerSender struct { + ctrl *gomock.Controller + recorder *MockdialerSenderMockRecorder +} + +// MockdialerSenderMockRecorder is the mock recorder for MockdialerSender. +type MockdialerSenderMockRecorder struct { + mock *MockdialerSender +} + +// NewMockdialerSender creates a new mock instance. +func NewMockdialerSender(ctrl *gomock.Controller) *MockdialerSender { + mock := &MockdialerSender{ctrl: ctrl} + mock.recorder = &MockdialerSenderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockdialerSender) EXPECT() *MockdialerSenderMockRecorder { + return m.recorder +} + +// DialAndSend mocks base method. +func (m_2 *MockdialerSender) DialAndSend(m ...*gomail.Message) error { + m_2.ctrl.T.Helper() + varargs := []interface{}{} + for _, a := range m { + varargs = append(varargs, a) + } + ret := m_2.ctrl.Call(m_2, "DialAndSend", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// DialAndSend indicates an expected call of DialAndSend. +func (mr *MockdialerSenderMockRecorder) DialAndSend(m ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DialAndSend", reflect.TypeOf((*MockdialerSender)(nil).DialAndSend), m...) +} diff --git a/mail/mail_test.go b/mail/mail_test.go new file mode 100644 index 0000000..597e0c2 --- /dev/null +++ b/mail/mail_test.go @@ -0,0 +1,79 @@ +package mail + +import ( + "errors" + "testing" + + "github.com/prashantv/gostub" + "github.com/stretchr/testify/assert" + "gopkg.in/gomail.v2" +) + +type ( + dialerSenderMock struct { + send func(...*gomail.Message) error + } +) + +func (d dialerSenderMock) DialAndSend(m ...*gomail.Message) error { + return d.send(m...) +} + +func TestNewClient(t *testing.T) { + tests := []struct { + name string + config Config + to []string + subject string + contentType string + body string + err error + }{{ + name: "to:1", + config: Config{User: "u"}, + to: []string{"1"}, + subject: "subject", + contentType: ContentTypePlain, + body: "body", + }, { + name: "to:2", + config: Config{User: "u"}, + to: []string{"1"}, + subject: "subject", + contentType: ContentTypeHTML, + body: "body", + }, { + name: "error", + config: Config{User: "u"}, + to: []string{"1"}, + subject: "subject", + contentType: ContentTypePlain, + body: "body", + err: errors.New("send error"), + }} + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ast := assert.New(t) + stubs := gostub.StubFunc(&newDialerSender, dialerSenderMock{ + send: func(msg ...*gomail.Message) error { + if ast.Len(msg, 1) { + ast.Equal([]string{test.config.User}, msg[0].GetHeader("From")) + ast.Equal(test.to, msg[0].GetHeader("To")) + ast.Equal([]string{test.subject}, msg[0].GetHeader("Subject")) + } + return test.err + }, + }) + defer stubs.Reset() + c := NewClient(test.config) + err := c.Send(test.to, test.subject, test.contentType, test.body) + ast.Equal(test.err, err) + }) + } +} + +func Test_newDialerSender(t *testing.T) { + d := newDialerSender(Config{}) + assert.IsType(t, (*gomail.Dialer)(nil), d) +}