Files
yay/sync_test.go
Jo 15057c8dab misc(ci): add modernize linter and update versioning (#2870)
* add modernize linter

* lint unused

* update imports

* ignore agent info
2026-06-17 21:09:53 +02:00

880 lines
24 KiB
Go

//go:build !integration
package main
import (
"context"
"errors"
"fmt"
"io"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"strings"
"sync"
"testing"
"time"
"github.com/Jguer/aur"
alpm "github.com/Jguer/dyalpm"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/Jguer/yay/v13/pkg/db"
"github.com/Jguer/yay/v13/pkg/db/mock"
mockaur "github.com/Jguer/yay/v13/pkg/dep/mock"
"github.com/Jguer/yay/v13/pkg/runtime"
"github.com/Jguer/yay/v13/pkg/settings"
"github.com/Jguer/yay/v13/pkg/settings/exe"
"github.com/Jguer/yay/v13/pkg/settings/parser"
"github.com/Jguer/yay/v13/pkg/text"
"github.com/Jguer/yay/v13/pkg/vcs"
)
func TestSyncUpgrade(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
gitBin := t.TempDir() + "/git"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return "", "", nil
}
showOverride := func(cmd *exec.Cmd) error {
return nil
}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
PacmanConfigPath: "/etc/pacman.conf",
GitBin: "git",
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg("S")
cmdArgs.AddArg("y")
cmdArgs.AddArg("u")
dbName := mock.NewDB("core")
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
RefreshHandleFn: func() error {
return nil
},
ReposFn: func() []string {
return []string{"core"}
},
InstalledRemotePackagesFn: func() map[string]alpm.Package {
return map[string]alpm.Package{}
},
InstalledRemotePackageNamesFn: func() []string {
return []string{}
},
SyncUpgradesFn: func(
bool,
) (map[string]db.SyncUpgrade, error) {
return map[string]db.SyncUpgrade{
"linux": {
Package: &mock.Package{
PName: "linux",
PVersion: "5.10.0",
PDB: dbName,
},
LocalVersion: "4.3.0",
Reason: alpm.PkgReasonExplicit,
},
}, nil
},
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
},
Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("\n"), true, "test"),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
err = handleCmd(context.Background(), run, cmdArgs, db)
require.NoError(t, err)
wantCapture := []string{}
wantShow := []string{
"pacman -S -y --config /etc/pacman.conf --",
"pacman -S -y -u --config /etc/pacman.conf --",
}
require.Len(t, mockRunner.ShowCalls, len(wantShow))
require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
remainingWantShow := wantShow
for i, call := range mockRunner.ShowCalls {
if len(remainingWantShow) == 0 {
t.Fatalf("unexpected number of show commands: got %d, want %d", i+1, len(wantShow))
}
wantShowValue := remainingWantShow[0]
remainingWantShow = remainingWantShow[1:]
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "git")
// options are in a different order on different systems and on CI root user is used
assert.Subset(t, strings.Split(show, " "), strings.Split(wantShowValue, " "), fmt.Sprintf("%d - %s", i, show))
}
}
func TestSyncUpgrade_IgnoreAll(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
gitBin := t.TempDir() + "/git"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return "", "", nil
}
showOverride := func(cmd *exec.Cmd) error {
return nil
}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
PacmanConfigPath: "/etc/pacman.conf",
GitBin: "git",
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg("S")
cmdArgs.AddArg("y")
cmdArgs.AddArg("u")
dbName := mock.NewDB("core")
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
RefreshHandleFn: func() error {
return nil
},
ReposFn: func() []string {
return []string{"core"}
},
InstalledRemotePackagesFn: func() map[string]alpm.Package {
return map[string]alpm.Package{}
},
InstalledRemotePackageNamesFn: func() []string {
return []string{}
},
SyncUpgradesFn: func(
bool,
) (map[string]db.SyncUpgrade, error) {
return map[string]db.SyncUpgrade{
"linux": {
Package: &mock.Package{
PName: "linux",
PVersion: "5.10.0",
PDB: dbName,
},
LocalVersion: "4.3.0",
Reason: alpm.PkgReasonExplicit,
},
}, nil
},
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
},
Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test"),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
err = handleCmd(context.Background(), run, cmdArgs, db)
require.NoError(t, err)
wantCapture := []string{}
wantShow := []string{
"pacman -S -y --config /etc/pacman.conf --",
}
require.Len(t, mockRunner.ShowCalls, len(wantShow))
require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
remainingWantShow := wantShow
for i, call := range mockRunner.ShowCalls {
if len(remainingWantShow) == 0 {
t.Fatalf("unexpected number of show commands: got %d, want %d", i+1, len(wantShow))
}
wantShowValue := remainingWantShow[0]
remainingWantShow = remainingWantShow[1:]
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "git")
// options are in a different order on different systems and on CI root user is used
assert.Subset(t, strings.Split(show, " "), strings.Split(wantShowValue, " "), fmt.Sprintf("%d - %s", i, show))
}
}
func TestSyncUpgrade_WaitsForCompletionCache(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir()
makepkgBin := tmpDir + "/makepkg"
pacmanBin := tmpDir + "/pacman"
gitBin := tmpDir + "/git"
for _, path := range []string{makepkgBin, pacmanBin, gitBin} {
f, err := os.OpenFile(path, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/packages.gz", r.URL.Path)
_, err := io.WriteString(w, "# Comment\nrepo-pkg\n")
require.NoError(t, err)
}))
defer server.Close()
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg("S")
cmdArgs.AddArg("y")
cmdArgs.AddArg("u")
showOverride := func(cmd *exec.Cmd) error {
return nil
}
mockRunner := &exe.MockRunner{CaptureFn: func(cmd *exec.Cmd) (string, string, error) {
return "", "", nil
}, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
PacmanConfigPath: "/etc/pacman.conf",
GitBin: gitBin,
Runner: mockRunner,
SudoLoopEnabled: false,
}
dbName := mock.NewDB("core")
syncPackagesEntered := make(chan struct{})
releaseSyncPackages := make(chan struct{})
var started sync.Once
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
RefreshHandleFn: func() error {
return nil
},
ReposFn: func() []string {
return []string{"core"}
},
InstalledRemotePackagesFn: func() map[string]alpm.Package {
return map[string]alpm.Package{}
},
InstalledRemotePackageNamesFn: func() []string {
return []string{}
},
SyncPackagesFn: func(...string) []mock.IPackage {
started.Do(func() {
close(syncPackagesEntered)
})
<-releaseSyncPackages
return []mock.IPackage{
&mock.Package{PName: "repo-pkg", PDB: dbName},
}
},
SyncUpgradesFn: func(bool) (map[string]db.SyncUpgrade, error) {
return map[string]db.SyncUpgrade{
"linux": {
Package: &mock.Package{
PName: "linux",
PVersion: "5.10.0",
PDB: dbName,
},
LocalVersion: "4.3.0",
Reason: alpm.PkgReasonExplicit,
},
}, nil
},
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
AURURL: server.URL,
CompletionInterval: 7,
CompletionPath: tmpDir + "/completion",
RemoveMake: "no",
},
Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("\n"), true, "test"),
CmdBuilder: cmdBuilder,
HTTPClient: server.Client(),
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
runFinished := make(chan error, 1)
go func() {
runFinished <- handleCmd(context.Background(), run, cmdArgs, db)
}()
select {
case <-syncPackagesEntered:
case <-time.After(2 * time.Second):
t.Fatal("completion cache refresh did not reach SyncPackages")
}
select {
case err := <-runFinished:
t.Fatalf("handleCmd returned before completion cache finished: %v", err)
case <-time.After(100 * time.Millisecond):
}
close(releaseSyncPackages)
select {
case err := <-runFinished:
require.NoError(t, err)
case <-time.After(2 * time.Second):
t.Fatal("handleCmd did not return after completion cache finished")
}
}
func TestSyncUpgrade_IgnoreOne(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
gitBin := t.TempDir() + "/git"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return "", "", nil
}
showOverride := func(cmd *exec.Cmd) error {
return nil
}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
PacmanConfigPath: "/etc/pacman.conf",
GitBin: "git",
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg("S")
cmdArgs.AddArg("y")
cmdArgs.AddArg("u")
dbName := mock.NewDB("core")
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
RefreshHandleFn: func() error {
return nil
},
ReposFn: func() []string {
return []string{"core"}
},
InstalledRemotePackagesFn: func() map[string]alpm.Package {
return map[string]alpm.Package{}
},
InstalledRemotePackageNamesFn: func() []string {
return []string{}
},
SyncUpgradesFn: func(
bool,
) (map[string]db.SyncUpgrade, error) {
return map[string]db.SyncUpgrade{
"gcc": {
Package: &mock.Package{
PName: "gcc",
PVersion: "6.0.0",
PDB: dbName,
},
LocalVersion: "5.0.0",
Reason: alpm.PkgReasonExplicit,
},
"linux": {
Package: &mock.Package{
PName: "linux",
PVersion: "5.10.0",
PDB: dbName,
},
LocalVersion: "4.3.0",
Reason: alpm.PkgReasonExplicit,
},
"linux-headers": {
Package: &mock.Package{
PName: "linux-headers",
PVersion: "5.10.0",
PDB: dbName,
},
LocalVersion: "4.3.0",
Reason: alpm.PkgReasonDepend,
},
}, nil
},
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
},
Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test"),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
err = handleCmd(context.Background(), run, cmdArgs, db)
require.NoError(t, err)
wantCapture := []string{}
wantShow := []string{
"pacman -S -y --config /etc/pacman.conf --",
"pacman -S -y -u --config /etc/pacman.conf --ignore linux-headers --",
}
require.Len(t, mockRunner.ShowCalls, len(wantShow))
require.Len(t, mockRunner.CaptureCalls, len(wantCapture))
for i, call := range mockRunner.ShowCalls {
require.Less(t, i, len(wantShow), "unexpected number of show commands")
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "git")
// options are in a different order on different systems and on CI root user is used
assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
}
}
// Pinned deps with rollup
func TestSyncUpgradeAURPinnedSplitPackage(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
tmpDir := t.TempDir()
gitBin := t.TempDir() + "/git"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
pkgBuildDir := tmpDir + "/vosk-api"
os.Mkdir(pkgBuildDir, 0o755)
fSource, err := os.OpenFile(pkgBuildDir+"/.SRCINFO", os.O_RDWR|os.O_CREATE, 0o666)
require.NoError(t, err)
n, errF := fSource.WriteString(`pkgbase = vosk-api
pkgdesc = Offline speech recognition toolkit
pkgver = 0.3.45
pkgrel = 1
url = https://alphacephei.com/vosk/
arch = x86_64
license = Apache
pkgname = vosk-api
pkgdesc = vosk-api
pkgname = python-vosk
pkgdesc = Python module for vosk-api
depends = vosk-api=0.3.45`)
require.NoError(t, errF)
require.Greater(t, n, 0)
require.NoError(t, fSource.Close())
tars := []string{
tmpDir + "/vosk-api-0.3.45-1-x86_64.pkg.tar.zst",
tmpDir + "/python-vosk-0.3.45-1-x86_64.pkg.tar.zst",
}
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return strings.Join(tars, "\n"), "", nil
}
once := sync.Once{}
showOverride := func(cmd *exec.Cmd) error {
once.Do(func() {
for _, tar := range tars {
f, err := os.OpenFile(tar, os.O_RDONLY|os.O_CREATE, 0o666)
require.NoError(t, err)
require.NoError(t, f.Close())
}
})
if sanitizeCall(cmd.String(), tmpDir, makepkgBin,
pacmanBin, gitBin) == "pacman -U --config /etc/pacman.conf -- /testdir/vosk-api-0.3.45-1-x86_64.pkg.tar.zst" {
return errors.New("Unsatisfied dependency")
}
return nil
}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
PacmanConfigPath: "/etc/pacman.conf",
GitBin: "git",
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg("S")
cmdArgs.AddArg("y")
cmdArgs.AddArg("u")
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
RefreshHandleFn: func() error {
return nil
},
ReposFn: func() []string {
return []string{"core"}
},
SyncSatisfierFn: func(s string) mock.IPackage {
return nil
},
InstalledRemotePackagesFn: func() map[string]alpm.Package {
return map[string]alpm.Package{
"vosk-api": &mock.Package{
PName: "vosk-api",
PVersion: "0.3.43-1",
PBase: "vosk-api",
PReason: alpm.PkgReasonDepend,
},
"python-vosk": &mock.Package{
PName: "python-vosk",
PVersion: "0.3.43-1",
PBase: "python-vosk",
PReason: alpm.PkgReasonExplicit,
// TODO: fix mock Depends
},
}
},
InstalledRemotePackageNamesFn: func() []string {
return []string{"vosk-api", "python-vosk"}
},
LocalSatisfierExistsFn: func(s string) bool {
return false
},
SyncUpgradesFn: func(
bool,
) (map[string]db.SyncUpgrade, error) {
return map[string]db.SyncUpgrade{}, nil
},
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
DoubleConfirm: true,
RemoveMake: "no",
BuildDir: tmpDir,
},
Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("\n\n\n\n"), true, "test"),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{
{
Name: "vosk-api",
PackageBase: "vosk-api",
Version: "0.3.45-1",
},
{
Name: "python-vosk",
PackageBase: "vosk-api",
Version: "0.3.45-1",
Depends: []string{
"vosk-api=0.3.45",
},
},
}, nil
},
},
}
err = handleCmd(context.Background(), run, cmdArgs, db)
require.NoError(t, err)
wantCapture := []string{
"/usr/bin/git -C /testdir/vosk-api reset --hard HEAD",
"/usr/bin/git -C /testdir/vosk-api merge --no-edit --ff",
"makepkg --packagelist",
"makepkg --packagelist",
}
wantShow := []string{
"pacman -S -y --config /etc/pacman.conf --",
"makepkg --verifysource --skippgpcheck -f -Cc", "makepkg --nobuild -f -C --ignorearch",
"makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/vosk-api-0.3.45-1-x86_64.pkg.tar.zst",
"makepkg --nobuild -f -C --ignorearch", "makepkg -c --nobuild --noextract --ignorearch",
"pacman -U --config /etc/pacman.conf -- /testdir/vosk-api-0.3.45-1-x86_64.pkg.tar.zst /testdir/python-vosk-0.3.45-1-x86_64.pkg.tar.zst",
"pacman -D -q --asdeps --config /etc/pacman.conf -- vosk-api",
"pacman -D -q --asexplicit --config /etc/pacman.conf -- python-vosk",
}
require.Len(t, mockRunner.ShowCalls, len(wantShow),
fmt.Sprintf("%#v", sanitizeCalls(mockRunner.ShowCalls, tmpDir, makepkgBin, pacmanBin, gitBin)))
require.Len(t, mockRunner.CaptureCalls, len(wantCapture),
fmt.Sprintf("%#v", sanitizeCalls(mockRunner.CaptureCalls, tmpDir, makepkgBin, pacmanBin, gitBin)))
for i, call := range mockRunner.ShowCalls {
require.Less(t, i, len(wantShow), "unexpected number of show commands")
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, tmpDir, "/testdir") // replace the temp dir with a static path
show = strings.ReplaceAll(show, makepkgBin, "makepkg")
show = strings.ReplaceAll(show, pacmanBin, "pacman")
show = strings.ReplaceAll(show, gitBin, "git")
// options are in a different order on different systems and on CI root user is used
assert.Subset(t, strings.Split(show, " "), strings.Split(wantShow[i], " "), fmt.Sprintf("%d - %s", i, show))
}
}
func sanitizeCalls(calls []exe.Call, tmpDir, makepkg, pacman, git string) []string {
san := make([]string, 0, len(calls))
for _, c := range calls {
s := c.Args[0].(*exec.Cmd).String()
san = append(san, sanitizeCall(s, tmpDir, makepkg, pacman, git))
}
return san
}
func sanitizeCall(s, tmpDir, makepkg, pacman, git string) string {
_, after, found := strings.Cut(s, makepkg)
if found {
s = "makepkg" + after
}
_, after, found = strings.Cut(s, pacman)
if found {
s = "pacman" + after
}
_, after, found = strings.Cut(s, git)
if found {
s = "git" + after
}
s = strings.ReplaceAll(s, tmpDir, "/testdir")
return s
}
func TestSyncUpgrade_NoCombinedUpgrade(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
combinedUpgrade bool
want []string
}{
{
name: "combined upgrade",
combinedUpgrade: true,
want: []string{"pacman -S -y -u --config /etc/pacman.conf --"},
},
{
name: "no combined upgrade",
combinedUpgrade: false,
want: []string{"pacman -S -y --config /etc/pacman.conf --"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
makepkgBin := t.TempDir() + "/makepkg"
pacmanBin := t.TempDir() + "/pacman"
gitBin := t.TempDir() + "/git"
f, err := os.OpenFile(makepkgBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(pacmanBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
f, err = os.OpenFile(gitBin, os.O_RDONLY|os.O_CREATE, 0o755)
require.NoError(t, err)
require.NoError(t, f.Close())
captureOverride := func(cmd *exec.Cmd) (stdout string, stderr string, err error) {
return "", "", nil
}
showOverride := func(cmd *exec.Cmd) error {
return nil
}
mockRunner := &exe.MockRunner{CaptureFn: captureOverride, ShowFn: showOverride}
cmdBuilder := &exe.CmdBuilder{
MakepkgBin: makepkgBin,
SudoBin: "su",
PacmanBin: pacmanBin,
PacmanConfigPath: "/etc/pacman.conf",
GitBin: "git",
Runner: mockRunner,
SudoLoopEnabled: false,
}
cmdArgs := parser.MakeArguments()
cmdArgs.AddArg("S")
cmdArgs.AddArg("y")
cmdArgs.AddArg("u")
db := &mock.DBExecutor{
AlpmArchitecturesFn: func() ([]string, error) {
return []string{"x86_64"}, nil
},
RefreshHandleFn: func() error {
return nil
},
ReposFn: func() []string {
return []string{"core"}
},
InstalledRemotePackagesFn: func() map[string]alpm.Package {
return map[string]alpm.Package{}
},
InstalledRemotePackageNamesFn: func() []string {
return []string{}
},
SyncUpgradesFn: func(
bool,
) (map[string]db.SyncUpgrade, error) {
return map[string]db.SyncUpgrade{}, nil
},
}
run := &runtime.Runtime{
Cfg: &settings.Configuration{
RemoveMake: "no",
CombinedUpgrade: false,
},
Logger: text.NewLogger(io.Discard, os.Stderr, strings.NewReader("1\n"), true, "test"),
CmdBuilder: cmdBuilder,
VCSStore: &vcs.Mock{},
AURClient: &mockaur.MockAUR{
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
return []aur.Pkg{}, nil
},
},
}
err = handleCmd(context.Background(), run, cmdArgs, db)
require.NoError(t, err)
require.Len(t, mockRunner.ShowCalls, len(tc.want))
require.Len(t, mockRunner.CaptureCalls, 0)
for i, call := range mockRunner.ShowCalls {
require.Less(t, i, len(tc.want), "unexpected number of show commands")
show := call.Args[0].(*exec.Cmd).String()
show = strings.ReplaceAll(show, pacmanBin, "pacman")
// options are in a different order on different systems and on CI root user is used
assert.Subset(t, strings.Split(show, " "), strings.Split(tc.want[i], " "), fmt.Sprintf("%d - %s", i, show))
}
})
}
}