Pulse/internal/updates/adapter_installsh_extra_test.go
2026-01-25 21:08:44 +00:00

259 lines
7.4 KiB
Go

package updates
import (
"context"
"crypto/sha256"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"time"
)
func writeCurlStub(t *testing.T, dir string) {
t.Helper()
cpPath, err := exec.LookPath("cp")
if err != nil {
t.Fatalf("find cp: %v", err)
}
script := fmt.Sprintf(`#!/bin/sh
set -e
out=""
url=""
while [ "$#" -gt 0 ]; do
case "$1" in
-o) out="$2"; shift 2;;
*) url="$1"; shift;;
esac
done
if [ -z "$out" ]; then
exit 1
fi
if echo "$url" | grep -q "\.sha256$"; then
exec %s "$PULSE_TEST_SHA" "$out"
fi
exec %s "$PULSE_TEST_FILE" "$out"
`, cpPath, cpPath)
writeStub(t, dir, "curl", script)
}
func TestInstallShAdapterDetectServiceName(t *testing.T) {
stubDir := t.TempDir()
writeStub(t, stubDir, "systemctl", `#!/bin/sh
if [ "$1" = "is-active" ] && [ "$2" = "pulse-backend" ]; then
echo "active"
exit 0
fi
exit 1
`)
t.Setenv("PATH", stubDir+string(os.PathListSeparator)+os.Getenv("PATH"))
adapter := &InstallShAdapter{}
name, err := adapter.detectServiceName()
if err != nil {
t.Fatalf("detectServiceName error: %v", err)
}
if name != "pulse-backend" {
t.Fatalf("expected pulse-backend, got %s", name)
}
writeStub(t, stubDir, "systemctl", `#!/bin/sh
echo "inactive"
exit 0
`)
name, err = adapter.detectServiceName()
if err != nil {
t.Fatalf("detectServiceName error: %v", err)
}
if name != "pulse" {
t.Fatalf("expected default pulse, got %s", name)
}
}
func TestInstallShAdapterDownloadInstallScript(t *testing.T) {
content := []byte("#!/bin/sh\necho ok\n")
sum := sha256.Sum256(content)
tmpDir := t.TempDir()
scriptPath := filepath.Join(tmpDir, "install.sh")
if err := os.WriteFile(scriptPath, content, 0600); err != nil {
t.Fatalf("write install.sh: %v", err)
}
checksumPath := filepath.Join(tmpDir, "install.sh.sha256")
if err := os.WriteFile(checksumPath, []byte(fmt.Sprintf("%x install.sh\n", sum)), 0600); err != nil {
t.Fatalf("write checksum: %v", err)
}
stubDir := t.TempDir()
writeCurlStub(t, stubDir)
t.Setenv("PATH", stubDir+string(os.PathListSeparator)+os.Getenv("PATH"))
t.Setenv("PULSE_TEST_FILE", scriptPath)
t.Setenv("PULSE_TEST_SHA", checksumPath)
adapter := NewInstallShAdapter(nil)
got, err := adapter.downloadInstallScript(context.Background())
if err != nil {
t.Fatalf("downloadInstallScript error: %v", err)
}
if got != string(content) {
t.Fatalf("unexpected script content: %q", got)
}
if err := os.WriteFile(checksumPath, []byte("deadbeef"), 0600); err != nil {
t.Fatalf("write checksum: %v", err)
}
if _, err := adapter.downloadInstallScript(context.Background()); err == nil {
t.Fatalf("expected checksum error")
}
}
func TestInstallShAdapterDownloadBinary(t *testing.T) {
extractDir := t.TempDir()
tarball := filepath.Join(extractDir, "pulse-v1.2.3-linux-amd64.tar.gz")
writeTarGz(t, tarball, map[string]string{
"bin/pulse": "binary",
})
data, err := os.ReadFile(tarball)
if err != nil {
t.Fatalf("read tarball: %v", err)
}
sum := sha256.Sum256(data)
checksumPath := tarball + ".sha256"
if err := os.WriteFile(checksumPath, []byte(fmt.Sprintf("%x %s\n", sum, filepath.Base(tarball))), 0600); err != nil {
t.Fatalf("write checksum: %v", err)
}
stubDir := t.TempDir()
writeCurlStub(t, stubDir)
t.Setenv("PATH", stubDir+string(os.PathListSeparator)+os.Getenv("PATH"))
t.Setenv("PULSE_TEST_FILE", tarball)
t.Setenv("PULSE_TEST_SHA", checksumPath)
adapter := &InstallShAdapter{}
binaryPath, err := adapter.downloadBinary(context.Background(), "1.2.3")
if err != nil {
t.Fatalf("downloadBinary error: %v", err)
}
got, err := os.ReadFile(binaryPath)
if err != nil {
t.Fatalf("read binary: %v", err)
}
if string(got) != "binary" {
t.Fatalf("unexpected binary content: %q", string(got))
}
if err := os.WriteFile(checksumPath, []byte("deadbeef"), 0600); err != nil {
t.Fatalf("write checksum: %v", err)
}
if _, err := adapter.downloadBinary(context.Background(), "1.2.3"); err == nil {
t.Fatalf("expected checksum mismatch error")
}
}
func TestInstallShAdapterReadLastLines(t *testing.T) {
adapter := &InstallShAdapter{}
if adapter.readLastLines(filepath.Join(t.TempDir(), "missing"), 2) != "" {
t.Fatalf("expected empty for missing file")
}
path := filepath.Join(t.TempDir(), "log.txt")
if err := os.WriteFile(path, []byte("a\nb\nc\n"), 0600); err != nil {
t.Fatalf("write log: %v", err)
}
got := adapter.readLastLines(path, 2)
if got != "b\nc" {
t.Fatalf("unexpected lines: %q", got)
}
if adapter.readLastLines(path, 0) != "" {
t.Fatalf("expected empty for zero lines")
}
}
func TestInstallShAdapterRestoreConfigAndInstallBinary(t *testing.T) {
cpPath, err := exec.LookPath("cp")
if err != nil {
t.Fatalf("find cp: %v", err)
}
stubDir := t.TempDir()
writeStub(t, stubDir, "cp", fmt.Sprintf("#!/bin/sh\nexec %s \"$@\"\n", cpPath))
writeStub(t, stubDir, "chown", "#!/bin/sh\nexit 0\n")
t.Setenv("PATH", stubDir+string(os.PathListSeparator)+os.Getenv("PATH"))
adapter := &InstallShAdapter{}
srcDir := filepath.Join(t.TempDir(), "backup")
destDir := filepath.Join(t.TempDir(), "config")
if err := os.MkdirAll(srcDir, 0755); err != nil {
t.Fatalf("mkdir src: %v", err)
}
if err := os.WriteFile(filepath.Join(srcDir, "app.conf"), []byte("ok"), 0600); err != nil {
t.Fatalf("write src file: %v", err)
}
if err := os.MkdirAll(destDir, 0755); err != nil {
t.Fatalf("mkdir dest: %v", err)
}
if err := adapter.restoreConfig(context.Background(), srcDir, destDir); err != nil {
t.Fatalf("restoreConfig error: %v", err)
}
if _, err := os.Stat(filepath.Join(destDir, "app.conf")); err != nil {
t.Fatalf("expected config restored: %v", err)
}
srcBinary := filepath.Join(t.TempDir(), "pulse")
if err := os.WriteFile(srcBinary, []byte("bin"), 0755); err != nil {
t.Fatalf("write source binary: %v", err)
}
targetBinary := filepath.Join(t.TempDir(), "pulse")
if err := os.WriteFile(targetBinary, []byte("old"), 0755); err != nil {
t.Fatalf("write target binary: %v", err)
}
if err := adapter.installBinary(context.Background(), srcBinary, targetBinary); err != nil {
t.Fatalf("installBinary error: %v", err)
}
if _, err := os.Stat(targetBinary + ".pre-rollback"); err != nil {
t.Fatalf("expected backup binary: %v", err)
}
}
func TestInstallShAdapterWaitForHealth(t *testing.T) {
stubDir := t.TempDir()
writeStub(t, stubDir, "curl", "#!/bin/sh\nexit 0\n")
t.Setenv("PATH", stubDir+string(os.PathListSeparator)+os.Getenv("PATH"))
adapter := &InstallShAdapter{}
if err := adapter.waitForHealth(context.Background(), time.Second); err != nil {
t.Fatalf("waitForHealth success error: %v", err)
}
writeStub(t, stubDir, "curl", "#!/bin/sh\nexit 1\n")
ctx, cancel := context.WithCancel(context.Background())
cancel()
if err := adapter.waitForHealth(ctx, time.Second); err == nil || !strings.Contains(err.Error(), "canceled") {
t.Fatalf("expected context error, got %v", err)
}
if err := adapter.waitForHealth(context.Background(), 0); err == nil {
t.Fatalf("expected timeout error")
}
}
func TestInstallShAdapterExecuteRollbackDownloadError(t *testing.T) {
stubDir := t.TempDir()
writeStub(t, stubDir, "systemctl", "#!/bin/sh\nexit 0\n")
writeStub(t, stubDir, "curl", "#!/bin/sh\nexit 1\n")
t.Setenv("PATH", stubDir+string(os.PathListSeparator)+os.Getenv("PATH"))
adapter := &InstallShAdapter{}
entry := &UpdateHistoryEntry{BackupPath: t.TempDir()}
if err := adapter.executeRollback(context.Background(), entry, "1.2.3"); err == nil {
t.Fatalf("expected download error")
}
}