mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-12 04:07:34 +00:00
三个模块新增 test/ 黑盒契约,测试仅通过导出 API 驱动。
## internal/plugins/mcp
TestListToolsThroughHTTP
TestCallToolSuccessAndEmptyContentHandling
TestCallToolSurfacesIsErrorAsFailure
TestCallToolSurfacesRPCError
TestCallToolSurfacesHTTPError
TestListToolsParseSSEStreamFrame
使用 httptest + NewSSETransport + NewServer,覆盖 JSON-RPC 工具列表、
成功调用、空内容失败、MCP isError、RPC 错误、HTTP 错误和 SSE data 帧。
## internal/plugin/proc
TestMajorVersionParsing
TestKnownCapabilitiesExcludesCoreAndIsStable
TestSharedRefSliceBoundary
TestPublicCapabilityConstantsStable
覆盖版本主段解析、能力清单排序/去重/core 排除、共享引用切片边界和能力常量。
## internal/plugins/cfgmgr
TestStartRegistersConfigToolsWithConcurrencyMarkers
TestStopIsSafe
通过公开 Start 注入 SDK 工具注册回调,观察七个配置工具及其
Serial/ParallelSafe 并发契约;未访问私有 handler。
## 变异验证
mcp 忽略 MCP isError → isError 未被判失败
proc MajorVersion 不截断 → 多组版本断言红
cfgmgr 写工具取消 Serial 标记 → 写工具未声明 Serial
## 验证
go build ./... PASS
go vet ./... PASS
gofmt -l 干净
python3 .github/scripts/check-ci-gates.py PASS
go test ./... -count=1 51 个测试包 / 100 包无测试 / 0 FAIL
3 个新包 -race 全绿
源码目录 _test.go 0
88 lines
2.3 KiB
Go
88 lines
2.3 KiB
Go
package test
|
||
|
||
// internal/plugin/proc 的黑盒判据:公开纯函数与值对象语义。
|
||
|
||
import (
|
||
"testing"
|
||
|
||
"github.com/JianFeeeee/HologramAgent/internal/plugin/proc"
|
||
)
|
||
|
||
func TestMajorVersionParsing(t *testing.T) {
|
||
cases := []struct{ in, want string }{
|
||
{"1.4.0", "1"},
|
||
{"v2.10.3", "2"},
|
||
{"0.9", "0"},
|
||
{"17", "17"},
|
||
{"", ""},
|
||
{" v3.1 ", "3"},
|
||
}
|
||
for _, c := range cases {
|
||
if got := proc.MajorVersion(c.in); got != c.want {
|
||
t.Errorf("MajorVersion(%q)=%q,期望 %q", c.in, got, c.want)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestKnownCapabilitiesExcludesCoreAndIsStable(t *testing.T) {
|
||
first := proc.KnownCapabilities()
|
||
second := proc.KnownCapabilities()
|
||
if len(first) == 0 {
|
||
t.Fatal("KnownCapabilities 为空")
|
||
}
|
||
for i := range first {
|
||
if first[i] == "core" {
|
||
t.Fatal("core 能力出现在声明清单里(无需声明)")
|
||
}
|
||
if i > 0 && first[i-1] >= first[i] {
|
||
t.Fatalf("能力清单未按升序:%v", first)
|
||
}
|
||
}
|
||
for i := range first {
|
||
if first[i] != second[i] {
|
||
t.Fatal("两次调用结果不一致(无确定性)")
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestSharedRefSliceBoundary(t *testing.T) {
|
||
data := []byte("0123456789")
|
||
ok := proc.SharedRef{Offset: 2, Length: 4}
|
||
if got := ok.Slice(data); string(got) != "2345" {
|
||
t.Fatalf("Slice=%q", got)
|
||
}
|
||
if ok.IsZero() {
|
||
t.Fatal("非零 ref 被判为零值")
|
||
}
|
||
if !(proc.SharedRef{}).IsZero() {
|
||
t.Fatal("零值 ref 未判零")
|
||
}
|
||
// 越界: offset+length 超出 data 时返回 nil
|
||
oob := proc.SharedRef{Offset: 8, Length: 8}
|
||
if oob.Slice(data) != nil {
|
||
t.Fatalf("越界 Slice 未返回 nil:%q", oob.Slice(data))
|
||
}
|
||
// 零值: 长度 0
|
||
if zero := (proc.SharedRef{}).Slice(data); zero != nil {
|
||
t.Fatalf("零值 ref Slice 应 nil:%q", zero)
|
||
}
|
||
}
|
||
|
||
func TestPublicCapabilityConstantsStable(t *testing.T) {
|
||
want := map[string]proc.Capability{
|
||
"core": proc.CapCore, "io": proc.CapIO, "memory": proc.CapMemory,
|
||
"doc_memory": proc.CapDocMemory, "knowledge": proc.CapKnowledge,
|
||
"text_memory": proc.CapTextMemory, "llm": proc.CapLLM,
|
||
"social": proc.CapSocial, "events": proc.CapEvents,
|
||
"plugin_mgr": proc.CapPluginMgr, "kernel_control": proc.CapKernelControl,
|
||
"settings_cross": proc.CapCrossPluginSettings,
|
||
}
|
||
for name, cap := range want {
|
||
if string(cap) != name {
|
||
t.Errorf("常量 %q 值与名不符:%q", name, cap)
|
||
}
|
||
}
|
||
_ = proc.MethodHandshake
|
||
_ = proc.DefaultHandshakeTimeout
|
||
}
|