// Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package integration import ( "archive/tar" "archive/zip" "bytes" "fmt" "io" "net/http" "testing" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" conda_module "code.gitea.io/gitea/modules/packages/conda" "code.gitea.io/gitea/tests" "github.com/dsnet/compress/bzip2" "github.com/klauspost/compress/zstd" "github.com/stretchr/testify/assert" ) func TestPackageConda(t *testing.T) { defer tests.PrepareTestEnv(t)() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) packageName := "test_package" packageVersion := "1.0.1" channel := "test-channel" root := fmt.Sprintf("/api/packages/%s/conda", user.Name) t.Run("Upload", func(t *testing.T) { tarContent := func() []byte { var buf bytes.Buffer tw := tar.NewWriter(&buf) content := []byte(`{"name":"` + packageName + `","version":"` + packageVersion + `","subdir":"noarch","build":"xxx"}`) hdr := &tar.Header{ Name: "info/index.json", Mode: 0o600, Size: int64(len(content)), } tw.WriteHeader(hdr) tw.Write(content) tw.Close() return buf.Bytes() }() t.Run(".tar.bz2", func(t *testing.T) { defer tests.PrintCurrentTest(t)() var buf bytes.Buffer bw, _ := bzip2.NewWriter(&buf, nil) io.Copy(bw, bytes.NewReader(tarContent)) bw.Close() filename := fmt.Sprintf("%s-%s.tar.bz2", packageName, packageVersion) req := NewRequestWithBody(t, "PUT", root+"/"+filename, bytes.NewReader(buf.Bytes())) MakeRequest(t, req, http.StatusUnauthorized) req = NewRequestWithBody(t, "PUT", root+"/"+filename, bytes.NewReader(buf.Bytes())) AddBasicAuthHeader(req, user.Name) MakeRequest(t, req, http.StatusCreated) req = NewRequestWithBody(t, "PUT", root+"/"+filename, bytes.NewReader(buf.Bytes())) AddBasicAuthHeader(req, user.Name) MakeRequest(t, req, http.StatusConflict) pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeConda) assert.NoError(t, err) assert.Len(t, pvs, 1) pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) assert.NoError(t, err) assert.Nil(t, pd.SemVer) assert.IsType(t, &conda_module.VersionMetadata{}, pd.Metadata) assert.Equal(t, packageName, pd.Package.Name) assert.Equal(t, packageVersion, pd.Version.Version) assert.Empty(t, pd.PackageProperties.GetByName(conda_module.PropertyChannel)) }) t.Run(".conda", func(t *testing.T) { defer tests.PrintCurrentTest(t)() var infoBuf bytes.Buffer zsw, _ := zstd.NewWriter(&infoBuf) io.Copy(zsw, bytes.NewReader(tarContent)) zsw.Close() var buf bytes.Buffer zpw := zip.NewWriter(&buf) w, _ := zpw.Create("info-x.tar.zst") w.Write(infoBuf.Bytes()) zpw.Close() fullName := channel + "/" + packageName filename := fmt.Sprintf("%s-%s.conda", packageName, packageVersion) req := NewRequestWithBody(t, "PUT", root+"/"+channel+"/"+filename, bytes.NewReader(buf.Bytes())) MakeRequest(t, req, http.StatusUnauthorized) req = NewRequestWithBody(t, "PUT", root+"/"+channel+"/"+filename, bytes.NewReader(buf.Bytes())) AddBasicAuthHeader(req, user.Name) MakeRequest(t, req, http.StatusCreated) req = NewRequestWithBody(t, "PUT", root+"/"+channel+"/"+filename, bytes.NewReader(buf.Bytes())) AddBasicAuthHeader(req, user.Name) MakeRequest(t, req, http.StatusConflict) pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeConda) assert.NoError(t, err) assert.Len(t, pvs, 2) pds, err := packages.GetPackageDescriptors(db.DefaultContext, pvs) assert.NoError(t, err) assert.Condition(t, func() bool { for _, pd := range pds { if pd.Package.Name == fullName { return true } } return false }) for _, pd := range pds { if pd.Package.Name == fullName { assert.Nil(t, pd.SemVer) assert.IsType(t, &conda_module.VersionMetadata{}, pd.Metadata) assert.Equal(t, fullName, pd.Package.Name) assert.Equal(t, packageVersion, pd.Version.Version) assert.Equal(t, channel, pd.PackageProperties.GetByName(conda_module.PropertyChannel)) } } }) }) t.Run("Download", func(t *testing.T) { t.Run(".tar.bz2", func(t *testing.T) { defer tests.PrintCurrentTest(t)() req := NewRequest(t, "GET", fmt.Sprintf("%s/noarch/%s-%s-xxx.tar.bz2", root, packageName, packageVersion)) MakeRequest(t, req, http.StatusOK) req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/noarch/%s-%s-xxx.tar.bz2", root, channel, packageName, packageVersion)) MakeRequest(t, req, http.StatusNotFound) }) t.Run(".conda", func(t *testing.T) { defer tests.PrintCurrentTest(t)() req := NewRequest(t, "GET", fmt.Sprintf("%s/noarch/%s-%s-xxx.conda", root, packageName, packageVersion)) MakeRequest(t, req, http.StatusNotFound) req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/noarch/%s-%s-xxx.conda", root, channel, packageName, packageVersion)) MakeRequest(t, req, http.StatusOK) }) }) t.Run("EnumeratePackages", func(t *testing.T) { type Info struct { Subdir string `json:"subdir"` } type PackageInfo struct { Name string `json:"name"` Version string `json:"version"` NoArch string `json:"noarch"` Subdir string `json:"subdir"` Timestamp int64 `json:"timestamp"` Build string `json:"build"` BuildNumber int64 `json:"build_number"` Dependencies []string `json:"depends"` License string `json:"license"` LicenseFamily string `json:"license_family"` HashMD5 string `json:"md5"` HashSHA256 string `json:"sha256"` Size int64 `json:"size"` } type RepoData struct { Info Info `json:"info"` Packages map[string]*PackageInfo `json:"packages"` PackagesConda map[string]*PackageInfo `json:"packages.conda"` Removed map[string]*PackageInfo `json:"removed"` } req := NewRequest(t, "GET", fmt.Sprintf("%s/noarch/repodata.json", root)) resp := MakeRequest(t, req, http.StatusOK) assert.Equal(t, "application/json", resp.Header().Get("Content-Type")) req = NewRequest(t, "GET", fmt.Sprintf("%s/noarch/repodata.json.bz2", root)) resp = MakeRequest(t, req, http.StatusOK) assert.Equal(t, "application/x-bzip2", resp.Header().Get("Content-Type")) req = NewRequest(t, "GET", fmt.Sprintf("%s/noarch/current_repodata.json", root)) resp = MakeRequest(t, req, http.StatusOK) assert.Equal(t, "application/json", resp.Header().Get("Content-Type")) req = NewRequest(t, "GET", fmt.Sprintf("%s/noarch/current_repodata.json.bz2", root)) resp = MakeRequest(t, req, http.StatusOK) assert.Equal(t, "application/x-bzip2", resp.Header().Get("Content-Type")) t.Run(".tar.bz2", func(t *testing.T) { defer tests.PrintCurrentTest(t)() pv, err := packages.GetVersionByNameAndVersion(db.DefaultContext, user.ID, packages.TypeConda, packageName, packageVersion) assert.NoError(t, err) pd, err := packages.GetPackageDescriptor(db.DefaultContext, pv) assert.NoError(t, err) req := NewRequest(t, "GET", fmt.Sprintf("%s/noarch/repodata.json", root)) resp := MakeRequest(t, req, http.StatusOK) var result RepoData DecodeJSON(t, resp, &result) assert.Equal(t, "noarch", result.Info.Subdir) assert.Empty(t, result.PackagesConda) assert.Empty(t, result.Removed) filename := fmt.Sprintf("%s-%s-xxx.tar.bz2", packageName, packageVersion) assert.Contains(t, result.Packages, filename) packageInfo := result.Packages[filename] assert.Equal(t, packageName, packageInfo.Name) assert.Equal(t, packageVersion, packageInfo.Version) assert.Equal(t, "noarch", packageInfo.Subdir) assert.Equal(t, "xxx", packageInfo.Build) assert.Equal(t, pd.Files[0].Blob.HashMD5, packageInfo.HashMD5) assert.Equal(t, pd.Files[0].Blob.HashSHA256, packageInfo.HashSHA256) assert.Equal(t, pd.Files[0].Blob.Size, packageInfo.Size) }) t.Run(".conda", func(t *testing.T) { defer tests.PrintCurrentTest(t)() pv, err := packages.GetVersionByNameAndVersion(db.DefaultContext, user.ID, packages.TypeConda, channel+"/"+packageName, packageVersion) assert.NoError(t, err) pd, err := packages.GetPackageDescriptor(db.DefaultContext, pv) assert.NoError(t, err) req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/noarch/repodata.json", root, channel)) resp := MakeRequest(t, req, http.StatusOK) var result RepoData DecodeJSON(t, resp, &result) assert.Equal(t, "noarch", result.Info.Subdir) assert.Empty(t, result.Packages) assert.Empty(t, result.Removed) filename := fmt.Sprintf("%s-%s-xxx.conda", packageName, packageVersion) assert.Contains(t, result.PackagesConda, filename) packageInfo := result.PackagesConda[filename] assert.Equal(t, packageName, packageInfo.Name) assert.Equal(t, packageVersion, packageInfo.Version) assert.Equal(t, "noarch", packageInfo.Subdir) assert.Equal(t, "xxx", packageInfo.Build) assert.Equal(t, pd.Files[0].Blob.HashMD5, packageInfo.HashMD5) assert.Equal(t, pd.Files[0].Blob.HashSHA256, packageInfo.HashSHA256) assert.Equal(t, pd.Files[0].Blob.Size, packageInfo.Size) }) }) }