Files
HomeAgent/internal/lua/test/vm_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

78 lines
2.4 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/lua 的黑盒判据:NewVM → LoadAdapterSource → 可观察输出。
// 不读适配器源码、不碰 v.pools。
import (
"strings"
"testing"
"github.com/JianFeeeee/HologramAgent/internal/lua"
)
func newVM(t *testing.T) *lua.VM {
t.Helper()
return lua.NewVM(t.TempDir())
}
// TestNewVMExposesAdapterDir:构造参数可从 AdapterDir 读回。
func TestNewVMExposesAdapterDir(t *testing.T) {
dir := t.TempDir()
v := lua.NewVM(dir)
if got := v.AdapterDir(); got != dir {
t.Fatalf("★ AdapterDir = %q,期望 %q", got, dir)
}
}
// TestListAdaptersEmptyThenLoaded:加载适配器后 ListAdapters 反映它。
func TestListAdaptersEmptyThenLoaded(t *testing.T) {
v := newVM(t)
if got := v.ListAdapters(); len(got) != 0 {
t.Fatalf("★ 新 VM 的 ListAdapters = %v,期望空", got)
}
// 最小可用适配器脚本(静态检查要求声明 name/version)
code := `-- name: contract-adapter
-- version: 1.0
return {
transform_request = function(raw) return raw end,
transform_response = function(raw) return raw end,
transform_stream_chunk = function(raw) return raw end,
endpoint = function() return "http://127.0.0.1/v1" end,
headers = function() return {} end,
}`
if err := v.LoadAdapterSource("contract-adapter", code); err != nil {
t.Fatalf("LoadAdapterSource: %v", err)
}
got := v.ListAdapters()
if len(got) != 1 {
t.Fatalf("★ ListAdapters = %v,期望 1 个", got)
}
if got[0].Name != "contract-adapter" {
t.Fatalf("★ Name = %q", got[0].Name)
}
}
// TestLoadAdapterSourceRejectsBadScript:明显不合法的脚本必须报错。
func TestLoadAdapterSourceRejectsBadScript(t *testing.T) {
v := newVM(t)
if err := v.LoadAdapterSource("bad", "this is not lua at all $$$"); err == nil {
t.Fatal("★ 非法脚本被接受")
}
}
// TestCallTransformRequestUnknownAdapter:未加载的适配器调用必须报错。
func TestCallTransformRequestUnknownAdapter(t *testing.T) {
v := newVM(t)
_, err := v.CallTransformRequest("ghost", `{}`)
if err == nil || !strings.Contains(err.Error(), "not loaded") {
t.Fatalf("★ 未加载适配器的报错不可诊断:%v", err)
}
}
// TestGetAdapterEndpointUnknownIsEmpty:未知适配器端点为空串、不 panic。
func TestGetAdapterEndpointUnknownIsEmpty(t *testing.T) {
if got := newVM(t).GetAdapterEndpoint("ghost"); got != "" {
t.Fatalf("★ 未知适配器端点 = %q,期望空", got)
}
}