mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-05 16:57:32 +00:00
## 一、e2e 一直 SKIP,等于从未验证
`TestExternalRTChild_*` 4 条判据长期 SKIP(缺插件产物)。本次用
`third_party/hologramagentsdk/example/rtvoice/build/plugin.bin`
实跑,立刻暴露两处**测试与插件不同步**:
rtvoice_inject 插件已改名为 <name>_say ⇒ 注入失败
rtvoice_dump 插件从来没有这个工具 ⇒ 观测失败
改名发生在插件侧,而判据长期不运行,所以没人发现。这正是
"SKIP 判据等于没有判据"的实证。
修法:
· 注入改用 `rtvoice-e2e_say`(插件当前唯一调试入口,走与麦克风
完全相同的 p.Push → InjectInterruptTextOpts 路径)
· 输出观测改为对 outputch 设备做一次**跨进程 RPC**(channelDevice
句柄 → 插件进程 → 拿回执),不依赖插件内部私有状态
## 二、策略撤销判据从源码扫描改为行为断言
`TestChildTeardownRevokesPolicy` 原先扫源码文本,找停止函数体里有没有
`revokeChildPolicy`。R2/R3 把撤销移进骨架 Cleanup 后文本匹配失效 ——
但行为是对的,判据自己变红。
这暴露出它抓不住的真实风险:`StopDeps.RevokePolicy` 是**注入**的,
漏填时 Cleanup 里的 nil 检查会静默跳过,而字符串仍在别处,源码扫描照样绿。
现在分两层:
· kernelmod/all 保留源码断言,但钉**两个环节成对**:
① Cleanup 真的调用 deps.RevokePolicy
② 各类型真的注入它
并增加"单数与复数必须共用骨架 Stop"的结构护栏
· agent/core 新增行为判据 childpolicy_test.go:
声明策略 → 走真实停止路径 → 主体表里必须消失
## 三、验证
go build ./... PASS
go vet ./... PASS
gofmt -l 干净
go test ./... -count=1 62 包全过,0 FAIL
go test ./... -count=1 -race 0 FAIL
rt 跨进程 e2e(4 条,不再 SKIP):
TestExternalRTChild_FullChain PASS(rtchild 跑了 1 轮)
TestExternalRTChild_ReuseDoesNotLeakTranscript PASS
TestExternalRTChild_BulkStopUnregistersInbound PASS
TestExternalRTChild_RepeatedCyclesDoNotAccumulate PASS
变异验证:断开 routeInputByOwner 的 rt 分支 ⇒
FullChain 立即红,并准确报出"输入没有到达 rtSlot"
干净检出(git worktree)单独 build + vet + test + e2e PASS
118 lines
4.0 KiB
Go
118 lines
4.0 KiB
Go
package core
|
||
|
||
// 判据:**销毁路径真的撤销了子策略**(行为判据)。
|
||
//
|
||
// ## 为什么需要它(而不是只靠源码扫描)
|
||
//
|
||
// R2/R3 把"撤销策略"从两个停止函数搬进了骨架 `child.Registry.Cleanup`,
|
||
// 由各类型注入的 `StopDeps.RevokePolicy` 触发。这带来一个新的、**源码扫描
|
||
// 看不见**的风险:
|
||
//
|
||
// StopDeps.RevokePolicy 漏填 ⇒ Cleanup 里那个 `if deps.RevokePolicy != nil`
|
||
// 直接跳过,静默不撤销
|
||
//
|
||
// 字符串 `revokeChildPolicy` 仍然出现在 `security_profile.go` 的定义体里,
|
||
// 所以源码扫描照样绿 —— 那正是结构判据的局限(engineering-lessons §1.1)。
|
||
//
|
||
// ## 判据语义
|
||
//
|
||
// 声明一条子策略 ⇒ 主体表里存在(前置)
|
||
// 走**真实销毁路径** ⇒ 主体表里消失(被测行为)
|
||
//
|
||
// 变红条件:
|
||
// - rtStopDeps 漏填 RevokePolicy ⇒ 红
|
||
// - Cleanup 不调 deps.RevokePolicy ⇒ 红
|
||
// - teardownResidentWith 删掉撤销那一步 ⇒ 红
|
||
|
||
import (
|
||
"testing"
|
||
|
||
"github.com/JianFeeeee/HologramAgent/internal/security"
|
||
)
|
||
|
||
// policyRTHost 造一个"父 agent + security 运行时 + 成对 rt 通道"。
|
||
//
|
||
// 复用 newRTHost 的注册方式,但多给一个 security 运行时(撤销策略要有地方落)。
|
||
func policyRTHost(t *testing.T) (*Agent, *security.Runtime) {
|
||
t.Helper()
|
||
rt := security.NewRuntime(nil)
|
||
host, _ := newRTHost(t, &rtBlockingProvider{})
|
||
host.security = rt
|
||
return host, rt
|
||
}
|
||
|
||
// TestRTChild_StopRevokesPolicy 钉住 rt 的**单数**停止撤销策略。
|
||
func TestRTChild_StopRevokesPolicy(t *testing.T) {
|
||
host, rt := policyRTHost(t)
|
||
defer host.StopRTChildren()
|
||
|
||
id := rtSpawn(t, host, "rp-1")
|
||
if err := host.ProbeDeclareChildPolicy(id, nil, []string{"cmd_run"}); err != nil {
|
||
t.Fatalf("声明子策略: %v", err)
|
||
}
|
||
if !rt.HasPrincipalPolicy(id) {
|
||
t.Fatal("前置失败:声明后主体表里没有该子")
|
||
}
|
||
|
||
if err := host.StopRTChild(id); err != nil {
|
||
t.Fatalf("StopRTChild: %v", err)
|
||
}
|
||
if rt.HasPrincipalPolicy(id) {
|
||
t.Fatal("★ StopRTChild 之后主体策略仍在 —— StopDeps.RevokePolicy 可能漏填," +
|
||
"Cleanup 里的 nil 检查会静默跳过。同名重建将继承上一代父留的约束")
|
||
}
|
||
}
|
||
|
||
// TestRTChildren_StopAllRevokesPolicy 钉住**复数**停止与单数对称。
|
||
//
|
||
// 这是 engineering-lessons.md §2.3「单数改了复数漏了」的行为版护栏:
|
||
// 源码扫描只能看到"函数里有没有那个字符串",看不到"复数路径到底撤没撤"。
|
||
func TestRTChildren_StopAllRevokesPolicy(t *testing.T) {
|
||
host, rt := policyRTHost(t)
|
||
defer host.StopRTChildren()
|
||
|
||
ids := []string{"rb-1", "rb-2", "rb-3"}
|
||
for _, id := range ids {
|
||
got := rtSpawn(t, host, id)
|
||
if err := host.ProbeDeclareChildPolicy(got, nil, []string{"cmd_run"}); err != nil {
|
||
t.Fatalf("声明 %s: %v", got, err)
|
||
}
|
||
if !rt.HasPrincipalPolicy(got) {
|
||
t.Fatalf("前置失败:声明后主体表里没有 %s", got)
|
||
}
|
||
}
|
||
|
||
if n := host.StopRTChildren(); n != len(ids) {
|
||
t.Fatalf("StopRTChildren 停止 %d 个,期望 %d", n, len(ids))
|
||
}
|
||
for _, id := range ids {
|
||
if rt.HasPrincipalPolicy(id) {
|
||
t.Fatalf("★ StopRTChildren 之后主体策略仍在(%s)—— "+
|
||
"复数路径与单数不对称(engineering-lessons §2.3 的形态)", id)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestResident_DestroyRevokesPolicy 钉住驻留子销毁撤销策略。
|
||
func TestResident_DestroyRevokesPolicy(t *testing.T) {
|
||
parent, _, dir := newRootWith(t, &countingProvider{})
|
||
rt := security.NewRuntime(nil)
|
||
parent.security = rt
|
||
|
||
const id = "res-rp"
|
||
spawnTestResident(t, parent, dir, id)
|
||
if err := parent.ProbeDeclareChildPolicy(id, nil, []string{"cmd_run"}); err != nil {
|
||
t.Fatalf("声明子策略: %v", err)
|
||
}
|
||
if !rt.HasPrincipalPolicy(id) {
|
||
t.Fatal("前置失败:声明后主体表里没有该驻留子")
|
||
}
|
||
|
||
if err := parent.DestroyResident(id); err != nil {
|
||
t.Fatalf("DestroyResident: %v", err)
|
||
}
|
||
if rt.HasPrincipalPolicy(id) {
|
||
t.Fatal("★ 驻留子销毁后主体策略仍在 —— 同名重建会继承上一代约束")
|
||
}
|
||
}
|