test: 按模块导出接口重写黑盒判据(批次 11:update / kernelboot / devicebridge)

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
This commit is contained in:
JianFeeeee
2026-11-09 14:59:40 +08:00
parent 3420ceca60
commit 08cbd3e7bd
3 changed files with 250 additions and 0 deletions

View File

@ -0,0 +1,36 @@
package test
// internal/devicebridge/client 的黑盒判据:BaseResult 输入 → map 输出。
import (
"testing"
"github.com/JianFeeeee/HologramAgent/internal/devicebridge/client"
)
// TestBaseResultAlwaysCarriesIdentity:op/req_id/status 始终存在。
func TestBaseResultAlwaysCarriesIdentity(t *testing.T) {
r := client.BaseResult("req-1", "ok", "", "")
if r["op"] != "cmd_result" || r["req_id"] != "req-1" || r["status"] != "ok" {
t.Fatalf("★ 身份字段缺失:%v", r)
}
}
// TestBaseResultOmitsEmptyOptionalFields:空 output/error 不进结果。
func TestBaseResultOmitsEmptyOptionalFields(t *testing.T) {
r := client.BaseResult("r", "ok", "", "")
if _, ok := r["output"]; ok {
t.Fatal("★ 空 output 被写进结果")
}
if _, ok := r["error"]; ok {
t.Fatal("★ 空 error 被写进结果")
}
}
// TestBaseResultCarriesOutputAndError:非空时分别落到对应键。
func TestBaseResultCarriesOutputAndError(t *testing.T) {
r := client.BaseResult("r", "error", "stdout-line", "boom")
if r["output"] != "stdout-line" || r["error"] != "boom" {
t.Fatalf("★ 可选字段不符:%v", r)
}
}