github.com/divyam234/rclone@v1.64.1/cmd/serve/http/http_test.go (about) 1 package http 2 3 import ( 4 "context" 5 "flag" 6 "io" 7 "net/http" 8 "os" 9 "path/filepath" 10 "strings" 11 "testing" 12 "time" 13 14 _ "github.com/divyam234/rclone/backend/local" 15 "github.com/divyam234/rclone/cmd/serve/proxy/proxyflags" 16 "github.com/divyam234/rclone/fs" 17 "github.com/divyam234/rclone/fs/filter" 18 libhttp "github.com/divyam234/rclone/lib/http" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 ) 22 23 var ( 24 updateGolden = flag.Bool("updategolden", false, "update golden files for regression test") 25 ) 26 27 const ( 28 testBindAddress = "localhost:0" 29 testUser = "user" 30 testPass = "pass" 31 testTemplate = "testdata/golden/testindex.html" 32 ) 33 34 func start(ctx context.Context, t *testing.T, f fs.Fs) (s *HTTP, testURL string) { 35 opts := Options{ 36 HTTP: libhttp.DefaultCfg(), 37 Template: libhttp.TemplateConfig{ 38 Path: testTemplate, 39 }, 40 } 41 opts.HTTP.ListenAddr = []string{testBindAddress} 42 if proxyflags.Opt.AuthProxy == "" { 43 opts.Auth.BasicUser = testUser 44 opts.Auth.BasicPass = testPass 45 } 46 47 s, err := run(ctx, f, opts) 48 require.NoError(t, err, "failed to start server") 49 50 urls := s.server.URLs() 51 require.Len(t, urls, 1, "expected one URL") 52 53 testURL = urls[0] 54 55 // try to connect to the test server 56 pause := time.Millisecond 57 for i := 0; i < 10; i++ { 58 resp, err := http.Head(testURL) 59 if err == nil { 60 _ = resp.Body.Close() 61 return 62 } 63 // t.Logf("couldn't connect, sleeping for %v: %v", pause, err) 64 time.Sleep(pause) 65 pause *= 2 66 } 67 t.Fatal("couldn't connect to server") 68 69 return s, testURL 70 } 71 72 var ( 73 datedObject = "two.txt" 74 expectedTime = time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC) 75 ) 76 77 // check body against the file, or re-write body if -updategolden is 78 // set. 79 func checkGolden(t *testing.T, fileName string, got []byte) { 80 if *updateGolden { 81 t.Logf("Updating golden file %q", fileName) 82 err := os.WriteFile(fileName, got, 0666) 83 require.NoError(t, err) 84 } else { 85 want, err := os.ReadFile(fileName) 86 require.NoError(t, err) 87 wants := strings.Split(string(want), "\n") 88 gots := strings.Split(string(got), "\n") 89 assert.Equal(t, wants, gots, fileName) 90 } 91 } 92 93 func testGET(t *testing.T, useProxy bool) { 94 ctx := context.Background() 95 // ci := fs.GetConfig(ctx) 96 // ci.LogLevel = fs.LogLevelDebug 97 98 // exclude files called hidden.txt and directories called hidden 99 fi := filter.GetConfig(ctx) 100 require.NoError(t, fi.AddRule("- hidden.txt")) 101 require.NoError(t, fi.AddRule("- hidden/**")) 102 103 var f fs.Fs 104 if useProxy { 105 // the backend config will be made by the proxy 106 prog, err := filepath.Abs("../servetest/proxy_code.go") 107 require.NoError(t, err) 108 files, err := filepath.Abs("testdata/files") 109 require.NoError(t, err) 110 cmd := "go run " + prog + " " + files 111 112 // FIXME this is untidy setting a global variable! 113 proxyflags.Opt.AuthProxy = cmd 114 defer func() { 115 proxyflags.Opt.AuthProxy = "" 116 }() 117 118 f = nil 119 } else { 120 // Create a test Fs 121 var err error 122 f, err = fs.NewFs(context.Background(), "testdata/files") 123 require.NoError(t, err) 124 125 // set date of datedObject to expectedTime 126 obj, err := f.NewObject(context.Background(), datedObject) 127 require.NoError(t, err) 128 require.NoError(t, obj.SetModTime(context.Background(), expectedTime)) 129 } 130 131 s, testURL := start(ctx, t, f) 132 defer func() { 133 assert.NoError(t, s.server.Shutdown()) 134 }() 135 136 for _, test := range []struct { 137 URL string 138 Status int 139 Golden string 140 Method string 141 Range string 142 }{ 143 { 144 URL: "", 145 Status: http.StatusOK, 146 Golden: "testdata/golden/index.html", 147 }, 148 { 149 URL: "notfound", 150 Status: http.StatusNotFound, 151 Golden: "testdata/golden/notfound.html", 152 }, 153 { 154 URL: "dirnotfound/", 155 Status: http.StatusNotFound, 156 Golden: "testdata/golden/dirnotfound.html", 157 }, 158 { 159 URL: "hidden/", 160 Status: http.StatusNotFound, 161 Golden: "testdata/golden/hiddendir.html", 162 }, 163 { 164 URL: "one%25.txt", 165 Status: http.StatusOK, 166 Golden: "testdata/golden/one.txt", 167 }, 168 { 169 URL: "hidden.txt", 170 Status: http.StatusNotFound, 171 Golden: "testdata/golden/hidden.txt", 172 }, 173 { 174 URL: "three/", 175 Status: http.StatusOK, 176 Golden: "testdata/golden/three.html", 177 }, 178 { 179 URL: "three/a.txt", 180 Status: http.StatusOK, 181 Golden: "testdata/golden/a.txt", 182 }, 183 { 184 URL: "", 185 Method: "HEAD", 186 Status: http.StatusOK, 187 Golden: "testdata/golden/indexhead.txt", 188 }, 189 { 190 URL: "one%25.txt", 191 Method: "HEAD", 192 Status: http.StatusOK, 193 Golden: "testdata/golden/onehead.txt", 194 }, 195 { 196 URL: "", 197 Method: "POST", 198 Status: http.StatusMethodNotAllowed, 199 Golden: "testdata/golden/indexpost.txt", 200 }, 201 { 202 URL: "one%25.txt", 203 Method: "POST", 204 Status: http.StatusMethodNotAllowed, 205 Golden: "testdata/golden/onepost.txt", 206 }, 207 { 208 URL: "two.txt", 209 Status: http.StatusOK, 210 Golden: "testdata/golden/two.txt", 211 }, 212 { 213 URL: "two.txt", 214 Status: http.StatusPartialContent, 215 Range: "bytes=2-5", 216 Golden: "testdata/golden/two2-5.txt", 217 }, 218 { 219 URL: "two.txt", 220 Status: http.StatusPartialContent, 221 Range: "bytes=0-6", 222 Golden: "testdata/golden/two-6.txt", 223 }, 224 { 225 URL: "two.txt", 226 Status: http.StatusPartialContent, 227 Range: "bytes=3-", 228 Golden: "testdata/golden/two3-.txt", 229 }, 230 } { 231 method := test.Method 232 if method == "" { 233 method = "GET" 234 } 235 req, err := http.NewRequest(method, testURL+test.URL, nil) 236 require.NoError(t, err) 237 if test.Range != "" { 238 req.Header.Add("Range", test.Range) 239 } 240 req.SetBasicAuth(testUser, testPass) 241 resp, err := http.DefaultClient.Do(req) 242 require.NoError(t, err) 243 assert.Equal(t, test.Status, resp.StatusCode, test.Golden) 244 body, err := io.ReadAll(resp.Body) 245 require.NoError(t, err) 246 247 // Check we got a Last-Modified header and that it is a valid date 248 if test.Status == http.StatusOK || test.Status == http.StatusPartialContent { 249 lastModified := resp.Header.Get("Last-Modified") 250 assert.NotEqual(t, "", lastModified, test.Golden) 251 modTime, err := http.ParseTime(lastModified) 252 assert.NoError(t, err, test.Golden) 253 // check the actual date on our special file 254 if test.URL == datedObject { 255 assert.Equal(t, expectedTime, modTime, test.Golden) 256 } 257 } 258 259 checkGolden(t, test.Golden, body) 260 } 261 } 262 263 func TestGET(t *testing.T) { 264 testGET(t, false) 265 } 266 267 func TestAuthProxy(t *testing.T) { 268 testGET(t, true) 269 }