mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-27 17:44:24 -04:00 
			
		
		
		
	enable mirror, usestdlibbars and perfsprint part of: https://github.com/go-gitea/gitea/issues/34083 --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
		
			
				
	
	
		
			337 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			337 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2023 The Gitea Authors. All rights reserved.
 | |
| // SPDX-License-Identifier: MIT
 | |
| 
 | |
| package integration
 | |
| 
 | |
| import (
 | |
| 	"archive/tar"
 | |
| 	"bytes"
 | |
| 	"compress/gzip"
 | |
| 	"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"
 | |
| 	arch_module "code.gitea.io/gitea/modules/packages/arch"
 | |
| 	arch_service "code.gitea.io/gitea/services/packages/arch"
 | |
| 	"code.gitea.io/gitea/tests"
 | |
| 
 | |
| 	"github.com/klauspost/compress/zstd"
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	"github.com/ulikunitz/xz"
 | |
| )
 | |
| 
 | |
| func TestPackageArch(t *testing.T) {
 | |
| 	defer tests.PrepareTestEnv(t)()
 | |
| 
 | |
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 | |
| 
 | |
| 	packageName := "gitea-test"
 | |
| 	packageVersion := "1.4.1-r3"
 | |
| 
 | |
| 	createPackage := func(compression, name, version, architecture string) []byte {
 | |
| 		var buf bytes.Buffer
 | |
| 		var cw io.WriteCloser
 | |
| 		switch compression {
 | |
| 		case "zst":
 | |
| 			cw, _ = zstd.NewWriter(&buf)
 | |
| 		case "xz":
 | |
| 			cw, _ = xz.NewWriter(&buf)
 | |
| 		case "gz":
 | |
| 			cw = gzip.NewWriter(&buf)
 | |
| 		}
 | |
| 		tw := tar.NewWriter(cw)
 | |
| 
 | |
| 		info := []byte(`pkgname = ` + name + `
 | |
| pkgbase = ` + name + `
 | |
| pkgver = ` + version + `
 | |
| pkgdesc = Description
 | |
| # comment
 | |
| builddate = 1678834800
 | |
| size = 8
 | |
| arch = ` + architecture + `
 | |
| license = MIT`)
 | |
| 
 | |
| 		hdr := &tar.Header{
 | |
| 			Name: ".PKGINFO",
 | |
| 			Mode: 0o600,
 | |
| 			Size: int64(len(info)),
 | |
| 		}
 | |
| 		tw.WriteHeader(hdr)
 | |
| 		tw.Write(info)
 | |
| 
 | |
| 		for _, file := range []string{"etc/dummy", "opt/file/bin"} {
 | |
| 			hdr := &tar.Header{
 | |
| 				Name: file,
 | |
| 				Mode: 0o600,
 | |
| 				Size: 4,
 | |
| 			}
 | |
| 			tw.WriteHeader(hdr)
 | |
| 			tw.Write([]byte("test"))
 | |
| 		}
 | |
| 
 | |
| 		tw.Close()
 | |
| 		cw.Close()
 | |
| 
 | |
| 		return buf.Bytes()
 | |
| 	}
 | |
| 	readIndexContent := func(r io.Reader) (map[string]string, error) {
 | |
| 		gzr, err := gzip.NewReader(r)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		content := make(map[string]string)
 | |
| 
 | |
| 		tr := tar.NewReader(gzr)
 | |
| 		for {
 | |
| 			hd, err := tr.Next()
 | |
| 			if err == io.EOF {
 | |
| 				break
 | |
| 			}
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 
 | |
| 			buf, err := io.ReadAll(tr)
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 
 | |
| 			content[hd.Name] = string(buf)
 | |
| 		}
 | |
| 
 | |
| 		return content, nil
 | |
| 	}
 | |
| 
 | |
| 	compressions := []string{"gz", "xz", "zst"}
 | |
| 	repositories := []string{"main", "testing", "with/slash", ""}
 | |
| 
 | |
| 	rootURL := fmt.Sprintf("/api/packages/%s/arch", user.Name)
 | |
| 
 | |
| 	t.Run("RepositoryKey", func(t *testing.T) {
 | |
| 		defer tests.PrintCurrentTest(t)()
 | |
| 
 | |
| 		req := NewRequest(t, "GET", rootURL+"/repository.key")
 | |
| 		resp := MakeRequest(t, req, http.StatusOK)
 | |
| 
 | |
| 		assert.Equal(t, "application/pgp-keys", resp.Header().Get("Content-Type"))
 | |
| 		assert.Contains(t, resp.Body.String(), "-----BEGIN PGP PUBLIC KEY BLOCK-----")
 | |
| 	})
 | |
| 
 | |
| 	contentAarch64Gz := createPackage("gz", packageName, packageVersion, "aarch64")
 | |
| 	for _, compression := range compressions {
 | |
| 		contentAarch64 := createPackage(compression, packageName, packageVersion, "aarch64")
 | |
| 		contentAny := createPackage(compression, packageName+"_"+arch_module.AnyArch, packageVersion, arch_module.AnyArch)
 | |
| 
 | |
| 		for _, repository := range repositories {
 | |
| 			t.Run(fmt.Sprintf("[%s,%s]", repository, compression), func(t *testing.T) {
 | |
| 				t.Run("Upload", func(t *testing.T) {
 | |
| 					defer tests.PrintCurrentTest(t)()
 | |
| 
 | |
| 					uploadURL := fmt.Sprintf("%s/%s", rootURL, repository)
 | |
| 
 | |
| 					req := NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader([]byte{}))
 | |
| 					MakeRequest(t, req, http.StatusUnauthorized)
 | |
| 
 | |
| 					req = NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader([]byte{})).
 | |
| 						AddBasicAuth(user.Name)
 | |
| 					MakeRequest(t, req, http.StatusBadRequest)
 | |
| 
 | |
| 					req = NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader(contentAarch64)).
 | |
| 						AddBasicAuth(user.Name)
 | |
| 					MakeRequest(t, req, http.StatusCreated)
 | |
| 
 | |
| 					pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeArch)
 | |
| 					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, &arch_module.VersionMetadata{}, pd.Metadata)
 | |
| 					assert.Equal(t, packageName, pd.Package.Name)
 | |
| 					assert.Equal(t, packageVersion, pd.Version.Version)
 | |
| 
 | |
| 					pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
 | |
| 					assert.NoError(t, err)
 | |
| 					assert.NotEmpty(t, pfs)
 | |
| 					assert.Condition(t, func() bool {
 | |
| 						seen := false
 | |
| 						expectedFilename := fmt.Sprintf("%s-%s-aarch64.pkg.tar.%s", packageName, packageVersion, compression)
 | |
| 						expectedCompositeKey := repository + "|aarch64"
 | |
| 						for _, pf := range pfs {
 | |
| 							if pf.Name == expectedFilename && pf.CompositeKey == expectedCompositeKey {
 | |
| 								if seen {
 | |
| 									return false
 | |
| 								}
 | |
| 								seen = true
 | |
| 
 | |
| 								assert.True(t, pf.IsLead)
 | |
| 
 | |
| 								pfps, err := packages.GetProperties(db.DefaultContext, packages.PropertyTypeFile, pf.ID)
 | |
| 								assert.NoError(t, err)
 | |
| 
 | |
| 								for _, pfp := range pfps {
 | |
| 									switch pfp.Name {
 | |
| 									case arch_module.PropertyRepository:
 | |
| 										assert.Equal(t, repository, pfp.Value)
 | |
| 									case arch_module.PropertyArchitecture:
 | |
| 										assert.Equal(t, "aarch64", pfp.Value)
 | |
| 									}
 | |
| 								}
 | |
| 							}
 | |
| 						}
 | |
| 						return seen
 | |
| 					})
 | |
| 
 | |
| 					req = NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader(contentAarch64)).
 | |
| 						AddBasicAuth(user.Name)
 | |
| 					MakeRequest(t, req, http.StatusConflict)
 | |
| 
 | |
| 					// Add same package with different compression leads to conflict
 | |
| 					req = NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader(contentAarch64Gz)).
 | |
| 						AddBasicAuth(user.Name)
 | |
| 					MakeRequest(t, req, http.StatusConflict)
 | |
| 				})
 | |
| 
 | |
| 				t.Run("Index", func(t *testing.T) {
 | |
| 					defer tests.PrintCurrentTest(t)()
 | |
| 
 | |
| 					req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/aarch64/%s", rootURL, repository, arch_service.IndexArchiveFilename))
 | |
| 					resp := MakeRequest(t, req, http.StatusOK)
 | |
| 
 | |
| 					content, err := readIndexContent(resp.Body)
 | |
| 					assert.NoError(t, err)
 | |
| 
 | |
| 					desc, has := content[fmt.Sprintf("%s-%s/desc", packageName, packageVersion)]
 | |
| 					assert.True(t, has)
 | |
| 					assert.Contains(t, desc, "%FILENAME%\n"+fmt.Sprintf("%s-%s-aarch64.pkg.tar.%s", packageName, packageVersion, compression)+"\n\n")
 | |
| 					assert.Contains(t, desc, "%NAME%\n"+packageName+"\n\n")
 | |
| 					assert.Contains(t, desc, "%VERSION%\n"+packageVersion+"\n\n")
 | |
| 					assert.Contains(t, desc, "%ARCH%\naarch64\n")
 | |
| 					assert.NotContains(t, desc, "%ARCH%\n"+arch_module.AnyArch+"\n")
 | |
| 					assert.Contains(t, desc, "%LICENSE%\nMIT\n")
 | |
| 
 | |
| 					files, has := content[fmt.Sprintf("%s-%s/files", packageName, packageVersion)]
 | |
| 					assert.True(t, has)
 | |
| 					assert.Contains(t, files, "%FILES%\netc/dummy\nopt/file/bin\n\n")
 | |
| 
 | |
| 					for _, indexFile := range []string{
 | |
| 						arch_service.IndexArchiveFilename,
 | |
| 						arch_service.IndexArchiveFilename + ".tar.gz",
 | |
| 						"index.db",
 | |
| 						"index.db.tar.gz",
 | |
| 						"index.files",
 | |
| 						"index.files.tar.gz",
 | |
| 					} {
 | |
| 						req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/aarch64/%s", rootURL, repository, indexFile))
 | |
| 						MakeRequest(t, req, http.StatusOK)
 | |
| 
 | |
| 						req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/aarch64/%s.sig", rootURL, repository, indexFile))
 | |
| 						MakeRequest(t, req, http.StatusOK)
 | |
| 					}
 | |
| 				})
 | |
| 
 | |
| 				t.Run("Download", func(t *testing.T) {
 | |
| 					defer tests.PrintCurrentTest(t)()
 | |
| 
 | |
| 					req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/aarch64/%s-%s-aarch64.pkg.tar.%s", rootURL, repository, packageName, packageVersion, compression))
 | |
| 					MakeRequest(t, req, http.StatusOK)
 | |
| 
 | |
| 					req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/aarch64/%s-%s-aarch64.pkg.tar.%s.sig", rootURL, repository, packageName, packageVersion, compression))
 | |
| 					MakeRequest(t, req, http.StatusOK)
 | |
| 				})
 | |
| 
 | |
| 				t.Run("Any", func(t *testing.T) {
 | |
| 					defer tests.PrintCurrentTest(t)()
 | |
| 
 | |
| 					req := NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/%s", rootURL, repository), bytes.NewReader(contentAny)).
 | |
| 						AddBasicAuth(user.Name)
 | |
| 					MakeRequest(t, req, http.StatusCreated)
 | |
| 
 | |
| 					req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/aarch64/%s", rootURL, repository, arch_service.IndexArchiveFilename))
 | |
| 					resp := MakeRequest(t, req, http.StatusOK)
 | |
| 
 | |
| 					content, err := readIndexContent(resp.Body)
 | |
| 					assert.NoError(t, err)
 | |
| 
 | |
| 					desc, has := content[fmt.Sprintf("%s-%s/desc", packageName, packageVersion)]
 | |
| 					assert.True(t, has)
 | |
| 					assert.Contains(t, desc, "%NAME%\n"+packageName+"\n\n")
 | |
| 					assert.Contains(t, desc, "%ARCH%\naarch64\n")
 | |
| 
 | |
| 					desc, has = content[fmt.Sprintf("%s-%s/desc", packageName+"_"+arch_module.AnyArch, packageVersion)]
 | |
| 					assert.True(t, has)
 | |
| 					assert.Contains(t, desc, "%NAME%\n"+packageName+"_any\n\n")
 | |
| 					assert.Contains(t, desc, "%ARCH%\n"+arch_module.AnyArch+"\n")
 | |
| 
 | |
| 					// "any" architecture package should be available with every architecture requested
 | |
| 					for _, arch := range []string{arch_module.AnyArch, "aarch64", "myarch"} {
 | |
| 						req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/%s-%s-any.pkg.tar.%s", rootURL, repository, arch, packageName+"_"+arch_module.AnyArch, packageVersion, compression))
 | |
| 						MakeRequest(t, req, http.StatusOK)
 | |
| 					}
 | |
| 
 | |
| 					req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s/%s/any", rootURL, repository, packageName+"_"+arch_module.AnyArch, packageVersion)).
 | |
| 						AddBasicAuth(user.Name)
 | |
| 					MakeRequest(t, req, http.StatusNoContent)
 | |
| 				})
 | |
| 
 | |
| 				t.Run("Delete", func(t *testing.T) {
 | |
| 					defer tests.PrintCurrentTest(t)()
 | |
| 
 | |
| 					req := NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s/%s/aarch64", rootURL, repository, packageName, packageVersion))
 | |
| 					MakeRequest(t, req, http.StatusUnauthorized)
 | |
| 
 | |
| 					req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s/%s/aarch64", rootURL, repository, packageName, packageVersion)).
 | |
| 						AddBasicAuth(user.Name)
 | |
| 					MakeRequest(t, req, http.StatusNoContent)
 | |
| 
 | |
| 					// Deleting the last file of an architecture should remove that index
 | |
| 					req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/aarch64/%s", rootURL, repository, arch_service.IndexArchiveFilename))
 | |
| 					MakeRequest(t, req, http.StatusNotFound)
 | |
| 				})
 | |
| 			})
 | |
| 		}
 | |
| 	}
 | |
| 	t.Run("KeepLastVersion", func(t *testing.T) {
 | |
| 		defer tests.PrintCurrentTest(t)()
 | |
| 		pkgVer1 := createPackage("gz", "gitea-test", "1.0.0", "aarch64")
 | |
| 		pkgVer2 := createPackage("gz", "gitea-test", "1.0.1", "aarch64")
 | |
| 		req := NewRequestWithBody(t, "PUT", rootURL, bytes.NewReader(pkgVer1)).
 | |
| 			AddBasicAuth(user.Name)
 | |
| 		MakeRequest(t, req, http.StatusCreated)
 | |
| 		req = NewRequestWithBody(t, "PUT", rootURL, bytes.NewReader(pkgVer2)).
 | |
| 			AddBasicAuth(user.Name)
 | |
| 		MakeRequest(t, req, http.StatusCreated)
 | |
| 
 | |
| 		req = NewRequest(t, "GET", fmt.Sprintf("%s/aarch64/%s", rootURL, arch_service.IndexArchiveFilename))
 | |
| 		resp := MakeRequest(t, req, http.StatusOK)
 | |
| 
 | |
| 		content, err := readIndexContent(resp.Body)
 | |
| 		assert.NoError(t, err)
 | |
| 		assert.Len(t, content, 2)
 | |
| 
 | |
| 		_, has := content["gitea-test-1.0.0/desc"]
 | |
| 		assert.False(t, has)
 | |
| 		_, has = content["gitea-test-1.0.1/desc"]
 | |
| 		assert.True(t, has)
 | |
| 
 | |
| 		req = NewRequest(t, "DELETE", rootURL+"/gitea-test/1.0.1/aarch64").
 | |
| 			AddBasicAuth(user.Name)
 | |
| 		MakeRequest(t, req, http.StatusNoContent)
 | |
| 
 | |
| 		req = NewRequest(t, "GET", fmt.Sprintf("%s/aarch64/%s", rootURL, arch_service.IndexArchiveFilename))
 | |
| 		resp = MakeRequest(t, req, http.StatusOK)
 | |
| 		content, err = readIndexContent(resp.Body)
 | |
| 		assert.NoError(t, err)
 | |
| 		assert.Len(t, content, 2)
 | |
| 		_, has = content["gitea-test-1.0.0/desc"]
 | |
| 		assert.True(t, has)
 | |
| 	})
 | |
| }
 |