Files
HomeAgent/internal/lua/test/vm_contract_test.go
JianFeeeee b215bfb3ad test: io/lua/system/tracker 改为模块职责级黑盒契约
## internal/agent/io

    原:只有 OutputCapability 位标志 + String
    现:
      InjectText → 输入事件(source/output_channel/request_id)
      InjectInputTo → 显式输出通道被保留
      InjectTextSync → 响应回程交到调用方
      RegisterDevice → GetDevice → StartAll/StopAll 真实调用设备
      重复注册被拒;StartAll 传播设备启动失败
    能力标志语义保留(任一位命中)
    变异:允许重复注册 / 吞掉启动错误 → 均断言红

## internal/lua

    新增:
      transform_request 真的执行 Lua body(原来只验证加载与未加载报错)
      endpoint/headers 静态声明经公开 API 读回
    变异:transform_request 直接返回入参 → 断言红

## internal/system

    原:只断言 archived=true 与 baseline 目录非空
    现:留档 → 覆盖 → RestoreFileFromBaseline 内容还原为原文
         非受保护路径与新建文件不留档
         内容未变时重复留档不产生新档案
    变异:留档写空内容 / 去掉幂等判断 → 均断言红

## internal/tracker

    原:无文件变更时断言无 changeset,rollback 起点为空
    现:真实 upper 目录写入 → PostAction 记录 ChangeFileCreated
         修改原文 → RollbackLatest 恢复原文内容
         新建文件 → RollbackLatest 后消失
         空历史定向回滚报错
    变异:不记录变更 → 两条断言红

## 验证

    go build ./... / go vet ./...              PASS
    gofmt -l                                  干净
    CI 门禁自检                                PASS
    go test ./... -count=1 -race              51 测试包 / 100 包无测试 / 0 FAIL
    源码目录 _test.go                          0
2026-11-14 10:22:12 +08:00

128 lines
4.0 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)
}
}
// TestTransformActuallyRunsAdapter:加载后调用 transform_request,
// 必须真的执行 Lua 脚本(而非只返回输入或报未加载)。
func TestTransformActuallyRunsAdapter(t *testing.T) {
v := newVM(t)
code := `-- name: contract-adapter
-- version: 1.0
return {
transform_request = function(raw) return '{"rewritten":true}' end,
transform_response = function(raw) return raw end,
transform_stream_chunk = function(raw) return raw end,
}`
if err := v.LoadAdapterSource("contract-adapter", code); err != nil {
t.Fatalf("LoadAdapterSource: %v", err)
}
out, err := v.CallTransformRequest("contract-adapter", `{"model":"m"}`)
if err != nil {
t.Fatalf("CallTransformRequest: %v", err)
}
if !strings.Contains(out, "rewritten") {
t.Fatalf("adapter body not executed: %q", out)
}
}
// TestAdapterHeadersAndEndpointExposed:静态声明的 endpoint/headers 可读回。
func TestAdapterHeadersAndEndpointExposed(t *testing.T) {
v := newVM(t)
code := `-- name: hdr
-- version: 1.0
return {
endpoint = 'http://127.0.0.1:9000/v1',
headers = { ['X-Contract'] = 'yes' },
transform_request = function(raw) return raw end,
transform_response = function(raw) return raw end,
transform_stream_chunk = function(raw) return raw end,
}`
if err := v.LoadAdapterSource("hdr", code); err != nil {
t.Fatalf("LoadAdapterSource: %v", err)
}
if got := v.GetAdapterEndpoint("hdr"); got != "http://127.0.0.1:9000/v1" {
t.Fatalf("endpoint = %q", got)
}
h, err := v.BuildHeaders("hdr", nil)
if err != nil {
t.Fatalf("BuildHeaders: %v", err)
}
if h["X-Contract"] != "yes" {
t.Fatalf("headers = %v", h)
}
}