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
96 lines
3.3 KiB
Go
96 lines
3.3 KiB
Go
package test
|
||
|
||
// internal/kernelboot 的黑盒判据:EncodeGraph → DecodeGraph / GraphDigest /
|
||
// CheckAgainstBinary 的输入 → 输出。不碰内部 payload 布局。
|
||
|
||
import (
|
||
"testing"
|
||
|
||
"github.com/JianFeeeee/HologramAgent/internal/kernelboot"
|
||
)
|
||
|
||
var edges = []kernelboot.GraphEdge{
|
||
{Name: "security", Provides: []string{"sec"}, Requires: nil, Layer: 1},
|
||
{Name: "agent", Provides: []string{"agent"}, Requires: []string{"sec"}, Layer: 2},
|
||
}
|
||
|
||
// TestGraphEncodeDecodeRoundTrip:编码后能解回同样模块与层号。
|
||
func TestGraphEncodeDecodeRoundTrip(t *testing.T) {
|
||
data := kernelboot.EncodeGraph(edges)
|
||
gf, err := kernelboot.DecodeGraph(data)
|
||
if err != nil {
|
||
t.Fatalf("DecodeGraph: %v", err)
|
||
}
|
||
if gf == nil || len(gf.Modules) != len(edges) {
|
||
t.Fatalf("★ 模块数不符:%+v", gf)
|
||
}
|
||
for i, m := range gf.Modules {
|
||
if m.Name != edges[i].Name || m.Layer != edges[i].Layer {
|
||
t.Fatalf("★ 第 %d 个模块不符:%+v", i, m)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestGraphDigestIsDeterministic:同一份声明 digest 稳定;换声明则变。
|
||
func TestGraphDigestIsDeterministic(t *testing.T) {
|
||
a := kernelboot.GraphDigest(edges)
|
||
b := kernelboot.GraphDigest(edges)
|
||
if a == "" || a != b {
|
||
t.Fatalf("★ digest 不稳定:%q vs %q", a, b)
|
||
}
|
||
other := []kernelboot.GraphEdge{{Name: "x", Provides: []string{"y"}}}
|
||
if c := kernelboot.GraphDigest(other); c == a {
|
||
t.Fatal("★ 不同声明得到同一 digest")
|
||
}
|
||
}
|
||
|
||
// TestDecodeGraphRejectsCorruption:位翻转 / 截断必须报错。
|
||
func TestDecodeGraphRejectsCorruption(t *testing.T) {
|
||
data := kernelboot.EncodeGraph(edges)
|
||
// 只翻转**数据区末尾**的字节(不动 magic / 长度),
|
||
// 这样唯一能抓到它的就是 hash 自校验。翻 magic 的话即使
|
||
// 跳过 hash 也会因格式标识报错,判据会假绿。
|
||
flipped := append([]byte(nil), data...)
|
||
pos := len(flipped) - 32 - 1 // 尾部 hash 之前最后一个 payload 字节
|
||
flipped[pos] ^= 0x01
|
||
if _, err := kernelboot.DecodeGraph(flipped); err == nil {
|
||
t.Fatal("★ 数据区位翻转的图被接受(hash 自校验失效)")
|
||
}
|
||
if _, err := kernelboot.DecodeGraph(data[:len(data)/2]); err == nil {
|
||
t.Fatal("★ 截断的图被接受")
|
||
}
|
||
}
|
||
|
||
// TestCheckAgainstBinary:空内嵌 hash 放行;不匹配报错;匹配通过。
|
||
func TestCheckAgainstBinary(t *testing.T) {
|
||
gf, err := kernelboot.DecodeGraph(kernelboot.EncodeGraph(edges))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := kernelboot.CheckAgainstBinary(gf, ""); err != nil {
|
||
t.Fatalf("★ dev 环境(空 hash)被拒:%v", err)
|
||
}
|
||
if err := kernelboot.CheckAgainstBinary(gf, "deadbeef"); err == nil {
|
||
t.Fatal("★ 不配套的 hash 被放行")
|
||
}
|
||
if err := kernelboot.CheckAgainstBinary(gf, gf.Digest); err != nil {
|
||
t.Fatalf("★ 配套图被拒:%v", err)
|
||
}
|
||
}
|
||
|
||
// TestFuncModuleExposesSpec:FuncModule 对外暴露 Name/Provides/Requires。
|
||
func TestFuncModuleExposesSpec(t *testing.T) {
|
||
m := kernelboot.FuncModule(kernelboot.FuncSpec{
|
||
Name: "contract", Provides: []string{"a"}, Requires: []string{"b"},
|
||
})
|
||
if m.Name() != "contract" {
|
||
t.Fatalf("★ Name = %q", m.Name())
|
||
}
|
||
if len(m.Provides()) != 1 || m.Provides()[0] != "a" {
|
||
t.Fatalf("★ Provides = %v", m.Provides())
|
||
}
|
||
if len(m.Requires()) != 1 || m.Requires()[0] != "b" {
|
||
t.Fatalf("★ Requires = %v", m.Requires())
|
||
}
|
||
}
|