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  }