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