mirror of
https://github.com/gogrlx/snack.git
synced 2026-04-02 05:08:42 -07:00
413 lines
10 KiB
Go
413 lines
10 KiB
Go
//go:build integration
|
|
|
|
package dnf_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/gogrlx/snack"
|
|
"github.com/gogrlx/snack/dnf"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestIntegration_DNF_Detection(t *testing.T) {
|
|
d := dnf.New()
|
|
if !d.Available() {
|
|
t.Skip("dnf not available")
|
|
}
|
|
t.Logf("IsDNF5: %v", d.IsDNF5())
|
|
}
|
|
|
|
func TestIntegration_DNF(t *testing.T) {
|
|
var mgr snack.Manager = dnf.New()
|
|
if !mgr.Available() {
|
|
t.Skip("dnf not available")
|
|
}
|
|
ctx := context.Background()
|
|
|
|
assert.Equal(t, "dnf", mgr.Name())
|
|
|
|
caps := snack.GetCapabilities(mgr)
|
|
assert.True(t, caps.VersionQuery, "dnf should support VersionQuery")
|
|
assert.True(t, caps.Hold, "dnf should support Hold")
|
|
assert.True(t, caps.Clean, "dnf should support Clean")
|
|
assert.True(t, caps.FileOwnership, "dnf should support FileOwnership")
|
|
assert.True(t, caps.RepoManagement, "dnf should support RepoManagement")
|
|
assert.True(t, caps.KeyManagement, "dnf should support KeyManagement")
|
|
assert.True(t, caps.Groups, "dnf should support Groups")
|
|
assert.True(t, caps.NameNormalize, "dnf should support NameNormalize")
|
|
|
|
t.Run("Update", func(t *testing.T) {
|
|
require.NoError(t, mgr.Update(ctx))
|
|
})
|
|
|
|
t.Run("Search", func(t *testing.T) {
|
|
pkgs, err := mgr.Search(ctx, "curl")
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, pkgs)
|
|
|
|
found := false
|
|
for _, p := range pkgs {
|
|
if p.Name == "curl" {
|
|
found = true
|
|
assert.NotEmpty(t, p.Description)
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "curl should appear in search results")
|
|
})
|
|
|
|
t.Run("Search_NoResults", func(t *testing.T) {
|
|
pkgs, err := mgr.Search(ctx, "xyznonexistentpackage999")
|
|
require.NoError(t, err)
|
|
assert.Empty(t, pkgs)
|
|
})
|
|
|
|
t.Run("Info", func(t *testing.T) {
|
|
pkg, err := mgr.Info(ctx, "bash")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, pkg)
|
|
assert.Equal(t, "bash", pkg.Name)
|
|
assert.NotEmpty(t, pkg.Version)
|
|
})
|
|
|
|
t.Run("Info_NotFound", func(t *testing.T) {
|
|
_, err := mgr.Info(ctx, "xyznonexistentpackage999")
|
|
assert.Error(t, err)
|
|
assert.ErrorIs(t, err, snack.ErrNotFound)
|
|
})
|
|
|
|
t.Run("Install_Single", func(t *testing.T) {
|
|
_, _ = mgr.Remove(ctx, snack.Targets("tree"), snack.WithAssumeYes())
|
|
_, err := mgr.Install(ctx, snack.Targets("tree"), snack.WithAssumeYes())
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("IsInstalled_True", func(t *testing.T) {
|
|
installed, err := mgr.IsInstalled(ctx, "tree")
|
|
require.NoError(t, err)
|
|
assert.True(t, installed)
|
|
})
|
|
|
|
t.Run("IsInstalled_False", func(t *testing.T) {
|
|
installed, err := mgr.IsInstalled(ctx, "xyznonexistentpackage999")
|
|
require.NoError(t, err)
|
|
assert.False(t, installed)
|
|
})
|
|
|
|
t.Run("Version_Installed", func(t *testing.T) {
|
|
ver, err := mgr.Version(ctx, "tree")
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, ver)
|
|
t.Logf("tree version: %s", ver)
|
|
})
|
|
|
|
t.Run("Version_NotInstalled", func(t *testing.T) {
|
|
_, err := mgr.Version(ctx, "xyznonexistentpackage999")
|
|
assert.Error(t, err)
|
|
assert.ErrorIs(t, err, snack.ErrNotInstalled)
|
|
})
|
|
|
|
t.Run("List_ContainsInstalled", func(t *testing.T) {
|
|
pkgs, err := mgr.List(ctx)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, pkgs)
|
|
|
|
found := false
|
|
for _, p := range pkgs {
|
|
if p.Name == "tree" {
|
|
found = true
|
|
assert.NotEmpty(t, p.Version)
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "tree should be in installed list")
|
|
})
|
|
|
|
t.Run("Install_Multiple", func(t *testing.T) {
|
|
_, err := mgr.Install(ctx, snack.Targets("tree", "less"), snack.WithAssumeYes())
|
|
require.NoError(t, err)
|
|
|
|
for _, pkg := range []string{"tree", "less"} {
|
|
installed, err := mgr.IsInstalled(ctx, pkg)
|
|
require.NoError(t, err)
|
|
assert.True(t, installed, "%s should be installed", pkg)
|
|
}
|
|
})
|
|
|
|
t.Run("Install_WithRefresh", func(t *testing.T) {
|
|
_, err := mgr.Install(ctx, snack.Targets("tree"), snack.WithAssumeYes(), snack.WithRefresh())
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("Purge", func(t *testing.T) {
|
|
err := mgr.Purge(ctx, snack.Targets("less"), snack.WithAssumeYes())
|
|
require.NoError(t, err)
|
|
|
|
installed, err := mgr.IsInstalled(ctx, "less")
|
|
require.NoError(t, err)
|
|
assert.False(t, installed)
|
|
})
|
|
|
|
t.Run("Remove", func(t *testing.T) {
|
|
_, err := mgr.Remove(ctx, snack.Targets("tree"), snack.WithAssumeYes())
|
|
require.NoError(t, err)
|
|
|
|
installed, err := mgr.IsInstalled(ctx, "tree")
|
|
require.NoError(t, err)
|
|
assert.False(t, installed)
|
|
})
|
|
|
|
// --- VersionQuerier ---
|
|
t.Run("VersionQuerier", func(t *testing.T) {
|
|
vq, ok := mgr.(snack.VersionQuerier)
|
|
require.True(t, ok)
|
|
|
|
t.Run("LatestVersion", func(t *testing.T) {
|
|
ver, err := vq.LatestVersion(ctx, "curl")
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, ver)
|
|
t.Logf("curl latest: %s", ver)
|
|
})
|
|
|
|
t.Run("LatestVersion_NotFound", func(t *testing.T) {
|
|
_, err := vq.LatestVersion(ctx, "xyznonexistentpackage999")
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("ListUpgrades", func(t *testing.T) {
|
|
pkgs, err := vq.ListUpgrades(ctx)
|
|
require.NoError(t, err)
|
|
t.Logf("upgradable packages: %d", len(pkgs))
|
|
for _, p := range pkgs {
|
|
assert.NotEmpty(t, p.Name)
|
|
assert.NotEmpty(t, p.Version)
|
|
}
|
|
})
|
|
|
|
t.Run("UpgradeAvailable", func(t *testing.T) {
|
|
avail, err := vq.UpgradeAvailable(ctx, "bash")
|
|
require.NoError(t, err)
|
|
_ = avail
|
|
})
|
|
|
|
t.Run("VersionCmp", func(t *testing.T) {
|
|
// rpmdev-vercmp may not be installed; skip if not available
|
|
cmp, err := vq.VersionCmp(ctx, "1.0-1", "1.0-2")
|
|
if err != nil {
|
|
t.Skip("rpmdev-vercmp not available")
|
|
}
|
|
assert.Equal(t, -1, cmp)
|
|
|
|
cmp, err = vq.VersionCmp(ctx, "2.0-1", "1.0-1")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, cmp)
|
|
|
|
cmp, err = vq.VersionCmp(ctx, "1.0-1", "1.0-1")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, cmp)
|
|
})
|
|
})
|
|
|
|
// --- Holder ---
|
|
t.Run("Holder", func(t *testing.T) {
|
|
h, ok := mgr.(snack.Holder)
|
|
require.True(t, ok)
|
|
|
|
// Install tree for hold tests; also ensure versionlock plugin
|
|
_, _ = mgr.Install(ctx, snack.Targets("tree"), snack.WithAssumeYes())
|
|
|
|
t.Run("Hold", func(t *testing.T) {
|
|
err := h.Hold(ctx, []string{"tree"})
|
|
if err != nil {
|
|
t.Skipf("versionlock plugin not available: %v", err)
|
|
}
|
|
|
|
t.Run("IsHeld", func(t *testing.T) {
|
|
held, err := h.IsHeld(ctx, "tree")
|
|
require.NoError(t, err)
|
|
assert.True(t, held, "tree should be held")
|
|
|
|
notHeld, err := h.IsHeld(ctx, "curl")
|
|
require.NoError(t, err)
|
|
assert.False(t, notHeld, "curl should not be held")
|
|
})
|
|
|
|
t.Run("ListHeld", func(t *testing.T) {
|
|
held, err := h.ListHeld(ctx)
|
|
require.NoError(t, err)
|
|
found := false
|
|
for _, p := range held {
|
|
if p.Name == "tree" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "tree should be in held list")
|
|
})
|
|
|
|
t.Run("Unhold", func(t *testing.T) {
|
|
err := h.Unhold(ctx, []string{"tree"})
|
|
require.NoError(t, err)
|
|
})
|
|
})
|
|
|
|
_, _ = mgr.Remove(ctx, snack.Targets("tree"), snack.WithAssumeYes())
|
|
})
|
|
|
|
// --- Cleaner ---
|
|
t.Run("Cleaner", func(t *testing.T) {
|
|
cl, ok := mgr.(snack.Cleaner)
|
|
require.True(t, ok)
|
|
|
|
t.Run("Autoremove", func(t *testing.T) {
|
|
err := cl.Autoremove(ctx)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("Clean", func(t *testing.T) {
|
|
err := cl.Clean(ctx)
|
|
require.NoError(t, err)
|
|
})
|
|
})
|
|
|
|
// --- FileOwner ---
|
|
t.Run("FileOwner", func(t *testing.T) {
|
|
fo, ok := mgr.(snack.FileOwner)
|
|
require.True(t, ok)
|
|
|
|
_, _ = mgr.Install(ctx, snack.Targets("tree"), snack.WithAssumeYes())
|
|
|
|
t.Run("FileList", func(t *testing.T) {
|
|
files, err := fo.FileList(ctx, "tree")
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, files)
|
|
|
|
found := false
|
|
for _, f := range files {
|
|
if f == "/usr/bin/tree" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "/usr/bin/tree should be in file list")
|
|
})
|
|
|
|
t.Run("FileList_NotInstalled", func(t *testing.T) {
|
|
_, err := fo.FileList(ctx, "xyznonexistentpackage999")
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("Owner", func(t *testing.T) {
|
|
owner, err := fo.Owner(ctx, "/usr/bin/tree")
|
|
require.NoError(t, err)
|
|
assert.Contains(t, owner, "tree")
|
|
})
|
|
|
|
t.Run("Owner_NotFound", func(t *testing.T) {
|
|
_, err := fo.Owner(ctx, "/nonexistent/path/xyz")
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
_, _ = mgr.Remove(ctx, snack.Targets("tree"), snack.WithAssumeYes())
|
|
})
|
|
|
|
// --- RepoManager ---
|
|
t.Run("RepoManager", func(t *testing.T) {
|
|
rm, ok := mgr.(snack.RepoManager)
|
|
require.True(t, ok)
|
|
|
|
t.Run("ListRepos", func(t *testing.T) {
|
|
repos, err := rm.ListRepos(ctx)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, repos)
|
|
for _, r := range repos {
|
|
assert.NotEmpty(t, r.ID)
|
|
}
|
|
t.Logf("repos: %d", len(repos))
|
|
})
|
|
})
|
|
|
|
// --- KeyManager ---
|
|
t.Run("KeyManager", func(t *testing.T) {
|
|
km, ok := mgr.(snack.KeyManager)
|
|
require.True(t, ok)
|
|
|
|
t.Run("ListKeys", func(t *testing.T) {
|
|
keys, err := km.ListKeys(ctx)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, keys, "Fedora should have GPG keys")
|
|
t.Logf("keys: %d", len(keys))
|
|
})
|
|
})
|
|
|
|
// --- Grouper ---
|
|
t.Run("Grouper", func(t *testing.T) {
|
|
g, ok := mgr.(snack.Grouper)
|
|
require.True(t, ok)
|
|
|
|
t.Run("GroupList", func(t *testing.T) {
|
|
groups, err := g.GroupList(ctx)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, groups)
|
|
t.Logf("groups: %d", len(groups))
|
|
})
|
|
|
|
t.Run("GroupInfo", func(t *testing.T) {
|
|
// Use a group that should exist
|
|
groups, err := g.GroupList(ctx)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, groups)
|
|
|
|
pkgs, err := g.GroupInfo(ctx, groups[0])
|
|
// Some groups may not have packages queryable by name
|
|
if err == nil {
|
|
t.Logf("group %q packages: %d", groups[0], len(pkgs))
|
|
}
|
|
})
|
|
|
|
t.Run("GroupInfo_NotFound", func(t *testing.T) {
|
|
// dnf5 may return empty instead of error for unknown groups
|
|
pkgs, err := g.GroupInfo(ctx, "xyznonexistentgroup999")
|
|
if err == nil {
|
|
assert.Empty(t, pkgs)
|
|
}
|
|
})
|
|
})
|
|
|
|
// --- NameNormalizer ---
|
|
t.Run("NameNormalizer", func(t *testing.T) {
|
|
nn, ok := mgr.(snack.NameNormalizer)
|
|
require.True(t, ok)
|
|
|
|
tests := []struct {
|
|
input, wantName, wantArch string
|
|
}{
|
|
{"curl.x86_64", "curl", "x86_64"},
|
|
{"bash.aarch64", "bash", "aarch64"},
|
|
{"python3.noarch", "python3", "noarch"},
|
|
{"python3.11.x86_64", "python3.11", "x86_64"},
|
|
{"glibc", "glibc", ""},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run("NormalizeName_"+tt.input, func(t *testing.T) {
|
|
got := nn.NormalizeName(tt.input)
|
|
assert.Equal(t, tt.wantName, got)
|
|
})
|
|
t.Run("ParseArch_"+tt.input, func(t *testing.T) {
|
|
name, arch := nn.ParseArch(tt.input)
|
|
assert.Equal(t, tt.wantName, name)
|
|
assert.Equal(t, tt.wantArch, arch)
|
|
})
|
|
}
|
|
})
|
|
|
|
// --- Upgrade ---
|
|
t.Run("Upgrade", func(t *testing.T) {
|
|
err := mgr.Upgrade(ctx, snack.WithAssumeYes())
|
|
require.NoError(t, err)
|
|
})
|
|
}
|