diff --git a/extensions/msteams/src/probe.test.ts b/extensions/msteams/src/probe.test.ts index c876a1469f4..a63bffef005 100644 --- a/extensions/msteams/src/probe.test.ts +++ b/extensions/msteams/src/probe.test.ts @@ -3,6 +3,15 @@ import type { MSTeamsConfig } from "../runtime-api.js"; const hostMockState = vi.hoisted(() => ({ tokenError: null as Error | null, + delegatedTokens: undefined as + | { + accessToken: string; + refreshToken: string; + expiresAt: number; + scopes: string[]; + userPrincipalName?: string; + } + | undefined, })); vi.mock("@microsoft/teams.apps", () => ({ @@ -33,11 +42,20 @@ vi.mock("@microsoft/teams.api", () => ({ }), })); +vi.mock("./token.js", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + loadDelegatedTokens: () => hostMockState.delegatedTokens, + }; +}); + import { probeMSTeams } from "./probe.js"; describe("msteams probe", () => { beforeEach(() => { hostMockState.tokenError = null; + hostMockState.delegatedTokens = undefined; vi.stubEnv("MSTEAMS_APP_ID", ""); vi.stubEnv("MSTEAMS_APP_PASSWORD", ""); vi.stubEnv("MSTEAMS_TENANT_ID", ""); @@ -83,4 +101,38 @@ describe("msteams probe", () => { error: "bad creds", }); }); + + it("reports delegated tokens expired when the process clock is invalid", async () => { + const nowSpy = vi.spyOn(Date, "now").mockReturnValue(Number.NaN); + hostMockState.delegatedTokens = { + accessToken: "delegated-token", + refreshToken: "refresh-token", + expiresAt: Date.parse("2030-01-01T00:00:00.000Z"), + scopes: ["ChatMessage.Send"], + userPrincipalName: "user@example.com", + }; + const cfg = { + enabled: true, + appId: "app", + appPassword: "pw", + tenantId: "tenant", + delegatedAuth: { enabled: true }, + } as unknown as MSTeamsConfig; + + try { + await expect(probeMSTeams(cfg)).resolves.toEqual({ + ok: true, + appId: "app", + graph: { ok: true, roles: undefined, scopes: undefined }, + delegatedAuth: { + ok: false, + scopes: ["ChatMessage.Send"], + userPrincipalName: "user@example.com", + error: "token expired (will auto-refresh on next use)", + }, + }); + } finally { + nowSpy.mockRestore(); + } + }); }); diff --git a/extensions/msteams/src/probe.ts b/extensions/msteams/src/probe.ts index a0ab0429732..d30e3f9c415 100644 --- a/extensions/msteams/src/probe.ts +++ b/extensions/msteams/src/probe.ts @@ -1,3 +1,4 @@ +import { isFutureDateTimestampMs } from "openclaw/plugin-sdk/number-runtime"; import { normalizeStringEntries, type BaseProbeResult, @@ -100,7 +101,7 @@ export async function probeMSTeams(cfg?: MSTeamsConfig): Promise