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

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