@openclaw/msteams 2026.5.2 → 2026.5.3-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +3 -0
- package/dist/channel-D7hdreTh.js +984 -0
- package/dist/channel-config-api.js +2 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-BC1ruIfN.js +573 -0
- package/dist/config-schema-B8QezH6t.js +15 -0
- package/dist/contract-api.js +2 -0
- package/dist/graph-users-9uQJepqr.js +1354 -0
- package/dist/index.js +22 -0
- package/dist/oauth-BWJyilR1.js +114 -0
- package/dist/oauth.token-xxpoLWy5.js +115 -0
- package/dist/policy-DTnU2GR7.js +142 -0
- package/dist/probe-D_H8yFps.js +2194 -0
- package/dist/resolve-allowlist-D41JSziq.js +219 -0
- package/dist/runtime-api-DV1iVMn1.js +28 -0
- package/dist/runtime-api.js +2 -0
- package/dist/secret-contract-BuoEXmPS.js +35 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/setup-entry.js +15 -0
- package/dist/setup-plugin-api.js +64 -0
- package/dist/setup-surface-BLkFQYIQ.js +313 -0
- package/dist/src-CFp1QpFd.js +4064 -0
- package/dist/test-api.js +2 -0
- package/package.json +14 -6
- package/api.ts +0 -3
- package/channel-config-api.ts +0 -1
- package/channel-plugin-api.ts +0 -2
- package/config-api.ts +0 -4
- package/contract-api.ts +0 -4
- package/index.ts +0 -20
- package/runtime-api.ts +0 -73
- package/secret-contract-api.ts +0 -5
- package/setup-entry.ts +0 -13
- package/setup-plugin-api.ts +0 -3
- package/src/ai-entity.ts +0 -7
- package/src/approval-auth.ts +0 -44
- package/src/attachments/bot-framework.test.ts +0 -461
- package/src/attachments/bot-framework.ts +0 -362
- package/src/attachments/download.ts +0 -311
- package/src/attachments/graph.test.ts +0 -416
- package/src/attachments/graph.ts +0 -484
- package/src/attachments/html.ts +0 -122
- package/src/attachments/payload.ts +0 -14
- package/src/attachments/remote-media.test.ts +0 -137
- package/src/attachments/remote-media.ts +0 -112
- package/src/attachments/shared.test.ts +0 -530
- package/src/attachments/shared.ts +0 -626
- package/src/attachments/types.ts +0 -47
- package/src/attachments.graph.test.ts +0 -342
- package/src/attachments.helpers.test.ts +0 -246
- package/src/attachments.test-helpers.ts +0 -17
- package/src/attachments.test.ts +0 -687
- package/src/attachments.ts +0 -18
- package/src/block-streaming-config.test.ts +0 -61
- package/src/channel-api.ts +0 -1
- package/src/channel.actions.test.ts +0 -742
- package/src/channel.directory.test.ts +0 -200
- package/src/channel.runtime.ts +0 -56
- package/src/channel.setup.ts +0 -77
- package/src/channel.test.ts +0 -128
- package/src/channel.ts +0 -1136
- package/src/config-schema.ts +0 -6
- package/src/config-ui-hints.ts +0 -12
- package/src/conversation-store-fs.test.ts +0 -74
- package/src/conversation-store-fs.ts +0 -149
- package/src/conversation-store-helpers.test.ts +0 -202
- package/src/conversation-store-helpers.ts +0 -105
- package/src/conversation-store-memory.ts +0 -51
- package/src/conversation-store.shared.test.ts +0 -225
- package/src/conversation-store.ts +0 -71
- package/src/directory-live.test.ts +0 -156
- package/src/directory-live.ts +0 -111
- package/src/doctor.ts +0 -27
- package/src/errors.test.ts +0 -133
- package/src/errors.ts +0 -246
- package/src/feedback-reflection-prompt.ts +0 -117
- package/src/feedback-reflection-store.ts +0 -114
- package/src/feedback-reflection.test.ts +0 -237
- package/src/feedback-reflection.ts +0 -283
- package/src/file-consent-helpers.test.ts +0 -326
- package/src/file-consent-helpers.ts +0 -126
- package/src/file-consent-invoke.ts +0 -150
- package/src/file-consent.test.ts +0 -363
- package/src/file-consent.ts +0 -287
- package/src/graph-chat.ts +0 -55
- package/src/graph-group-management.test.ts +0 -318
- package/src/graph-group-management.ts +0 -168
- package/src/graph-members.test.ts +0 -89
- package/src/graph-members.ts +0 -48
- package/src/graph-messages.actions.test.ts +0 -243
- package/src/graph-messages.read.test.ts +0 -391
- package/src/graph-messages.search.test.ts +0 -213
- package/src/graph-messages.test-helpers.ts +0 -50
- package/src/graph-messages.ts +0 -534
- package/src/graph-teams.test.ts +0 -215
- package/src/graph-teams.ts +0 -114
- package/src/graph-thread.test.ts +0 -246
- package/src/graph-thread.ts +0 -146
- package/src/graph-upload.test.ts +0 -258
- package/src/graph-upload.ts +0 -531
- package/src/graph-users.ts +0 -29
- package/src/graph.test.ts +0 -516
- package/src/graph.ts +0 -293
- package/src/inbound.test.ts +0 -221
- package/src/inbound.ts +0 -148
- package/src/index.ts +0 -4
- package/src/media-helpers.test.ts +0 -202
- package/src/media-helpers.ts +0 -105
- package/src/mentions.test.ts +0 -244
- package/src/mentions.ts +0 -114
- package/src/messenger.test.ts +0 -865
- package/src/messenger.ts +0 -605
- package/src/monitor-handler/access.ts +0 -125
- package/src/monitor-handler/inbound-media.test.ts +0 -289
- package/src/monitor-handler/inbound-media.ts +0 -180
- package/src/monitor-handler/message-handler-mock-support.test-support.ts +0 -28
- package/src/monitor-handler/message-handler.authz.test.ts +0 -669
- package/src/monitor-handler/message-handler.dm-media.test.ts +0 -54
- package/src/monitor-handler/message-handler.test-support.ts +0 -100
- package/src/monitor-handler/message-handler.thread-parent.test.ts +0 -223
- package/src/monitor-handler/message-handler.thread-session.test.ts +0 -77
- package/src/monitor-handler/message-handler.ts +0 -1000
- package/src/monitor-handler/reaction-handler.test.ts +0 -267
- package/src/monitor-handler/reaction-handler.ts +0 -210
- package/src/monitor-handler/thread-session.ts +0 -17
- package/src/monitor-handler.adaptive-card.test.ts +0 -162
- package/src/monitor-handler.feedback-authz.test.ts +0 -314
- package/src/monitor-handler.file-consent.test.ts +0 -423
- package/src/monitor-handler.sso.test.ts +0 -563
- package/src/monitor-handler.test-helpers.ts +0 -180
- package/src/monitor-handler.ts +0 -534
- package/src/monitor-handler.types.ts +0 -27
- package/src/monitor-types.ts +0 -6
- package/src/monitor.lifecycle.test.ts +0 -278
- package/src/monitor.test.ts +0 -119
- package/src/monitor.ts +0 -442
- package/src/oauth.flow.ts +0 -77
- package/src/oauth.shared.ts +0 -37
- package/src/oauth.test.ts +0 -305
- package/src/oauth.token.ts +0 -158
- package/src/oauth.ts +0 -130
- package/src/outbound.test.ts +0 -130
- package/src/outbound.ts +0 -71
- package/src/pending-uploads-fs.test.ts +0 -246
- package/src/pending-uploads-fs.ts +0 -235
- package/src/pending-uploads.test.ts +0 -173
- package/src/pending-uploads.ts +0 -121
- package/src/policy.test.ts +0 -240
- package/src/policy.ts +0 -262
- package/src/polls-store-memory.ts +0 -32
- package/src/polls.test.ts +0 -160
- package/src/polls.ts +0 -323
- package/src/presentation.ts +0 -68
- package/src/probe.test.ts +0 -77
- package/src/probe.ts +0 -132
- package/src/reply-dispatcher.test.ts +0 -437
- package/src/reply-dispatcher.ts +0 -346
- package/src/reply-stream-controller.test.ts +0 -235
- package/src/reply-stream-controller.ts +0 -147
- package/src/resolve-allowlist.test.ts +0 -250
- package/src/resolve-allowlist.ts +0 -309
- package/src/revoked-context.ts +0 -17
- package/src/runtime.ts +0 -9
- package/src/sdk-types.ts +0 -59
- package/src/sdk.test.ts +0 -666
- package/src/sdk.ts +0 -884
- package/src/secret-contract.ts +0 -49
- package/src/secret-input.ts +0 -7
- package/src/send-context.ts +0 -231
- package/src/send.test.ts +0 -493
- package/src/send.ts +0 -637
- package/src/sent-message-cache.test.ts +0 -15
- package/src/sent-message-cache.ts +0 -56
- package/src/session-route.ts +0 -40
- package/src/setup-core.ts +0 -160
- package/src/setup-surface.test.ts +0 -202
- package/src/setup-surface.ts +0 -320
- package/src/sso-token-store.test.ts +0 -72
- package/src/sso-token-store.ts +0 -166
- package/src/sso.ts +0 -300
- package/src/storage.ts +0 -25
- package/src/store-fs.ts +0 -44
- package/src/streaming-message.test.ts +0 -262
- package/src/streaming-message.ts +0 -297
- package/src/test-runtime.ts +0 -16
- package/src/thread-parent-context.test.ts +0 -224
- package/src/thread-parent-context.ts +0 -159
- package/src/token-response.ts +0 -11
- package/src/token.test.ts +0 -259
- package/src/token.ts +0 -195
- package/src/user-agent.test.ts +0 -86
- package/src/user-agent.ts +0 -53
- package/src/webhook-timeouts.ts +0 -27
- package/src/welcome-card.test.ts +0 -81
- package/src/welcome-card.ts +0 -57
- package/test-api.ts +0 -1
- package/tsconfig.json +0 -16
|
@@ -1,416 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
// Mock shared.js to avoid transitive runtime-api imports that pull in uninstalled packages.
|
|
4
|
-
vi.mock("./shared.js", async (importOriginal) => {
|
|
5
|
-
const actual = await importOriginal<typeof import("./shared.js")>();
|
|
6
|
-
return {
|
|
7
|
-
...actual,
|
|
8
|
-
applyAuthorizationHeaderForUrl: vi.fn(),
|
|
9
|
-
GRAPH_ROOT: "https://graph.microsoft.com/v1.0",
|
|
10
|
-
inferPlaceholder: vi.fn(({ contentType }: { contentType?: string }) =>
|
|
11
|
-
contentType?.startsWith("image/") ? "[image]" : "[file]",
|
|
12
|
-
),
|
|
13
|
-
isRecord: (v: unknown) => typeof v === "object" && v !== null && !Array.isArray(v),
|
|
14
|
-
isUrlAllowed: vi.fn(() => true),
|
|
15
|
-
normalizeContentType: vi.fn((ct: string | null | undefined) => ct ?? undefined),
|
|
16
|
-
resolveMediaSsrfPolicy: vi.fn(() => undefined),
|
|
17
|
-
resolveAttachmentFetchPolicy: vi.fn(() => ({ allowHosts: ["*"], authAllowHosts: ["*"] })),
|
|
18
|
-
resolveRequestUrl: vi.fn((input: string) => input),
|
|
19
|
-
safeFetchWithPolicy: vi.fn(),
|
|
20
|
-
};
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
vi.mock("openclaw/plugin-sdk/ssrf-runtime", () => ({
|
|
24
|
-
fetchWithSsrFGuard: vi.fn(),
|
|
25
|
-
}));
|
|
26
|
-
|
|
27
|
-
vi.mock("../runtime.js", () => ({
|
|
28
|
-
getMSTeamsRuntime: vi.fn(() => ({
|
|
29
|
-
media: {
|
|
30
|
-
detectMime: vi.fn(async () => "image/png"),
|
|
31
|
-
},
|
|
32
|
-
channel: {
|
|
33
|
-
media: {
|
|
34
|
-
saveMediaBuffer: vi.fn(async (_buf: Buffer, ct: string) => ({
|
|
35
|
-
path: "/tmp/saved.png",
|
|
36
|
-
contentType: ct ?? "image/png",
|
|
37
|
-
})),
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
})),
|
|
41
|
-
}));
|
|
42
|
-
|
|
43
|
-
vi.mock("./download.js", () => ({
|
|
44
|
-
downloadMSTeamsAttachments: vi.fn(async () => []),
|
|
45
|
-
}));
|
|
46
|
-
|
|
47
|
-
vi.mock("./remote-media.js", () => ({
|
|
48
|
-
downloadAndStoreMSTeamsRemoteMedia: vi.fn(),
|
|
49
|
-
}));
|
|
50
|
-
|
|
51
|
-
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
52
|
-
import { downloadMSTeamsGraphMedia } from "./graph.js";
|
|
53
|
-
import { downloadAndStoreMSTeamsRemoteMedia } from "./remote-media.js";
|
|
54
|
-
import { safeFetchWithPolicy } from "./shared.js";
|
|
55
|
-
|
|
56
|
-
function mockFetchResponse(body: unknown, status = 200) {
|
|
57
|
-
const bodyStr = typeof body === "string" ? body : JSON.stringify(body);
|
|
58
|
-
return new Response(bodyStr, { status, headers: { "content-type": "application/json" } });
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function mockBinaryResponse(data: Uint8Array, status = 200) {
|
|
62
|
-
return new Response(Buffer.from(data) as BodyInit, { status });
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
type GuardedFetchParams = { url: string; init?: RequestInit };
|
|
66
|
-
|
|
67
|
-
function guardedFetchResult(params: GuardedFetchParams, response: Response) {
|
|
68
|
-
return {
|
|
69
|
-
response,
|
|
70
|
-
release: async () => {},
|
|
71
|
-
finalUrl: params.url,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function mockGraphMediaFetch(options: {
|
|
76
|
-
messageId: string;
|
|
77
|
-
messageResponse?: unknown;
|
|
78
|
-
hostedContents?: unknown[];
|
|
79
|
-
valueResponses?: Record<string, Response>;
|
|
80
|
-
fetchCalls?: string[];
|
|
81
|
-
}) {
|
|
82
|
-
vi.mocked(fetchWithSsrFGuard).mockImplementation(async (params: GuardedFetchParams) => {
|
|
83
|
-
options.fetchCalls?.push(params.url);
|
|
84
|
-
const url = params.url;
|
|
85
|
-
if (url.endsWith(`/messages/${options.messageId}`) && !url.includes("hostedContents")) {
|
|
86
|
-
return guardedFetchResult(
|
|
87
|
-
params,
|
|
88
|
-
mockFetchResponse(options.messageResponse ?? { body: {}, attachments: [] }),
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
if (url.endsWith("/hostedContents")) {
|
|
92
|
-
return guardedFetchResult(params, mockFetchResponse({ value: options.hostedContents ?? [] }));
|
|
93
|
-
}
|
|
94
|
-
for (const [fragment, response] of Object.entries(options.valueResponses ?? {})) {
|
|
95
|
-
if (url.includes(fragment)) {
|
|
96
|
-
return guardedFetchResult(params, response);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return guardedFetchResult(params, mockFetchResponse({}, 404));
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
describe("downloadMSTeamsGraphMedia hosted content $value fallback", () => {
|
|
104
|
-
beforeEach(() => {
|
|
105
|
-
vi.clearAllMocks();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("fetches $value endpoint when contentBytes is null but item.id exists", async () => {
|
|
109
|
-
const imageBytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47]); // PNG magic bytes
|
|
110
|
-
|
|
111
|
-
const fetchCalls: string[] = [];
|
|
112
|
-
|
|
113
|
-
mockGraphMediaFetch({
|
|
114
|
-
messageId: "msg-1",
|
|
115
|
-
hostedContents: [{ id: "hosted-123", contentType: "image/png", contentBytes: null }],
|
|
116
|
-
valueResponses: {
|
|
117
|
-
"/hostedContents/hosted-123/$value": mockBinaryResponse(imageBytes),
|
|
118
|
-
},
|
|
119
|
-
fetchCalls,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
const result = await downloadMSTeamsGraphMedia({
|
|
123
|
-
messageUrl: "https://graph.microsoft.com/v1.0/chats/c/messages/msg-1",
|
|
124
|
-
tokenProvider: { getAccessToken: vi.fn(async () => "test-token") },
|
|
125
|
-
maxBytes: 10 * 1024 * 1024,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
// Verify the $value endpoint was fetched
|
|
129
|
-
const valueCall = fetchCalls.find((u) => u.includes("/hostedContents/hosted-123/$value"));
|
|
130
|
-
expect(valueCall).toBeDefined();
|
|
131
|
-
expect(result.media.length).toBeGreaterThan(0);
|
|
132
|
-
expect(result.hostedCount).toBe(1);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it("skips hosted content when contentBytes is null and id is missing", async () => {
|
|
136
|
-
mockGraphMediaFetch({
|
|
137
|
-
messageId: "msg-2",
|
|
138
|
-
hostedContents: [{ contentType: "image/png", contentBytes: null }],
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
const result = await downloadMSTeamsGraphMedia({
|
|
142
|
-
messageUrl: "https://graph.microsoft.com/v1.0/chats/c/messages/msg-2",
|
|
143
|
-
tokenProvider: { getAccessToken: vi.fn(async () => "test-token") },
|
|
144
|
-
maxBytes: 10 * 1024 * 1024,
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// No media because there's no id to fetch $value from and no contentBytes
|
|
148
|
-
expect(result.media).toHaveLength(0);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it("skips $value content when Content-Length exceeds maxBytes", async () => {
|
|
152
|
-
const fetchCalls: string[] = [];
|
|
153
|
-
|
|
154
|
-
mockGraphMediaFetch({
|
|
155
|
-
messageId: "msg-cl",
|
|
156
|
-
hostedContents: [{ id: "hosted-big", contentType: "image/png", contentBytes: null }],
|
|
157
|
-
valueResponses: {
|
|
158
|
-
"/hostedContents/hosted-big/$value": new Response(
|
|
159
|
-
Buffer.from(new Uint8Array([0x89, 0x50, 0x4e, 0x47])) as BodyInit,
|
|
160
|
-
{
|
|
161
|
-
status: 200,
|
|
162
|
-
headers: { "content-length": "999999999" },
|
|
163
|
-
},
|
|
164
|
-
),
|
|
165
|
-
},
|
|
166
|
-
fetchCalls,
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
const result = await downloadMSTeamsGraphMedia({
|
|
170
|
-
messageUrl: "https://graph.microsoft.com/v1.0/chats/c/messages/msg-cl",
|
|
171
|
-
tokenProvider: { getAccessToken: vi.fn(async () => "test-token") },
|
|
172
|
-
maxBytes: 1024, // 1 KB limit
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
// $value was fetched but skipped due to Content-Length exceeding maxBytes
|
|
176
|
-
const valueCall = fetchCalls.find((u) => u.includes("/hostedContents/hosted-big/$value"));
|
|
177
|
-
expect(valueCall).toBeDefined();
|
|
178
|
-
expect(result.media).toHaveLength(0);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it("uses inline contentBytes when available instead of $value", async () => {
|
|
182
|
-
const fetchCalls: string[] = [];
|
|
183
|
-
const base64Png = Buffer.from([0x89, 0x50, 0x4e, 0x47]).toString("base64");
|
|
184
|
-
|
|
185
|
-
mockGraphMediaFetch({
|
|
186
|
-
messageId: "msg-3",
|
|
187
|
-
hostedContents: [{ id: "hosted-456", contentType: "image/png", contentBytes: base64Png }],
|
|
188
|
-
fetchCalls,
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
const result = await downloadMSTeamsGraphMedia({
|
|
192
|
-
messageUrl: "https://graph.microsoft.com/v1.0/chats/c/messages/msg-3",
|
|
193
|
-
tokenProvider: { getAccessToken: vi.fn(async () => "test-token") },
|
|
194
|
-
maxBytes: 10 * 1024 * 1024,
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
// Should NOT have fetched $value since contentBytes was available
|
|
198
|
-
const valueCall = fetchCalls.find((u) => u.includes("/$value"));
|
|
199
|
-
expect(valueCall).toBeUndefined();
|
|
200
|
-
expect(result.media.length).toBeGreaterThan(0);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it("adds the OpenClaw User-Agent to guarded Graph attachment fetches", async () => {
|
|
204
|
-
mockGraphMediaFetch({ messageId: "msg-ua" });
|
|
205
|
-
|
|
206
|
-
await downloadMSTeamsGraphMedia({
|
|
207
|
-
messageUrl: "https://graph.microsoft.com/v1.0/chats/c/messages/msg-ua",
|
|
208
|
-
tokenProvider: { getAccessToken: vi.fn(async () => "test-token") },
|
|
209
|
-
maxBytes: 10 * 1024 * 1024,
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
const guardCalls = vi.mocked(fetchWithSsrFGuard).mock.calls;
|
|
213
|
-
for (const [call] of guardCalls) {
|
|
214
|
-
const headers = call.init?.headers;
|
|
215
|
-
expect(headers).toBeInstanceOf(Headers);
|
|
216
|
-
expect((headers as Headers).get("Authorization")).toBe("Bearer test-token");
|
|
217
|
-
expect((headers as Headers).get("User-Agent")).toMatch(
|
|
218
|
-
/^teams\.ts\[apps\]\/.+ OpenClaw\/.+$/,
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it("adds the OpenClaw User-Agent to Graph shares downloads for reference attachments", async () => {
|
|
224
|
-
mockGraphMediaFetch({
|
|
225
|
-
messageId: "msg-share",
|
|
226
|
-
messageResponse: {
|
|
227
|
-
body: {},
|
|
228
|
-
attachments: [
|
|
229
|
-
{
|
|
230
|
-
contentType: "reference",
|
|
231
|
-
contentUrl: "https://tenant.sharepoint.com/file.docx",
|
|
232
|
-
name: "file.docx",
|
|
233
|
-
},
|
|
234
|
-
],
|
|
235
|
-
},
|
|
236
|
-
});
|
|
237
|
-
vi.mocked(safeFetchWithPolicy).mockResolvedValue(new Response(null, { status: 200 }));
|
|
238
|
-
vi.mocked(downloadAndStoreMSTeamsRemoteMedia).mockImplementation(async (params) => {
|
|
239
|
-
if (params.fetchImpl) {
|
|
240
|
-
await params.fetchImpl(params.url, {});
|
|
241
|
-
}
|
|
242
|
-
return {
|
|
243
|
-
path: "/tmp/file.docx",
|
|
244
|
-
contentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
245
|
-
placeholder: "[file]",
|
|
246
|
-
};
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
await downloadMSTeamsGraphMedia({
|
|
250
|
-
messageUrl: "https://graph.microsoft.com/v1.0/chats/c/messages/msg-share",
|
|
251
|
-
tokenProvider: { getAccessToken: vi.fn(async () => "test-token") },
|
|
252
|
-
maxBytes: 10 * 1024 * 1024,
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
expect(safeFetchWithPolicy).toHaveBeenCalledWith(
|
|
256
|
-
expect.objectContaining({
|
|
257
|
-
requestInit: expect.objectContaining({
|
|
258
|
-
headers: expect.any(Headers),
|
|
259
|
-
}),
|
|
260
|
-
}),
|
|
261
|
-
);
|
|
262
|
-
const requestInit = vi.mocked(safeFetchWithPolicy).mock.calls[0]?.[0]?.requestInit;
|
|
263
|
-
const headers = requestInit?.headers as Headers;
|
|
264
|
-
expect(headers.get("User-Agent")).toMatch(/^teams\.ts\[apps\]\/.+ OpenClaw\/.+$/);
|
|
265
|
-
});
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
describe("downloadMSTeamsGraphMedia attachment sourcing and error logging", () => {
|
|
269
|
-
beforeEach(() => {
|
|
270
|
-
vi.clearAllMocks();
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
it("does NOT call the nonexistent ${messageUrl}/attachments sub-resource", async () => {
|
|
274
|
-
// The Graph v1.0 API does not expose a `/attachments` sub-resource on
|
|
275
|
-
// channel or chat messages. Issue #58617 documented that the old code
|
|
276
|
-
// path called this endpoint and recorded a 404 in diagnostics. After
|
|
277
|
-
// this fix, the helper must source attachments from the main message
|
|
278
|
-
// resource's inline `attachments` array instead.
|
|
279
|
-
const fetchCalls: string[] = [];
|
|
280
|
-
|
|
281
|
-
mockGraphMediaFetch({
|
|
282
|
-
messageId: "msg-no-sub",
|
|
283
|
-
messageResponse: {
|
|
284
|
-
body: { content: "hi" },
|
|
285
|
-
attachments: [],
|
|
286
|
-
},
|
|
287
|
-
fetchCalls,
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
await downloadMSTeamsGraphMedia({
|
|
291
|
-
messageUrl: "https://graph.microsoft.com/v1.0/chats/c/messages/msg-no-sub",
|
|
292
|
-
tokenProvider: { getAccessToken: vi.fn(async () => "test-token") },
|
|
293
|
-
maxBytes: 10 * 1024 * 1024,
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
const calledSubResource = fetchCalls.some((u) =>
|
|
297
|
-
u.endsWith("/messages/msg-no-sub/attachments"),
|
|
298
|
-
);
|
|
299
|
-
expect(calledSubResource).toBe(false);
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
it("sources reference attachments from the message body's attachments array", async () => {
|
|
303
|
-
// Before the fix, the helper fetched `/attachments` and used that list.
|
|
304
|
-
// After the fix, it must use `msgData.attachments` from the main fetch.
|
|
305
|
-
mockGraphMediaFetch({
|
|
306
|
-
messageId: "msg-inline",
|
|
307
|
-
messageResponse: {
|
|
308
|
-
body: {},
|
|
309
|
-
attachments: [
|
|
310
|
-
{
|
|
311
|
-
contentType: "reference",
|
|
312
|
-
contentUrl: "https://tenant.sharepoint.com/inline.pdf",
|
|
313
|
-
name: "inline.pdf",
|
|
314
|
-
},
|
|
315
|
-
],
|
|
316
|
-
},
|
|
317
|
-
});
|
|
318
|
-
vi.mocked(safeFetchWithPolicy).mockResolvedValue(new Response(null, { status: 200 }));
|
|
319
|
-
vi.mocked(downloadAndStoreMSTeamsRemoteMedia).mockResolvedValue({
|
|
320
|
-
path: "/tmp/inline.pdf",
|
|
321
|
-
contentType: "application/pdf",
|
|
322
|
-
placeholder: "[file]",
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
const result = await downloadMSTeamsGraphMedia({
|
|
326
|
-
messageUrl: "https://graph.microsoft.com/v1.0/chats/c/messages/msg-inline",
|
|
327
|
-
tokenProvider: { getAccessToken: vi.fn(async () => "test-token") },
|
|
328
|
-
maxBytes: 10 * 1024 * 1024,
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
expect(result.media).toHaveLength(1);
|
|
332
|
-
expect(result.media[0]?.path).toBe("/tmp/inline.pdf");
|
|
333
|
-
// Regression guard: attachmentCount now reflects real inline attachments,
|
|
334
|
-
// not the imaginary `/attachments` sub-resource count.
|
|
335
|
-
expect(result.attachmentCount).toBe(1);
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
it("logs a debug event when the message fetch throws instead of swallowing it", async () => {
|
|
339
|
-
// Regression test for #51749: empty `catch {}` blocks used to hide the
|
|
340
|
-
// real error, producing misleading `graph media fetch empty` diagnostics
|
|
341
|
-
// without surfacing the underlying cause.
|
|
342
|
-
vi.mocked(fetchWithSsrFGuard).mockImplementation(async (params: GuardedFetchParams) => {
|
|
343
|
-
if (params.url.endsWith("/messages/msg-err")) {
|
|
344
|
-
throw new Error("network boom");
|
|
345
|
-
}
|
|
346
|
-
// hostedContents and any other paths succeed so the error branch under
|
|
347
|
-
// test is the only one that fires.
|
|
348
|
-
return guardedFetchResult(params, mockFetchResponse({ value: [] }));
|
|
349
|
-
});
|
|
350
|
-
const logger = { warn: vi.fn() };
|
|
351
|
-
|
|
352
|
-
const result = await downloadMSTeamsGraphMedia({
|
|
353
|
-
messageUrl: "https://graph.microsoft.com/v1.0/chats/c/messages/msg-err",
|
|
354
|
-
tokenProvider: { getAccessToken: vi.fn(async () => "test-token") },
|
|
355
|
-
maxBytes: 10 * 1024 * 1024,
|
|
356
|
-
logger,
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
expect(result.media).toHaveLength(0);
|
|
360
|
-
expect(logger.warn).toHaveBeenCalledWith(
|
|
361
|
-
"msteams graph message fetch failed",
|
|
362
|
-
expect.objectContaining({ error: "network boom" }),
|
|
363
|
-
);
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
it("logs a debug event when the message fetch returns non-ok", async () => {
|
|
367
|
-
// If the message endpoint returns 403/404, we want that recorded so
|
|
368
|
-
// operators can distinguish auth issues from empty result sets.
|
|
369
|
-
vi.mocked(fetchWithSsrFGuard).mockImplementation(async (params: GuardedFetchParams) => {
|
|
370
|
-
const url = params.url;
|
|
371
|
-
if (url.endsWith("/hostedContents")) {
|
|
372
|
-
return guardedFetchResult(params, mockFetchResponse({ value: [] }));
|
|
373
|
-
}
|
|
374
|
-
return guardedFetchResult(params, mockFetchResponse({ error: "forbidden" }, 403));
|
|
375
|
-
});
|
|
376
|
-
const log = { debug: vi.fn() };
|
|
377
|
-
|
|
378
|
-
const result = await downloadMSTeamsGraphMedia({
|
|
379
|
-
messageUrl: "https://graph.microsoft.com/v1.0/chats/c/messages/msg-403",
|
|
380
|
-
tokenProvider: { getAccessToken: vi.fn(async () => "test-token") },
|
|
381
|
-
maxBytes: 10 * 1024 * 1024,
|
|
382
|
-
log,
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
expect(result.media).toHaveLength(0);
|
|
386
|
-
expect(result.attachmentStatus).toBe(403);
|
|
387
|
-
expect(log.debug).toHaveBeenCalledWith(
|
|
388
|
-
"graph media message fetch not ok",
|
|
389
|
-
expect.objectContaining({ status: 403 }),
|
|
390
|
-
);
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
it("logs a debug event when token acquisition fails", async () => {
|
|
394
|
-
vi.mocked(fetchWithSsrFGuard).mockImplementation(async (params: GuardedFetchParams) =>
|
|
395
|
-
guardedFetchResult(params, mockFetchResponse({})),
|
|
396
|
-
);
|
|
397
|
-
const logger = { warn: vi.fn() };
|
|
398
|
-
|
|
399
|
-
const result = await downloadMSTeamsGraphMedia({
|
|
400
|
-
messageUrl: "https://graph.microsoft.com/v1.0/chats/c/messages/msg-token",
|
|
401
|
-
tokenProvider: {
|
|
402
|
-
getAccessToken: vi.fn(async () => {
|
|
403
|
-
throw new Error("token expired");
|
|
404
|
-
}),
|
|
405
|
-
},
|
|
406
|
-
maxBytes: 10 * 1024 * 1024,
|
|
407
|
-
logger,
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
expect(result.tokenError).toBe(true);
|
|
411
|
-
expect(logger.warn).toHaveBeenCalledWith(
|
|
412
|
-
"msteams graph token acquisition failed",
|
|
413
|
-
expect.objectContaining({ error: "token expired" }),
|
|
414
|
-
);
|
|
415
|
-
});
|
|
416
|
-
});
|