chz-telegram-bot 0.7.13 → 0.7.15
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/dtos/propertyProviderSets.d.ts +0 -1
- package/dist/dtos/propertyProviderSets.d.ts.map +1 -1
- package/dist/entities/actions/commandAction.d.ts +1 -1
- package/dist/entities/actions/commandAction.d.ts.map +1 -1
- package/dist/entities/actions/commandAction.js +2 -2
- package/dist/helpers/builders/commandActionBuilder.d.ts.map +1 -1
- package/dist/helpers/builders/commandActionBuilder.js +2 -3
- package/package.json +4 -1
- package/eslint.config.ts +0 -62
- package/src/builtin/helpAction.ts +0 -17
- package/src/dtos/chatHistoryMessage.ts +0 -22
- package/src/dtos/chatInfo.ts +0 -12
- package/src/dtos/commandTriggerCheckResult.ts +0 -40
- package/src/dtos/cooldownInfo.ts +0 -10
- package/src/dtos/incomingMessage.ts +0 -71
- package/src/dtos/incomingQuery.ts +0 -14
- package/src/dtos/messageInfo.ts +0 -15
- package/src/dtos/propertyProviderSets.ts +0 -21
- package/src/dtos/replyInfo.ts +0 -9
- package/src/dtos/responses/delay.ts +0 -28
- package/src/dtos/responses/imageMessage.ts +0 -41
- package/src/dtos/responses/inlineQueryResponse.ts +0 -26
- package/src/dtos/responses/reaction.ts +0 -30
- package/src/dtos/responses/textMessage.ts +0 -44
- package/src/dtos/responses/unpin.ts +0 -27
- package/src/dtos/responses/videoMessage.ts +0 -41
- package/src/dtos/userInfo.ts +0 -8
- package/src/entities/actions/commandAction.ts +0 -275
- package/src/entities/actions/inlineQueryAction.ts +0 -83
- package/src/entities/actions/replyCaptureAction.ts +0 -110
- package/src/entities/actions/scheduledAction.ts +0 -182
- package/src/entities/botInstance.ts +0 -92
- package/src/entities/cachedStateFactory.ts +0 -14
- package/src/entities/context/baseContext.ts +0 -111
- package/src/entities/context/chatContext.ts +0 -135
- package/src/entities/context/inlineQueryContext.ts +0 -63
- package/src/entities/context/messageContext.ts +0 -250
- package/src/entities/context/replyContext.ts +0 -260
- package/src/entities/states/actionStateBase.ts +0 -6
- package/src/entities/taskRecord.ts +0 -11
- package/src/helpers/builders/commandActionBuilder.ts +0 -214
- package/src/helpers/builders/inlineQueryActionBuilder.ts +0 -71
- package/src/helpers/builders/scheduledActionBuilder.ts +0 -143
- package/src/helpers/mapUtils.ts +0 -28
- package/src/helpers/noop.ts +0 -20
- package/src/helpers/objectFromEntries.ts +0 -7
- package/src/helpers/timeConvertions.ts +0 -13
- package/src/helpers/toArray.ts +0 -3
- package/src/helpers/traceFactory.ts +0 -11
- package/src/index.ts +0 -33
- package/src/main.ts +0 -76
- package/src/services/actionProcessingService.ts +0 -125
- package/src/services/actionProcessors/baseProcessor.ts +0 -67
- package/src/services/actionProcessors/commandActionProcessor.ts +0 -231
- package/src/services/actionProcessors/inlineQueryActionProcessor.ts +0 -165
- package/src/services/actionProcessors/scheduledActionProcessor.ts +0 -136
- package/src/services/jsonFileStorage.ts +0 -181
- package/src/services/nodeTimeoutScheduler.ts +0 -79
- package/src/services/responseProcessingQueue.ts +0 -57
- package/src/services/telegramApi.ts +0 -278
- package/src/types/action.ts +0 -15
- package/src/types/actionState.ts +0 -4
- package/src/types/cachedValueAccessor.ts +0 -1
- package/src/types/capture.ts +0 -33
- package/src/types/commandCondition.ts +0 -9
- package/src/types/commandTrigger.ts +0 -1
- package/src/types/events.ts +0 -286
- package/src/types/externalAliases.ts +0 -18
- package/src/types/handlers.ts +0 -26
- package/src/types/inputFile.ts +0 -4
- package/src/types/messageSendingOptions.ts +0 -10
- package/src/types/messageTypes.ts +0 -21
- package/src/types/propertyProvider.ts +0 -14
- package/src/types/response.ts +0 -51
- package/src/types/scheduler.ts +0 -20
- package/src/types/storage.ts +0 -23
- package/src/types/timeValues.ts +0 -33
- package/src/types/trace.ts +0 -5
- package/tests/dtos/commandTriggerCheckResult.test.ts +0 -301
- package/tests/entities/actions/inlineQueryAction.test.ts +0 -359
- package/tests/entities/actions/replyCaptureAction.test.ts +0 -501
- package/tests/entities/cachedStateFactory.test.ts +0 -98
- package/tests/entities/context/chatContext.test.ts +0 -606
- package/tests/entities/context/messageContext.test.ts +0 -370
- package/tests/entities/states/actionStateBase.test.ts +0 -138
- package/tests/entities/taskRecord.test.ts +0 -195
- package/tests/helpers/mapUtils.test.ts +0 -163
- package/tests/helpers/timeConvertions.test.ts +0 -129
- package/tests/services/actionProcessors/baseActionProcessor.test.ts +0 -359
- package/tests/services/actionProcessors/commandActionProcessor.test.ts +0 -268
- package/tests/services/actionProcessors/inlineQueryActionProcessor.test.ts +0 -616
- package/tests/services/actionProcessors/processorTestHelpers.ts +0 -147
- package/tests/services/actionProcessors/scheduledActionProcessor.test.ts +0 -153
- package/tests/services/jsonFileStorage.test.ts +0 -927
- package/tests/services/nodeTimeoutScheduler.test.ts +0 -421
- package/tests/services/responseProcessingQueue.test.ts +0 -388
- package/tsconfig.build.json +0 -8
- package/tsconfig.json +0 -118
|
@@ -1,501 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, mock } from 'bun:test';
|
|
2
|
-
import { ReplyCaptureAction } from '../../../src/entities/actions/replyCaptureAction';
|
|
3
|
-
import { ReplyContextInternal } from '../../../src/entities/context/replyContext';
|
|
4
|
-
import { ActionKey, IAction } from '../../../src/types/action';
|
|
5
|
-
import { TypedEventEmitter, BotEventType } from '../../../src/types/events';
|
|
6
|
-
import { Noop } from '../../../src/helpers/noop';
|
|
7
|
-
import { ActionStateBase } from '../../../src/entities/states/actionStateBase';
|
|
8
|
-
import { MessageType, MessageTypeValue } from '../../../src/types/messageTypes';
|
|
9
|
-
import { Message } from '@telegraf/types';
|
|
10
|
-
import {
|
|
11
|
-
createMockStorage,
|
|
12
|
-
createMockScheduler
|
|
13
|
-
} from '../../services/actionProcessors/processorTestHelpers';
|
|
14
|
-
import { IncomingMessage } from '../../../src/dtos/incomingMessage';
|
|
15
|
-
|
|
16
|
-
function createMockParentAction(): IAction {
|
|
17
|
-
return {
|
|
18
|
-
key: 'command:parent-action' as ActionKey,
|
|
19
|
-
exec: mock(() => Promise.resolve([]))
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function createMockReplyContext(
|
|
24
|
-
replyMessageId: number | undefined,
|
|
25
|
-
messageText: string,
|
|
26
|
-
messageType: MessageTypeValue = MessageType.Text
|
|
27
|
-
): ReplyContextInternal<ActionStateBase> {
|
|
28
|
-
const storage = createMockStorage();
|
|
29
|
-
const scheduler = createMockScheduler();
|
|
30
|
-
const eventEmitter = new TypedEventEmitter();
|
|
31
|
-
const action = new ReplyCaptureAction(
|
|
32
|
-
100,
|
|
33
|
-
createMockParentAction(),
|
|
34
|
-
mock(() => Promise.resolve()),
|
|
35
|
-
[messageText],
|
|
36
|
-
new AbortController()
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
// Create a TelegramMessage with the appropriate structure for the message type
|
|
40
|
-
let telegramMessage: Message;
|
|
41
|
-
|
|
42
|
-
if (messageType === MessageType.Photo) {
|
|
43
|
-
telegramMessage = {
|
|
44
|
-
message_id: 100,
|
|
45
|
-
date: Math.floor(Date.now() / 1000),
|
|
46
|
-
chat: { id: 12345, type: 'private' },
|
|
47
|
-
from: { id: 123, is_bot: false, first_name: 'TestUser' },
|
|
48
|
-
photo: [
|
|
49
|
-
{
|
|
50
|
-
file_id: 'test-file-id',
|
|
51
|
-
file_unique_id: 'test-unique-id',
|
|
52
|
-
width: 100,
|
|
53
|
-
height: 100
|
|
54
|
-
}
|
|
55
|
-
],
|
|
56
|
-
caption: messageText,
|
|
57
|
-
...(replyMessageId && {
|
|
58
|
-
reply_to_message: { message_id: replyMessageId }
|
|
59
|
-
})
|
|
60
|
-
} as Message;
|
|
61
|
-
} else {
|
|
62
|
-
// Default to Text message
|
|
63
|
-
telegramMessage = {
|
|
64
|
-
message_id: 100,
|
|
65
|
-
date: Math.floor(Date.now() / 1000),
|
|
66
|
-
chat: { id: 12345, type: 'private' },
|
|
67
|
-
from: { id: 123, is_bot: false, first_name: 'TestUser' },
|
|
68
|
-
text: messageText,
|
|
69
|
-
...(replyMessageId && {
|
|
70
|
-
reply_to_message: { message_id: replyMessageId }
|
|
71
|
-
})
|
|
72
|
-
} as Message;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const incomingMessage = new IncomingMessage(telegramMessage, 'TestBot', []);
|
|
76
|
-
|
|
77
|
-
const botInfo = {
|
|
78
|
-
id: 111,
|
|
79
|
-
is_bot: true,
|
|
80
|
-
first_name: 'Bot',
|
|
81
|
-
username: 'testbot',
|
|
82
|
-
can_join_groups: true,
|
|
83
|
-
can_read_all_group_messages: false,
|
|
84
|
-
supports_inline_queries: false,
|
|
85
|
-
can_connect_to_business: false
|
|
86
|
-
} as const;
|
|
87
|
-
|
|
88
|
-
const ctx = new ReplyContextInternal<ActionStateBase>(
|
|
89
|
-
storage,
|
|
90
|
-
scheduler,
|
|
91
|
-
eventEmitter,
|
|
92
|
-
action,
|
|
93
|
-
incomingMessage,
|
|
94
|
-
'TestBot',
|
|
95
|
-
botInfo
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
return ctx;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
describe('ReplyCaptureAction', () => {
|
|
102
|
-
describe('constructor', () => {
|
|
103
|
-
test('should set parentMessageId', () => {
|
|
104
|
-
const action = new ReplyCaptureAction(
|
|
105
|
-
123,
|
|
106
|
-
createMockParentAction(),
|
|
107
|
-
mock(() => Promise.resolve()),
|
|
108
|
-
['yes'],
|
|
109
|
-
new AbortController()
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
expect(action.parentMessageId).toBe(123);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
test('should set triggers', () => {
|
|
116
|
-
const triggers = ['yes', 'no', /maybe/];
|
|
117
|
-
const action = new ReplyCaptureAction(
|
|
118
|
-
123,
|
|
119
|
-
createMockParentAction(),
|
|
120
|
-
mock(() => Promise.resolve()),
|
|
121
|
-
triggers,
|
|
122
|
-
new AbortController()
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
expect(action.triggers).toBe(triggers);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test('should set handler', () => {
|
|
129
|
-
const handler = mock(() => Promise.resolve());
|
|
130
|
-
const action = new ReplyCaptureAction(
|
|
131
|
-
123,
|
|
132
|
-
createMockParentAction(),
|
|
133
|
-
handler,
|
|
134
|
-
['yes'],
|
|
135
|
-
new AbortController()
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
expect(action.handler).toBe(handler);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
test('should set abortController', () => {
|
|
142
|
-
const abortController = new AbortController();
|
|
143
|
-
const action = new ReplyCaptureAction(
|
|
144
|
-
123,
|
|
145
|
-
createMockParentAction(),
|
|
146
|
-
mock(() => Promise.resolve()),
|
|
147
|
-
['yes'],
|
|
148
|
-
abortController
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
expect(action.abortController).toBe(abortController);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
test('should generate key with capture prefix and parent action key', () => {
|
|
155
|
-
const parentAction = createMockParentAction();
|
|
156
|
-
const action = new ReplyCaptureAction(
|
|
157
|
-
123,
|
|
158
|
-
parentAction,
|
|
159
|
-
mock(() => Promise.resolve()),
|
|
160
|
-
['yes'],
|
|
161
|
-
new AbortController()
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
expect(
|
|
165
|
-
action.key.startsWith('capture:command:parent-action:')
|
|
166
|
-
).toBe(true);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
test('should generate unique keys for different instances', () => {
|
|
170
|
-
const parentAction = createMockParentAction();
|
|
171
|
-
|
|
172
|
-
const action1 = new ReplyCaptureAction(
|
|
173
|
-
123,
|
|
174
|
-
parentAction,
|
|
175
|
-
mock(() => Promise.resolve()),
|
|
176
|
-
['yes'],
|
|
177
|
-
new AbortController()
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
const action2 = new ReplyCaptureAction(
|
|
181
|
-
123,
|
|
182
|
-
parentAction,
|
|
183
|
-
mock(() => Promise.resolve()),
|
|
184
|
-
['yes'],
|
|
185
|
-
new AbortController()
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
expect(action1.key).not.toBe(action2.key);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
describe('exec', () => {
|
|
193
|
-
test('should throw if context is not initialized', () => {
|
|
194
|
-
// Note: isInitialized property no longer exists as context is always initialized
|
|
195
|
-
// with all required properties at construction time
|
|
196
|
-
// This test case is no longer applicable
|
|
197
|
-
const ctx = createMockReplyContext(123, 'yes');
|
|
198
|
-
|
|
199
|
-
// Verify context is properly initialized
|
|
200
|
-
expect(ctx.action).toBeDefined();
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
test('should return NoResponse if reply is not to parent message', async () => {
|
|
204
|
-
const action = new ReplyCaptureAction(
|
|
205
|
-
123,
|
|
206
|
-
createMockParentAction(),
|
|
207
|
-
mock(() => Promise.resolve()),
|
|
208
|
-
['yes'],
|
|
209
|
-
new AbortController()
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
const ctx = createMockReplyContext(999, 'yes'); // Different message id
|
|
213
|
-
const result = await action.exec(ctx);
|
|
214
|
-
|
|
215
|
-
expect(result).toBe(Noop.NoResponse);
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
test('should return NoResponse if reply message id is undefined', async () => {
|
|
219
|
-
const action = new ReplyCaptureAction(
|
|
220
|
-
123,
|
|
221
|
-
createMockParentAction(),
|
|
222
|
-
mock(() => Promise.resolve()),
|
|
223
|
-
['yes'],
|
|
224
|
-
new AbortController()
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
const ctx = createMockReplyContext(undefined, 'yes');
|
|
228
|
-
const result = await action.exec(ctx);
|
|
229
|
-
|
|
230
|
-
expect(result).toBe(Noop.NoResponse);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
test('should return NoResponse if no trigger matches', async () => {
|
|
234
|
-
const handler = mock(() => Promise.resolve());
|
|
235
|
-
const action = new ReplyCaptureAction(
|
|
236
|
-
123,
|
|
237
|
-
createMockParentAction(),
|
|
238
|
-
handler,
|
|
239
|
-
['yes', 'no'],
|
|
240
|
-
new AbortController()
|
|
241
|
-
);
|
|
242
|
-
|
|
243
|
-
const ctx = createMockReplyContext(123, 'maybe');
|
|
244
|
-
const result = await action.exec(ctx);
|
|
245
|
-
|
|
246
|
-
expect(result).toBe(Noop.NoResponse);
|
|
247
|
-
expect(handler).not.toHaveBeenCalled();
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
test('should execute handler when string trigger matches (case-insensitive)', async () => {
|
|
251
|
-
const handler = mock(() => Promise.resolve());
|
|
252
|
-
const action = new ReplyCaptureAction(
|
|
253
|
-
123,
|
|
254
|
-
createMockParentAction(),
|
|
255
|
-
handler,
|
|
256
|
-
['YES'],
|
|
257
|
-
new AbortController()
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
const ctx = createMockReplyContext(123, 'yes');
|
|
261
|
-
await action.exec(ctx);
|
|
262
|
-
|
|
263
|
-
expect(handler).toHaveBeenCalledTimes(1);
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
test('should execute handler when regex trigger matches', async () => {
|
|
267
|
-
const handler = mock(() => Promise.resolve());
|
|
268
|
-
const action = new ReplyCaptureAction(
|
|
269
|
-
123,
|
|
270
|
-
createMockParentAction(),
|
|
271
|
-
handler,
|
|
272
|
-
[/\d+/],
|
|
273
|
-
new AbortController()
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
const ctx = createMockReplyContext(123, 'order 42');
|
|
277
|
-
await action.exec(ctx);
|
|
278
|
-
|
|
279
|
-
expect(handler).toHaveBeenCalledTimes(1);
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
test('should execute handler when MessageType trigger matches', async () => {
|
|
283
|
-
const handler = mock(() => Promise.resolve());
|
|
284
|
-
const action = new ReplyCaptureAction(
|
|
285
|
-
123,
|
|
286
|
-
createMockParentAction(),
|
|
287
|
-
handler,
|
|
288
|
-
[MessageType.Photo],
|
|
289
|
-
new AbortController()
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
const ctx = createMockReplyContext(123, '', MessageType.Photo);
|
|
293
|
-
await action.exec(ctx);
|
|
294
|
-
|
|
295
|
-
expect(handler).toHaveBeenCalledTimes(1);
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
test('should set matchResults on context for regex trigger', async () => {
|
|
299
|
-
const action = new ReplyCaptureAction(
|
|
300
|
-
123,
|
|
301
|
-
createMockParentAction(),
|
|
302
|
-
mock(() => Promise.resolve()),
|
|
303
|
-
[/order (\d+)/],
|
|
304
|
-
new AbortController()
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
const ctx = createMockReplyContext(123, 'order 42');
|
|
308
|
-
await action.exec(ctx);
|
|
309
|
-
|
|
310
|
-
expect(ctx.matchResults.length).toBe(1);
|
|
311
|
-
expect(ctx.matchResults[0][0]).toBe('order 42');
|
|
312
|
-
expect(ctx.matchResults[0][1]).toBe('42');
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
test('should handle global regex with multiple matches', async () => {
|
|
316
|
-
const action = new ReplyCaptureAction(
|
|
317
|
-
123,
|
|
318
|
-
createMockParentAction(),
|
|
319
|
-
mock(() => Promise.resolve()),
|
|
320
|
-
[/\d+/g],
|
|
321
|
-
new AbortController()
|
|
322
|
-
);
|
|
323
|
-
|
|
324
|
-
const ctx = createMockReplyContext(123, '1 2 3 4 5');
|
|
325
|
-
await action.exec(ctx);
|
|
326
|
-
|
|
327
|
-
expect(ctx.matchResults.length).toBe(5);
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
test('should emit replyActionExecuting event before handler', async () => {
|
|
331
|
-
const events: string[] = [];
|
|
332
|
-
const handler = mock(() => {
|
|
333
|
-
events.push('handler');
|
|
334
|
-
return Promise.resolve();
|
|
335
|
-
});
|
|
336
|
-
const action = new ReplyCaptureAction(
|
|
337
|
-
123,
|
|
338
|
-
createMockParentAction(),
|
|
339
|
-
handler,
|
|
340
|
-
['yes'],
|
|
341
|
-
new AbortController()
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
const ctx = createMockReplyContext(123, 'yes');
|
|
345
|
-
ctx.observability.eventEmitter.on(
|
|
346
|
-
BotEventType.replyActionExecuting,
|
|
347
|
-
() => {
|
|
348
|
-
events.push('executing');
|
|
349
|
-
}
|
|
350
|
-
);
|
|
351
|
-
|
|
352
|
-
await action.exec(ctx);
|
|
353
|
-
|
|
354
|
-
expect(events).toEqual(['executing', 'handler']);
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
test('should emit replyActionExecuted event after handler', async () => {
|
|
358
|
-
const events: string[] = [];
|
|
359
|
-
const handler = mock(() => {
|
|
360
|
-
events.push('handler');
|
|
361
|
-
return Promise.resolve();
|
|
362
|
-
});
|
|
363
|
-
const action = new ReplyCaptureAction(
|
|
364
|
-
123,
|
|
365
|
-
createMockParentAction(),
|
|
366
|
-
handler,
|
|
367
|
-
['yes'],
|
|
368
|
-
new AbortController()
|
|
369
|
-
);
|
|
370
|
-
|
|
371
|
-
const ctx = createMockReplyContext(123, 'yes');
|
|
372
|
-
ctx.observability.eventEmitter.on(
|
|
373
|
-
BotEventType.replyActionExecuted,
|
|
374
|
-
() => {
|
|
375
|
-
events.push('executed');
|
|
376
|
-
}
|
|
377
|
-
);
|
|
378
|
-
|
|
379
|
-
await action.exec(ctx);
|
|
380
|
-
|
|
381
|
-
expect(events).toEqual(['handler', 'executed']);
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
test('should return context responses after execution', async () => {
|
|
385
|
-
const action = new ReplyCaptureAction(
|
|
386
|
-
123,
|
|
387
|
-
createMockParentAction(),
|
|
388
|
-
mock(() => Promise.resolve()),
|
|
389
|
-
['yes'],
|
|
390
|
-
new AbortController()
|
|
391
|
-
);
|
|
392
|
-
|
|
393
|
-
const ctx = createMockReplyContext(123, 'yes');
|
|
394
|
-
const result = await action.exec(ctx);
|
|
395
|
-
|
|
396
|
-
expect(result).toBe(ctx.responses);
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
test('should try all triggers until one matches', async () => {
|
|
400
|
-
const handler = mock(() => Promise.resolve());
|
|
401
|
-
const action = new ReplyCaptureAction(
|
|
402
|
-
123,
|
|
403
|
-
createMockParentAction(),
|
|
404
|
-
handler,
|
|
405
|
-
['no', 'maybe', 'yes'],
|
|
406
|
-
new AbortController()
|
|
407
|
-
);
|
|
408
|
-
|
|
409
|
-
const ctx = createMockReplyContext(123, 'yes');
|
|
410
|
-
await action.exec(ctx);
|
|
411
|
-
|
|
412
|
-
expect(handler).toHaveBeenCalledTimes(1);
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
test('should use mergeWith to combine trigger results', async () => {
|
|
416
|
-
// Test that multiple matching triggers get their results merged
|
|
417
|
-
const action = new ReplyCaptureAction(
|
|
418
|
-
123,
|
|
419
|
-
createMockParentAction(),
|
|
420
|
-
mock(() => Promise.resolve()),
|
|
421
|
-
[/(\w+)/, /(\d+)/],
|
|
422
|
-
new AbortController()
|
|
423
|
-
);
|
|
424
|
-
|
|
425
|
-
const ctx = createMockReplyContext(123, 'test 123');
|
|
426
|
-
await action.exec(ctx);
|
|
427
|
-
|
|
428
|
-
// Both regexes should match and results should be merged
|
|
429
|
-
expect(ctx.matchResults.length).toBeGreaterThanOrEqual(1);
|
|
430
|
-
});
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
describe('trigger matching', () => {
|
|
434
|
-
test('should match string trigger exactly (case-insensitive)', async () => {
|
|
435
|
-
const handler = mock(() => Promise.resolve());
|
|
436
|
-
const action = new ReplyCaptureAction(
|
|
437
|
-
123,
|
|
438
|
-
createMockParentAction(),
|
|
439
|
-
handler,
|
|
440
|
-
['Hello World'],
|
|
441
|
-
new AbortController()
|
|
442
|
-
);
|
|
443
|
-
|
|
444
|
-
const ctx = createMockReplyContext(123, 'hello world');
|
|
445
|
-
await action.exec(ctx);
|
|
446
|
-
|
|
447
|
-
expect(handler).toHaveBeenCalled();
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
test('should not match partial string trigger', async () => {
|
|
451
|
-
const handler = mock(() => Promise.resolve());
|
|
452
|
-
const action = new ReplyCaptureAction(
|
|
453
|
-
123,
|
|
454
|
-
createMockParentAction(),
|
|
455
|
-
handler,
|
|
456
|
-
['yes'],
|
|
457
|
-
new AbortController()
|
|
458
|
-
);
|
|
459
|
-
|
|
460
|
-
const ctx = createMockReplyContext(123, 'yes please');
|
|
461
|
-
await action.exec(ctx);
|
|
462
|
-
|
|
463
|
-
expect(handler).not.toHaveBeenCalled();
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
test('should reset regex lastIndex before matching', async () => {
|
|
467
|
-
const pattern = /test/g;
|
|
468
|
-
pattern.lastIndex = 100;
|
|
469
|
-
|
|
470
|
-
const handler = mock(() => Promise.resolve());
|
|
471
|
-
const action = new ReplyCaptureAction(
|
|
472
|
-
123,
|
|
473
|
-
createMockParentAction(),
|
|
474
|
-
handler,
|
|
475
|
-
[pattern],
|
|
476
|
-
new AbortController()
|
|
477
|
-
);
|
|
478
|
-
|
|
479
|
-
const ctx = createMockReplyContext(123, 'test message');
|
|
480
|
-
await action.exec(ctx);
|
|
481
|
-
|
|
482
|
-
expect(handler).toHaveBeenCalled();
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
test('should limit regex matches to 100', async () => {
|
|
486
|
-
const longText = 'a '.repeat(150);
|
|
487
|
-
const action = new ReplyCaptureAction(
|
|
488
|
-
123,
|
|
489
|
-
createMockParentAction(),
|
|
490
|
-
mock(() => Promise.resolve()),
|
|
491
|
-
[/a/g],
|
|
492
|
-
new AbortController()
|
|
493
|
-
);
|
|
494
|
-
|
|
495
|
-
const ctx = createMockReplyContext(123, longText);
|
|
496
|
-
await action.exec(ctx);
|
|
497
|
-
|
|
498
|
-
expect(ctx.matchResults.length).toBeLessThanOrEqual(101);
|
|
499
|
-
});
|
|
500
|
-
});
|
|
501
|
-
});
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, mock } from 'bun:test';
|
|
2
|
-
import { CachedStateFactory } from '../../src/entities/cachedStateFactory';
|
|
3
|
-
import { Hours } from '../../src/types/timeValues';
|
|
4
|
-
|
|
5
|
-
describe('CachedStateFactory', () => {
|
|
6
|
-
describe('constructor', () => {
|
|
7
|
-
test('should store the getValue function', () => {
|
|
8
|
-
const factory = mock(() => Promise.resolve('test-value'));
|
|
9
|
-
const timeout = 1 as Hours;
|
|
10
|
-
|
|
11
|
-
const cached = new CachedStateFactory(factory, timeout);
|
|
12
|
-
|
|
13
|
-
expect(cached.getValue).toBe(factory);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test('should store the invalidationTimeoutInHours', () => {
|
|
17
|
-
const factory = mock(() => Promise.resolve('test-value'));
|
|
18
|
-
const timeout = 24 as Hours;
|
|
19
|
-
|
|
20
|
-
const cached = new CachedStateFactory(factory, timeout);
|
|
21
|
-
|
|
22
|
-
expect(cached.invalidationTimeoutInHours).toBe(timeout);
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
describe('getValue', () => {
|
|
27
|
-
test('should call the provided factory function', async () => {
|
|
28
|
-
const factory = mock(() => Promise.resolve('fetched-data'));
|
|
29
|
-
const cached = new CachedStateFactory(factory, 1 as Hours);
|
|
30
|
-
|
|
31
|
-
await cached.getValue();
|
|
32
|
-
|
|
33
|
-
expect(factory).toHaveBeenCalledTimes(1);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('should return the value from factory', async () => {
|
|
37
|
-
const expectedData = { key: 'value', count: 42 };
|
|
38
|
-
const factory = mock(() => Promise.resolve(expectedData));
|
|
39
|
-
const cached = new CachedStateFactory(factory, 1 as Hours);
|
|
40
|
-
|
|
41
|
-
const result = await cached.getValue();
|
|
42
|
-
|
|
43
|
-
expect(result).toEqual(expectedData);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test('should return different values on multiple calls if factory returns different values', async () => {
|
|
47
|
-
let callCount = 0;
|
|
48
|
-
const factory = mock(() => Promise.resolve(`call-${++callCount}`));
|
|
49
|
-
const cached = new CachedStateFactory(factory, 1 as Hours);
|
|
50
|
-
|
|
51
|
-
const result1 = await cached.getValue();
|
|
52
|
-
const result2 = await cached.getValue();
|
|
53
|
-
|
|
54
|
-
expect(result1).toBe('call-1');
|
|
55
|
-
expect(result2).toBe('call-2');
|
|
56
|
-
expect(factory).toHaveBeenCalledTimes(2);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test('should propagate errors from factory', async () => {
|
|
60
|
-
const factory = mock(() =>
|
|
61
|
-
Promise.reject(new Error('Factory error'))
|
|
62
|
-
);
|
|
63
|
-
const cached = new CachedStateFactory(factory, 1 as Hours);
|
|
64
|
-
|
|
65
|
-
let caught = false;
|
|
66
|
-
try {
|
|
67
|
-
await cached.getValue();
|
|
68
|
-
} catch (error) {
|
|
69
|
-
caught = true;
|
|
70
|
-
expect((error as Error).message).toBe('Factory error');
|
|
71
|
-
}
|
|
72
|
-
expect(caught).toBe(true);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
describe('invalidationTimeoutInHours', () => {
|
|
77
|
-
test('should be readonly', () => {
|
|
78
|
-
const timeout = 12 as Hours;
|
|
79
|
-
const cached = new CachedStateFactory(
|
|
80
|
-
() => Promise.resolve(null),
|
|
81
|
-
timeout
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
// TypeScript enforces readonly, but we can verify it exists
|
|
85
|
-
expect(cached.invalidationTimeoutInHours).toBe(timeout);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test('should preserve Hours type value', () => {
|
|
89
|
-
const timeout = 48 as Hours;
|
|
90
|
-
const cached = new CachedStateFactory(
|
|
91
|
-
() => Promise.resolve(null),
|
|
92
|
-
timeout
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
expect(cached.invalidationTimeoutInHours).toBe(timeout);
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
});
|