返回 DeepSeek-Reasonix
mcp_activation_test.go
根目录 / internal / config / mcp_activation_test.go
1 package config
2
3 import (
4 "fmt"
5 "os"
6 "path/filepath"
7 "runtime"
8 "sync"
9 "testing"
10 )
11
12 func TestMCPActivationStoreDefaultsAndOverrides(t *testing.T) {
13 home := t.TempDir()
14 store := NewMCPActivationStore(home)
15
16 entry := PluginEntry{Name: "chrome-devtools", Source: MCPSourceUserConfig}
17 enabled, err := store.IsEnabled(entry, "")
18 if err != nil || !enabled {
19 t.Fatalf("default user install should be enabled: enabled=%v err=%v", enabled, err)
20 }
21
22 disabled := false
23 entry.AutoStart = &disabled
24 enabled, err = store.IsEnabled(entry, "")
25 if err != nil || enabled {
26 t.Fatalf("auto_start=false without override should disable: enabled=%v err=%v", enabled, err)
27 }
28
29 if err := store.SetEnabled(MCPActivationOverride{
30 Scope: MCPActivationGlobal,
31 Source: string(MCPSourceUserConfig),
32 Server: "chrome-devtools",
33 Enabled: true,
34 }); err != nil {
35 t.Fatalf("SetEnabled: %v", err)
36 }
37 enabled, err = store.IsEnabled(entry, "")
38 if err != nil || !enabled {
39 t.Fatalf("override should re-enable despite auto_start=false: enabled=%v err=%v", enabled, err)
40 }
41
42 path := MCPActivationPath(home)
43 info, err := os.Stat(path)
44 if err != nil {
45 t.Fatalf("stat activation file: %v", err)
46 }
47 if perm := info.Mode().Perm(); runtime.GOOS != "windows" && perm != 0o600 {
48 t.Fatalf("activation file mode = %o, want 0600", perm)
49 }
50
51 // Atomic rewrite should keep a valid JSON document.
52 raw, err := os.ReadFile(path)
53 if err != nil {
54 t.Fatalf("read activation: %v", err)
55 }
56 if len(raw) == 0 || raw[0] != '{' {
57 t.Fatalf("activation JSON = %q", raw)
58 }
59 if filepath.Base(path) != "mcp-activation.json" {
60 t.Fatalf("unexpected activation path base %q", path)
61 }
62 }
63
64 func TestProjectMCPIsTrustedButActivationRemainsWorkspaceScoped(t *testing.T) {
65 for _, source := range []MCPConfigSource{MCPSourceProjectConfig, MCPSourceProjectMCPJSON} {
66 entry := PluginEntry{Name: "project-mcp", Source: source}
67 if !source.UserAuthorized() || !source.ProjectScoped() {
68 t.Fatalf("project source %q policy = authorized:%v scoped:%v, want trusted and workspace scoped",
69 source, source.UserAuthorized(), source.ProjectScoped())
70 }
71 scopeA, workspaceA, gotSource, _ := ActivationIdentity(entry, "/workspace/a")
72 scopeB, workspaceB, _, _ := ActivationIdentity(entry, "/workspace/b")
73 if scopeA != MCPActivationWorkspace || scopeB != MCPActivationWorkspace || workspaceA == "" || workspaceA == workspaceB {
74 t.Fatalf("project activation identities = (%q,%q,%q) and (%q,%q), want distinct workspace scopes",
75 scopeA, workspaceA, gotSource, scopeB, workspaceB)
76 }
77 }
78 }
79
80 func TestMCPActivationStoreConcurrentIndependentWriters(t *testing.T) {
81 home := t.TempDir()
82 const writers = 24
83 stores := make([]*MCPActivationStore, writers)
84 for i := range stores {
85 stores[i] = NewMCPActivationStore(home)
86 }
87
88 start := make(chan struct{})
89 var wg sync.WaitGroup
90 for i, store := range stores {
91 wg.Add(1)
92 go func() {
93 defer wg.Done()
94 <-start
95 if err := store.SetEnabled(MCPActivationOverride{
96 Scope: MCPActivationGlobal,
97 Server: fmt.Sprintf("server-%02d", i),
98 Enabled: true,
99 }); err != nil {
100 t.Errorf("SetEnabled(%d): %v", i, err)
101 }
102 }()
103 }
104 close(start)
105 wg.Wait()
106
107 file, err := NewMCPActivationStore(home).Load()
108 if err != nil {
109 t.Fatalf("Load: %v", err)
110 }
111 if len(file.Overrides) != writers {
112 t.Fatalf("activation overrides = %d, want %d (lost update)", len(file.Overrides), writers)
113 }
114 }
115
116 func TestEnabledPluginsHonorsActivation(t *testing.T) {
117 home := t.TempDir()
118 t.Setenv("REASONIX_HOME", home)
119 store := NewMCPActivationStore(home)
120 if err := store.SetEnabled(MCPActivationOverride{
121 Scope: MCPActivationGlobal,
122 Source: string(MCPSourceUserConfig),
123 Server: "a",
124 Enabled: false,
125 }); err != nil {
126 t.Fatalf("SetEnabled: %v", err)
127 }
128 cfg := &Config{Plugins: []PluginEntry{
129 {Name: "a", Source: MCPSourceUserConfig},
130 {Name: "b", Source: MCPSourceUserConfig},
131 }}
132 got := cfg.EnabledPlugins("", store)
133 if len(got) != 1 || got[0].Name != "b" {
134 t.Fatalf("EnabledPlugins = %+v, want [b]", got)
135 }
136 }
137
137 lines GO