mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-12 04:07:34 +00:00
test: 按模块导出接口重写黑盒判据(批次 12:plugin / qwen3vl / chineseclip)
3 个文件全部在 `<module>/test/`,源码目录仍零 `_test.go`。
新增公开输入→输出判据:
## internal/plugin —— SKILL 内容与加载
TestValidateSKILLContent
合法内容通过;空内容/无描述/非法工具名分别被拒
TestLoadSKILLDirectoryExposesMetadataAndTools
临时 SKILL.md → Name/Version/Description/Tools/IOConfig
TestLoadSKILLMissingFileReturnsError
## providers/qwen3vl —— BPE tokenizer 公开契约
TestTokenizerSpecialTokenRoundTrip
临时 tokenizer.json → VocabSize/SpecialID/Encode/DecodeOne
TestTokenizerMissingFileReturnsError
## providers/chineseclip —— WordPiece tokenizer 公开契约
TestTokenizerEncodeUsesSpecialTokensAndPadding
临时 vocab.txt → 固定长度 ids 与 attention mask
TestTokenizerRejectsInvalidVocabulary
缺特殊 token / 非正 maxLength → 报错
所有测试都用 `package test` 导入模块,通过导出 API 操作;不访问私有符号,
不读取源码/文档作为业务断言。
## 变异验证
plugin ValidateSKILLContent 放过无描述内容 → no_description 断言红
qwen3vl Encode 丢弃特殊 token → Encode=[] 断言红
chineseclip Encode 不补 padding → 固定长度判据红
## 验证
go build ./... PASS
go vet ./... PASS
gofmt -l 干净
python3 .github/scripts/check-ci-gates.py PASS
go test ./... -count=1 45 个测试包 / 100 包无测试 / 0 FAIL
3 个新包 -race 全绿
源码目录 _test.go 0
This commit is contained in:
62
internal/plugin/test/skill_contract_test.go
Normal file
62
internal/plugin/test/skill_contract_test.go
Normal file
@ -0,0 +1,62 @@
|
||||
package test
|
||||
|
||||
// internal/plugin 的黑盒判据:公开内容校验与 SKILL 文件加载。
|
||||
// 测试只通过 ValidateSKILLContent/LoadSKILL 及 Plugin 接口观察结果。
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/JianFeeeee/HologramAgent/internal/plugin"
|
||||
)
|
||||
|
||||
func TestValidateSKILLContent(t *testing.T) {
|
||||
valid := "# Demo\n\n这是一个用于测试的技能。\n\n## greet\n向用户打招呼。\n- name: 用户名\n"
|
||||
if err := plugin.ValidateSKILLContent(valid); err != nil {
|
||||
t.Fatalf("合法 SKILL 被拒:%v", err)
|
||||
}
|
||||
for _, tc := range []struct{ name, content, want string }{
|
||||
{"empty", "", "empty"},
|
||||
{"no description", "# Only heading", "no description"},
|
||||
{"bad tool name", "正文\n\n## bad/name\n描述", "invalid tool name"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if err := plugin.ValidateSKILLContent(tc.content); err == nil || !strings.Contains(err.Error(), tc.want) {
|
||||
t.Fatalf("错误内容的报错不符:%v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadSKILLDirectoryExposesMetadataAndTools(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
content := "---\nversion: 2.1\nauthor: test\nio_type: text\nio_route: chat\nio_output_caps: text,markdown\n---\n# Demo\n\n用于回归的技能。\n\n## greet\n向用户打招呼。\n- name: 用户名\n"
|
||||
if err := os.WriteFile(filepath.Join(dir, "SKILL.md"), []byte(content), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
p, err := plugin.LoadSKILL(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadSKILL: %v", err)
|
||||
}
|
||||
if p.Name() != filepath.Base(dir) || p.Version() != "2.1" || p.Description() != "用于回归的技能。" {
|
||||
t.Fatalf("公开元数据不符:name=%q version=%q description=%q", p.Name(), p.Version(), p.Description())
|
||||
}
|
||||
if p.PluginType() != plugin.PluginTypeSKILL || !p.Enabled() || p.RawContent() != content {
|
||||
t.Fatalf("公开属性不符:type=%q enabled=%v", p.PluginType(), p.Enabled())
|
||||
}
|
||||
if len(p.Tools()) != 1 || p.Tools()[0].Name != "greet" {
|
||||
t.Fatalf("工具定义不符:%+v", p.Tools())
|
||||
}
|
||||
io := p.IOConfig()
|
||||
if io == nil || io.Type != "text" || io.InputRoute != "chat" || io.OutputRoute != "chat" || len(io.OutputCaps) != 2 {
|
||||
t.Fatalf("IO 配置不符:%+v", io)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadSKILLMissingFileReturnsError(t *testing.T) {
|
||||
if _, err := plugin.LoadSKILL(filepath.Join(t.TempDir(), "missing")); err == nil {
|
||||
t.Fatal("不存在的 SKILL 路径却加载成功")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user