gitea源码

api_packages_rubygems_test.go 9.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "archive/tar"
  6. "bytes"
  7. "compress/gzip"
  8. "crypto/sha256"
  9. "crypto/sha512"
  10. "encoding/base64"
  11. "fmt"
  12. "mime/multipart"
  13. "net/http"
  14. "testing"
  15. "code.gitea.io/gitea/models/packages"
  16. "code.gitea.io/gitea/models/unittest"
  17. user_model "code.gitea.io/gitea/models/user"
  18. "code.gitea.io/gitea/modules/packages/rubygems"
  19. "code.gitea.io/gitea/tests"
  20. "github.com/stretchr/testify/assert"
  21. )
  22. type tarFile struct {
  23. Name string
  24. Data []byte
  25. }
  26. func makeArchiveFileTar(files []*tarFile) []byte {
  27. buf := new(bytes.Buffer)
  28. tarWriter := tar.NewWriter(buf)
  29. for _, file := range files {
  30. _ = tarWriter.WriteHeader(&tar.Header{
  31. Typeflag: tar.TypeReg,
  32. Name: file.Name,
  33. Mode: 0o644,
  34. Size: int64(len(file.Data)),
  35. })
  36. _, _ = tarWriter.Write(file.Data)
  37. }
  38. _ = tarWriter.Close()
  39. return buf.Bytes()
  40. }
  41. func makeArchiveFileGz(data []byte) []byte {
  42. buf := new(bytes.Buffer)
  43. gzWriter, _ := gzip.NewWriterLevel(buf, gzip.NoCompression)
  44. _, _ = gzWriter.Write(data)
  45. _ = gzWriter.Close()
  46. return buf.Bytes()
  47. }
  48. func makeRubyGem(name, version string) []byte {
  49. metadataContent := fmt.Sprintf(`--- !ruby/object:Gem::Specification
  50. name: %s
  51. version: !ruby/object:Gem::Version
  52. version: %s
  53. platform: ruby
  54. authors:
  55. - Gitea
  56. autorequire:
  57. bindir: bin
  58. cert_chain: []
  59. date: 2021-08-23 00:00:00.000000000 Z
  60. dependencies:
  61. - !ruby/object:Gem::Dependency
  62. name: runtime-dep
  63. requirement: !ruby/object:Gem::Requirement
  64. requirements:
  65. - - ">="
  66. - !ruby/object:Gem::Version
  67. version: 1.2.0
  68. - - "<"
  69. - !ruby/object:Gem::Version
  70. version: '2.0'
  71. type: :runtime
  72. prerelease: false
  73. version_requirements: !ruby/object:Gem::Requirement
  74. requirements:
  75. - - ">="
  76. - !ruby/object:Gem::Version
  77. version: 1.2.0
  78. - - "<"
  79. - !ruby/object:Gem::Version
  80. version: '2.0'
  81. - !ruby/object:Gem::Dependency
  82. name: dev-dep
  83. requirement: !ruby/object:Gem::Requirement
  84. requirements:
  85. - - "~>"
  86. - !ruby/object:Gem::Version
  87. version: '5.2'
  88. type: :development
  89. prerelease: false
  90. version_requirements: !ruby/object:Gem::Requirement
  91. requirements:
  92. - - "~>"
  93. - !ruby/object:Gem::Version
  94. version: '5.2'
  95. description: RubyGems package test
  96. email: rubygems@gitea.io
  97. executables: []
  98. extensions: []
  99. extra_rdoc_files: []
  100. files:
  101. - lib/gitea.rb
  102. homepage: https://gitea.io/
  103. licenses:
  104. - MIT
  105. metadata: {}
  106. post_install_message:
  107. rdoc_options: []
  108. require_paths:
  109. - lib
  110. required_ruby_version: !ruby/object:Gem::Requirement
  111. requirements:
  112. - - ">="
  113. - !ruby/object:Gem::Version
  114. version: 2.3.0
  115. required_rubygems_version: !ruby/object:Gem::Requirement
  116. requirements:
  117. - - ">="
  118. - !ruby/object:Gem::Version
  119. version: '1.0'
  120. requirements: []
  121. rubyforge_project:
  122. rubygems_version: 2.7.6.2
  123. signing_key:
  124. specification_version: 4
  125. summary: Gitea package
  126. test_files: []
  127. `, name, version)
  128. metadataGz := makeArchiveFileGz([]byte(metadataContent))
  129. dataTarGz := makeArchiveFileGz(makeArchiveFileTar([]*tarFile{
  130. {
  131. Name: "lib/gitea.rb",
  132. Data: []byte("class Gitea\nend"),
  133. },
  134. }))
  135. checksumsYaml := fmt.Sprintf(`---
  136. SHA256:
  137. metadata.gz: %x
  138. data.tar.gz: %x
  139. SHA512:
  140. metadata.gz: %x
  141. data.tar.gz: %x
  142. `, sha256.Sum256(metadataGz), sha256.Sum256(dataTarGz), sha512.Sum512(metadataGz), sha512.Sum512(dataTarGz))
  143. files := []*tarFile{
  144. {
  145. Name: "data.tar.gz",
  146. Data: dataTarGz,
  147. },
  148. {
  149. Name: "metadata.gz",
  150. Data: metadataGz,
  151. },
  152. {
  153. Name: "checksums.yaml.gz",
  154. Data: makeArchiveFileGz([]byte(checksumsYaml)),
  155. },
  156. }
  157. return makeArchiveFileTar(files)
  158. }
  159. func TestPackageRubyGems(t *testing.T) {
  160. defer tests.PrepareTestEnv(t)()
  161. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
  162. testGemName := "gitea"
  163. testGemVersion := "1.0.5"
  164. testGemContent := makeRubyGem(testGemName, testGemVersion)
  165. testGemContentChecksum := fmt.Sprintf("%x", sha256.Sum256(testGemContent))
  166. testAnotherGemName := "gitea-another"
  167. testAnotherGemVersion := "0.99"
  168. root := fmt.Sprintf("/api/packages/%s/rubygems", user.Name)
  169. uploadFile := func(t *testing.T, content []byte, expectedStatus int) {
  170. req := NewRequestWithBody(t, "POST", root+"/api/v1/gems", bytes.NewReader(content)).
  171. AddBasicAuth(user.Name)
  172. MakeRequest(t, req, expectedStatus)
  173. }
  174. t.Run("Upload", func(t *testing.T) {
  175. defer tests.PrintCurrentTest(t)()
  176. uploadFile(t, testGemContent, http.StatusCreated)
  177. pvs, err := packages.GetVersionsByPackageType(t.Context(), user.ID, packages.TypeRubyGems)
  178. assert.NoError(t, err)
  179. assert.Len(t, pvs, 1)
  180. pd, err := packages.GetPackageDescriptor(t.Context(), pvs[0])
  181. assert.NoError(t, err)
  182. assert.NotNil(t, pd.SemVer)
  183. assert.IsType(t, &rubygems.Metadata{}, pd.Metadata)
  184. assert.Equal(t, testGemName, pd.Package.Name)
  185. assert.Equal(t, testGemVersion, pd.Version.Version)
  186. pfs, err := packages.GetFilesByVersionID(t.Context(), pvs[0].ID)
  187. assert.NoError(t, err)
  188. assert.Len(t, pfs, 1)
  189. assert.Equal(t, fmt.Sprintf("%s-%s.gem", testGemName, testGemVersion), pfs[0].Name)
  190. assert.True(t, pfs[0].IsLead)
  191. pb, err := packages.GetBlobByID(t.Context(), pfs[0].BlobID)
  192. assert.NoError(t, err)
  193. assert.EqualValues(t, len(testGemContent), pb.Size)
  194. })
  195. t.Run("UploadExists", func(t *testing.T) {
  196. defer tests.PrintCurrentTest(t)()
  197. uploadFile(t, testGemContent, http.StatusConflict)
  198. })
  199. t.Run("Download", func(t *testing.T) {
  200. defer tests.PrintCurrentTest(t)()
  201. req := NewRequest(t, "GET", fmt.Sprintf("%s/gems/%s-%s.gem", root, testGemName, testGemVersion)).
  202. AddBasicAuth(user.Name)
  203. resp := MakeRequest(t, req, http.StatusOK)
  204. assert.Equal(t, testGemContent, resp.Body.Bytes())
  205. pvs, err := packages.GetVersionsByPackageType(t.Context(), user.ID, packages.TypeRubyGems)
  206. assert.NoError(t, err)
  207. assert.Len(t, pvs, 1)
  208. assert.Equal(t, int64(1), pvs[0].DownloadCount)
  209. })
  210. t.Run("DownloadGemspec", func(t *testing.T) {
  211. defer tests.PrintCurrentTest(t)()
  212. req := NewRequest(t, "GET", fmt.Sprintf("%s/quick/Marshal.4.8/%s-%s.gemspec.rz", root, testGemName, testGemVersion)).
  213. AddBasicAuth(user.Name)
  214. resp := MakeRequest(t, req, http.StatusOK)
  215. b, _ := base64.StdEncoding.DecodeString(`eJxi4Si1EndPzbWyCi5ITc5My0xOLMnMz2M8zMIRLeGpxGWsZ6RnzGbF5hqSyempxJWeWZKayGbN
  216. EBJqJQjWFZZaVJyZnxfN5qnEZahnoGcKkjTwVBJyB6lUKEhMzk5MTwULGngqcRaVJlWCONEMBp5K
  217. DGAWSKc7zFhPJamg0qRK99TcYphehZLU4hKInFhGSUlBsZW+PtgZepn5+iDxECRzDUDGcfh6hoA4
  218. gAAAAP//MS06Gw==`)
  219. assert.Equal(t, b, resp.Body.Bytes())
  220. pvs, err := packages.GetVersionsByPackageType(t.Context(), user.ID, packages.TypeRubyGems)
  221. assert.NoError(t, err)
  222. assert.Len(t, pvs, 1)
  223. assert.Equal(t, int64(1), pvs[0].DownloadCount)
  224. })
  225. t.Run("EnumeratePackages", func(t *testing.T) {
  226. defer tests.PrintCurrentTest(t)()
  227. enumeratePackages := func(t *testing.T, endpoint string, expectedContent []byte) {
  228. req := NewRequest(t, "GET", fmt.Sprintf("%s/%s", root, endpoint)).
  229. AddBasicAuth(user.Name)
  230. resp := MakeRequest(t, req, http.StatusOK)
  231. assert.Equal(t, expectedContent, resp.Body.Bytes())
  232. }
  233. b, _ := base64.StdEncoding.DecodeString(`H4sICAAAAAAA/3NwZWNzLjQuOABi4Yhmi+bwVOJKzyxJTWSzYnMNCbUSdE/NtbIKSy0qzszPi2bzVOIy1DPQM2WzZgjxVOIsKk2qBDEBAQAA///xOEYKOwAAAA==`)
  234. enumeratePackages(t, "specs.4.8.gz", b)
  235. b, _ = base64.StdEncoding.DecodeString(`H4sICAAAAAAA/2xhdGVzdF9zcGVjcy40LjgAYuGIZovm8FTiSs8sSU1ks2JzDQm1EnRPzbWyCkstKs7Mz4tm81TiMtQz0DNls2YI8VTiLCpNqgQxAQEAAP//8ThGCjsAAAA=`)
  236. enumeratePackages(t, "latest_specs.4.8.gz", b)
  237. b, _ = base64.StdEncoding.DecodeString(`H4sICAAAAAAA/3ByZXJlbGVhc2Vfc3BlY3MuNC44AGLhiGYABAAA//9snXr5BAAAAA==`)
  238. enumeratePackages(t, "prerelease_specs.4.8.gz", b)
  239. })
  240. t.Run("UploadAnother", func(t *testing.T) {
  241. defer tests.PrintCurrentTest(t)()
  242. uploadFile(t, makeRubyGem(testAnotherGemName, testAnotherGemVersion), http.StatusCreated)
  243. })
  244. t.Run("PackageInfo", func(t *testing.T) {
  245. defer tests.PrintCurrentTest(t)()
  246. req := NewRequest(t, "GET", fmt.Sprintf("%s/info/%s", root, testGemName)).AddBasicAuth(user.Name)
  247. resp := MakeRequest(t, req, http.StatusOK)
  248. expected := fmt.Sprintf(`---
  249. 1.0.5 runtime-dep:>= 1.2.0&< 2.0|checksum:%s,ruby:>= 2.3.0,rubygems:>= 1.0
  250. `, testGemContentChecksum)
  251. assert.Equal(t, expected, resp.Body.String())
  252. })
  253. t.Run("Versions", func(t *testing.T) {
  254. defer tests.PrintCurrentTest(t)()
  255. req := NewRequest(t, "GET", root+"/versions").AddBasicAuth(user.Name)
  256. resp := MakeRequest(t, req, http.StatusOK)
  257. assert.Equal(t, `---
  258. gitea 1.0.5 08843c2dd0ea19910e6b056b98e38f1c
  259. gitea-another 0.99 8b639e4048d282941485368ec42609be
  260. `, resp.Body.String())
  261. })
  262. deleteGemPackage := func(t *testing.T, packageName, packageVersion string) {
  263. body := bytes.Buffer{}
  264. writer := multipart.NewWriter(&body)
  265. _ = writer.WriteField("gem_name", packageName)
  266. _ = writer.WriteField("version", packageVersion)
  267. _ = writer.Close()
  268. req := NewRequestWithBody(t, "DELETE", root+"/api/v1/gems/yank", &body).
  269. SetHeader("Content-Type", writer.FormDataContentType()).
  270. AddBasicAuth(user.Name)
  271. MakeRequest(t, req, http.StatusOK)
  272. }
  273. t.Run("DeleteAll", func(t *testing.T) {
  274. defer tests.PrintCurrentTest(t)()
  275. deleteGemPackage(t, testGemName, testGemVersion)
  276. deleteGemPackage(t, testAnotherGemName, testAnotherGemVersion)
  277. pvs, err := packages.GetVersionsByPackageType(t.Context(), user.ID, packages.TypeRubyGems)
  278. assert.NoError(t, err)
  279. assert.Empty(t, pvs)
  280. })
  281. t.Run("PackageInfoAfterDelete", func(t *testing.T) {
  282. defer tests.PrintCurrentTest(t)()
  283. req := NewRequest(t, "GET", fmt.Sprintf("%s/info/%s", root, testGemName)).AddBasicAuth(user.Name)
  284. MakeRequest(t, req, http.StatusNotFound)
  285. })
  286. t.Run("VersionsAfterDelete", func(t *testing.T) {
  287. defer tests.PrintCurrentTest(t)()
  288. req := NewRequest(t, "GET", root+"/versions").AddBasicAuth(user.Name)
  289. resp := MakeRequest(t, req, http.StatusOK)
  290. assert.Equal(t, "---\n", resp.Body.String())
  291. })
  292. }