From f2771fa2cc4fc5f580e3067b7dc05f68d50a2842 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 6 Feb 2020 10:32:34 +0800 Subject: [PATCH 1/5] Add notifications --- cmd.go | 8 +++++ notifier.go | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 notifier.go diff --git a/cmd.go b/cmd.go index b02153d..d987736 100644 --- a/cmd.go +++ b/cmd.go @@ -113,6 +113,8 @@ func (cmd commandAppe) Execute(conn *Conn, param string) { targetPath := conn.buildPath(param) conn.writeMessage(150, "Data transfer starting") + notifiers.BeforeFilePut(conn, targetPath) + bytes, err := conn.driver.PutFile(targetPath, conn.dataConn, true) if err == nil { msg := "OK, received " + strconv.Itoa(int(bytes)) + " bytes" @@ -120,6 +122,8 @@ func (cmd commandAppe) Execute(conn *Conn, param string) { } else { conn.writeMessage(450, fmt.Sprint("error during transfer: ", err)) } + + notifiers.AfterFilePut(conn, targetPath, err) } type commandOpts struct{} @@ -261,12 +265,16 @@ func (cmd commandDele) RequireAuth() bool { func (cmd commandDele) Execute(conn *Conn, param string) { path := conn.buildPath(param) + notifiers.BeforeFileDeleted(path) + err := conn.driver.DeleteFile(path) if err == nil { conn.writeMessage(250, "File deleted") } else { conn.writeMessage(550, fmt.Sprint("File delete failed: ", err)) } + + notifiers.AfterFileDeleted(path, err) } // commandEprt responds to the EPRT FTP command. It allows the client to diff --git a/notifier.go b/notifier.go new file mode 100644 index 0000000..0e240d2 --- /dev/null +++ b/notifier.go @@ -0,0 +1,92 @@ +// Copyright 2020 The goftp Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package server + +// Notifier represents a notification operator interface +type Notifier interface { + BeforeUserLogin(conn *Conn, userName, password string) + BeforeFilePut(conn *Conn, dstPath string) + BeforeFileDeleted(dstPath string) + BeforeDirCreated(dstPath string) + BeforeDirDeleted(dstPath string) + BeforeFileDownload(dstPath string) + AfterUserLogin(conn *Conn, userName, password string) + AfterFilePut(conn *Conn, dstPath string, err error) + AfterFileDeleted(dstPath string, err error) + AfterDirCreated(dstPath string) + AfterDirDeleted(dstPath string) + AfterFileDownload(dstPath string) +} + +type notifierList []Notifier + +var ( + notifiers notifierList +) + +// RegisterNotifer registers a notifier +func RegisterNotifer(notifier Notifier) { + notifiers = append(notifiers, notifier) +} + +func (notifiers notifierList) BeforeFilePut(conn *Conn, dstPath string) { + for _, notifier := range notifiers { + notifier.BeforeFilePut(conn, dstPath) + } +} + +func (notifierList) BeforeFileDeleted(dstPath string) { + for _, notifier := range notifiers { + notifier.BeforeFileDeleted(dstPath) + } +} + +func (notifierList) BeforeDirCreated(dstPath string) { + for _, notifier := range notifiers { + notifier.BeforeDirCreated(dstPath) + } +} + +func (notifierList) BeforeDirDeleted(dstPath string) { + for _, notifier := range notifiers { + notifier.BeforeDirDeleted(dstPath) + } +} + +func (notifierList) BeforeFileDownload(dstPath string) { + for _, notifier := range notifiers { + notifier.BeforeFileDownload(dstPath) + } +} + +func (notifierList) AfterFilePut(conn *Conn, dstPath string, err error) { + for _, notifier := range notifiers { + notifier.AfterFilePut(conn, dstPath, err) + } +} + +func (notifierList) AfterFileDeleted(dstPath string, err error) { + for _, notifier := range notifiers { + notifier.AfterFileDeleted(dstPath, err) + } +} + +func (notifierList) AfterDirCreated(dstPath string) { + for _, notifier := range notifiers { + notifier.AfterDirCreated(dstPath) + } +} + +func (notifierList) AfterDirDeleted(dstPath string) { + for _, notifier := range notifiers { + notifier.AfterDirDeleted(dstPath) + } +} + +func (notifierList) AfterFileDownload(dstPath string) { + for _, notifier := range notifiers { + notifier.AfterFileDownload(dstPath) + } +} -- 2.40.1 From a22113432ec615aee47e994acc0243186e8ab968 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 9 Feb 2020 16:43:46 +0800 Subject: [PATCH 2/5] Finished notifier interface and add test --- cmd.go | 36 +++++++---- conn.go | 2 + driver_minio_test.go | 2 +- notifier.go | 97 +++++++++++++++++------------ notifier_test.go | 144 +++++++++++++++++++++++++++++++++++++++++++ server.go | 6 ++ server_test.go | 7 ++- 7 files changed, 240 insertions(+), 54 deletions(-) create mode 100644 notifier_test.go diff --git a/cmd.go b/cmd.go index d987736..704fb60 100644 --- a/cmd.go +++ b/cmd.go @@ -113,17 +113,16 @@ func (cmd commandAppe) Execute(conn *Conn, param string) { targetPath := conn.buildPath(param) conn.writeMessage(150, "Data transfer starting") - notifiers.BeforeFilePut(conn, targetPath) - - bytes, err := conn.driver.PutFile(targetPath, conn.dataConn, true) + conn.server.notifiers.BeforePutFile(conn, targetPath) + size, err := conn.driver.PutFile(targetPath, conn.dataConn, true) + conn.server.notifiers.AfterFilePut(conn, targetPath, size, err) if err == nil { - msg := "OK, received " + strconv.Itoa(int(bytes)) + " bytes" + msg := fmt.Sprintf("OK, received %d bytes", size) conn.writeMessage(226, msg) } else { conn.writeMessage(450, fmt.Sprint("error during transfer: ", err)) } - notifiers.AfterFilePut(conn, targetPath, err) } type commandOpts struct{} @@ -239,7 +238,9 @@ func (cmd commandCwd) Execute(conn *Conn, param string) { return } + conn.server.notifiers.BeforeChangeCurDir(conn, conn.curDir, path) err = conn.changeCurDir(path) + conn.server.notifiers.AfterCurDirChanged(conn, conn.curDir, path, err) if err == nil { conn.writeMessage(250, "Directory changed to "+path) } else { @@ -265,16 +266,14 @@ func (cmd commandDele) RequireAuth() bool { func (cmd commandDele) Execute(conn *Conn, param string) { path := conn.buildPath(param) - notifiers.BeforeFileDeleted(path) - + conn.server.notifiers.BeforeDeleteFile(conn, path) err := conn.driver.DeleteFile(path) + conn.server.notifiers.AfterFileDeleted(conn, path, err) if err == nil { conn.writeMessage(250, "File deleted") } else { conn.writeMessage(550, fmt.Sprint("File delete failed: ", err)) } - - notifiers.AfterFileDeleted(path, err) } // commandEprt responds to the EPRT FTP command. It allows the client to @@ -554,7 +553,9 @@ func (cmd commandMkd) RequireAuth() bool { func (cmd commandMkd) Execute(conn *Conn, param string) { path := conn.buildPath(param) + conn.server.notifiers.BeforeCreateDir(conn, path) err := conn.driver.MakeDir(path) + conn.server.notifiers.AfterDirCreated(conn, path, err) if err == nil { conn.writeMessage(257, "Directory created") } else { @@ -630,6 +631,7 @@ func (cmd commandPass) RequireAuth() bool { func (cmd commandPass) Execute(conn *Conn, param string) { ok, err := conn.server.Auth.CheckPasswd(conn.reqUser, param) + conn.server.notifiers.AfterUserLogin(conn, conn.reqUser, param, ok, err) if err != nil { conn.writeMessage(550, "Checking password error") return @@ -782,15 +784,18 @@ func (cmd commandRetr) Execute(conn *Conn, param string) { conn.lastFilePos = 0 conn.appendData = false }() - bytes, data, err := conn.driver.GetFile(path, conn.lastFilePos) + conn.server.notifiers.BeforeDownloadFile(conn, path) + size, data, err := conn.driver.GetFile(path, conn.lastFilePos) if err == nil { defer data.Close() - conn.writeMessage(150, fmt.Sprintf("Data transfer starting %v bytes", bytes)) + conn.writeMessage(150, fmt.Sprintf("Data transfer starting %d bytes", size)) err = conn.sendOutofBandDataWriter(data) + conn.server.notifiers.AfterFileDownloaded(conn, path, size, err) if err != nil { conn.writeMessage(551, "Error reading file") } } else { + conn.server.notifiers.AfterFileDownloaded(conn, path, size, err) conn.writeMessage(551, "File not available") } } @@ -891,7 +896,9 @@ func (cmd commandRmd) RequireAuth() bool { func (cmd commandRmd) Execute(conn *Conn, param string) { path := conn.buildPath(param) + conn.server.notifiers.BeforeDeleteDir(conn, path) err := conn.driver.DeleteDir(path) + conn.server.notifiers.AfterDirDeleted(conn, path, err) if err == nil { conn.writeMessage(250, "Directory deleted") } else { @@ -1112,9 +1119,11 @@ func (cmd commandStor) Execute(conn *Conn, param string) { conn.appendData = false }() - bytes, err := conn.driver.PutFile(targetPath, conn.dataConn, conn.appendData) + conn.server.notifiers.BeforePutFile(conn, targetPath) + size, err := conn.driver.PutFile(targetPath, conn.dataConn, conn.appendData) + conn.server.notifiers.AfterFilePut(conn, targetPath, size, err) if err == nil { - msg := "OK, received " + strconv.Itoa(int(bytes)) + " bytes" + msg := fmt.Sprintf("OK, received %d bytes", size) conn.writeMessage(226, msg) } else { conn.writeMessage(450, fmt.Sprint("error during transfer: ", err)) @@ -1222,6 +1231,7 @@ func (cmd commandUser) RequireAuth() bool { func (cmd commandUser) Execute(conn *Conn, param string) { conn.reqUser = param + conn.server.notifiers.BeforeLoginUser(conn, conn.reqUser) if conn.tls || conn.tlsConfig == nil { conn.writeMessage(331, "User name ok, password required") } else { diff --git a/conn.go b/conn.go index 744a39e..1fa1807 100644 --- a/conn.go +++ b/conn.go @@ -142,6 +142,8 @@ func (conn *Conn) Serve() { func (conn *Conn) Close() { conn.conn.Close() conn.closed = true + conn.reqUser = "" + conn.user = "" if conn.dataConn != nil { conn.dataConn.Close() conn.dataConn = nil diff --git a/driver_minio_test.go b/driver_minio_test.go index c075640..c2bb041 100644 --- a/driver_minio_test.go +++ b/driver_minio_test.go @@ -39,7 +39,7 @@ func TestMinioDriver(t *testing.T) { Logger: new(DiscardLogger), } - runServer(t, opt, func() { + runServer(t, opt, nil, func() { // Give server 0.5 seconds to get to the listening state timeout := time.NewTimer(time.Millisecond * 500) for { diff --git a/notifier.go b/notifier.go index 0e240d2..5b8a786 100644 --- a/notifier.go +++ b/notifier.go @@ -6,87 +6,108 @@ package server // Notifier represents a notification operator interface type Notifier interface { - BeforeUserLogin(conn *Conn, userName, password string) - BeforeFilePut(conn *Conn, dstPath string) - BeforeFileDeleted(dstPath string) - BeforeDirCreated(dstPath string) - BeforeDirDeleted(dstPath string) - BeforeFileDownload(dstPath string) - AfterUserLogin(conn *Conn, userName, password string) - AfterFilePut(conn *Conn, dstPath string, err error) - AfterFileDeleted(dstPath string, err error) - AfterDirCreated(dstPath string) - AfterDirDeleted(dstPath string) - AfterFileDownload(dstPath string) + BeforeLoginUser(conn *Conn, userName string) + BeforePutFile(conn *Conn, dstPath string) + BeforeDeleteFile(conn *Conn, dstPath string) + BeforeChangeCurDir(conn *Conn, oldCurDir, newCurDir string) + BeforeCreateDir(conn *Conn, dstPath string) + BeforeDeleteDir(conn *Conn, dstPath string) + BeforeDownloadFile(conn *Conn, dstPath string) + AfterUserLogin(conn *Conn, userName, password string, passMatched bool, err error) + AfterFilePut(conn *Conn, dstPath string, size int64, err error) + AfterFileDeleted(conn *Conn, dstPath string, err error) + AfterFileDownloaded(conn *Conn, dstPath string, size int64, err error) + AfterCurDirChanged(conn *Conn, oldCurDir, newCurDir string, err error) + AfterDirCreated(conn *Conn, dstPath string, err error) + AfterDirDeleted(conn *Conn, dstPath string, err error) } type notifierList []Notifier var ( - notifiers notifierList + _ Notifier = notifierList{} ) -// RegisterNotifer registers a notifier -func RegisterNotifer(notifier Notifier) { - notifiers = append(notifiers, notifier) -} - -func (notifiers notifierList) BeforeFilePut(conn *Conn, dstPath string) { +func (notifiers notifierList) BeforeLoginUser(conn *Conn, userName string) { for _, notifier := range notifiers { - notifier.BeforeFilePut(conn, dstPath) + notifier.BeforeLoginUser(conn, userName) } } -func (notifierList) BeforeFileDeleted(dstPath string) { +func (notifiers notifierList) BeforePutFile(conn *Conn, dstPath string) { for _, notifier := range notifiers { - notifier.BeforeFileDeleted(dstPath) + notifier.BeforePutFile(conn, dstPath) } } -func (notifierList) BeforeDirCreated(dstPath string) { +func (notifiers notifierList) BeforeDeleteFile(conn *Conn, dstPath string) { for _, notifier := range notifiers { - notifier.BeforeDirCreated(dstPath) + notifier.BeforeDeleteFile(conn, dstPath) } } -func (notifierList) BeforeDirDeleted(dstPath string) { +func (notifiers notifierList) BeforeChangeCurDir(conn *Conn, oldCurDir, newCurDir string) { for _, notifier := range notifiers { - notifier.BeforeDirDeleted(dstPath) + notifier.BeforeChangeCurDir(conn, oldCurDir, newCurDir) } } -func (notifierList) BeforeFileDownload(dstPath string) { +func (notifiers notifierList) BeforeCreateDir(conn *Conn, dstPath string) { for _, notifier := range notifiers { - notifier.BeforeFileDownload(dstPath) + notifier.BeforeCreateDir(conn, dstPath) } } -func (notifierList) AfterFilePut(conn *Conn, dstPath string, err error) { +func (notifiers notifierList) BeforeDeleteDir(conn *Conn, dstPath string) { for _, notifier := range notifiers { - notifier.AfterFilePut(conn, dstPath, err) + notifier.BeforeDeleteDir(conn, dstPath) } } -func (notifierList) AfterFileDeleted(dstPath string, err error) { +func (notifiers notifierList) BeforeDownloadFile(conn *Conn, dstPath string) { for _, notifier := range notifiers { - notifier.AfterFileDeleted(dstPath, err) + notifier.BeforeDownloadFile(conn, dstPath) } } -func (notifierList) AfterDirCreated(dstPath string) { +func (notifiers notifierList) AfterUserLogin(conn *Conn, userName, password string, passMatched bool, err error) { for _, notifier := range notifiers { - notifier.AfterDirCreated(dstPath) + notifier.AfterUserLogin(conn, userName, password, passMatched, err) } } -func (notifierList) AfterDirDeleted(dstPath string) { +func (notifiers notifierList) AfterFilePut(conn *Conn, dstPath string, size int64, err error) { for _, notifier := range notifiers { - notifier.AfterDirDeleted(dstPath) + notifier.AfterFilePut(conn, dstPath, size, err) } } -func (notifierList) AfterFileDownload(dstPath string) { +func (notifiers notifierList) AfterFileDeleted(conn *Conn, dstPath string, err error) { for _, notifier := range notifiers { - notifier.AfterFileDownload(dstPath) + notifier.AfterFileDeleted(conn, dstPath, err) + } +} + +func (notifiers notifierList) AfterFileDownloaded(conn *Conn, dstPath string, size int64, err error) { + for _, notifier := range notifiers { + notifier.AfterFileDownloaded(conn, dstPath, size, err) + } +} + +func (notifiers notifierList) AfterCurDirChanged(conn *Conn, oldCurDir, newCurDir string, err error) { + for _, notifier := range notifiers { + notifier.AfterCurDirChanged(conn, oldCurDir, newCurDir, err) + } +} + +func (notifiers notifierList) AfterDirCreated(conn *Conn, dstPath string, err error) { + for _, notifier := range notifiers { + notifier.AfterDirCreated(conn, dstPath, err) + } +} + +func (notifiers notifierList) AfterDirDeleted(conn *Conn, dstPath string, err error) { + for _, notifier := range notifiers { + notifier.AfterDirDeleted(conn, dstPath, err) } } diff --git a/notifier_test.go b/notifier_test.go new file mode 100644 index 0000000..d2034e9 --- /dev/null +++ b/notifier_test.go @@ -0,0 +1,144 @@ +// Copyright 2020 The goftp Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package server + +import ( + "io/ioutil" + "os" + "strings" + "testing" + "time" + + "github.com/jlaffaye/ftp" + "github.com/stretchr/testify/assert" +) + +type mockNotifier struct { + actions []string +} + +func (m *mockNotifier) BeforeLoginUser(conn *Conn, userName string) { + m.actions = append(m.actions, "BeforeLoginUser") +} +func (m *mockNotifier) BeforePutFile(conn *Conn, dstPath string) { + m.actions = append(m.actions, "BeforePutFile") +} +func (m *mockNotifier) BeforeDeleteFile(conn *Conn, dstPath string) { + m.actions = append(m.actions, "BeforeDeleteFile") +} +func (m *mockNotifier) BeforeChangeCurDir(conn *Conn, oldCurDir, newCurDir string) { + m.actions = append(m.actions, "BeforeChangeCurDir") +} +func (m *mockNotifier) BeforeCreateDir(conn *Conn, dstPath string) { + m.actions = append(m.actions, "BeforeCreateDir") +} +func (m *mockNotifier) BeforeDeleteDir(conn *Conn, dstPath string) { + m.actions = append(m.actions, "BeforeDeleteDir") +} +func (m *mockNotifier) BeforeDownloadFile(conn *Conn, dstPath string) { + m.actions = append(m.actions, "BeforeDownloadFile") +} +func (m *mockNotifier) AfterUserLogin(conn *Conn, userName, password string, passMatched bool, err error) { + m.actions = append(m.actions, "AfterUserLogin") +} +func (m *mockNotifier) AfterFilePut(conn *Conn, dstPath string, size int64, err error) { + m.actions = append(m.actions, "AfterFilePut") +} +func (m *mockNotifier) AfterFileDeleted(conn *Conn, dstPath string, err error) { + m.actions = append(m.actions, "AfterFileDeleted") +} +func (m *mockNotifier) AfterCurDirChanged(conn *Conn, oldCurDir, newCurDir string, err error) { + m.actions = append(m.actions, "AfterCurDirChanged") +} +func (m *mockNotifier) AfterDirCreated(conn *Conn, dstPath string, err error) { + m.actions = append(m.actions, "AfterDirCreated") +} +func (m *mockNotifier) AfterDirDeleted(conn *Conn, dstPath string, err error) { + m.actions = append(m.actions, "AfterDirDeleted") +} +func (m *mockNotifier) AfterFileDownloaded(conn *Conn, dstPath string, size int64, err error) { + m.actions = append(m.actions, "AfterFileDownloaded") +} + +func assetMockNotifier(t *testing.T, mock *mockNotifier, lastActions []string) { + if len(lastActions) == 0 { + return + } + assert.EqualValues(t, lastActions, mock.actions[len(mock.actions)-len(lastActions):]) +} + +func TestNotification(t *testing.T) { + os.MkdirAll("./testdata", os.ModePerm) + + var perm = NewSimplePerm("test", "test") + opt := &ServerOpts{ + Name: "test ftpd", + Factory: &FileDriverFactory{ + RootPath: "./testdata", + Perm: perm, + }, + Port: 2121, + Auth: &SimpleAuth{ + Name: "admin", + Password: "admin", + }, + Logger: new(DiscardLogger), + } + + mock := &mockNotifier{} + + runServer(t, opt, notifierList{mock}, func() { + // Give server 0.5 seconds to get to the listening state + timeout := time.NewTimer(time.Millisecond * 500) + + for { + f, err := ftp.Connect("localhost:2121") + if err != nil && len(timeout.C) == 0 { // Retry errors + continue + } + assert.NoError(t, err) + + assert.NoError(t, f.Login("admin", "admin")) + assetMockNotifier(t, mock, []string{"BeforeLoginUser", "AfterUserLogin"}) + + assert.Error(t, f.Login("admin", "1111")) + assetMockNotifier(t, mock, []string{"BeforeLoginUser", "AfterUserLogin"}) + + var content = `test` + assert.NoError(t, f.Stor("server_test.go", strings.NewReader(content))) + assetMockNotifier(t, mock, []string{"BeforePutFile", "AfterFilePut"}) + + r, err := f.RetrFrom("/server_test.go", 2) + assert.NoError(t, err) + assetMockNotifier(t, mock, []string{"BeforeDownloadFile"}) + + buf, err := ioutil.ReadAll(r) + r.Close() + assert.NoError(t, err) + assert.EqualValues(t, "st", string(buf)) + assetMockNotifier(t, mock, []string{"AfterFileDownloaded"}) + + err = f.Rename("/server_test.go", "/test.go") + assert.NoError(t, err) + + err = f.MakeDir("/src") + assetMockNotifier(t, mock, []string{"BeforeCreateDir", "AfterDirCreated"}) + + err = f.Delete("/test.go") + assetMockNotifier(t, mock, []string{"BeforeDeleteFile", "AfterFileDeleted"}) + + err = f.ChangeDir("/src") + assetMockNotifier(t, mock, []string{"BeforeChangeCurDir", "AfterCurDirChanged"}) + + err = f.RemoveDir("/src") + assetMockNotifier(t, mock, []string{"BeforeDeleteDir", "AfterDirDeleted"}) + + err = f.Quit() + assert.NoError(t, err) + + break + } + }) +} diff --git a/server.go b/server.go index 7706e03..b527708 100644 --- a/server.go +++ b/server.go @@ -75,6 +75,7 @@ type Server struct { ctx context.Context cancel context.CancelFunc feats string + notifiers notifierList } // ErrServerClosed is returned by ListenAndServe() or Serve() when a shutdown @@ -157,6 +158,11 @@ func NewServer(opts *ServerOpts) *Server { return s } +// RegisterNotifer registers a notifier +func (server *Server) RegisterNotifer(notifier Notifier) { + server.notifiers = append(server.notifiers, notifier) +} + // NewConn constructs a new object that will handle the FTP protocol over // an active net.TCPConn. The TCP connection should already be open before // it is handed to this functions. driver is an instance of FTPDriver that diff --git a/server_test.go b/server_test.go index 289387f..b16d1c7 100644 --- a/server_test.go +++ b/server_test.go @@ -16,8 +16,11 @@ import ( "github.com/stretchr/testify/assert" ) -func runServer(t *testing.T, opt *ServerOpts, execute func()) { +func runServer(t *testing.T, opt *ServerOpts, notifiers notifierList, execute func()) { s := NewServer(opt) + for _, notifier := range notifiers { + s.RegisterNotifer(notifier) + } go func() { err := s.ListenAndServe() assert.EqualError(t, err, ErrServerClosed.Error()) @@ -46,7 +49,7 @@ func TestFileDriver(t *testing.T) { //Logger: new(DiscardLogger), } - runServer(t, opt, func() { + runServer(t, opt, nil, func() { // Give server 0.5 seconds to get to the listening state timeout := time.NewTimer(time.Millisecond * 500) -- 2.40.1 From b14088698c627b416a14e131f92baae382e6e5cc Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 9 Feb 2020 17:11:48 +0800 Subject: [PATCH 3/5] Fix test race --- notifier_test.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/notifier_test.go b/notifier_test.go index d2034e9..2c4fe13 100644 --- a/notifier_test.go +++ b/notifier_test.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" "strings" + "sync" "testing" "time" @@ -17,56 +18,87 @@ import ( type mockNotifier struct { actions []string + lock sync.Mutex } func (m *mockNotifier) BeforeLoginUser(conn *Conn, userName string) { + m.lock.Lock() m.actions = append(m.actions, "BeforeLoginUser") + m.lock.Unlock() } func (m *mockNotifier) BeforePutFile(conn *Conn, dstPath string) { + m.lock.Lock() m.actions = append(m.actions, "BeforePutFile") + m.lock.Unlock() } func (m *mockNotifier) BeforeDeleteFile(conn *Conn, dstPath string) { + m.lock.Lock() m.actions = append(m.actions, "BeforeDeleteFile") + m.lock.Unlock() } func (m *mockNotifier) BeforeChangeCurDir(conn *Conn, oldCurDir, newCurDir string) { + m.lock.Lock() m.actions = append(m.actions, "BeforeChangeCurDir") + m.lock.Unlock() } func (m *mockNotifier) BeforeCreateDir(conn *Conn, dstPath string) { + m.lock.Lock() m.actions = append(m.actions, "BeforeCreateDir") + m.lock.Unlock() } func (m *mockNotifier) BeforeDeleteDir(conn *Conn, dstPath string) { + m.lock.Lock() m.actions = append(m.actions, "BeforeDeleteDir") + m.lock.Unlock() } func (m *mockNotifier) BeforeDownloadFile(conn *Conn, dstPath string) { + m.lock.Lock() m.actions = append(m.actions, "BeforeDownloadFile") + m.lock.Unlock() } func (m *mockNotifier) AfterUserLogin(conn *Conn, userName, password string, passMatched bool, err error) { + m.lock.Lock() m.actions = append(m.actions, "AfterUserLogin") + m.lock.Unlock() } func (m *mockNotifier) AfterFilePut(conn *Conn, dstPath string, size int64, err error) { + m.lock.Lock() m.actions = append(m.actions, "AfterFilePut") + m.lock.Unlock() } func (m *mockNotifier) AfterFileDeleted(conn *Conn, dstPath string, err error) { + m.lock.Lock() m.actions = append(m.actions, "AfterFileDeleted") + m.lock.Unlock() } func (m *mockNotifier) AfterCurDirChanged(conn *Conn, oldCurDir, newCurDir string, err error) { + m.lock.Lock() m.actions = append(m.actions, "AfterCurDirChanged") + m.lock.Unlock() } func (m *mockNotifier) AfterDirCreated(conn *Conn, dstPath string, err error) { + m.lock.Lock() m.actions = append(m.actions, "AfterDirCreated") + m.lock.Unlock() } func (m *mockNotifier) AfterDirDeleted(conn *Conn, dstPath string, err error) { + m.lock.Lock() m.actions = append(m.actions, "AfterDirDeleted") + m.lock.Unlock() } func (m *mockNotifier) AfterFileDownloaded(conn *Conn, dstPath string, size int64, err error) { + m.lock.Lock() m.actions = append(m.actions, "AfterFileDownloaded") + m.lock.Unlock() } func assetMockNotifier(t *testing.T, mock *mockNotifier, lastActions []string) { if len(lastActions) == 0 { return } + mock.lock.Lock() assert.EqualValues(t, lastActions, mock.actions[len(mock.actions)-len(lastActions):]) + mock.lock.Unlock() } func TestNotification(t *testing.T) { -- 2.40.1 From edf8b40f6bd449f39a60436b7e6d697634868d02 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 19 Feb 2020 14:08:11 +0800 Subject: [PATCH 4/5] Fix test --- server_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_test.go b/server_test.go index b16d1c7..a454911 100644 --- a/server_test.go +++ b/server_test.go @@ -46,7 +46,7 @@ func TestFileDriver(t *testing.T) { Name: "admin", Password: "admin", }, - //Logger: new(DiscardLogger), + Logger: new(DiscardLogger), } runServer(t, opt, nil, func() { -- 2.40.1 From 66f6bd18b9e34b45d5fbefd7f5006a85249a8f7e Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 19 Feb 2020 14:15:29 +0800 Subject: [PATCH 5/5] Fix test --- notifier_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/notifier_test.go b/notifier_test.go index 2c4fe13..7eb2020 100644 --- a/notifier_test.go +++ b/notifier_test.go @@ -144,13 +144,12 @@ func TestNotification(t *testing.T) { r, err := f.RetrFrom("/server_test.go", 2) assert.NoError(t, err) - assetMockNotifier(t, mock, []string{"BeforeDownloadFile"}) buf, err := ioutil.ReadAll(r) r.Close() assert.NoError(t, err) assert.EqualValues(t, "st", string(buf)) - assetMockNotifier(t, mock, []string{"AfterFileDownloaded"}) + assetMockNotifier(t, mock, []string{"BeforeDownloadFile", "AfterFileDownloaded"}) err = f.Rename("/server_test.go", "/test.go") assert.NoError(t, err) -- 2.40.1