import { beforeEach, describe, expect, it, vi } from "vitest"; import SettingsService, { type SettingsApiResponse, } from "#/api/settings-service/settings-service.api"; import { substituteRedactedMcpCredentials } from "./mcp-redacted-credentials"; import { REDACTED_MCP_SECRET_VALUE } from "#/utils/mcp-config"; describe("substituteRedactedMcpCredentials", () => { beforeEach(() => { vi.clearAllMocks(); }); it("preserves encrypted stdio env when the server is renamed", async () => { // Regression: renaming a stdio server left a redacted env value unchanged, // so the lookup by the new display name missed the stored entry and the // literal redaction placeholder overwrote the stored encrypted secret. vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({ agent_settings: { mcp_config: { "old-name": { command: "npx", env: { API_KEY: "gAAAAA-encrypted-api-key" }, }, }, }, } as unknown as SettingsApiResponse); const result = await substituteRedactedMcpCredentials({ id: "old-name", type: "stdio", name: "new-name", command: "npx", env: { API_KEY: REDACTED_MCP_SECRET_VALUE }, }); expect(result.env).toEqual({ API_KEY: "gAAAAA-encrypted-api-key" }); expect(result.name).toBe("new-name"); }); it("does not restore another server's secret when renamed onto an existing name", async () => { // Renaming "alpha" onto "beta"'s name must still resolve alpha's stored // entry by stable key, not beta's entry. vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({ agent_settings: { mcp_config: { alpha: { command: "npx", env: { TOKEN: "gAAAAA-alpha-token" } }, beta: { command: "npx", env: { TOKEN: "gAAAAA-beta-token" } }, }, }, } as unknown as SettingsApiResponse); const result = await substituteRedactedMcpCredentials({ id: "alpha", type: "stdio", name: "beta", command: "npx", env: { TOKEN: REDACTED_MCP_SECRET_VALUE }, }); expect(result.env).toEqual({ TOKEN: "gAAAAA-alpha-token" }); }); it("leaves typed (non-redacted) env values untouched", async () => { vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({ agent_settings: { mcp_config: { "my-server": { command: "npx", env: { API_KEY: "gAAAAA-encrypted", REGION: "us-east-1" }, }, }, }, } as unknown as SettingsApiResponse); const result = await substituteRedactedMcpCredentials({ id: "my-server", type: "stdio", name: "my-server", command: "npx", env: { API_KEY: REDACTED_MCP_SECRET_VALUE, REGION: "eu-west-1", }, }); expect(result.env).toEqual({ API_KEY: "gAAAAA-encrypted", REGION: "eu-west-1", }); }); it("returns the server unchanged when no env value is redacted", async () => { const server = { id: "stdio-0", type: "stdio" as const, name: "my-server", command: "npx", env: { API_KEY: "plaintext" }, }; const result = await substituteRedactedMcpCredentials(server); expect(result).toBe(server); expect(SettingsService.fetchSettingsFromApi).not.toHaveBeenCalled(); }); it("keeps the placeholder when the stored stdio entry is missing", async () => { vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({ agent_settings: { mcp_config: {} }, } as unknown as SettingsApiResponse); const result = await substituteRedactedMcpCredentials({ id: "stdio-0", type: "stdio", name: "new-name", command: "npx", env: { API_KEY: REDACTED_MCP_SECRET_VALUE }, }); expect(result.env).toEqual({ API_KEY: REDACTED_MCP_SECRET_VALUE }); }); it("replaces redacted OAuth state with the encrypted stored subtree", async () => { vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({ agent_settings: { mcp_config: { "superhuman-mail": { url: "https://mcp.mail.superhuman.com/mcp", auth: { strategy: "oauth2", state: { tokens: { access_token: "gAAAAA-encrypted-access-token", refresh_token: "gAAAAA-encrypted-refresh-token", }, client_info: { client_id: "superhuman-client", client_secret: "gAAAAA-encrypted-client-secret", }, }, }, }, }, }, } as unknown as SettingsApiResponse); const result = await substituteRedactedMcpCredentials({ id: "superhuman-mail", type: "shttp", name: "superhuman-mail", url: "https://mcp.mail.superhuman.com/mcp", auth: { strategy: "oauth2", state: { tokens: { access_token: REDACTED_MCP_SECRET_VALUE, refresh_token: REDACTED_MCP_SECRET_VALUE, }, client_info: { client_id: "superhuman-client", client_secret: REDACTED_MCP_SECRET_VALUE, }, }, }, }); expect(result.auth).toEqual({ strategy: "oauth2", state: { tokens: { access_token: "gAAAAA-encrypted-access-token", refresh_token: "gAAAAA-encrypted-refresh-token", }, client_info: { client_id: "superhuman-client", client_secret: "gAAAAA-encrypted-client-secret", }, }, }); }); it("preserves fresh OAuth edits while substituting only redacted leaves", async () => { vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({ agent_settings: { mcp_config: { "superhuman-mail": { url: "https://mcp.mail.superhuman.com/mcp", auth: { strategy: "oauth2", state: { tokens: { access_token: "gAAAAA-encrypted-access-token", refresh_token: "gAAAAA-encrypted-refresh-token", }, client_info: { client_id: "old-client-id", client_secret: "gAAAAA-encrypted-client-secret", }, }, }, }, }, }, } as unknown as SettingsApiResponse); const result = await substituteRedactedMcpCredentials({ id: "superhuman-mail", type: "shttp", name: "superhuman-mail", url: "https://mcp.mail.superhuman.com/mcp", auth: { strategy: "oauth2", state: { tokens: { access_token: REDACTED_MCP_SECRET_VALUE, refresh_token: REDACTED_MCP_SECRET_VALUE, }, client_info: { client_id: "new-client-id", client_secret: REDACTED_MCP_SECRET_VALUE, }, }, }, }); expect(result.auth).toEqual({ strategy: "oauth2", state: { tokens: { access_token: "gAAAAA-encrypted-access-token", refresh_token: "gAAAAA-encrypted-refresh-token", }, client_info: { client_id: "new-client-id", client_secret: "gAAAAA-encrypted-client-secret", }, }, }); }); it("preserves fresh header-auth edits while substituting only redacted leaves", async () => { vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({ agent_settings: { mcp_config: { mail: { url: "https://mail.example/mcp", auth: { strategy: "header", headers: { "X-API-Key": "gAAAAA-encrypted-api-key", "X-Region": "us-east-1", }, }, }, }, }, } as unknown as SettingsApiResponse); const result = await substituteRedactedMcpCredentials({ id: "mail", type: "shttp", name: "mail", url: "https://mail.example/mcp", auth: { strategy: "header", headers: { "X-API-Key": REDACTED_MCP_SECRET_VALUE, "X-Region": "eu-west-1", }, }, }); expect(result.auth).toEqual({ strategy: "header", headers: { "X-API-Key": "gAAAAA-encrypted-api-key", "X-Region": "eu-west-1", }, }); }); it("replaces redacted raw remote headers with encrypted stored values", async () => { vi.spyOn(SettingsService, "fetchSettingsFromApi").mockResolvedValue({ agent_settings: { mcp_config: { github: { url: "https://github.example/mcp", headers: { Authorization: "Bearer gAAAAA-encrypted-github-token", }, }, }, }, } as unknown as SettingsApiResponse); const result = await substituteRedactedMcpCredentials({ id: "github", type: "shttp", name: "github", url: "https://github.example/mcp", headers: { Authorization: REDACTED_MCP_SECRET_VALUE }, }); expect(result.headers).toEqual({ Authorization: "Bearer gAAAAA-encrypted-github-token", }); }); });