test: harden coverage and make tests parallel-safe (#2797)

* Test coverage hardening and parallel-safe refactors

Add focused test coverage for under-tested operational paths, edge branches,
and make key table-driven suites parallel-safe. Also fixed flaky installer
test execution under repeated/parallel runs.

* chore: fix goimports ordering for golangci-lint

* Test(download): expand integration coverage for download paths

Add integration scenarios for force/pull repo updates, repo-only and skip semantics,
and direct AUR download/scanner coverage used by getpkgbuild and package preparation.

* Test(exe): relax systemd-run path assertion

Fix flaky TestBuildGitCmd by accepting an absolute systemd-run path while still
asserting the expected binary is used.

* Test(text): avoid race in color tests

Remove parallel execution from TestColorHash so global UseColor writes do not
overlap with color consumers in parallel tests and trigger race detector
failures.

* fix potential race conditions
This commit is contained in:
Jo authored and GitHub committed 2026-03-25 00:51:34 +01:00
1 parent 506d726bae
commit 3b79933638
22 files changed
+1713 -78

No files matched your search

+77
View File
@@ -0,0 +1,77 @@
//go:build !integration
// +build !integration
package main
import (
"context"
"io"
"net/http"
"strings"
"testing"
"github.com/Jguer/aur"
"github.com/stretchr/testify/require"
gock "gopkg.in/h2non/gock.v1"
"github.com/Jguer/yay/v12/pkg/db"
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
)
func TestPrintPkgbuilds(t *testing.T) {
t.Parallel()
tests := []struct {
name string
targets []string
results []aur.Pkg
wantErr bool
}{
{
name: "prints pkgbuild when package exists",
targets: []string{"aur/pkg"},
results: []aur.Pkg{{Name: "pkg", PackageBase: "pkg"}},
},
{
name: "returns error when package does not exist",
targets: []string{"aur/found", "aur/missing"},
results: nil,
wantErr: true,
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
defer gock.Off()
gock.New("https://aur.archlinux.org").
Get("/cgit/aur.git/plain/PKGBUILD").
Reply(200).
BodyString("pkgbuild")
err := printPkgbuilds(&mockDBSearcher{}, &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return tc.results, nil
},
}, &http.Client{}, text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test"),
tc.targets, parser.ModeAny, "https://aur.archlinux.org")
if tc.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
})
}
}
type mockDBSearcher struct{}
func (m *mockDBSearcher) SyncPackage(string) db.IPackage {
return nil
}
func (m *mockDBSearcher) SyncPackageFromDB(string, string) db.IPackage {
return nil
}
+83
View File
@@ -0,0 +1,83 @@
//go:build !integration
// +build !integration
package main
import (
"bytes"
"context"
"io"
"os"
"strings"
"testing"
"github.com/Jguer/aur"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/db/mock"
"github.com/Jguer/yay/v12/pkg/dep"
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
"github.com/Jguer/yay/v12/pkg/text"
)
func TestGraphPackageRequiresSingleTarget(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
grapher := dep.NewGrapher(&mock.DBExecutor{}, &mockaur.MockAUR{}, false, false, false, false, false, logger)
err := graphPackage(context.Background(), grapher, []string{"one", "two"})
require.Error(t, err)
}
func TestGraphPackage(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
grapher := dep.NewGrapher(&mock.DBExecutor{
LocalPackageFn: func(string) db.IPackage { return nil },
}, &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{
{
Name: "target",
PackageBase: "target",
Version: "1.2.3",
},
}, nil
},
}, false, false, false, false, false, logger)
output := captureStdout(t, func() {
err := graphPackage(context.Background(), grapher, []string{"target"})
require.NoError(t, err)
})
require.Contains(t, output, "digraph {")
require.Contains(t, output, "layers map")
}
func captureStdout(t *testing.T, fn func()) string {
orig := os.Stdout
r, w, err := os.Pipe()
require.NoError(t, err)
os.Stdout = w
defer func() {
os.Stdout = orig
}()
buffer := &bytes.Buffer{}
done := make(chan struct{})
go func() {
_, _ = io.Copy(buffer, r)
close(done)
}()
fn()
require.NoError(t, w.Close())
<-done
return buffer.String()
}
+200
View File
@@ -0,0 +1,200 @@
//go:build !integration
// +build !integration
package dep
import (
"context"
"io"
"strings"
"testing"
"github.com/Jguer/aur"
"github.com/Jguer/dyalpm"
gosrc "github.com/Morganamilo/go-srcinfo"
mapset "github.com/deckarep/golang-set/v2"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/db/mock"
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
"github.com/Jguer/yay/v12/pkg/query"
"github.com/Jguer/yay/v12/pkg/text"
)
func TestGrapher_GraphFromTargetsBranches(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
dbExecutor := &mock.DBExecutor{
LocalPackageFn: func(string) mock.IPackage { return nil },
SatisfierFromDBFn: func(_, db string) (mock.IPackage, error) {
return &mock.Package{
PName: "repo-pkg",
PVersion: "1",
PDB: mock.NewDB(db),
}, nil
},
PackagesFromGroupAndDBFn: func(name, dbName string) ([]mock.IPackage, error) {
return nil, nil
},
}
grapher := NewGrapher(dbExecutor, &mockaur.MockAUR{}, false, false, false, false, false, logger)
graph, err := grapher.GraphFromTargets(context.Background(), nil, []string{"core/repo-pkg"})
require.NoError(t, err)
require.True(t, graph.Exists("repo-pkg"))
}
func TestGrapher_GraphFromTargetsFallbackToGroup(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
dbExecutor := &mock.DBExecutor{
SyncSatisfierFn: func(string) mock.IPackage {
return nil
},
PackagesFromGroupFn: func(name string) []mock.IPackage {
return []mock.IPackage{
&mock.Package{
PName: "grouped",
PDB: mock.NewDB("extra"),
},
}
},
}
grapher := NewGrapher(dbExecutor, &mockaur.MockAUR{}, false, false, false, false, false, logger)
graph, err := grapher.GraphFromTargets(context.Background(), nil, []string{"grouped"})
require.NoError(t, err)
require.True(t, graph.Exists("grouped"))
require.True(t, graph.GetNodeInfo("grouped").Value.IsGroup)
}
func TestGrapher_GraphFromTargetsFallbackToAur(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
dbExecutor := &mock.DBExecutor{
SyncSatisfierFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return nil },
LocalPackageFn: func(string) mock.IPackage { return nil },
}
grapher := NewGrapher(dbExecutor, &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{{Name: "aur-target", Version: "1"}}, nil
},
}, false, false, false, false, false, logger)
graph, err := grapher.GraphFromTargets(context.Background(), nil, []string{"aur-target"})
require.NoError(t, err)
require.Equal(t, AUR, graph.GetNodeInfo("aur-target").Value.Source)
}
func TestGrapher_GraphFromAURNeededSkipsUpToDate(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
dbExecutor := &mock.DBExecutor{
LocalPackageFn: func(name string) mock.IPackage {
return &mock.Package{
PName: name,
PVersion: "2",
PReason: dyalpm.PkgReasonExplicit,
}
},
}
grapher := NewGrapher(dbExecutor, &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{{
Name: "needless",
Version: "1",
}}, nil
},
}, false, false, false, false, true, logger)
graph, err := grapher.GraphFromAUR(context.Background(), nil, []string{"needless"})
require.NoError(t, err)
require.False(t, graph.Exists("needless"))
}
func TestGrapher_GraphFromAURNotFoundReturnsErr(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
grapher := NewGrapher(&mock.DBExecutor{}, &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return nil, nil
},
}, false, false, false, false, false, logger)
graph, err := grapher.GraphFromAUR(context.Background(), nil, []string{"ghost"})
var targetNotFound *query.ErrTargetNotFound
require.ErrorAs(t, err, &targetNotFound)
require.NotNil(t, graph)
require.False(t, graph.Exists("ghost"))
}
func TestGrapher_GraphFromSrcInfosSinglePkg(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
dbExecutor := &mock.DBExecutor{
LocalPackageFn: func(string) mock.IPackage { return nil },
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
}
grapher := NewGrapher(dbExecutor, &mockaur.MockAUR{}, false, false, false, false, false, logger)
graph, err := grapher.GraphFromSrcInfos(context.Background(), nil, map[string]*gosrc.Srcinfo{
"foo": {
PackageBase: gosrc.PackageBase{
Pkgbase: "foo",
Pkgver: "1",
},
Packages: []gosrc.Package{
{
Pkgname: "foo",
},
},
},
})
require.NoError(t, err)
info := graph.GetNodeInfo("foo")
require.NotNil(t, info)
require.Equal(t, SrcInfo, info.Value.Source)
}
func TestGrapher_FindDepsFromAURSatisfiesMissingAndErrorPaths(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
dbExecutor := &mock.DBExecutor{
LocalSatisfierExistsFn: func(string) bool { return false },
SyncSatisfierFn: func(string) mock.IPackage { return nil },
}
grapher := NewGrapher(dbExecutor, &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{
{
Name: "dep-provider",
Version: "2.0",
Provides: []string{
"dep-lib",
},
},
}, nil
},
}, false, false, false, false, false, logger)
graph := NewGraph()
graph.AddNode("root")
graph.AddProvides("dep-lib", &dyalpm.Depend{Name: "dep-lib", Version: "2.0"}, "dep-provider")
deps := mapset.NewThreadUnsafeSet("dep-lib")
found := grapher.findDepsFromAUR(context.Background(), graph, "root", deps)
require.Len(t, found, 1)
}
+50 -26
View File
@@ -29,6 +29,8 @@ import (
// This validates that the RPC metadata is correctly parsed and dependencies
// are properly resolved without needing PKGBUILD parsing.
func TestGrapher_ReliableParser_AWSCliGit(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
@@ -93,36 +95,42 @@ func TestGrapher_ReliableParser_AWSCliGit(t *testing.T) {
return []aur.Pkg{}, nil
}}
t.Run("parses aws-cli-git with all its dependencies", func(t *testing.T) {
t.Run("parses aws-cli-git with all its dependencies", func(td *testing.T) {
td.Parallel()
g := NewGrapher(mockDB, mockAUR, false, true, false, false, false,
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
got, err := g.GraphFromTargets(context.Background(), nil, []string{"aws-cli-git"})
require.NoError(t, err)
require.NoError(td, err)
layers := got.TopoSortedLayers(nil)
require.NotEmpty(t, layers)
require.Contains(t, layers[0], "aws-cli-git")
require.Equal(t, "1.27.145.r11217.g5885ee4dc-1", layers[0]["aws-cli-git"].Version)
require.Equal(t, "aws-cli-git", *layers[0]["aws-cli-git"].AURBase)
require.Equal(t, Explicit, layers[0]["aws-cli-git"].Reason)
require.Equal(t, AUR, layers[0]["aws-cli-git"].Source)
require.NotEmpty(td, layers)
require.Contains(td, layers[0], "aws-cli-git")
require.Equal(td, "1.27.145.r11217.g5885ee4dc-1", layers[0]["aws-cli-git"].Version)
require.Equal(td, "aws-cli-git", *layers[0]["aws-cli-git"].AURBase)
require.Equal(td, Explicit, layers[0]["aws-cli-git"].Reason)
require.Equal(td, AUR, layers[0]["aws-cli-git"].Source)
})
t.Run("validates provides field for aws-cli", func(t *testing.T) {
t.Run("validates provides field for aws-cli", func(td *testing.T) {
td.Parallel()
g := NewGrapher(mockDB, mockAUR, false, true, false, false, false,
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
got, err := g.GraphFromTargets(context.Background(), nil, []string{"aws-cli-git"})
require.NoError(t, err)
require.NoError(td, err)
layers := got.TopoSortedLayers(nil)
require.NotEmpty(t, layers)
require.Contains(t, layers[0], "aws-cli-git")
require.NotEmpty(td, layers)
require.Contains(td, layers[0], "aws-cli-git")
})
}
// TestGrapher_ReliableSolver_LiriDesktopGit tests the dependency solver
// with complex dependency chains like liri-desktop-git metapackage.
func TestGrapher_ReliableSolver_LiriDesktopGit(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
@@ -181,11 +189,13 @@ func TestGrapher_ReliableSolver_LiriDesktopGit(t *testing.T) {
return []aur.Pkg{}, nil
}}
t.Run("liri-desktop-git pulls all dependencies", func(t *testing.T) {
t.Run("liri-desktop-git pulls all dependencies", func(td *testing.T) {
td.Parallel()
g := NewGrapher(mockDB, mockAUR, false, true, false, false, false,
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
got, err := g.GraphFromTargets(context.Background(), nil, []string{"liri-desktop-git"})
require.NoError(t, err)
require.NoError(td, err)
layers := got.TopoSortedLayers(nil)
totalPkgs := 0
@@ -193,19 +203,21 @@ func TestGrapher_ReliableSolver_LiriDesktopGit(t *testing.T) {
totalPkgs += len(layer)
}
// 6 packages: liri-desktop-git + 4 deps + liri-cmake-shared-git (makedep)
require.Equal(t, 6, totalPkgs)
require.Contains(t, layers[0], "liri-desktop-git")
require.Equal(t, Explicit, layers[0]["liri-desktop-git"].Reason)
require.Equal(td, 6, totalPkgs)
require.Contains(td, layers[0], "liri-desktop-git")
require.Equal(td, Explicit, layers[0]["liri-desktop-git"].Reason)
})
t.Run("complex dependency chain resolves in correct order", func(t *testing.T) {
t.Run("complex dependency chain resolves in correct order", func(td *testing.T) {
td.Parallel()
g := NewGrapher(mockDB, mockAUR, false, true, false, false, false,
text.NewLogger(io.Discard, io.Discard, &os.File{}, true, "test"))
got, err := g.GraphFromTargets(context.Background(), nil, []string{"liri-desktop-git"})
require.NoError(t, err)
require.NoError(td, err)
layers := got.TopoSortedLayers(nil)
require.Contains(t, layers[0], "liri-desktop-git")
require.Contains(td, layers[0], "liri-desktop-git")
allPkgs := make(map[string]bool)
for _, layer := range layers {
@@ -213,18 +225,20 @@ func TestGrapher_ReliableSolver_LiriDesktopGit(t *testing.T) {
allPkgs[pkg] = true
}
}
require.True(t, allPkgs["liri-desktop-git"])
require.True(t, allPkgs["liri-shell-git"])
require.True(t, allPkgs["liri-settings-git"])
require.True(t, allPkgs["fluid-git"])
require.True(t, allPkgs["libliri-git"])
require.True(t, allPkgs["liri-cmake-shared-git"]) // makedepend
require.True(td, allPkgs["liri-desktop-git"])
require.True(td, allPkgs["liri-shell-git"])
require.True(td, allPkgs["liri-settings-git"])
require.True(td, allPkgs["fluid-git"])
require.True(td, allPkgs["libliri-git"])
require.True(td, allPkgs["liri-cmake-shared-git"]) // makedepend
})
}
// TestGrapher_SplitPackages_Clion tests split packages where multiple packages
// come from the same package base, ensuring no rebuilding or reinstalling multiple times.
func TestGrapher_SplitPackages_Clion(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
@@ -340,6 +354,8 @@ func TestGrapher_SplitPackages_Clion(t *testing.T) {
// TestGrapher_SplitPackages_SamsungUnifiedDriver tests split packages where
// packages depend on another package from the same package base.
func TestGrapher_SplitPackages_SamsungUnifiedDriver(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
@@ -473,6 +489,8 @@ func TestGrapher_SplitPackages_SamsungUnifiedDriver(t *testing.T) {
// TestGrapher_SplitPackages_NX tests independent split packages like nxproxy and nxagent.
func TestGrapher_SplitPackages_NX(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
@@ -637,6 +655,8 @@ func TestGrapher_SplitPackages_NX(t *testing.T) {
// TestGrapher_SplitPackages_ReversedOrder tests that split packages resolve
// correctly regardless of the order they are specified in.
func TestGrapher_SplitPackages_ReversedOrder(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
@@ -696,6 +716,8 @@ func TestGrapher_SplitPackages_ReversedOrder(t *testing.T) {
// TestGrapher_MultipleInstallInfo ensures that when the same package appears as
// both explicit target and dependency, the explicit reason takes precedence.
func TestGrapher_MultipleInstallInfo(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
@@ -755,6 +777,8 @@ func TestGrapher_MultipleInstallInfo(t *testing.T) {
// TestGrapher_VersionedDependencies tests proper handling of versioned dependencies.
func TestGrapher_VersionedDependencies(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
+20
View File
@@ -47,6 +47,8 @@ func getFromFile(t testing.TB, filePath string) mockaur.GetFunc {
}
func TestGrapher_findDepsFromAUR_logsRequiredByForMissingDep(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{}
mockAUR := &mockaur.MockAUR{GetFn: func(ctx context.Context, query *aurc.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
@@ -73,6 +75,8 @@ func TestGrapher_findDepsFromAUR_logsRequiredByForMissingDep(t *testing.T) {
}
func TestGrapher_GraphFromTargets_jellyfin(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
SyncSatisfierFn: func(s string) mock.IPackage {
@@ -242,6 +246,8 @@ func TestGrapher_GraphFromTargets_jellyfin(t *testing.T) {
}
func TestGrapher_GraphProvides_androidsdk(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
SyncSatisfierFn: func(s string) mock.IPackage {
@@ -356,6 +362,8 @@ func TestGrapher_GraphProvides_androidsdk(t *testing.T) {
}
func TestGrapher_GraphFromAUR_Deps_ceph_bin(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
@@ -558,6 +566,8 @@ func TestGrapher_GraphFromAUR_Deps_ceph_bin(t *testing.T) {
}
func TestGrapher_GraphFromAUR_Deps_gourou(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
@@ -703,6 +713,8 @@ func TestGrapher_GraphFromAUR_Deps_gourou(t *testing.T) {
}
func TestGrapher_GraphFromTargets_ReinstalledDeps(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
@@ -841,6 +853,8 @@ func TestGrapher_GraphFromTargets_ReinstalledDeps(t *testing.T) {
}
func TestGrapher_GraphFromTargets_TargetNotFound(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncSatisfierFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return nil },
@@ -909,6 +923,8 @@ func TestGrapher_GraphFromTargets_TargetNotFound(t *testing.T) {
// TestGrapher_GraphFromAUR_SplitPkgInternalDeps tests split packages where
// packages from the same base depend on each other (like gstreamer-git).
func TestGrapher_GraphFromAUR_SplitPkgInternalDeps(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
@@ -1089,6 +1105,8 @@ func TestGrapher_GraphFromAUR_SplitPkgInternalDeps(t *testing.T) {
// TestGrapher_GraphFromAUR_CheckDeps tests packages with CheckDepends.
func TestGrapher_GraphFromAUR_CheckDeps(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
@@ -1193,6 +1211,8 @@ func TestGrapher_GraphFromAUR_CheckDeps(t *testing.T) {
// TestGrapher_GraphFromAUR_VirtualProvides tests packages that provide virtual packages
// (like mesa-git providing vulkan-driver, opengl-driver).
func TestGrapher_GraphFromAUR_VirtualProvides(t *testing.T) {
t.Parallel()
mockDB := &mock.DBExecutor{
SyncPackageFn: func(string) mock.IPackage { return nil },
PackagesFromGroupFn: func(string) []mock.IPackage { return []mock.IPackage{} },
+231
View File
@@ -0,0 +1,231 @@
//go:build !integration
// +build !integration
package dep
import (
"context"
"errors"
"io"
"strings"
"testing"
"github.com/Jguer/aur"
alpm "github.com/Jguer/dyalpm"
gosrc "github.com/Morganamilo/go-srcinfo"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/db"
"github.com/Jguer/yay/v12/pkg/db/mock"
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
"github.com/Jguer/yay/v12/pkg/dep/topo"
"github.com/Jguer/yay/v12/pkg/text"
)
func TestDepSplitDep(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input string
wantName string
wantMod string
wantDepVer string
}{
{name: "plain", input: "base", wantName: "base"},
{name: "greater", input: "base>=1.0", wantName: "base", wantMod: ">=", wantDepVer: "1.0"},
{name: "less", input: "base<2.0", wantName: "base", wantMod: "<", wantDepVer: "2.0"},
{name: "equal", input: "base=1", wantName: "base", wantMod: "=", wantDepVer: "1"},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
name, mod, depVer := splitDep(tc.input)
require.Equal(t, tc.wantName, name)
require.Equal(t, tc.wantMod, mod)
require.Equal(t, tc.wantDepVer, depVer)
})
}
}
func TestDepSatisfies(t *testing.T) {
t.Parallel()
require.True(t, pkgSatisfies("linux", "1.0", "linux"))
require.False(t, pkgSatisfies("linux", "1.0", "linux>=1.5"))
require.True(t, pkgSatisfies("linux", "1.7", "linux>=1.5"))
require.True(t, verSatisfies("1.5", "<=", "1.5"))
require.False(t, verSatisfies("1.4", ">=", "1.5"))
}
func TestProvideSatisfies(t *testing.T) {
t.Parallel()
require.True(t, provideSatisfies("dep=2.0", "dep>=1.0", "1.0"))
require.True(t, provideSatisfies("dep", "dep>=1.0", "2.0"))
require.False(t, provideSatisfies("dep", "dep>=3.0", "2.0"))
require.False(t, provideSatisfies("other=2.0", "dep>=1.0", "2.0"))
}
func TestSatisfiesAurProvides(t *testing.T) {
t.Parallel()
pkg := &aur.Pkg{
Name: "pkgbase",
Version: "10",
Provides: []string{
"pkgbase",
"oldpkg=1.2",
},
}
require.True(t, satisfiesAur("pkgbase>=10", pkg))
require.True(t, satisfiesAur("oldpkg=1.2", pkg))
require.False(t, satisfiesAur("oldpkg>=2", pkg))
}
func TestTargetParsing(t *testing.T) {
t.Parallel()
target := ToTarget("core/libpng>=1.6")
require.Equal(t, "core", target.DB)
require.Equal(t, "libpng", target.Name)
require.Equal(t, ">=", target.Mod)
require.Equal(t, "1.6", target.Version)
target = ToTarget("yay")
require.Equal(t, "", target.DB)
require.Equal(t, "yay", target.Name)
require.Equal(t, "", target.Mod)
require.Equal(t, "", target.Version)
}
func TestGrapher_GraphSyncPkgAndUpgrade(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
mockDb := &mock.DBExecutor{
LocalPackageFn: func(s string) mock.IPackage {
return &mock.Package{
PName: "yay",
PVersion: "1.0.0-1",
PReason: alpm.PkgReasonDepend,
PDB: mock.NewDB("core"),
}
},
}
grapher := NewGrapher(mockDb, &mockaur.MockAUR{}, false, false, false, false, false, logger)
graph := grapher.GraphSyncPkg(context.TODO(), nil, &mock.Package{
PName: "yay",
PVersion: "1.2.0",
PDB: mock.NewDB("core"),
}, nil)
info := graph.GetNodeInfo("yay").Value
require.NotNil(t, info)
require.Equal(t, Dep, info.Reason)
require.False(t, info.Upgrade)
upgraded := grapher.GraphSyncPkg(context.TODO(), nil, &mock.Package{
PName: "yaysrc",
PVersion: "2.0.0",
PDB: mock.NewDB("core"),
}, &db.SyncUpgrade{
Package: &mock.Package{
PName: "yaysrc",
PDB: mock.NewDB("core"),
},
LocalVersion: "1.0.0",
Reason: alpm.PkgReasonExplicit,
})
upgradeInfo := upgraded.GetNodeInfo("yaysrc").Value
require.NotNil(t, upgradeInfo)
require.True(t, upgradeInfo.Upgrade)
require.Equal(t, "1.0.0", upgradeInfo.LocalVersion)
}
func TestGrapher_GraphSyncGroupAndValidateNodeInfo(t *testing.T) {
t.Parallel()
grapher := NewGrapher(&mock.DBExecutor{}, &mockaur.MockAUR{}, false, false, false, false, false, text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test"))
graph := grapher.GraphSyncGroup(context.TODO(), nil, "editors", "community")
groupInfo := graph.GetNodeInfo("editors").Value
require.NotNil(t, groupInfo)
require.True(t, groupInfo.IsGroup)
require.Equal(t, "community", *groupInfo.SyncDBName)
target := "grouped"
graph.SetNodeInfo(target, &topo.NodeInfo[*InstallInfo]{Value: &InstallInfo{Reason: Explicit}})
grapher.ValidateAndSetNodeInfo(graph, target, &topo.NodeInfo[*InstallInfo]{Value: &InstallInfo{Reason: MakeDep}})
require.Equal(t, Explicit, graph.GetNodeInfo(target).Value.Reason)
graph.SetNodeInfo(target, &topo.NodeInfo[*InstallInfo]{Value: &InstallInfo{Reason: Explicit, Upgrade: true}})
grapher.ValidateAndSetNodeInfo(graph, target, &topo.NodeInfo[*InstallInfo]{Value: &InstallInfo{Reason: Explicit, Upgrade: false}})
require.True(t, graph.GetNodeInfo(target).Value.Upgrade)
}
func TestProvideMenuAndMakeAURPKGFromSrcinfo(t *testing.T) {
t.Parallel()
grapher := NewGrapher(&mock.DBExecutor{}, &mockaur.MockAUR{}, false, true, false, false, false, text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test"))
opts := []aur.Pkg{
{Name: "aur-pkg-one", Version: "1"},
{Name: "aur-pkg-two", Version: "2"},
}
require.Equal(t, "aur-pkg-one", grapher.provideMenu("dep", opts).Name)
grapherNoConfirm := NewGrapher(&mock.DBExecutor{}, &mockaur.MockAUR{}, false, false, false, false, false,
text.NewLogger(io.Discard, io.Discard, strings.NewReader("2\n"), false, "test"))
require.Equal(t, "aur-pkg-two", grapherNoConfirm.provideMenu("dep", opts).Name)
}
func TestMakeAURPKGFromSrcinfo(t *testing.T) {
t.Parallel()
assertErr := errors.New("arch error")
dbExecutor := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
}
srcinfo := &gosrc.Srcinfo{
PackageBase: gosrc.PackageBase{
Pkgbase: "yay",
},
Package: gosrc.Package{
Depends: []gosrc.ArchString{
{Arch: "x86_64", Value: "xdep"},
{Arch: "arm", Value: "ignored"},
},
},
Packages: []gosrc.Package{
{
Pkgname: "yay",
Depends: []gosrc.ArchString{
{Value: "pkgdep"},
},
},
},
}
pkgs, err := makeAURPKGFromSrcinfo(dbExecutor, srcinfo)
require.NoError(t, err)
require.Len(t, pkgs, 1)
require.Equal(t, []string{"pkgdep", "xdep"}, pkgs[0].Depends)
dbFail := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return nil, assertErr
},
}
_, err = makeAURPKGFromSrcinfo(dbFail, srcinfo)
require.Error(t, err)
}
+117
View File
@@ -0,0 +1,117 @@
//go:build !integration
// +build !integration
package topo
import (
"errors"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestGraph_AddNodeAndLenAndExists(t *testing.T) {
t.Parallel()
graph := New[string, struct{}]()
graph.AddNode("core")
graph.AddNode("extra")
graph.AddNode("core")
require.Equal(t, 2, graph.Len())
require.True(t, graph.Exists("core"))
require.False(t, graph.Exists("missing"))
}
func TestGraph_DependOnRejectsInvalidEdges(t *testing.T) {
t.Parallel()
graph := New[string, struct{}]()
require.EqualError(t, ErrSelfReferential, graph.DependOn("a", "a").Error())
require.NoError(t, graph.DependOn("a", "b"))
require.EqualError(t, ErrCircular, graph.DependOn("b", "a").Error())
require.NoError(t, graph.DependOn("c", "b"))
}
func TestGraph_ForEachAndForEachError(t *testing.T) {
t.Parallel()
graph := New[string, int]()
graph.AddNode("one")
graph.SetNodeInfo("one", &NodeInfo[int]{Value: 1})
graph.AddNode("two")
graph.SetNodeInfo("two", &NodeInfo[int]{Value: 2})
var seen []string
err := graph.ForEach(func(node string, value int) error {
seen = append(seen, node)
return nil
})
require.NoError(t, err)
require.Len(t, seen, 2)
err = graph.ForEach(func(node string, value int) error {
if node == "one" {
return errors.New("stop")
}
return nil
})
require.EqualError(t, err, "stop")
}
func TestGraph_TopoSortedLayers_WithCheckFn(t *testing.T) {
t.Parallel()
graph := New[string, int]()
graph.AddNode("root")
graph.AddNode("leaf")
require.NoError(t, graph.DependOn("leaf", "root"))
called := 0
layers := graph.TopoSortedLayers(func(node string, value int) error {
called++
return nil
})
require.Equal(t, 2, called)
require.Len(t, layers, 2)
require.Equal(t, map[string]int{"root": 0}, layers[0])
called = 0
layers = graph.TopoSortedLayers(func(node string, value int) error {
called++
if strings.Contains(node, "leaf") {
return errors.New("halt")
}
return nil
})
require.Nil(t, layers)
require.Equal(t, 2, called)
}
func TestGraph_PrunedNodes(t *testing.T) {
t.Parallel()
graph := New[string, int]()
require.NoError(t, graph.DependOn("a", "b"))
require.NoError(t, graph.DependOn("c", "a"))
pruned := graph.Prune("a")
require.Len(t, pruned, 3)
require.Equal(t, 0, graph.Len())
require.False(t, graph.Exists("a"))
require.False(t, graph.Exists("b"))
require.False(t, graph.Exists("c"))
set := make(map[string]struct{}, len(pruned))
for _, node := range pruned {
set[node] = struct{}{}
}
require.Contains(t, set, "a")
require.Contains(t, set, "b")
require.Contains(t, set, "c")
}
+115
View File
@@ -0,0 +1,115 @@
//go:build integration
// +build integration
package download
import (
"bytes"
"compress/gzip"
"context"
"io"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/settings/exe"
"github.com/Jguer/yay/v12/pkg/text"
)
// WHEN some AUR bases exist and others are fresh
// THEN AURPKGBUILDRepos returns mixed newClone flags
func TestIntegrationAURPKGBUILDReposMixedCloneAndPull(t *testing.T) {
dir := t.TempDir()
testLogger := text.NewLogger(os.Stdout, os.Stderr, strings.NewReader(""), true, "test")
cmdRunner := &exe.OSRunner{Log: testLogger}
cmdBuilder := &exe.CmdBuilder{
Runner: cmdRunner,
GitBin: "git",
GitFlags: []string{},
Log: testLogger,
}
targets := []string{"yay-bin", "yay-git"}
cloned1, err1 := AURPKGBUILDRepos(context.Background(), cmdBuilder, testLogger.Child("dl"),
targets, "https://aur.archlinux.org", dir, false)
require.NoError(t, err1)
assert.EqualValues(t, map[string]bool{"yay-bin": true, "yay-git": true}, cloned1)
cloned2, err2 := AURPKGBUILDRepos(context.Background(), cmdBuilder, testLogger.Child("dl2"),
targets, "https://aur.archlinux.org", dir, false)
require.NoError(t, err2)
assert.EqualValues(t, map[string]bool{"yay-bin": false, "yay-git": false}, cloned2)
}
func TestIntegrationGetPackageScannerGzip(t *testing.T) {
var buf bytes.Buffer
gw := gzip.NewWriter(&buf)
_, err := gw.Write([]byte("line1\nline2\n"))
require.NoError(t, err)
require.NoError(t, gw.Close())
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/packages.gz" {
http.NotFound(w, r)
return
}
w.WriteHeader(http.StatusOK)
_, _ = w.Write(buf.Bytes())
}))
t.Cleanup(srv.Close)
testLogger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
sc, err := GetPackageScanner(context.Background(), srv.Client(), srv.URL, testLogger)
require.NoError(t, err)
t.Cleanup(func() { assert.NoError(t, sc.Close()) })
var lines []string
for sc.Scan() {
lines = append(lines, sc.Text())
}
require.NoError(t, sc.Err())
assert.Equal(t, []string{"line1", "line2"}, lines)
}
func TestIntegrationGetPackageScannerRawFallback(t *testing.T) {
body := []byte("alpha\nbeta\n")
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/packages.gz" {
http.NotFound(w, r)
return
}
w.WriteHeader(http.StatusOK)
_, _ = w.Write(body)
}))
t.Cleanup(srv.Close)
testLogger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
sc, err := GetPackageScanner(context.Background(), srv.Client(), srv.URL, testLogger)
require.NoError(t, err)
t.Cleanup(func() { assert.NoError(t, sc.Close()) })
var lines []string
for sc.Scan() {
lines = append(lines, sc.Text())
}
require.NoError(t, sc.Err())
assert.Equal(t, []string{"alpha", "beta"}, lines)
}
func TestIntegrationGetPackageScannerHTTPError(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "gone", http.StatusNotFound)
}))
t.Cleanup(srv.Close)
testLogger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
sc, err := GetPackageScanner(context.Background(), srv.Client(), srv.URL, testLogger)
assert.Error(t, err)
assert.Nil(t, sc)
}
+171
View File
@@ -5,6 +5,7 @@ package download
import (
"context"
"errors"
"net/http"
"os"
"strings"
@@ -20,6 +21,19 @@ import (
"github.com/Jguer/yay/v12/pkg/text"
)
func integrationCmdBuilder(t *testing.T) (*text.Logger, *exe.CmdBuilder) {
t.Helper()
testLogger := text.NewLogger(os.Stdout, os.Stderr, strings.NewReader(""), true, "test")
cmdRunner := &exe.OSRunner{Log: testLogger}
cmdBuilder := &exe.CmdBuilder{
Runner: cmdRunner,
GitBin: "git",
GitFlags: []string{},
Log: testLogger,
}
return testLogger, cmdBuilder
}
func TestIntegrationPKGBUILDReposDefinedDBClone(t *testing.T) {
dir := t.TempDir()
@@ -104,3 +118,160 @@ func TestIntegrationPKGBUILDFull(t *testing.T) {
assert.NotEmpty(t, fetched[target])
}
}
// WHEN checkouts already exist and force is false
// THEN git pull runs and newClone is false for each target
func TestIntegrationPKGBUILDReposPullWhenExists(t *testing.T) {
dir := t.TempDir()
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{{}}, nil
},
}
targets := []string{"core/linux", "yay-bin"}
testLogger, cmdBuilder := integrationCmdBuilder(t)
searcher := &testDBSearcher{absPackagesDB: map[string]string{"linux": "core"}}
cloned1, err1 := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, testLogger.Child("test"),
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err1)
assert.EqualValues(t, map[string]bool{"core/linux": true, "yay-bin": true}, cloned1)
cloned2, err2 := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, testLogger.Child("test2"),
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err2)
assert.EqualValues(t, map[string]bool{"core/linux": false, "yay-bin": false}, cloned2)
}
// WHEN checkouts already exist and force is true
// THEN directories are removed and fresh clones return newClone true
func TestIntegrationPKGBUILDReposForceRecloneAfterClone(t *testing.T) {
dir := t.TempDir()
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{{}}, nil
},
}
targets := []string{"core/linux", "yay-bin"}
testLogger, cmdBuilder := integrationCmdBuilder(t)
searcher := &testDBSearcher{absPackagesDB: map[string]string{"linux": "core"}}
cloned1, err1 := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, testLogger.Child("test"),
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err1)
assert.EqualValues(t, map[string]bool{"core/linux": true, "yay-bin": true}, cloned1)
cloned2, err2 := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, testLogger.Child("test2"),
targets, parser.ModeAny, "https://aur.archlinux.org", dir, true)
assert.NoError(t, err2)
assert.EqualValues(t, map[string]bool{"core/linux": true, "yay-bin": true}, cloned2)
}
// WHEN mode is repo-only
// THEN only sync db targets are cloned; bare AUR names are skipped
func TestIntegrationPKGBUILDReposModeRepoOnlyRepo(t *testing.T) {
dir := t.TempDir()
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
}
targets := []string{"linux", "yay-bin", "yay-git"}
testLogger, cmdBuilder := integrationCmdBuilder(t)
searcher := &testDBSearcher{absPackagesDB: map[string]string{"linux": "core"}}
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, testLogger.Child("test"),
targets, parser.ModeRepo, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err)
assert.EqualValues(t, map[string]bool{"linux": true}, cloned)
}
// WHEN a repo/db-qualified target is wrong for the package
// THEN that target is skipped and AUR targets still clone
func TestIntegrationPKGBUILDReposWrongDBSkipsTarget(t *testing.T) {
dir := t.TempDir()
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{{}}, nil
},
}
targets := []string{"extra/yay", "yay-bin"}
testLogger, cmdBuilder := integrationCmdBuilder(t)
searcher := &testDBSearcher{absPackagesDB: map[string]string{"yay": "core"}}
cloned, err := PKGBUILDRepos(context.Background(), searcher, mockClient,
cmdBuilder, testLogger.Child("test"),
targets, parser.ModeAny, "https://aur.archlinux.org", dir, false)
assert.NoError(t, err)
assert.EqualValues(t, map[string]bool{"yay-bin": true}, cloned)
}
// WHEN mode is repo-only
// THEN PKGBUILD bytes are only fetched for repo targets
func TestIntegrationPKGBUILDsModeRepoOnlyRepo(t *testing.T) {
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
}
targets := []string{"core/linux", "yay-bin"}
testLogger, _ := integrationCmdBuilder(t)
searcher := &testDBSearcher{absPackagesDB: map[string]string{"linux": "core"}}
fetched, err := PKGBUILDs(searcher, mockClient, &http.Client{}, testLogger.Child("test"),
targets, "https://aur.archlinux.org", parser.ModeRepo)
assert.NoError(t, err)
assert.Len(t, fetched, 1)
assert.Contains(t, fetched, "core/linux")
assert.NotEmpty(t, fetched["core/linux"])
}
// WHEN AUR RPC fails for an AUR target
// THEN repo PKGBUILD still downloads and error aggregates only failed fetches
func TestIntegrationPKGBUILDsAURRPCErrorStillFetchesRepo(t *testing.T) {
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return nil, errors.New("aur rpc unavailable")
},
}
targets := []string{"core/linux", "yay-git"}
testLogger, _ := integrationCmdBuilder(t)
searcher := &testDBSearcher{absPackagesDB: map[string]string{"linux": "core"}}
fetched, err := PKGBUILDs(searcher, mockClient, &http.Client{}, testLogger.Child("test"),
targets, "https://aur.archlinux.org", parser.ModeAny)
assert.NoError(t, err)
assert.Contains(t, fetched, "core/linux")
assert.NotEmpty(t, fetched["core/linux"])
assert.NotContains(t, fetched, "yay-git")
}
// WHEN AUR returns no packages for an AUR needle
// THEN that target is skipped and repo fetch still succeeds
func TestIntegrationPKGBUILDsAUREmptySkipsAUR(t *testing.T) {
mockClient := &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
}
targets := []string{"core/linux", "yay-git"}
testLogger, _ := integrationCmdBuilder(t)
searcher := &testDBSearcher{absPackagesDB: map[string]string{"linux": "core"}}
fetched, err := PKGBUILDs(searcher, mockClient, &http.Client{}, testLogger.Child("test"),
targets, "https://aur.archlinux.org", parser.ModeAny)
assert.NoError(t, err)
assert.Contains(t, fetched, "core/linux")
assert.NotEmpty(t, fetched["core/linux"])
assert.NotContains(t, fetched, "yay-git")
}
+105
View File
@@ -0,0 +1,105 @@
//go:build !integration
// +build !integration
package menus
import (
"context"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
mapset "github.com/deckarep/golang-set/v2"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/runtime"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
)
func TestEditor(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test")
editorPath, args := editor(logger, "echo", "--help", false)
require.NotEmpty(t, editorPath)
require.Contains(t, args, "--help")
}
func TestEditPkgbuilds(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test")
pkgb := t.TempDir()
require.NoError(t, os.WriteFile(filepath.Join(pkgb, "PKGBUILD"), []byte("test"), 0o644))
require.NoError(t, os.WriteFile(filepath.Join(pkgb, "subpackage"), []byte("pkg"), 0o644))
err := editPkgbuilds(logger, map[string]string{"pkg": pkgb}, []string{"pkg"}, "echo", "", nil, false)
require.NoError(t, err)
}
func TestCleanFnNoDirs(t *testing.T) {
t.Parallel()
run := &runtime.Runtime{
Logger: text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test"),
Cfg: &settings.Configuration{},
}
require.NoError(t, CleanFn(context.Background(), run, io.Discard, map[string]string{}, mapset.NewThreadUnsafeSet[string]()))
}
func TestCleanFnWithDirs(t *testing.T) {
t.Parallel()
execCmd := &fakeCmdBuilder{}
run := &runtime.Runtime{
Logger: text.NewLogger(io.Discard, io.Discard, strings.NewReader("a\n"), false, "test"),
CmdBuilder: execCmd,
Cfg: &settings.Configuration{},
}
dirs := map[string]string{
"pkg": t.TempDir(),
}
installed := mapset.NewThreadUnsafeSet[string]()
err := CleanFn(context.Background(), run, io.Discard, dirs, installed)
require.NoError(t, err)
require.Len(t, execCmd.showCalls, 2)
require.Contains(t, strings.Join(execCmd.showCalls, " "), "reset")
}
type fakeCmdBuilder struct {
showCalls []string
}
func (f *fakeCmdBuilder) BuildGitCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd {
return exec.CommandContext(ctx, "git", extraArgs...)
}
func (f *fakeCmdBuilder) BuildPacmanCmd(ctx context.Context, args *parser.Arguments, mode parser.TargetMode, noConfirm bool) *exec.Cmd {
return exec.CommandContext(ctx, "pacman", args.FormatGlobals()...)
}
func (f *fakeCmdBuilder) BuildMakepkgCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd {
return exec.CommandContext(ctx, "makepkg", extraArgs...)
}
func (f *fakeCmdBuilder) BuildGPGCmd(ctx context.Context, extraArgs ...string) *exec.Cmd {
return exec.CommandContext(ctx, "gpg", extraArgs...)
}
func (f *fakeCmdBuilder) AddMakepkgFlag(flag string) {}
func (f *fakeCmdBuilder) GetKeepSrc() bool { return false }
func (f *fakeCmdBuilder) SudoLoop() {}
func (f *fakeCmdBuilder) Capture(cmd *exec.Cmd) (string, string, error) {
return "", "", nil
}
func (f *fakeCmdBuilder) Show(cmd *exec.Cmd) error {
f.showCalls = append(f.showCalls, cmd.String())
return nil
}
+130
View File
@@ -0,0 +1,130 @@
//go:build !integration
// +build !integration
package menus
import (
"bytes"
"context"
"io"
"os/exec"
"strings"
"testing"
mapset "github.com/deckarep/golang-set/v2"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
)
func TestSelectionMenu(t *testing.T) {
t.Parallel()
installed := mapset.NewThreadUnsafeSet[string]()
installed.Add("alpha")
basenames := []string{"alpha", "beta"}
dirs := map[string]string{
"alpha": "existing-alpha",
"beta": "missing-beta",
}
tests := []struct {
name string
input string
expected int
}{
{name: "all", input: "a\n", expected: 2},
{name: "none", input: "n\n", expected: 0},
{name: "installed only", input: "i\n", expected: 1},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
logger := text.NewLogger(&bytes.Buffer{}, &bytes.Buffer{},
strings.NewReader(tc.input), false, "test")
selected, err := selectionMenu(logger, dirs, basenames, installed, "choose", false, "", nil)
require.NoError(t, err)
require.Len(t, selected, tc.expected)
})
}
}
func TestPkgbuildNumberMenu(t *testing.T) {
t.Parallel()
var out bytes.Buffer
logger := text.NewLogger(&out, io.Discard, strings.NewReader(""), false, "test")
dirs := map[string]string{
"alpha": t.TempDir(),
"beta": t.TempDir(),
}
installed := mapset.NewThreadUnsafeSet[string]()
installed.Add("alpha")
basenames := []string{"alpha", "beta"}
pkgbuildNumberMenu(logger, dirs, basenames, installed)
require.Contains(t, out.String(), "alpha")
require.Contains(t, out.String(), "beta")
}
func TestShowPkgbuildDiffHelpers(t *testing.T) {
t.Parallel()
builder := &fakeMenusCmdBuilder{
captureFn: func(cmd *exec.Cmd) (string, string, error) {
if strings.Contains(cmd.String(), "--quiet --verify AUR_SEEN") {
return "", "", nil
}
return "deadbeef\n", "", nil
},
}
require.True(t, gitHasLastSeenRef(context.Background(), builder, "/tmp"))
ref, err := getLastSeenHash(context.Background(), builder, "/tmp")
require.NoError(t, err)
require.Equal(t, "deadbeef", ref)
}
type fakeMenusCmdBuilder struct {
captureFn func(*exec.Cmd) (string, string, error)
captureCnt int
}
func (f *fakeMenusCmdBuilder) BuildGitCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd {
return exec.CommandContext(ctx, "git", extraArgs...)
}
func (f *fakeMenusCmdBuilder) BuildMakepkgCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd {
return exec.CommandContext(ctx, "makepkg", extraArgs...)
}
func (f *fakeMenusCmdBuilder) BuildGPGCmd(ctx context.Context, extraArgs ...string) *exec.Cmd {
return exec.CommandContext(ctx, "gpg", extraArgs...)
}
func (f *fakeMenusCmdBuilder) BuildPacmanCmd(ctx context.Context, args *parser.Arguments, mode parser.TargetMode, noConfirm bool) *exec.Cmd {
return exec.CommandContext(ctx, "pacman")
}
func (f *fakeMenusCmdBuilder) AddMakepkgFlag(flag string) {}
func (f *fakeMenusCmdBuilder) GetKeepSrc() bool { return false }
func (f *fakeMenusCmdBuilder) SudoLoop() {}
func (f *fakeMenusCmdBuilder) Capture(cmd *exec.Cmd) (string, string, error) {
f.captureCnt++
if f.captureFn != nil {
return f.captureFn(cmd)
}
return "", "", nil
}
func (f *fakeMenusCmdBuilder) Show(cmd *exec.Cmd) error {
return nil
}
+26
View File
@@ -0,0 +1,26 @@
//go:build !integration
// +build !integration
package multierror
import (
"errors"
"testing"
"github.com/stretchr/testify/require"
)
func TestMultiError(t *testing.T) {
t.Parallel()
merr := &MultiError{}
require.NoError(t, merr.Return())
merr.Add(errors.New("first"))
require.Equal(t, "first", merr.Error())
require.EqualError(t, merr.Return(), "first")
merr.Add(nil)
merr.Add(errors.New("second"))
require.Equal(t, "first\nsecond", merr.Error())
}
+74
View File
@@ -0,0 +1,74 @@
//go:build !integration
// +build !integration
package exe
import (
"context"
"io"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
)
func TestBuildGitCmd(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test")
runner := &MockRunner{}
builder := &CmdBuilder{
GitBin: "git",
Runner: runner,
SudoBin: "su",
Log: logger,
}
cmd := builder.BuildGitCmd(context.Background(), "repo", "status")
if filepath.Base(cmd.Path) == "git" {
require.Equal(t, []string{"git", "-C", "repo", "status"}, cmd.Args)
require.NotContains(t, strings.Join(cmd.Env, "|"), "GIT_WORK_TREE=")
require.NotContains(t, strings.Join(cmd.Env, "|"), "GIT_DIR=")
} else {
require.Equal(t, "systemd-run", filepath.Base(cmd.Path))
}
}
func TestBuildPacmanCmd(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test")
builder := &CmdBuilder{
PacmanBin: "pacman",
PacmanConfigPath: "/etc/pacman.conf",
PacmanDBPath: t.TempDir(),
Runner: &MockRunner{},
Log: logger,
}
args := parser.MakeArguments()
args.AddArg("Q")
cmd := builder.BuildPacmanCmd(context.Background(), args, parser.ModeAny, true)
require.Equal(t, "pacman", filepath.Base(cmd.Path))
require.Contains(t, strings.Join(cmd.Args, " "), "--noconfirm")
require.Contains(t, strings.Join(cmd.Args, " "), "--config /etc/pacman.conf")
require.Contains(t, strings.Join(cmd.Args, " "), "--")
}
func TestBuildPrivilegeElevatorCommand(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test")
builder := &CmdBuilder{
SudoBin: "su",
Runner: &MockRunner{},
Log: logger,
}
cmd := builder.buildPrivilegeElevatorCommand(context.Background(), []string{"echo", "hello"})
require.Equal(t, "su", filepath.Base(cmd.Path))
require.Equal(t, []string{"su", "-c", "echo hello"}, cmd.Args)
}
+33
View File
@@ -0,0 +1,33 @@
//go:build !integration
// +build !integration
package exe
import (
"context"
"io"
"os/exec"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/text"
)
func TestOSRunnerCapture(t *testing.T) {
t.Parallel()
runner := NewOSRunner(text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test"))
cmd := exec.CommandContext(context.Background(), "sh", "-c", "printf 'ok'; printf 'err' >&2")
out, errOut, err := runner.Capture(cmd)
require.NoError(t, err)
require.Equal(t, "ok", out)
require.Empty(t, errOut)
cmdFail := exec.CommandContext(context.Background(), "sh", "-c", "exit 1")
_, stderr, err := runner.Capture(cmdFail)
require.Error(t, err)
require.Empty(t, stderr)
}
+19
View File
@@ -88,7 +88,9 @@ func TestInstaller_InstallNeeded(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.desc, func(td *testing.T) {
td.Parallel()
tmpDir := td.TempDir()
pkgTar := tmpDir + "/yay-91.0.0-1-x86_64.pkg.tar.zst"
@@ -451,6 +453,7 @@ func TestInstaller_InstallMixedSourcesAndLayers(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.desc, func(td *testing.T) {
pkgTar := tmpDir + "/yay-91.0.0-1-x86_64.pkg.tar.zst"
jfinPkgTar := tmpDirJfin + "/jellyfin-server-10.8.8-1-x86_64.pkg.tar.zst"
@@ -655,7 +658,9 @@ func TestInstaller_CompileFailed(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.desc, func(td *testing.T) {
td.Parallel()
pkgTar := tmpDir + "/yay-91.0.0-1-x86_64.pkg.tar.zst"
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
@@ -811,7 +816,9 @@ func TestInstaller_InstallSplitPackage(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.desc, func(td *testing.T) {
td.Parallel()
pkgTars := []string{
tmpDir + "/jellyfin-10.8.4-1-x86_64.pkg.tar.zst",
tmpDir + "/jellyfin-web-10.8.4-1-x86_64.pkg.tar.zst",
@@ -946,7 +953,9 @@ func TestInstaller_InstallDownloadOnly(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.desc, func(td *testing.T) {
td.Parallel()
tmpDir := td.TempDir()
pkgTar := tmpDir + "/yay-91.0.0-1-x86_64.pkg.tar.zst"
@@ -1070,7 +1079,9 @@ func TestInstaller_InstallGroup(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.desc, func(td *testing.T) {
td.Parallel()
tmpDir := td.TempDir()
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
@@ -1266,7 +1277,9 @@ func TestInstaller_InstallRebuild(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.desc, func(td *testing.T) {
td.Parallel()
tmpDir := td.TempDir()
pkgTar := tmpDir + "/yay-91.0.0-1-x86_64.pkg.tar.zst"
@@ -1383,7 +1396,9 @@ func TestInstaller_InstallUpgrade(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.desc, func(td *testing.T) {
td.Parallel()
mockDB := &mock.DBExecutor{}
mockRunner := &exe.MockRunner{}
cmdBuilder := &exe.CmdBuilder{
@@ -1479,7 +1494,9 @@ func TestInstaller_KeepSrc(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.desc, func(td *testing.T) {
td.Parallel()
tmpDir := td.TempDir()
pkgTar := tmpDir + "/yay-92.0.0-1-x86_64.pkg.tar.zst"
@@ -1619,7 +1636,9 @@ func TestInstaller_InstallAsExplicit(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.desc, func(td *testing.T) {
td.Parallel()
pkgTar := tmpDir + "/yay-91.0.0-1-x86_64.pkg.tar.zst"
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
+114
View File
@@ -0,0 +1,114 @@
//go:build !integration
// +build !integration
package workdir
import (
"context"
"os/exec"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/runtime"
"github.com/Jguer/yay/v12/pkg/settings"
"github.com/Jguer/yay/v12/pkg/settings/parser"
"github.com/Jguer/yay/v12/pkg/text"
)
func TestMergePkgbuilds(t *testing.T) {
t.Parallel()
builder := &fakeWorkdirCmdBuilder{}
dirs := map[string]string{
"one": "/tmp/pkg-one",
"two": "/tmp/pkg-two",
}
err := mergePkgbuilds(context.Background(), builder, dirs)
require.NoError(t, err)
require.Len(t, builder.captureCalls, 4)
require.True(t, strings.Contains(builder.captureCalls[0], "reset"))
require.True(t, strings.Contains(builder.captureCalls[1], "merge"))
}
func TestCleanAfter(t *testing.T) {
t.Parallel()
builder := &fakeWorkdirCmdBuilder{}
run := &runtime.Runtime{
Logger: text.NewLogger(&strings.Builder{}, &strings.Builder{}, strings.NewReader(""), false, "test"),
CmdBuilder: builder,
}
dirs := map[string]string{
"one": "/tmp/pkg-one",
"two": "/tmp/pkg-two",
}
cleanAfter(context.Background(), run, builder, dirs)
require.Len(t, builder.captureCalls, 2)
require.Len(t, builder.showCalls, 2)
require.True(t, strings.Contains(builder.captureCalls[0], "reset"))
require.True(t, strings.Contains(builder.showCalls[0], "clean"))
}
func TestRemoveMake(t *testing.T) {
var old bool
old, settings.NoConfirm = settings.NoConfirm, false
defer func() { settings.NoConfirm = old }()
builder := &fakeWorkdirCmdBuilder{}
args := parser.MakeArguments()
args.AddArg("Q")
_ = removeMake(context.Background(), &settings.Configuration{}, builder, []string{"foo", "bar"}, args)
require.Len(t, builder.showCalls, 1)
require.Contains(t, builder.showCalls[0], "pacman")
require.Contains(t, builder.showCalls[0], "-R")
require.Contains(t, builder.showCalls[0], "foo")
require.Contains(t, builder.showCalls[0], "bar")
require.True(t, strings.Contains(builder.showCalls[0], "--noconfirm"))
}
type fakeWorkdirCmdBuilder struct {
captureCalls []string
showCalls []string
}
func (f *fakeWorkdirCmdBuilder) BuildGitCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd {
return exec.CommandContext(ctx, "git", extraArgs...)
}
func (f *fakeWorkdirCmdBuilder) BuildPacmanCmd(ctx context.Context, args *parser.Arguments, mode parser.TargetMode, noConfirm bool) *exec.Cmd {
cmdArgs := append([]string{"pacman"}, args.FormatGlobals()...)
cmdArgs = append(cmdArgs, args.FormatArgs()...)
cmdArgs = append(cmdArgs, args.Targets...)
if noConfirm {
cmdArgs = append(cmdArgs, "--noconfirm")
}
return exec.CommandContext(ctx, cmdArgs[0], cmdArgs[1:]...)
}
func (f *fakeWorkdirCmdBuilder) BuildMakepkgCmd(ctx context.Context, dir string, extraArgs ...string) *exec.Cmd {
return exec.CommandContext(ctx, "makepkg", extraArgs...)
}
func (f *fakeWorkdirCmdBuilder) BuildGPGCmd(ctx context.Context, extraArgs ...string) *exec.Cmd {
return exec.CommandContext(ctx, "gpg", extraArgs...)
}
func (f *fakeWorkdirCmdBuilder) AddMakepkgFlag(string) {}
func (f *fakeWorkdirCmdBuilder) GetKeepSrc() bool { return false }
func (f *fakeWorkdirCmdBuilder) SudoLoop() {}
func (f *fakeWorkdirCmdBuilder) Capture(cmd *exec.Cmd) (string, string, error) {
f.captureCalls = append(f.captureCalls, cmd.String())
return "", "", nil
}
func (f *fakeWorkdirCmdBuilder) Show(cmd *exec.Cmd) error {
f.showCalls = append(f.showCalls, cmd.String())
return nil
}
+25
View File
@@ -0,0 +1,25 @@
//go:build !integration
// +build !integration
package text
import (
"testing"
"github.com/stretchr/testify/require"
)
// TestColorHash is intentionally not parallel because it mutates the
// package-level UseColor variable. No other parallel test in this
// package reads UseColor, so sequential execution is sufficient.
func TestColorHash(t *testing.T) {
original := UseColor
defer func() { UseColor = original }()
UseColor = true
require.Equal(t, ColorHash("core"), ColorHash("core"))
require.NotEqual(t, ColorHash("core"), ColorHash("extra"))
UseColor = false
require.Equal(t, "core", ColorHash("core"))
}
+2 -52
View File
@@ -81,11 +81,10 @@ func TestContinueTask(t *testing.T) {
}
}
func TestContinueTaskRU(t *testing.T) {
func TestContinueTaskLocalized(t *testing.T) {
strCustom := `
msgid "yes"
msgstr "да"
`
msgstr "да"`
// Create Locales directory and files on temp location
tmpDir := t.TempDir()
@@ -129,55 +128,6 @@ msgstr "да"
gotext.SetLanguage("")
}
func TestContinueTaskDE(t *testing.T) {
strCustom := `
msgid "yes"
msgstr "ja"
`
// Create Locales directory and files on temp location
tmpDir := t.TempDir()
dirname := path.Join(tmpDir, "en_US")
err := os.MkdirAll(dirname, os.ModePerm)
require.NoError(t, err)
fDefault, err := os.Create(path.Join(dirname, "yay.po"))
require.NoError(t, err)
defer fDefault.Close()
_, err = fDefault.WriteString(strCustom)
require.NoError(t, err)
gotext.Configure(tmpDir, "en_US", "yay")
require.Equal(t, "ja", gotext.Get("yes"))
type args struct {
s string
preset bool
noConfirm bool
input string
}
tests := []struct {
name string
args args
want bool
}{
{name: "default input false", args: args{s: "", input: "n", preset: true, noConfirm: false}, want: false},
{name: "default input true", args: args{s: "", input: "y", preset: false, noConfirm: false}, want: true},
{name: "custom input true", args: args{s: "", input: "j", preset: false, noConfirm: false}, want: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
in := strings.NewReader(tt.args.input)
logger := NewLogger(io.Discard, io.Discard, in, false, "test")
got := logger.ContinueTask(tt.args.s, tt.args.preset, tt.args.noConfirm)
require.Equal(t, tt.want, got)
})
}
gotext.SetLanguage("")
}
func TestCreateRepoLink(t *testing.T) {
tests := []struct {
name string
+26
View File
@@ -0,0 +1,26 @@
//go:build !integration
// +build !integration
package text
import (
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestFormatTime(t *testing.T) {
t.Parallel()
require.Equal(t, "1970-01-01", FormatTime(0))
now := time.Unix(0, 0)
require.Equal(t, now.Format("2006-01-02"), FormatTime(0))
}
func TestFormatTimeQuery(t *testing.T) {
t.Parallel()
now := time.Unix(0, 0)
require.Equal(t, now.Format("Mon 02 Jan 2006 03:04:05 PM MST"), FormatTimeQuery(0))
}
+37
View File
@@ -0,0 +1,37 @@
//go:build !integration
// +build !integration
package text
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestSplitDBFromName(t *testing.T) {
t.Parallel()
db, pkg := SplitDBFromName("core/pkg")
require.Equal(t, "core", db)
require.Equal(t, "pkg", pkg)
db, pkg = SplitDBFromName("pkg")
require.Equal(t, "", db)
require.Equal(t, "pkg", pkg)
}
func TestCreateOSC8Link(t *testing.T) {
t.Parallel()
got := CreateOSC8Link("https://example.com", "text")
require.Equal(t, "\033]8;;https://example.com\033\\text\033]8;;\033\\", got)
}
func TestHumanReadable(t *testing.T) {
t.Parallel()
require.Equal(t, "10.0 B", Human(10))
require.Equal(t, "1.0 KiB", Human(1024))
require.Equal(t, "1.5 KiB", Human(1536))
}
+29
View File
@@ -0,0 +1,29 @@
//go:build !integration
// +build !integration
package main
import (
"testing"
"github.com/Jguer/aur"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v12/pkg/dep"
)
func TestInfoToInstallInfo(t *testing.T) {
info := infoToInstallInfo([]aur.Pkg{
{Name: "foo", PackageBase: "foo-base"},
{Name: "bar", PackageBase: "bar-base"},
})
require.Len(t, info, 1)
require.Len(t, info[0], 2)
require.Equal(t, &dep.InstallInfo{AURBase: ptr("foo-base"), Source: dep.AUR}, info[0]["foo"])
require.Equal(t, &dep.InstallInfo{AURBase: ptr("bar-base"), Source: dep.AUR}, info[0]["bar"])
}
func ptr(s string) *string {
return &s
}
+29
View File
@@ -0,0 +1,29 @@
//go:build !integration
// +build !integration
package main
import (
"context"
"io"
"strings"
"testing"
"github.com/Jguer/aur"
"github.com/stretchr/testify/require"
mockaur "github.com/Jguer/yay/v12/pkg/dep/mock"
"github.com/Jguer/yay/v12/pkg/text"
)
func TestHandlePackageVoteNoResults(t *testing.T) {
t.Parallel()
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test")
err := handlePackageVote(context.Background(), []string{"missing"}, &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return nil, nil
},
}, logger, nil, true)
require.NoError(t, err)
}