402 lines
14 KiB
Go
402 lines
14 KiB
Go
package main
|
|
|
|
// Last wave of coverage tests targeting setupFirecrackerVM (testable
|
|
// early-return branches), the remaining install handler edge cases, and
|
|
// a few stragglers.
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// ── setupFirecrackerVM: early returns ───────────────────────────────────
|
|
|
|
func TestSetupFirecrackerVMNotMac(t *testing.T) {
|
|
defer resetMocks()
|
|
isMacOS = false
|
|
setupFirecrackerVM() // should no-op
|
|
}
|
|
|
|
func TestSetupFirecrackerVMBackendEmpty(t *testing.T) {
|
|
defer resetMocks()
|
|
isMacOS = true
|
|
archName = "aarch64"
|
|
// Apple M2 on macOS 14 → selectVMBackend returns "" → setup skips.
|
|
probe = func(argv []string, _ time.Duration) (CmdResult, bool) {
|
|
if argv[0] == "sw_vers" {
|
|
return CmdResult{ExitCode: 0, Stdout: []byte("14.0")}, true
|
|
}
|
|
return CmdResult{ExitCode: 0, Stdout: []byte("Apple M2")}, true
|
|
}
|
|
captureStdout(t, func() {
|
|
setupFirecrackerVM()
|
|
})
|
|
}
|
|
|
|
func TestSetupFirecrackerVMSshKeygenFails(t *testing.T) {
|
|
defer resetMocks()
|
|
isMacOS = true
|
|
archName = "x86_64"
|
|
osStat = func(_ string) (os.FileInfo, error) { return nil, os.ErrNotExist }
|
|
osMkdirAll = func(_ string, _ os.FileMode) error { return nil }
|
|
runCmd = func(argv []string, _ CmdOpts) CmdResult {
|
|
if argv[0] == "ssh-keygen" {
|
|
return CmdResult{ExitCode: 1}
|
|
}
|
|
return CmdResult{ExitCode: 0}
|
|
}
|
|
captureStdout(t, func() {
|
|
setupFirecrackerVM()
|
|
})
|
|
if !hasIssueContaining("ssh-keygen failed") {
|
|
t.Error("expected ssh-keygen failure error")
|
|
}
|
|
}
|
|
|
|
func TestSetupFirecrackerVMFedoraImageLookupFails(t *testing.T) {
|
|
defer resetMocks()
|
|
isMacOS = true
|
|
archName = "x86_64"
|
|
osStat = func(name string) (os.FileInfo, error) {
|
|
// Key exists; qcow2 missing.
|
|
if strings.HasSuffix(name, "id_ed25519") {
|
|
return nil, nil
|
|
}
|
|
return nil, os.ErrNotExist
|
|
}
|
|
osMkdirAll = func(_ string, _ os.FileMode) error { return nil }
|
|
runCmd = func(_ []string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} }
|
|
fetchText = func(_ string) string { return "" }
|
|
captureStdout(t, func() {
|
|
setupFirecrackerVM()
|
|
})
|
|
if !hasIssueContaining("Could not resolve latest Fedora") {
|
|
t.Error("expected Fedora lookup failure error")
|
|
}
|
|
}
|
|
|
|
func TestSetupFirecrackerVMPubKeyReadFails(t *testing.T) {
|
|
defer resetMocks()
|
|
isMacOS = true
|
|
archName = "x86_64"
|
|
osStat = func(_ string) (os.FileInfo, error) { return nil, nil } // key + qcow2 exist
|
|
osMkdirAll = func(_ string, _ os.FileMode) error { return nil }
|
|
osReadFile = func(_ string) ([]byte, error) { return nil, os.ErrNotExist }
|
|
captureStdout(t, func() {
|
|
setupFirecrackerVM()
|
|
})
|
|
if !hasIssueContaining("could not read public key") {
|
|
t.Error("expected pub-key read failure")
|
|
}
|
|
}
|
|
|
|
// ── installFirecracker: archive contains non-firecracker file ──────────
|
|
|
|
func TestInstallFirecrackerSkipsNonMatchingFiles(t *testing.T) {
|
|
defer resetMocks()
|
|
tmp := t.TempDir()
|
|
runCmd = func(argv []string, _ CmdOpts) CmdResult {
|
|
if argv[0] == "tar" {
|
|
// Drop a file that doesn't start with "firecracker" — should be skipped.
|
|
_ = os.WriteFile(filepath.Join(tmp, "README"), []byte("x"), 0o644)
|
|
_ = os.WriteFile(filepath.Join(tmp, "firecracker-v1"), []byte("x"), 0o755)
|
|
}
|
|
return CmdResult{ExitCode: 0}
|
|
}
|
|
installFirecracker(filepath.Join(tmp, "fc.tgz"), tmp)
|
|
}
|
|
|
|
// ── installZig: existing glob match in /usr/local needs a writable parent ──
|
|
|
|
// We can't write to /usr/local in tests, but we can verify the symlink
|
|
// path runs through end-to-end with a no-op runCmd. The Glob returns []
|
|
// in tests, so the loop body stays uncovered.
|
|
|
|
// ── resolveLatestGo: version trimmed to empty (release tag was just "go") ──
|
|
|
|
func TestResolveLatestGoEmptyTrimmedVersion(t *testing.T) {
|
|
defer resetMocks()
|
|
fetchJSON = func(_ string, v any) bool {
|
|
// Release with version "go" → trim → empty.
|
|
data := `[{"version":"go","files":[]}]`
|
|
_ = v
|
|
// Marshal manually since we don't import json here; use the helper
|
|
// via reflection-free path: use the canonical mock from elsewhere.
|
|
return jsonUnmarshal([]byte(data), v)
|
|
}
|
|
if _, _, ok := resolveLatestGo(nil); ok {
|
|
t.Error("expected resolveLatestGo false when version is empty after trim")
|
|
}
|
|
}
|
|
|
|
func TestResolveLatestFirecrackerEmptyTagTrim(t *testing.T) {
|
|
defer resetMocks()
|
|
isMacOS = false
|
|
fetchJSON = func(_ string, v any) bool {
|
|
v.(*ghRelease).TagName = "v" // → trimmed to ""
|
|
return true
|
|
}
|
|
if _, _, ok := resolveLatestFirecracker(nil); ok {
|
|
t.Error("expected false when trimmed tag is empty")
|
|
}
|
|
}
|
|
|
|
// ── runMain: empty package lists short-circuit ──────────────────────────
|
|
|
|
func TestRunMainEmptyOnlyValid(t *testing.T) {
|
|
defer resetMocks()
|
|
pkgMgr = "dnf"
|
|
osStat = func(_ string) (os.FileInfo, error) { return nil, nil }
|
|
probe = func(_ []string, _ time.Duration) (CmdResult, bool) { return CmdResult{ExitCode: 0}, true }
|
|
hasCmd = func(_ string) bool { return true }
|
|
runCmd = func(_ []string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} }
|
|
runShell = func(_ string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} }
|
|
osExit = func(_ int) {}
|
|
captureStdout(t, func() {
|
|
// "" only flag (default) with everything reported as installed.
|
|
runMain([]string{"bootstrap_environment"})
|
|
})
|
|
}
|
|
|
|
func jsonUnmarshal(data []byte, v any) bool {
|
|
return json.Unmarshal(data, v) == nil
|
|
}
|
|
|
|
// ── extra runMain branches ──────────────────────────────────────────────
|
|
|
|
func TestRunMainSystemInstallPath(t *testing.T) {
|
|
defer resetMocks()
|
|
pkgMgr = "dnf"
|
|
osStat = func(_ string) (os.FileInfo, error) { return nil, os.ErrNotExist }
|
|
probe = func(_ []string, _ time.Duration) (CmdResult, bool) { return CmdResult{ExitCode: 1}, true }
|
|
hasCmd = func(_ string) bool { return true }
|
|
runCmd = func(_ []string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} }
|
|
runShell = func(_ string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} }
|
|
stdin = strings.NewReader("y\n")
|
|
defer func() { stdin = os.Stdin }()
|
|
osExit = func(_ int) {}
|
|
captureStdout(t, func() {
|
|
runMain([]string{"bootstrap_environment", "--only", "system"})
|
|
})
|
|
}
|
|
|
|
func TestRunMainCustomInstallPath(t *testing.T) {
|
|
defer resetMocks()
|
|
pkgMgr = "dnf"
|
|
// Custom pkgs all need install (not present). The osStat mock runs
|
|
// from multiple goroutines via parallel Wave A, so it must be
|
|
// goroutine-safe (no shared mutable state outside of read-only env
|
|
// inspection).
|
|
osStat = func(name string) (os.FileInfo, error) {
|
|
// ~/.nvm exists so the npm batch path runs.
|
|
if strings.HasSuffix(name, ".nvm") || strings.HasSuffix(name, ".pyenv") {
|
|
return nil, nil
|
|
}
|
|
return nil, os.ErrNotExist
|
|
}
|
|
probe = func(_ []string, _ time.Duration) (CmdResult, bool) { return CmdResult{ExitCode: 1}, true }
|
|
hasCmd = func(_ string) bool { return true }
|
|
runCmd = func(_ []string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} }
|
|
runShell = func(_ string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0, Stdout: []byte("v20\n")} }
|
|
download = func(_, dest string) bool {
|
|
return os.WriteFile(dest, []byte("x"), 0o644) == nil
|
|
}
|
|
fetchJSON = func(_ string, _ any) bool { return false }
|
|
fetchText = func(_ string) string { return "" }
|
|
stdin = strings.NewReader("y\n")
|
|
defer func() { stdin = os.Stdin }()
|
|
osExit = func(_ int) {}
|
|
captureStdout(t, func() {
|
|
runMain([]string{"bootstrap_environment", "--only", "custom"})
|
|
})
|
|
}
|
|
|
|
// ── runShellReal: probe times out via tiny timeout ──────────────────────
|
|
|
|
func TestRunShellRealTimeout(t *testing.T) {
|
|
r := runShellReal("sleep 1", CmdOpts{Timeout: 10 * time.Millisecond})
|
|
if r.ExitCode != 124 {
|
|
t.Errorf("expected timeout (124), got %d", r.ExitCode)
|
|
}
|
|
}
|
|
|
|
// ── runCmdReal: command times out ───────────────────────────────────────
|
|
|
|
func TestRunCmdRealTimeout(t *testing.T) {
|
|
r := runCmdReal([]string{"sleep", "1"}, CmdOpts{Timeout: 10 * time.Millisecond})
|
|
if r.ExitCode != 124 {
|
|
t.Errorf("expected timeout (124), got %d", r.ExitCode)
|
|
}
|
|
}
|
|
|
|
// ── runCmdReal: cwd + input passing ────────────────────────────────────
|
|
|
|
func TestRunCmdRealCwdAndInput(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
r := runCmdReal([]string{"sh", "-c", "cat > out.txt; pwd"},
|
|
CmdOpts{Cwd: tmp, Input: []byte("data"), Capture: true})
|
|
if !r.OK() {
|
|
t.Fatalf("expected OK, got: %v / %s", r.Err, r.Stderr)
|
|
}
|
|
if !strings.Contains(string(r.Stdout), tmp) {
|
|
t.Errorf("expected stdout to contain cwd %s, got: %s", tmp, r.Stdout)
|
|
}
|
|
if data, err := os.ReadFile(filepath.Join(tmp, "out.txt")); err != nil || string(data) != "data" {
|
|
t.Errorf("expected stdin data to be written, got: %q (err=%v)", data, err)
|
|
}
|
|
}
|
|
|
|
// ── runShellReal: cwd + input ──────────────────────────────────────────
|
|
|
|
func TestRunShellRealCwdAndInput(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
r := runShellReal("cat > shell-out.txt; pwd",
|
|
CmdOpts{Cwd: tmp, Input: []byte("shelldata"), Capture: true})
|
|
if !r.OK() {
|
|
t.Fatalf("expected OK, got %v", r.Err)
|
|
}
|
|
if data, err := os.ReadFile(filepath.Join(tmp, "shell-out.txt")); err != nil || string(data) != "shelldata" {
|
|
t.Errorf("expected stdin data written via shell, got %q err=%v", data, err)
|
|
}
|
|
}
|
|
|
|
// ── installPip apt-get fallback secondary failure ──────────────────────
|
|
|
|
func TestInstallPipAptFallbackFails(t *testing.T) {
|
|
defer resetMocks()
|
|
pkgMgr = "apt-get"
|
|
hasCmd = func(_ string) bool { return true }
|
|
probe = func(_ []string, _ time.Duration) (CmdResult, bool) { return CmdResult{ExitCode: 0}, true }
|
|
runCmd = func(_ []string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 1} } // everything fails
|
|
installPip()
|
|
if !hasIssueContaining("python3-pip failed to install via apt-get") {
|
|
t.Error("expected apt python3-pip failure")
|
|
}
|
|
}
|
|
|
|
// ── ensureXcodeCLT non-macOS quick exit (already added but exercise the cov path) ──
|
|
|
|
// ── invokingUser: SUDO_USER unset, user.Current succeeds ──
|
|
|
|
func TestInvokingUserNoSudoCurrentUser(t *testing.T) {
|
|
t.Setenv("SUDO_USER", "")
|
|
if invokingUser() == "" {
|
|
t.Error("expected invokingUser to fall back to user.Current()")
|
|
}
|
|
}
|
|
|
|
// ── installSystemPackages: tmpdir works for specials ──────────────────
|
|
|
|
func TestInstallSystemPackagesWithSpecialReal(t *testing.T) {
|
|
defer resetMocks()
|
|
pkgMgr = "dnf"
|
|
hasCmd = func(_ string) bool { return true }
|
|
runCmd = func(_ []string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} }
|
|
captureStdout(t, func() {
|
|
installSystemPackages([]string{"git"}, []string{"pipx"})
|
|
})
|
|
}
|
|
|
|
// ── npmInstalled: home-dir failure path ────────────────────────────────
|
|
|
|
// The os.UserHomeDir call only returns an error when HOME is unset on Unix
|
|
// AND no /etc/passwd entry exists. Hard to trigger reliably across CI; the
|
|
// branch is mostly defensive. Skip explicit coverage.
|
|
|
|
// ── runOneCustomInstall: install path missing, with name != pip ────────
|
|
|
|
func TestRunOneCustomInstallNoCheckPath(t *testing.T) {
|
|
defer resetMocks()
|
|
// Pretend nothing is installed and use a package that has no install path
|
|
// AND no URL — should warn twice.
|
|
osStat = func(_ string) (os.FileInfo, error) { return nil, os.ErrNotExist }
|
|
hasCmd = func(_ string) bool { return false }
|
|
runOneCustomInstall(&CustomPackage{Name: "unknownpkg-2"})
|
|
}
|
|
|
|
// ── repos: apt-get docker setup (no existing file) ────────────────────
|
|
// Without docker installed this exercises the gpg+keyring branch via mocks.
|
|
|
|
func TestSetupDockerRepoApt(t *testing.T) {
|
|
defer resetMocks()
|
|
pkgMgr = "apt-get"
|
|
osStat = func(_ string) (os.FileInfo, error) { return nil, os.ErrNotExist }
|
|
runCmd = func(_ []string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} }
|
|
runShell = func(_ string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} }
|
|
osReadFile = func(_ string) ([]byte, error) {
|
|
return []byte("ID=ubuntu\n"), nil
|
|
}
|
|
captureStdout(t, func() {
|
|
setupDockerRepo()
|
|
})
|
|
}
|
|
|
|
func TestSetupChromeRepoApt(t *testing.T) {
|
|
defer resetMocks()
|
|
pkgMgr = "apt-get"
|
|
archName = "x86_64"
|
|
osStat = func(_ string) (os.FileInfo, error) { return nil, os.ErrNotExist }
|
|
runShell = func(_ string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} }
|
|
captureStdout(t, func() {
|
|
setupChromeRepo()
|
|
})
|
|
}
|
|
|
|
func TestSetupVivaldiRepoApt(t *testing.T) {
|
|
defer resetMocks()
|
|
pkgMgr = "apt-get"
|
|
archName = "x86_64"
|
|
osStat = func(_ string) (os.FileInfo, error) { return nil, os.ErrNotExist }
|
|
runShell = func(_ string, _ CmdOpts) CmdResult { return CmdResult{ExitCode: 0} }
|
|
captureStdout(t, func() {
|
|
setupVivaldiRepo()
|
|
})
|
|
}
|
|
|
|
// ── installFirecrackerZshFunction: existing block gets replaced ───────
|
|
|
|
func TestInstallFirecrackerZshFunctionReplaceExisting(t *testing.T) {
|
|
defer resetMocks()
|
|
tmp := t.TempDir()
|
|
t.Setenv("HOME", tmp)
|
|
osReadFile = func(_ string) ([]byte, error) {
|
|
return []byte("# >>> firecracker-vm wrapper >>>\nold body\n# <<< firecracker-vm wrapper <<<\n\nelse"), nil
|
|
}
|
|
written := ""
|
|
osWriteFile = func(_ string, data []byte, _ os.FileMode) error {
|
|
written = string(data)
|
|
return nil
|
|
}
|
|
newBlock := "# >>> firecracker-vm wrapper >>>\nnew body\n# <<< firecracker-vm wrapper <<<\n"
|
|
installFirecrackerZshFunction(newBlock)
|
|
if !strings.Contains(written, "new body") {
|
|
t.Errorf("expected new body in output, got: %q", written)
|
|
}
|
|
if strings.Contains(written, "old body") {
|
|
t.Errorf("expected old body to be removed, got: %q", written)
|
|
}
|
|
}
|
|
|
|
// ── latestFedoraCloudImage: missing checksum entry ────────────────────
|
|
|
|
func TestLatestFedoraCloudImageMissingFiles(t *testing.T) {
|
|
defer resetMocks()
|
|
archName = "x86_64"
|
|
calls := 0
|
|
fetchText = func(_ string) string {
|
|
calls++
|
|
if calls == 1 {
|
|
return `href="40/"`
|
|
}
|
|
// images dir has no matching qcow / checksum.
|
|
return `href="not-fedora.iso"`
|
|
}
|
|
if _, _, _, ok := latestFedoraCloudImage(); ok {
|
|
t.Error("expected false when matches not found")
|
|
}
|
|
}
|