feat(admin): enable multi-users (#67)

* feat(userstore): support ListUsers

* feat(userstore): support del users

* feat(multiusers): support list users and delete user apis

* feat(client/web): add new apis to web client

* fix(ui/panes): move each pane out of the container

* feat(ui): add admin pane

* feat(users): support force set password api

* feat(ui/admin-pane): add functions to admin pane

* feat(users): support self API and move uploading folder to home

* fix(users): remove home folder when deleting user

* fix(ui): remove useless function

* feat(ui/panes): hide admin menu if user is not admin

* fix(server/files): list home path is incorrect

* fix(server): 1.listHome return incorrect cwd 2.addUser init folder with incorrect uid 3.check ns before using

* test(server): add regression test cases

* test(users, files): add e2e test for concurrent operations

* fix(test): clean ups
This commit is contained in:
Hexxa 2021-07-30 21:59:33 -05:00 committed by GitHub
parent 916ec7c2dc
commit aefaca98b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 1562 additions and 478 deletions

View file

@ -178,12 +178,16 @@ func initHandlers(router *gin.Engine, cfg gocfg.ICfg, deps *depidx.Deps) (*gin.E
usersAPI.POST("/logout", userHdrs.Logout)
usersAPI.GET("/isauthed", userHdrs.IsAuthed)
usersAPI.PATCH("/pwd", userHdrs.SetPwd)
usersAPI.PATCH("/pwd/force-set", userHdrs.ForceSetPwd)
usersAPI.POST("/", userHdrs.AddUser)
usersAPI.DELETE("/", userHdrs.DelUser)
usersAPI.GET("/list", userHdrs.ListUsers)
usersAPI.GET("/self", userHdrs.Self)
rolesAPI := v1.Group("/roles")
rolesAPI.POST("/", userHdrs.AddRole)
rolesAPI.DELETE("/", userHdrs.DelRole)
rolesAPI.GET("/", userHdrs.ListRoles)
rolesAPI.GET("/list", userHdrs.ListRoles)
filesAPI := v1.Group("/fs")
filesAPI.POST("/files", fileHdrs.Create)

View file

@ -0,0 +1,134 @@
package server
import (
"fmt"
"os"
"sync"
"testing"
"github.com/ihexxa/quickshare/src/client"
q "github.com/ihexxa/quickshare/src/handlers"
"github.com/ihexxa/quickshare/src/userstore"
)
func TestConcurrency(t *testing.T) {
addr := "http://127.0.0.1:8686"
root := "testData"
config := `{
"users": {
"enableAuth": true,
"minUserNameLen": 2,
"minPwdLen": 4
},
"server": {
"debug": true
},
"fs": {
"root": "testData"
}
}`
adminName := "qs"
adminPwd := "quicksh@re"
os.Setenv("DEFAULTADMIN", adminName)
os.Setenv("DEFAULTADMINPWD", adminPwd)
os.RemoveAll(root)
err := os.MkdirAll(root, 0700)
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(root)
srv := startTestServer(config)
defer srv.Shutdown()
// fs := srv.depsFS()
if !waitForReady(addr) {
t.Fatal("fail to start server")
}
usersCl := client.NewSingleUserClient(addr)
resp, _, errs := usersCl.Login(adminName, adminPwd)
if len(errs) > 0 {
t.Fatal(errs)
} else if resp.StatusCode != 200 {
t.Fatal(resp.StatusCode)
}
token := client.GetCookie(resp.Cookies(), q.TokenCookie)
userCount := 5
userPwd := "1234"
users := map[string]string{}
getUserName := func(id int) string {
return fmt.Sprintf("user_%d", id)
}
for i := range make([]int, userCount) {
userName := getUserName(i)
resp, adResp, errs := usersCl.AddUser(userName, userPwd, userstore.UserRole, token)
if len(errs) > 0 {
t.Fatal(errs)
} else if resp.StatusCode != 200 {
t.Fatal("failed to add user")
}
users[userName] = adResp.ID
}
filesSize := 10
mockClient := func(id, name, pwd string, wg *sync.WaitGroup) {
usersCl := client.NewSingleUserClient(addr)
resp, _, errs := usersCl.Login(name, pwd)
if len(errs) > 0 {
t.Fatal(errs)
} else if resp.StatusCode != 200 {
t.Fatal("failed to add user")
}
token := client.GetCookie(resp.Cookies(), q.TokenCookie)
files := map[string]string{}
content := "12345678"
for i := range make([]int, filesSize, filesSize) {
files[fmt.Sprintf("%s/files/home_file_%d", id, i)] = content
}
for filePath, content := range files {
assertUploadOK(t, filePath, content, addr, token)
assertDownloadOK(t, filePath, content, addr, token)
}
filesCl := client.NewFilesClient(addr, token)
resp, lsResp, errs := filesCl.ListHome()
if len(errs) > 0 {
t.Fatal(errs)
} else if resp.StatusCode != 200 {
t.Fatal("failed to add user")
}
if lsResp.Cwd != fmt.Sprintf("%s/files", id) {
t.Fatalf("incorrct cwd (%s)", lsResp.Cwd)
} else if len(lsResp.Metadatas) != len(files) {
t.Fatalf("incorrct metadata size (%d)", len(lsResp.Metadatas))
}
wg.Done()
}
var wg sync.WaitGroup
t.Run("ListHome", func(t *testing.T) {
for userName, userID := range users {
wg.Add(1)
go mockClient(userID, userName, userPwd, &wg)
}
wg.Wait()
})
resp, _, errs = usersCl.Logout(token)
if len(errs) > 0 {
t.Fatal(errs)
} else if resp.StatusCode != 200 {
t.Fatal(resp.StatusCode)
}
}

View file

@ -1,15 +1,11 @@
package server
import (
"crypto/sha1"
"encoding/base64"
"fmt"
"math/rand"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"sync"
"testing"
@ -65,120 +61,67 @@ func TestFileHandlers(t *testing.T) {
token := client.GetCookie(resp.Cookies(), q.TokenCookie)
cl := client.NewFilesClient(addr, token)
assertUploadOK := func(t *testing.T, filePath, content string) bool {
cl := client.NewFilesClient(addr, token)
// TODO: remove all files under home folder before testing
// or the count of files is incorrect
t.Run("ListHome", func(t *testing.T) {
files := map[string]string{
"0/files/home_file1": "12345678",
"0/files/home_file2": "12345678",
}
fileSize := int64(len([]byte(content)))
res, _, errs := cl.Create(filePath, fileSize)
for filePath, content := range files {
assertUploadOK(t, filePath, content, addr, token)
err = fs.Sync()
if err != nil {
t.Fatal(err)
}
}
resp, lhResp, errs := cl.ListHome()
if len(errs) > 0 {
t.Error(errs)
return false
} else if res.StatusCode != 200 {
t.Error(res.StatusCode)
return false
}
base64Content := base64.StdEncoding.EncodeToString([]byte(content))
res, _, errs = cl.UploadChunk(filePath, base64Content, 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),
})
}
fileName := path.Base(filePath)
contentDispositionHeader := res.Header.Get("Content-Disposition")
if len(errs) > 0 {
t.Error(errs)
return false
}
if res.StatusCode != 200 && res.StatusCode != 206 {
t.Error(res.StatusCode)
return false
}
if contentDispositionHeader != fmt.Sprintf(`attachment; filename="%s"`, fileName) {
t.Errorf("incorrect Content-Disposition header: %s", contentDispositionHeader)
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
t.Fatal(errs)
} else if resp.StatusCode != 200 {
t.Fatal(resp.StatusCode)
} else if lhResp.Cwd != "0/files" {
t.Fatalf("incorrect ListHome cwd %s", lhResp.Cwd)
} else if len(lhResp.Metadatas) != len(files) {
for _, metadata := range lhResp.Metadatas {
fmt.Printf("%v\n", metadata)
}
t.Fatalf("incorrect ListHome content %d", len(lhResp.Metadatas))
}
return true
}
infos := map[string]*fileshdr.MetadataResp{}
for _, metadata := range lhResp.Metadatas {
infos[metadata.Name] = metadata
}
if infos["home_file1"].Size != int64(len(files["0/files/home_file1"])) {
t.Fatalf("incorrect file size %d", infos["home_file1"].Size)
} else if infos["home_file1"].IsDir {
t.Fatal("incorrect item type")
}
if infos["home_file2"].Size != int64(len(files["0/files/home_file2"])) {
t.Fatalf("incorrect file size %d", infos["home_file2"].Size)
} else if infos["home_file2"].IsDir {
t.Fatal("incorrect item type")
}
})
t.Run("test uploading files with duplicated names", func(t *testing.T) {
files := map[string]string{
"0/dupdir/dup_file1": "12345678",
"0/dupdir/dup_file2.ext": "12345678",
"0/files/dupdir/dup_file1": "12345678",
"0/files/dupdir/dup_file2.ext": "12345678",
}
renames := map[string]string{
"0/dupdir/dup_file1": "0/dupdir/dup_file1_1",
"0/dupdir/dup_file2.ext": "0/dupdir/dup_file2_1.ext",
"0/files/dupdir/dup_file1": "0/files/dupdir/dup_file1_1",
"0/files/dupdir/dup_file2.ext": "0/files/dupdir/dup_file2_1.ext",
}
for filePath, content := range files {
for i := 0; i < 2; i++ {
assertUploadOK(t, filePath, content)
assertUploadOK(t, filePath, content, addr, token)
err = fs.Sync()
if err != nil {
@ -186,13 +129,13 @@ func TestFileHandlers(t *testing.T) {
}
if i == 0 {
assetDownloadOK(t, filePath, content)
assertDownloadOK(t, filePath, content, addr, token)
} else if i == 1 {
renamedFilePath, ok := renames[filePath]
if !ok {
t.Fatal("new name not found")
}
assetDownloadOK(t, renamedFilePath, content)
assertDownloadOK(t, renamedFilePath, content, addr, token)
}
}
}
@ -200,8 +143,8 @@ func TestFileHandlers(t *testing.T) {
t.Run("test files APIs: Create-UploadChunk-UploadStatus-Metadata-Delete", func(t *testing.T) {
for filePath, content := range map[string]string{
"0/path1/f1.md": "1111 1111 1111 1111",
"0/path1/path2/f2.md": "1010 1010 1111 0000 0010",
"0/files/path1/f1.md": "1111 1111 1111 1111",
"0/files/path1/path2/f2.md": "1010 1010 1111 0000 0010",
} {
fileSize := int64(len([]byte(content)))
// create a file
@ -213,7 +156,7 @@ func TestFileHandlers(t *testing.T) {
}
// check uploading file
uploadFilePath := path.Join(q.UploadDir, "0", fmt.Sprintf("%x", sha1.Sum([]byte(filePath))))
uploadFilePath := q.UploadPath("0", filePath)
info, err := fs.Stat(uploadFilePath)
if err != nil {
t.Fatal(err)
@ -290,11 +233,11 @@ func TestFileHandlers(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{
"0/dir/path1": map[string]string{
"0/files/dir/path1": map[string]string{
"f1.md": "11111",
"f2.md": "22222222222",
},
"0/dir/path2/path2": map[string]string{
"0/files/dir/path2/path2": map[string]string{
"f3.md": "3333333",
},
} {
@ -307,7 +250,7 @@ func TestFileHandlers(t *testing.T) {
for fileName, content := range files {
filePath := filepath.Join(dirPath, fileName)
assertUploadOK(t, filePath, content)
assertUploadOK(t, filePath, content, addr, token)
}
err = fs.Sync()
@ -331,8 +274,8 @@ func TestFileHandlers(t *testing.T) {
})
t.Run("test operation APIs: Mkdir-Create-UploadChunk-Move-List", func(t *testing.T) {
srcDir := "0/move/src"
dstDir := "0/move/dst"
srcDir := "0/files/move/src"
dstDir := "0/files/move/dst"
for _, dirPath := range []string{srcDir, dstDir} {
res, _, errs := cl.Mkdir(dirPath)
@ -352,7 +295,7 @@ func TestFileHandlers(t *testing.T) {
oldPath := filepath.Join(srcDir, fileName)
newPath := filepath.Join(dstDir, fileName)
// fileSize := int64(len([]byte(content)))
assertUploadOK(t, oldPath, content)
assertUploadOK(t, oldPath, content, addr, token)
res, _, errs := cl.Move(oldPath, newPath)
if len(errs) > 0 {
@ -383,17 +326,17 @@ func TestFileHandlers(t *testing.T) {
t.Run("test download APIs: Download(normal, ranges)", func(t *testing.T) {
for filePath, content := range map[string]string{
"0/download/path1/f1": "123456",
"0/download/path1/path2": "12345678",
"0/files/download/path1/f1": "123456",
"0/files/download/path1/path2": "12345678",
} {
assertUploadOK(t, filePath, content)
assertUploadOK(t, filePath, content, addr, token)
err = fs.Sync()
if err != nil {
t.Fatal(err)
}
assetDownloadOK(t, filePath, content)
assertDownloadOK(t, filePath, content, addr, token)
}
})
@ -407,7 +350,7 @@ func TestFileHandlers(t *testing.T) {
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) {
if !assertUploadOK(t, fmt.Sprintf("%s_%d", file.FilePath, i), file.Content, addr, token) {
break
}
@ -416,7 +359,7 @@ func TestFileHandlers(t *testing.T) {
t.Fatal(err)
}
if !assetDownloadOK(t, fmt.Sprintf("%s_%d", file.FilePath, i), file.Content) {
if !assertDownloadOK(t, fmt.Sprintf("%s_%d", file.FilePath, i), file.Content, addr, token) {
break
}
}
@ -446,10 +389,18 @@ func TestFileHandlers(t *testing.T) {
wg.Wait()
})
t.Run("test uploading APIs: Create, ListUploadings, DelUploading", func(t *testing.T) {
t.Run("test uploading APIs: ListUploadings, Create, ListUploadings, DelUploading", func(t *testing.T) {
// it should return no error even no file is uploaded
res, lResp, errs := cl.ListUploadings()
if len(errs) > 0 {
t.Fatal(errs)
} else if res.StatusCode != 200 {
t.Fatal(res.StatusCode)
}
files := map[string]string{
"0/uploadings/path1/f1": "123456",
"0/uploadings/path1/path2": "12345678",
"0/files/uploadings/path1/f1": "123456",
"0/files/uploadings/path1/path2": "12345678",
}
for filePath, content := range files {
@ -462,7 +413,7 @@ func TestFileHandlers(t *testing.T) {
}
}
res, lResp, errs := cl.ListUploadings()
res, lResp, errs = cl.ListUploadings()
if len(errs) > 0 {
t.Fatal(errs)
} else if res.StatusCode != 200 {
@ -507,7 +458,7 @@ func TestFileHandlers(t *testing.T) {
// cl := client.NewFilesClient(addr)
files := map[string]string{
"0/uploadings/path1/f1": "12345678",
"0/files/uploadings/path1/f1": "12345678",
}
for filePath, content := range files {
@ -554,7 +505,7 @@ func TestFileHandlers(t *testing.T) {
t.Fatal("incorrect uploaded size", mRes)
}
assetDownloadOK(t, filePath, content)
assertDownloadOK(t, filePath, content, addr, token)
}
})
@ -562,9 +513,9 @@ func TestFileHandlers(t *testing.T) {
// cl := client.NewFilesClient(addr)
files := map[string]string{
"0/uploadings/random/path1/f1": "12345678",
"0/uploadings/random/path1/f2": "87654321",
"0/uploadings/random/path1/f3": "17654321",
"0/files/uploadings/random/path1/f1": "12345678",
"0/files/uploadings/random/path1/f2": "87654321",
"0/files/uploadings/random/path1/f3": "17654321",
}
for filePath, content := range files {
@ -622,7 +573,7 @@ func TestFileHandlers(t *testing.T) {
t.Fatalf("file content not equal: %s", filePath)
}
assetDownloadOK(t, filePath, content)
assertDownloadOK(t, filePath, content, addr, token)
}
})

View file

@ -3,14 +3,16 @@ package server
import (
"fmt"
"os"
"strconv"
"testing"
"github.com/ihexxa/quickshare/src/client"
q "github.com/ihexxa/quickshare/src/handlers"
su "github.com/ihexxa/quickshare/src/handlers/singleuserhdr"
"github.com/ihexxa/quickshare/src/userstore"
)
func TestSingleUserHandlers(t *testing.T) {
func TestUsersHandlers(t *testing.T) {
addr := "http://127.0.0.1:8686"
root := "testData"
config := `{
@ -41,6 +43,7 @@ func TestSingleUserHandlers(t *testing.T) {
srv := startTestServer(config)
defer srv.Shutdown()
fs := srv.depsFS()
usersCl := client.NewSingleUserClient(addr)
@ -48,7 +51,7 @@ func TestSingleUserHandlers(t *testing.T) {
t.Fatal("fail to start server")
}
t.Run("test users APIs: Login-SetPwd-Logout-Login", func(t *testing.T) {
t.Run("test users APIs: Login-Self-SetPwd-Logout-Login", func(t *testing.T) {
resp, _, errs := usersCl.Login(adminName, adminPwd)
if len(errs) > 0 {
t.Fatal(errs)
@ -58,6 +61,17 @@ func TestSingleUserHandlers(t *testing.T) {
token := client.GetCookie(resp.Cookies(), su.TokenCookie)
resp, selfResp, errs := usersCl.Self(token)
if len(errs) > 0 {
t.Fatal(errs)
} else if resp.StatusCode != 200 {
t.Fatal(resp.StatusCode)
} else if selfResp.ID != "0" ||
selfResp.Name != adminName ||
selfResp.Role != userstore.AdminRole {
t.Fatalf("user infos don't match %v", selfResp)
}
resp, _, errs = usersCl.SetPwd(adminPwd, adminNewPwd, token)
if len(errs) > 0 {
t.Fatal(errs)
@ -90,7 +104,7 @@ func TestSingleUserHandlers(t *testing.T) {
token := client.GetCookie(resp.Cookies(), su.TokenCookie)
userName, userPwd := "user", "1234"
userName, userPwd := "user_login", "1234"
resp, auResp, errs := usersCl.AddUser(userName, userPwd, userstore.UserRole, token)
if len(errs) > 0 {
t.Fatal(errs)
@ -100,6 +114,18 @@ func TestSingleUserHandlers(t *testing.T) {
// TODO: check id
fmt.Printf("new user id: %v\n", auResp)
// check uploading file
userFsRootFolder := q.FsRootPath(auResp.ID, "/")
_, err = fs.Stat(userFsRootFolder)
if err != nil {
t.Fatal(err)
}
userUploadFolder := q.UploadFolder(auResp.ID)
_, err = fs.Stat(userUploadFolder)
if err != nil {
t.Fatal(err)
}
resp, _, errs = usersCl.Logout(token)
if len(errs) > 0 {
t.Fatal(errs)
@ -114,6 +140,85 @@ func TestSingleUserHandlers(t *testing.T) {
t.Fatal(resp.StatusCode)
}
resp, _, errs = usersCl.DelUser(auResp.ID, token)
if len(errs) > 0 {
t.Fatal(errs)
} else if resp.StatusCode != 200 {
t.Fatal(resp.StatusCode)
}
resp, _, errs = usersCl.Logout(token)
if len(errs) > 0 {
t.Fatal(errs)
} else if resp.StatusCode != 200 {
t.Fatal(resp.StatusCode)
}
})
t.Run("test users APIs: Login-AddUser-ListUsers-DelUser-ListUsers", func(t *testing.T) {
resp, _, errs := usersCl.Login(adminName, adminNewPwd)
if len(errs) > 0 {
t.Fatal(errs)
} else if resp.StatusCode != 200 {
t.Fatal(resp.StatusCode)
}
token := client.GetCookie(resp.Cookies(), su.TokenCookie)
userName, userPwd, userRole := "user_admin", "1234", userstore.UserRole
resp, auResp, errs := usersCl.AddUser(userName, userPwd, userRole, token)
if len(errs) > 0 {
t.Fatal(errs)
} else if resp.StatusCode != 200 {
t.Fatal(resp.StatusCode)
}
// TODO: check id
fmt.Printf("new user id: %v\n", auResp)
newUserID, err := strconv.ParseUint(auResp.ID, 10, 64)
if err != nil {
t.Fatal(err)
}
resp, lsResp, errs := usersCl.ListUsers(token)
if len(errs) > 0 {
t.Fatal(errs)
} else if resp.StatusCode != 200 {
t.Fatal(resp.StatusCode)
}
if len(lsResp.Users) != 2 {
t.Fatal(fmt.Errorf("incorrect users size (%d)", len(lsResp.Users)))
} else if lsResp.Users[0].ID != 0 ||
lsResp.Users[0].Name != adminName ||
lsResp.Users[0].Role != userstore.AdminRole {
t.Fatal(fmt.Errorf("incorrect root info (%v)", lsResp.Users[0]))
} else if lsResp.Users[1].ID != newUserID ||
lsResp.Users[1].Name != userName ||
lsResp.Users[1].Role != userRole {
t.Fatal(fmt.Errorf("incorrect user info (%v)", lsResp.Users[1]))
}
resp, _, errs = usersCl.DelUser(auResp.ID, token)
if len(errs) > 0 {
t.Fatal(errs)
} else if resp.StatusCode != 200 {
t.Fatal(resp.StatusCode)
}
resp, lsResp, errs = usersCl.ListUsers(token)
if len(errs) > 0 {
t.Fatal(errs)
} else if resp.StatusCode != 200 {
t.Fatal(resp.StatusCode)
}
if len(lsResp.Users) != 1 {
t.Fatal(fmt.Errorf("incorrect users size (%d)", len(lsResp.Users)))
} else if lsResp.Users[0].ID != 0 ||
lsResp.Users[0].Name != adminName ||
lsResp.Users[0].Role != userstore.AdminRole {
t.Fatal(fmt.Errorf("incorrect root info (%v)", lsResp.Users[0]))
}
resp, _, errs = usersCl.Logout(token)
if len(errs) > 0 {
t.Fatal(errs)

View file

@ -1,8 +1,14 @@
package server
import (
"encoding/base64"
"fmt"
"io/ioutil"
// "path"
"math/rand"
"net/http"
"path"
"strings"
"testing"
"time"
"github.com/ihexxa/gocfg"
@ -64,3 +70,104 @@ func compareFileContent(fs fspkg.ISimpleFS, uid, filePath string, expectedConten
return string(gotContent) == expectedContent, nil
}
func assertUploadOK(t *testing.T, filePath, content, addr string, token *http.Cookie) bool {
cl := client.NewFilesClient(addr, token)
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
}
base64Content := base64.StdEncoding.EncodeToString([]byte(content))
res, _, errs = cl.UploadChunk(filePath, base64Content, 0)
if len(errs) > 0 {
t.Error(errs)
return false
} else if res.StatusCode != 200 {
t.Error(res.StatusCode)
return false
}
return true
}
func assertDownloadOK(t *testing.T, filePath, content, addr string, token *http.Cookie) bool {
var (
res *http.Response
body string
errs []error
fileSize = int64(len([]byte(content)))
)
cl := client.NewFilesClient(addr, token)
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),
})
}
fileName := path.Base(filePath)
contentDispositionHeader := res.Header.Get("Content-Disposition")
if len(errs) > 0 {
t.Error(errs)
return false
}
if res.StatusCode != 200 && res.StatusCode != 206 {
t.Error(res.StatusCode)
return false
}
if contentDispositionHeader != fmt.Sprintf(`attachment; filename="%s"`, fileName) {
t.Errorf("incorrect Content-Disposition header: %s", contentDispositionHeader)
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
}