Files
HomeAgent/internal/agent/test/personality_contract_test.go
JianFeeeee 3420ceca60 test: 按模块导出接口重写黑盒判据(批次 10:agent / lua / ollama / log)
4 个文件全部在 `<module>/test/`,源码目录仍零 `_test.go`。
共 20 条判据,只调导出接口,输入 → 可观察输出。

## internal/agent —— 人格文件

    TestSaveThenLoadPersonality            Save → Load 内容/路径
    TestLoadMissingPersonalityGivesEmpty   文件不存在 → 空人格
    TestInjectPromptContainsContentAndRules 注入含原文 + 内核说话规则
    TestInjectPromptEmptyPersonalityIsEmpty
    TestPersonaStaleHints                  陈旧措辞给提示;干净文本不报
    TestSavePersonalityCreatesParentDirs   自动建父目录

## internal/lua —— VM 适配器

    TestNewVMExposesAdapterDir             构造参数 → AdapterDir
    TestListAdaptersEmptyThenLoaded        LoadAdapterSource → ListAdapters
    TestLoadAdapterSourceRejectsBadScript
    TestCallTransformRequestUnknownAdapter
    TestGetAdapterEndpointUnknownIsEmpty

## providers/ollama —— httptest 真实 HTTP

    TestGenerateReturnsText                200 → Response.Text
    TestGenerateMarksTruncated             done_reason=length → Truncated
    TestGenerateHTTPError                  5xx → 报错
    TestGenerateRejectsInvalidJSONSchema
    TestNewFallsBackToDefaultModel         空 model 走默认值

**判据写错过一次**:第一版断言"空 model 构造失败",但 `optString`
对空值回退默认,构造成功才是真实契约,按此修正。

## internal/log —— Manager 生命周期

    TestManagerStartStopIsSafe             Start/cancel/Stop 不挂住
    TestManagerStopIsIdempotent            连停两次不 panic
    TestRetentionPoliciesAreDistinct       四个策略常量互异

## 变异验证(改回旧实现必须红)

    agent  SavePersonality 不建父目录  → ★ 文件没落盘
    lua    ListAdapters 恒空           → ★ ListAdapters = [],期望 1 个
    ollama 丢 Truncated               → ★ done_reason=length 未标记 Truncated
    log    Stop 重复 close             → panic: close of closed channel

lua / log 第一版变异分别是文本不匹配与"import 变未使用导致编译失败",
都不算干净的断言红;重做成只改行为后确认有效。

## 验证

    go build ./...                            PASS
    go vet ./...                              PASS
    gofmt -l                                  干净
    python3 .github/scripts/check-ci-gates.py PASS
    go test ./... -count=1                    39 个测试包 / 100 包无测试 / 0 FAIL
    4 个新包 -race                            全绿
    源码目录 _test.go                         0
2026-11-09 14:41:43 +08:00

86 lines
2.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package test
// internal/agent 的黑盒判据:人格文件往返 + InjectPrompt 输出。
// 不碰内部正则表、不读源码。
import (
"os"
"path/filepath"
"strings"
"testing"
"github.com/JianFeeeee/HologramAgent/internal/agent"
)
// TestSaveThenLoadPersonality:Save → Load 拿回原文与路径。
func TestSaveThenLoadPersonality(t *testing.T) {
path := filepath.Join(t.TempDir(), "nested", "personal.md")
const content = "你是小宅,说话自然一点。"
if err := agent.SavePersonality(path, content); err != nil {
t.Fatalf("SavePersonality: %v", err)
}
p, err := agent.LoadPersonality(path)
if err != nil {
t.Fatalf("LoadPersonality: %v", err)
}
if p.Content != content {
t.Fatalf("★ Content = %q,期望原文", p.Content)
}
if p.Path != path {
t.Fatalf("★ Path = %q,期望 %q", p.Path, path)
}
}
// TestLoadMissingPersonalityGivesEmpty:文件不存在返回空人格而非报错。
func TestLoadMissingPersonalityGivesEmpty(t *testing.T) {
p, err := agent.LoadPersonality(filepath.Join(t.TempDir(), "nope.md"))
if err != nil {
t.Fatalf("★ 文件不存在却报错:%v", err)
}
if p == nil || p.Content != "" {
t.Fatalf("★ 期望空人格,实际 %+v", p)
}
}
// TestInjectPromptContainsContentAndRules:非空人格注入后含原文与说话规则。
func TestInjectPromptContainsContentAndRules(t *testing.T) {
p := &agent.Personality{Content: "你是小宅"}
out := p.InjectPrompt()
if !strings.Contains(out, "你是小宅") || !strings.Contains(out, "[Persona]") {
t.Fatalf("★ InjectPrompt 输出不符:%q", out)
}
if len(out) <= len(p.Content) {
t.Fatal("★ 内核没追加说话规则")
}
}
// TestInjectPromptEmptyPersonalityIsEmpty:空人格不注入任何内容。
func TestInjectPromptEmptyPersonalityIsEmpty(t *testing.T) {
if got := (&agent.Personality{}).InjectPrompt(); got != "" {
t.Fatalf("★ 空人格却注入了内容:%q", got)
}
}
// TestPersonaStaleHints:含版本号字面量/陈旧措辞时给出提示;干净文本不提示。
func TestPersonaStaleHints(t *testing.T) {
dirty := "你是 HΔ-Kernel v1.0.3 型号的家政型 AI 管家助手。"
if got := agent.PersonaStaleHints(dirty); len(got) == 0 {
t.Fatal("★ 陈旧人格卡没给出提示")
}
clean := "你是小宅,说话自然,别用客服腔。"
if got := agent.PersonaStaleHints(clean); len(got) != 0 {
t.Fatalf("★ 干净人格卡被误报:%v", got)
}
}
// TestSavePersonalityCreatesParentDirs:父目录不存在时自动创建。
func TestSavePersonalityCreatesParentDirs(t *testing.T) {
path := filepath.Join(t.TempDir(), "a", "b", "personal.md")
if err := agent.SavePersonality(path, "x"); err != nil {
t.Fatalf("SavePersonality: %v", err)
}
if _, err := os.Stat(path); err != nil {
t.Fatalf("★ 文件没落盘:%v", err)
}
}