Files
Redflag/aggregator-agent/internal/scanner/winget_parser_test.go
jpetree331 8901f22a96 fix(windows): C-1 Windows-specific bug fixes
- Apply B-2 jitter and backoff fixes to Windows service (F-C1-5)
  Proportional jitter and exponential backoff now in service polling loop
- Add known winget install location search for SYSTEM account (F-C1-1)
  Checks PATH then system-wide WindowsApps locations
- Fix winget text parser for package names with spaces (F-C1-2)
  Column-position parsing from header keywords replaces whitespace split
- Add ghost update post-install state verification (F-C1-3)
  RebootRequired flag on InstallResult marks pending reboot
- Replace fmt.Printf with log.Printf in winget scanner (F-C1-6)
- Remove emoji from Windows service log messages (F-C1-7)

GOOS=linux build: PASS. All tests pass, no regressions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 09:13:21 -04:00

158 lines
5.6 KiB
Go

package scanner
// winget_parser_test.go — Pre-fix tests for winget output parsing.
// [SHARED] — no build tag, compiles on all platforms.
//
// F-C1-2 MEDIUM: Text fallback parser splits on whitespace,
// breaks on package names with spaces.
// F-C1-8 LOW: No winget parsing tests existed before this file.
//
// Run: cd aggregator-agent && go test ./internal/scanner/... -v -run TestWinget
import (
"encoding/json"
"testing"
)
// ---------------------------------------------------------------------------
// Test 2.1 — Text parser handles spaces in package names (assert fix)
//
// Category: FAIL-NOW / PASS-AFTER-FIX
// ---------------------------------------------------------------------------
func TestWingetTextParserHandlesSpacesInPackageNames(t *testing.T) {
// F-C1-2 MEDIUM: Text parser splits on all whitespace.
// Package names with spaces are truncated to the first word.
// After fix: use column-position parsing based on header separator line.
scanner := NewWingetScanner()
mockOutput := "Name Id Version Available\n" +
"------------------------------------------------------------------------------------\n" +
"Microsoft Visual Studio Code Microsoft.VisualStudioCode 1.85.0 1.86.0\n" +
"Git Git.Git 2.43.0 2.44.0\n"
updates, err := scanner.parseWingetTextOutput(mockOutput)
if err != nil {
t.Fatalf("parse error: %v", err)
}
if len(updates) == 0 {
t.Fatal("[ERROR] [agent] [scanner] no updates parsed from text output")
}
// The first package should have the full name
firstName := updates[0].PackageName
if firstName != "Microsoft Visual Studio Code" {
t.Errorf("[ERROR] [agent] [scanner] expected full name \"Microsoft Visual Studio Code\", got %q.\n"+
"F-C1-2: text parser truncates names at first space.", firstName)
}
}
// ---------------------------------------------------------------------------
// Test 2.2 — Documents text parser truncation (F-C1-2)
//
// Category: PASS-NOW (documents the bug)
// ---------------------------------------------------------------------------
func TestWingetTextParserCurrentlyBreaksOnSpaces(t *testing.T) {
// POST-FIX (F-C1-2): Text parser now uses column-position parsing.
// Full package names with spaces are preserved.
scanner := NewWingetScanner()
mockOutput := "Name Id Version Available\n" +
"------------------------------------------------------------------------------------\n" +
"Microsoft Visual Studio Code Microsoft.VisualStudioCode 1.85.0 1.86.0\n"
updates, err := scanner.parseWingetTextOutput(mockOutput)
if err != nil {
t.Fatalf("parse error: %v", err)
}
if len(updates) == 0 {
t.Fatal("[ERROR] [agent] [scanner] no updates parsed")
}
if updates[0].PackageName != "Microsoft Visual Studio Code" {
t.Errorf("[ERROR] [agent] [scanner] expected full name, got %q", updates[0].PackageName)
}
t.Log("[INFO] [agent] [scanner] F-C1-2 FIXED: full package name preserved")
}
// ---------------------------------------------------------------------------
// Test 2.3 — JSON parser handles spaces correctly
//
// Category: PASS-NOW (JSON path works)
// ---------------------------------------------------------------------------
func TestWingetJsonParserHandlesSpacesInPackageNames(t *testing.T) {
// F-C1-2: JSON parser handles spaces correctly.
// The text fallback is the broken path.
scanner := NewWingetScanner()
// Mock JSON output matching WingetPackage struct
mockJSON := `[
{"Name":"Microsoft Visual Studio Code","Id":"Microsoft.VisualStudioCode","Version":"1.85.0","Available":"1.86.0","Source":"winget"},
{"Name":"Git","Id":"Git.Git","Version":"2.43.0","Available":"2.44.0","Source":"winget"}
]`
var packages []WingetPackage
if err := json.Unmarshal([]byte(mockJSON), &packages); err != nil {
t.Fatalf("JSON parse error: %v", err)
}
if len(packages) < 1 {
t.Fatal("[ERROR] [agent] [scanner] no packages parsed from JSON")
}
// JSON parser should get full name
item := scanner.parseWingetPackage(packages[0])
if item.PackageName != "Microsoft Visual Studio Code" {
t.Errorf("[ERROR] [agent] [scanner] expected full name, got %q", item.PackageName)
}
t.Log("[INFO] [agent] [scanner] F-C1-2: JSON parser handles spaces correctly")
}
// ---------------------------------------------------------------------------
// Test 2.4 — Both parsers return consistent structure
//
// Category: PASS-NOW
// ---------------------------------------------------------------------------
func TestWingetParserReturnsConsistentStructure(t *testing.T) {
// F-C1-2: Both parsers must return the same struct type.
scanner := NewWingetScanner()
// JSON path
pkg := WingetPackage{
Name: "Git",
ID: "Git.Git",
Version: "2.43.0",
Available: "2.44.0",
Source: "winget",
}
jsonResult := scanner.parseWingetPackage(pkg)
// Text path (simple name without spaces)
textOutput := "Name Id Version Available\n" +
"----------------------------------\n" +
"Git Git.Git 2.43.0 2.44.0\n"
textResults, err := scanner.parseWingetTextOutput(textOutput)
if err != nil {
t.Fatalf("text parse error: %v", err)
}
if len(textResults) == 0 {
t.Fatal("[ERROR] [agent] [scanner] no results from text parser")
}
// Both should have the same package type
if jsonResult.PackageType != textResults[0].PackageType {
t.Errorf("[ERROR] [agent] [scanner] package type mismatch: json=%q text=%q",
jsonResult.PackageType, textResults[0].PackageType)
}
t.Log("[INFO] [agent] [scanner] F-C1-2: both parsers return consistent structure")
}