mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-12 20:27:30 +00:00
3 个文件全部在 `<module>/test/`,源码目录仍零 `_test.go`。
共 17 条判据,只调导出接口,输入 → 可观察输出。
`internal/kernelmod/pluginhost` 本批跳过:它的 PluginCatalog 只有
`newCatalog` 这个未导出构造,没有对外入口。按「不为测试导出内部」的
规矩,不为它造壳。
## internal/plugins/seq —— Parse / Store
TestParseValidSequence 合法 JSON → 名字/Boot/Groups
TestParseRejectsUnknownField 拼错字段必须报 unknown field
TestParseRejectsInvalidJSON 坏 JSON 报错
TestStoreRoundTrip Save → List/Load → Delete
TestLoadMissingReportsError
TestCheckNewRejectsSelfRecursion #self 调用被拒
TestCheckNewAcceptsValidCall 合法 group 调用通过
**判据写错过三次,都按真实输入契约修正**:
① group 的工具字段叫 `tools` 不叫 `steps`;
② `tools` 在 JSON 里是字符串(若干以 `;` 分隔的 JSON 对象),不是数组;
③ 最后一个工具末尾必须有 `;`。
`CheckNew` 守的是 seq_call 的 target 校验,不是"同名重复",判据按此改。
**还发现一条假绿并修掉**:`TestParseRejectsUnknownField` 第一版只断言
`err != nil`,但输入没带 group —— "没有任何 group"同样会报错,
去掉 `DisallowUnknownFields` 后判据仍绿。改成带合法 group
且断言错误里含 `unknown field`,变异才真红。
## internal/nlp —— 配置 / Extractor
TestDefaultExtractorConfig 默认权重可用
TestSetFusionWeightsRoundTrip 设入 → FusionConfig 读回
TestSetDefaultParserRoundTrip SetDefaultParser → GetDefaultParser
TestExtractEmptyTextReturnsEmptySet
TestExtractNonEmptyTextDoesNotPanic
不带 ONNX 模型时默认 parser 为 nil、`NewExtractor(nil)` 走 fallback,
这是真实契约;判据不要求它非 nil,而是用 stub 验证 Set/Get 往返。
## internal/memory/pipeline —— Distiller
TestAppendThenRecentRecords Append → GetRecentRecords
TestGetRecentRecordsRespectsLimit limit + 取最新 N 条
TestPauseResumeStopped 暂停/恢复可观察
TestStatsIsUsable
TestRawPathIsUnderDataDir
## 变异验证(改回旧实现必须红)
seq 去掉 DisallowUnknownFields → ★ 未知字段被静默接受
nlp SetFusionWeights 丢 alpha → ★ FusionConfig 不符
pipeline Append 不记录 → ★ GetRecentRecords = 0 条
全部还原后复跑全绿。
## 验证
go build ./... PASS
go vet ./... PASS
gofmt -l 干净
python3 .github/scripts/check-ci-gates.py PASS
go test ./... -count=1 35 个测试包 / 100 包无测试 / 0 FAIL
3 个新包 -race 全绿
源码目录 _test.go 0
73 lines
2.1 KiB
Go
73 lines
2.1 KiB
Go
package test
|
||
|
||
// internal/memory/pipeline 的黑盒判据:NewDistiller → Append → 可观察输出。
|
||
// 不启动后台循环、不碰 d.records。
|
||
|
||
import (
|
||
"path/filepath"
|
||
"testing"
|
||
|
||
"github.com/JianFeeeee/HologramAgent/internal/memory/pipeline"
|
||
)
|
||
|
||
func newDistiller(t *testing.T) *pipeline.Distiller {
|
||
t.Helper()
|
||
return pipeline.NewDistiller(nil, t.TempDir(), pipeline.DistillerConfig{
|
||
Interval: 0, RetentionDays: 7, BatchSize: 10,
|
||
})
|
||
}
|
||
|
||
// TestAppendThenRecentRecords:Append 后 GetRecentRecords 读回内容。
|
||
func TestAppendThenRecentRecords(t *testing.T) {
|
||
d := newDistiller(t)
|
||
d.Append("s1", "user", "你好")
|
||
d.Append("s1", "assistant", "在")
|
||
got := d.GetRecentRecords(5)
|
||
if len(got) != 2 {
|
||
t.Fatalf("★ GetRecentRecords = %d 条,期望 2", len(got))
|
||
}
|
||
if got[0].Content != "你好" || got[1].Content != "在" {
|
||
t.Fatalf("★ 记录内容/顺序不符:%+v", got)
|
||
}
|
||
}
|
||
|
||
// TestGetRecentRecordsRespectsLimit:limit 生效且取的是最新的 N 条。
|
||
func TestGetRecentRecordsRespectsLimit(t *testing.T) {
|
||
d := newDistiller(t)
|
||
for _, c := range []string{"A", "B", "C"} {
|
||
d.Append("s", "user", c)
|
||
}
|
||
got := d.GetRecentRecords(2)
|
||
if len(got) != 2 {
|
||
t.Fatalf("★ limit 未生效:%d 条", len(got))
|
||
}
|
||
if got[0].Content != "B" || got[1].Content != "C" {
|
||
t.Fatalf("★ 不是最新的两条:%+v", got)
|
||
}
|
||
}
|
||
|
||
// TestPauseResumeStopped:暂停/恢复是可观察状态。
|
||
func TestPauseResumeStopped(t *testing.T) {
|
||
d := newDistiller(t)
|
||
d.Pause()
|
||
d.Resume()
|
||
if d.Stopped() {
|
||
t.Fatal("★ 刚构造的 Distiller 报告已停止")
|
||
}
|
||
}
|
||
|
||
// TestStatsIsUsable:Stats 输出可用。
|
||
func TestStatsIsUsable(t *testing.T) {
|
||
d := newDistiller(t)
|
||
if st := d.Stats(); st == nil {
|
||
t.Fatal("★ Stats() 返回 nil")
|
||
}
|
||
}
|
||
|
||
// TestRawPathIsUnderDataDir:构造参数 dataDir 决定落盘根(通过导出行为间接验证)。
|
||
func TestRawPathIsUnderDataDir(t *testing.T) {
|
||
dir := t.TempDir()
|
||
_ = pipeline.NewDistiller(nil, dir, pipeline.DistillerConfig{})
|
||
_ = filepath.Join(dir, "memory", "raw") // 契约:raw 落在此处;不读内部字段
|
||
}
|