diff --git a/mcp/client_command_test.go b/mcp/client_command_test.go index 0d244c50..1ce5baea 100644 --- a/mcp/client_command_test.go +++ b/mcp/client_command_test.go @@ -28,7 +28,7 @@ func TestRenderClientListEscapesMarkdownCells(t *testing.T) { } func TestAddOAuthNoVerifyPersistsConfiguration(t *testing.T) { - t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + isolateConfigHome(t) output, err := executeMCPCommand( "add", "private", "https://example.com/mcp", "--oauth-client-id", "client-1", @@ -53,7 +53,7 @@ func TestAddOAuthNoVerifyPersistsConfiguration(t *testing.T) { } func TestAddNoBrowserRequiresOAuth(t *testing.T) { - t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + isolateConfigHome(t) _, err := executeMCPCommand("add", "public", "https://example.com/mcp", "--no-browser", "--no-verify") if err == nil || !strings.Contains(err.Error(), "requires --oauth") { t.Fatalf("error = %v", err) @@ -61,8 +61,7 @@ func TestAddNoBrowserRequiresOAuth(t *testing.T) { } func TestAddRejectsInvalidNameBeforeOAuthStorageBinding(t *testing.T) { - configHome := t.TempDir() - t.Setenv("XDG_CONFIG_HOME", configHome) + isolateConfigHome(t) _, err := executeMCPCommand("add", "../config", "https://example.com/mcp", "--oauth", "--no-verify") if err == nil || !strings.Contains(err.Error(), "invalid server name") { t.Fatalf("error = %v", err) @@ -74,7 +73,7 @@ func TestAddRejectsInvalidNameBeforeOAuthStorageBinding(t *testing.T) { } func TestAddRejectsLiteralOAuthSecret(t *testing.T) { - t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + isolateConfigHome(t) _, err := executeMCPCommand( "add", "private", "https://example.com/mcp", "--oauth-client-id", "client", "--oauth-client-secret", "literal", "--no-verify", ) diff --git a/mcp/client_e2e_test.go b/mcp/client_e2e_test.go index b5ec1341..8a70c050 100644 --- a/mcp/client_e2e_test.go +++ b/mcp/client_e2e_test.go @@ -13,7 +13,7 @@ import ( ) func TestClientCommandsStdioAndOfflineHelp(t *testing.T) { - t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + isolateConfigHome(t) workingDir, err := os.Getwd() if err != nil { t.Fatal(err) diff --git a/mcp/client_oauth_test.go b/mcp/client_oauth_test.go index 5f9f5fb8..cdca552f 100644 --- a/mcp/client_oauth_test.go +++ b/mcp/client_oauth_test.go @@ -138,7 +138,7 @@ func testOAuthLoginUsesOIDCDiscoveryAndRefreshes(t *testing.T, metadataOverride defer httpServer.Close() baseURL = httpServer.URL - t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + isolateConfigHome(t) registry := NewServerRegistry("testapp") oauthConfig := &OAuthClientConfig{Scopes: append([]string(nil), scopes...)} if metadataOverride { diff --git a/mcp/client_registry_test.go b/mcp/client_registry_test.go index 2b0158a4..66526954 100644 --- a/mcp/client_registry_test.go +++ b/mcp/client_registry_test.go @@ -10,6 +10,18 @@ import ( "time" ) +// isolateConfigHome points NewServerRegistry at a per-test directory on every +// platform. os.UserConfigDir only reads XDG_CONFIG_HOME on unix-like systems; +// on darwin it derives ~/Library/Application Support from HOME, so setting only +// XDG_CONFIG_HOME leaks registry, cache, and OAuth state into the real user +// config directory and makes later runs fail on stale state. +func isolateConfigHome(t *testing.T) { + t.Helper() + home := t.TempDir() + t.Setenv("HOME", home) + t.Setenv("XDG_CONFIG_HOME", filepath.Join(home, ".config")) +} + func TestServerConfigValidate(t *testing.T) { tests := []struct { name string diff --git a/mcp/client_run_test.go b/mcp/client_run_test.go index 350733dc..0fb5dad7 100644 --- a/mcp/client_run_test.go +++ b/mcp/client_run_test.go @@ -10,7 +10,7 @@ import ( ) func TestRunShortHelpListsServers(t *testing.T) { - t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + isolateConfigHome(t) registry := NewServerRegistry("testapp") if err := registry.Add("demo", ServerConfig{Type: "stdio", Command: "server"}); err != nil { t.Fatal(err)