package packagequery import "testing" // TestParseAptPolicyVersionOutput verifies Candidate-line extraction from // `apt-cache policy` output (captured under LC_ALL=C, so labels are English // even on localized systems). func TestParseAptPolicyVersionOutput(t *testing.T) { tests := []struct { name string out string wantVersion string wantErr bool }{ { name: "installed and candidate identical", out: `mpv: Installed: 0.35.1-1 Candidate: 0.35.1-1 Version table: *** 0.35.1-1 500 500 http://deb.debian.org/debian bookworm/main amd64 Packages 100 /var/lib/dpkg/status `, wantVersion: "0.35.1-1", }, { name: "newer candidate available", out: `mpv: Installed: 0.35.1-1 Candidate: 0.36.0-1 Version table: 0.36.0-1 500 500 http://deb.debian.org/debian bookworm-backports/main amd64 Packages *** 0.35.1-1 100 100 /var/lib/dpkg/status `, wantVersion: "0.36.0-1", }, { name: "epoch version preserved", out: `mpv: Installed: (none) Candidate: 1:0.41.0-2 Version table: 1:0.41.0-2 500 500 http://archive.ubuntu.com/ubuntu noble/universe amd64 Packages `, wantVersion: "1:0.41.0-2", }, { name: "no candidate available", out: `somepackage: Installed: (none) Candidate: (none) Version table: `, wantVersion: "", }, { name: "unknown package prints nothing", out: "", wantErr: true, }, { name: "unparseable output", out: `N: Unable to locate package `, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { version, err := parseAptPolicyOutput(tt.out) if (err != nil) != tt.wantErr { t.Fatalf("parseAptPolicyOutput() error = %v, wantErr %v", err, tt.wantErr) } if version != tt.wantVersion { t.Errorf("parseAptPolicyOutput() = %q, want %q", version, tt.wantVersion) } }) } } // TestParseDnfInfoVersionOutput verifies Version-field extraction from // `dnf info --available` output (captured under LC_ALL=C), including // multi-stanza output when several repos carry the package. func TestParseDnfInfoVersionOutput(t *testing.T) { tests := []struct { name string out string wantVersion string wantErr bool }{ { name: "single available stanza", out: `Last metadata expiration check: 0:32:14 ago on Sat 01 Aug 2026 10:00:00 AM UTC. Available Packages Name : mpv Version : 0.37.0 Release : 2.fc40 Architecture : x86_64 Size : 1.4 M Source : mpv-0.37.0-2.fc40.src.rpm Repository : rpmfusion-free Summary : Movie player playing most video formats and DVDs URL : https://mpv.io/ License : GPLv2+ Description : mpv is a movie player based on MPlayer and mplayer2. `, wantVersion: "0.37.0", }, { name: "multiple stanzas uses first", out: `Available Packages Name : mpv Version : 0.36.0 Release : 1.fc40 Architecture : x86_64 Size : 1.4 M Source : mpv-0.36.0-1.fc40.src.rpm Repository : rpmfusion-free Summary : Movie player playing most video formats and DVDs Name : mpv Version : 0.37.0 Release : 2.fc40 Architecture : x86_64 Size : 1.4 M Source : mpv-0.37.0-2.fc40.src.rpm Repository : rpmfusion-free-updates Summary : Movie player playing most video formats and DVDs `, wantVersion: "0.36.0", }, { name: "package not in repos", out: "No matching Packages to list\n", wantVersion: "", }, { name: "unparseable output", out: "Error: dnf exploded\n", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { version, err := parseDnfInfoOutput(tt.out) if (err != nil) != tt.wantErr { t.Fatalf("parseDnfInfoOutput() error = %v, wantErr %v", err, tt.wantErr) } if version != tt.wantVersion { t.Errorf("parseDnfInfoOutput() = %q, want %q", version, tt.wantVersion) } }) } } // TestParseZypperInfoVersionOutput verifies Version-field extraction from // `zypper --non-interactive info` output (captured under LC_ALL=C). func TestParseZypperInfoVersionOutput(t *testing.T) { tests := []struct { name string out string wantVersion string wantErr bool }{ { name: "available but not installed", out: `Loading repository data... Reading installed packages... Information for package mpv: ---------------------------- Repository : Main Repository (OSS) Name : mpv Version : 0.39.0-bp160.1.5 Arch : x86_64 Vendor : openSUSE Installed Size : 3.1 MiB Installed : No Status : not installed Source package : mpv-0.39.0-bp160.1.5.src Upstream URL : https://mpv.io/ Summary : Video player based on MPlayer/mplayer2 Description : mpv is a movie player based on MPlayer and mplayer2. `, wantVersion: "0.39.0-bp160.1.5", }, { name: "package not found", out: `Loading repository data... Reading installed packages... Package 'mpvfoo' not found. `, wantVersion: "", }, { name: "unparseable output", out: "Unexpected zypper failure\n", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { version, err := parseZypperInfoOutput(tt.out) if (err != nil) != tt.wantErr { t.Fatalf("parseZypperInfoOutput() error = %v, wantErr %v", err, tt.wantErr) } if version != tt.wantVersion { t.Errorf("parseZypperInfoOutput() = %q, want %q", version, tt.wantVersion) } }) } }