import { describe, it, expect, spyOn, afterEach } from "bun:test" import * as mcpOauth from "./mcp-oauth" describe("mcp-oauth check", () => { describe("getMcpOAuthCheckDefinition", () => { it("returns check definition with correct properties", () => { // given // when getting definition const def = mcpOauth.getMcpOAuthCheckDefinition() // then should have correct structure expect(def.id).toBe("mcp-oauth-tokens") expect(def.name).toBe("MCP OAuth Tokens") expect(def.category).toBe("tools") expect(def.critical).toBe(false) expect(typeof def.check).toBe("function") }) }) describe("checkMcpOAuthTokens", () => { let readStoreSpy: ReturnType afterEach(() => { readStoreSpy?.mockRestore() }) it("returns skip when no tokens stored", async () => { // given no OAuth tokens configured readStoreSpy = spyOn(mcpOauth, "readTokenStore").mockReturnValue(null) // when checking OAuth tokens const result = await mcpOauth.checkMcpOAuthTokens() // then should skip expect(result.status).toBe("skip") expect(result.message).toContain("No OAuth") }) it("returns pass when all tokens valid", async () => { // given valid tokens with future expiry (expiresAt is in epoch seconds) const futureTime = Math.floor(Date.now() / 1000) + 3600 readStoreSpy = spyOn(mcpOauth, "readTokenStore").mockReturnValue({ "example.com/resource1": { accessToken: "token1", expiresAt: futureTime, }, "example.com/resource2": { accessToken: "token2", expiresAt: futureTime, }, }) // when checking OAuth tokens const result = await mcpOauth.checkMcpOAuthTokens() // then should pass expect(result.status).toBe("pass") expect(result.message).toContain("2") expect(result.message).toContain("valid") }) it("returns warn when some tokens expired", async () => { // given mix of valid and expired tokens (expiresAt is in epoch seconds) const futureTime = Math.floor(Date.now() / 1000) + 3600 const pastTime = Math.floor(Date.now() / 1000) - 3600 readStoreSpy = spyOn(mcpOauth, "readTokenStore").mockReturnValue({ "example.com/resource1": { accessToken: "token1", expiresAt: futureTime, }, "example.com/resource2": { accessToken: "token2", expiresAt: pastTime, }, }) // when checking OAuth tokens const result = await mcpOauth.checkMcpOAuthTokens() // then should warn expect(result.status).toBe("warn") expect(result.message).toContain("1") expect(result.message).toContain("expired") expect(result.details?.some((d: string) => d.includes("Expired"))).toBe( true ) }) it("returns pass when tokens have no expiry", async () => { // given tokens without expiry info readStoreSpy = spyOn(mcpOauth, "readTokenStore").mockReturnValue({ "example.com/resource1": { accessToken: "token1", }, }) // when checking OAuth tokens const result = await mcpOauth.checkMcpOAuthTokens() // then should pass (no expiry = assume valid) expect(result.status).toBe("pass") expect(result.message).toContain("1") }) it("includes token details in output", async () => { // given multiple tokens const futureTime = Math.floor(Date.now() / 1000) + 3600 readStoreSpy = spyOn(mcpOauth, "readTokenStore").mockReturnValue({ "api.example.com/v1": { accessToken: "token1", expiresAt: futureTime, }, "auth.example.com/oauth": { accessToken: "token2", expiresAt: futureTime, }, }) // when checking OAuth tokens const result = await mcpOauth.checkMcpOAuthTokens() // then should list tokens in details expect(result.details).toBeDefined() expect(result.details?.length).toBeGreaterThan(0) expect( result.details?.some((d: string) => d.includes("api.example.com")) ).toBe(true) expect( result.details?.some((d: string) => d.includes("auth.example.com")) ).toBe(true) }) }) })