package tools import ( "context" "encoding/json" "fmt" "strings" "sync" "testing" "github.com/google/uuid" "github.com/nextlevelbuilder/goclaw/internal/store" ) // mockMCPServerStore implements store.MCPServerStore with in-memory state for tests. type mockMCPServerStore struct { mu sync.Mutex servers map[string]*store.MCPServerData // keyed by name serverIDs map[uuid.UUID]string // id -> name credentials map[credKey]*store.MCPUserCredentials accessible []store.MCPAccessInfo } type credKey struct { serverID uuid.UUID userID string } func newMockMCPServerStore() *mockMCPServerStore { return &mockMCPServerStore{ servers: make(map[string]*store.MCPServerData), serverIDs: make(map[uuid.UUID]string), credentials: make(map[credKey]*store.MCPUserCredentials), } } func (m *mockMCPServerStore) addServer(srv *store.MCPServerData) { m.mu.Lock() defer m.mu.Unlock() m.servers[srv.Name] = srv m.serverIDs[srv.ID] = srv.Name } func (m *mockMCPServerStore) setCredentials(serverID uuid.UUID, userID string, creds *store.MCPUserCredentials) { m.mu.Lock() defer m.mu.Unlock() if creds == nil { delete(m.credentials, credKey{serverID: serverID, userID: userID}) } else { m.credentials[credKey{serverID: serverID, userID: userID}] = creds } } // MCPServerStore interface methods used by the tool: func (m *mockMCPServerStore) ListAccessible(_ context.Context, _ uuid.UUID, _ string) ([]store.MCPAccessInfo, error) { m.mu.Lock() defer m.mu.Unlock() return m.accessible, nil } func (m *mockMCPServerStore) GetServerByName(_ context.Context, name string) (*store.MCPServerData, error) { m.mu.Lock() defer m.mu.Unlock() srv, ok := m.servers[name] if !ok { return nil, fmt.Errorf("server %q not found", name) } return srv, nil } func (m *mockMCPServerStore) GetUserCredentials(_ context.Context, serverID uuid.UUID, userID string) (*store.MCPUserCredentials, error) { m.mu.Lock() defer m.mu.Unlock() creds, ok := m.credentials[credKey{serverID: serverID, userID: userID}] if !ok { return nil, nil } return creds, nil } func (m *mockMCPServerStore) SetUserCredentials(_ context.Context, serverID uuid.UUID, userID string, creds store.MCPUserCredentials) error { m.mu.Lock() defer m.mu.Unlock() m.credentials[credKey{serverID: serverID, userID: userID}] = &store.MCPUserCredentials{ APIKey: creds.APIKey, Headers: creds.Headers, Env: creds.Env, } return nil } func (m *mockMCPServerStore) DeleteUserCredentials(_ context.Context, serverID uuid.UUID, userID string) error { m.mu.Lock() defer m.mu.Unlock() delete(m.credentials, credKey{serverID: serverID, userID: userID}) return nil } // Stubs for unused MCPServerStore interface methods: func (m *mockMCPServerStore) CreateServer(_ context.Context, _ *store.MCPServerData) error { return fmt.Errorf("not implemented") } func (m *mockMCPServerStore) GetServer(_ context.Context, _ uuid.UUID) (*store.MCPServerData, error) { return nil, fmt.Errorf("not implemented") } func (m *mockMCPServerStore) ListServers(_ context.Context) ([]store.MCPServerData, error) { return nil, fmt.Errorf("not implemented") } func (m *mockMCPServerStore) UpdateServer(_ context.Context, _ uuid.UUID, _ map[string]any) error { return fmt.Errorf("not implemented") } func (m *mockMCPServerStore) DeleteServer(_ context.Context, _ uuid.UUID) error { return fmt.Errorf("not implemented") } func (m *mockMCPServerStore) GrantToAgent(_ context.Context, _ *store.MCPAgentGrant) error { return fmt.Errorf("not implemented") } func (m *mockMCPServerStore) RevokeFromAgent(_ context.Context, _, _ uuid.UUID) error { return fmt.Errorf("not implemented") } func (m *mockMCPServerStore) ListAgentGrants(_ context.Context, _ uuid.UUID) ([]store.MCPAgentGrant, error) { return nil, fmt.Errorf("not implemented") } func (m *mockMCPServerStore) ListServerGrants(_ context.Context, _ uuid.UUID) ([]store.MCPAgentGrant, error) { return nil, fmt.Errorf("not implemented") } func (m *mockMCPServerStore) GrantToUser(_ context.Context, _ *store.MCPUserGrant) error { return fmt.Errorf("not implemented") } func (m *mockMCPServerStore) RevokeFromUser(_ context.Context, _ uuid.UUID, _ string) error { return fmt.Errorf("not implemented") } func (m *mockMCPServerStore) CountAgentGrantsByServer(_ context.Context) (map[uuid.UUID]int, error) { return nil, fmt.Errorf("not implemented") } func (m *mockMCPServerStore) CreateRequest(_ context.Context, _ *store.MCPAccessRequest) error { return fmt.Errorf("not implemented") } func (m *mockMCPServerStore) ListPendingRequests(_ context.Context) ([]store.MCPAccessRequest, error) { return nil, fmt.Errorf("not implemented") } func (m *mockMCPServerStore) ReviewRequest(_ context.Context, _ uuid.UUID, _ bool, _, _ string) error { return fmt.Errorf("not implemented") } func (m *mockMCPServerStore) CacheToolDescriptions(_ context.Context, _ uuid.UUID, _ map[string]store.CachedToolInfo) error { return fmt.Errorf("not implemented") } // --- Test setup helpers --- var ( mcpCredTestAgentID = uuid.MustParse("11111111-1111-1111-1111-111111111111") mcpCredTestUserID = "test-user-42" mcpCredTestTenantID = uuid.MustParse("22222222-2222-2222-2222-222222222222") mcpCredServerGitHub = &store.MCPServerData{ BaseModel: store.BaseModel{ID: uuid.MustParse("33333333-3333-3333-3333-333333333333")}, Name: "github", DisplayName: "GitHub API", Transport: "streamable-http", URL: "https://mcp.github.io", Enabled: true, } mcpCredServerPostgres = &store.MCPServerData{ BaseModel: store.BaseModel{ID: uuid.MustParse("44444444-4444-4444-4444-444444444444")}, Name: "postgres", DisplayName: "", Transport: "stdio", Command: "npx", Args: json.RawMessage(`["@mcp/postgres"]`), Enabled: true, } mcpCredServerSlack = &store.MCPServerData{ BaseModel: store.BaseModel{ID: uuid.MustParse("55555555-5555-5555-5555-555555555555")}, Name: "slack", DisplayName: "Slack", Transport: "streamable-http", URL: "https://mcp.slack.com", Enabled: true, RequireUserCredentials: true, } mcpCredServerVault = &store.MCPServerData{ BaseModel: store.BaseModel{ID: uuid.MustParse("66666666-6666-6666-6666-666666666666")}, Name: "vault", Transport: "streamable-http", URL: "https://vault.example.com", Enabled: true, Settings: json.RawMessage(`{"require_user_credentials":true}`), } ) func mcpCredCtx() context.Context { ctx := context.Background() ctx = store.WithAgentID(ctx, mcpCredTestAgentID) ctx = store.WithUserID(ctx, mcpCredTestUserID) ctx = store.WithTenantID(ctx, mcpCredTestTenantID) return ctx } func newTestMCPCredTool() (*mockMCPServerStore, *MCPCredentialManagerTool) { mock := newMockMCPServerStore() tool := NewMCPCredentialManagerTool() tool.SetMCPServerStore(mock) return mock, tool } // --- Tests --- func TestMCPCredentialManager_NilStore(t *testing.T) { tool := NewMCPCredentialManagerTool() // store not set res := tool.Execute(mcpCredCtx(), map[string]any{"action": "list_servers"}) if !res.IsError { t.Fatal("expected error for nil store") } if !strings.Contains(res.ForLLM, "MCP server store not available") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_EmptyAction(t *testing.T) { _, tool := newTestMCPCredTool() res := tool.Execute(mcpCredCtx(), map[string]any{}) if !res.IsError { t.Fatal("expected error for empty action") } if !strings.Contains(res.ForLLM, "action is required") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_UnknownAction(t *testing.T) { _, tool := newTestMCPCredTool() res := tool.Execute(mcpCredCtx(), map[string]any{"action": "fly_to_moon"}) if !res.IsError { t.Fatal("expected error for unknown action") } if !strings.Contains(res.ForLLM, "unsupported action") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_ListServers_Empty(t *testing.T) { mock, tool := newTestMCPCredTool() mock.accessible = nil res := tool.Execute(mcpCredCtx(), map[string]any{"action": "list_servers"}) if res.IsError { t.Fatalf("unexpected error: %s", res.ForLLM) } if !strings.Contains(res.ForLLM, "don't have any MCP servers") { t.Fatalf("unexpected result: %s", res.ForLLM) } } func TestMCPCredentialManager_ListServers_AllScenarios(t *testing.T) { mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerGitHub) mock.addServer(mcpCredServerPostgres) mock.addServer(mcpCredServerSlack) mock.addServer(mcpCredServerVault) mock.accessible = []store.MCPAccessInfo{ {Server: *mcpCredServerGitHub}, {Server: *mcpCredServerPostgres}, {Server: *mcpCredServerSlack}, {Server: *mcpCredServerVault}, } // Pre-set credentials for Slack (requires user creds, has creds) mock.setCredentials(mcpCredServerSlack.ID, mcpCredTestUserID, &store.MCPUserCredentials{APIKey: "xoxb-secret-token"}) res := tool.Execute(mcpCredCtx(), map[string]any{"action": "list_servers"}) if res.IsError { t.Fatalf("unexpected error: %s", res.ForLLM) } // All 4 servers listed if !strings.Contains(res.ForLLM, "4") { t.Fatalf("expected 4 servers, got: %s", res.ForLLM) } // GitHub — no credentials needed, no custom creds if !strings.Contains(res.ForLLM, "GitHub API") || !strings.Contains(res.ForLLM, "no credentials needed") { t.Fatalf("expected GitHub with 'no credentials needed': %s", res.ForLLM) } // Postgres — no display name, should show raw name if !strings.Contains(res.ForLLM, "postgres") || !strings.Contains(res.ForLLM, "no credentials needed") { t.Fatalf("expected postgres with 'no credentials needed': %s", res.ForLLM) } // Slack — requires user credentials and has them if !strings.Contains(res.ForLLM, "credentials set") { t.Fatalf("expected Slack with 'credentials set': %s", res.ForLLM) } // Vault — requires user credentials (from settings) but not set if !strings.Contains(res.ForLLM, "credentials required") { t.Fatalf("expected vault with 'credentials required': %s", res.ForLLM) } } func TestMCPCredentialManager_CredentialStatus_NoServerName(t *testing.T) { _, tool := newTestMCPCredTool() res := tool.Execute(mcpCredCtx(), map[string]any{"action": "credential_status"}) if !res.IsError { t.Fatal("expected error for missing server_name") } if !strings.Contains(res.ForLLM, "server_name is required") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_CredentialStatus_ServerNotFound(t *testing.T) { mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerGitHub) res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "credential_status", "server_name": "nonexistent", }) if !res.IsError { t.Fatal("expected error for nonexistent server") } if !strings.Contains(res.ForLLM, "not found") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_CredentialStatus_NoUserID(t *testing.T) { _, tool := newTestMCPCredTool() ctx := context.Background() ctx = store.WithAgentID(ctx, mcpCredTestAgentID) // No user ID set res := tool.Execute(ctx, map[string]any{ "action": "credential_status", "server_name": "github", }) if !res.IsError { t.Fatal("expected error for missing user ID") } if !strings.Contains(res.ForLLM, "no user identity") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_CredentialStatus_NoCredsSet(t *testing.T) { mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerGitHub) mock.accessible = []store.MCPAccessInfo{{Server: *mcpCredServerGitHub}} res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "credential_status", "server_name": "github", }) if res.IsError { t.Fatalf("unexpected error: %s", res.ForLLM) } if !strings.Contains(res.ForLLM, "have not set any credentials") { t.Fatalf("expected 'no credentials' message: %s", res.ForLLM) } } func TestMCPCredentialManager_CredentialStatus_NeedsCredsNoCreds(t *testing.T) { mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerSlack) mock.accessible = []store.MCPAccessInfo{{Server: *mcpCredServerSlack}} res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "credential_status", "server_name": "slack", }) if res.IsError { t.Fatalf("unexpected error: %s", res.ForLLM) } if !strings.Contains(res.ForLLM, "requires per-user credentials") { t.Fatalf("expected 'requires credentials': %s", res.ForLLM) } if !strings.Contains(res.ForLLM, "set_credentials") { t.Fatalf("expected hint about set_credentials: %s", res.ForLLM) } } func TestMCPCredentialManager_CredentialStatus_WithCreds(t *testing.T) { mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerGitHub) mock.accessible = []store.MCPAccessInfo{{Server: *mcpCredServerGitHub}} mock.setCredentials(mcpCredServerGitHub.ID, mcpCredTestUserID, &store.MCPUserCredentials{ APIKey: "ghp_abcdef1234567890abcdef1234567890", Headers: map[string]string{"X-Custom": "value"}, Env: map[string]string{"MY_VAR": "my_value"}, }) res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "credential_status", "server_name": "github", }) if res.IsError { t.Fatalf("unexpected error: %s", res.ForLLM) } if !strings.Contains(res.ForLLM, "ghp_") { t.Fatalf("expected masked API key: %s", res.ForLLM) } if !strings.Contains(res.ForLLM, "X-Custom") { t.Fatalf("expected header key listed: %s", res.ForLLM) } if !strings.Contains(res.ForLLM, "MY_VAR") { t.Fatalf("expected env var key listed: %s", res.ForLLM) } // The actual value should NOT appear (masked) if strings.Contains(res.ForLLM, "abcdef1234567890abcdef1234567890") { t.Fatalf("API key value leaked in output: %s", res.ForLLM) } } func TestMCPCredentialManager_SetCredentials_NoServerName(t *testing.T) { _, tool := newTestMCPCredTool() res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "set_credentials", "api_key": "my-key", }) if !res.IsError { t.Fatal("expected error for missing server_name") } if !strings.Contains(res.ForLLM, "server_name is required") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_SetCredentials_NoUserID(t *testing.T) { _, tool := newTestMCPCredTool() ctx := context.Background() ctx = store.WithAgentID(ctx, mcpCredTestAgentID) res := tool.Execute(ctx, map[string]any{ "action": "set_credentials", "server_name": "github", "api_key": "my-key", }) if !res.IsError { t.Fatal("expected error for missing user ID") } if !strings.Contains(res.ForLLM, "no user identity") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_SetCredentials_ServerNotFound(t *testing.T) { mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerGitHub) res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "set_credentials", "server_name": "nonexistent", "api_key": "my-key", }) if !res.IsError { t.Fatal("expected error for nonexistent server") } if !strings.Contains(res.ForLLM, "not found") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_SetCredentials_NoValues(t *testing.T) { mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerGitHub) res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "set_credentials", "server_name": "github", }) if !res.IsError { t.Fatal("expected error for no values") } if !strings.Contains(res.ForLLM, "at least one of") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_SetCredentials_Success(t *testing.T) { mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerGitHub) res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "set_credentials", "server_name": "github", "api_key": "ghp_my_secret_key", "headers": map[string]any{"X-Custom": "val1"}, "env": map[string]any{"MY_ENV": "val2"}, }) if res.IsError { t.Fatalf("unexpected error: %s", res.ForLLM) } if !strings.Contains(res.ForLLM, "Successfully set credentials") { t.Fatalf("expected success: %s", res.ForLLM) } if !strings.Contains(res.ForLLM, "GitHub API") { t.Fatalf("expected display name: %s", res.ForLLM) } // Verify the credential was stored creds, err := mock.GetUserCredentials(context.Background(), mcpCredServerGitHub.ID, mcpCredTestUserID) if err != nil { t.Fatalf("unexpected error: %v", err) } if creds == nil { t.Fatal("credentials not stored") } if creds.APIKey != "ghp_my_secret_key" { t.Fatalf("expected API key 'ghp_my_secret_key', got %q", creds.APIKey) } if creds.Headers["X-Custom"] != "val1" { t.Fatalf("expected header X-Custom=val1, got %q", creds.Headers["X-Custom"]) } if creds.Env["MY_ENV"] != "val2" { t.Fatalf("expected env MY_ENV=val2, got %q", creds.Env["MY_ENV"]) } } func TestMCPCredentialManager_SetCredentials_SuccessNoDisplayName(t *testing.T) { mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerPostgres) res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "set_credentials", "server_name": "postgres", "api_key": "pg-key", }) if res.IsError { t.Fatalf("unexpected error: %s", res.ForLLM) } // Falls back to server name when DisplayName is empty if !strings.Contains(res.ForLLM, "postgres") { t.Fatalf("expected server name fallback: %s", res.ForLLM) } } func TestMCPCredentialManager_SetBearerToken_NoServerName(t *testing.T) { _, tool := newTestMCPCredTool() res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "set_bearer_token", "token": "my-token", }) if !res.IsError { t.Fatal("expected error for missing server_name") } if !strings.Contains(res.ForLLM, "server_name is required") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_SetBearerToken_NoToken(t *testing.T) { _, tool := newTestMCPCredTool() res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "set_bearer_token", "server_name": "github", }) if !res.IsError { t.Fatal("expected error for missing token") } if !strings.Contains(res.ForLLM, "token is required") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_SetBearerToken_NoUserID(t *testing.T) { _, tool := newTestMCPCredTool() ctx := context.Background() ctx = store.WithAgentID(ctx, mcpCredTestAgentID) res := tool.Execute(ctx, map[string]any{ "action": "set_bearer_token", "server_name": "github", "token": "my-token", }) if !res.IsError { t.Fatal("expected error for missing user ID") } if !strings.Contains(res.ForLLM, "no user identity") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_SetBearerToken_ServerNotFound(t *testing.T) { mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerGitHub) res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "set_bearer_token", "server_name": "nonexistent", "token": "my-token", }) if !res.IsError { t.Fatal("expected error for nonexistent server") } if !strings.Contains(res.ForLLM, "not found") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_SetBearerToken_Success(t *testing.T) { mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerGitHub) res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "set_bearer_token", "server_name": "github", "token": "ghp_bearer_token_value", }) if res.IsError { t.Fatalf("unexpected error: %s", res.ForLLM) } if !strings.Contains(res.ForLLM, "Successfully set Bearer token") { t.Fatalf("expected success: %s", res.ForLLM) } if !strings.Contains(res.ForLLM, "Authorization: Bearer") { t.Fatalf("expected Authorization Bearer mention: %s", res.ForLLM) } // Verify the credential was stored as API key creds, err := mock.GetUserCredentials(context.Background(), mcpCredServerGitHub.ID, mcpCredTestUserID) if err != nil { t.Fatalf("unexpected error: %v", err) } if creds == nil { t.Fatal("credentials not stored") } if creds.APIKey != "ghp_bearer_token_value" { t.Fatalf("expected API key 'ghp_bearer_token_value', got %q", creds.APIKey) } } func TestMCPCredentialManager_SetBearerToken_OverwritesExisting(t *testing.T) { mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerSlack) // Pre-set existing credentials mock.setCredentials(mcpCredServerSlack.ID, mcpCredTestUserID, &store.MCPUserCredentials{ APIKey: "old-token", Headers: map[string]string{"X-Old": "value"}, }) res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "set_bearer_token", "server_name": "slack", "token": "new-bearer-token", }) if res.IsError { t.Fatalf("unexpected error: %s", res.ForLLM) } // Verify old credentials were replaced creds, err := mock.GetUserCredentials(context.Background(), mcpCredServerSlack.ID, mcpCredTestUserID) if err != nil { t.Fatalf("unexpected error: %v", err) } if creds == nil { t.Fatal("credentials not stored") } if creds.APIKey != "new-bearer-token" { t.Fatalf("expected 'new-bearer-token', got %q", creds.APIKey) } // Old headers are cleared if len(creds.Headers) > 0 { t.Fatalf("expected headers cleared, got %v", creds.Headers) } } func TestMCPCredentialManager_DeleteCredentials_NoServerName(t *testing.T) { _, tool := newTestMCPCredTool() res := tool.Execute(mcpCredCtx(), map[string]any{"action": "delete_credentials"}) if !res.IsError { t.Fatal("expected error for missing server_name") } if !strings.Contains(res.ForLLM, "server_name is required") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_DeleteCredentials_NoUserID(t *testing.T) { _, tool := newTestMCPCredTool() ctx := context.Background() ctx = store.WithAgentID(ctx, mcpCredTestAgentID) res := tool.Execute(ctx, map[string]any{ "action": "delete_credentials", "server_name": "github", }) if !res.IsError { t.Fatal("expected error for missing user ID") } if !strings.Contains(res.ForLLM, "no user identity") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_DeleteCredentials_ServerNotFound(t *testing.T) { mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerGitHub) res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "delete_credentials", "server_name": "nonexistent", }) if !res.IsError { t.Fatal("expected error for nonexistent server") } if !strings.Contains(res.ForLLM, "not found") { t.Fatalf("unexpected error: %s", res.ForLLM) } } func TestMCPCredentialManager_DeleteCredentials_Success(t *testing.T) { mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerGitHub) mock.setCredentials(mcpCredServerGitHub.ID, mcpCredTestUserID, &store.MCPUserCredentials{ APIKey: "ghp_my_key", }) // Verify it exists first creds, _ := mock.GetUserCredentials(context.Background(), mcpCredServerGitHub.ID, mcpCredTestUserID) if creds == nil { t.Fatal("precondition failed: credentials should exist") } res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "delete_credentials", "server_name": "github", }) if res.IsError { t.Fatalf("unexpected error: %s", res.ForLLM) } if !strings.Contains(res.ForLLM, "Successfully deleted credentials") { t.Fatalf("expected success: %s", res.ForLLM) } // Verify credentials are gone creds, err := mock.GetUserCredentials(context.Background(), mcpCredServerGitHub.ID, mcpCredTestUserID) if err != nil { t.Fatalf("unexpected error: %v", err) } if creds != nil { t.Fatal("credentials should have been deleted") } } func TestMCPCredentialManager_DeleteCredentials_Idempotent(t *testing.T) { mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerGitHub) // No credentials pre-set res := tool.Execute(mcpCredCtx(), map[string]any{ "action": "delete_credentials", "server_name": "github", }) if res.IsError { t.Fatalf("expected delete to be idempotent, got error: %s", res.ForLLM) } if !strings.Contains(res.ForLLM, "Successfully deleted credentials") { t.Fatalf("expected success: %s", res.ForLLM) } } func TestMCPCredentialManager_StoreAccessibleReturnsListAccessible(t *testing.T) { // Verify that listServers calls ListAccessible by checking the number of // servers returned matches the mock's accessible count. mock, tool := newTestMCPCredTool() mock.addServer(mcpCredServerGitHub) mock.addServer(mcpCredServerPostgres) mock.accessible = []store.MCPAccessInfo{ {Server: *mcpCredServerGitHub}, {Server: *mcpCredServerPostgres}, } res := tool.Execute(mcpCredCtx(), map[string]any{"action": "list_servers"}) if res.IsError { t.Fatalf("unexpected error: %s", res.ForLLM) } if !strings.Contains(res.ForLLM, "2") { t.Fatalf("expected 2 servers, got: %s", res.ForLLM) } } // Test that requireUserCredsFromSettings returns true for settings with require_user_credentials. func TestRequireUserCredsFromSettings(t *testing.T) { tests := []struct { name string settings json.RawMessage want bool }{ {"nil settings", nil, false}, {"empty settings", json.RawMessage(""), false}, {"empty object", json.RawMessage("{}"), false}, {"require_user_credentials true", json.RawMessage(`{"require_user_credentials":true}`), true}, {"require_user_credentials false", json.RawMessage(`{"require_user_credentials":false}`), false}, {"nested unrelated", json.RawMessage(`{"timeout":30}`), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := requireUserCredsFromSettings(tt.settings) if got != tt.want { t.Errorf("requireUserCredsFromSettings(%s) = %v, want %v", string(tt.settings), got, tt.want) } }) } } // Test that maskString does not leak full secrets. func TestMaskString(t *testing.T) { tests := []struct { input string want string }{ {"short", "*****"}, {"12345678", "********"}, {"abcdefghijklmnop", "abcd********mnop"}, {"abcdefghij", "abcd**ghij"}, } for _, tt := range tests { t.Run(tt.input, func(t *testing.T) { got := maskString(tt.input) if got != tt.want { t.Errorf("maskString(%q) = %q, want %q", tt.input, got, tt.want) } // Ensure no part of the input beyond first 4 and last 4 leaks if len(tt.input) > 8 { middle := tt.input[4 : len(tt.input)-4] if strings.Contains(got, middle) { t.Errorf("maskString(%q) leaked middle part %q in %q", tt.input, middle, got) } } }) } }