131 lines
4.1 KiB
Go
131 lines
4.1 KiB
Go
package mcpclient
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestFilesystemConfig(t *testing.T) {
|
|
dir := t.TempDir()
|
|
c := ServerConfig{Name: "Files", Transport: "filesystem", Enabled: true, Args: []string{dir}, TimeoutSeconds: 20, Command: "should-not-run", URL: "http://unused"}
|
|
normalized, err := normalize(c)
|
|
if err != nil || normalized.Command != "" || normalized.URL != "" {
|
|
t.Fatalf("normalize: %+v %v", normalized, err)
|
|
}
|
|
for _, dirs := range [][]string{nil, {"relative"}} {
|
|
c.Args = dirs
|
|
if _, err := normalize(c); err == nil {
|
|
t.Fatalf("accepted invalid roots %v", dirs)
|
|
}
|
|
}
|
|
c.Args = []string{filepath.Join(dir, "missing")}
|
|
if err := validateFilesystemDirs(c); err == nil {
|
|
t.Fatal("accepted missing directory")
|
|
}
|
|
if err := validateSecrets(normalized, Secrets{Env: map[string]string{"NODE_OPTIONS": "--inspect"}}); err == nil {
|
|
t.Fatal("accepted inherited config secrets")
|
|
}
|
|
s, _ := testService(t)
|
|
view, err := s.SaveServer(ServerInput{Config: normalized})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
restored := newService(s.path, memorySecrets{})
|
|
views, err := restored.ListServers()
|
|
if err != nil || len(views) != 1 || views[0].Config.Args[0] != dir || views[0].Config.ID != view.Config.ID {
|
|
t.Fatalf("restore: %+v %v", views, err)
|
|
}
|
|
}
|
|
|
|
func TestBundledFilesystem(t *testing.T) {
|
|
if os.Getenv("ITUOKE_TEST_FILESYSTEM") != "1" {
|
|
t.Skip("set ITUOKE_TEST_FILESYSTEM=1 after staging official runtime")
|
|
}
|
|
t.Chdir(filepath.Join("..", ".."))
|
|
t.Setenv("PATH", t.TempDir()) // No client-installed node/npm may be required.
|
|
t.Setenv("NODE_OPTIONS", "--invalid-option-from-parent")
|
|
s, _ := testService(t)
|
|
view, err := s.AddFilesystem()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
second, err := s.AddFilesystem()
|
|
if err != nil || second.Config.ID != view.Config.ID {
|
|
t.Fatal("duplicate preset", err)
|
|
}
|
|
// Relocate the root to exercise spaces and Unicode paths.
|
|
dir := filepath.Join(filepath.Dir(s.path), "产品资料 with spaces")
|
|
if err := os.MkdirAll(dir, 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
view.Config.Args = []string{dir}
|
|
if _, err := s.SaveServer(ServerInput{Config: view.Config}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
result := s.TestServer(context.Background(), ServerInput{Config: view.Config})
|
|
if !result.OK {
|
|
t.Fatalf("discovery failed: %s", result.Message)
|
|
}
|
|
names := map[string]bool{}
|
|
for _, tool := range result.Tools {
|
|
names[tool.Name] = true
|
|
}
|
|
for _, tool := range []string{"read_text_file", "write_file", "search_files", "move_file", "list_allowed_directories"} {
|
|
if !names[tool] {
|
|
t.Fatalf("missing tool %s", tool)
|
|
}
|
|
}
|
|
result = s.TestFilesystem(context.Background(), view.Config.ID)
|
|
if !result.OK {
|
|
t.Fatal(result.Message)
|
|
}
|
|
entries, err := os.ReadDir(dir)
|
|
if err != nil || len(entries) != 0 {
|
|
t.Fatalf("probe files remain: %v %v", entries, err)
|
|
}
|
|
raw, _ := os.ReadFile(s.path)
|
|
if strings.Contains(string(raw), "node.exe") || strings.Contains(string(raw), "node_modules") {
|
|
t.Fatal("machine-specific launch paths persisted")
|
|
}
|
|
t.Logf("official Filesystem MCP: %d tools; %s", len(result.Tools), result.Message)
|
|
}
|
|
|
|
func TestSystemFilesystemPreservesConfigurationAndCannotBeDeleted(t *testing.T) {
|
|
s, _ := testService(t)
|
|
if err := s.ensureFilesystem(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.ensureFilesystem(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
views, err := s.ListServers()
|
|
if err != nil || len(views) != 1 || views[0].Config.ID != filesystemServerID {
|
|
t.Fatalf("preset: %+v %v", views, err)
|
|
}
|
|
c := views[0].Config
|
|
c.Args = []string{t.TempDir()}
|
|
c.Enabled = false
|
|
if _, err := s.SaveServer(ServerInput{Config: c}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.DeleteServer(c.ID); err == nil {
|
|
t.Fatal("deleted system Filesystem")
|
|
}
|
|
restored := newService(s.path, memorySecrets{})
|
|
if err := restored.ensureFilesystem(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
views, err = restored.ListServers()
|
|
if err != nil || len(views) != 1 || views[0].Config.Enabled || views[0].Config.Args[0] != c.Args[0] {
|
|
t.Fatalf("configuration changed: %+v %v", views, err)
|
|
}
|
|
c.Transport = "stdio"
|
|
c.Command = "unexpected"
|
|
if _, err := s.SaveServer(ServerInput{Config: c}); err == nil {
|
|
t.Fatal("changed builtin transport")
|
|
}
|
|
}
|