package megauploader import ( "bytes" "encoding/json" "flag" "fmt" "io/ioutil" "mime/multipart" "net/http" "net/http/httptest" "os" "strconv" "strings" "testing" "github.com/Flaque/filet" "github.com/headzoo/surf" ) var ( doHTTP = flag.Bool("http-tests", false, "run HTTP integration tests") ) func TestMain(m *testing.M) { flag.Parse() result := m.Run() os.Exit(result) } func getServer(cfg string) *httptest.Server { conf, err := ParseConf([]byte(cfg)) if err != nil { panic("Error parsing conf in getServer") } mu := NewMegaUploader(conf) ts := httptest.NewServer(mu.SetupRoutes()) return ts } // ask home page without authentication; 401 expected func TestAuthDeny(t *testing.T) { if !*doHTTP { return } ts := getServer(``) defer ts.Close() resp, err := ts.Client().Get(ts.URL) if err != nil { t.Fatalf("Error asking home to test server") return } if resp.StatusCode != 401 { t.Errorf("Viewing home should require authentication; got `%s` instead", resp.Status, ) } } // ask for non-existing page without authentication; 404 expected func TestNotFoundWhenUnauthenticated(t *testing.T) { if !*doHTTP { return } ts := getServer(``) defer ts.Close() resp, err := ts.Client().Get(ts.URL + "/dontexist") if err != nil { t.Fatalf("Error asking non-existent URL to test server: \n%s\n", err) return } if resp.StatusCode != 404 { t.Errorf("Asked not existing endpoint, expected 404; got `%s` instead", resp.Status, ) } } func userRequest(url, user string, t *testing.T) *http.Request { req, err := http.NewRequest("GET", url, nil) if err != nil { t.Fatalf("error preparing request: \n%s\n", err) return nil } req.Header.Add("X-Forwarded-User", user) return req } // ask for home with valid user func TestAuthOk(t *testing.T) { if !*doHTTP { return } ts := getServer(``) defer ts.Close() cl := ts.Client() req := userRequest(ts.URL, "someone", t) resp, err := cl.Do(req) if err != nil { t.Fatalf("Error asking home to test server") return } if resp.StatusCode != 200 { t.Errorf("Viewing home should require authentication; got `%s` instead", resp.Status, ) } } func getShareList(ts *httptest.Server, user string, t *testing.T) []Share { req := userRequest(ts.URL+"/api/share", user, t) cl := ts.Client() resp, err := cl.Do(req) if err != nil { t.Fatalf("Error asking share list to test server: \n%s\n", err) return []Share{} } body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal("Error reading response", err) return []Share{} } var shares []Share err = json.Unmarshal(body, &shares) if err != nil { t.Fatal("Invalid JSON received", err) return []Share{} } return shares } // share list with invalid user func TestShareRejected(t *testing.T) { if !*doHTTP { return } ts := getServer(` global: excluded: - john@doe.us - foo bar shares: - name: foo authorized: ["*"] description: foo `) defer ts.Close() shares := getShareList(ts, "john@doe.us", t) if len(shares) != 0 { t.Fatal("Asked share list with banned user, expected empty, got", shares) return } } // share list with valid user func TestShareOk(t *testing.T) { if !*doHTTP { return } ts := getServer(` global: excluded: - john@doe.us - foo bar shares: - name: foo authorized: ["*"] description: foo `) defer ts.Close() shares := getShareList(ts, "someone elese", t) if len(shares) == 0 { t.Fatal("Asked share list with not banned user, expected [foo], got", shares) return } if shares[0].Name != "foo" { t.Error("The only share should be `foo`, got", shares[0].Name) return } } func TestUpload(t *testing.T) { if !*doHTTP { return } defer filet.CleanUp(t) d := filet.TmpDir(t, "") conf := ` shares: - name: foo dir: DIR authorized: ["*"] description: foo ` conf = strings.Replace(conf, "DIR", d, 1) ts := getServer(conf) cl := ts.Client() bodyBuf := bytes.Buffer{} bodyWriter := multipart.NewWriter(&bodyBuf) fileWriter, err := bodyWriter.CreateFormFile("file", "foo.txt") if err != nil { t.Fatal("error creating form file", err) return } _, err = fileWriter.Write([]byte(`example content`)) if err != nil { t.Fatal("error writing on form file", err) return } contentType := bodyWriter.FormDataContentType() bodyWriter.Close() req := userRequest(ts.URL+"/api/upload/foo", "someone", t) req.Method = "POST" req.Header.Set("Content-Type", contentType) req.ContentLength = int64(bodyBuf.Len()) req.Body = ioutil.NopCloser(&bodyBuf) resp, err := cl.Do(req) if err != nil { t.Fatal("Error POSTing file", err) return } defer resp.Body.Close() respBody, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal("Error reading server response", err) return } if resp.StatusCode < 200 || resp.StatusCode > 299 { t.Error("Server status is not success:", resp.Status, respBody) } fname := d + "/" + string(respBody) if !filet.Exists(t, fname) { t.Error("File does not exist", fname) } else if !filet.FileSays(t, fname, []byte("example content")) { t.Error("File exists but has wrong content") } } func TestUploadBig(t *testing.T) { if !*doHTTP { return } limit := 21 if testing.Short() { limit = 2 } // test with 1,5,9,13,17,21MB for i := 1; i < limit; i += 4 { t.Run(fmt.Sprintf("S=%d", i), testUploadBig) } } func testUploadBig(t *testing.T) { size, err := strconv.Atoi(strings.SplitN(t.Name(), "=", 2)[1]) if err != nil { t.Fatalf("Cannot convert size %s to int: %s", t.Name(), err) return } size *= 1024 * 1024 defer filet.CleanUp(t) d := filet.TmpDir(t, "") conf := ` shares: - name: foo dir: DIR authorized: ["*"] description: foo ` conf = strings.Replace(conf, "DIR", d, 1) ts := getServer(conf) cl := ts.Client() bodyBuf := bytes.Buffer{} bodyWriter := multipart.NewWriter(&bodyBuf) fileWriter, err := bodyWriter.CreateFormFile("file", "foo.txt") if err != nil { t.Fatal("error creating form file", err) return } // write 3MB made of 'a' for i := 0; i < size; i++ { _, err = fileWriter.Write([]byte(`a`)) if err != nil { t.Fatal("error writing on form file", err) return } } contentType := bodyWriter.FormDataContentType() bodyWriter.Close() req := userRequest(ts.URL+"/api/upload/foo", "someone", t) req.Method = "POST" req.Header.Set("Content-Type", contentType) req.ContentLength = int64(bodyBuf.Len()) req.Body = ioutil.NopCloser(&bodyBuf) resp, err := cl.Do(req) if err != nil { t.Fatal("Error POSTing file", err) return } defer resp.Body.Close() respBody, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal("Error reading server response", err) return } if resp.StatusCode < 200 || resp.StatusCode > 299 { t.Error("Server status is not success:", resp.Status, respBody) } fname := d + "/" + string(respBody) if !filet.Exists(t, fname) { t.Error("File does not exist", fname) } else { info, err := os.Stat(fname) if err != nil { t.Fatal("error reading info on uploaded file", err) return } if info.Size() != int64(size) { t.Errorf("File exists but has wrong size: expected %d, found %d", size, info.Size()) } } } func TestUploadTooBig(t *testing.T) { size := 900*1024 + 1 defer filet.CleanUp(t) d := filet.TmpDir(t, "") conf := ` shares: - name: foo dir: DIR sizelimit: 900K authorized: ["*"] description: foo ` conf = strings.Replace(conf, "DIR", d, 1) ts := getServer(conf) cl := ts.Client() bodyBuf := bytes.Buffer{} bodyWriter := multipart.NewWriter(&bodyBuf) fileWriter, err := bodyWriter.CreateFormFile("file", "foo.txt") if err != nil { t.Fatal("error creating form file", err) return } // write 3MB made of 'a' for i := 0; i < size; i++ { _, err = fileWriter.Write([]byte(`a`)) if err != nil { t.Fatal("error writing on form file", err) return } } contentType := bodyWriter.FormDataContentType() bodyWriter.Close() req := userRequest(ts.URL+"/api/upload/foo", "someone", t) req.Method = "POST" req.Header.Set("Content-Type", contentType) req.ContentLength = int64(bodyBuf.Len()) req.Body = ioutil.NopCloser(&bodyBuf) resp, err := cl.Do(req) if err != nil { t.Fatal("Error POSTing file", err) return } defer resp.Body.Close() _, err = ioutil.ReadAll(resp.Body) if err != nil { t.Fatal("Error reading server response", err) return } if resp.StatusCode < 299 { t.Error("Upload succeeded despite high size", resp.Status) } files, err := ioutil.ReadDir(d) if err != nil { t.Fatal("Error checking if upload dir is empty", err) } if len(files) != 0 { t.Error("Upload dir not empty; should be refused!", len(files)) t.Log(files[0]) } } func TestSurfUpload(t *testing.T) { if !*doHTTP { return } defer filet.CleanUp(t) d := filet.TmpDir(t, "") conf := ` shares: - name: foo dir: DIR authorized: ["*"] description: example sizelimit: 1M ` conf = strings.Replace(conf, "DIR", d, 1) ts := getServer(conf) bow := surf.NewBrowser() bow.AddRequestHeader("X-Forwarded-User", "someone") var err error if err = bow.Open(ts.URL); err != nil { t.Fatal("error opening home", err) return } if err = bow.Click("li a"); err != nil { t.Fatal("error clicking on share", err) return } form, err := bow.Form("form") if err != nil { t.Fatal("can't find form", err) return } form.SetFile("file", "foo.txt", strings.NewReader("example content")) if err = form.Submit(); err != nil { t.Error("error submitting form", err) } if bow.StatusCode() > 299 { t.Error("Invalid status code", bow.StatusCode(), bow.Body()) } fname := d + "/" + bow.Body() if !filet.Exists(t, fname) { t.Error("File does not exist", fname) } else if !filet.FileSays(t, fname, []byte("example content")) { t.Error("File exists but has wrong content") } }