mirror of
https://github.com/gogrlx/snack.git
synced 2026-04-02 05:08:42 -07:00
test(dpkg,apk): exhaustive tests for dpkg extras, fix apk parseInfoNameVersion panic
- dpkg: add normalize_test.go with NormalizeName/ParseArch table tests - dpkg: add capabilities, DryRunner, interface compliance, parse edge cases - apk: fix parseInfoNameVersion panic on empty input - apk: add empty/whitespace test cases for parseInfoNameVersion 807 tests passing, 0 failures.
This commit is contained in:
@@ -312,6 +312,18 @@ func TestParseInfoNameVersion(t *testing.T) {
|
||||
wantN: "lib-ssl-dev",
|
||||
wantV: "3.0.0-r0",
|
||||
},
|
||||
{
|
||||
name: "empty",
|
||||
input: "",
|
||||
wantN: "",
|
||||
wantV: "",
|
||||
},
|
||||
{
|
||||
name: "whitespace_only",
|
||||
input: " ",
|
||||
wantN: "",
|
||||
wantV: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
10
apk/parse.go
10
apk/parse.go
@@ -127,10 +127,12 @@ func parseInfo(output string) *snack.Package {
|
||||
// The first line is typically "pkgname-version description".
|
||||
func parseInfoNameVersion(output string) (string, string) {
|
||||
lines := strings.Split(strings.TrimSpace(output), "\n")
|
||||
if len(lines) == 0 {
|
||||
if len(lines) == 0 || lines[0] == "" {
|
||||
return "", ""
|
||||
}
|
||||
// first line: name-version
|
||||
first := strings.Fields(lines[0])[0]
|
||||
return splitNameVersion(first)
|
||||
fields := strings.Fields(lines[0])
|
||||
if len(fields) == 0 {
|
||||
return "", ""
|
||||
}
|
||||
return splitNameVersion(fields[0])
|
||||
}
|
||||
|
||||
@@ -86,5 +86,144 @@ func TestUpdateUnsupported(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Verify Dpkg implements snack.Manager at compile time.
|
||||
var _ snack.Manager = (*Dpkg)(nil)
|
||||
// Compile-time interface assertions.
|
||||
var (
|
||||
_ snack.Manager = (*Dpkg)(nil)
|
||||
_ snack.FileOwner = (*Dpkg)(nil)
|
||||
_ snack.NameNormalizer = (*Dpkg)(nil)
|
||||
_ snack.DryRunner = (*Dpkg)(nil)
|
||||
)
|
||||
|
||||
func TestSupportsDryRun(t *testing.T) {
|
||||
d := New()
|
||||
if !d.SupportsDryRun() {
|
||||
t.Error("expected SupportsDryRun() = true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCapabilities(t *testing.T) {
|
||||
caps := snack.GetCapabilities(New())
|
||||
checks := []struct {
|
||||
name string
|
||||
got bool
|
||||
want bool
|
||||
}{
|
||||
{"FileOwnership", caps.FileOwnership, true},
|
||||
{"NameNormalize", caps.NameNormalize, true},
|
||||
{"DryRun", caps.DryRun, true},
|
||||
{"VersionQuery", caps.VersionQuery, false},
|
||||
{"Hold", caps.Hold, false},
|
||||
{"Clean", caps.Clean, false},
|
||||
{"RepoManagement", caps.RepoManagement, false},
|
||||
{"KeyManagement", caps.KeyManagement, false},
|
||||
{"Groups", caps.Groups, false},
|
||||
}
|
||||
for _, c := range checks {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
if c.got != c.want {
|
||||
t.Errorf("%s = %v, want %v", c.name, c.got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeNameMethod(t *testing.T) {
|
||||
d := New()
|
||||
if got := d.NormalizeName("curl:amd64"); got != "curl" {
|
||||
t.Errorf("NormalizeName(curl:amd64) = %q, want %q", got, "curl")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseArchMethod(t *testing.T) {
|
||||
d := New()
|
||||
name, arch := d.ParseArch("bash:arm64")
|
||||
if name != "bash" || arch != "arm64" {
|
||||
t.Errorf("ParseArch(bash:arm64) = (%q, %q), want (bash, arm64)", name, arch)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseListEdgeCases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want int
|
||||
}{
|
||||
{"empty", "", 0},
|
||||
{"whitespace_only", " \n \n ", 0},
|
||||
{"single_installed", "bash\t5.2-1\tinstall ok installed", 1},
|
||||
{"no_status_field", "bash\t5.2-1", 1},
|
||||
{"blank_lines_mixed", "\nbash\t5.2-1\tinstall ok installed\n\ncurl\t7.88\tinstall ok installed\n", 2},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
pkgs := parseList(tt.input)
|
||||
if len(pkgs) != tt.want {
|
||||
t.Errorf("parseList() returned %d packages, want %d", len(pkgs), tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseDpkgListEdgeCases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want int
|
||||
}{
|
||||
{"empty", "", 0},
|
||||
{"header_only", "Desired=Unknown/Install/Remove/Purge/Hold\n||/ Name Version Architecture Description\n+++-====-====-====-====", 0},
|
||||
{"single_package", "ii bash 5.2-1 amd64 GNU Bourne Again SHell", 1},
|
||||
{"held_package", "hi nginx 1.24 amd64 web server", 1},
|
||||
{"purge_pending", "pn oldpkg 1.0 amd64 old package", 1},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
pkgs := parseDpkgList(tt.input)
|
||||
if len(pkgs) != tt.want {
|
||||
t.Errorf("parseDpkgList() returned %d packages, want %d", len(pkgs), tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseInfoEdgeCases(t *testing.T) {
|
||||
t.Run("no_package_field", func(t *testing.T) {
|
||||
_, err := parseInfo("Version: 1.0\nArchitecture: amd64\n")
|
||||
if err != snack.ErrNotFound {
|
||||
t.Errorf("expected ErrNotFound, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("all_fields", func(t *testing.T) {
|
||||
input := "Package: vim\nStatus: install ok installed\nVersion: 9.0\nArchitecture: arm64\nDescription: Vi IMproved\n"
|
||||
p, err := parseInfo(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if p.Name != "vim" || p.Version != "9.0" || p.Arch != "arm64" || !p.Installed {
|
||||
t.Errorf("unexpected: %+v", p)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("version_with_epoch", func(t *testing.T) {
|
||||
input := "Package: systemd\nVersion: 1:252-2\n"
|
||||
p, err := parseInfo(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if p.Version != "1:252-2" {
|
||||
t.Errorf("Version = %q, want %q", p.Version, "1:252-2")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("not_installed_status", func(t *testing.T) {
|
||||
input := "Package: curl\nStatus: deinstall ok config-files\nVersion: 7.88\n"
|
||||
p, err := parseInfo(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if p.Installed {
|
||||
t.Error("expected Installed=false for deinstall status")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
74
dpkg/normalize_test.go
Normal file
74
dpkg/normalize_test.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package dpkg
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestNormalizeName(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{"curl", "curl"},
|
||||
{"curl:amd64", "curl"},
|
||||
{"bash:arm64", "bash"},
|
||||
{"python3:i386", "python3"},
|
||||
{"libc6:armhf", "libc6"},
|
||||
{"pkg:armel", "pkg"},
|
||||
{"pkg:mips", "pkg"},
|
||||
{"pkg:mipsel", "pkg"},
|
||||
{"pkg:mips64el", "pkg"},
|
||||
{"pkg:ppc64el", "pkg"},
|
||||
{"pkg:s390x", "pkg"},
|
||||
{"pkg:all", "pkg"},
|
||||
{"pkg:any", "pkg"},
|
||||
// Unknown arch suffix should be kept
|
||||
{"pkg:unknown", "pkg:unknown"},
|
||||
{"libstdc++6:amd64", "libstdc++6"},
|
||||
{"", ""},
|
||||
// Multiple colons — only last one checked
|
||||
{"a:b:amd64", "a:b"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.input, func(t *testing.T) {
|
||||
got := normalizeName(tt.input)
|
||||
if got != tt.want {
|
||||
t.Errorf("normalizeName(%q) = %q, want %q", tt.input, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseArch(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
wantName string
|
||||
wantArch string
|
||||
}{
|
||||
{"curl:amd64", "curl", "amd64"},
|
||||
{"bash:arm64", "bash", "arm64"},
|
||||
{"python3", "python3", ""},
|
||||
{"curl:i386", "curl", "i386"},
|
||||
{"pkg:armhf", "pkg", "armhf"},
|
||||
{"pkg:armel", "pkg", "armel"},
|
||||
{"pkg:mips", "pkg", "mips"},
|
||||
{"pkg:mipsel", "pkg", "mipsel"},
|
||||
{"pkg:mips64el", "pkg", "mips64el"},
|
||||
{"pkg:ppc64el", "pkg", "ppc64el"},
|
||||
{"pkg:s390x", "pkg", "s390x"},
|
||||
{"pkg:all", "pkg", "all"},
|
||||
{"pkg:any", "pkg", "any"},
|
||||
// Unknown arch — not split
|
||||
{"pkg:foobar", "pkg:foobar", ""},
|
||||
{"", "", ""},
|
||||
// Multiple colons
|
||||
{"a:b:arm64", "a:b", "arm64"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.input, func(t *testing.T) {
|
||||
name, arch := parseArch(tt.input)
|
||||
if name != tt.wantName || arch != tt.wantArch {
|
||||
t.Errorf("parseArch(%q) = (%q, %q), want (%q, %q)",
|
||||
tt.input, name, arch, tt.wantName, tt.wantArch)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user