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