Files
openclaw/extensions/msteams/src/token.test.ts
2026-05-08 05:28:12 +01:00

260 lines
8.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { readAccessToken } from "./token-response.js";
import { hasConfiguredMSTeamsCredentials, resolveMSTeamsCredentials } from "./token.js";
vi.mock("./secret-input.js", () => ({
normalizeSecretInputString: (v: unknown) =>
typeof v === "string" && v.trim() ? v.trim() : undefined,
normalizeResolvedSecretInputString: (opts: { value: unknown; path: string }) =>
typeof opts.value === "string" && opts.value.trim() ? opts.value.trim() : undefined,
hasConfiguredSecretInput: (v: unknown) => typeof v === "string" && v.trim().length > 0,
}));
const ENV_KEYS = [
"MSTEAMS_APP_ID",
"MSTEAMS_APP_PASSWORD",
"MSTEAMS_TENANT_ID",
"MSTEAMS_AUTH_TYPE",
"MSTEAMS_CERTIFICATE_PATH",
"MSTEAMS_CERTIFICATE_THUMBPRINT",
"MSTEAMS_USE_MANAGED_IDENTITY",
"MSTEAMS_MANAGED_IDENTITY_CLIENT_ID",
] as const;
let savedEnv: Record<string, string | undefined> = {};
function saveAndClearEnv() {
savedEnv = {};
for (const key of ENV_KEYS) {
savedEnv[key] = process.env[key];
delete process.env[key];
}
}
function restoreEnv() {
for (const key of ENV_KEYS) {
if (savedEnv[key] !== undefined) {
process.env[key] = savedEnv[key];
} else {
delete process.env[key];
}
}
}
describe("token secret credentials", () => {
beforeEach(saveAndClearEnv);
afterEach(restoreEnv);
it("returns true when appId + appPassword + tenantId are provided in config", () => {
const cfg = { appId: "app-id", appPassword: "app-pw", tenantId: "tenant-id" } as any;
expect(hasConfiguredMSTeamsCredentials(cfg)).toBe(true);
});
it("returns false when appPassword is missing", () => {
const cfg = { appId: "app-id", tenantId: "tenant-id" } as any;
expect(hasConfiguredMSTeamsCredentials(cfg)).toBe(false);
});
it("returns false when no config is given and no env vars set", () => {
expect(hasConfiguredMSTeamsCredentials(undefined)).toBe(false);
});
it("resolves secret credentials from config", () => {
const cfg = { appId: "app-id", appPassword: "app-pw", tenantId: "tenant-id" } as any;
const result = resolveMSTeamsCredentials(cfg);
expect(result).toEqual({
type: "secret",
appId: "app-id",
appPassword: "app-pw",
tenantId: "tenant-id",
});
});
it("resolves secret credentials from env vars", () => {
process.env.MSTEAMS_APP_ID = "env-app-id";
process.env.MSTEAMS_APP_PASSWORD = "env-app-pw";
process.env.MSTEAMS_TENANT_ID = "env-tenant-id";
const result = resolveMSTeamsCredentials(undefined);
expect(result).toEqual({
type: "secret",
appId: "env-app-id",
appPassword: "env-app-pw",
tenantId: "env-tenant-id",
});
});
it("returns undefined when appPassword is missing", () => {
const cfg = { appId: "app-id", tenantId: "tenant-id" } as any;
expect(resolveMSTeamsCredentials(cfg)).toBeUndefined();
});
});
describe("token federated credentials (certificate)", () => {
beforeEach(saveAndClearEnv);
afterEach(restoreEnv);
it("hasConfigured returns true when certificate path is provided", () => {
const cfg = {
appId: "app-id",
tenantId: "tenant-id",
authType: "federated",
certificatePath: "/cert.pem",
} as any;
expect(hasConfiguredMSTeamsCredentials(cfg)).toBe(true);
});
it("hasConfigured returns false when neither cert nor MI is provided", () => {
const cfg = { appId: "app-id", tenantId: "tenant-id", authType: "federated" } as any;
expect(hasConfiguredMSTeamsCredentials(cfg)).toBe(false);
});
it("resolves federated credentials with certificate from config", () => {
const cfg = {
appId: "app-id",
tenantId: "tenant-id",
authType: "federated",
certificatePath: "/cert.pem",
certificateThumbprint: "AABBCCDD",
} as any;
const result = resolveMSTeamsCredentials(cfg);
expect(result).toEqual({
type: "federated",
appId: "app-id",
tenantId: "tenant-id",
certificatePath: "/cert.pem",
certificateThumbprint: "AABBCCDD",
useManagedIdentity: undefined,
managedIdentityClientId: undefined,
});
});
it("resolves federated credentials from env vars", () => {
process.env.MSTEAMS_AUTH_TYPE = "federated";
process.env.MSTEAMS_APP_ID = "env-app-id";
process.env.MSTEAMS_TENANT_ID = "env-tenant-id";
process.env.MSTEAMS_CERTIFICATE_PATH = "/env/cert.pem";
process.env.MSTEAMS_CERTIFICATE_THUMBPRINT = "EEFF0011";
const result = resolveMSTeamsCredentials(undefined);
expect(result).toEqual({
type: "federated",
appId: "env-app-id",
tenantId: "env-tenant-id",
certificatePath: "/env/cert.pem",
certificateThumbprint: "EEFF0011",
useManagedIdentity: undefined,
managedIdentityClientId: undefined,
});
});
});
describe("token federated credentials (managed identity)", () => {
beforeEach(saveAndClearEnv);
afterEach(restoreEnv);
it("resolves managed identity from config", () => {
const cfg = {
appId: "app-id",
tenantId: "tenant-id",
authType: "federated",
useManagedIdentity: true,
managedIdentityClientId: "mi-client-id",
} as any;
const result = resolveMSTeamsCredentials(cfg);
expect(result).toEqual({
type: "federated",
appId: "app-id",
tenantId: "tenant-id",
certificatePath: undefined,
certificateThumbprint: undefined,
useManagedIdentity: true,
managedIdentityClientId: "mi-client-id",
});
});
it("resolves system-assigned managed identity (no clientId)", () => {
const cfg = {
appId: "app-id",
tenantId: "tenant-id",
authType: "federated",
useManagedIdentity: true,
} as any;
const result = resolveMSTeamsCredentials(cfg);
expect(result).toEqual({
type: "federated",
appId: "app-id",
tenantId: "tenant-id",
certificatePath: undefined,
certificateThumbprint: undefined,
useManagedIdentity: true,
managedIdentityClientId: undefined,
});
});
it("hasConfigured returns true for managed identity via env", () => {
process.env.MSTEAMS_AUTH_TYPE = "federated";
process.env.MSTEAMS_APP_ID = "env-app-id";
process.env.MSTEAMS_TENANT_ID = "env-tenant-id";
process.env.MSTEAMS_USE_MANAGED_IDENTITY = "true";
expect(hasConfiguredMSTeamsCredentials(undefined)).toBe(true);
});
it("config useManagedIdentity=false overrides env MSTEAMS_USE_MANAGED_IDENTITY=true", () => {
process.env.MSTEAMS_USE_MANAGED_IDENTITY = "true";
const cfg = {
appId: "app-id",
tenantId: "tenant-id",
authType: "federated",
certificatePath: "/cert.pem",
useManagedIdentity: false,
} as any;
const result = resolveMSTeamsCredentials(cfg);
expect(result).toMatchObject({
type: "federated",
certificatePath: "/cert.pem",
});
expect(result).not.toHaveProperty("useManagedIdentity", true);
});
});
describe("token backward compatibility", () => {
beforeEach(saveAndClearEnv);
afterEach(restoreEnv);
it("defaults to secret when authType is absent", () => {
const cfg = { appId: "app-id", appPassword: "pw", tenantId: "tenant-id" } as any;
const result = resolveMSTeamsCredentials(cfg);
expect(result).toMatchObject({ type: "secret" });
});
it("explicit authType=secret behaves same as absent", () => {
const cfg = {
appId: "app-id",
appPassword: "pw",
tenantId: "tenant-id",
authType: "secret",
} as any;
const result = resolveMSTeamsCredentials(cfg);
expect(result).toEqual({
type: "secret",
appId: "app-id",
appPassword: "pw",
tenantId: "tenant-id",
});
});
});
describe("readAccessToken", () => {
it("reads string and object token forms", () => {
expect(readAccessToken("abc")).toBe("abc");
expect(readAccessToken({ accessToken: "access-token" })).toBe("access-token");
expect(readAccessToken({ token: "fallback-token" })).toBe("fallback-token");
});
it("returns null for unsupported token payloads", () => {
expect(readAccessToken({ accessToken: 123 })).toBeNull();
expect(readAccessToken({ token: false })).toBeNull();
expect(readAccessToken(null)).toBeNull();
expect(readAccessToken(undefined)).toBeNull();
});
});