@auxiora/channels 1.0.0
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/LICENSE +191 -0
- package/dist/adapters/bluebubbles.d.ts +63 -0
- package/dist/adapters/bluebubbles.d.ts.map +1 -0
- package/dist/adapters/bluebubbles.js +197 -0
- package/dist/adapters/bluebubbles.js.map +1 -0
- package/dist/adapters/discord.d.ts +27 -0
- package/dist/adapters/discord.d.ts.map +1 -0
- package/dist/adapters/discord.js +202 -0
- package/dist/adapters/discord.js.map +1 -0
- package/dist/adapters/email.d.ts +39 -0
- package/dist/adapters/email.d.ts.map +1 -0
- package/dist/adapters/email.js +359 -0
- package/dist/adapters/email.js.map +1 -0
- package/dist/adapters/googlechat.d.ts +77 -0
- package/dist/adapters/googlechat.d.ts.map +1 -0
- package/dist/adapters/googlechat.js +232 -0
- package/dist/adapters/googlechat.js.map +1 -0
- package/dist/adapters/matrix.d.ts +37 -0
- package/dist/adapters/matrix.d.ts.map +1 -0
- package/dist/adapters/matrix.js +262 -0
- package/dist/adapters/matrix.js.map +1 -0
- package/dist/adapters/signal.d.ts +32 -0
- package/dist/adapters/signal.d.ts.map +1 -0
- package/dist/adapters/signal.js +216 -0
- package/dist/adapters/signal.js.map +1 -0
- package/dist/adapters/slack.d.ts +29 -0
- package/dist/adapters/slack.d.ts.map +1 -0
- package/dist/adapters/slack.js +202 -0
- package/dist/adapters/slack.js.map +1 -0
- package/dist/adapters/teams.d.ts +66 -0
- package/dist/adapters/teams.d.ts.map +1 -0
- package/dist/adapters/teams.js +227 -0
- package/dist/adapters/teams.js.map +1 -0
- package/dist/adapters/telegram.d.ts +28 -0
- package/dist/adapters/telegram.d.ts.map +1 -0
- package/dist/adapters/telegram.js +170 -0
- package/dist/adapters/telegram.js.map +1 -0
- package/dist/adapters/twilio.d.ts +63 -0
- package/dist/adapters/twilio.d.ts.map +1 -0
- package/dist/adapters/twilio.js +193 -0
- package/dist/adapters/twilio.js.map +1 -0
- package/dist/adapters/whatsapp.d.ts +99 -0
- package/dist/adapters/whatsapp.d.ts.map +1 -0
- package/dist/adapters/whatsapp.js +218 -0
- package/dist/adapters/whatsapp.js.map +1 -0
- package/dist/adapters/zalo.d.ts +64 -0
- package/dist/adapters/zalo.d.ts.map +1 -0
- package/dist/adapters/zalo.js +216 -0
- package/dist/adapters/zalo.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/manager.d.ts +35 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +127 -0
- package/dist/manager.js.map +1 -0
- package/dist/types.d.ts +71 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +32 -0
- package/src/adapters/bluebubbles.ts +294 -0
- package/src/adapters/discord.ts +253 -0
- package/src/adapters/email.ts +457 -0
- package/src/adapters/googlechat.ts +364 -0
- package/src/adapters/matrix.ts +376 -0
- package/src/adapters/signal.ts +313 -0
- package/src/adapters/slack.ts +252 -0
- package/src/adapters/teams.ts +320 -0
- package/src/adapters/telegram.ts +208 -0
- package/src/adapters/twilio.ts +256 -0
- package/src/adapters/whatsapp.ts +342 -0
- package/src/adapters/zalo.ts +319 -0
- package/src/index.ts +78 -0
- package/src/manager.ts +180 -0
- package/src/types.ts +84 -0
- package/tests/bluebubbles.test.ts +438 -0
- package/tests/email.test.ts +136 -0
- package/tests/googlechat.test.ts +439 -0
- package/tests/matrix.test.ts +564 -0
- package/tests/signal.test.ts +404 -0
- package/tests/slack.test.ts +343 -0
- package/tests/teams.test.ts +429 -0
- package/tests/twilio.test.ts +269 -0
- package/tests/whatsapp.test.ts +530 -0
- package/tests/zalo.test.ts +499 -0
- package/tsconfig.json +8 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { GoogleChatAdapter } from '../src/adapters/googlechat.js';
|
|
3
|
+
|
|
4
|
+
// Mock audit
|
|
5
|
+
vi.mock('@auxiora/audit', () => ({
|
|
6
|
+
audit: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
// Mock crypto.subtle for JWT signing
|
|
10
|
+
const mockCryptoKey = {} as CryptoKey;
|
|
11
|
+
const originalSubtle = globalThis.crypto?.subtle;
|
|
12
|
+
|
|
13
|
+
const TEST_SERVICE_ACCOUNT = JSON.stringify({
|
|
14
|
+
client_email: 'bot@project.iam.gserviceaccount.com',
|
|
15
|
+
private_key: '-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBg==\n-----END PRIVATE KEY-----',
|
|
16
|
+
token_uri: 'https://oauth2.googleapis.com/token',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('GoogleChatAdapter', () => {
|
|
20
|
+
let adapter: GoogleChatAdapter;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
adapter = new GoogleChatAdapter({
|
|
24
|
+
serviceAccountKey: TEST_SERVICE_ACCOUNT,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Mock crypto.subtle
|
|
28
|
+
Object.defineProperty(globalThis, 'crypto', {
|
|
29
|
+
value: {
|
|
30
|
+
subtle: {
|
|
31
|
+
importKey: vi.fn().mockResolvedValue(mockCryptoKey),
|
|
32
|
+
sign: vi.fn().mockResolvedValue(new ArrayBuffer(256)),
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
writable: true,
|
|
36
|
+
configurable: true,
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
vi.restoreAllMocks();
|
|
42
|
+
if (originalSubtle) {
|
|
43
|
+
Object.defineProperty(globalThis, 'crypto', {
|
|
44
|
+
value: { subtle: originalSubtle },
|
|
45
|
+
writable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should have correct metadata', () => {
|
|
52
|
+
expect(adapter.type).toBe('googlechat');
|
|
53
|
+
expect(adapter.name).toBe('Google Chat');
|
|
54
|
+
expect(adapter.isConnected()).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should connect successfully', async () => {
|
|
58
|
+
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
|
|
59
|
+
ok: true,
|
|
60
|
+
json: async () => ({
|
|
61
|
+
access_token: 'test-token',
|
|
62
|
+
expires_in: 3600,
|
|
63
|
+
token_type: 'Bearer',
|
|
64
|
+
}),
|
|
65
|
+
} as Response);
|
|
66
|
+
|
|
67
|
+
await adapter.connect();
|
|
68
|
+
expect(adapter.isConnected()).toBe(true);
|
|
69
|
+
|
|
70
|
+
await adapter.disconnect();
|
|
71
|
+
expect(adapter.isConnected()).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should fail to connect with invalid credentials', async () => {
|
|
75
|
+
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
|
|
76
|
+
ok: false,
|
|
77
|
+
status: 401,
|
|
78
|
+
statusText: 'Unauthorized',
|
|
79
|
+
} as Response);
|
|
80
|
+
|
|
81
|
+
await expect(adapter.connect()).rejects.toThrow('Failed to obtain Google Chat token');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should handle incoming MESSAGE event', async () => {
|
|
85
|
+
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
|
|
86
|
+
ok: true,
|
|
87
|
+
json: async () => ({
|
|
88
|
+
access_token: 'test-token',
|
|
89
|
+
expires_in: 3600,
|
|
90
|
+
token_type: 'Bearer',
|
|
91
|
+
}),
|
|
92
|
+
} as Response);
|
|
93
|
+
await adapter.connect();
|
|
94
|
+
|
|
95
|
+
const receivedMessages: unknown[] = [];
|
|
96
|
+
adapter.onMessage(async (msg) => {
|
|
97
|
+
receivedMessages.push(msg);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
await adapter.handleWebhook({
|
|
101
|
+
type: 'MESSAGE',
|
|
102
|
+
eventTime: '2024-01-01T00:00:00Z',
|
|
103
|
+
message: {
|
|
104
|
+
name: 'spaces/space-1/messages/msg-1',
|
|
105
|
+
sender: {
|
|
106
|
+
name: 'users/user-1',
|
|
107
|
+
displayName: 'Alice',
|
|
108
|
+
type: 'HUMAN',
|
|
109
|
+
},
|
|
110
|
+
createTime: '2024-01-01T00:00:00Z',
|
|
111
|
+
text: 'Hello Google Chat!',
|
|
112
|
+
space: {
|
|
113
|
+
name: 'spaces/space-1',
|
|
114
|
+
type: 'ROOM',
|
|
115
|
+
displayName: 'Test Space',
|
|
116
|
+
},
|
|
117
|
+
argumentText: 'Hello Google Chat!',
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
expect(receivedMessages).toHaveLength(1);
|
|
122
|
+
const msg = receivedMessages[0] as {
|
|
123
|
+
content: string;
|
|
124
|
+
senderId: string;
|
|
125
|
+
senderName: string;
|
|
126
|
+
channelId: string;
|
|
127
|
+
};
|
|
128
|
+
expect(msg.content).toBe('Hello Google Chat!');
|
|
129
|
+
expect(msg.senderId).toBe('users/user-1');
|
|
130
|
+
expect(msg.senderName).toBe('Alice');
|
|
131
|
+
expect(msg.channelId).toBe('spaces/space-1');
|
|
132
|
+
|
|
133
|
+
await adapter.disconnect();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should use argumentText to strip mentions', async () => {
|
|
137
|
+
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
|
|
138
|
+
ok: true,
|
|
139
|
+
json: async () => ({
|
|
140
|
+
access_token: 'test-token',
|
|
141
|
+
expires_in: 3600,
|
|
142
|
+
token_type: 'Bearer',
|
|
143
|
+
}),
|
|
144
|
+
} as Response);
|
|
145
|
+
await adapter.connect();
|
|
146
|
+
|
|
147
|
+
const receivedMessages: unknown[] = [];
|
|
148
|
+
adapter.onMessage(async (msg) => {
|
|
149
|
+
receivedMessages.push(msg);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
await adapter.handleWebhook({
|
|
153
|
+
type: 'MESSAGE',
|
|
154
|
+
eventTime: '2024-01-01T00:00:00Z',
|
|
155
|
+
message: {
|
|
156
|
+
name: 'spaces/space-1/messages/msg-2',
|
|
157
|
+
sender: {
|
|
158
|
+
name: 'users/user-1',
|
|
159
|
+
displayName: 'Alice',
|
|
160
|
+
type: 'HUMAN',
|
|
161
|
+
},
|
|
162
|
+
createTime: '2024-01-01T00:00:00Z',
|
|
163
|
+
text: '@Bot what is the weather?',
|
|
164
|
+
space: {
|
|
165
|
+
name: 'spaces/space-1',
|
|
166
|
+
type: 'ROOM',
|
|
167
|
+
},
|
|
168
|
+
argumentText: ' what is the weather?',
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
expect(receivedMessages).toHaveLength(1);
|
|
173
|
+
const msg = receivedMessages[0] as { content: string };
|
|
174
|
+
expect(msg.content).toBe('what is the weather?');
|
|
175
|
+
|
|
176
|
+
await adapter.disconnect();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should ignore non-MESSAGE events', async () => {
|
|
180
|
+
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
|
|
181
|
+
ok: true,
|
|
182
|
+
json: async () => ({
|
|
183
|
+
access_token: 'test-token',
|
|
184
|
+
expires_in: 3600,
|
|
185
|
+
token_type: 'Bearer',
|
|
186
|
+
}),
|
|
187
|
+
} as Response);
|
|
188
|
+
await adapter.connect();
|
|
189
|
+
|
|
190
|
+
const receivedMessages: unknown[] = [];
|
|
191
|
+
adapter.onMessage(async (msg) => {
|
|
192
|
+
receivedMessages.push(msg);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
await adapter.handleWebhook({
|
|
196
|
+
type: 'ADDED_TO_SPACE',
|
|
197
|
+
eventTime: '2024-01-01T00:00:00Z',
|
|
198
|
+
space: {
|
|
199
|
+
name: 'spaces/space-1',
|
|
200
|
+
type: 'ROOM',
|
|
201
|
+
},
|
|
202
|
+
user: {
|
|
203
|
+
name: 'users/user-1',
|
|
204
|
+
displayName: 'Alice',
|
|
205
|
+
type: 'HUMAN',
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
expect(receivedMessages).toHaveLength(0);
|
|
210
|
+
|
|
211
|
+
await adapter.disconnect();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should send a message successfully', async () => {
|
|
215
|
+
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
|
|
216
|
+
ok: true,
|
|
217
|
+
json: async () => ({
|
|
218
|
+
access_token: 'test-token',
|
|
219
|
+
expires_in: 3600,
|
|
220
|
+
token_type: 'Bearer',
|
|
221
|
+
}),
|
|
222
|
+
} as Response);
|
|
223
|
+
await adapter.connect();
|
|
224
|
+
|
|
225
|
+
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
|
|
226
|
+
ok: true,
|
|
227
|
+
json: async () => ({
|
|
228
|
+
name: 'spaces/space-1/messages/msg-sent-1',
|
|
229
|
+
sender: { name: 'users/bot', displayName: 'Bot' },
|
|
230
|
+
createTime: '2024-01-01T00:00:01Z',
|
|
231
|
+
text: 'Hello from bot!',
|
|
232
|
+
thread: { name: 'spaces/space-1/threads/thread-1' },
|
|
233
|
+
}),
|
|
234
|
+
} as Response);
|
|
235
|
+
|
|
236
|
+
const result = await adapter.send('spaces/space-1', {
|
|
237
|
+
content: 'Hello from bot!',
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
expect(result.success).toBe(true);
|
|
241
|
+
expect(result.messageId).toBe('spaces/space-1/messages/msg-sent-1');
|
|
242
|
+
|
|
243
|
+
await adapter.disconnect();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('should handle send errors', async () => {
|
|
247
|
+
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
|
|
248
|
+
ok: true,
|
|
249
|
+
json: async () => ({
|
|
250
|
+
access_token: 'test-token',
|
|
251
|
+
expires_in: 3600,
|
|
252
|
+
token_type: 'Bearer',
|
|
253
|
+
}),
|
|
254
|
+
} as Response);
|
|
255
|
+
await adapter.connect();
|
|
256
|
+
|
|
257
|
+
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
|
|
258
|
+
ok: false,
|
|
259
|
+
status: 403,
|
|
260
|
+
statusText: 'Forbidden',
|
|
261
|
+
text: async () => 'Bot not authorized',
|
|
262
|
+
} as unknown as Response);
|
|
263
|
+
|
|
264
|
+
const result = await adapter.send('spaces/space-1', {
|
|
265
|
+
content: 'Should fail',
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
expect(result.success).toBe(false);
|
|
269
|
+
expect(result.error).toContain('403');
|
|
270
|
+
|
|
271
|
+
await adapter.disconnect();
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('should cache access token', async () => {
|
|
275
|
+
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
|
|
276
|
+
ok: true,
|
|
277
|
+
json: async () => ({
|
|
278
|
+
access_token: 'test-token',
|
|
279
|
+
expires_in: 3600,
|
|
280
|
+
token_type: 'Bearer',
|
|
281
|
+
}),
|
|
282
|
+
} as Response);
|
|
283
|
+
await adapter.connect();
|
|
284
|
+
|
|
285
|
+
// Send two messages - should only call token endpoint once
|
|
286
|
+
fetchSpy.mockResolvedValueOnce({
|
|
287
|
+
ok: true,
|
|
288
|
+
json: async () => ({
|
|
289
|
+
name: 'spaces/space-1/messages/msg-1',
|
|
290
|
+
sender: { name: 'users/bot', displayName: 'Bot' },
|
|
291
|
+
createTime: '2024-01-01T00:00:01Z',
|
|
292
|
+
text: 'Message 1',
|
|
293
|
+
thread: { name: 'spaces/space-1/threads/t1' },
|
|
294
|
+
}),
|
|
295
|
+
} as Response);
|
|
296
|
+
await adapter.send('spaces/space-1', { content: 'Message 1' });
|
|
297
|
+
|
|
298
|
+
fetchSpy.mockResolvedValueOnce({
|
|
299
|
+
ok: true,
|
|
300
|
+
json: async () => ({
|
|
301
|
+
name: 'spaces/space-1/messages/msg-2',
|
|
302
|
+
sender: { name: 'users/bot', displayName: 'Bot' },
|
|
303
|
+
createTime: '2024-01-01T00:00:02Z',
|
|
304
|
+
text: 'Message 2',
|
|
305
|
+
thread: { name: 'spaces/space-1/threads/t1' },
|
|
306
|
+
}),
|
|
307
|
+
} as Response);
|
|
308
|
+
await adapter.send('spaces/space-1', { content: 'Message 2' });
|
|
309
|
+
|
|
310
|
+
// 1 token call (connect) + 2 send calls = 3 total
|
|
311
|
+
expect(fetchSpy).toHaveBeenCalledTimes(3);
|
|
312
|
+
|
|
313
|
+
await adapter.disconnect();
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('should register error handler', () => {
|
|
317
|
+
const handler = vi.fn();
|
|
318
|
+
adapter.onError(handler);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('should return noop for startTyping', async () => {
|
|
322
|
+
const cleanup = await adapter.startTyping('spaces/space-1');
|
|
323
|
+
expect(typeof cleanup).toBe('function');
|
|
324
|
+
cleanup();
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
describe('space filtering', () => {
|
|
328
|
+
it('should allow all messages when allowedSpaces is not set', async () => {
|
|
329
|
+
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
|
|
330
|
+
ok: true,
|
|
331
|
+
json: async () => ({
|
|
332
|
+
access_token: 'test-token',
|
|
333
|
+
expires_in: 3600,
|
|
334
|
+
token_type: 'Bearer',
|
|
335
|
+
}),
|
|
336
|
+
} as Response);
|
|
337
|
+
await adapter.connect();
|
|
338
|
+
|
|
339
|
+
const receivedMessages: unknown[] = [];
|
|
340
|
+
adapter.onMessage(async (msg) => {
|
|
341
|
+
receivedMessages.push(msg);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
await adapter.handleWebhook({
|
|
345
|
+
type: 'MESSAGE',
|
|
346
|
+
eventTime: '2024-01-01T00:00:00Z',
|
|
347
|
+
message: {
|
|
348
|
+
name: 'spaces/any-space/messages/msg-1',
|
|
349
|
+
sender: { name: 'users/user-1', displayName: 'Alice', type: 'HUMAN' },
|
|
350
|
+
createTime: '2024-01-01T00:00:00Z',
|
|
351
|
+
text: 'Hello!',
|
|
352
|
+
space: { name: 'spaces/any-space', type: 'ROOM' },
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
expect(receivedMessages).toHaveLength(1);
|
|
357
|
+
await adapter.disconnect();
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('should block messages from non-allowed spaces', async () => {
|
|
361
|
+
const { audit } = await import('@auxiora/audit');
|
|
362
|
+
const filteredAdapter = new GoogleChatAdapter({
|
|
363
|
+
serviceAccountKey: TEST_SERVICE_ACCOUNT,
|
|
364
|
+
allowedSpaces: ['spaces/allowed-space'],
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
|
|
368
|
+
ok: true,
|
|
369
|
+
json: async () => ({
|
|
370
|
+
access_token: 'test-token',
|
|
371
|
+
expires_in: 3600,
|
|
372
|
+
token_type: 'Bearer',
|
|
373
|
+
}),
|
|
374
|
+
} as Response);
|
|
375
|
+
await filteredAdapter.connect();
|
|
376
|
+
|
|
377
|
+
const receivedMessages: unknown[] = [];
|
|
378
|
+
filteredAdapter.onMessage(async (msg) => {
|
|
379
|
+
receivedMessages.push(msg);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
await filteredAdapter.handleWebhook({
|
|
383
|
+
type: 'MESSAGE',
|
|
384
|
+
eventTime: '2024-01-01T00:00:00Z',
|
|
385
|
+
message: {
|
|
386
|
+
name: 'spaces/blocked-space/messages/msg-1',
|
|
387
|
+
sender: { name: 'users/user-1', displayName: 'Alice', type: 'HUMAN' },
|
|
388
|
+
createTime: '2024-01-01T00:00:00Z',
|
|
389
|
+
text: 'Blocked!',
|
|
390
|
+
space: { name: 'spaces/blocked-space', type: 'ROOM' },
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
expect(receivedMessages).toHaveLength(0);
|
|
395
|
+
expect(audit).toHaveBeenCalledWith(
|
|
396
|
+
'message.filtered',
|
|
397
|
+
expect.objectContaining({
|
|
398
|
+
channelType: 'googlechat',
|
|
399
|
+
reason: 'space_not_allowed',
|
|
400
|
+
}),
|
|
401
|
+
);
|
|
402
|
+
await filteredAdapter.disconnect();
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it('should handle message handler errors gracefully', async () => {
|
|
407
|
+
vi.spyOn(globalThis, 'fetch').mockResolvedValueOnce({
|
|
408
|
+
ok: true,
|
|
409
|
+
json: async () => ({
|
|
410
|
+
access_token: 'test-token',
|
|
411
|
+
expires_in: 3600,
|
|
412
|
+
token_type: 'Bearer',
|
|
413
|
+
}),
|
|
414
|
+
} as Response);
|
|
415
|
+
await adapter.connect();
|
|
416
|
+
|
|
417
|
+
const errorHandler = vi.fn();
|
|
418
|
+
adapter.onError(errorHandler);
|
|
419
|
+
adapter.onMessage(async () => {
|
|
420
|
+
throw new Error('Handler error');
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
await adapter.handleWebhook({
|
|
424
|
+
type: 'MESSAGE',
|
|
425
|
+
eventTime: '2024-01-01T00:00:00Z',
|
|
426
|
+
message: {
|
|
427
|
+
name: 'spaces/space-1/messages/msg-err',
|
|
428
|
+
sender: { name: 'users/user-1', displayName: 'Alice', type: 'HUMAN' },
|
|
429
|
+
createTime: '2024-01-01T00:00:00Z',
|
|
430
|
+
text: 'Trigger error',
|
|
431
|
+
space: { name: 'spaces/space-1', type: 'ROOM' },
|
|
432
|
+
},
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
expect(errorHandler).toHaveBeenCalledWith(expect.any(Error));
|
|
436
|
+
|
|
437
|
+
await adapter.disconnect();
|
|
438
|
+
});
|
|
439
|
+
});
|