test(files, users): add e2e test and fix issues
This commit is contained in:
parent
b065684a47
commit
0265baf1b1
15 changed files with 5015 additions and 5327 deletions
|
@ -49,7 +49,7 @@ func DefaultConfig() (string, error) {
|
|||
OpenTTL: 60, // 1 min
|
||||
},
|
||||
Users: &UsersCfg{
|
||||
EnableAuth: true,
|
||||
EnableAuth: false,
|
||||
DefaultAdmin: "",
|
||||
DefaultAdminPwd: "",
|
||||
CookieTTL: 3600 * 24 * 7, // 1 week
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/static"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/ihexxa/gocfg"
|
||||
|
||||
|
@ -16,6 +17,7 @@ import (
|
|||
"github.com/ihexxa/quickshare/src/fs"
|
||||
"github.com/ihexxa/quickshare/src/fs/local"
|
||||
"github.com/ihexxa/quickshare/src/handlers/fileshdr"
|
||||
"github.com/ihexxa/quickshare/src/handlers/settings"
|
||||
"github.com/ihexxa/quickshare/src/handlers/singleuserhdr"
|
||||
"github.com/ihexxa/quickshare/src/idgen/simpleidgen"
|
||||
"github.com/ihexxa/quickshare/src/kvstore"
|
||||
|
@ -114,31 +116,41 @@ func initHandlers(router *gin.Engine, cfg gocfg.ICfg, deps *depidx.Deps) (*gin.E
|
|||
return nil, err
|
||||
}
|
||||
|
||||
settingsSvc, err := settings.NewSettingsSvc(cfg, deps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// middleware
|
||||
router.Use(userHdrs.Auth())
|
||||
// tmp static server
|
||||
router.Use(static.Serve("/", static.LocalFile("../static", false)))
|
||||
|
||||
// handler
|
||||
v1 := router.Group("/v1")
|
||||
|
||||
users := v1.Group("/users")
|
||||
users.POST("/login", userHdrs.Login)
|
||||
users.POST("/logout", userHdrs.Logout)
|
||||
users.PATCH("/pwd", userHdrs.SetPwd)
|
||||
usersAPI := v1.Group("/users")
|
||||
usersAPI.POST("/login", userHdrs.Login)
|
||||
usersAPI.POST("/logout", userHdrs.Logout)
|
||||
usersAPI.PATCH("/pwd", userHdrs.SetPwd)
|
||||
|
||||
filesSvc := v1.Group("/fs")
|
||||
filesSvc.POST("/files", fileHdrs.Create)
|
||||
filesSvc.DELETE("/files", fileHdrs.Delete)
|
||||
filesSvc.GET("/files", fileHdrs.Download)
|
||||
filesSvc.PATCH("/files/chunks", fileHdrs.UploadChunk)
|
||||
filesSvc.GET("/files/chunks", fileHdrs.UploadStatus)
|
||||
filesSvc.PATCH("/files/copy", fileHdrs.Copy)
|
||||
filesSvc.PATCH("/files/move", fileHdrs.Move)
|
||||
filesAPI := v1.Group("/fs")
|
||||
filesAPI.POST("/files", fileHdrs.Create)
|
||||
filesAPI.DELETE("/files", fileHdrs.Delete)
|
||||
filesAPI.GET("/files", fileHdrs.Download)
|
||||
filesAPI.PATCH("/files/chunks", fileHdrs.UploadChunk)
|
||||
filesAPI.GET("/files/chunks", fileHdrs.UploadStatus)
|
||||
filesAPI.PATCH("/files/copy", fileHdrs.Copy)
|
||||
filesAPI.PATCH("/files/move", fileHdrs.Move)
|
||||
|
||||
filesSvc.GET("/dirs", fileHdrs.List)
|
||||
filesSvc.POST("/dirs", fileHdrs.Mkdir)
|
||||
filesAPI.GET("/dirs", fileHdrs.List)
|
||||
filesAPI.POST("/dirs", fileHdrs.Mkdir)
|
||||
// files.POST("/dirs/copy", fileHdrs.CopyDir)
|
||||
|
||||
filesSvc.GET("/metadata", fileHdrs.Metadata)
|
||||
filesAPI.GET("/metadata", fileHdrs.Metadata)
|
||||
|
||||
settingsAPI := v1.Group("/settings")
|
||||
settingsAPI.OPTIONS("/health", settingsSvc.Health)
|
||||
|
||||
return router, nil
|
||||
}
|
||||
|
|
|
@ -3,11 +3,14 @@ package server
|
|||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ihexxa/quickshare/src/client"
|
||||
"github.com/ihexxa/quickshare/src/handlers/fileshdr"
|
||||
|
@ -16,7 +19,6 @@ import (
|
|||
func TestFileHandlers(t *testing.T) {
|
||||
addr := "http://127.0.0.1:8888"
|
||||
root := "testData"
|
||||
chunkSize := 2
|
||||
config := `{
|
||||
"users": {
|
||||
"enableAuth": false
|
||||
|
@ -29,6 +31,7 @@ func TestFileHandlers(t *testing.T) {
|
|||
}
|
||||
}`
|
||||
|
||||
os.RemoveAll(root)
|
||||
err := os.MkdirAll(root, 0700)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -37,17 +40,110 @@ func TestFileHandlers(t *testing.T) {
|
|||
|
||||
srv := startTestServer(config)
|
||||
defer srv.Shutdown()
|
||||
// kv := srv.depsKVStore()
|
||||
fs := srv.depsFS()
|
||||
cl := client.NewFilesClient(addr)
|
||||
|
||||
// TODO: remove this
|
||||
time.Sleep(500)
|
||||
if !waitForReady(addr) {
|
||||
t.Fatal("fail to start server")
|
||||
}
|
||||
|
||||
t.Run("test file APIs: Create-UploadChunk-UploadStatus-Metadata-Delete", func(t *testing.T) {
|
||||
assertUploadOK := func(t *testing.T, filePath, content string) bool {
|
||||
cl := client.NewFilesClient(addr)
|
||||
|
||||
fileSize := int64(len([]byte(content)))
|
||||
res, _, errs := cl.Create(filePath, fileSize)
|
||||
if len(errs) > 0 {
|
||||
t.Error(errs)
|
||||
return false
|
||||
} else if res.StatusCode != 200 {
|
||||
t.Error(res.StatusCode)
|
||||
return false
|
||||
}
|
||||
|
||||
res, _, errs = cl.UploadChunk(filePath, content, 0)
|
||||
if len(errs) > 0 {
|
||||
t.Error(errs)
|
||||
return false
|
||||
} else if res.StatusCode != 200 {
|
||||
t.Error(res.StatusCode)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
assetDownloadOK := func(t *testing.T, filePath, content string) bool {
|
||||
var (
|
||||
res *http.Response
|
||||
body string
|
||||
errs []error
|
||||
fileSize = int64(len([]byte(content)))
|
||||
)
|
||||
|
||||
cl := client.NewFilesClient(addr)
|
||||
|
||||
rd := rand.Intn(3)
|
||||
switch rd {
|
||||
case 0:
|
||||
res, body, errs = cl.Download(filePath, map[string]string{})
|
||||
case 1:
|
||||
res, body, errs = cl.Download(filePath, map[string]string{
|
||||
"Range": fmt.Sprintf("bytes=0-%d", fileSize-1),
|
||||
})
|
||||
case 2:
|
||||
res, body, errs = cl.Download(filePath, map[string]string{
|
||||
"Range": fmt.Sprintf("bytes=0-%d, %d-%d", (fileSize-1)/2, (fileSize-1)/2+1, fileSize-1),
|
||||
})
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
t.Error(errs)
|
||||
return false
|
||||
} else if res.StatusCode != 200 && res.StatusCode != 206 {
|
||||
t.Error(res.StatusCode)
|
||||
return false
|
||||
}
|
||||
switch rd {
|
||||
case 0:
|
||||
if body != content {
|
||||
t.Errorf("body not equal got(%s) expect(%s)\n", body, content)
|
||||
return false
|
||||
}
|
||||
case 1:
|
||||
|
||||
if body[2:] != content { // body returned by gorequest contains the first CRLF
|
||||
t.Errorf("body not equal got(%s) expect(%s)\n", body[2:], content)
|
||||
return false
|
||||
}
|
||||
default:
|
||||
body = body[2:] // body returned by gorequest contains the first CRLF
|
||||
realBody := ""
|
||||
boundaryEnd := strings.Index(body, "\r\n")
|
||||
boundary := body[0:boundaryEnd]
|
||||
bodyParts := strings.Split(body, boundary)
|
||||
|
||||
for i, bodyPart := range bodyParts {
|
||||
if i == 0 || i == len(bodyParts)-1 {
|
||||
continue
|
||||
}
|
||||
start := strings.Index(bodyPart, "\r\n\r\n")
|
||||
|
||||
fmt.Printf("<%s>", bodyPart[start+4:len(bodyPart)-2]) // ignore the last CRLF
|
||||
realBody += bodyPart[start+4 : len(bodyPart)-2]
|
||||
}
|
||||
if realBody != content {
|
||||
t.Errorf("multi body not equal got(%s) expect(%s)\n", realBody, content)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
t.Run("test files APIs: Create-UploadChunk-UploadStatus-Metadata-Delete", func(t *testing.T) {
|
||||
for filePath, content := range map[string]string{
|
||||
"path1/f1.md": "11111",
|
||||
"path1/path2/f2.md": "101010",
|
||||
"path1/f1.md": "1111 1111 1111 1111",
|
||||
"path1/path2/f2.md": "1010 1010 1111 0000 0010",
|
||||
} {
|
||||
fileSize := int64(len([]byte(content)))
|
||||
// create a file
|
||||
|
@ -71,7 +167,7 @@ func TestFileHandlers(t *testing.T) {
|
|||
i := 0
|
||||
contentBytes := []byte(content)
|
||||
for i < len(contentBytes) {
|
||||
right := i + chunkSize
|
||||
right := i + rand.Intn(3) + 1
|
||||
if right > len(contentBytes) {
|
||||
right = len(contentBytes)
|
||||
}
|
||||
|
@ -127,7 +223,7 @@ func TestFileHandlers(t *testing.T) {
|
|||
}
|
||||
})
|
||||
|
||||
t.Run("test file APIs: Mkdir-Create-UploadChunk-List", func(t *testing.T) {
|
||||
t.Run("test dirs APIs: Mkdir-Create-UploadChunk-List", func(t *testing.T) {
|
||||
for dirPath, files := range map[string]map[string]string{
|
||||
"dir/path1/": map[string]string{
|
||||
"f1.md": "11111",
|
||||
|
@ -146,22 +242,7 @@ func TestFileHandlers(t *testing.T) {
|
|||
|
||||
for fileName, content := range files {
|
||||
filePath := filepath.Join(dirPath, fileName)
|
||||
|
||||
fileSize := int64(len([]byte(content)))
|
||||
// create a file
|
||||
res, _, errs := cl.Create(filePath, fileSize)
|
||||
if len(errs) > 0 {
|
||||
t.Fatal(errs)
|
||||
} else if res.StatusCode != 200 {
|
||||
t.Fatal(res.StatusCode)
|
||||
}
|
||||
|
||||
res, _, errs = cl.UploadChunk(filePath, content, 0)
|
||||
if len(errs) > 0 {
|
||||
t.Fatal(errs)
|
||||
} else if res.StatusCode != 200 {
|
||||
t.Fatal(res.StatusCode)
|
||||
}
|
||||
assertUploadOK(t, filePath, content)
|
||||
}
|
||||
|
||||
_, lResp, errs := cl.List(dirPath)
|
||||
|
@ -179,7 +260,7 @@ func TestFileHandlers(t *testing.T) {
|
|||
}
|
||||
})
|
||||
|
||||
t.Run("test file APIs: Mkdir-Create-UploadChunk-Move-List", func(t *testing.T) {
|
||||
t.Run("test operation APIs: Mkdir-Create-UploadChunk-Move-List", func(t *testing.T) {
|
||||
srcDir := "move/src"
|
||||
dstDir := "move/dst"
|
||||
|
||||
|
@ -200,24 +281,10 @@ func TestFileHandlers(t *testing.T) {
|
|||
for fileName, content := range files {
|
||||
oldPath := filepath.Join(srcDir, fileName)
|
||||
newPath := filepath.Join(dstDir, fileName)
|
||||
fileSize := int64(len([]byte(content)))
|
||||
// fileSize := int64(len([]byte(content)))
|
||||
assertUploadOK(t, oldPath, content)
|
||||
|
||||
// create a file
|
||||
res, _, errs := cl.Create(oldPath, fileSize)
|
||||
if len(errs) > 0 {
|
||||
t.Fatal(errs)
|
||||
} else if res.StatusCode != 200 {
|
||||
t.Fatal(res.StatusCode)
|
||||
}
|
||||
|
||||
res, _, errs = cl.UploadChunk(oldPath, content, 0)
|
||||
if len(errs) > 0 {
|
||||
t.Fatal(errs)
|
||||
} else if res.StatusCode != 200 {
|
||||
t.Fatal(res.StatusCode)
|
||||
}
|
||||
|
||||
res, _, errs = cl.Move(oldPath, newPath)
|
||||
res, _, errs := cl.Move(oldPath, newPath)
|
||||
if len(errs) > 0 {
|
||||
t.Fatal(errs)
|
||||
} else if res.StatusCode != 200 {
|
||||
|
@ -238,4 +305,69 @@ func TestFileHandlers(t *testing.T) {
|
|||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test download APIs: Download(normal, ranges)", func(t *testing.T) {
|
||||
for filePath, content := range map[string]string{
|
||||
"download/path1/f1": "123456",
|
||||
"download/path1/path2": "12345678",
|
||||
} {
|
||||
assertUploadOK(t, filePath, content)
|
||||
|
||||
err = fs.Sync()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assetDownloadOK(t, filePath, content)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test concurrently uploading & downloading", func(t *testing.T) {
|
||||
type mockFile struct {
|
||||
FilePath string
|
||||
Content string
|
||||
}
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
startClient := func(files []*mockFile) {
|
||||
for i := 0; i < 5; i++ {
|
||||
for _, file := range files {
|
||||
if !assertUploadOK(t, fmt.Sprintf("%s_%d", file.FilePath, i), file.Content) {
|
||||
break
|
||||
}
|
||||
|
||||
err = fs.Sync()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !assetDownloadOK(t, fmt.Sprintf("%s_%d", file.FilePath, i), file.Content) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
for _, clientFiles := range [][]*mockFile{
|
||||
[]*mockFile{
|
||||
&mockFile{"concurrent/p0/f0", "00"},
|
||||
&mockFile{"concurrent/f0.md", "0000 0000 0000 0"},
|
||||
},
|
||||
[]*mockFile{
|
||||
&mockFile{"concurrent/p1/f1", "11"},
|
||||
&mockFile{"concurrent/f1.md", "1111 1111 1"},
|
||||
},
|
||||
[]*mockFile{
|
||||
&mockFile{"concurrent/p2/f2", "22"},
|
||||
&mockFile{"concurrent/f2.md", "222"},
|
||||
},
|
||||
} {
|
||||
wg.Add(1)
|
||||
go startClient(clientFiles)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package server
|
|||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ihexxa/quickshare/src/client"
|
||||
su "github.com/ihexxa/quickshare/src/handlers/singleuserhdr"
|
||||
|
@ -29,6 +28,7 @@ func TestSingleUserHandlers(t *testing.T) {
|
|||
os.Setenv("DEFAULTADMIN", adminName)
|
||||
os.Setenv("DEFAULTADMINPWD", adminPwd)
|
||||
|
||||
os.RemoveAll(root)
|
||||
err := os.MkdirAll(root, 0700)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -39,10 +39,10 @@ func TestSingleUserHandlers(t *testing.T) {
|
|||
defer srv.Shutdown()
|
||||
|
||||
suCl := client.NewSingleUserClient(addr)
|
||||
// fCl := client.NewFilesClient(addr)
|
||||
|
||||
// TODO: remove this
|
||||
time.Sleep(1000)
|
||||
if !waitForReady(addr) {
|
||||
t.Fatal("fail to start server")
|
||||
}
|
||||
|
||||
t.Run("test single user APIs: Login-SetPwd-Logout-Login", func(t *testing.T) {
|
||||
resp, _, errs := suCl.Login(adminName, adminPwd)
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
package server
|
||||
|
||||
import "github.com/ihexxa/gocfg"
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ihexxa/gocfg"
|
||||
"github.com/ihexxa/quickshare/src/client"
|
||||
)
|
||||
|
||||
func startTestServer(config string) *Server {
|
||||
defaultCfg, err := DefaultConfig()
|
||||
|
@ -25,3 +30,20 @@ func startTestServer(config string) *Server {
|
|||
go srv.Start()
|
||||
return srv
|
||||
}
|
||||
|
||||
func waitForReady(addr string) bool {
|
||||
retry := 10
|
||||
setCl := client.NewSettingsClient(addr)
|
||||
|
||||
for retry > 0 {
|
||||
_, _, errs := setCl.Health()
|
||||
if len(errs) > 0 {
|
||||
time.Sleep(100)
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
retry--
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue