code.gitea.io/gitea@v1.22.3/tests/integration/api_packages_conda_test.go (about)

     1  // Copyright 2022 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package integration
     5  
     6  import (
     7  	"archive/tar"
     8  	"archive/zip"
     9  	"bytes"
    10  	"fmt"
    11  	"io"
    12  	"net/http"
    13  	"testing"
    14  
    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  	conda_module "code.gitea.io/gitea/modules/packages/conda"
    20  	"code.gitea.io/gitea/tests"
    21  
    22  	"github.com/dsnet/compress/bzip2"
    23  	"github.com/klauspost/compress/zstd"
    24  	"github.com/stretchr/testify/assert"
    25  )
    26  
    27  func TestPackageConda(t *testing.T) {
    28  	defer tests.PrepareTestEnv(t)()
    29  
    30  	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
    31  
    32  	packageName := "test_package"
    33  	packageVersion := "1.0.1"
    34  
    35  	channel := "test-channel"
    36  	root := fmt.Sprintf("/api/packages/%s/conda", user.Name)
    37  
    38  	t.Run("Upload", func(t *testing.T) {
    39  		tarContent := func() []byte {
    40  			var buf bytes.Buffer
    41  			tw := tar.NewWriter(&buf)
    42  
    43  			content := []byte(`{"name":"` + packageName + `","version":"` + packageVersion + `","subdir":"noarch","build":"xxx"}`)
    44  
    45  			hdr := &tar.Header{
    46  				Name: "info/index.json",
    47  				Mode: 0o600,
    48  				Size: int64(len(content)),
    49  			}
    50  			tw.WriteHeader(hdr)
    51  			tw.Write(content)
    52  			tw.Close()
    53  			return buf.Bytes()
    54  		}()
    55  
    56  		t.Run(".tar.bz2", func(t *testing.T) {
    57  			defer tests.PrintCurrentTest(t)()
    58  
    59  			var buf bytes.Buffer
    60  			bw, _ := bzip2.NewWriter(&buf, nil)
    61  			io.Copy(bw, bytes.NewReader(tarContent))
    62  			bw.Close()
    63  
    64  			filename := fmt.Sprintf("%s-%s.tar.bz2", packageName, packageVersion)
    65  
    66  			req := NewRequestWithBody(t, "PUT", root+"/"+filename, bytes.NewReader(buf.Bytes()))
    67  			MakeRequest(t, req, http.StatusUnauthorized)
    68  
    69  			req = NewRequestWithBody(t, "PUT", root+"/"+filename, bytes.NewReader(buf.Bytes())).
    70  				AddBasicAuth(user.Name)
    71  			MakeRequest(t, req, http.StatusCreated)
    72  
    73  			req = NewRequestWithBody(t, "PUT", root+"/"+filename, bytes.NewReader(buf.Bytes())).
    74  				AddBasicAuth(user.Name)
    75  			MakeRequest(t, req, http.StatusConflict)
    76  
    77  			pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeConda)
    78  			assert.NoError(t, err)
    79  			assert.Len(t, pvs, 1)
    80  
    81  			pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
    82  			assert.NoError(t, err)
    83  			assert.Nil(t, pd.SemVer)
    84  			assert.IsType(t, &conda_module.VersionMetadata{}, pd.Metadata)
    85  			assert.Equal(t, packageName, pd.Package.Name)
    86  			assert.Equal(t, packageVersion, pd.Version.Version)
    87  			assert.Empty(t, pd.PackageProperties.GetByName(conda_module.PropertyChannel))
    88  		})
    89  
    90  		t.Run(".conda", func(t *testing.T) {
    91  			defer tests.PrintCurrentTest(t)()
    92  
    93  			var infoBuf bytes.Buffer
    94  			zsw, _ := zstd.NewWriter(&infoBuf)
    95  			io.Copy(zsw, bytes.NewReader(tarContent))
    96  			zsw.Close()
    97  
    98  			var buf bytes.Buffer
    99  			zpw := zip.NewWriter(&buf)
   100  			w, _ := zpw.Create("info-x.tar.zst")
   101  			w.Write(infoBuf.Bytes())
   102  			zpw.Close()
   103  
   104  			fullName := channel + "/" + packageName
   105  			filename := fmt.Sprintf("%s-%s.conda", packageName, packageVersion)
   106  
   107  			req := NewRequestWithBody(t, "PUT", root+"/"+channel+"/"+filename, bytes.NewReader(buf.Bytes()))
   108  			MakeRequest(t, req, http.StatusUnauthorized)
   109  
   110  			req = NewRequestWithBody(t, "PUT", root+"/"+channel+"/"+filename, bytes.NewReader(buf.Bytes())).
   111  				AddBasicAuth(user.Name)
   112  			MakeRequest(t, req, http.StatusCreated)
   113  
   114  			req = NewRequestWithBody(t, "PUT", root+"/"+channel+"/"+filename, bytes.NewReader(buf.Bytes())).
   115  				AddBasicAuth(user.Name)
   116  			MakeRequest(t, req, http.StatusConflict)
   117  
   118  			pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeConda)
   119  			assert.NoError(t, err)
   120  			assert.Len(t, pvs, 2)
   121  
   122  			pds, err := packages.GetPackageDescriptors(db.DefaultContext, pvs)
   123  			assert.NoError(t, err)
   124  
   125  			assert.Condition(t, func() bool {
   126  				for _, pd := range pds {
   127  					if pd.Package.Name == fullName {
   128  						return true
   129  					}
   130  				}
   131  				return false
   132  			})
   133  
   134  			for _, pd := range pds {
   135  				if pd.Package.Name == fullName {
   136  					assert.Nil(t, pd.SemVer)
   137  					assert.IsType(t, &conda_module.VersionMetadata{}, pd.Metadata)
   138  					assert.Equal(t, fullName, pd.Package.Name)
   139  					assert.Equal(t, packageVersion, pd.Version.Version)
   140  					assert.Equal(t, channel, pd.PackageProperties.GetByName(conda_module.PropertyChannel))
   141  				}
   142  			}
   143  		})
   144  	})
   145  
   146  	t.Run("Download", func(t *testing.T) {
   147  		t.Run(".tar.bz2", func(t *testing.T) {
   148  			defer tests.PrintCurrentTest(t)()
   149  
   150  			req := NewRequest(t, "GET", fmt.Sprintf("%s/noarch/%s-%s-xxx.tar.bz2", root, packageName, packageVersion))
   151  			MakeRequest(t, req, http.StatusOK)
   152  
   153  			req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/noarch/%s-%s-xxx.tar.bz2", root, channel, packageName, packageVersion))
   154  			MakeRequest(t, req, http.StatusNotFound)
   155  		})
   156  
   157  		t.Run(".conda", func(t *testing.T) {
   158  			defer tests.PrintCurrentTest(t)()
   159  
   160  			req := NewRequest(t, "GET", fmt.Sprintf("%s/noarch/%s-%s-xxx.conda", root, packageName, packageVersion))
   161  			MakeRequest(t, req, http.StatusNotFound)
   162  
   163  			req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/noarch/%s-%s-xxx.conda", root, channel, packageName, packageVersion))
   164  			MakeRequest(t, req, http.StatusOK)
   165  		})
   166  	})
   167  
   168  	t.Run("EnumeratePackages", func(t *testing.T) {
   169  		type Info struct {
   170  			Subdir string `json:"subdir"`
   171  		}
   172  
   173  		type PackageInfo struct {
   174  			Name          string   `json:"name"`
   175  			Version       string   `json:"version"`
   176  			NoArch        string   `json:"noarch"`
   177  			Subdir        string   `json:"subdir"`
   178  			Timestamp     int64    `json:"timestamp"`
   179  			Build         string   `json:"build"`
   180  			BuildNumber   int64    `json:"build_number"`
   181  			Dependencies  []string `json:"depends"`
   182  			License       string   `json:"license"`
   183  			LicenseFamily string   `json:"license_family"`
   184  			HashMD5       string   `json:"md5"`
   185  			HashSHA256    string   `json:"sha256"`
   186  			Size          int64    `json:"size"`
   187  		}
   188  
   189  		type RepoData struct {
   190  			Info          Info                    `json:"info"`
   191  			Packages      map[string]*PackageInfo `json:"packages"`
   192  			PackagesConda map[string]*PackageInfo `json:"packages.conda"`
   193  			Removed       map[string]*PackageInfo `json:"removed"`
   194  		}
   195  
   196  		req := NewRequest(t, "GET", fmt.Sprintf("%s/noarch/repodata.json", root))
   197  		resp := MakeRequest(t, req, http.StatusOK)
   198  		assert.Equal(t, "application/json", resp.Header().Get("Content-Type"))
   199  
   200  		req = NewRequest(t, "GET", fmt.Sprintf("%s/noarch/repodata.json.bz2", root))
   201  		resp = MakeRequest(t, req, http.StatusOK)
   202  		assert.Equal(t, "application/x-bzip2", resp.Header().Get("Content-Type"))
   203  
   204  		req = NewRequest(t, "GET", fmt.Sprintf("%s/noarch/current_repodata.json", root))
   205  		resp = MakeRequest(t, req, http.StatusOK)
   206  		assert.Equal(t, "application/json", resp.Header().Get("Content-Type"))
   207  
   208  		req = NewRequest(t, "GET", fmt.Sprintf("%s/noarch/current_repodata.json.bz2", root))
   209  		resp = MakeRequest(t, req, http.StatusOK)
   210  		assert.Equal(t, "application/x-bzip2", resp.Header().Get("Content-Type"))
   211  
   212  		t.Run(".tar.bz2", func(t *testing.T) {
   213  			defer tests.PrintCurrentTest(t)()
   214  
   215  			pv, err := packages.GetVersionByNameAndVersion(db.DefaultContext, user.ID, packages.TypeConda, packageName, packageVersion)
   216  			assert.NoError(t, err)
   217  
   218  			pd, err := packages.GetPackageDescriptor(db.DefaultContext, pv)
   219  			assert.NoError(t, err)
   220  
   221  			req := NewRequest(t, "GET", fmt.Sprintf("%s/noarch/repodata.json", root))
   222  			resp := MakeRequest(t, req, http.StatusOK)
   223  
   224  			var result RepoData
   225  			DecodeJSON(t, resp, &result)
   226  
   227  			assert.Equal(t, "noarch", result.Info.Subdir)
   228  			assert.Empty(t, result.PackagesConda)
   229  			assert.Empty(t, result.Removed)
   230  
   231  			filename := fmt.Sprintf("%s-%s-xxx.tar.bz2", packageName, packageVersion)
   232  			assert.Contains(t, result.Packages, filename)
   233  			packageInfo := result.Packages[filename]
   234  			assert.Equal(t, packageName, packageInfo.Name)
   235  			assert.Equal(t, packageVersion, packageInfo.Version)
   236  			assert.Equal(t, "noarch", packageInfo.Subdir)
   237  			assert.Equal(t, "xxx", packageInfo.Build)
   238  			assert.Equal(t, pd.Files[0].Blob.HashMD5, packageInfo.HashMD5)
   239  			assert.Equal(t, pd.Files[0].Blob.HashSHA256, packageInfo.HashSHA256)
   240  			assert.Equal(t, pd.Files[0].Blob.Size, packageInfo.Size)
   241  		})
   242  
   243  		t.Run(".conda", func(t *testing.T) {
   244  			defer tests.PrintCurrentTest(t)()
   245  
   246  			pv, err := packages.GetVersionByNameAndVersion(db.DefaultContext, user.ID, packages.TypeConda, channel+"/"+packageName, packageVersion)
   247  			assert.NoError(t, err)
   248  
   249  			pd, err := packages.GetPackageDescriptor(db.DefaultContext, pv)
   250  			assert.NoError(t, err)
   251  
   252  			req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/noarch/repodata.json", root, channel))
   253  			resp := MakeRequest(t, req, http.StatusOK)
   254  
   255  			var result RepoData
   256  			DecodeJSON(t, resp, &result)
   257  
   258  			assert.Equal(t, "noarch", result.Info.Subdir)
   259  			assert.Empty(t, result.Packages)
   260  			assert.Empty(t, result.Removed)
   261  
   262  			filename := fmt.Sprintf("%s-%s-xxx.conda", packageName, packageVersion)
   263  			assert.Contains(t, result.PackagesConda, filename)
   264  			packageInfo := result.PackagesConda[filename]
   265  			assert.Equal(t, packageName, packageInfo.Name)
   266  			assert.Equal(t, packageVersion, packageInfo.Version)
   267  			assert.Equal(t, "noarch", packageInfo.Subdir)
   268  			assert.Equal(t, "xxx", packageInfo.Build)
   269  			assert.Equal(t, pd.Files[0].Blob.HashMD5, packageInfo.HashMD5)
   270  			assert.Equal(t, pd.Files[0].Blob.HashSHA256, packageInfo.HashSHA256)
   271  			assert.Equal(t, pd.Files[0].Blob.Size, packageInfo.Size)
   272  		})
   273  	})
   274  }