Files
HomeAgent/internal/plugin/proc/e2e_secret_test.go
JianFeeeee 3f834794c5 refactor: SDK module 路径统一改为 hologramagentsdk(59 文件 + go.mod)
## 为什么必须做

SDK 仓(独立仓库)已把 module 从 `github.com/JianFeeeee/homeagentsdk`
改名为 `hologramagentsdk`。主仓通过 `replace` 直接引用它的**物理目录**:

    replace github.com/JianFeeeee/homeagentsdk => ./third_party/homeagent-sdk

一旦 SDK 侧改成新 module 名,主仓立刻炸:

    go: ./third_party/homeagent-sdk@ used for two different module paths
        (github.com/JianFeeeee/hologramagentsdk and github.com/JianFeeeee/homeagentsdk)

Go **不允许**同一目录被两个 module 路径 replace。所以不能"新旧并存",
只能把主仓 59 个文件的 import 全部改到新名,并删掉旧 replace。

## 一个被证伪的前提

我最初按"third_party/homeagent-sdk 是独立仓库,不能在此仓修改其源码"
来处理,并因此判断两个仓的改动可以隔离。实际上主仓是靠 `replace` 指向
该目录的物理副本 —— **两个仓的改动不可能真正隔离**:SDK 侧改 module 名,
主仓必然跟着改 import。

这个前提应该在做任何改动之前用 `go list -m` 验证,而不是假设。

## 连带修掉两处

| 缺陷 | 性质 |
|---|---|
| `TestLuaSDKMockSingleSource` 失败 | SDK 的 `sdk/lua/sdk.lua` 变了,主仓内嵌副本未同步(跑 `scripts/sync-lua-sdk.sh` 修复) |
| `sdkModuleNames` 分散两处 | 已在 SDK 仓合并为单一数据源(见 SDK 仓 725732b) |

第一条是**副本一致性判据抓到的**,不是我自己发现的 —— 这类判据的价值在此:
两个仓必然有副本,靠人记住"改了 A 要同步 B"是做不到的。

## 验证

- `go build ./...` / `go vet ./...` 干净
- `go test ./... -count=1`:**58 包 0 失败**
- `internal/plugin`(Lua SDK 副本一致性)+ `internal/sdk` 全绿
- 主仓 `third_party/homeagent-sdk/` 与 SDK 仓 `main` 无差异
2026-10-23 15:51:02 +08:00

139 lines
4.3 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 proc
// 端到端:插件的**加密存储**能力(settings.setSecret/getSecret)。
//
// ## 为何必须做真机端到端
//
// 这条链路上有 5 段,任何一段漏掉都表现为「插件拿不到值」:
//
// ① 插件调 sdk.Settings().SetSecret
// ② 模板 procSettings.SetSecret → RPC "settings.setSecret"
// ③ proc 协议常量 MethodSettingsSetSecret
// ④ coreHandler 分发 → settingsSetSecret(可选接口断言)
// ⑤ ConfigRegistry.PluginSettings.SetSecret → 真加密落盘
//
// 单测只能证明 ⑤ 对。②③④ 是新写的,必须让**真实模板编译出的插件**
// 真的跑一遍 —— 否则就是「模板 e2e 报了 missing method,而我只在
// 内核侧修好了接口」这类错位(本次实际踩到过一次)。
import (
"os"
"path/filepath"
"strings"
"testing"
internalConfig "github.com/JianFeeeee/HologramAgent/internal/config"
internalSDK "github.com/JianFeeeee/HologramAgent/internal/sdk"
pubsdk "github.com/JianFeeeee/hologramagentsdk/sdk"
)
// secretE2EPlugin 是一个**用真实模板**编译的插件:它只做两件事 ——
// 把密钥写进加密存储,然后读回来并通过返回值报告。
const secretE2EPlugin = `package main
import (
sdk "github.com/JianFeeeee/hologramagentsdk/sdk"
)
type secretPlugin struct{ name string }
func NewPluginFactory(name string, config map[string]interface{}) (sdk.Plugin, error) {
return &secretPlugin{name: name}, nil
}
func (p *secretPlugin) Name() string { return p.name }
func (p *secretPlugin) Stop() error { return nil }
func (p *secretPlugin) Start(s *sdk.PluginSDK) error {
s.RegisterTool("e2e_secret", sdk.ToolDef{
Description: "加密存储往返测试",
Parameters: map[string]interface{}{"type": "object"},
}, func(args map[string]interface{}) (interface{}, error) {
sett := s.Settings()
if sett == nil {
return "NO_SETTINGS", nil
}
if err := sett.SetSecret("blob", "e2e-secret-value-9876543210"); err != nil {
return "SET_ERR: " + err.Error(), nil
}
got, err := sett.GetSecret("blob")
if err != nil {
return "GET_ERR: " + err.Error(), nil
}
return got, nil
})
return nil
}
`
// ★★ 判据:真实模板编译出的插件,其 SetSecret/GetSecret 全链路可用,
// 且**磁盘上是密文**、插件读回是明文。
func TestE2E_RealTemplateSecretRoundTrip(t *testing.T) {
bin := buildPluginWithRealTemplate(t, secretE2EPlugin)
// 用**真实** ConfigRegistry(真加密)+ 真主密钥
dir := t.TempDir()
setTestCodec(t)
reg := internalConfig.NewConfigRegistry(filepath.Join(dir, "config.db"))
defer reg.Close()
host, err := NewHost()
if err != nil {
t.Fatalf("NewHost: %v", err)
}
defer host.Close()
core := newFakeCore()
// 用**生产同款**适配器(sdk.NewSettings),而不是直接塞 PluginSettings ——
// 后者缺 DataDir 等成员,不是完整 SettingsAPI;用生产适配器才能
// 顺带验证「内核装配的那一层」也是通的。
core.settingsAPI = internalSDK.NewSettings("e2e", reg)
p := New("e2e", bin, dir, nil, host, nil)
if err := p.Start(core); err != nil {
t.Fatalf("Start: %v", err)
}
defer p.Close()
core.mu.Lock()
handler, ok := core.tools["e2e_secret"]
core.mu.Unlock()
if !ok {
t.Fatal("插件未注册 e2e_secret 工具")
}
res, err := handler(map[string]interface{}{})
if err != nil {
t.Fatalf("工具调用失败: %v", err)
}
got, _ := res.(string)
if strings.HasPrefix(got, "SET_ERR") || strings.HasPrefix(got, "GET_ERR") {
t.Fatalf("加密存储往返失败: %s", got)
}
if got != "e2e-secret-value-9876543210" {
t.Fatalf("插件应读回明文,实际 %q", got)
}
// ★ 磁盘上必须是密文(这才是本特性的目的)
var raw string
if err := reg.DB().QueryRow(
`SELECT value FROM config_e2e WHERE key = 'blob'`).Scan(&raw); err != nil {
t.Fatalf("读原始值失败: %v", err)
}
if strings.Contains(raw, "e2e-secret-value") {
t.Fatalf("磁盘上出现明文(加密未生效): %q", raw)
}
if !strings.HasPrefix(raw, "enc:v1:") {
t.Fatalf("磁盘值应为 enc:v1: 密文,实际 %q", raw)
}
t.Logf("✅ 插件读回明文(%d 字节),磁盘存密文(%d 字节)", len(got), len(raw))
_ = os.Remove
_ = pubsdk.ConfigDef{}
}
// setTestCodec 装配一把测试主密钥。
func setTestCodec(t *testing.T) {
t.Helper()
internalConfig.ResetTestCodec(t)
}