func TestErrorStatusWithCustomMessage(t *testing.T) { cfg := config.New() u, err := url.Parse("https://lfs-server.com/objects/oid") if err != nil { t.Fatal(err) } statuses := map[int]string{ 400: "not panic", 401: "not panic", 403: "not panic", 404: "not panic", 405: "not panic", 406: "not panic", 429: "not panic", 500: "panic", 501: "not panic", 503: "panic", 504: "panic", 507: "not panic", 509: "not panic", } for status, panicMsg := range statuses { cliErr := &ClientError{ Message: fmt.Sprintf("custom error for %d", status), } by, err := json.Marshal(cliErr) if err != nil { t.Errorf("Error building json for status %d: %s", status, err) continue } res := &http.Response{ StatusCode: status, Header: make(http.Header), Body: ioutil.NopCloser(bytes.NewReader(by)), Request: &http.Request{URL: u}, } res.Header.Set("Content-Type", "application/vnd.git-lfs+json; charset=utf-8") err = handleResponse(cfg, res, nil) if err == nil { t.Errorf("No error from HTTP %d", status) continue } expected := fmt.Sprintf("custom error for %d", status) if actual := err.Error(); !strings.HasSuffix(actual, expected) { t.Errorf("Expected for HTTP %d:\n%s\nACTUAL:\n%s", status, expected, actual) continue } if errors.IsFatalError(err) == (panicMsg != "panic") { t.Errorf("Error for HTTP %d should %s", status, panicMsg) continue } } }
func errorWith(err error, fatalErrFn func(error, string, ...interface{}), errFn func(string, ...interface{})) { if Debugging || errors.IsFatalError(err) { fatalErrFn(err, "%s", err) return } errFn("%s", err) }
func TestUploadApiError(t *testing.T) { SetupTestCredentialsFunc() repo := test.NewRepo(t) repo.Pushd() defer func() { repo.Popd() repo.Cleanup() RestoreCredentialsFunc() }() mux := http.NewServeMux() server := httptest.NewServer(mux) tmp := tempdir(t) defer server.Close() defer os.RemoveAll(tmp) postCalled := false mux.HandleFunc("/media/objects", func(w http.ResponseWriter, r *http.Request) { postCalled = true w.WriteHeader(404) }) cfg := config.NewFrom(config.Values{ Git: map[string]string{ "lfs.url": server.URL + "/media", }, }) oidPath, _ := lfs.LocalMediaPath("988881adc9fc3655077dc2d4d757d480b5ea0e11") if err := ioutil.WriteFile(oidPath, []byte("test"), 0744); err != nil { t.Fatal(err) } oid := filepath.Base(oidPath) stat, _ := os.Stat(oidPath) _, _, err := api.BatchOrLegacySingle(cfg, &api.ObjectResource{Oid: oid, Size: stat.Size()}, "upload", []string{"basic"}) if err == nil { t.Fatal(err) } if errors.IsFatalError(err) { t.Fatal("should not panic") } if isDockerConnectionError(err) { return } expected := "LFS: " + fmt.Sprintf(httputil.GetDefaultError(404), server.URL+"/media/objects") if err.Error() != expected { t.Fatalf("Expected: %s\nGot: %s", expected, err.Error()) } if !postCalled { t.Errorf("POST not called") } }
func buildTestData() (oidsExist, oidsMissing []TestObject, err error) { const oidCount = 50 oidsExist = make([]TestObject, 0, oidCount) oidsMissing = make([]TestObject, 0, oidCount) // Build test data for existing files & upload // Use test repo for this to simplify the process of making sure data matches oid // We're not performing a real test at this point (although an upload fail will break it) var callback testDataCallback repo := test.NewRepo(&callback) repo.Pushd() defer repo.Cleanup() // just one commit commit := test.CommitInput{CommitterName: "A N Other", CommitterEmail: "*****@*****.**"} var totalSize int64 for i := 0; i < oidCount; i++ { filename := fmt.Sprintf("file%d.dat", i) sz := int64(rand.Intn(200)) + 50 commit.Files = append(commit.Files, &test.FileInput{Filename: filename, Size: sz}) totalSize += sz } outputs := repo.AddCommits([]*test.CommitInput{&commit}) // now upload uploadQueue := lfs.NewUploadQueue(len(oidsExist), totalSize, false) for _, f := range outputs[0].Files { oidsExist = append(oidsExist, TestObject{Oid: f.Oid, Size: f.Size}) u, err := lfs.NewUploadable(f.Oid, "Test file") if err != nil { return nil, nil, err } uploadQueue.Add(u) } uploadQueue.Wait() for _, err := range uploadQueue.Errors() { if errors.IsFatalError(err) { exit("Fatal error setting up test data: %s", err) } } // Generate SHAs for missing files, random but repeatable // No actual file content needed for these rand.Seed(int64(oidCount)) runningSha := sha256.New() for i := 0; i < oidCount; i++ { runningSha.Write([]byte{byte(rand.Intn(256))}) oid := hex.EncodeToString(runningSha.Sum(nil)) sz := int64(rand.Intn(200)) + 50 oidsMissing = append(oidsMissing, TestObject{Oid: oid, Size: sz}) } return oidsExist, oidsMissing, nil }
func TestDownloadAPIError(t *testing.T) { SetupTestCredentialsFunc() defer func() { RestoreCredentialsFunc() }() mux := http.NewServeMux() server := httptest.NewServer(mux) defer server.Close() tmp := tempdir(t) defer os.RemoveAll(tmp) mux.HandleFunc("/media/objects/oid", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(404) }) cfg := config.NewFrom(config.Values{ Git: map[string]string{ "lfs.batch": "false", "lfs.url": server.URL + "/media", }, }) _, _, err := api.BatchOrLegacySingle(cfg, &api.ObjectResource{Oid: "oid"}, "download", []string{"basic"}) if err == nil { t.Fatal("no error?") } if errors.IsFatalError(err) { t.Fatal("should not panic") } if isDockerConnectionError(err) { return } expected := fmt.Sprintf(httputil.GetDefaultError(404), server.URL+"/media/objects/oid") if err.Error() != expected { t.Fatalf("Expected: %s\nGot: %s", expected, err.Error()) } }
func TestUploadVerifyError(t *testing.T) { SetupTestCredentialsFunc() repo := test.NewRepo(t) repo.Pushd() defer func() { repo.Popd() repo.Cleanup() RestoreCredentialsFunc() }() mux := http.NewServeMux() server := httptest.NewServer(mux) tmp := tempdir(t) defer server.Close() defer os.RemoveAll(tmp) postCalled := false verifyCalled := false mux.HandleFunc("/media/objects", func(w http.ResponseWriter, r *http.Request) { t.Logf("Server: %s %s", r.Method, r.URL) if r.Method != "POST" { w.WriteHeader(405) return } if r.Header.Get("Accept") != api.MediaType { t.Errorf("Invalid Accept") } if r.Header.Get("Content-Type") != api.MediaType { t.Errorf("Invalid Content-Type") } buf := &bytes.Buffer{} tee := io.TeeReader(r.Body, buf) reqObj := &api.ObjectResource{} err := json.NewDecoder(tee).Decode(reqObj) t.Logf("request header: %v", r.Header) t.Logf("request body: %s", buf.String()) if err != nil { t.Fatal(err) } if reqObj.Oid != "988881adc9fc3655077dc2d4d757d480b5ea0e11" { t.Errorf("invalid oid from request: %s", reqObj.Oid) } if reqObj.Size != 4 { t.Errorf("invalid size from request: %d", reqObj.Size) } obj := &api.ObjectResource{ Oid: reqObj.Oid, Size: reqObj.Size, Actions: map[string]*api.LinkRelation{ "upload": &api.LinkRelation{ Href: server.URL + "/upload", Header: map[string]string{"A": "1"}, }, "verify": &api.LinkRelation{ Href: server.URL + "/verify", Header: map[string]string{"B": "2"}, }, }, } by, err := json.Marshal(obj) if err != nil { t.Fatal(err) } postCalled = true head := w.Header() head.Set("Content-Type", api.MediaType) head.Set("Content-Length", strconv.Itoa(len(by))) w.WriteHeader(202) w.Write(by) }) mux.HandleFunc("/verify", func(w http.ResponseWriter, r *http.Request) { verifyCalled = true w.WriteHeader(404) }) cfg := config.NewFrom(config.Values{ Git: map[string]string{ "lfs.url": server.URL + "/media", }, }) oidPath, _ := lfs.LocalMediaPath("988881adc9fc3655077dc2d4d757d480b5ea0e11") if err := ioutil.WriteFile(oidPath, []byte("test"), 0744); err != nil { t.Fatal(err) } oid := filepath.Base(oidPath) stat, _ := os.Stat(oidPath) o, _, err := api.BatchOrLegacySingle(cfg, &api.ObjectResource{Oid: oid, Size: stat.Size()}, "upload", []string{"basic"}) if err != nil { if isDockerConnectionError(err) { return } t.Fatal(err) } err = api.VerifyUpload(cfg, o) if err == nil { t.Fatal("verify should fail") } if errors.IsFatalError(err) { t.Fatal("should not panic") } expected := fmt.Sprintf(httputil.GetDefaultError(404), server.URL+"/verify") if err.Error() != expected { t.Fatalf("Expected: %s\nGot: %s", expected, err.Error()) } if !postCalled { t.Errorf("POST not called") } if !verifyCalled { t.Errorf("verify not called") } }
func TestErrorStatusWithDefaultMessage(t *testing.T) { cfg := config.New() rawurl := "https://lfs-server.com/objects/oid" u, err := url.Parse(rawurl) if err != nil { t.Fatal(err) } statuses := map[int][]string{ 400: {defaultErrors[400], "not panic"}, 401: {defaultErrors[401], "not panic"}, 403: {defaultErrors[401], "not panic"}, 404: {defaultErrors[404], "not panic"}, 405: {defaultErrors[400] + " from HTTP 405", "not panic"}, 406: {defaultErrors[400] + " from HTTP 406", "not panic"}, 429: {defaultErrors[429], "not panic"}, 500: {defaultErrors[500], "panic"}, 501: {defaultErrors[500] + " from HTTP 501", "not panic"}, 503: {defaultErrors[500] + " from HTTP 503", "panic"}, 504: {defaultErrors[500] + " from HTTP 504", "panic"}, 507: {defaultErrors[507], "not panic"}, 509: {defaultErrors[509], "not panic"}, } for status, results := range statuses { cliErr := &ClientError{ Message: fmt.Sprintf("custom error for %d", status), } by, err := json.Marshal(cliErr) if err != nil { t.Errorf("Error building json for status %d: %s", status, err) continue } res := &http.Response{ StatusCode: status, Header: make(http.Header), Body: ioutil.NopCloser(bytes.NewReader(by)), Request: &http.Request{URL: u}, } // purposely wrong content type so it falls back to default res.Header.Set("Content-Type", "application/vnd.git-lfs+json2") err = handleResponse(cfg, res, nil) if err == nil { t.Errorf("No error from HTTP %d", status) continue } expected := fmt.Sprintf(results[0], rawurl) if actual := err.Error(); !strings.HasSuffix(actual, expected) { t.Errorf("Expected for HTTP %d:\n%s\nACTUAL:\n%s", status, expected, actual) continue } if errors.IsFatalError(err) == (results[1] != "panic") { t.Errorf("Error for HTTP %d should %s", status, results[1]) continue } } }