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