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_packages_npm_test.go 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "encoding/base64"
  6. "fmt"
  7. "net/http"
  8. "net/url"
  9. "strings"
  10. "testing"
  11. "code.gitea.io/gitea/models/db"
  12. "code.gitea.io/gitea/models/packages"
  13. "code.gitea.io/gitea/models/unittest"
  14. user_model "code.gitea.io/gitea/models/user"
  15. "code.gitea.io/gitea/modules/packages/npm"
  16. "code.gitea.io/gitea/modules/setting"
  17. "code.gitea.io/gitea/tests"
  18. "github.com/stretchr/testify/assert"
  19. )
  20. func TestPackageNpm(t *testing.T) {
  21. defer tests.PrepareTestEnv(t)()
  22. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
  23. token := fmt.Sprintf("Bearer %s", getTokenForLoggedInUser(t, loginUser(t, user.Name)))
  24. packageName := "@scope/test-package"
  25. packageVersion := "1.0.1-pre"
  26. packageTag := "latest"
  27. packageTag2 := "release"
  28. packageAuthor := "KN4CK3R"
  29. packageDescription := "Test Description"
  30. packageBinName := "cli"
  31. packageBinPath := "./cli.sh"
  32. data := "H4sIAAAAAAAA/ytITM5OTE/VL4DQelnF+XkMVAYGBgZmJiYK2MRBwNDcSIHB2NTMwNDQzMwAqA7IMDUxA9LUdgg2UFpcklgEdAql5kD8ogCnhwio5lJQUMpLzE1VslJQcihOzi9I1S9JLS7RhSYIJR2QgrLUouLM/DyQGkM9Az1D3YIiqExKanFyUWZBCVQ2BKhVwQVJDKwosbQkI78IJO/tZ+LsbRykxFXLNdA+HwWjYBSMgpENACgAbtAACAAA"
  33. buildUpload := func(version string) string {
  34. return `{
  35. "_id": "` + packageName + `",
  36. "name": "` + packageName + `",
  37. "description": "` + packageDescription + `",
  38. "dist-tags": {
  39. "` + packageTag + `": "` + version + `"
  40. },
  41. "versions": {
  42. "` + version + `": {
  43. "name": "` + packageName + `",
  44. "version": "` + version + `",
  45. "description": "` + packageDescription + `",
  46. "author": {
  47. "name": "` + packageAuthor + `"
  48. },
  49. "bin": {
  50. "` + packageBinName + `": "` + packageBinPath + `"
  51. },
  52. "dist": {
  53. "integrity": "sha512-yA4FJsVhetynGfOC1jFf79BuS+jrHbm0fhh+aHzCQkOaOBXKf9oBnC4a6DnLLnEsHQDRLYd00cwj8sCXpC+wIg==",
  54. "shasum": "aaa7eaf852a948b0aa05afeda35b1badca155d90"
  55. }
  56. }
  57. },
  58. "_attachments": {
  59. "` + packageName + `-` + version + `.tgz": {
  60. "data": "` + data + `"
  61. }
  62. }
  63. }`
  64. }
  65. root := fmt.Sprintf("/api/packages/%s/npm/%s", user.Name, url.QueryEscape(packageName))
  66. tagsRoot := fmt.Sprintf("/api/packages/%s/npm/-/package/%s/dist-tags", user.Name, url.QueryEscape(packageName))
  67. filename := fmt.Sprintf("%s-%s.tgz", strings.Split(packageName, "/")[1], packageVersion)
  68. t.Run("Upload", func(t *testing.T) {
  69. defer tests.PrintCurrentTest(t)()
  70. req := NewRequestWithBody(t, "PUT", root, strings.NewReader(buildUpload(packageVersion)))
  71. req = addTokenAuthHeader(req, token)
  72. MakeRequest(t, req, http.StatusCreated)
  73. pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNpm)
  74. assert.NoError(t, err)
  75. assert.Len(t, pvs, 1)
  76. pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
  77. assert.NoError(t, err)
  78. assert.NotNil(t, pd.SemVer)
  79. assert.IsType(t, &npm.Metadata{}, pd.Metadata)
  80. assert.Equal(t, packageName, pd.Package.Name)
  81. assert.Equal(t, packageVersion, pd.Version.Version)
  82. assert.Len(t, pd.VersionProperties, 1)
  83. assert.Equal(t, npm.TagProperty, pd.VersionProperties[0].Name)
  84. assert.Equal(t, packageTag, pd.VersionProperties[0].Value)
  85. pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
  86. assert.NoError(t, err)
  87. assert.Len(t, pfs, 1)
  88. assert.Equal(t, filename, pfs[0].Name)
  89. assert.True(t, pfs[0].IsLead)
  90. pb, err := packages.GetBlobByID(db.DefaultContext, pfs[0].BlobID)
  91. assert.NoError(t, err)
  92. assert.Equal(t, int64(192), pb.Size)
  93. })
  94. t.Run("UploadExists", func(t *testing.T) {
  95. defer tests.PrintCurrentTest(t)()
  96. req := NewRequestWithBody(t, "PUT", root, strings.NewReader(buildUpload(packageVersion)))
  97. req = addTokenAuthHeader(req, token)
  98. MakeRequest(t, req, http.StatusBadRequest)
  99. })
  100. t.Run("Download", func(t *testing.T) {
  101. defer tests.PrintCurrentTest(t)()
  102. req := NewRequest(t, "GET", fmt.Sprintf("%s/-/%s/%s", root, packageVersion, filename))
  103. req = addTokenAuthHeader(req, token)
  104. resp := MakeRequest(t, req, http.StatusOK)
  105. b, _ := base64.StdEncoding.DecodeString(data)
  106. assert.Equal(t, b, resp.Body.Bytes())
  107. req = NewRequest(t, "GET", fmt.Sprintf("%s/-/%s", root, filename))
  108. req = addTokenAuthHeader(req, token)
  109. resp = MakeRequest(t, req, http.StatusOK)
  110. assert.Equal(t, b, resp.Body.Bytes())
  111. pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNpm)
  112. assert.NoError(t, err)
  113. assert.Len(t, pvs, 1)
  114. assert.Equal(t, int64(2), pvs[0].DownloadCount)
  115. })
  116. t.Run("PackageMetadata", func(t *testing.T) {
  117. defer tests.PrintCurrentTest(t)()
  118. req := NewRequest(t, "GET", fmt.Sprintf("/api/packages/%s/npm/%s", user.Name, "does-not-exist"))
  119. req = addTokenAuthHeader(req, token)
  120. MakeRequest(t, req, http.StatusNotFound)
  121. req = NewRequest(t, "GET", root)
  122. req = addTokenAuthHeader(req, token)
  123. resp := MakeRequest(t, req, http.StatusOK)
  124. var result npm.PackageMetadata
  125. DecodeJSON(t, resp, &result)
  126. assert.Equal(t, packageName, result.ID)
  127. assert.Equal(t, packageName, result.Name)
  128. assert.Equal(t, packageDescription, result.Description)
  129. assert.Contains(t, result.DistTags, packageTag)
  130. assert.Equal(t, packageVersion, result.DistTags[packageTag])
  131. assert.Equal(t, packageAuthor, result.Author.Name)
  132. assert.Contains(t, result.Versions, packageVersion)
  133. pmv := result.Versions[packageVersion]
  134. assert.Equal(t, fmt.Sprintf("%s@%s", packageName, packageVersion), pmv.ID)
  135. assert.Equal(t, packageName, pmv.Name)
  136. assert.Equal(t, packageDescription, pmv.Description)
  137. assert.Equal(t, packageAuthor, pmv.Author.Name)
  138. assert.Equal(t, packageBinPath, pmv.Bin[packageBinName])
  139. assert.Equal(t, "sha512-yA4FJsVhetynGfOC1jFf79BuS+jrHbm0fhh+aHzCQkOaOBXKf9oBnC4a6DnLLnEsHQDRLYd00cwj8sCXpC+wIg==", pmv.Dist.Integrity)
  140. assert.Equal(t, "aaa7eaf852a948b0aa05afeda35b1badca155d90", pmv.Dist.Shasum)
  141. assert.Equal(t, fmt.Sprintf("%s%s/-/%s/%s", setting.AppURL, root[1:], packageVersion, filename), pmv.Dist.Tarball)
  142. })
  143. t.Run("AddTag", func(t *testing.T) {
  144. defer tests.PrintCurrentTest(t)()
  145. test := func(t *testing.T, status int, tag, version string) {
  146. req := NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/%s", tagsRoot, tag), strings.NewReader(`"`+version+`"`))
  147. req = addTokenAuthHeader(req, token)
  148. MakeRequest(t, req, status)
  149. }
  150. test(t, http.StatusBadRequest, "1.0", packageVersion)
  151. test(t, http.StatusBadRequest, "v1.0", packageVersion)
  152. test(t, http.StatusNotFound, packageTag2, "1.2")
  153. test(t, http.StatusOK, packageTag, packageVersion)
  154. test(t, http.StatusOK, packageTag2, packageVersion)
  155. })
  156. t.Run("ListTags", func(t *testing.T) {
  157. defer tests.PrintCurrentTest(t)()
  158. req := NewRequest(t, "GET", tagsRoot)
  159. req = addTokenAuthHeader(req, token)
  160. resp := MakeRequest(t, req, http.StatusOK)
  161. var result map[string]string
  162. DecodeJSON(t, resp, &result)
  163. assert.Len(t, result, 2)
  164. assert.Contains(t, result, packageTag)
  165. assert.Equal(t, packageVersion, result[packageTag])
  166. assert.Contains(t, result, packageTag2)
  167. assert.Equal(t, packageVersion, result[packageTag2])
  168. })
  169. t.Run("PackageMetadataDistTags", func(t *testing.T) {
  170. defer tests.PrintCurrentTest(t)()
  171. req := NewRequest(t, "GET", root)
  172. req = addTokenAuthHeader(req, token)
  173. resp := MakeRequest(t, req, http.StatusOK)
  174. var result npm.PackageMetadata
  175. DecodeJSON(t, resp, &result)
  176. assert.Len(t, result.DistTags, 2)
  177. assert.Contains(t, result.DistTags, packageTag)
  178. assert.Equal(t, packageVersion, result.DistTags[packageTag])
  179. assert.Contains(t, result.DistTags, packageTag2)
  180. assert.Equal(t, packageVersion, result.DistTags[packageTag2])
  181. })
  182. t.Run("DeleteTag", func(t *testing.T) {
  183. defer tests.PrintCurrentTest(t)()
  184. test := func(t *testing.T, status int, tag string) {
  185. req := NewRequest(t, "DELETE", fmt.Sprintf("%s/%s", tagsRoot, tag))
  186. req = addTokenAuthHeader(req, token)
  187. MakeRequest(t, req, status)
  188. }
  189. test(t, http.StatusBadRequest, "v1.0")
  190. test(t, http.StatusBadRequest, "1.0")
  191. test(t, http.StatusOK, "dummy")
  192. test(t, http.StatusOK, packageTag2)
  193. })
  194. t.Run("Search", func(t *testing.T) {
  195. defer tests.PrintCurrentTest(t)()
  196. url := fmt.Sprintf("/api/packages/%s/npm/-/v1/search", user.Name)
  197. cases := []struct {
  198. Query string
  199. Skip int
  200. Take int
  201. ExpectedTotal int64
  202. ExpectedResults int
  203. }{
  204. {"", 0, 0, 1, 1},
  205. {"", 0, 10, 1, 1},
  206. {"gitea", 0, 10, 0, 0},
  207. {"test", 0, 10, 1, 1},
  208. {"test", 1, 10, 1, 0},
  209. }
  210. for i, c := range cases {
  211. req := NewRequest(t, "GET", fmt.Sprintf("%s?text=%s&from=%d&size=%d", url, c.Query, c.Skip, c.Take))
  212. resp := MakeRequest(t, req, http.StatusOK)
  213. var result npm.PackageSearch
  214. DecodeJSON(t, resp, &result)
  215. assert.Equal(t, c.ExpectedTotal, result.Total, "case %d: unexpected total hits", i)
  216. assert.Len(t, result.Objects, c.ExpectedResults, "case %d: unexpected result count", i)
  217. }
  218. })
  219. t.Run("Delete", func(t *testing.T) {
  220. defer tests.PrintCurrentTest(t)()
  221. req := NewRequestWithBody(t, "PUT", root, strings.NewReader(buildUpload(packageVersion+"-dummy")))
  222. req = addTokenAuthHeader(req, token)
  223. MakeRequest(t, req, http.StatusCreated)
  224. req = NewRequest(t, "PUT", root+"/-rev/dummy")
  225. MakeRequest(t, req, http.StatusUnauthorized)
  226. req = NewRequest(t, "PUT", root+"/-rev/dummy")
  227. req = addTokenAuthHeader(req, token)
  228. MakeRequest(t, req, http.StatusOK)
  229. t.Run("Version", func(t *testing.T) {
  230. defer tests.PrintCurrentTest(t)()
  231. pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNpm)
  232. assert.NoError(t, err)
  233. assert.Len(t, pvs, 2)
  234. req := NewRequest(t, "DELETE", fmt.Sprintf("%s/-/%s/%s/-rev/dummy", root, packageVersion, filename))
  235. MakeRequest(t, req, http.StatusUnauthorized)
  236. req = NewRequest(t, "DELETE", fmt.Sprintf("%s/-/%s/%s/-rev/dummy", root, packageVersion, filename))
  237. req = addTokenAuthHeader(req, token)
  238. MakeRequest(t, req, http.StatusOK)
  239. pvs, err = packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNpm)
  240. assert.NoError(t, err)
  241. assert.Len(t, pvs, 1)
  242. })
  243. t.Run("Full", func(t *testing.T) {
  244. defer tests.PrintCurrentTest(t)()
  245. pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNpm)
  246. assert.NoError(t, err)
  247. assert.Len(t, pvs, 1)
  248. req := NewRequest(t, "DELETE", root+"/-rev/dummy")
  249. MakeRequest(t, req, http.StatusUnauthorized)
  250. req = NewRequest(t, "DELETE", root+"/-rev/dummy")
  251. req = addTokenAuthHeader(req, token)
  252. MakeRequest(t, req, http.StatusOK)
  253. pvs, err = packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNpm)
  254. assert.NoError(t, err)
  255. assert.Len(t, pvs, 0)
  256. })
  257. })
  258. }