github.com/divyam234/rclone@v1.64.1/cmd/serve/webdav/webdav_test.go (about) 1 // Serve webdav tests set up a server and run the integration tests 2 // for the webdav remote against it. 3 // 4 // We skip tests on platforms with troublesome character mappings 5 6 //go:build !windows && !darwin 7 // +build !windows,!darwin 8 9 package webdav 10 11 import ( 12 "context" 13 "flag" 14 "io" 15 "net/http" 16 "os" 17 "strings" 18 "testing" 19 "time" 20 21 _ "github.com/divyam234/rclone/backend/local" 22 "github.com/divyam234/rclone/cmd/serve/servetest" 23 "github.com/divyam234/rclone/fs" 24 "github.com/divyam234/rclone/fs/config/configmap" 25 "github.com/divyam234/rclone/fs/config/obscure" 26 "github.com/divyam234/rclone/fs/filter" 27 "github.com/divyam234/rclone/fs/hash" 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 "golang.org/x/net/webdav" 31 ) 32 33 const ( 34 testBindAddress = "localhost:0" 35 testUser = "user" 36 testPass = "pass" 37 testTemplate = "../http/testdata/golden/testindex.html" 38 ) 39 40 // check interfaces 41 var ( 42 _ os.FileInfo = FileInfo{nil, nil} 43 _ webdav.ETager = FileInfo{nil, nil} 44 _ webdav.ContentTyper = FileInfo{nil, nil} 45 ) 46 47 // TestWebDav runs the webdav server then runs the unit tests for the 48 // webdav remote against it. 49 func TestWebDav(t *testing.T) { 50 // Configure and start the server 51 start := func(f fs.Fs) (configmap.Simple, func()) { 52 opt := DefaultOpt 53 opt.HTTP.ListenAddr = []string{testBindAddress} 54 opt.HTTP.BaseURL = "/prefix" 55 opt.Auth.BasicUser = testUser 56 opt.Auth.BasicPass = testPass 57 opt.Template.Path = testTemplate 58 opt.HashType = hash.MD5 59 60 // Start the server 61 w, err := newWebDAV(context.Background(), f, &opt) 62 require.NoError(t, err) 63 require.NoError(t, w.serve()) 64 65 // Config for the backend we'll use to connect to the server 66 config := configmap.Simple{ 67 "type": "webdav", 68 "vendor": "owncloud", 69 "url": w.Server.URLs()[0], 70 "user": testUser, 71 "pass": obscure.MustObscure(testPass), 72 } 73 74 return config, func() { 75 assert.NoError(t, w.Shutdown()) 76 w.Wait() 77 } 78 } 79 80 servetest.Run(t, "webdav", start) 81 } 82 83 // Test serve http functionality in serve webdav 84 // While similar to http serve, there are some inconsistencies 85 // in the handling of some requests such as POST requests 86 87 var ( 88 updateGolden = flag.Bool("updategolden", false, "update golden files for regression test") 89 ) 90 91 func TestHTTPFunction(t *testing.T) { 92 ctx := context.Background() 93 // exclude files called hidden.txt and directories called hidden 94 fi := filter.GetConfig(ctx) 95 require.NoError(t, fi.AddRule("- hidden.txt")) 96 require.NoError(t, fi.AddRule("- hidden/**")) 97 98 // Uses the same test files as http tests but with different golden. 99 f, err := fs.NewFs(context.Background(), "../http/testdata/files") 100 assert.NoError(t, err) 101 102 opt := DefaultOpt 103 opt.HTTP.ListenAddr = []string{testBindAddress} 104 opt.Template.Path = testTemplate 105 106 // Start the server 107 w, err := newWebDAV(context.Background(), f, &opt) 108 assert.NoError(t, err) 109 require.NoError(t, w.serve()) 110 defer func() { 111 assert.NoError(t, w.Shutdown()) 112 w.Wait() 113 }() 114 testURL := w.Server.URLs()[0] 115 pause := time.Millisecond 116 i := 0 117 for ; i < 10; i++ { 118 resp, err := http.Head(testURL) 119 if err == nil { 120 _ = resp.Body.Close() 121 break 122 } 123 // t.Logf("couldn't connect, sleeping for %v: %v", pause, err) 124 time.Sleep(pause) 125 pause *= 2 126 } 127 if i >= 10 { 128 t.Fatal("couldn't connect to server") 129 } 130 131 HelpTestGET(t, testURL) 132 } 133 134 // check body against the file, or re-write body if -updategolden is 135 // set. 136 func checkGolden(t *testing.T, fileName string, got []byte) { 137 if *updateGolden { 138 t.Logf("Updating golden file %q", fileName) 139 err := os.WriteFile(fileName, got, 0666) 140 require.NoError(t, err) 141 } else { 142 want, err := os.ReadFile(fileName) 143 require.NoError(t, err, "problem") 144 wants := strings.Split(string(want), "\n") 145 gots := strings.Split(string(got), "\n") 146 assert.Equal(t, wants, gots, fileName) 147 } 148 } 149 150 func HelpTestGET(t *testing.T, testURL string) { 151 for _, test := range []struct { 152 URL string 153 Status int 154 Golden string 155 Method string 156 Range string 157 }{ 158 { 159 URL: "", 160 Status: http.StatusOK, 161 Golden: "testdata/golden/index.html", 162 }, 163 { 164 URL: "notfound", 165 Status: http.StatusNotFound, 166 Golden: "testdata/golden/notfound.html", 167 }, 168 { 169 URL: "dirnotfound/", 170 Status: http.StatusNotFound, 171 Golden: "testdata/golden/dirnotfound.html", 172 }, 173 { 174 URL: "hidden/", 175 Status: http.StatusNotFound, 176 Golden: "testdata/golden/hiddendir.html", 177 }, 178 { 179 URL: "one%25.txt", 180 Status: http.StatusOK, 181 Golden: "testdata/golden/one.txt", 182 }, 183 { 184 URL: "hidden.txt", 185 Status: http.StatusNotFound, 186 Golden: "testdata/golden/hidden.txt", 187 }, 188 { 189 URL: "three/", 190 Status: http.StatusOK, 191 Golden: "testdata/golden/three.html", 192 }, 193 { 194 URL: "three/a.txt", 195 Status: http.StatusOK, 196 Golden: "testdata/golden/a.txt", 197 }, 198 { 199 URL: "", 200 Method: "HEAD", 201 Status: http.StatusOK, 202 Golden: "testdata/golden/indexhead.txt", 203 }, 204 { 205 URL: "one%25.txt", 206 Method: "HEAD", 207 Status: http.StatusOK, 208 Golden: "testdata/golden/onehead.txt", 209 }, 210 { 211 URL: "", 212 Method: "POST", 213 Status: http.StatusMethodNotAllowed, 214 Golden: "testdata/golden/indexpost.txt", 215 }, 216 { 217 URL: "one%25.txt", 218 Method: "POST", 219 Status: http.StatusOK, 220 Golden: "testdata/golden/onepost.txt", 221 }, 222 { 223 URL: "two.txt", 224 Status: http.StatusOK, 225 Golden: "testdata/golden/two.txt", 226 }, 227 { 228 URL: "two.txt", 229 Status: http.StatusPartialContent, 230 Range: "bytes=2-5", 231 Golden: "testdata/golden/two2-5.txt", 232 }, 233 { 234 URL: "two.txt", 235 Status: http.StatusPartialContent, 236 Range: "bytes=0-6", 237 Golden: "testdata/golden/two-6.txt", 238 }, 239 { 240 URL: "two.txt", 241 Status: http.StatusPartialContent, 242 Range: "bytes=3-", 243 Golden: "testdata/golden/two3-.txt", 244 }, 245 } { 246 method := test.Method 247 if method == "" { 248 method = "GET" 249 } 250 req, err := http.NewRequest(method, testURL+test.URL, nil) 251 require.NoError(t, err) 252 if test.Range != "" { 253 req.Header.Add("Range", test.Range) 254 } 255 resp, err := http.DefaultClient.Do(req) 256 require.NoError(t, err) 257 assert.Equal(t, test.Status, resp.StatusCode, test.Golden) 258 body, err := io.ReadAll(resp.Body) 259 require.NoError(t, err) 260 261 checkGolden(t, test.Golden, body) 262 } 263 }