mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-12 12:17:36 +00:00
test: 按模块导出接口重写黑盒判据(批次 5:vector / systemd / recovery)
3 个文件全部在 `<module>/test/`,源码目录仍零 `_test.go`。
共 17 条判据,只调导出接口,输入 → 可观察输出。
## internal/memory/vector —— 纯数学 + Store
TestCosineSimilarity 稀疏向量:同向/正交/零向量
TestDenseCosine 稠密向量:三种基本情形 + 非单位向量
TestStoreInsertSearchRemove Insert → Search 命中 → Remove → 不再命中
**变异第一版没红**:`DenseCosine` 去掉归一化后,我给的 `{1,0}`
用例点积恰好还是 1 ⇒ 空变异。补 `{4,4} / {2,2}`(不归一化得 16,
归一化得 1)后才真红。**变异必须真的改变被测行为**,这条按纪律记下。
## internal/systemd —— State / NewMigratePlan
TestStateInSync 相同/等价/空/不同路径
TestStateDescribe 一致→空;不一致→含两路径与后果
TestSyncCommand 不一致时给出可执行命令
TestNewMigratePlanNormalizesPaths 路径 Clean;脚本内联新旧路径 + shebang
TestRestartCommand 命令含给定地址
不读单元文件、不执行脚本、不碰系统状态。
## internal/recovery —— 文件往返
TestTaskRoundTrip SaveTask → LoadTask 全字段 + Plugins()
TestTaskPathsAreStable TaskPath/ResultPath 稳定且互异
TestResultRoundTrip SaveResult → LoadResult 结论字段
TestLoadTaskMissingFileReportsError
TestSortSteps 输出可用
## 变异验证(改回旧实现必须红)
vector 不归一化 → ★ 非单位同向 = 16,期望 1
systemd InSync 恒 true → ★ 真不一致: InSync=true
recovery 丢 PluginList → ★ PluginList 丢失:[]
全部还原后复跑全绿。
## 验证
go build ./... PASS
go vet ./... PASS
gofmt -l 干净
python3 .github/scripts/check-ci-gates.py PASS
go test ./... -count=1 20 个测试包 / 100 包无测试 / 0 FAIL
3 个新包 -race 全绿
源码目录 _test.go 0
This commit is contained in:
86
internal/systemd/test/sync_contract_test.go
Normal file
86
internal/systemd/test/sync_contract_test.go
Normal file
@ -0,0 +1,86 @@
|
||||
package test
|
||||
|
||||
// internal/systemd 的黑盒判据:State/NewMigratePlan 的导出方法,输入 → 输出。
|
||||
// 不读单元文件、不执行脚本、不碰系统状态。
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/JianFeeeee/HologramAgent/internal/systemd"
|
||||
)
|
||||
|
||||
// TestStateInSync:相同路径(含 ./ 与尾部斜杠)判一致;不同判不一致。
|
||||
func TestStateInSync(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
r, c string
|
||||
want bool
|
||||
}{
|
||||
{"完全相同", "/data/home", "/data/home", true},
|
||||
{"路径等价", "/data/home/", "/data/home/./", true},
|
||||
{"空运行期", "", "/data/home", true},
|
||||
{"空配置", "/data/home", "", true},
|
||||
{"真不一致", "/data/a", "/data/b", false},
|
||||
}
|
||||
for _, c := range cases {
|
||||
s := systemd.State{RuntimeDataDir: c.r, ConfigDataDir: c.c}
|
||||
if got := s.InSync(); got != c.want {
|
||||
t.Errorf("★ %s: InSync=%v,期望 %v", c.name, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestStateDescribe:一致时诊断为空;不一致时给出两个路径与后果。
|
||||
func TestStateDescribe(t *testing.T) {
|
||||
ok := systemd.State{RuntimeDataDir: "/d", ConfigDataDir: "/d"}
|
||||
if d := ok.Describe(); d != "" {
|
||||
t.Fatalf("★ 一致时 Describe 应为空,实际 %q", d)
|
||||
}
|
||||
bad := systemd.State{RuntimeDataDir: "/data/a", ConfigDataDir: "/data/b"}
|
||||
d := bad.Describe()
|
||||
if !strings.Contains(d, "/data/a") || !strings.Contains(d, "/data/b") {
|
||||
t.Fatalf("★ Describe 缺两个路径:%q", d)
|
||||
}
|
||||
if !strings.Contains(d, "数据分裂") {
|
||||
t.Fatalf("★ Describe 没说明后果:%q", d)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSyncCommand:不一致时给出可执行的同步命令。
|
||||
func TestSyncCommand(t *testing.T) {
|
||||
s := systemd.State{RuntimeDataDir: "/data/a", ConfigDataDir: "/data/b"}
|
||||
cmd := s.SyncCommand("/data/a")
|
||||
if cmd == "" {
|
||||
t.Fatal("★ 不一致时 SyncCommand 为空")
|
||||
}
|
||||
if !strings.Contains(cmd, "/data/a") {
|
||||
t.Fatalf("★ SyncCommand 不含目标路径:%q", cmd)
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewMigratePlanNormalizesPaths:构造计划时路径被 Clean,脚本含新旧路径。
|
||||
func TestNewMigratePlanNormalizesPaths(t *testing.T) {
|
||||
old := "/data/old/./"
|
||||
newDir := "/data/new//"
|
||||
p := systemd.NewMigratePlan(old, newDir)
|
||||
if p.OldDir != filepath.Clean(old) || p.NewDir != filepath.Clean(newDir) {
|
||||
t.Fatalf("★ 路径未 Clean:old=%q new=%q", p.OldDir, p.NewDir)
|
||||
}
|
||||
script := p.Script()
|
||||
if !strings.Contains(script, p.OldDir) || !strings.Contains(script, p.NewDir) {
|
||||
t.Fatal("★ 迁移脚本没内联新旧路径")
|
||||
}
|
||||
if !strings.HasPrefix(script, "#!/usr/bin/env bash") {
|
||||
t.Fatal("★ 脚本缺 shebang —— 不能直接执行")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRestartCommand:重启命令必须包含给定地址。
|
||||
func TestRestartCommand(t *testing.T) {
|
||||
cmd := systemd.RestartCommand("127.0.0.1:8080", "test-key")
|
||||
if !strings.Contains(cmd, "127.0.0.1:8080") {
|
||||
t.Fatalf("★ RestartCommand 不含地址:%q", cmd)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user