Files
Agent-System-Backend/internal/server/compiled_list_test.go
T
weichen adcd8fefd2 Fence U3 account writes and stabilize directory cursors
Why: Account disable could be followed by late Backend business writes, API key creation could implicitly create users, account bootstrap locks could invert, and directory cursors were process-local or leaked out-of-scope changes.

Evidence: Local go test ./... and isolated PostgreSQL package suites passed on the staged candidate; exact committed-head SELF replay and stage 6 human review remain pending.

Impact: Adds final account transactions and external-effect checks across HTTP, Host, Bus and run manager; tightens API key issuance and persists versioned cursor signing. Delegated RDM rename still needs the separately proposed real-operator contract, so A1 and stage 6 are not complete.

Refs: Documents U3 r1, approved A1-A4 amendment fb3a821, pending delegated-projection amendment 5783940, Dev feature/iam-u3-account-directory-recovery.
2026-10-14 07:33:54 +00:00

125 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 server
import (
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"agent-system-backend/internal/bus"
"agent-system-backend/internal/plugins"
"agent-system-backend/internal/profiles"
"agent-system-backend/internal/runmgr"
"agent-system-backend/internal/runtime"
"agent-system-backend/internal/settings"
"agent-system-backend/internal/store"
"agent-system-backend/internal/tools"
"agent-system-backend/pkg/extension"
)
type compiledListView struct {
Manifest struct {
ID string `json:"id"`
Name string `json:"name"`
Version string `json:"version"`
APIVersion string `json:"apiVersion"`
Provides struct {
Commands []string `json:"commands"`
Intents []string `json:"intents"`
} `json:"provides"`
Backend struct {
Mode string `json:"mode"`
} `json:"backend"`
} `json:"manifest"`
}
// WP3-I4:/api/plugins 合并编译插件条目——backend.mode=inproc、apiVersion 取
// 扩展面版本、provides 来自注册面摘要;目录插件同名时目录条目权威不重复。
func TestListPluginsMergesCompiled(t *testing.T) {
dir := t.TempDir()
st, err := store.OpenSQLite(filepath.Join(dir, "test.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { st.Close() })
b := bus.New()
reg := tools.NewRegistry()
reg.RegisterBuiltin()
setSvc := settings.NewService(st)
profSvc := profiles.New(st)
profSvc.EnsureDefault()
mgr := runmgr.New(st, b, reg, profSvc, setSvc, &runtime.LLMRunner{}, &runtime.SubLLMRunner{}, &runtime.MockRunner{}, &runtime.MockSubagent{})
pluginsRoot := filepath.Join(dir, "plugins")
// 目录插件与编译条目同名:目录条目权威,编译合成条目不重复列出。
if err := os.MkdirAll(filepath.Join(pluginsRoot, "generative-ui"), 0o755); err != nil {
t.Fatal(err)
}
manifest := `{"id":"generative-ui","name":"目录版","version":"1.2.3","apiVersion":"1.0","provides":{}}`
if err := os.WriteFile(filepath.Join(pluginsRoot, "generative-ui", "plugin.json"), []byte(manifest), 0o644); err != nil {
t.Fatal(err)
}
ph := plugins.New(st, reg, setSvc, pluginsRoot)
for id, err := range ph.LoadDir() {
if err != nil {
t.Fatalf("LoadDir %s: %v", id, err)
}
}
srv := New(st, mgr, reg, profSvc, setSvc, ph)
srv.Auth = NewAuthenticator(st, true)
srv.CompiledPlugins = []CompiledPlugin{
{ID: "rdm", Commands: []string{"rdm.ingest", "rdm.inspect.capabilities"}, Intents: []string{"rdm.write-proposal"}},
{ID: "generative-ui", Commands: []string{"generative-ui.present"}},
}
h := srv.Handler()
key, err := createLegacyAPIKeyFixture(t, st, "alice", "e2e")
if err != nil {
t.Fatal(err)
}
req := httptest.NewRequest(http.MethodGet, "/api/plugins", nil)
req.Header.Set("Authorization", "Bearer "+key)
rec := httptest.NewRecorder()
h.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status %d: %s", rec.Code, rec.Body.String())
}
var list []compiledListView
if err := json.Unmarshal(rec.Body.Bytes(), &list); err != nil {
t.Fatal(err)
}
var rdm, genui *compiledListView
genuiCount := 0
for i := range list {
switch list[i].Manifest.ID {
case "rdm":
rdm = &list[i]
case "generative-ui":
genui = &list[i]
genuiCount++
}
}
if rdm == nil {
t.Fatalf("compiled rdm entry missing: %s", rec.Body.String())
}
if rdm.Manifest.Name != "rdm" || rdm.Manifest.Backend.Mode != "inproc" {
t.Fatalf("rdm entry = %+v", rdm.Manifest)
}
if rdm.Manifest.APIVersion != extension.SurfaceVersion {
t.Fatalf("rdm apiVersion = %q, want %q", rdm.Manifest.APIVersion, extension.SurfaceVersion)
}
if len(rdm.Manifest.Provides.Commands) != 2 || len(rdm.Manifest.Provides.Intents) != 1 {
t.Fatalf("rdm provides = %+v", rdm.Manifest.Provides)
}
if genuiCount != 1 || genui == nil {
t.Fatalf("generative-ui listed %d times", genuiCount)
}
// 目录条目权威:同名编译条目不重复、manifest 不被合成版覆盖。
if genui.Manifest.Name != "目录版" || genui.Manifest.Version != "1.2.3" || genui.Manifest.Backend.Mode != "" {
t.Fatalf("genui entry = %+v", genui.Manifest)
}
}