code.gitea.io/gitea@v1.22.3/tests/integration/api_repo_lfs_test.go (about)

     1  // Copyright 2021 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package integration
     5  
     6  import (
     7  	"bytes"
     8  	"net/http"
     9  	"path"
    10  	"strconv"
    11  	"strings"
    12  	"testing"
    13  
    14  	auth_model "code.gitea.io/gitea/models/auth"
    15  	"code.gitea.io/gitea/models/db"
    16  	git_model "code.gitea.io/gitea/models/git"
    17  	repo_model "code.gitea.io/gitea/models/repo"
    18  	"code.gitea.io/gitea/models/unittest"
    19  	user_model "code.gitea.io/gitea/models/user"
    20  	"code.gitea.io/gitea/modules/json"
    21  	"code.gitea.io/gitea/modules/lfs"
    22  	"code.gitea.io/gitea/modules/setting"
    23  	"code.gitea.io/gitea/tests"
    24  
    25  	"github.com/stretchr/testify/assert"
    26  )
    27  
    28  func TestAPILFSNotStarted(t *testing.T) {
    29  	defer tests.PrepareTestEnv(t)()
    30  
    31  	setting.LFS.StartServer = false
    32  
    33  	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
    34  	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
    35  
    36  	req := NewRequestf(t, "POST", "/%s/%s.git/info/lfs/objects/batch", user.Name, repo.Name)
    37  	MakeRequest(t, req, http.StatusNotFound)
    38  	req = NewRequestf(t, "PUT", "/%s/%s.git/info/lfs/objects/oid/10", user.Name, repo.Name)
    39  	MakeRequest(t, req, http.StatusNotFound)
    40  	req = NewRequestf(t, "GET", "/%s/%s.git/info/lfs/objects/oid/name", user.Name, repo.Name)
    41  	MakeRequest(t, req, http.StatusNotFound)
    42  	req = NewRequestf(t, "GET", "/%s/%s.git/info/lfs/objects/oid", user.Name, repo.Name)
    43  	MakeRequest(t, req, http.StatusNotFound)
    44  	req = NewRequestf(t, "POST", "/%s/%s.git/info/lfs/verify", user.Name, repo.Name)
    45  	MakeRequest(t, req, http.StatusNotFound)
    46  }
    47  
    48  func TestAPILFSMediaType(t *testing.T) {
    49  	defer tests.PrepareTestEnv(t)()
    50  
    51  	setting.LFS.StartServer = true
    52  
    53  	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
    54  	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
    55  
    56  	req := NewRequestf(t, "POST", "/%s/%s.git/info/lfs/objects/batch", user.Name, repo.Name)
    57  	MakeRequest(t, req, http.StatusUnsupportedMediaType)
    58  	req = NewRequestf(t, "POST", "/%s/%s.git/info/lfs/verify", user.Name, repo.Name)
    59  	MakeRequest(t, req, http.StatusUnsupportedMediaType)
    60  }
    61  
    62  func createLFSTestRepository(t *testing.T, name string) *repo_model.Repository {
    63  	ctx := NewAPITestContext(t, "user2", "lfs-"+name+"-repo", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
    64  	t.Run("CreateRepo", doAPICreateRepository(ctx, false))
    65  
    66  	repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "lfs-"+name+"-repo")
    67  	assert.NoError(t, err)
    68  
    69  	return repo
    70  }
    71  
    72  func TestAPILFSBatch(t *testing.T) {
    73  	defer tests.PrepareTestEnv(t)()
    74  
    75  	setting.LFS.StartServer = true
    76  
    77  	repo := createLFSTestRepository(t, "batch")
    78  
    79  	content := []byte("dummy1")
    80  	oid := storeObjectInRepo(t, repo.ID, &content)
    81  	defer git_model.RemoveLFSMetaObjectByOid(db.DefaultContext, repo.ID, oid)
    82  
    83  	session := loginUser(t, "user2")
    84  
    85  	newRequest := func(t testing.TB, br *lfs.BatchRequest) *RequestWrapper {
    86  		return NewRequestWithJSON(t, "POST", "/user2/lfs-batch-repo.git/info/lfs/objects/batch", br).
    87  			SetHeader("Accept", lfs.AcceptHeader).
    88  			SetHeader("Content-Type", lfs.MediaType)
    89  	}
    90  	decodeResponse := func(t *testing.T, b *bytes.Buffer) *lfs.BatchResponse {
    91  		var br lfs.BatchResponse
    92  
    93  		assert.NoError(t, json.Unmarshal(b.Bytes(), &br))
    94  		return &br
    95  	}
    96  
    97  	t.Run("InvalidJsonRequest", func(t *testing.T) {
    98  		defer tests.PrintCurrentTest(t)()
    99  
   100  		req := newRequest(t, nil)
   101  
   102  		session.MakeRequest(t, req, http.StatusBadRequest)
   103  	})
   104  
   105  	t.Run("InvalidOperation", func(t *testing.T) {
   106  		defer tests.PrintCurrentTest(t)()
   107  
   108  		req := newRequest(t, &lfs.BatchRequest{
   109  			Operation: "dummy",
   110  		})
   111  
   112  		session.MakeRequest(t, req, http.StatusBadRequest)
   113  	})
   114  
   115  	t.Run("InvalidPointer", func(t *testing.T) {
   116  		defer tests.PrintCurrentTest(t)()
   117  
   118  		req := newRequest(t, &lfs.BatchRequest{
   119  			Operation: "download",
   120  			Objects: []lfs.Pointer{
   121  				{Oid: "dummy"},
   122  				{Oid: oid, Size: -1},
   123  			},
   124  		})
   125  
   126  		resp := session.MakeRequest(t, req, http.StatusOK)
   127  		br := decodeResponse(t, resp.Body)
   128  		assert.Len(t, br.Objects, 2)
   129  		assert.Equal(t, "dummy", br.Objects[0].Oid)
   130  		assert.Equal(t, oid, br.Objects[1].Oid)
   131  		assert.Equal(t, int64(0), br.Objects[0].Size)
   132  		assert.Equal(t, int64(-1), br.Objects[1].Size)
   133  		assert.NotNil(t, br.Objects[0].Error)
   134  		assert.NotNil(t, br.Objects[1].Error)
   135  		assert.Equal(t, http.StatusUnprocessableEntity, br.Objects[0].Error.Code)
   136  		assert.Equal(t, http.StatusUnprocessableEntity, br.Objects[1].Error.Code)
   137  		assert.Equal(t, "Oid or size are invalid", br.Objects[0].Error.Message)
   138  		assert.Equal(t, "Oid or size are invalid", br.Objects[1].Error.Message)
   139  	})
   140  
   141  	t.Run("PointerSizeMismatch", func(t *testing.T) {
   142  		defer tests.PrintCurrentTest(t)()
   143  
   144  		req := newRequest(t, &lfs.BatchRequest{
   145  			Operation: "download",
   146  			Objects: []lfs.Pointer{
   147  				{Oid: oid, Size: 1},
   148  			},
   149  		})
   150  
   151  		resp := session.MakeRequest(t, req, http.StatusOK)
   152  		br := decodeResponse(t, resp.Body)
   153  		assert.Len(t, br.Objects, 1)
   154  		assert.NotNil(t, br.Objects[0].Error)
   155  		assert.Equal(t, http.StatusUnprocessableEntity, br.Objects[0].Error.Code)
   156  		assert.Equal(t, "Object "+oid+" is not 1 bytes", br.Objects[0].Error.Message)
   157  	})
   158  
   159  	t.Run("Download", func(t *testing.T) {
   160  		defer tests.PrintCurrentTest(t)()
   161  
   162  		t.Run("PointerNotInStore", func(t *testing.T) {
   163  			defer tests.PrintCurrentTest(t)()
   164  
   165  			req := newRequest(t, &lfs.BatchRequest{
   166  				Operation: "download",
   167  				Objects: []lfs.Pointer{
   168  					{Oid: "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab042", Size: 6},
   169  				},
   170  			})
   171  
   172  			resp := session.MakeRequest(t, req, http.StatusOK)
   173  			br := decodeResponse(t, resp.Body)
   174  			assert.Len(t, br.Objects, 1)
   175  			assert.NotNil(t, br.Objects[0].Error)
   176  			assert.Equal(t, http.StatusNotFound, br.Objects[0].Error.Code)
   177  		})
   178  
   179  		t.Run("MetaNotFound", func(t *testing.T) {
   180  			defer tests.PrintCurrentTest(t)()
   181  
   182  			p := lfs.Pointer{Oid: "05eeb4eb5be71f2dd291ca39157d6d9effd7d1ea19cbdc8a99411fe2a8f26a00", Size: 6}
   183  
   184  			contentStore := lfs.NewContentStore()
   185  			exist, err := contentStore.Exists(p)
   186  			assert.NoError(t, err)
   187  			assert.False(t, exist)
   188  			err = contentStore.Put(p, bytes.NewReader([]byte("dummy0")))
   189  			assert.NoError(t, err)
   190  
   191  			req := newRequest(t, &lfs.BatchRequest{
   192  				Operation: "download",
   193  				Objects:   []lfs.Pointer{p},
   194  			})
   195  
   196  			resp := session.MakeRequest(t, req, http.StatusOK)
   197  			br := decodeResponse(t, resp.Body)
   198  			assert.Len(t, br.Objects, 1)
   199  			assert.NotNil(t, br.Objects[0].Error)
   200  			assert.Equal(t, http.StatusNotFound, br.Objects[0].Error.Code)
   201  		})
   202  
   203  		t.Run("Success", func(t *testing.T) {
   204  			defer tests.PrintCurrentTest(t)()
   205  
   206  			req := newRequest(t, &lfs.BatchRequest{
   207  				Operation: "download",
   208  				Objects: []lfs.Pointer{
   209  					{Oid: oid, Size: 6},
   210  				},
   211  			})
   212  
   213  			resp := session.MakeRequest(t, req, http.StatusOK)
   214  			br := decodeResponse(t, resp.Body)
   215  			assert.Len(t, br.Objects, 1)
   216  			assert.Nil(t, br.Objects[0].Error)
   217  			assert.Contains(t, br.Objects[0].Actions, "download")
   218  			l := br.Objects[0].Actions["download"]
   219  			assert.NotNil(t, l)
   220  			assert.NotEmpty(t, l.Href)
   221  		})
   222  	})
   223  
   224  	t.Run("Upload", func(t *testing.T) {
   225  		defer tests.PrintCurrentTest(t)()
   226  
   227  		t.Run("FileTooBig", func(t *testing.T) {
   228  			defer tests.PrintCurrentTest(t)()
   229  
   230  			oldMaxFileSize := setting.LFS.MaxFileSize
   231  			setting.LFS.MaxFileSize = 2
   232  
   233  			req := newRequest(t, &lfs.BatchRequest{
   234  				Operation: "upload",
   235  				Objects: []lfs.Pointer{
   236  					{Oid: "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab042", Size: 6},
   237  				},
   238  			})
   239  
   240  			resp := session.MakeRequest(t, req, http.StatusOK)
   241  			br := decodeResponse(t, resp.Body)
   242  			assert.Len(t, br.Objects, 1)
   243  			assert.NotNil(t, br.Objects[0].Error)
   244  			assert.Equal(t, http.StatusUnprocessableEntity, br.Objects[0].Error.Code)
   245  			assert.Equal(t, "Size must be less than or equal to 2", br.Objects[0].Error.Message)
   246  
   247  			setting.LFS.MaxFileSize = oldMaxFileSize
   248  		})
   249  
   250  		t.Run("AddMeta", func(t *testing.T) {
   251  			defer tests.PrintCurrentTest(t)()
   252  
   253  			p := lfs.Pointer{Oid: "05eeb4eb5be71f2dd291ca39157d6d9effd7d1ea19cbdc8a99411fe2a8f26a00", Size: 6}
   254  
   255  			contentStore := lfs.NewContentStore()
   256  			exist, err := contentStore.Exists(p)
   257  			assert.NoError(t, err)
   258  			assert.True(t, exist)
   259  
   260  			repo2 := createLFSTestRepository(t, "batch2")
   261  			content := []byte("dummy0")
   262  			storeObjectInRepo(t, repo2.ID, &content)
   263  
   264  			meta, err := git_model.GetLFSMetaObjectByOid(db.DefaultContext, repo.ID, p.Oid)
   265  			assert.Nil(t, meta)
   266  			assert.Equal(t, git_model.ErrLFSObjectNotExist, err)
   267  
   268  			req := newRequest(t, &lfs.BatchRequest{
   269  				Operation: "upload",
   270  				Objects:   []lfs.Pointer{p},
   271  			})
   272  
   273  			resp := session.MakeRequest(t, req, http.StatusOK)
   274  			br := decodeResponse(t, resp.Body)
   275  			assert.Len(t, br.Objects, 1)
   276  			assert.Nil(t, br.Objects[0].Error)
   277  			assert.Empty(t, br.Objects[0].Actions)
   278  
   279  			meta, err = git_model.GetLFSMetaObjectByOid(db.DefaultContext, repo.ID, p.Oid)
   280  			assert.NoError(t, err)
   281  			assert.NotNil(t, meta)
   282  
   283  			// Cleanup
   284  			err = contentStore.Delete(p.RelativePath())
   285  			assert.NoError(t, err)
   286  		})
   287  
   288  		t.Run("AlreadyExists", func(t *testing.T) {
   289  			defer tests.PrintCurrentTest(t)()
   290  
   291  			req := newRequest(t, &lfs.BatchRequest{
   292  				Operation: "upload",
   293  				Objects: []lfs.Pointer{
   294  					{Oid: oid, Size: 6},
   295  				},
   296  			})
   297  
   298  			resp := session.MakeRequest(t, req, http.StatusOK)
   299  			br := decodeResponse(t, resp.Body)
   300  			assert.Len(t, br.Objects, 1)
   301  			assert.Nil(t, br.Objects[0].Error)
   302  			assert.Empty(t, br.Objects[0].Actions)
   303  		})
   304  
   305  		t.Run("NewFile", func(t *testing.T) {
   306  			defer tests.PrintCurrentTest(t)()
   307  
   308  			req := newRequest(t, &lfs.BatchRequest{
   309  				Operation: "upload",
   310  				Objects: []lfs.Pointer{
   311  					{Oid: "d6f175817f886ec6fbbc1515326465fa96c3bfd54a4ea06cfd6dbbd8340e0153", Size: 1},
   312  				},
   313  			})
   314  
   315  			resp := session.MakeRequest(t, req, http.StatusOK)
   316  			br := decodeResponse(t, resp.Body)
   317  			assert.Len(t, br.Objects, 1)
   318  			assert.Nil(t, br.Objects[0].Error)
   319  			assert.Contains(t, br.Objects[0].Actions, "upload")
   320  			ul := br.Objects[0].Actions["upload"]
   321  			assert.NotNil(t, ul)
   322  			assert.NotEmpty(t, ul.Href)
   323  			assert.Contains(t, br.Objects[0].Actions, "verify")
   324  			vl := br.Objects[0].Actions["verify"]
   325  			assert.NotNil(t, vl)
   326  			assert.NotEmpty(t, vl.Href)
   327  		})
   328  	})
   329  }
   330  
   331  func TestAPILFSUpload(t *testing.T) {
   332  	defer tests.PrepareTestEnv(t)()
   333  
   334  	setting.LFS.StartServer = true
   335  
   336  	repo := createLFSTestRepository(t, "upload")
   337  
   338  	content := []byte("dummy3")
   339  	oid := storeObjectInRepo(t, repo.ID, &content)
   340  	defer git_model.RemoveLFSMetaObjectByOid(db.DefaultContext, repo.ID, oid)
   341  
   342  	session := loginUser(t, "user2")
   343  
   344  	newRequest := func(t testing.TB, p lfs.Pointer, content string) *RequestWrapper {
   345  		return NewRequestWithBody(t, "PUT", path.Join("/user2/lfs-upload-repo.git/info/lfs/objects/", p.Oid, strconv.FormatInt(p.Size, 10)), strings.NewReader(content))
   346  	}
   347  
   348  	t.Run("InvalidPointer", func(t *testing.T) {
   349  		defer tests.PrintCurrentTest(t)()
   350  
   351  		req := newRequest(t, lfs.Pointer{Oid: "dummy"}, "")
   352  
   353  		session.MakeRequest(t, req, http.StatusUnprocessableEntity)
   354  	})
   355  
   356  	t.Run("AlreadyExistsInStore", func(t *testing.T) {
   357  		defer tests.PrintCurrentTest(t)()
   358  
   359  		p := lfs.Pointer{Oid: "83de2e488b89a0aa1c97496b888120a28b0c1e15463a4adb8405578c540f36d4", Size: 6}
   360  
   361  		contentStore := lfs.NewContentStore()
   362  		exist, err := contentStore.Exists(p)
   363  		assert.NoError(t, err)
   364  		assert.False(t, exist)
   365  		err = contentStore.Put(p, bytes.NewReader([]byte("dummy5")))
   366  		assert.NoError(t, err)
   367  
   368  		meta, err := git_model.GetLFSMetaObjectByOid(db.DefaultContext, repo.ID, p.Oid)
   369  		assert.Nil(t, meta)
   370  		assert.Equal(t, git_model.ErrLFSObjectNotExist, err)
   371  
   372  		t.Run("InvalidAccess", func(t *testing.T) {
   373  			req := newRequest(t, p, "invalid")
   374  			session.MakeRequest(t, req, http.StatusUnprocessableEntity)
   375  		})
   376  
   377  		t.Run("ValidAccess", func(t *testing.T) {
   378  			req := newRequest(t, p, "dummy5")
   379  
   380  			session.MakeRequest(t, req, http.StatusOK)
   381  			meta, err = git_model.GetLFSMetaObjectByOid(db.DefaultContext, repo.ID, p.Oid)
   382  			assert.NoError(t, err)
   383  			assert.NotNil(t, meta)
   384  		})
   385  
   386  		// Cleanup
   387  		err = contentStore.Delete(p.RelativePath())
   388  		assert.NoError(t, err)
   389  	})
   390  
   391  	t.Run("MetaAlreadyExists", func(t *testing.T) {
   392  		defer tests.PrintCurrentTest(t)()
   393  
   394  		req := newRequest(t, lfs.Pointer{Oid: oid, Size: 6}, "")
   395  
   396  		session.MakeRequest(t, req, http.StatusOK)
   397  	})
   398  
   399  	t.Run("HashMismatch", func(t *testing.T) {
   400  		defer tests.PrintCurrentTest(t)()
   401  
   402  		req := newRequest(t, lfs.Pointer{Oid: "2581dd7bbc1fe44726de4b7dd806a087a978b9c5aec0a60481259e34be09b06a", Size: 1}, "a")
   403  
   404  		session.MakeRequest(t, req, http.StatusUnprocessableEntity)
   405  	})
   406  
   407  	t.Run("SizeMismatch", func(t *testing.T) {
   408  		defer tests.PrintCurrentTest(t)()
   409  
   410  		req := newRequest(t, lfs.Pointer{Oid: "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", Size: 2}, "a")
   411  
   412  		session.MakeRequest(t, req, http.StatusUnprocessableEntity)
   413  	})
   414  
   415  	t.Run("Success", func(t *testing.T) {
   416  		defer tests.PrintCurrentTest(t)()
   417  
   418  		p := lfs.Pointer{Oid: "6ccce4863b70f258d691f59609d31b4502e1ba5199942d3bc5d35d17a4ce771d", Size: 5}
   419  
   420  		req := newRequest(t, p, "gitea")
   421  
   422  		session.MakeRequest(t, req, http.StatusOK)
   423  
   424  		contentStore := lfs.NewContentStore()
   425  		exist, err := contentStore.Exists(p)
   426  		assert.NoError(t, err)
   427  		assert.True(t, exist)
   428  
   429  		meta, err := git_model.GetLFSMetaObjectByOid(db.DefaultContext, repo.ID, p.Oid)
   430  		assert.NoError(t, err)
   431  		assert.NotNil(t, meta)
   432  	})
   433  }
   434  
   435  func TestAPILFSVerify(t *testing.T) {
   436  	defer tests.PrepareTestEnv(t)()
   437  
   438  	setting.LFS.StartServer = true
   439  
   440  	repo := createLFSTestRepository(t, "verify")
   441  
   442  	content := []byte("dummy3")
   443  	oid := storeObjectInRepo(t, repo.ID, &content)
   444  	defer git_model.RemoveLFSMetaObjectByOid(db.DefaultContext, repo.ID, oid)
   445  
   446  	session := loginUser(t, "user2")
   447  
   448  	newRequest := func(t testing.TB, p *lfs.Pointer) *RequestWrapper {
   449  		return NewRequestWithJSON(t, "POST", "/user2/lfs-verify-repo.git/info/lfs/verify", p).
   450  			SetHeader("Accept", lfs.AcceptHeader).
   451  			SetHeader("Content-Type", lfs.MediaType)
   452  	}
   453  
   454  	t.Run("InvalidJsonRequest", func(t *testing.T) {
   455  		defer tests.PrintCurrentTest(t)()
   456  
   457  		req := newRequest(t, nil)
   458  
   459  		session.MakeRequest(t, req, http.StatusUnprocessableEntity)
   460  	})
   461  
   462  	t.Run("InvalidPointer", func(t *testing.T) {
   463  		defer tests.PrintCurrentTest(t)()
   464  
   465  		req := newRequest(t, &lfs.Pointer{})
   466  
   467  		session.MakeRequest(t, req, http.StatusUnprocessableEntity)
   468  	})
   469  
   470  	t.Run("PointerNotExisting", func(t *testing.T) {
   471  		defer tests.PrintCurrentTest(t)()
   472  
   473  		req := newRequest(t, &lfs.Pointer{Oid: "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab042", Size: 6})
   474  
   475  		session.MakeRequest(t, req, http.StatusNotFound)
   476  	})
   477  
   478  	t.Run("Success", func(t *testing.T) {
   479  		defer tests.PrintCurrentTest(t)()
   480  
   481  		req := newRequest(t, &lfs.Pointer{Oid: oid, Size: 6})
   482  
   483  		session.MakeRequest(t, req, http.StatusOK)
   484  	})
   485  }