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 }