Files
OmniRoute/tests/unit/dashboard/m365-har-import.test.ts
N123 Project 6cd4d38e21 fix(m365): BizChat invocation shape drift + HAR-import UX + Antigravity alias note (#11069)
5 — M365 Copilot (BizChat) individual/consumer path — 3 itens: (1) forma de invocação do #10718 derivou de novo (2026-08-21 capture): optionsSets 14→34, allowedMessageTypes 6→30, tone "magic"→"Magic", plugins []→[{BingWebSearch}], disconnectBehavior em todos os tiers, +8 keys de clientInfo; verificado contra conta real com round-trip WebSocket (ping-then-close → resposta real). (2) Aviso sobre o alias Antigravity gemini-3.1-pro-high ainda não publicado (3.8.49 pré-data). (3) Botão "Import .har file" no modal de credencial M365.

Conflito resolvido em copilot-m365-frames.ts (board vs release tip): mantive o forwarding de opts.plugins/toolChoice/customInstructions do HEAD com os NOVOS defaults da captura (BingWebSearch builtin, tone "Magic"). Alinhei 3 testes pré-existentes que afirmavam o contrato antigo (m365-bizchat-frames-4042 clientInfo, m365-tone-model-variants tone, copilot-m365-tool-calls plugins) — propagação de contrato, não mascaramento. Rebaselinei AddApiKeyModal 1073→1080 (crescimento próprio da parte 3, ~Har import button) com anotação.

Validação: typecheck limpo, 142/142 testes m365/copilot verdes, changelog-integrit/file-size/eslint OK.
2026-08-21 22:32:19 -03:00

79 lines
3.0 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import {
extractM365CredentialFromHar,
describeHarImportExpiry,
M365_CHATHUB_WS_PREFIX,
} from "../../../src/shared/utils/m365HarImport.ts";
function fakeJwt(exp: number): string {
const b64 = (value: unknown) => Buffer.from(JSON.stringify(value)).toString("base64url");
return `${b64({ alg: "none" })}.${b64({ exp })}.sig`;
}
function harWithUrl(url: string | null): string {
return JSON.stringify({
log: { entries: url ? [{ request: { url } }] : [] },
});
}
test("extracts access_token + chathubPath from a matching ChatHub WS entry", () => {
const exp = Math.floor(Date.now() / 1000) + 3600;
const token = fakeJwt(exp);
const url = `${M365_CHATHUB_WS_PREFIX}oid-123%40tenant-456?chatsessionid=abc&access_token=${token}`;
const result = extractM365CredentialFromHar(harWithUrl(url));
assert.equal(result.ok, true);
if (!result.ok) return;
assert.equal(result.chathubPath, "oid-123@tenant-456");
assert.equal(result.apiKey, `access_token=${token}; chathubPath=oid-123@tenant-456`);
assert.ok(result.expiresAt !== null && Math.abs(result.expiresAt - exp * 1000) < 1000);
});
test("picks the LAST matching entry when a HAR has multiple turns", () => {
const oldToken = fakeJwt(1000);
const freshToken = fakeJwt(9999999999);
const har = JSON.stringify({
log: {
entries: [
{ request: { url: `${M365_CHATHUB_WS_PREFIX}u%40t?access_token=${oldToken}` } },
{ request: { url: "https://unrelated.example/" } },
{ request: { url: `${M365_CHATHUB_WS_PREFIX}u%40t?access_token=${freshToken}` } },
],
},
});
const result = extractM365CredentialFromHar(har);
assert.equal(result.ok, true);
if (!result.ok) return;
assert.ok(result.apiKey.includes(freshToken));
});
test("returns notJson for malformed input", () => {
const result = extractM365CredentialFromHar("not json{{{");
assert.deepEqual(result, { ok: false, error: "notJson" });
});
test("returns noEntries when log.entries is missing/not an array", () => {
const result = extractM365CredentialFromHar(JSON.stringify({ log: {} }));
assert.deepEqual(result, { ok: false, error: "noEntries" });
});
test("returns noChathubUrl when no request matches the ChatHub prefix", () => {
const result = extractM365CredentialFromHar(harWithUrl("https://example.com/"));
assert.deepEqual(result, { ok: false, error: "noChathubUrl" });
});
test("returns missingFields when the URL matches but lacks access_token or chathubPath", () => {
const result = extractM365CredentialFromHar(harWithUrl(`${M365_CHATHUB_WS_PREFIX}`));
assert.equal(result.ok, false);
});
test("describeHarImportExpiry classifies ok/warn/bad/unknown", () => {
const now = Date.now();
assert.equal(describeHarImportExpiry(now + 30 * 60000, now).tone, "ok");
assert.equal(describeHarImportExpiry(now + 5 * 60000, now).tone, "warn");
assert.equal(describeHarImportExpiry(now - 60000, now).tone, "bad");
assert.equal(describeHarImportExpiry(null, now).tone, "unknown");
});