mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-17 20:32:25 +03:00
#7858 — accumulateBotContent() silently returned an empty delta for any unrecognized frame shape, and finish() only had a fallback for the type:2 finalResultMessage case; a turn with no content in ANY known shape closed with a bare `stop` + `[DONE]`, indistinguishable from a genuine empty answer. finish() now emits a sanitized error (Hard Rule #12) naming the resolved tier and the likely causes, and unrecognized update-frame shapes are logged by argument KEY only (never content, tokens, or cookies). #7870 — the enterprise tier only changed buildWsUrl() query params; buildChatInvocation() always fell back to the consumer M365_DEFAULT_OPTION_SETS (which declares the MSA-only enable_msa_user flag) and tone:"". resolveConnectionParams()/resolveTierOverrides() now also resolve and surface the tier itself, threaded through wsChat() -> sendChat() -> buildChatInvocation() via a new resolveChatInvocationOverrides() helper, so an enterprise-tier invocation declares the enterprise_*/bizchat_* option sets, the wider allowedMessageTypes captured from the real enterprise HAR (Discussion #7850), and tone:"Magic" — while individual and EDU payloads stay byte-identical to today. Regression tests: tests/unit/copilot-m365-web-silent-empty-7858.test.ts, tests/unit/copilot-m365-enterprise-invocation-7870.test.ts.
93 lines
3.1 KiB
TypeScript
93 lines
3.1 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import {
|
|
CopilotM365WebExecutor,
|
|
__setCopilotM365WebSocketForTesting,
|
|
} from "../../open-sse/executors/copilot-m365-web.ts";
|
|
import { encodeFrame } from "../../open-sse/executors/copilot-m365-frames.ts";
|
|
|
|
function makeFakeWsCtor(frames: Array<Record<string, unknown>>) {
|
|
return class FakeWS {
|
|
private handlers: Record<string, (arg?: unknown) => void> = {};
|
|
constructor(_url: string) {
|
|
setImmediate(() => this.handlers.open?.());
|
|
}
|
|
on(event: string, cb: (arg?: unknown) => void) {
|
|
this.handlers[event] = cb;
|
|
return this;
|
|
}
|
|
send(data: unknown) {
|
|
const str = typeof data === "string" ? data : String(data);
|
|
if (str.includes('"protocol":"json"')) {
|
|
setImmediate(() => this.handlers.message?.(Buffer.from(encodeFrame({}))));
|
|
} else if (str.includes('"target":"chat"')) {
|
|
setImmediate(() => {
|
|
for (const frame of frames) this.handlers.message?.(Buffer.from(encodeFrame(frame)));
|
|
});
|
|
}
|
|
}
|
|
close() {}
|
|
};
|
|
}
|
|
|
|
test("copilot-m365-web: unrecognized frame shape must surface an error, not a silent stop [#7858]", async () => {
|
|
const frames = [
|
|
{ type: 1, target: "update", arguments: [{ someUnknownField: "not a known shape" }] },
|
|
{ type: 3 },
|
|
];
|
|
const restore = __setCopilotM365WebSocketForTesting(makeFakeWsCtor(frames) as never);
|
|
let body: string;
|
|
try {
|
|
const { response } = await new CopilotM365WebExecutor().execute({
|
|
model: "copilot-m365",
|
|
body: { messages: [{ role: "user", content: "hi" }] },
|
|
stream: true,
|
|
credentials: {
|
|
apiKey: "access_token=test-token",
|
|
providerSpecificData: { chathubPath: "user-oid@tenant-id" },
|
|
},
|
|
log: null,
|
|
} as never);
|
|
body = await response.text();
|
|
} finally {
|
|
restore();
|
|
}
|
|
|
|
assert.ok(
|
|
body.includes('"error"'),
|
|
`expected the stream to carry an explicit error for a fully-empty turn, got: ${body}`
|
|
);
|
|
});
|
|
|
|
test("copilot-m365-web: a legitimate content-bearing turn is unaffected [#7858 regression guard]", async () => {
|
|
const frames = [
|
|
{
|
|
type: 1,
|
|
target: "update",
|
|
arguments: [{ messages: [{ author: "bot", text: "hello there" }] }],
|
|
},
|
|
{ type: 3 },
|
|
];
|
|
const restore = __setCopilotM365WebSocketForTesting(makeFakeWsCtor(frames) as never);
|
|
let body: string;
|
|
try {
|
|
const { response } = await new CopilotM365WebExecutor().execute({
|
|
model: "copilot-m365",
|
|
body: { messages: [{ role: "user", content: "hi" }] },
|
|
stream: true,
|
|
credentials: {
|
|
apiKey: "access_token=test-token",
|
|
providerSpecificData: { chathubPath: "user-oid@tenant-id" },
|
|
},
|
|
log: null,
|
|
} as never);
|
|
body = await response.text();
|
|
} finally {
|
|
restore();
|
|
}
|
|
|
|
assert.ok(!body.includes('"error"'), `expected no error for a content-bearing turn, got: ${body}`);
|
|
assert.ok(body.includes("hello there"), `expected the streamed content, got: ${body}`);
|
|
assert.ok(body.includes('"finish_reason":"stop"'), `expected a stop chunk, got: ${body}`);
|
|
});
|