code.gitea.io/gitea@v1.21.7/tests/integration/api_packages_cargo_test.go (about)

     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  
     5  package integration
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"fmt"
    11  	"io"
    12  	"net/http"
    13  	neturl "net/url"
    14  	"testing"
    15  
    16  	"code.gitea.io/gitea/models/db"
    17  	"code.gitea.io/gitea/models/packages"
    18  	repo_model "code.gitea.io/gitea/models/repo"
    19  	"code.gitea.io/gitea/models/unittest"
    20  	user_model "code.gitea.io/gitea/models/user"
    21  	"code.gitea.io/gitea/modules/git"
    22  	"code.gitea.io/gitea/modules/json"
    23  	cargo_module "code.gitea.io/gitea/modules/packages/cargo"
    24  	"code.gitea.io/gitea/modules/setting"
    25  	cargo_router "code.gitea.io/gitea/routers/api/packages/cargo"
    26  	cargo_service "code.gitea.io/gitea/services/packages/cargo"
    27  	"code.gitea.io/gitea/tests"
    28  
    29  	"github.com/stretchr/testify/assert"
    30  )
    31  
    32  func TestPackageCargo(t *testing.T) {
    33  	onGiteaRun(t, testPackageCargo)
    34  }
    35  
    36  func testPackageCargo(t *testing.T, _ *neturl.URL) {
    37  	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
    38  
    39  	packageName := "cargo-package"
    40  	packageVersion := "1.0.3"
    41  	packageDescription := "Package Description"
    42  	packageAuthor := "KN4CK3R"
    43  	packageHomepage := "https://gitea.io/"
    44  	packageLicense := "MIT"
    45  
    46  	createPackage := func(name, version string) io.Reader {
    47  		metadata := `{
    48     "name":"` + name + `",
    49     "vers":"` + version + `",
    50     "description":"` + packageDescription + `",
    51     "authors": ["` + packageAuthor + `"],
    52     "deps":[
    53        {
    54           "name":"dep",
    55           "version_req":"1.0",
    56           "registry": "https://gitea.io/user/_cargo-index",
    57           "kind": "normal",
    58           "default_features": true
    59        }
    60     ],
    61     "homepage":"` + packageHomepage + `",
    62     "license":"` + packageLicense + `"
    63  }`
    64  
    65  		var buf bytes.Buffer
    66  		binary.Write(&buf, binary.LittleEndian, uint32(len(metadata)))
    67  		buf.WriteString(metadata)
    68  		binary.Write(&buf, binary.LittleEndian, uint32(4))
    69  		buf.WriteString("test")
    70  		return &buf
    71  	}
    72  
    73  	err := cargo_service.InitializeIndexRepository(db.DefaultContext, user, user)
    74  	assert.NoError(t, err)
    75  
    76  	repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, user.Name, cargo_service.IndexRepositoryName)
    77  	assert.NotNil(t, repo)
    78  	assert.NoError(t, err)
    79  
    80  	readGitContent := func(t *testing.T, path string) string {
    81  		gitRepo, err := git.OpenRepository(db.DefaultContext, repo.RepoPath())
    82  		assert.NoError(t, err)
    83  		defer gitRepo.Close()
    84  
    85  		commit, err := gitRepo.GetBranchCommit(repo.DefaultBranch)
    86  		assert.NoError(t, err)
    87  
    88  		blob, err := commit.GetBlobByPath(path)
    89  		assert.NoError(t, err)
    90  
    91  		content, err := blob.GetBlobContent(1024)
    92  		assert.NoError(t, err)
    93  
    94  		return content
    95  	}
    96  
    97  	root := fmt.Sprintf("%sapi/packages/%s/cargo", setting.AppURL, user.Name)
    98  	url := fmt.Sprintf("%s/api/v1/crates", root)
    99  
   100  	t.Run("Index", func(t *testing.T) {
   101  		t.Run("Git/Config", func(t *testing.T) {
   102  			defer tests.PrintCurrentTest(t)()
   103  
   104  			content := readGitContent(t, cargo_service.ConfigFileName)
   105  
   106  			var config cargo_service.Config
   107  			err := json.Unmarshal([]byte(content), &config)
   108  			assert.NoError(t, err)
   109  
   110  			assert.Equal(t, url, config.DownloadURL)
   111  			assert.Equal(t, root, config.APIURL)
   112  		})
   113  
   114  		t.Run("HTTP/Config", func(t *testing.T) {
   115  			defer tests.PrintCurrentTest(t)()
   116  
   117  			req := NewRequest(t, "GET", root+"/"+cargo_service.ConfigFileName)
   118  			resp := MakeRequest(t, req, http.StatusOK)
   119  
   120  			var config cargo_service.Config
   121  			err := json.Unmarshal(resp.Body.Bytes(), &config)
   122  			assert.NoError(t, err)
   123  
   124  			assert.Equal(t, url, config.DownloadURL)
   125  			assert.Equal(t, root, config.APIURL)
   126  		})
   127  	})
   128  
   129  	t.Run("Upload", func(t *testing.T) {
   130  		t.Run("InvalidNameOrVersion", func(t *testing.T) {
   131  			defer tests.PrintCurrentTest(t)()
   132  
   133  			content := createPackage("0test", "1.0.0")
   134  
   135  			req := NewRequestWithBody(t, "PUT", url+"/new", content)
   136  			req = AddBasicAuthHeader(req, user.Name)
   137  			resp := MakeRequest(t, req, http.StatusBadRequest)
   138  
   139  			var status cargo_router.StatusResponse
   140  			DecodeJSON(t, resp, &status)
   141  			assert.False(t, status.OK)
   142  
   143  			content = createPackage("test", "-1.0.0")
   144  
   145  			req = NewRequestWithBody(t, "PUT", url+"/new", content)
   146  			req = AddBasicAuthHeader(req, user.Name)
   147  			resp = MakeRequest(t, req, http.StatusBadRequest)
   148  
   149  			DecodeJSON(t, resp, &status)
   150  			assert.False(t, status.OK)
   151  		})
   152  
   153  		t.Run("InvalidContent", func(t *testing.T) {
   154  			defer tests.PrintCurrentTest(t)()
   155  
   156  			metadata := `{"name":"test","vers":"1.0.0"}`
   157  
   158  			var buf bytes.Buffer
   159  			binary.Write(&buf, binary.LittleEndian, uint32(len(metadata)))
   160  			buf.WriteString(metadata)
   161  			binary.Write(&buf, binary.LittleEndian, uint32(4))
   162  			buf.WriteString("te")
   163  
   164  			req := NewRequestWithBody(t, "PUT", url+"/new", &buf)
   165  			req = AddBasicAuthHeader(req, user.Name)
   166  			MakeRequest(t, req, http.StatusBadRequest)
   167  		})
   168  
   169  		t.Run("Valid", func(t *testing.T) {
   170  			defer tests.PrintCurrentTest(t)()
   171  
   172  			req := NewRequestWithBody(t, "PUT", url+"/new", createPackage(packageName, packageVersion))
   173  			MakeRequest(t, req, http.StatusUnauthorized)
   174  
   175  			req = NewRequestWithBody(t, "PUT", url+"/new", createPackage(packageName, packageVersion))
   176  			req = AddBasicAuthHeader(req, user.Name)
   177  			resp := MakeRequest(t, req, http.StatusOK)
   178  
   179  			var status cargo_router.StatusResponse
   180  			DecodeJSON(t, resp, &status)
   181  			assert.True(t, status.OK)
   182  
   183  			pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeCargo)
   184  			assert.NoError(t, err)
   185  			assert.Len(t, pvs, 1)
   186  
   187  			pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
   188  			assert.NoError(t, err)
   189  			assert.NotNil(t, pd.SemVer)
   190  			assert.IsType(t, &cargo_module.Metadata{}, pd.Metadata)
   191  			assert.Equal(t, packageName, pd.Package.Name)
   192  			assert.Equal(t, packageVersion, pd.Version.Version)
   193  
   194  			pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
   195  			assert.NoError(t, err)
   196  			assert.Len(t, pfs, 1)
   197  			assert.Equal(t, fmt.Sprintf("%s-%s.crate", packageName, packageVersion), pfs[0].Name)
   198  			assert.True(t, pfs[0].IsLead)
   199  
   200  			pb, err := packages.GetBlobByID(db.DefaultContext, pfs[0].BlobID)
   201  			assert.NoError(t, err)
   202  			assert.EqualValues(t, 4, pb.Size)
   203  
   204  			req = NewRequestWithBody(t, "PUT", url+"/new", createPackage(packageName, packageVersion))
   205  			req = AddBasicAuthHeader(req, user.Name)
   206  			MakeRequest(t, req, http.StatusConflict)
   207  
   208  			t.Run("Index", func(t *testing.T) {
   209  				t.Run("Git", func(t *testing.T) {
   210  					t.Run("Entry", func(t *testing.T) {
   211  						defer tests.PrintCurrentTest(t)()
   212  
   213  						content := readGitContent(t, cargo_service.BuildPackagePath(packageName))
   214  
   215  						var entry cargo_service.IndexVersionEntry
   216  						err := json.Unmarshal([]byte(content), &entry)
   217  						assert.NoError(t, err)
   218  
   219  						assert.Equal(t, packageName, entry.Name)
   220  						assert.Equal(t, packageVersion, entry.Version)
   221  						assert.Equal(t, pb.HashSHA256, entry.FileChecksum)
   222  						assert.False(t, entry.Yanked)
   223  						assert.Len(t, entry.Dependencies, 1)
   224  						dep := entry.Dependencies[0]
   225  						assert.Equal(t, "dep", dep.Name)
   226  						assert.Equal(t, "1.0", dep.Req)
   227  						assert.Equal(t, "normal", dep.Kind)
   228  						assert.True(t, dep.DefaultFeatures)
   229  						assert.Empty(t, dep.Features)
   230  						assert.False(t, dep.Optional)
   231  						assert.Nil(t, dep.Target)
   232  						assert.NotNil(t, dep.Registry)
   233  						assert.Equal(t, "https://gitea.io/user/_cargo-index", *dep.Registry)
   234  						assert.Nil(t, dep.Package)
   235  					})
   236  
   237  					t.Run("Rebuild", func(t *testing.T) {
   238  						defer tests.PrintCurrentTest(t)()
   239  
   240  						err := cargo_service.RebuildIndex(db.DefaultContext, user, user)
   241  						assert.NoError(t, err)
   242  
   243  						_ = readGitContent(t, cargo_service.BuildPackagePath(packageName))
   244  					})
   245  				})
   246  
   247  				t.Run("HTTP", func(t *testing.T) {
   248  					t.Run("Entry", func(t *testing.T) {
   249  						defer tests.PrintCurrentTest(t)()
   250  
   251  						req := NewRequest(t, "GET", root+"/"+cargo_service.BuildPackagePath(packageName))
   252  						resp := MakeRequest(t, req, http.StatusOK)
   253  
   254  						var entry cargo_service.IndexVersionEntry
   255  						err := json.Unmarshal(resp.Body.Bytes(), &entry)
   256  						assert.NoError(t, err)
   257  
   258  						assert.Equal(t, packageName, entry.Name)
   259  						assert.Equal(t, packageVersion, entry.Version)
   260  						assert.Equal(t, pb.HashSHA256, entry.FileChecksum)
   261  						assert.False(t, entry.Yanked)
   262  						assert.Len(t, entry.Dependencies, 1)
   263  						dep := entry.Dependencies[0]
   264  						assert.Equal(t, "dep", dep.Name)
   265  						assert.Equal(t, "1.0", dep.Req)
   266  						assert.Equal(t, "normal", dep.Kind)
   267  						assert.True(t, dep.DefaultFeatures)
   268  						assert.Empty(t, dep.Features)
   269  						assert.False(t, dep.Optional)
   270  						assert.Nil(t, dep.Target)
   271  						assert.NotNil(t, dep.Registry)
   272  						assert.Equal(t, "https://gitea.io/user/_cargo-index", *dep.Registry)
   273  						assert.Nil(t, dep.Package)
   274  					})
   275  				})
   276  			})
   277  		})
   278  	})
   279  
   280  	t.Run("Download", func(t *testing.T) {
   281  		defer tests.PrintCurrentTest(t)()
   282  
   283  		pv, err := packages.GetVersionByNameAndVersion(db.DefaultContext, user.ID, packages.TypeCargo, packageName, packageVersion)
   284  		assert.NoError(t, err)
   285  		assert.EqualValues(t, 0, pv.DownloadCount)
   286  
   287  		pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pv.ID)
   288  		assert.NoError(t, err)
   289  		assert.Len(t, pfs, 1)
   290  
   291  		req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/download", url, neturl.PathEscape(packageName), neturl.PathEscape(pv.Version)))
   292  		req = AddBasicAuthHeader(req, user.Name)
   293  		resp := MakeRequest(t, req, http.StatusOK)
   294  
   295  		assert.Equal(t, "test", resp.Body.String())
   296  
   297  		pv, err = packages.GetVersionByNameAndVersion(db.DefaultContext, user.ID, packages.TypeCargo, packageName, packageVersion)
   298  		assert.NoError(t, err)
   299  		assert.EqualValues(t, 1, pv.DownloadCount)
   300  	})
   301  
   302  	t.Run("Search", func(t *testing.T) {
   303  		defer tests.PrintCurrentTest(t)()
   304  
   305  		cases := []struct {
   306  			Query           string
   307  			Page            int
   308  			PerPage         int
   309  			ExpectedTotal   int64
   310  			ExpectedResults int
   311  		}{
   312  			{"", 0, 0, 1, 1},
   313  			{"", 1, 10, 1, 1},
   314  			{"cargo", 1, 0, 1, 1},
   315  			{"cargo", 1, 10, 1, 1},
   316  			{"cargo", 2, 10, 1, 0},
   317  			{"test", 0, 10, 0, 0},
   318  		}
   319  
   320  		for i, c := range cases {
   321  			req := NewRequest(t, "GET", fmt.Sprintf("%s?q=%s&page=%d&per_page=%d", url, c.Query, c.Page, c.PerPage))
   322  			req = AddBasicAuthHeader(req, user.Name)
   323  			resp := MakeRequest(t, req, http.StatusOK)
   324  
   325  			var result cargo_router.SearchResult
   326  			DecodeJSON(t, resp, &result)
   327  
   328  			assert.Equal(t, c.ExpectedTotal, result.Meta.Total, "case %d: unexpected total hits", i)
   329  			assert.Len(t, result.Crates, c.ExpectedResults, "case %d: unexpected result count", i)
   330  		}
   331  	})
   332  
   333  	t.Run("Yank", func(t *testing.T) {
   334  		defer tests.PrintCurrentTest(t)()
   335  
   336  		req := NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s/yank", url, neturl.PathEscape(packageName), neturl.PathEscape(packageVersion)))
   337  		req = AddBasicAuthHeader(req, user.Name)
   338  		resp := MakeRequest(t, req, http.StatusOK)
   339  
   340  		var status cargo_router.StatusResponse
   341  		DecodeJSON(t, resp, &status)
   342  		assert.True(t, status.OK)
   343  
   344  		content := readGitContent(t, cargo_service.BuildPackagePath(packageName))
   345  
   346  		var entry cargo_service.IndexVersionEntry
   347  		err := json.Unmarshal([]byte(content), &entry)
   348  		assert.NoError(t, err)
   349  
   350  		assert.True(t, entry.Yanked)
   351  	})
   352  
   353  	t.Run("Unyank", func(t *testing.T) {
   354  		defer tests.PrintCurrentTest(t)()
   355  
   356  		req := NewRequest(t, "PUT", fmt.Sprintf("%s/%s/%s/unyank", url, neturl.PathEscape(packageName), neturl.PathEscape(packageVersion)))
   357  		req = AddBasicAuthHeader(req, user.Name)
   358  		resp := MakeRequest(t, req, http.StatusOK)
   359  
   360  		var status cargo_router.StatusResponse
   361  		DecodeJSON(t, resp, &status)
   362  		assert.True(t, status.OK)
   363  
   364  		content := readGitContent(t, cargo_service.BuildPackagePath(packageName))
   365  
   366  		var entry cargo_service.IndexVersionEntry
   367  		err := json.Unmarshal([]byte(content), &entry)
   368  		assert.NoError(t, err)
   369  
   370  		assert.False(t, entry.Yanked)
   371  	})
   372  
   373  	t.Run("ListOwners", func(t *testing.T) {
   374  		defer tests.PrintCurrentTest(t)()
   375  
   376  		req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/owners", url, neturl.PathEscape(packageName)))
   377  		resp := MakeRequest(t, req, http.StatusOK)
   378  
   379  		var owners cargo_router.Owners
   380  		DecodeJSON(t, resp, &owners)
   381  
   382  		assert.Len(t, owners.Users, 1)
   383  		assert.Equal(t, user.ID, owners.Users[0].ID)
   384  		assert.Equal(t, user.Name, owners.Users[0].Login)
   385  		assert.Equal(t, user.DisplayName(), owners.Users[0].Name)
   386  	})
   387  }