@ihazz/bitrix24 1.0.3 → 1.1.1

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.
@@ -4,12 +4,14 @@ import type { IncomingMessage, ServerResponse } from 'node:http';
4
4
  import {
5
5
  __setGatewayStateForTests,
6
6
  buildBotCodeCandidates,
7
+ buildConversationSessionKey,
7
8
  canCoalesceDirectMessage,
8
9
  convertButtonsToKeyboard,
9
10
  extractKeyboardFromPayload,
10
11
  handleWebhookRequest,
11
12
  mergeBufferedDirectMessages,
12
13
  mergeForwardedMessageContext,
14
+ resolveConversationRef,
13
15
  resolveDirectMessageCoalesceDelay,
14
16
  shouldSkipJoinChatWelcome,
15
17
  bitrix24Plugin,
@@ -72,6 +74,19 @@ describe('convertButtonsToKeyboard', () => {
72
74
  }
73
75
  });
74
76
 
77
+ it('converts a registered command without leading slash', () => {
78
+ const kb = convertButtonsToKeyboard([
79
+ [{ text: 'Help', callback_data: 'help' }],
80
+ ]);
81
+
82
+ const btn = kb[0];
83
+ expect(isButton(btn)).toBe(true);
84
+ if (isButton(btn)) {
85
+ expect(btn.COMMAND).toBe('help');
86
+ expect(btn.COMMAND_PARAMS).toBeUndefined();
87
+ }
88
+ });
89
+
75
90
  it('converts slash command with params', () => {
76
91
  const kb = convertButtonsToKeyboard([
77
92
  [{ text: 'Set Model', callback_data: '/model gpt-4o' }],
@@ -85,6 +100,18 @@ describe('convertButtonsToKeyboard', () => {
85
100
  }
86
101
  });
87
102
 
103
+ it('converts a registered command with params without leading slash', () => {
104
+ const kb = convertButtonsToKeyboard([
105
+ [{ text: 'Set Model', callback_data: 'model gpt-4o' }],
106
+ ]);
107
+
108
+ const btn = kb[0];
109
+ if (isButton(btn)) {
110
+ expect(btn.COMMAND).toBe('model');
111
+ expect(btn.COMMAND_PARAMS).toBe('gpt-4o');
112
+ }
113
+ });
114
+
88
115
  it('converts slash command with multiple params', () => {
89
116
  const kb = convertButtonsToKeyboard([
90
117
  [{ text: 'Think', callback_data: '/think high extra' }],
@@ -359,14 +386,16 @@ describe('direct message coalescing helpers', () => {
359
386
  ...baseMsgCtx,
360
387
  messageId: '103',
361
388
  text: 'Forwarded body',
389
+ replyToMessageId: '77',
362
390
  media: [{ id: '1', name: 'photo.jpg', extension: '', size: 42, type: 'file' }],
363
391
  isForwarded: true,
364
392
  },
365
393
  );
366
394
 
367
- expect(merged.text).toBe('hello\n\n[Forwarded message]\nForwarded body');
395
+ expect(merged.text).toBe('[User question about the forwarded message]\n\nhello\n\n[/User question]\n\nForwarded body');
368
396
  expect(merged.messageId).toBe('103');
369
397
  expect(merged.media).toHaveLength(1);
398
+ expect(merged.replyToMessageId).toBeUndefined();
370
399
  expect(merged.isForwarded).toBe(false);
371
400
  });
372
401
 
@@ -393,6 +422,45 @@ describe('direct message coalescing helpers', () => {
393
422
  });
394
423
  });
395
424
 
425
+ describe('conversation helpers', () => {
426
+ it('uses direct dialog id as the stable conversation identity', () => {
427
+ const conversation = resolveConversationRef({
428
+ accountId: 'default',
429
+ dialogId: '2386',
430
+ isDirect: true,
431
+ });
432
+
433
+ expect(conversation.dialogId).toBe('2386');
434
+ expect(conversation.address).toBe('bitrix24:2386');
435
+ expect(conversation.historyKey).toBe('default:2386');
436
+ expect(conversation.peer).toEqual({ kind: 'direct', id: '2386' });
437
+ });
438
+
439
+ it('uses group dialog id as the stable conversation identity', () => {
440
+ const conversation = resolveConversationRef({
441
+ accountId: 'default',
442
+ dialogId: 'chat520',
443
+ isDirect: false,
444
+ });
445
+
446
+ expect(conversation.dialogId).toBe('chat520');
447
+ expect(conversation.address).toBe('bitrix24:chat520');
448
+ expect(conversation.historyKey).toBe('default:chat520');
449
+ expect(conversation.peer).toEqual({ kind: 'group', id: 'chat520' });
450
+ });
451
+
452
+ it('builds session keys from the stable conversation address', () => {
453
+ const conversation = resolveConversationRef({
454
+ accountId: 'default',
455
+ dialogId: '2386',
456
+ isDirect: true,
457
+ });
458
+
459
+ expect(buildConversationSessionKey('route:session', conversation))
460
+ .toBe('route:session:bitrix24:2386');
461
+ });
462
+ });
463
+
396
464
  describe('join chat helpers', () => {
397
465
  it('does not skip unsupported group chats even in webhookUser mode', () => {
398
466
  expect(shouldSkipJoinChatWelcome({
@@ -554,7 +622,7 @@ describe('bitrix24Plugin', () => {
554
622
  });
555
623
 
556
624
  it('declares capabilities', () => {
557
- expect(bitrix24Plugin.capabilities.chatTypes).toEqual(['direct']);
625
+ expect(bitrix24Plugin.capabilities.chatTypes).toEqual(['direct', 'group']);
558
626
  expect(bitrix24Plugin.capabilities.media).toBe(true);
559
627
  expect(bitrix24Plugin.capabilities.reactions).toBe(true);
560
628
  expect(bitrix24Plugin.capabilities.threads).toBe(false);
@@ -574,6 +642,24 @@ describe('bitrix24Plugin', () => {
574
642
  expect(result.approveHint).toContain('openclaw pairing approve bitrix24');
575
643
  });
576
644
 
645
+ it('resolveDmPolicy returns allowlist policy when configured', () => {
646
+ const result = bitrix24Plugin.security.resolveDmPolicy({
647
+ account: { config: { dmPolicy: 'allowlist', allowFrom: ['bitrix24:42'] } },
648
+ });
649
+
650
+ expect(result.policy).toBe('allowlist');
651
+ expect(result.allowFrom).toEqual(['42']);
652
+ });
653
+
654
+ it('resolveDmPolicy returns open policy when configured', () => {
655
+ const result = bitrix24Plugin.security.resolveDmPolicy({
656
+ account: { config: { dmPolicy: 'open' } },
657
+ });
658
+
659
+ expect(result.policy).toBe('open');
660
+ expect(result.allowFrom).toEqual([]);
661
+ });
662
+
577
663
  it('resolveDmPolicy defaults to webhookUser', () => {
578
664
  const result = bitrix24Plugin.security.resolveDmPolicy({});
579
665
  expect(result.policy).toBe('webhookUser');
@@ -1,5 +1,6 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { getConfig, isConfigured, listAccountIds, resolveAccount } from '../src/config.js';
3
+ import { Bitrix24ConfigSchema } from '../src/config-schema.js';
3
4
 
4
5
  const mockCfg = {
5
6
  channels: {
@@ -24,6 +25,7 @@ describe('getConfig', () => {
24
25
  const cfg = getConfig(mockCfg);
25
26
  expect(cfg.webhookUrl).toBe('https://test.bitrix24.com/rest/1/abc123/');
26
27
  expect(cfg.botName).toBe('TestBot');
28
+ expect(cfg.dmPolicy).toBe('pairing');
27
29
  expect(cfg.allowFrom).toEqual(['bitrix24:1']);
28
30
  });
29
31
 
@@ -43,6 +45,41 @@ describe('getConfig', () => {
43
45
  const cfg = getConfig(mockCfg, 'nonexistent');
44
46
  expect(cfg.webhookUrl).toBe('https://test.bitrix24.com/rest/1/abc123/');
45
47
  });
48
+
49
+ it('keeps group-related config fields intact', () => {
50
+ const cfg = getConfig({
51
+ channels: {
52
+ bitrix24: {
53
+ webhookUrl: 'https://test.bitrix24.com/rest/1/abc123/',
54
+ groupPolicy: 'webhookUser',
55
+ groupAllowFrom: ['chat208', '615'],
56
+ requireMention: true,
57
+ historyLimit: 100,
58
+ groups: {
59
+ '*': { requireMention: true },
60
+ chat208: {
61
+ groupPolicy: 'open',
62
+ requireMention: false,
63
+ watch: [{ userId: '77', topics: ['секрет'] }],
64
+ },
65
+ },
66
+ },
67
+ },
68
+ });
69
+
70
+ expect(cfg.groupPolicy).toBe('webhookUser');
71
+ expect(cfg.groupAllowFrom).toEqual(['chat208', '615']);
72
+ expect(cfg.requireMention).toBe(true);
73
+ expect(cfg.historyLimit).toBe(100);
74
+ expect(cfg.groups).toEqual({
75
+ '*': { requireMention: true },
76
+ chat208: {
77
+ groupPolicy: 'open',
78
+ requireMention: false,
79
+ watch: [{ userId: '77', topics: ['секрет'] }],
80
+ },
81
+ });
82
+ });
46
83
  });
47
84
 
48
85
  describe('isConfigured', () => {
@@ -87,4 +124,87 @@ describe('resolveAccount', () => {
87
124
  const acct = resolveAccount({}, 'missing');
88
125
  expect(acct.configured).toBe(false);
89
126
  });
127
+
128
+ it('accepts new group policy fields in account config', () => {
129
+ const acct = resolveAccount({
130
+ channels: {
131
+ bitrix24: {
132
+ webhookUrl: 'https://test.bitrix24.com/rest/1/abc123/',
133
+ dmPolicy: 'allowlist',
134
+ groupPolicy: 'pairing',
135
+ groupAllowFrom: ['chat208'],
136
+ requireMention: false,
137
+ historyLimit: 50,
138
+ groups: {
139
+ '208': { groupPolicy: 'open' },
140
+ },
141
+ },
142
+ },
143
+ });
144
+
145
+ expect(acct.configured).toBe(true);
146
+ expect(acct.config.groupPolicy).toBe('pairing');
147
+ expect(acct.config.groupAllowFrom).toEqual(['chat208']);
148
+ expect(acct.config.requireMention).toBe(false);
149
+ expect(acct.config.historyLimit).toBe(50);
150
+ expect(acct.config.groups).toEqual({
151
+ '208': { groupPolicy: 'open', requireMention: true },
152
+ });
153
+ });
154
+
155
+ it('accepts agent mode watch rules in account config', () => {
156
+ const acct = resolveAccount({
157
+ channels: {
158
+ bitrix24: {
159
+ webhookUrl: 'https://test.bitrix24.com/rest/1/abc123/',
160
+ agentMode: true,
161
+ agentWatch: {
162
+ '*': [
163
+ { userId: '*', topics: ['авария'], mode: 'notifyOwnerDm' },
164
+ ],
165
+ '77': [
166
+ { userId: '*', topics: ['срочно'] },
167
+ ],
168
+ },
169
+ },
170
+ },
171
+ });
172
+
173
+ expect(acct.configured).toBe(true);
174
+ expect(acct.config.agentMode).toBe(true);
175
+ expect(acct.config.agentWatch).toEqual({
176
+ '*': [
177
+ { userId: '*', topics: ['авария'], mode: 'notifyOwnerDm' },
178
+ ],
179
+ '77': [
180
+ { userId: '*', topics: ['срочно'] },
181
+ ],
182
+ });
183
+ });
184
+
185
+ it('rejects invalid group policy values', () => {
186
+ expect(() => resolveAccount({
187
+ channels: {
188
+ bitrix24: {
189
+ webhookUrl: 'https://test.bitrix24.com/rest/1/abc123/',
190
+ groupPolicy: 'invalid',
191
+ },
192
+ },
193
+ } as unknown as Record<string, unknown>)).toThrow('Invalid config');
194
+ });
195
+ });
196
+
197
+ describe('Bitrix24ConfigSchema', () => {
198
+ it('applies defaults for new access and history options', () => {
199
+ const parsed = Bitrix24ConfigSchema.parse({
200
+ webhookUrl: 'https://test.bitrix24.com/rest/1/abc123/',
201
+ });
202
+
203
+ expect(parsed.dmPolicy).toBe('webhookUser');
204
+ expect(parsed.groupPolicy).toBe('webhookUser');
205
+ expect(parsed.agentMode).toBe(false);
206
+ expect(parsed.requireMention).toBe(true);
207
+ expect(parsed.historyLimit).toBe(100);
208
+ expect(parsed.showTyping).toBe(true);
209
+ });
90
210
  });
@@ -0,0 +1,340 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import {
3
+ buildGroupMatchKeys,
4
+ checkGroupAccessPassive,
5
+ checkGroupAccessWithPairing,
6
+ normalizeGroupAllowList,
7
+ normalizeGroupEntry,
8
+ resolveGroupAccess,
9
+ } from '../src/group-access.js';
10
+ import type { PluginRuntime, ChannelPairingAdapter } from '../src/runtime.js';
11
+
12
+ function makeMockRuntime(storeAllowFrom: string[] = []): PluginRuntime {
13
+ return {
14
+ config: { loadConfig: () => ({}) },
15
+ channel: {
16
+ routing: { resolveAgentRoute: vi.fn() },
17
+ reply: {
18
+ finalizeInboundContext: vi.fn(),
19
+ dispatchReplyWithBufferedBlockDispatcher: vi.fn(),
20
+ },
21
+ session: { recordInboundSession: vi.fn() },
22
+ pairing: {
23
+ readAllowFromStore: vi.fn().mockResolvedValue(storeAllowFrom),
24
+ upsertPairingRequest: vi.fn().mockResolvedValue({ code: 'ABCD1234', created: true }),
25
+ buildPairingReply: vi.fn(),
26
+ },
27
+ },
28
+ logging: { getChildLogger: () => ({ info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() }) },
29
+ } as unknown as PluginRuntime;
30
+ }
31
+
32
+ const mockAdapter: ChannelPairingAdapter = {
33
+ idLabel: 'bitrix24UserId',
34
+ normalizeAllowEntry: (entry) => entry,
35
+ };
36
+
37
+ describe('group-access helpers', () => {
38
+ it('normalizes numeric and dialog group ids', () => {
39
+ expect(normalizeGroupEntry(' 208 ')).toBe('208');
40
+ expect(normalizeGroupEntry(' Chat208 ')).toBe('chat208');
41
+ });
42
+
43
+ it('normalizes and deduplicates group allowlist entries', () => {
44
+ expect(normalizeGroupAllowList(['208', 'chat208', '208'])).toEqual(['208', 'chat208']);
45
+ });
46
+
47
+ it('builds group match keys from dialogId and chatId', () => {
48
+ expect(buildGroupMatchKeys({ dialogId: 'chat208', chatId: '208' })).toEqual(['chat208', '208']);
49
+ });
50
+ });
51
+
52
+ describe('resolveGroupAccess', () => {
53
+ it('defaults to webhookUser and mention-required groups', () => {
54
+ expect(resolveGroupAccess({
55
+ config: {},
56
+ dialogId: 'chat208',
57
+ chatId: '208',
58
+ })).toMatchObject({
59
+ groupPolicy: 'webhookUser',
60
+ requireMention: true,
61
+ groupAllowed: true,
62
+ senderAllowFrom: [],
63
+ watch: [],
64
+ });
65
+ });
66
+
67
+ it('matches explicit group config before wildcard', () => {
68
+ const result = resolveGroupAccess({
69
+ config: {
70
+ groupPolicy: 'webhookUser',
71
+ groups: {
72
+ '*': { requireMention: true },
73
+ chat208: { groupPolicy: 'open', requireMention: false, allowFrom: ['42'] },
74
+ },
75
+ },
76
+ dialogId: 'chat208',
77
+ chatId: '208',
78
+ });
79
+
80
+ expect(result.groupPolicy).toBe('open');
81
+ expect(result.requireMention).toBe(false);
82
+ expect(result.senderAllowFrom).toEqual(['42']);
83
+ expect(result.matchedGroupKey).toBe('chat208');
84
+ });
85
+
86
+ it('resolves watch rules from matched group config', () => {
87
+ const result = resolveGroupAccess({
88
+ config: {
89
+ groups: {
90
+ chat208: {
91
+ watch: [
92
+ { userId: 'bitrix24:77', topics: ['секрет', 'важно'], mode: 'notifyOwnerDm' },
93
+ ],
94
+ },
95
+ },
96
+ },
97
+ dialogId: 'chat208',
98
+ chatId: '208',
99
+ });
100
+
101
+ expect(result.watch).toEqual([
102
+ { userId: '77', topics: ['секрет', 'важно'], mode: 'notifyOwnerDm' },
103
+ ]);
104
+ });
105
+
106
+ it('preserves wildcard watch sender ids for any-user matching', () => {
107
+ const result = resolveGroupAccess({
108
+ config: {
109
+ groups: {
110
+ chat208: {
111
+ watch: [
112
+ { userId: '*', topics: ['авария'] },
113
+ ],
114
+ },
115
+ },
116
+ },
117
+ dialogId: 'chat208',
118
+ chatId: '208',
119
+ });
120
+
121
+ expect(result.watch).toEqual([
122
+ { userId: '*', topics: ['авария'], mode: 'reply' },
123
+ ]);
124
+ });
125
+
126
+ it('merges explicit group watch rules before wildcard watch rules', () => {
127
+ const result = resolveGroupAccess({
128
+ config: {
129
+ groups: {
130
+ '*': {
131
+ watch: [
132
+ { userId: '*', topics: ['авария'], mode: 'notifyOwnerDm' },
133
+ ],
134
+ },
135
+ chat208: {
136
+ watch: [
137
+ { userId: '77', topics: ['секрет'] },
138
+ ],
139
+ },
140
+ },
141
+ },
142
+ dialogId: 'chat208',
143
+ chatId: '208',
144
+ });
145
+
146
+ expect(result.watch).toEqual([
147
+ { userId: '77', topics: ['секрет'], mode: 'reply' },
148
+ { userId: '*', topics: ['авария'], mode: 'notifyOwnerDm' },
149
+ ]);
150
+ });
151
+
152
+ it('matches numeric chat id after dialog id lookup misses', () => {
153
+ const result = resolveGroupAccess({
154
+ config: {
155
+ groups: {
156
+ '208': { groupPolicy: 'open', requireMention: false },
157
+ '*': { groupPolicy: 'webhookUser', requireMention: true },
158
+ },
159
+ },
160
+ dialogId: 'chat208',
161
+ chatId: '208',
162
+ });
163
+
164
+ expect(result.groupPolicy).toBe('open');
165
+ expect(result.requireMention).toBe(false);
166
+ expect(result.matchedGroupKey).toBe('208');
167
+ });
168
+
169
+ it('falls back to wildcard group config when no exact match exists', () => {
170
+ const result = resolveGroupAccess({
171
+ config: {
172
+ groups: {
173
+ '*': { groupPolicy: 'allowlist', requireMention: false, allowFrom: ['42'] },
174
+ },
175
+ },
176
+ dialogId: 'chat615',
177
+ chatId: '615',
178
+ });
179
+
180
+ expect(result.groupPolicy).toBe('allowlist');
181
+ expect(result.requireMention).toBe(false);
182
+ expect(result.senderAllowFrom).toEqual(['42']);
183
+ expect(result.matchedGroupKey).toBe('*');
184
+ });
185
+
186
+ it('treats explicit group override as allowed even when top-level group allowlist is present', () => {
187
+ const result = resolveGroupAccess({
188
+ config: {
189
+ groupAllowFrom: ['chat300'],
190
+ groups: {
191
+ chat208: { groupPolicy: 'open' },
192
+ },
193
+ },
194
+ dialogId: 'chat208',
195
+ chatId: '208',
196
+ });
197
+
198
+ expect(result.groupAllowed).toBe(true);
199
+ });
200
+ });
201
+
202
+ describe('checkGroupAccessWithPairing', () => {
203
+ it('reports pairing passively without creating a request', async () => {
204
+ const runtime = makeMockRuntime();
205
+ const result = await checkGroupAccessPassive({
206
+ senderId: '99',
207
+ dialogId: 'chat208',
208
+ chatId: '208',
209
+ config: { groupPolicy: 'pairing' },
210
+ runtime,
211
+ accountId: 'default',
212
+ });
213
+
214
+ expect(result).toBe('pairing');
215
+ expect(runtime.channel.pairing.upsertPairingRequest).not.toHaveBeenCalled();
216
+ });
217
+
218
+ it('allows any sender in open mode', async () => {
219
+ const runtime = makeMockRuntime();
220
+ const result = await checkGroupAccessWithPairing({
221
+ senderId: '77',
222
+ dialogId: 'chat208',
223
+ chatId: '208',
224
+ config: { groupPolicy: 'open' },
225
+ runtime,
226
+ accountId: 'default',
227
+ pairingAdapter: mockAdapter,
228
+ });
229
+
230
+ expect(result).toBe('allow');
231
+ });
232
+
233
+ it('allows only webhook owner in webhookUser mode', async () => {
234
+ const runtime = makeMockRuntime();
235
+ const result = await checkGroupAccessWithPairing({
236
+ senderId: '42',
237
+ dialogId: 'chat208',
238
+ chatId: '208',
239
+ config: {
240
+ groupPolicy: 'webhookUser',
241
+ webhookUrl: 'https://test.bitrix24.com/rest/42/token/',
242
+ },
243
+ runtime,
244
+ accountId: 'default',
245
+ pairingAdapter: mockAdapter,
246
+ });
247
+
248
+ expect(result).toBe('allow');
249
+ });
250
+
251
+ it('uses merged top-level and per-group allowFrom in allowlist mode', async () => {
252
+ const runtime = makeMockRuntime();
253
+ const allowed = await checkGroupAccessWithPairing({
254
+ senderId: '42',
255
+ dialogId: 'chat208',
256
+ chatId: '208',
257
+ config: {
258
+ groupPolicy: 'allowlist',
259
+ allowFrom: ['7'],
260
+ groups: {
261
+ chat208: { allowFrom: ['42'] },
262
+ },
263
+ },
264
+ runtime,
265
+ accountId: 'default',
266
+ pairingAdapter: mockAdapter,
267
+ });
268
+
269
+ const denied = await checkGroupAccessWithPairing({
270
+ senderId: '99',
271
+ dialogId: 'chat208',
272
+ chatId: '208',
273
+ config: {
274
+ groupPolicy: 'allowlist',
275
+ allowFrom: ['7'],
276
+ groups: {
277
+ chat208: { allowFrom: ['42'] },
278
+ },
279
+ },
280
+ runtime,
281
+ accountId: 'default',
282
+ pairingAdapter: mockAdapter,
283
+ });
284
+
285
+ expect(allowed).toBe('allow');
286
+ expect(denied).toBe('deny');
287
+ });
288
+
289
+ it('creates pairing request for unknown sender in group pairing mode', async () => {
290
+ const runtime = makeMockRuntime();
291
+ const result = await checkGroupAccessWithPairing({
292
+ senderId: '99',
293
+ dialogId: 'chat208',
294
+ chatId: '208',
295
+ config: { groupPolicy: 'pairing' },
296
+ runtime,
297
+ accountId: 'default',
298
+ pairingAdapter: mockAdapter,
299
+ });
300
+
301
+ expect(result).toBe('pairing');
302
+ expect(runtime.channel.pairing.upsertPairingRequest).toHaveBeenCalled();
303
+ });
304
+
305
+ it('denies groups outside configured group allowlist', async () => {
306
+ const runtime = makeMockRuntime();
307
+ const result = await checkGroupAccessWithPairing({
308
+ senderId: '42',
309
+ dialogId: 'chat208',
310
+ chatId: '208',
311
+ config: {
312
+ groupPolicy: 'open',
313
+ groupAllowFrom: ['chat300'],
314
+ },
315
+ runtime,
316
+ accountId: 'default',
317
+ pairingAdapter: mockAdapter,
318
+ });
319
+
320
+ expect(result).toBe('deny');
321
+ });
322
+
323
+ it('allows groups listed by numeric chat id in top-level group allowlist', async () => {
324
+ const runtime = makeMockRuntime();
325
+ const result = await checkGroupAccessWithPairing({
326
+ senderId: '42',
327
+ dialogId: 'chat208',
328
+ chatId: '208',
329
+ config: {
330
+ groupPolicy: 'open',
331
+ groupAllowFrom: ['208'],
332
+ },
333
+ runtime,
334
+ accountId: 'default',
335
+ pairingAdapter: mockAdapter,
336
+ });
337
+
338
+ expect(result).toBe('allow');
339
+ });
340
+ });