github.com/go-email-validator/go-email-validator@v0.0.0-20230409163946-b8b9e6a0552e/pkg/ev/evsmtp/mock_test.go (about)

     1  package evsmtp
     2  
     3  import (
     4  	"bytes"
     5  	"github.com/go-email-validator/go-email-validator/pkg/ev/evsmtp/smtpclient"
     6  	"github.com/go-email-validator/go-email-validator/pkg/ev/evtests"
     7  	"github.com/stretchr/testify/require"
     8  	"io"
     9  	"net/smtp"
    10  	"reflect"
    11  	"strings"
    12  	"sync"
    13  	"testing"
    14  	"time"
    15  )
    16  
    17  const (
    18  	helloName = "helloName"
    19  
    20  	smClient         = "Client"
    21  	smHello          = "Hello "
    22  	smHelloLocalhost = "Hello localhost"
    23  	smAuth           = "Auth"
    24  	smMail           = "Mail "
    25  	smRCPTs          = "Rcpts "
    26  	smData           = "Data"
    27  	smWrite          = "Write"
    28  	smQuit           = "Quit"
    29  	smClose          = "Close"
    30  	smWriteMessage   = "Write message"
    31  	smWCloseWriter   = "Close writer"
    32  )
    33  
    34  var (
    35  	testUser           = "testUser"
    36  	testPwd            = "testPwd"
    37  	testHost           = "testHost"
    38  	testAuth           = smtp.PlainAuth("", testUser, testPwd, testHost)
    39  	testMsg            = "msg"
    40  	mockWriterInstance = &mockWriter{}
    41  )
    42  
    43  func stringsJoin(strs []string) string {
    44  	return strings.Join(strs, ",")
    45  }
    46  
    47  // TODO create mock by gomock
    48  type sendMailWant struct {
    49  	stage   string
    50  	message string
    51  	sleep   time.Duration
    52  	ret     interface{}
    53  }
    54  
    55  type mockSendMail struct {
    56  	mu   sync.Mutex
    57  	t    *testing.T
    58  	i    int
    59  	want []sendMailWant
    60  }
    61  
    62  func (s *mockSendMail) Client() smtpclient.SMTPClient {
    63  	return s.do(smClient).(smtpclient.SMTPClient)
    64  }
    65  
    66  func (s *mockSendMail) Hello(localName string) error {
    67  	return evtests.ToError(s.do(smHello + localName))
    68  }
    69  
    70  func (s *mockSendMail) Auth(a smtp.Auth) error {
    71  	ret := s.do(smAuth).([]interface{})
    72  	if !reflect.DeepEqual(a, ret[0]) {
    73  		s.t.Errorf("Invalid auth, got %#v, want %#v", a, testAuth)
    74  	}
    75  	return evtests.ToError(ret[1])
    76  }
    77  
    78  func (s *mockSendMail) Mail(from string) error {
    79  	return evtests.ToError(s.do(smMail + from))
    80  }
    81  
    82  func (s *mockSendMail) RCPTs(addr []string) map[string]error {
    83  	err := s.do(smRCPTs + stringsJoin(addr))
    84  
    85  	if err == nil {
    86  		return nil
    87  	}
    88  
    89  	return map[string]error{
    90  		addr[0]: evtests.ToError(err),
    91  	}
    92  }
    93  
    94  func (s *mockSendMail) Data() (io.WriteCloser, error) {
    95  	return &mockWriter{s: s, want: testMsg}, evtests.ToError(s.do(smData))
    96  }
    97  
    98  func (s *mockSendMail) Write(w io.WriteCloser, msg []byte) error {
    99  	w.Write(msg)
   100  	w.Close()
   101  
   102  	return evtests.ToError(s.do(smWrite))
   103  }
   104  
   105  func (s *mockSendMail) Quit() error {
   106  	return evtests.ToError(s.do(smQuit))
   107  }
   108  
   109  func (s *mockSendMail) Close() error {
   110  	return evtests.ToError(s.do(smClose))
   111  }
   112  
   113  func (s *mockSendMail) do(cmd string) interface{} {
   114  	s.mu.Lock()
   115  	defer s.mu.Unlock()
   116  	if s.i >= len(s.want) {
   117  		s.t.Fatalf("Invalid command %q", cmd)
   118  	}
   119  
   120  	want := s.want[s.i]
   121  	if cmd != want.message {
   122  		s.t.Fatalf("Invalid command, got %q, want %q", cmd, want.message)
   123  	}
   124  	s.i++
   125  
   126  	time.Sleep(want.sleep)
   127  
   128  	return want.ret
   129  }
   130  
   131  type mockWriter struct {
   132  	want string
   133  	s    *mockSendMail
   134  	buf  bytes.Buffer
   135  }
   136  
   137  func (w *mockWriter) Write(p []byte) (int, error) {
   138  	if w.buf.Len() == 0 {
   139  		w.s.do(smWriteMessage)
   140  	}
   141  	w.buf.Write(p)
   142  	return len(p), nil
   143  }
   144  
   145  func (w *mockWriter) Close() error {
   146  	require.Equal(w.s.t, w.buf.String(), w.want)
   147  	w.s.do(smWCloseWriter)
   148  	return nil
   149  }
   150  
   151  var defaultWantMap = map[string]sendMailWant{
   152  	smHello: {
   153  		message: smHelloLocalhost,
   154  	},
   155  	smAuth: {
   156  		message: smAuth,
   157  		ret:     []interface{}{nil, nil},
   158  	},
   159  	smMail: {
   160  		message: smMail + emailFrom.String(),
   161  		ret:     nil,
   162  	},
   163  	smRCPTs: {
   164  		message: smRCPTs + randomAddress.String(),
   165  		ret:     nil,
   166  	},
   167  }
   168  
   169  var quitStageWant = sendMailWant{
   170  	message: smQuit,
   171  	ret:     nil,
   172  }
   173  
   174  var closeStageWant = sendMailWant{
   175  	message: smClose,
   176  	ret:     nil,
   177  }
   178  
   179  var wantSuccessList = []string{
   180  	smHello,
   181  	smAuth,
   182  	smMail,
   183  	smRCPTs, // only random email call rcpt
   184  	smQuit,
   185  }
   186  
   187  func failWant(failStage *sendMailWant, withClose bool) []sendMailWant {
   188  	wants := make([]sendMailWant, 0)
   189  	for _, stage := range wantSuccessList {
   190  
   191  		var want sendMailWant
   192  		want, ok := defaultWantMap[stage]
   193  		if !ok {
   194  			want = sendMailWant{message: stage}
   195  		}
   196  
   197  		if failStage != nil && stage == failStage.stage {
   198  			wants = append(wants, *failStage)
   199  			break
   200  		}
   201  		wants = append(wants, want)
   202  	}
   203  
   204  	if withClose {
   205  		wants = append(wants, closeStageWant)
   206  	}
   207  
   208  	return wants
   209  }