mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-12 12:17:36 +00:00
3 个文件全部在 `<module>/test/`,源码目录仍零 `_test.go`。
共 15 条判据,只调导出接口,输入 → 可观察输出。
## internal/update —— 版本/升级纯函数
TestParseSemver 三段式(可带 v);非纯版本返回 nil
TestCmpSemver 大小/相等;nil 返回 0(不可比)
TestNewer 只认纯三段式;dev 后缀不误报
TestCompareVersions 升级/降级/完全相同/同版本不同 commit
TestDeltaAssetName 产物名内联版本/平台/来源 commit
TestShortCommit 长 commit 截 8 位
TestBrokenDirPath 输出落在 dataDir 下且含时间戳
## internal/kernelboot —— 依赖图编解码
TestGraphEncodeDecodeRoundTrip Encode → Decode 模块与层号
TestGraphDigestIsDeterministic 同声明稳定;换声明则变
TestDecodeGraphRejectsCorruption 位翻转/截断必拒
TestCheckAgainstBinary dev 放行 / 不配套拒 / 配套通过
TestFuncModuleExposesSpec Name/Provides/Requires
**发现一条假绿并修掉**:`TestDecodeGraphRejectsCorruption` 第一版
翻转 `data[10]`(magic/长度区),跳过 hash 校验后仍会因格式标识报错
⇒ 判据假绿。改成只翻转尾部 hash 之前的最后一个 payload 字节,
让唯一能抓到它的就是 hash 自校验,变异才真红。
## internal/devicebridge/client —— BaseResult
TestBaseResultAlwaysCarriesIdentity op/req_id/status
TestBaseResultOmitsEmptyOptionalFields 空 output/error 不进结果
TestBaseResultCarriesOutputAndError 非空时落对应键
## 变异验证(改回旧实现必须红)
update CmpSemver 恒 0 → ★ 版本大小比较错误
kernelboot 跳过 hash 校验 → ★ 数据区位翻转的图被接受
devicebridge 丢 req_id → ★ 身份字段缺失
kernelboot 第一版变异造成「未使用变量 → 编译失败」,不算干净断言红;
改成跳过校验但保留变量后确认判据有效。
## 验证
go build ./... PASS
go vet ./... PASS
gofmt -l 干净
python3 .github/scripts/check-ci-gates.py PASS
go test ./... -count=1 42 个测试包 / 100 包无测试 / 0 FAIL
3 个新包 -race 全绿
源码目录 _test.go 0
120 lines
3.6 KiB
Go
120 lines
3.6 KiB
Go
package test
|
||
|
||
// internal/update 的黑盒判据:纯函数的输入 → 输出。
|
||
// 不读源码、不跑外部命令、不碰内部正则表。
|
||
|
||
import (
|
||
"strings"
|
||
"testing"
|
||
|
||
"github.com/JianFeeeee/HologramAgent/internal/update"
|
||
)
|
||
|
||
// TestParseSemver:合法三段式(可带 v)解析;非纯版本返回 nil。
|
||
func TestParseSemver(t *testing.T) {
|
||
cases := []struct {
|
||
in string
|
||
wantNil bool
|
||
maj, min, pat int
|
||
}{
|
||
{"1.4.0", false, 1, 4, 0},
|
||
{"v2.10.3", false, 2, 10, 3},
|
||
{"1.4.0dev", true, 0, 0, 0},
|
||
{"1.4", true, 0, 0, 0},
|
||
{"abc", true, 0, 0, 0},
|
||
{"", true, 0, 0, 0},
|
||
}
|
||
for _, c := range cases {
|
||
got := update.ParseSemver(c.in)
|
||
if c.wantNil {
|
||
if got != nil {
|
||
t.Errorf("★ ParseSemver(%q) = %+v,期望 nil", c.in, got)
|
||
}
|
||
continue
|
||
}
|
||
if got == nil || got.Major != c.maj || got.Minor != c.min || got.Patch != c.pat {
|
||
t.Errorf("★ ParseSemver(%q) = %+v", c.in, got)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestCmpSemver:大小与相等;nil 返回 0(不可比)。
|
||
func TestCmpSemver(t *testing.T) {
|
||
a := update.ParseSemver("1.4.0")
|
||
b := update.ParseSemver("1.4.1")
|
||
c := update.ParseSemver("1.4.0")
|
||
if update.CmpSemver(a, b) >= 0 || update.CmpSemver(b, a) <= 0 {
|
||
t.Fatal("★ 版本大小比较错误")
|
||
}
|
||
if update.CmpSemver(a, c) != 0 {
|
||
t.Fatal("★ 同版本未判等")
|
||
}
|
||
if update.CmpSemver(nil, a) != 0 || update.CmpSemver(a, nil) != 0 {
|
||
t.Fatal("★ nil 未返回 0(不可比)")
|
||
}
|
||
}
|
||
|
||
// TestNewer:只认纯三段式;dev 后缀不误报。
|
||
func TestNewer(t *testing.T) {
|
||
if !update.Newer("1.5.0", "1.4.0") {
|
||
t.Fatal("★ 新版本未判新")
|
||
}
|
||
if update.Newer("1.4.0", "1.4.0") {
|
||
t.Fatal("★ 相同版本判新")
|
||
}
|
||
if update.Newer("1.4.0dev", "1.4.0") {
|
||
t.Fatal("★ dev 后缀被误判为新版本")
|
||
}
|
||
}
|
||
|
||
// TestCompareVersions:升级 / 降级 / 完全相同 / 同版本不同 commit。
|
||
func TestCompareVersions(t *testing.T) {
|
||
cur := &update.BinVersion{Version: "1.4.0", Commit: "aaaaaaaa"}
|
||
cases := []struct {
|
||
nv *update.BinVersion
|
||
contains string
|
||
}{
|
||
{&update.BinVersion{Version: "1.5.0", Commit: "bbbbbbbb"}, "可升级"},
|
||
{&update.BinVersion{Version: "1.3.0", Commit: "bbbbbbbb"}, "降级"},
|
||
{&update.BinVersion{Version: "1.4.0", Commit: "aaaaaaaa"}, "完全相同"},
|
||
{&update.BinVersion{Version: "1.4.0", Commit: "bbbbbbbb"}, "commit 不同"},
|
||
}
|
||
for _, c := range cases {
|
||
got := update.CompareVersions(cur, c.nv)
|
||
if !strings.Contains(got, c.contains) {
|
||
t.Errorf("★ CompareVersions = %q,期望含 %q", got, c.contains)
|
||
}
|
||
}
|
||
if got := update.CompareVersions(nil, cur); !strings.Contains(got, "无法读取") {
|
||
t.Fatalf("★ nil 当前版本的输出不可诊断:%q", got)
|
||
}
|
||
}
|
||
|
||
// TestDeltaAssetName:产物名内联版本/平台/来源 commit。
|
||
func TestDeltaAssetName(t *testing.T) {
|
||
got := update.DeltaAssetName("1.4.0", "linux", "amd64", "abcdef123456")
|
||
for _, want := range []string{"1.4.0", "linux", "amd64", "abcdef12", ".delta"} {
|
||
if !strings.Contains(got, want) {
|
||
t.Errorf("★ DeltaAssetName = %q,缺 %q", got, want)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestShortCommit:长 commit 截 8 位;空/unknown 原样。
|
||
func TestShortCommit(t *testing.T) {
|
||
if got := update.ShortCommit("abcdef123456"); got != "abcdef12" {
|
||
t.Fatalf("★ ShortCommit = %q", got)
|
||
}
|
||
if got := update.ShortCommit("unknown"); got != "unknown" {
|
||
t.Fatalf("★ unknown 未保留:%q", got)
|
||
}
|
||
}
|
||
|
||
// TestBrokenDirPath:输出落在 dataDir 下且含时间戳。
|
||
func TestBrokenDirPath(t *testing.T) {
|
||
got := update.BrokenDirPath("/data/home", "20261109-120000")
|
||
if !strings.HasPrefix(got, "/data/home") || !strings.Contains(got, "20261109-120000") {
|
||
t.Fatalf("★ BrokenDirPath = %q", got)
|
||
}
|
||
}
|