返回 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.Go(func() {
92 <-start
93 if err := store.SetEnabled(MCPActivationOverride{
94 Scope: MCPActivationGlobal,
95 Server: fmt.Sprintf("server-%02d", i),
96 Enabled: true,
97 }); err != nil {
98 t.Errorf("SetEnabled(%d): %v", i, err)
99 }
100 })
101 }
102 close(start)
103 wg.Wait()
104
105 file, err := NewMCPActivationStore(home).Load()
106 if err != nil {
107 t.Fatalf("Load: %v", err)
108 }
109 if len(file.Overrides) != writers {
110 t.Fatalf("activation overrides = %d, want %d (lost update)", len(file.Overrides), writers)
111 }
112 }
113
114 func TestEnabledPluginsHonorsActivation(t *testing.T) {
115 home := t.TempDir()
116 t.Setenv("REASONIX_HOME", home)
117 store := NewMCPActivationStore(home)
118 if err := store.SetEnabled(MCPActivationOverride{
119 Scope: MCPActivationGlobal,
120 Source: string(MCPSourceUserConfig),
121 Server: "a",
122 Enabled: false,
123 }); err != nil {
124 t.Fatalf("SetEnabled: %v", err)
125 }
126 cfg := &Config{Plugins: []PluginEntry{
127 {Name: "a", Source: MCPSourceUserConfig},
128 {Name: "b", Source: MCPSourceUserConfig},
129 }}
130 got := cfg.EnabledPlugins("", store)
131 if len(got) != 1 || got[0].Name != "b" {
132 t.Fatalf("EnabledPlugins = %+v, want [b]", got)
133 }
134 }
135
135 lines GO