| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- package httpd
- import (
- "context"
- "fmt"
- "html/template"
- "io/ioutil"
- "net/http"
- "net/http/httptest"
- "net/url"
- "os"
- "path/filepath"
- "runtime"
- "strings"
- "testing"
- "github.com/go-chi/chi"
- "github.com/stretchr/testify/assert"
- "github.com/drakkan/sftpgo/common"
- "github.com/drakkan/sftpgo/dataprovider"
- "github.com/drakkan/sftpgo/utils"
- "github.com/drakkan/sftpgo/vfs"
- )
- const (
- invalidURL = "http://foo\x7f.com/"
- inactiveURL = "http://127.0.0.1:12345"
- )
- func TestGetRespStatus(t *testing.T) {
- var err error
- err = &dataprovider.MethodDisabledError{}
- respStatus := getRespStatus(err)
- assert.Equal(t, http.StatusForbidden, respStatus)
- err = fmt.Errorf("generic error")
- respStatus = getRespStatus(err)
- assert.Equal(t, http.StatusInternalServerError, respStatus)
- }
- func TestCheckResponse(t *testing.T) {
- err := checkResponse(http.StatusOK, http.StatusCreated)
- assert.Error(t, err)
- err = checkResponse(http.StatusBadRequest, http.StatusBadRequest)
- assert.NoError(t, err)
- }
- func TestCheckFolder(t *testing.T) {
- expected := &vfs.BaseVirtualFolder{}
- actual := &vfs.BaseVirtualFolder{}
- err := checkFolder(expected, actual)
- assert.Error(t, err)
- expected.ID = 1
- actual.ID = 2
- err = checkFolder(expected, actual)
- assert.Error(t, err)
- expected.ID = 2
- actual.ID = 2
- expected.MappedPath = "path"
- err = checkFolder(expected, actual)
- assert.Error(t, err)
- expected.MappedPath = ""
- expected.LastQuotaUpdate = 1
- err = checkFolder(expected, actual)
- assert.Error(t, err)
- expected.LastQuotaUpdate = 0
- expected.UsedQuotaFiles = 1
- err = checkFolder(expected, actual)
- assert.Error(t, err)
- expected.UsedQuotaFiles = 0
- expected.UsedQuotaSize = 1
- err = checkFolder(expected, actual)
- assert.Error(t, err)
- expected.UsedQuotaSize = 0
- expected.Users = append(expected.Users, "user1")
- err = checkFolder(expected, actual)
- assert.Error(t, err)
- actual.Users = append(actual.Users, "user2")
- err = checkFolder(expected, actual)
- assert.Error(t, err)
- expected.Users = nil
- actual.Users = nil
- }
- func TestCheckUser(t *testing.T) {
- expected := &dataprovider.User{}
- actual := &dataprovider.User{}
- actual.Password = "password"
- err := checkUser(expected, actual)
- assert.Error(t, err)
- actual.Password = ""
- err = checkUser(expected, actual)
- assert.Error(t, err)
- expected.ID = 1
- actual.ID = 2
- err = checkUser(expected, actual)
- assert.Error(t, err)
- expected.ID = 2
- actual.ID = 2
- expected.Permissions = make(map[string][]string)
- expected.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermDelete, dataprovider.PermDownload}
- actual.Permissions = make(map[string][]string)
- err = checkUser(expected, actual)
- assert.Error(t, err)
- actual.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
- err = checkUser(expected, actual)
- assert.Error(t, err)
- expected.Permissions["/"] = append(expected.Permissions["/"], dataprovider.PermRename)
- err = checkUser(expected, actual)
- assert.Error(t, err)
- expected.Permissions = make(map[string][]string)
- expected.Permissions["/somedir"] = []string{dataprovider.PermAny}
- actual.Permissions = make(map[string][]string)
- actual.Permissions["/otherdir"] = []string{dataprovider.PermCreateDirs, dataprovider.PermCreateSymlinks}
- err = checkUser(expected, actual)
- assert.Error(t, err)
- expected.Permissions = make(map[string][]string)
- actual.Permissions = make(map[string][]string)
- actual.FsConfig.Provider = 1
- err = checkUser(expected, actual)
- assert.Error(t, err)
- actual.FsConfig.Provider = 0
- expected.VirtualFolders = append(expected.VirtualFolders, vfs.VirtualFolder{
- BaseVirtualFolder: vfs.BaseVirtualFolder{
- MappedPath: os.TempDir(),
- },
- VirtualPath: "/vdir",
- })
- err = checkUser(expected, actual)
- assert.Error(t, err)
- actual.VirtualFolders = append(actual.VirtualFolders, vfs.VirtualFolder{
- BaseVirtualFolder: vfs.BaseVirtualFolder{
- MappedPath: os.TempDir(),
- },
- VirtualPath: "/vdir1",
- })
- err = checkUser(expected, actual)
- assert.Error(t, err)
- }
- func TestCompareUserFilters(t *testing.T) {
- expected := &dataprovider.User{}
- actual := &dataprovider.User{}
- actual.ID = 1
- expected.ID = 1
- expected.Filters.AllowedIP = []string{}
- actual.Filters.AllowedIP = []string{"192.168.1.2/32"}
- err := checkUser(expected, actual)
- assert.Error(t, err)
- expected.Filters.AllowedIP = []string{"192.168.1.3/32"}
- err = checkUser(expected, actual)
- assert.Error(t, err)
- expected.Filters.AllowedIP = []string{}
- actual.Filters.AllowedIP = []string{}
- expected.Filters.DeniedIP = []string{}
- actual.Filters.DeniedIP = []string{"192.168.1.2/32"}
- err = checkUser(expected, actual)
- assert.Error(t, err)
- expected.Filters.DeniedIP = []string{"192.168.1.3/32"}
- err = checkUser(expected, actual)
- assert.Error(t, err)
- expected.Filters.DeniedIP = []string{}
- actual.Filters.DeniedIP = []string{}
- expected.Filters.DeniedLoginMethods = []string{}
- actual.Filters.DeniedLoginMethods = []string{dataprovider.SSHLoginMethodPublicKey}
- err = checkUser(expected, actual)
- assert.Error(t, err)
- expected.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodPassword}
- err = checkUser(expected, actual)
- assert.Error(t, err)
- expected.Filters.DeniedLoginMethods = []string{}
- actual.Filters.DeniedLoginMethods = []string{}
- expected.Filters.MaxUploadFileSize = 0
- actual.Filters.MaxUploadFileSize = 100
- err = checkUser(expected, actual)
- assert.Error(t, err)
- actual.Filters.MaxUploadFileSize = 0
- expected.Filters.FileExtensions = append(expected.Filters.FileExtensions, dataprovider.ExtensionsFilter{
- Path: "/",
- AllowedExtensions: []string{".jpg", ".png"},
- DeniedExtensions: []string{".zip", ".rar"},
- })
- err = checkUser(expected, actual)
- assert.Error(t, err)
- actual.Filters.FileExtensions = append(actual.Filters.FileExtensions, dataprovider.ExtensionsFilter{
- Path: "/sub",
- AllowedExtensions: []string{".jpg", ".png"},
- DeniedExtensions: []string{".zip", ".rar"},
- })
- err = checkUser(expected, actual)
- assert.Error(t, err)
- actual.Filters.FileExtensions[0] = dataprovider.ExtensionsFilter{
- Path: "/",
- AllowedExtensions: []string{".jpg"},
- DeniedExtensions: []string{".zip", ".rar"},
- }
- err = checkUser(expected, actual)
- assert.Error(t, err)
- actual.Filters.FileExtensions[0] = dataprovider.ExtensionsFilter{
- Path: "/",
- AllowedExtensions: []string{".tiff", ".png"},
- DeniedExtensions: []string{".zip", ".rar"},
- }
- err = checkUser(expected, actual)
- assert.Error(t, err)
- actual.Filters.FileExtensions[0] = dataprovider.ExtensionsFilter{
- Path: "/",
- AllowedExtensions: []string{".jpg", ".png"},
- DeniedExtensions: []string{".tar.gz", ".rar"},
- }
- err = checkUser(expected, actual)
- assert.Error(t, err)
- }
- func TestCompareUserFields(t *testing.T) {
- expected := &dataprovider.User{}
- actual := &dataprovider.User{}
- expected.Permissions = make(map[string][]string)
- actual.Permissions = make(map[string][]string)
- expected.Username = "test"
- err := compareEqualsUserFields(expected, actual)
- assert.Error(t, err)
- expected.Username = ""
- expected.HomeDir = "homedir"
- err = compareEqualsUserFields(expected, actual)
- assert.Error(t, err)
- expected.HomeDir = ""
- expected.UID = 1
- err = compareEqualsUserFields(expected, actual)
- assert.Error(t, err)
- expected.UID = 0
- expected.GID = 1
- err = compareEqualsUserFields(expected, actual)
- assert.Error(t, err)
- expected.GID = 0
- expected.MaxSessions = 2
- err = compareEqualsUserFields(expected, actual)
- assert.Error(t, err)
- expected.MaxSessions = 0
- expected.QuotaSize = 4096
- err = compareEqualsUserFields(expected, actual)
- assert.Error(t, err)
- expected.QuotaSize = 0
- expected.QuotaFiles = 2
- err = compareEqualsUserFields(expected, actual)
- assert.Error(t, err)
- expected.QuotaFiles = 0
- expected.Permissions["/"] = []string{dataprovider.PermCreateDirs}
- err = compareEqualsUserFields(expected, actual)
- assert.Error(t, err)
- expected.Permissions = nil
- expected.UploadBandwidth = 64
- err = compareEqualsUserFields(expected, actual)
- assert.Error(t, err)
- expected.UploadBandwidth = 0
- expected.DownloadBandwidth = 128
- err = compareEqualsUserFields(expected, actual)
- assert.Error(t, err)
- expected.DownloadBandwidth = 0
- expected.Status = 1
- err = compareEqualsUserFields(expected, actual)
- assert.Error(t, err)
- expected.Status = 0
- expected.ExpirationDate = 123
- err = compareEqualsUserFields(expected, actual)
- assert.Error(t, err)
- }
- func TestCompareUserFsConfig(t *testing.T) {
- expected := &dataprovider.User{}
- actual := &dataprovider.User{}
- expected.FsConfig.Provider = 1
- err := compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- expected.FsConfig.Provider = 0
- expected.FsConfig.S3Config.Bucket = "bucket"
- err = compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- expected.FsConfig.S3Config.Bucket = ""
- expected.FsConfig.S3Config.Region = "region"
- err = compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- expected.FsConfig.S3Config.Region = ""
- expected.FsConfig.S3Config.AccessKey = "access key"
- err = compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- expected.FsConfig.S3Config.AccessKey = ""
- actual.FsConfig.S3Config.AccessSecret = "access secret"
- err = compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- secret, _ := utils.EncryptData("access secret")
- actual.FsConfig.S3Config.AccessSecret = ""
- expected.FsConfig.S3Config.AccessSecret = secret
- err = compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- expected.FsConfig.S3Config.AccessSecret = utils.RemoveDecryptionKey(secret)
- actual.FsConfig.S3Config.AccessSecret = utils.RemoveDecryptionKey(secret) + "a"
- err = compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- expected.FsConfig.S3Config.AccessSecret = "test"
- actual.FsConfig.S3Config.AccessSecret = ""
- err = compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- expected.FsConfig.S3Config.AccessSecret = ""
- actual.FsConfig.S3Config.AccessSecret = ""
- expected.FsConfig.S3Config.Endpoint = "http://127.0.0.1:9000/"
- err = compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- expected.FsConfig.S3Config.Endpoint = ""
- expected.FsConfig.S3Config.StorageClass = "Standard"
- err = compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- expected.FsConfig.S3Config.StorageClass = ""
- expected.FsConfig.S3Config.KeyPrefix = "somedir/subdir"
- err = compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- expected.FsConfig.S3Config.KeyPrefix = ""
- expected.FsConfig.S3Config.UploadPartSize = 10
- err = compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- expected.FsConfig.S3Config.UploadPartSize = 0
- expected.FsConfig.S3Config.UploadConcurrency = 3
- err = compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- }
- func TestCompareUserGCSConfig(t *testing.T) {
- expected := &dataprovider.User{}
- actual := &dataprovider.User{}
- expected.FsConfig.GCSConfig.KeyPrefix = "somedir/subdir"
- err := compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- expected.FsConfig.GCSConfig.KeyPrefix = ""
- expected.FsConfig.GCSConfig.Bucket = "bucket"
- err = compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- expected.FsConfig.GCSConfig.Bucket = ""
- expected.FsConfig.GCSConfig.StorageClass = "Standard"
- err = compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- expected.FsConfig.GCSConfig.StorageClass = ""
- expected.FsConfig.GCSConfig.AutomaticCredentials = 1
- err = compareUserFsConfig(expected, actual)
- assert.Error(t, err)
- expected.FsConfig.GCSConfig.AutomaticCredentials = 0
- }
- func TestGCSWebInvalidFormFile(t *testing.T) {
- form := make(url.Values)
- form.Set("username", "test_username")
- form.Set("fs_provider", "2")
- req, _ := http.NewRequest(http.MethodPost, webUserPath, strings.NewReader(form.Encode()))
- req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
- err := req.ParseForm()
- assert.NoError(t, err)
- _, err = getFsConfigFromUserPostFields(req)
- assert.EqualError(t, err, http.ErrNotMultipart.Error())
- }
- func TestApiCallsWithBadURL(t *testing.T) {
- oldBaseURL := httpBaseURL
- oldAuthUsername := authUsername
- oldAuthPassword := authPassword
- SetBaseURLAndCredentials(invalidURL, oldAuthUsername, oldAuthPassword)
- folder := vfs.BaseVirtualFolder{
- MappedPath: os.TempDir(),
- }
- u := dataprovider.User{}
- _, _, err := UpdateUser(u, http.StatusBadRequest)
- assert.Error(t, err)
- _, err = RemoveUser(u, http.StatusNotFound)
- assert.Error(t, err)
- _, err = RemoveFolder(folder, http.StatusNotFound)
- assert.Error(t, err)
- _, _, err = GetUsers(1, 0, "", http.StatusBadRequest)
- assert.Error(t, err)
- _, _, err = GetFolders(1, 0, "", http.StatusBadRequest)
- assert.Error(t, err)
- _, err = UpdateQuotaUsage(u, "", http.StatusNotFound)
- assert.Error(t, err)
- _, err = UpdateFolderQuotaUsage(folder, "", http.StatusNotFound)
- assert.Error(t, err)
- _, err = CloseConnection("non_existent_id", http.StatusNotFound)
- assert.Error(t, err)
- _, _, err = Dumpdata("backup.json", "", http.StatusBadRequest)
- assert.Error(t, err)
- _, _, err = Loaddata("/tmp/backup.json", "", "", http.StatusBadRequest)
- assert.Error(t, err)
- SetBaseURLAndCredentials(oldBaseURL, oldAuthUsername, oldAuthPassword)
- }
- func TestApiCallToNotListeningServer(t *testing.T) {
- oldBaseURL := httpBaseURL
- oldAuthUsername := authUsername
- oldAuthPassword := authPassword
- SetBaseURLAndCredentials(inactiveURL, oldAuthUsername, oldAuthPassword)
- u := dataprovider.User{}
- _, _, err := AddUser(u, http.StatusBadRequest)
- assert.Error(t, err)
- _, _, err = UpdateUser(u, http.StatusNotFound)
- assert.Error(t, err)
- _, err = RemoveUser(u, http.StatusNotFound)
- assert.Error(t, err)
- _, _, err = GetUserByID(-1, http.StatusNotFound)
- assert.Error(t, err)
- _, _, err = GetUsers(100, 0, "", http.StatusOK)
- assert.Error(t, err)
- _, err = UpdateQuotaUsage(u, "", http.StatusNotFound)
- assert.Error(t, err)
- _, _, err = GetQuotaScans(http.StatusOK)
- assert.Error(t, err)
- _, err = StartQuotaScan(u, http.StatusNotFound)
- assert.Error(t, err)
- folder := vfs.BaseVirtualFolder{
- MappedPath: os.TempDir(),
- }
- _, err = StartFolderQuotaScan(folder, http.StatusNotFound)
- assert.Error(t, err)
- _, _, err = AddFolder(folder, http.StatusOK)
- assert.Error(t, err)
- _, err = RemoveFolder(folder, http.StatusOK)
- assert.Error(t, err)
- _, _, err = GetFolders(0, 0, "", http.StatusOK)
- assert.Error(t, err)
- _, err = UpdateFolderQuotaUsage(folder, "", http.StatusNotFound)
- assert.Error(t, err)
- _, _, err = GetFoldersQuotaScans(http.StatusOK)
- assert.Error(t, err)
- _, _, err = GetConnections(http.StatusOK)
- assert.Error(t, err)
- _, err = CloseConnection("non_existent_id", http.StatusNotFound)
- assert.Error(t, err)
- _, _, err = GetVersion(http.StatusOK)
- assert.Error(t, err)
- _, _, err = GetProviderStatus(http.StatusOK)
- assert.Error(t, err)
- _, _, err = Dumpdata("backup.json", "0", http.StatusOK)
- assert.Error(t, err)
- _, _, err = Loaddata("/tmp/backup.json", "", "", http.StatusOK)
- assert.Error(t, err)
- SetBaseURLAndCredentials(oldBaseURL, oldAuthUsername, oldAuthPassword)
- }
- func TestBasicAuth(t *testing.T) {
- oldAuthUsername := authUsername
- oldAuthPassword := authPassword
- authUserFile := filepath.Join(os.TempDir(), "http_users.txt")
- authUserData := []byte("test1:$2y$05$bcHSED7aO1cfLto6ZdDBOOKzlwftslVhtpIkRhAtSa4GuLmk5mola\n")
- err := ioutil.WriteFile(authUserFile, authUserData, os.ModePerm)
- assert.NoError(t, err)
- httpAuth, _ = newBasicAuthProvider(authUserFile)
- _, _, err = GetVersion(http.StatusUnauthorized)
- assert.NoError(t, err)
- SetBaseURLAndCredentials(httpBaseURL, "test1", "password1")
- _, _, err = GetVersion(http.StatusOK)
- assert.NoError(t, err)
- SetBaseURLAndCredentials(httpBaseURL, "test1", "wrong_password")
- resp, _ := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(metricsPath), nil, "")
- defer resp.Body.Close()
- assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
- authUserData = append(authUserData, []byte("test2:$apr1$gLnIkRIf$Xr/6aJfmIrihP4b2N2tcs/\n")...)
- err = ioutil.WriteFile(authUserFile, authUserData, os.ModePerm)
- assert.NoError(t, err)
- SetBaseURLAndCredentials(httpBaseURL, "test2", "password2")
- _, _, err = GetVersion(http.StatusOK)
- assert.NoError(t, err)
- SetBaseURLAndCredentials(httpBaseURL, "test2", "wrong_password")
- _, _, err = GetVersion(http.StatusOK)
- assert.Error(t, err)
- authUserData = append(authUserData, []byte("test3:$apr1$gLnIkRIf$Xr/6$aJfmIr$ihP4b2N2tcs/\n")...)
- err = ioutil.WriteFile(authUserFile, authUserData, os.ModePerm)
- assert.NoError(t, err)
- SetBaseURLAndCredentials(httpBaseURL, "test3", "wrong_password")
- _, _, err = GetVersion(http.StatusUnauthorized)
- assert.NoError(t, err)
- authUserData = append(authUserData, []byte("test4:$invalid$gLnIkRIf$Xr/6$aJfmIr$ihP4b2N2tcs/\n")...)
- err = ioutil.WriteFile(authUserFile, authUserData, os.ModePerm)
- assert.NoError(t, err)
- SetBaseURLAndCredentials(httpBaseURL, "test3", "password2")
- _, _, err = GetVersion(http.StatusUnauthorized)
- assert.NoError(t, err)
- if runtime.GOOS != "windows" {
- authUserData = append(authUserData, []byte("test5:$apr1$gLnIkRIf$Xr/6aJfmIrihP4b2N2tcs/\n")...)
- err = ioutil.WriteFile(authUserFile, authUserData, os.ModePerm)
- assert.NoError(t, err)
- err = os.Chmod(authUserFile, 0001)
- assert.NoError(t, err)
- SetBaseURLAndCredentials(httpBaseURL, "test5", "password2")
- _, _, err = GetVersion(http.StatusUnauthorized)
- assert.NoError(t, err)
- err = os.Chmod(authUserFile, os.ModePerm)
- assert.NoError(t, err)
- }
- authUserData = append(authUserData, []byte("\"foo\"bar\"\r\n")...)
- err = ioutil.WriteFile(authUserFile, authUserData, os.ModePerm)
- assert.NoError(t, err)
- SetBaseURLAndCredentials(httpBaseURL, "test2", "password2")
- _, _, err = GetVersion(http.StatusUnauthorized)
- assert.NoError(t, err)
- err = os.Remove(authUserFile)
- assert.NoError(t, err)
- SetBaseURLAndCredentials(httpBaseURL, oldAuthUsername, oldAuthPassword)
- httpAuth, _ = newBasicAuthProvider("")
- }
- func TestCloseConnectionHandler(t *testing.T) {
- req, _ := http.NewRequest(http.MethodDelete, activeConnectionsPath+"/connectionID", nil)
- rctx := chi.NewRouteContext()
- rctx.URLParams.Add("connectionID", "")
- req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx))
- rr := httptest.NewRecorder()
- handleCloseConnection(rr, req)
- assert.Equal(t, http.StatusBadRequest, rr.Code)
- }
- func TestRenderInvalidTemplate(t *testing.T) {
- tmpl, err := template.New("test").Parse("{{.Count}}")
- if assert.NoError(t, err) {
- templates["no_match"] = tmpl
- rw := httptest.NewRecorder()
- renderTemplate(rw, "no_match", map[string]string{})
- assert.Equal(t, http.StatusInternalServerError, rw.Code)
- }
- }
- func TestQuotaScanInvalidFs(t *testing.T) {
- user := dataprovider.User{
- Username: "test",
- HomeDir: os.TempDir(),
- FsConfig: dataprovider.Filesystem{
- Provider: 1,
- },
- }
- common.QuotaScans.AddUserQuotaScan(user.Username)
- err := doQuotaScan(user)
- assert.Error(t, err)
- }
|