You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

api_repo_lfs_test.go 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package integrations
  5. import (
  6. "bytes"
  7. "net/http"
  8. "path"
  9. "strconv"
  10. "strings"
  11. "testing"
  12. git_model "code.gitea.io/gitea/models/git"
  13. repo_model "code.gitea.io/gitea/models/repo"
  14. "code.gitea.io/gitea/models/unittest"
  15. user_model "code.gitea.io/gitea/models/user"
  16. "code.gitea.io/gitea/modules/json"
  17. "code.gitea.io/gitea/modules/lfs"
  18. "code.gitea.io/gitea/modules/setting"
  19. "github.com/stretchr/testify/assert"
  20. )
  21. func TestAPILFSNotStarted(t *testing.T) {
  22. defer prepareTestEnv(t)()
  23. setting.LFS.StartServer = false
  24. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
  25. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
  26. req := NewRequestf(t, "POST", "/%s/%s.git/info/lfs/objects/batch", user.Name, repo.Name)
  27. MakeRequest(t, req, http.StatusNotFound)
  28. req = NewRequestf(t, "PUT", "/%s/%s.git/info/lfs/objects/oid/10", user.Name, repo.Name)
  29. MakeRequest(t, req, http.StatusNotFound)
  30. req = NewRequestf(t, "GET", "/%s/%s.git/info/lfs/objects/oid/name", user.Name, repo.Name)
  31. MakeRequest(t, req, http.StatusNotFound)
  32. req = NewRequestf(t, "GET", "/%s/%s.git/info/lfs/objects/oid", user.Name, repo.Name)
  33. MakeRequest(t, req, http.StatusNotFound)
  34. req = NewRequestf(t, "POST", "/%s/%s.git/info/lfs/verify", user.Name, repo.Name)
  35. MakeRequest(t, req, http.StatusNotFound)
  36. }
  37. func TestAPILFSMediaType(t *testing.T) {
  38. defer prepareTestEnv(t)()
  39. setting.LFS.StartServer = true
  40. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
  41. repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
  42. req := NewRequestf(t, "POST", "/%s/%s.git/info/lfs/objects/batch", user.Name, repo.Name)
  43. MakeRequest(t, req, http.StatusUnsupportedMediaType)
  44. req = NewRequestf(t, "POST", "/%s/%s.git/info/lfs/verify", user.Name, repo.Name)
  45. MakeRequest(t, req, http.StatusUnsupportedMediaType)
  46. }
  47. func createLFSTestRepository(t *testing.T, name string) *repo_model.Repository {
  48. ctx := NewAPITestContext(t, "user2", "lfs-"+name+"-repo")
  49. t.Run("CreateRepo", doAPICreateRepository(ctx, false))
  50. repo, err := repo_model.GetRepositoryByOwnerAndName("user2", "lfs-"+name+"-repo")
  51. assert.NoError(t, err)
  52. return repo
  53. }
  54. func TestAPILFSBatch(t *testing.T) {
  55. defer prepareTestEnv(t)()
  56. setting.LFS.StartServer = true
  57. repo := createLFSTestRepository(t, "batch")
  58. content := []byte("dummy1")
  59. oid := storeObjectInRepo(t, repo.ID, &content)
  60. defer git_model.RemoveLFSMetaObjectByOid(repo.ID, oid)
  61. session := loginUser(t, "user2")
  62. newRequest := func(t testing.TB, br *lfs.BatchRequest) *http.Request {
  63. req := NewRequestWithJSON(t, "POST", "/user2/lfs-batch-repo.git/info/lfs/objects/batch", br)
  64. req.Header.Set("Accept", lfs.MediaType)
  65. req.Header.Set("Content-Type", lfs.MediaType)
  66. return req
  67. }
  68. decodeResponse := func(t *testing.T, b *bytes.Buffer) *lfs.BatchResponse {
  69. var br lfs.BatchResponse
  70. assert.NoError(t, json.Unmarshal(b.Bytes(), &br))
  71. return &br
  72. }
  73. t.Run("InvalidJsonRequest", func(t *testing.T) {
  74. defer PrintCurrentTest(t)()
  75. req := newRequest(t, nil)
  76. session.MakeRequest(t, req, http.StatusBadRequest)
  77. })
  78. t.Run("InvalidOperation", func(t *testing.T) {
  79. defer PrintCurrentTest(t)()
  80. req := newRequest(t, &lfs.BatchRequest{
  81. Operation: "dummy",
  82. })
  83. session.MakeRequest(t, req, http.StatusBadRequest)
  84. })
  85. t.Run("InvalidPointer", func(t *testing.T) {
  86. defer PrintCurrentTest(t)()
  87. req := newRequest(t, &lfs.BatchRequest{
  88. Operation: "download",
  89. Objects: []lfs.Pointer{
  90. {Oid: "dummy"},
  91. {Oid: oid, Size: -1},
  92. },
  93. })
  94. resp := session.MakeRequest(t, req, http.StatusOK)
  95. br := decodeResponse(t, resp.Body)
  96. assert.Len(t, br.Objects, 2)
  97. assert.Equal(t, "dummy", br.Objects[0].Oid)
  98. assert.Equal(t, oid, br.Objects[1].Oid)
  99. assert.Equal(t, int64(0), br.Objects[0].Size)
  100. assert.Equal(t, int64(-1), br.Objects[1].Size)
  101. assert.NotNil(t, br.Objects[0].Error)
  102. assert.NotNil(t, br.Objects[1].Error)
  103. assert.Equal(t, http.StatusUnprocessableEntity, br.Objects[0].Error.Code)
  104. assert.Equal(t, http.StatusUnprocessableEntity, br.Objects[1].Error.Code)
  105. assert.Equal(t, "Oid or size are invalid", br.Objects[0].Error.Message)
  106. assert.Equal(t, "Oid or size are invalid", br.Objects[1].Error.Message)
  107. })
  108. t.Run("PointerSizeMismatch", func(t *testing.T) {
  109. defer PrintCurrentTest(t)()
  110. req := newRequest(t, &lfs.BatchRequest{
  111. Operation: "download",
  112. Objects: []lfs.Pointer{
  113. {Oid: oid, Size: 1},
  114. },
  115. })
  116. resp := session.MakeRequest(t, req, http.StatusOK)
  117. br := decodeResponse(t, resp.Body)
  118. assert.Len(t, br.Objects, 1)
  119. assert.NotNil(t, br.Objects[0].Error)
  120. assert.Equal(t, http.StatusUnprocessableEntity, br.Objects[0].Error.Code)
  121. assert.Equal(t, "Object "+oid+" is not 1 bytes", br.Objects[0].Error.Message)
  122. })
  123. t.Run("Download", func(t *testing.T) {
  124. defer PrintCurrentTest(t)()
  125. t.Run("PointerNotInStore", func(t *testing.T) {
  126. defer PrintCurrentTest(t)()
  127. req := newRequest(t, &lfs.BatchRequest{
  128. Operation: "download",
  129. Objects: []lfs.Pointer{
  130. {Oid: "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab042", Size: 6},
  131. },
  132. })
  133. resp := session.MakeRequest(t, req, http.StatusOK)
  134. br := decodeResponse(t, resp.Body)
  135. assert.Len(t, br.Objects, 1)
  136. assert.NotNil(t, br.Objects[0].Error)
  137. assert.Equal(t, http.StatusNotFound, br.Objects[0].Error.Code)
  138. })
  139. t.Run("MetaNotFound", func(t *testing.T) {
  140. defer PrintCurrentTest(t)()
  141. p := lfs.Pointer{Oid: "05eeb4eb5be71f2dd291ca39157d6d9effd7d1ea19cbdc8a99411fe2a8f26a00", Size: 6}
  142. contentStore := lfs.NewContentStore()
  143. exist, err := contentStore.Exists(p)
  144. assert.NoError(t, err)
  145. assert.False(t, exist)
  146. err = contentStore.Put(p, bytes.NewReader([]byte("dummy0")))
  147. assert.NoError(t, err)
  148. req := newRequest(t, &lfs.BatchRequest{
  149. Operation: "download",
  150. Objects: []lfs.Pointer{p},
  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.StatusNotFound, br.Objects[0].Error.Code)
  157. })
  158. t.Run("Success", func(t *testing.T) {
  159. defer PrintCurrentTest(t)()
  160. req := newRequest(t, &lfs.BatchRequest{
  161. Operation: "download",
  162. Objects: []lfs.Pointer{
  163. {Oid: oid, Size: 6},
  164. },
  165. })
  166. resp := session.MakeRequest(t, req, http.StatusOK)
  167. br := decodeResponse(t, resp.Body)
  168. assert.Len(t, br.Objects, 1)
  169. assert.Nil(t, br.Objects[0].Error)
  170. assert.Contains(t, br.Objects[0].Actions, "download")
  171. l := br.Objects[0].Actions["download"]
  172. assert.NotNil(t, l)
  173. assert.NotEmpty(t, l.Href)
  174. })
  175. })
  176. t.Run("Upload", func(t *testing.T) {
  177. defer PrintCurrentTest(t)()
  178. t.Run("FileTooBig", func(t *testing.T) {
  179. defer PrintCurrentTest(t)()
  180. oldMaxFileSize := setting.LFS.MaxFileSize
  181. setting.LFS.MaxFileSize = 2
  182. req := newRequest(t, &lfs.BatchRequest{
  183. Operation: "upload",
  184. Objects: []lfs.Pointer{
  185. {Oid: "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab042", Size: 6},
  186. },
  187. })
  188. resp := session.MakeRequest(t, req, http.StatusOK)
  189. br := decodeResponse(t, resp.Body)
  190. assert.Len(t, br.Objects, 1)
  191. assert.NotNil(t, br.Objects[0].Error)
  192. assert.Equal(t, http.StatusUnprocessableEntity, br.Objects[0].Error.Code)
  193. assert.Equal(t, "Size must be less than or equal to 2", br.Objects[0].Error.Message)
  194. setting.LFS.MaxFileSize = oldMaxFileSize
  195. })
  196. t.Run("AddMeta", func(t *testing.T) {
  197. defer PrintCurrentTest(t)()
  198. p := lfs.Pointer{Oid: "05eeb4eb5be71f2dd291ca39157d6d9effd7d1ea19cbdc8a99411fe2a8f26a00", Size: 6}
  199. contentStore := lfs.NewContentStore()
  200. exist, err := contentStore.Exists(p)
  201. assert.NoError(t, err)
  202. assert.True(t, exist)
  203. repo2 := createLFSTestRepository(t, "batch2")
  204. content := []byte("dummy0")
  205. storeObjectInRepo(t, repo2.ID, &content)
  206. meta, err := git_model.GetLFSMetaObjectByOid(repo.ID, p.Oid)
  207. assert.Nil(t, meta)
  208. assert.Equal(t, git_model.ErrLFSObjectNotExist, err)
  209. req := newRequest(t, &lfs.BatchRequest{
  210. Operation: "upload",
  211. Objects: []lfs.Pointer{p},
  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.Empty(t, br.Objects[0].Actions)
  218. meta, err = git_model.GetLFSMetaObjectByOid(repo.ID, p.Oid)
  219. assert.NoError(t, err)
  220. assert.NotNil(t, meta)
  221. // Cleanup
  222. err = contentStore.Delete(p.RelativePath())
  223. assert.NoError(t, err)
  224. })
  225. t.Run("AlreadyExists", func(t *testing.T) {
  226. defer PrintCurrentTest(t)()
  227. req := newRequest(t, &lfs.BatchRequest{
  228. Operation: "upload",
  229. Objects: []lfs.Pointer{
  230. {Oid: oid, Size: 6},
  231. },
  232. })
  233. resp := session.MakeRequest(t, req, http.StatusOK)
  234. br := decodeResponse(t, resp.Body)
  235. assert.Len(t, br.Objects, 1)
  236. assert.Nil(t, br.Objects[0].Error)
  237. assert.Empty(t, br.Objects[0].Actions)
  238. })
  239. t.Run("NewFile", func(t *testing.T) {
  240. defer PrintCurrentTest(t)()
  241. req := newRequest(t, &lfs.BatchRequest{
  242. Operation: "upload",
  243. Objects: []lfs.Pointer{
  244. {Oid: "d6f175817f886ec6fbbc1515326465fa96c3bfd54a4ea06cfd6dbbd8340e0153", Size: 1},
  245. },
  246. })
  247. resp := session.MakeRequest(t, req, http.StatusOK)
  248. br := decodeResponse(t, resp.Body)
  249. assert.Len(t, br.Objects, 1)
  250. assert.Nil(t, br.Objects[0].Error)
  251. assert.Contains(t, br.Objects[0].Actions, "upload")
  252. ul := br.Objects[0].Actions["upload"]
  253. assert.NotNil(t, ul)
  254. assert.NotEmpty(t, ul.Href)
  255. assert.Contains(t, br.Objects[0].Actions, "verify")
  256. vl := br.Objects[0].Actions["verify"]
  257. assert.NotNil(t, vl)
  258. assert.NotEmpty(t, vl.Href)
  259. })
  260. })
  261. }
  262. func TestAPILFSUpload(t *testing.T) {
  263. defer prepareTestEnv(t)()
  264. setting.LFS.StartServer = true
  265. repo := createLFSTestRepository(t, "upload")
  266. content := []byte("dummy3")
  267. oid := storeObjectInRepo(t, repo.ID, &content)
  268. defer git_model.RemoveLFSMetaObjectByOid(repo.ID, oid)
  269. session := loginUser(t, "user2")
  270. newRequest := func(t testing.TB, p lfs.Pointer, content string) *http.Request {
  271. req := NewRequestWithBody(t, "PUT", path.Join("/user2/lfs-upload-repo.git/info/lfs/objects/", p.Oid, strconv.FormatInt(p.Size, 10)), strings.NewReader(content))
  272. return req
  273. }
  274. t.Run("InvalidPointer", func(t *testing.T) {
  275. defer PrintCurrentTest(t)()
  276. req := newRequest(t, lfs.Pointer{Oid: "dummy"}, "")
  277. session.MakeRequest(t, req, http.StatusUnprocessableEntity)
  278. })
  279. t.Run("AlreadyExistsInStore", func(t *testing.T) {
  280. defer PrintCurrentTest(t)()
  281. p := lfs.Pointer{Oid: "83de2e488b89a0aa1c97496b888120a28b0c1e15463a4adb8405578c540f36d4", Size: 6}
  282. contentStore := lfs.NewContentStore()
  283. exist, err := contentStore.Exists(p)
  284. assert.NoError(t, err)
  285. assert.False(t, exist)
  286. err = contentStore.Put(p, bytes.NewReader([]byte("dummy5")))
  287. assert.NoError(t, err)
  288. meta, err := git_model.GetLFSMetaObjectByOid(repo.ID, p.Oid)
  289. assert.Nil(t, meta)
  290. assert.Equal(t, git_model.ErrLFSObjectNotExist, err)
  291. t.Run("InvalidAccess", func(t *testing.T) {
  292. req := newRequest(t, p, "invalid")
  293. session.MakeRequest(t, req, http.StatusUnprocessableEntity)
  294. })
  295. t.Run("ValidAccess", func(t *testing.T) {
  296. req := newRequest(t, p, "dummy5")
  297. session.MakeRequest(t, req, http.StatusOK)
  298. meta, err = git_model.GetLFSMetaObjectByOid(repo.ID, p.Oid)
  299. assert.NoError(t, err)
  300. assert.NotNil(t, meta)
  301. })
  302. // Cleanup
  303. err = contentStore.Delete(p.RelativePath())
  304. assert.NoError(t, err)
  305. })
  306. t.Run("MetaAlreadyExists", func(t *testing.T) {
  307. defer PrintCurrentTest(t)()
  308. req := newRequest(t, lfs.Pointer{Oid: oid, Size: 6}, "")
  309. session.MakeRequest(t, req, http.StatusOK)
  310. })
  311. t.Run("HashMismatch", func(t *testing.T) {
  312. defer PrintCurrentTest(t)()
  313. req := newRequest(t, lfs.Pointer{Oid: "2581dd7bbc1fe44726de4b7dd806a087a978b9c5aec0a60481259e34be09b06a", Size: 1}, "a")
  314. session.MakeRequest(t, req, http.StatusUnprocessableEntity)
  315. })
  316. t.Run("SizeMismatch", func(t *testing.T) {
  317. defer PrintCurrentTest(t)()
  318. req := newRequest(t, lfs.Pointer{Oid: "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", Size: 2}, "a")
  319. session.MakeRequest(t, req, http.StatusUnprocessableEntity)
  320. })
  321. t.Run("Success", func(t *testing.T) {
  322. defer PrintCurrentTest(t)()
  323. p := lfs.Pointer{Oid: "6ccce4863b70f258d691f59609d31b4502e1ba5199942d3bc5d35d17a4ce771d", Size: 5}
  324. req := newRequest(t, p, "gitea")
  325. session.MakeRequest(t, req, http.StatusOK)
  326. contentStore := lfs.NewContentStore()
  327. exist, err := contentStore.Exists(p)
  328. assert.NoError(t, err)
  329. assert.True(t, exist)
  330. meta, err := git_model.GetLFSMetaObjectByOid(repo.ID, p.Oid)
  331. assert.NoError(t, err)
  332. assert.NotNil(t, meta)
  333. })
  334. }
  335. func TestAPILFSVerify(t *testing.T) {
  336. defer prepareTestEnv(t)()
  337. setting.LFS.StartServer = true
  338. repo := createLFSTestRepository(t, "verify")
  339. content := []byte("dummy3")
  340. oid := storeObjectInRepo(t, repo.ID, &content)
  341. defer git_model.RemoveLFSMetaObjectByOid(repo.ID, oid)
  342. session := loginUser(t, "user2")
  343. newRequest := func(t testing.TB, p *lfs.Pointer) *http.Request {
  344. req := NewRequestWithJSON(t, "POST", "/user2/lfs-verify-repo.git/info/lfs/verify", p)
  345. req.Header.Set("Accept", lfs.MediaType)
  346. req.Header.Set("Content-Type", lfs.MediaType)
  347. return req
  348. }
  349. t.Run("InvalidJsonRequest", func(t *testing.T) {
  350. defer PrintCurrentTest(t)()
  351. req := newRequest(t, nil)
  352. session.MakeRequest(t, req, http.StatusUnprocessableEntity)
  353. })
  354. t.Run("InvalidPointer", func(t *testing.T) {
  355. defer PrintCurrentTest(t)()
  356. req := newRequest(t, &lfs.Pointer{})
  357. session.MakeRequest(t, req, http.StatusUnprocessableEntity)
  358. })
  359. t.Run("PointerNotExisting", func(t *testing.T) {
  360. defer PrintCurrentTest(t)()
  361. req := newRequest(t, &lfs.Pointer{Oid: "fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab042", Size: 6})
  362. session.MakeRequest(t, req, http.StatusNotFound)
  363. })
  364. t.Run("Success", func(t *testing.T) {
  365. defer PrintCurrentTest(t)()
  366. req := newRequest(t, &lfs.Pointer{Oid: oid, Size: 6})
  367. session.MakeRequest(t, req, http.StatusOK)
  368. })
  369. }