ituoke/internal/mcpclient/filesystem_test.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")
}
}