446 lines
9.4 KiB
Go
446 lines
9.4 KiB
Go
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||
|
// SPDX-License-Identifier: MIT
|
||
|
|
||
|
package arch
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"errors"
|
||
|
"os"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
"testing/fstest"
|
||
|
"time"
|
||
|
|
||
|
"code.gitea.io/gitea/modules/packages"
|
||
|
|
||
|
"github.com/mholt/archiver/v3"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
)
|
||
|
|
||
|
func TestParsePackage(t *testing.T) {
|
||
|
// Minimal PKGINFO contents and test FS
|
||
|
const PKGINFO = `pkgname = a
|
||
|
pkgbase = b
|
||
|
pkgver = 1-2
|
||
|
arch = x86_64
|
||
|
`
|
||
|
fs := fstest.MapFS{
|
||
|
"pkginfo": &fstest.MapFile{
|
||
|
Data: []byte(PKGINFO),
|
||
|
Mode: os.ModePerm,
|
||
|
ModTime: time.Now(),
|
||
|
},
|
||
|
"mtree": &fstest.MapFile{
|
||
|
Data: []byte("data"),
|
||
|
Mode: os.ModePerm,
|
||
|
ModTime: time.Now(),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
// Test .PKGINFO file
|
||
|
pinf, err := fs.Stat("pkginfo")
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
pfile, err := fs.Open("pkginfo")
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
parcname, err := archiver.NameInArchive(pinf, ".PKGINFO", ".PKGINFO")
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
// Test .MTREE file
|
||
|
minf, err := fs.Stat("mtree")
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
mfile, err := fs.Open("mtree")
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
marcname, err := archiver.NameInArchive(minf, ".MTREE", ".MTREE")
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
t.Run("normal archive", func(t *testing.T) {
|
||
|
var buf bytes.Buffer
|
||
|
|
||
|
archive := archiver.NewTarZstd()
|
||
|
archive.Create(&buf)
|
||
|
|
||
|
err = archive.Write(archiver.File{
|
||
|
FileInfo: archiver.FileInfo{
|
||
|
FileInfo: pinf,
|
||
|
CustomName: parcname,
|
||
|
},
|
||
|
ReadCloser: pfile,
|
||
|
})
|
||
|
require.NoError(t, errors.Join(pfile.Close(), err))
|
||
|
|
||
|
err = archive.Write(archiver.File{
|
||
|
FileInfo: archiver.FileInfo{
|
||
|
FileInfo: minf,
|
||
|
CustomName: marcname,
|
||
|
},
|
||
|
ReadCloser: mfile,
|
||
|
})
|
||
|
require.NoError(t, errors.Join(mfile.Close(), archive.Close(), err))
|
||
|
|
||
|
reader, err := packages.CreateHashedBufferFromReader(&buf)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
defer reader.Close()
|
||
|
_, err = ParsePackage(reader)
|
||
|
|
||
|
require.NoError(t, err)
|
||
|
})
|
||
|
|
||
|
t.Run("missing .PKGINFO", func(t *testing.T) {
|
||
|
var buf bytes.Buffer
|
||
|
|
||
|
archive := archiver.NewTarZstd()
|
||
|
archive.Create(&buf)
|
||
|
require.NoError(t, archive.Close())
|
||
|
|
||
|
reader, err := packages.CreateHashedBufferFromReader(&buf)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
defer reader.Close()
|
||
|
_, err = ParsePackage(reader)
|
||
|
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), ".PKGINFO file not found")
|
||
|
})
|
||
|
|
||
|
t.Run("missing .MTREE", func(t *testing.T) {
|
||
|
var buf bytes.Buffer
|
||
|
|
||
|
pfile, err := fs.Open("pkginfo")
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
archive := archiver.NewTarZstd()
|
||
|
archive.Create(&buf)
|
||
|
|
||
|
err = archive.Write(archiver.File{
|
||
|
FileInfo: archiver.FileInfo{
|
||
|
FileInfo: pinf,
|
||
|
CustomName: parcname,
|
||
|
},
|
||
|
ReadCloser: pfile,
|
||
|
})
|
||
|
require.NoError(t, errors.Join(pfile.Close(), archive.Close(), err))
|
||
|
reader, err := packages.CreateHashedBufferFromReader(&buf)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
defer reader.Close()
|
||
|
_, err = ParsePackage(reader)
|
||
|
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), ".MTREE file not found")
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestParsePackageInfo(t *testing.T) {
|
||
|
const PKGINFO = `# Generated by makepkg 6.0.2
|
||
|
# using fakeroot version 1.31
|
||
|
pkgname = a
|
||
|
pkgbase = b
|
||
|
pkgver = 1-2
|
||
|
pkgdesc = comment
|
||
|
url = https://example.com/
|
||
|
group = group
|
||
|
builddate = 3
|
||
|
packager = Name Surname <login@example.com>
|
||
|
size = 5
|
||
|
arch = x86_64
|
||
|
license = BSD
|
||
|
provides = pvd
|
||
|
depend = smth
|
||
|
optdepend = hex
|
||
|
checkdepend = ola
|
||
|
makedepend = cmake
|
||
|
backup = usr/bin/paket1
|
||
|
`
|
||
|
p, err := ParsePackageInfo(strings.NewReader(PKGINFO))
|
||
|
require.NoError(t, err)
|
||
|
require.Equal(t, Package{
|
||
|
Name: "a",
|
||
|
Version: "1-2",
|
||
|
VersionMetadata: VersionMetadata{
|
||
|
Base: "b",
|
||
|
Description: "comment",
|
||
|
ProjectURL: "https://example.com/",
|
||
|
Groups: []string{"group"},
|
||
|
Provides: []string{"pvd"},
|
||
|
License: []string{"BSD"},
|
||
|
Depends: []string{"smth"},
|
||
|
OptDepends: []string{"hex"},
|
||
|
MakeDepends: []string{"cmake"},
|
||
|
CheckDepends: []string{"ola"},
|
||
|
Backup: []string{"usr/bin/paket1"},
|
||
|
},
|
||
|
FileMetadata: FileMetadata{
|
||
|
InstalledSize: 5,
|
||
|
BuildDate: 3,
|
||
|
Packager: "Name Surname <login@example.com>",
|
||
|
Arch: "x86_64",
|
||
|
},
|
||
|
}, *p)
|
||
|
}
|
||
|
|
||
|
func TestValidatePackageSpec(t *testing.T) {
|
||
|
newpkg := func() Package {
|
||
|
return Package{
|
||
|
Name: "abc",
|
||
|
Version: "1-1",
|
||
|
VersionMetadata: VersionMetadata{
|
||
|
Base: "ghx",
|
||
|
Description: "whoami",
|
||
|
ProjectURL: "https://example.com/",
|
||
|
Groups: []string{"gnome"},
|
||
|
Provides: []string{"abc", "def"},
|
||
|
License: []string{"GPL"},
|
||
|
Depends: []string{"go", "gpg=1", "curl>=3", "git<=7"},
|
||
|
OptDepends: []string{"git: something", "make"},
|
||
|
MakeDepends: []string{"chrom"},
|
||
|
CheckDepends: []string{"bariy"},
|
||
|
Backup: []string{"etc/pacman.d/filo"},
|
||
|
},
|
||
|
FileMetadata: FileMetadata{
|
||
|
CompressedSize: 1,
|
||
|
InstalledSize: 2,
|
||
|
SHA256: "def",
|
||
|
BuildDate: 3,
|
||
|
Packager: "smon",
|
||
|
Arch: "x86_64",
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
t.Run("valid package", func(t *testing.T) {
|
||
|
p := newpkg()
|
||
|
|
||
|
err := ValidatePackageSpec(&p)
|
||
|
|
||
|
require.NoError(t, err)
|
||
|
})
|
||
|
|
||
|
t.Run("invalid package name", func(t *testing.T) {
|
||
|
p := newpkg()
|
||
|
p.Name = "!$%@^!*&()"
|
||
|
|
||
|
err := ValidatePackageSpec(&p)
|
||
|
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), "invalid package name")
|
||
|
})
|
||
|
|
||
|
t.Run("invalid package base", func(t *testing.T) {
|
||
|
p := newpkg()
|
||
|
p.VersionMetadata.Base = "!$%@^!*&()"
|
||
|
|
||
|
err := ValidatePackageSpec(&p)
|
||
|
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), "invalid package base")
|
||
|
})
|
||
|
|
||
|
t.Run("invalid package version", func(t *testing.T) {
|
||
|
p := newpkg()
|
||
|
p.VersionMetadata.Base = "una-luna?"
|
||
|
|
||
|
err := ValidatePackageSpec(&p)
|
||
|
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), "invalid package base")
|
||
|
})
|
||
|
|
||
|
t.Run("invalid package version", func(t *testing.T) {
|
||
|
p := newpkg()
|
||
|
p.Version = "una-luna"
|
||
|
|
||
|
err := ValidatePackageSpec(&p)
|
||
|
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), "invalid package version")
|
||
|
})
|
||
|
|
||
|
t.Run("missing architecture", func(t *testing.T) {
|
||
|
p := newpkg()
|
||
|
p.FileMetadata.Arch = ""
|
||
|
|
||
|
err := ValidatePackageSpec(&p)
|
||
|
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), "architecture should be specified")
|
||
|
})
|
||
|
|
||
|
t.Run("invalid URL", func(t *testing.T) {
|
||
|
p := newpkg()
|
||
|
p.VersionMetadata.ProjectURL = "http%%$#"
|
||
|
|
||
|
err := ValidatePackageSpec(&p)
|
||
|
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), "invalid project URL")
|
||
|
})
|
||
|
|
||
|
t.Run("invalid check dependency", func(t *testing.T) {
|
||
|
p := newpkg()
|
||
|
p.VersionMetadata.CheckDepends = []string{"Err^_^"}
|
||
|
|
||
|
err := ValidatePackageSpec(&p)
|
||
|
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), "invalid check dependency")
|
||
|
})
|
||
|
|
||
|
t.Run("invalid dependency", func(t *testing.T) {
|
||
|
p := newpkg()
|
||
|
p.VersionMetadata.Depends = []string{"^^abc"}
|
||
|
|
||
|
err := ValidatePackageSpec(&p)
|
||
|
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), "invalid dependency")
|
||
|
})
|
||
|
|
||
|
t.Run("invalid make dependency", func(t *testing.T) {
|
||
|
p := newpkg()
|
||
|
p.VersionMetadata.MakeDepends = []string{"^m^"}
|
||
|
|
||
|
err := ValidatePackageSpec(&p)
|
||
|
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), "invalid make dependency")
|
||
|
})
|
||
|
|
||
|
t.Run("invalid provides", func(t *testing.T) {
|
||
|
p := newpkg()
|
||
|
p.VersionMetadata.Provides = []string{"^m^"}
|
||
|
|
||
|
err := ValidatePackageSpec(&p)
|
||
|
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), "invalid provides")
|
||
|
})
|
||
|
|
||
|
t.Run("invalid optional dependency", func(t *testing.T) {
|
||
|
p := newpkg()
|
||
|
p.VersionMetadata.OptDepends = []string{"^m^:MM"}
|
||
|
|
||
|
err := ValidatePackageSpec(&p)
|
||
|
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), "invalid optional dependency")
|
||
|
})
|
||
|
|
||
|
t.Run("invalid optional dependency", func(t *testing.T) {
|
||
|
p := newpkg()
|
||
|
p.VersionMetadata.Backup = []string{"/ola/cola"}
|
||
|
|
||
|
err := ValidatePackageSpec(&p)
|
||
|
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), "backup file contains leading forward slash")
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestDescString(t *testing.T) {
|
||
|
const pkgdesc = `%FILENAME%
|
||
|
zstd-1.5.5-1-x86_64.pkg.tar.zst
|
||
|
|
||
|
%NAME%
|
||
|
zstd
|
||
|
|
||
|
%BASE%
|
||
|
zstd
|
||
|
|
||
|
%VERSION%
|
||
|
1.5.5-1
|
||
|
|
||
|
%DESC%
|
||
|
Zstandard - Fast real-time compression algorithm
|
||
|
|
||
|
%GROUPS%
|
||
|
dummy1
|
||
|
dummy2
|
||
|
|
||
|
%CSIZE%
|
||
|
401
|
||
|
|
||
|
%ISIZE%
|
||
|
1500453
|
||
|
|
||
|
%MD5SUM%
|
||
|
5016660ef3d9aa148a7b72a08d3df1b2
|
||
|
|
||
|
%SHA256SUM%
|
||
|
9fa4ede47e35f5971e4f26ecadcbfb66ab79f1d638317ac80334a3362dedbabd
|
||
|
|
||
|
%URL%
|
||
|
https://facebook.github.io/zstd/
|
||
|
|
||
|
%LICENSE%
|
||
|
BSD
|
||
|
GPL2
|
||
|
|
||
|
%ARCH%
|
||
|
x86_64
|
||
|
|
||
|
%BUILDDATE%
|
||
|
1681646714
|
||
|
|
||
|
%PACKAGER%
|
||
|
Jelle van der Waa <jelle@archlinux.org>
|
||
|
|
||
|
%PROVIDES%
|
||
|
libzstd.so=1-64
|
||
|
|
||
|
%DEPENDS%
|
||
|
glibc
|
||
|
gcc-libs
|
||
|
zlib
|
||
|
xz
|
||
|
lz4
|
||
|
|
||
|
%OPTDEPENDS%
|
||
|
dummy3
|
||
|
dummy4
|
||
|
|
||
|
%MAKEDEPENDS%
|
||
|
cmake
|
||
|
gtest
|
||
|
ninja
|
||
|
|
||
|
%CHECKDEPENDS%
|
||
|
dummy5
|
||
|
dummy6
|
||
|
|
||
|
`
|
||
|
|
||
|
md := &Package{
|
||
|
Name: "zstd",
|
||
|
Version: "1.5.5-1",
|
||
|
VersionMetadata: VersionMetadata{
|
||
|
Base: "zstd",
|
||
|
Description: "Zstandard - Fast real-time compression algorithm",
|
||
|
ProjectURL: "https://facebook.github.io/zstd/",
|
||
|
Groups: []string{"dummy1", "dummy2"},
|
||
|
Provides: []string{"libzstd.so=1-64"},
|
||
|
License: []string{"BSD", "GPL2"},
|
||
|
Depends: []string{"glibc", "gcc-libs", "zlib", "xz", "lz4"},
|
||
|
OptDepends: []string{"dummy3", "dummy4"},
|
||
|
MakeDepends: []string{"cmake", "gtest", "ninja"},
|
||
|
CheckDepends: []string{"dummy5", "dummy6"},
|
||
|
},
|
||
|
FileMetadata: FileMetadata{
|
||
|
CompressedSize: 401,
|
||
|
InstalledSize: 1500453,
|
||
|
MD5: "5016660ef3d9aa148a7b72a08d3df1b2",
|
||
|
SHA256: "9fa4ede47e35f5971e4f26ecadcbfb66ab79f1d638317ac80334a3362dedbabd",
|
||
|
BuildDate: 1681646714,
|
||
|
Packager: "Jelle van der Waa <jelle@archlinux.org>",
|
||
|
Arch: "x86_64",
|
||
|
},
|
||
|
}
|
||
|
require.Equal(t, pkgdesc, md.Desc())
|
||
|
}
|