127 lines
4.0 KiB
Go
127 lines
4.0 KiB
Go
package update
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// writeTempFile 在临时目录写入内容并返回路径
|
|
func writeTempFile(t *testing.T, name, content string) string {
|
|
t.Helper()
|
|
path := filepath.Join(t.TempDir(), name)
|
|
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
|
|
t.Fatalf("写入临时文件失败: %v", err)
|
|
}
|
|
return path
|
|
}
|
|
|
|
// serveChecksums 启动一个返回指定内容的测试 HTTP 服务
|
|
func serveChecksums(t *testing.T, status int, body string) *httptest.Server {
|
|
t.Helper()
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(status)
|
|
_, _ = w.Write([]byte(body))
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
return srv
|
|
}
|
|
|
|
func TestVerifyChecksum_Success(t *testing.T) {
|
|
content := "fake-binary-content"
|
|
filePath := writeTempFile(t, "team-api-1.0.0-linux-amd64.tar.gz", content)
|
|
|
|
sum := sha256.Sum256([]byte(content))
|
|
hash := hex.EncodeToString(sum[:])
|
|
|
|
srv := serveChecksums(t, http.StatusOK, hash+" team-api-1.0.0-linux-amd64.tar.gz\n")
|
|
|
|
if err := VerifyChecksum(context.Background(), filePath, srv.URL, "team-api-1.0.0-linux-amd64.tar.gz"); err != nil {
|
|
t.Fatalf("校验应通过,实际失败: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestVerifyChecksum_HashMismatchErrors(t *testing.T) {
|
|
filePath := writeTempFile(t, "asset.tar.gz", "actual-content")
|
|
|
|
wrongHash := strings.Repeat("0", 64)
|
|
srv := serveChecksums(t, http.StatusOK, wrongHash+" asset.tar.gz\n")
|
|
|
|
err := VerifyChecksum(context.Background(), filePath, srv.URL, "asset.tar.gz")
|
|
if err == nil {
|
|
t.Fatal("哈希不一致必须返回错误,不允许更新")
|
|
}
|
|
if !strings.Contains(err.Error(), "SHA256 校验不一致") {
|
|
t.Errorf("错误信息应说明校验不一致,实际: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestVerifyChecksum_MissingChecksumURLRejected(t *testing.T) {
|
|
filePath := writeTempFile(t, "asset.tar.gz", "content")
|
|
|
|
err := VerifyChecksum(context.Background(), filePath, "", "asset.tar.gz")
|
|
if err == nil {
|
|
t.Fatal("无 checksum URL 时必须返回错误,不允许跳过校验放行")
|
|
}
|
|
if !strings.Contains(err.Error(), "未提供") {
|
|
t.Errorf("错误信息应说明缺少校验文件,实际: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestVerifyChecksum_ChecksumFetchFailureRejected(t *testing.T) {
|
|
filePath := writeTempFile(t, "asset.tar.gz", "content")
|
|
|
|
// 服务返回 500,模拟校验文件不可用
|
|
srv := serveChecksums(t, http.StatusInternalServerError, "boom")
|
|
|
|
err := VerifyChecksum(context.Background(), filePath, srv.URL, "asset.tar.gz")
|
|
if err == nil {
|
|
t.Fatal("校验文件拉取失败时必须返回错误,不允许跳过校验放行")
|
|
}
|
|
}
|
|
|
|
func TestFetchExpectedHash_ParsesFormat(t *testing.T) {
|
|
content := "aaa file-a.tar.gz\nbbb *file-b.tar.gz\n"
|
|
srv := serveChecksums(t, http.StatusOK, content)
|
|
ctx := context.Background()
|
|
|
|
// 标准格式(两个空格分隔)
|
|
if got, err := fetchExpectedHash(ctx, srv.URL, "file-a.tar.gz"); err != nil || got != "aaa" {
|
|
t.Errorf("双空格格式解析失败: got=%q err=%v", got, err)
|
|
}
|
|
// 二进制格式(* 前缀)
|
|
if got, err := fetchExpectedHash(ctx, srv.URL, "file-b.tar.gz"); err != nil || got != "bbb" {
|
|
t.Errorf("* 前缀格式解析失败: got=%q err=%v", got, err)
|
|
}
|
|
// 传入带路径的文件名应按 basename 匹配
|
|
if got, err := fetchExpectedHash(ctx, srv.URL, "/some/dir/file-a.tar.gz"); err != nil || got != "aaa" {
|
|
t.Errorf("basename 匹配失败: got=%q err=%v", got, err)
|
|
}
|
|
// 文件名不存在应报错
|
|
if _, err := fetchExpectedHash(ctx, srv.URL, "not-exist.tar.gz"); err == nil {
|
|
t.Error("校验文件中不存在目标文件时应返回错误")
|
|
}
|
|
}
|
|
|
|
func TestComputeSHA256(t *testing.T) {
|
|
content := "hello team-api"
|
|
filePath := writeTempFile(t, "bin", content)
|
|
|
|
sum := sha256.Sum256([]byte(content))
|
|
want := hex.EncodeToString(sum[:])
|
|
|
|
got, err := computeSHA256(context.Background(), filePath)
|
|
if err != nil {
|
|
t.Fatalf("计算 SHA256 失败: %v", err)
|
|
}
|
|
if got != want {
|
|
t.Errorf("SHA256 不匹配: got=%s want=%s", got, want)
|
|
}
|
|
}
|