Files
klink/scripts/install_test.go
T
gary e9f9fdd6fc
Release / goreleaser (push) Successful in 3m1s
feat: release klink v0.1.0
2026-07-19 22:27:20 +08:00

152 lines
4.3 KiB
Go

package scripts
import (
"archive/tar"
"bytes"
"compress/gzip"
"crypto/sha256"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
)
func TestUnixInstallerInstallsVerifiedRelease(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("install.sh is not used on Windows")
}
root := repositoryRoot(t)
repository := t.TempDir()
installDir := t.TempDir()
createUnixRelease(t, repository, false)
output, err := runUnixInstaller(root, repository, installDir)
if err != nil {
t.Fatalf("install.sh failed: %v\n%s", err, output)
}
if !strings.Contains(output, "Installed klink v0.1.0") {
t.Fatalf("install output does not report success:\n%s", output)
}
binary := filepath.Join(installDir, "klink")
info, err := os.Stat(binary)
if err != nil {
t.Fatalf("installed binary: %v", err)
}
if info.Mode()&0o111 == 0 {
t.Fatalf("installed binary is not executable: mode %v", info.Mode())
}
commandOutput, err := exec.Command(binary).CombinedOutput()
if err != nil {
t.Fatalf("run installed binary: %v\n%s", err, commandOutput)
}
if got := strings.TrimSpace(string(commandOutput)); got != "klink installer test" {
t.Fatalf("installed binary output = %q", got)
}
}
func TestUnixInstallerRejectsChecksumMismatch(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("install.sh is not used on Windows")
}
root := repositoryRoot(t)
repository := t.TempDir()
installDir := t.TempDir()
createUnixRelease(t, repository, true)
output, err := runUnixInstaller(root, repository, installDir)
if err == nil {
t.Fatalf("install.sh accepted an invalid checksum:\n%s", output)
}
if !strings.Contains(output, "SHA256 checksum mismatch") {
t.Fatalf("unexpected checksum failure:\n%s", output)
}
if _, statErr := os.Stat(filepath.Join(installDir, "klink")); !os.IsNotExist(statErr) {
t.Fatalf("installer left a binary after checksum failure: %v", statErr)
}
}
func repositoryRoot(t *testing.T) string {
t.Helper()
_, filename, _, ok := runtime.Caller(0)
if !ok {
t.Fatal("could not locate installer test")
}
return filepath.Dir(filepath.Dir(filename))
}
func createUnixRelease(t *testing.T, repository string, corruptChecksum bool) {
t.Helper()
const asset = "klink_0.1.0_linux_amd64.tar.gz"
releaseDir := filepath.Join(repository, "releases", "download", "v0.1.0")
if err := os.MkdirAll(releaseDir, 0o755); err != nil {
t.Fatal(err)
}
var archive bytes.Buffer
gzipWriter := gzip.NewWriter(&archive)
tarWriter := tar.NewWriter(gzipWriter)
content := []byte("#!/bin/sh\nprintf '%s\\n' 'klink installer test'\n")
header := &tar.Header{Name: "klink", Mode: 0o755, Size: int64(len(content))}
if err := tarWriter.WriteHeader(header); err != nil {
t.Fatal(err)
}
if _, err := tarWriter.Write(content); err != nil {
t.Fatal(err)
}
if err := tarWriter.Close(); err != nil {
t.Fatal(err)
}
if err := gzipWriter.Close(); err != nil {
t.Fatal(err)
}
archiveBytes := archive.Bytes()
if err := os.WriteFile(filepath.Join(releaseDir, asset), archiveBytes, 0o644); err != nil {
t.Fatal(err)
}
digest := sha256.Sum256(archiveBytes)
if corruptChecksum {
digest = sha256.Sum256([]byte("not the release archive"))
}
checksums := fmt.Sprintf("%x %s\n", digest, asset)
if err := os.WriteFile(filepath.Join(releaseDir, "checksums.txt"), []byte(checksums), 0o644); err != nil {
t.Fatal(err)
}
}
func runUnixInstaller(root, repository, installDir string) (string, error) {
command := exec.Command("sh", filepath.Join(root, "install.sh"))
command.Env = installerEnvironment(repository, installDir)
output, err := command.CombinedOutput()
return string(output), err
}
func installerEnvironment(repository, installDir string) []string {
overrides := map[string]string{
"KLINK_ALLOW_INSECURE": "1",
"KLINK_ARCH": "amd64",
"KLINK_INSTALL_DIR": installDir,
"KLINK_OS": "linux",
"KLINK_REPOSITORY_URL": "file://" + filepath.ToSlash(repository),
"KLINK_VERSION": "v0.1.0",
}
environment := make([]string, 0, len(os.Environ())+len(overrides))
for _, entry := range os.Environ() {
name, _, found := strings.Cut(entry, "=")
if !found {
continue
}
if _, overridden := overrides[name]; !overridden {
environment = append(environment, entry)
}
}
for name, value := range overrides {
environment = append(environment, name+"="+value)
}
return environment
}