talon-agent 1.0.0 → 1.2.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 +21 -0
- package/README.md +1 -0
- package/package.json +15 -11
- package/prompts/dream.md +7 -3
- package/prompts/heartbeat.md +30 -0
- package/prompts/identity.md +1 -0
- package/prompts/teams.md +3 -0
- package/prompts/telegram.md +1 -0
- package/src/__tests__/chat-settings.test.ts +108 -2
- package/src/__tests__/cleanup-registry.test.ts +58 -0
- package/src/__tests__/config.test.ts +118 -52
- package/src/__tests__/cron-store-extended.test.ts +661 -0
- package/src/__tests__/cron-store.test.ts +145 -11
- package/src/__tests__/daily-log.test.ts +224 -13
- package/src/__tests__/dispatcher.test.ts +424 -23
- package/src/__tests__/dream.test.ts +1028 -0
- package/src/__tests__/errors-extended.test.ts +428 -0
- package/src/__tests__/errors.test.ts +95 -3
- package/src/__tests__/fuzz.test.ts +87 -15
- package/src/__tests__/gateway-actions.test.ts +1174 -433
- package/src/__tests__/gateway-http.test.ts +210 -19
- package/src/__tests__/gateway-retry.test.ts +359 -0
- package/src/__tests__/gateway-withRetry-extended.test.ts +343 -0
- package/src/__tests__/graph.test.ts +830 -0
- package/src/__tests__/handlers-stream.test.ts +208 -0
- package/src/__tests__/handlers.test.ts +2539 -70
- package/src/__tests__/heartbeat.test.ts +364 -0
- package/src/__tests__/history-extended.test.ts +775 -0
- package/src/__tests__/history-persistence.test.ts +74 -19
- package/src/__tests__/history.test.ts +113 -79
- package/src/__tests__/integration.test.ts +43 -8
- package/src/__tests__/log-init.test.ts +129 -0
- package/src/__tests__/log.test.ts +23 -5
- package/src/__tests__/media-index.test.ts +317 -35
- package/src/__tests__/plugin.test.ts +314 -0
- package/src/__tests__/prompt-builder-extended.test.ts +296 -0
- package/src/__tests__/prompt-builder.test.ts +44 -9
- package/src/__tests__/sessions.test.ts +258 -4
- package/src/__tests__/storage-save-errors.test.ts +342 -0
- package/src/__tests__/teams-frontend.test.ts +526 -31
- package/src/__tests__/telegram-formatting.test.ts +82 -0
- package/src/__tests__/terminal-commands.test.ts +208 -1
- package/src/__tests__/terminal-renderer.test.ts +223 -0
- package/src/__tests__/time.test.ts +107 -0
- package/src/__tests__/workspace-migrate.test.ts +256 -0
- package/src/__tests__/workspace.test.ts +63 -1
- package/src/backend/claude-sdk/tools.ts +64 -18
- package/src/bootstrap.ts +14 -14
- package/src/cli.ts +440 -125
- package/src/core/cron.ts +20 -5
- package/src/core/dispatcher.ts +27 -9
- package/src/core/dream.ts +79 -24
- package/src/core/errors.ts +12 -2
- package/src/core/gateway-actions.ts +182 -46
- package/src/core/gateway.ts +93 -41
- package/src/core/heartbeat.ts +515 -0
- package/src/core/plugin.ts +1 -1
- package/src/core/prompt-builder.ts +1 -4
- package/src/core/pulse.ts +4 -3
- package/src/frontend/teams/actions.ts +3 -1
- package/src/frontend/teams/formatting.ts +47 -8
- package/src/frontend/teams/graph.ts +35 -11
- package/src/frontend/teams/index.ts +155 -57
- package/src/frontend/teams/tools.ts +4 -6
- package/src/frontend/telegram/actions.ts +358 -82
- package/src/frontend/telegram/admin.ts +162 -72
- package/src/frontend/telegram/callbacks.ts +16 -10
- package/src/frontend/telegram/commands.ts +37 -21
- package/src/frontend/telegram/formatting.ts +2 -4
- package/src/frontend/telegram/handlers.ts +262 -66
- package/src/frontend/telegram/index.ts +39 -14
- package/src/frontend/telegram/middleware.ts +14 -4
- package/src/frontend/telegram/userbot.ts +16 -4
- package/src/frontend/terminal/renderer.ts +1 -4
- package/src/index.ts +28 -4
- package/src/storage/chat-settings.ts +32 -9
- package/src/storage/cron-store.ts +53 -11
- package/src/storage/daily-log.ts +72 -19
- package/src/storage/history.ts +39 -21
- package/src/storage/media-index.ts +37 -12
- package/src/storage/sessions.ts +3 -2
- package/src/util/cleanup-registry.ts +34 -0
- package/src/util/config.ts +85 -23
- package/src/util/log.ts +47 -17
- package/src/util/paths.ts +10 -0
- package/src/util/time.ts +29 -6
- package/src/util/watchdog.ts +5 -1
- package/src/util/workspace.ts +51 -10
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extended Gateway class tests — covers methods and branches not tested in
|
|
3
|
+
* gateway-context.test.ts or gateway-retry.test.ts.
|
|
4
|
+
*
|
|
5
|
+
* Specifically exercises:
|
|
6
|
+
* - isChatBusy() true / false states
|
|
7
|
+
* - incrementMessages() + getMessageCount() interaction
|
|
8
|
+
* - getActiveChats() as contexts are added and removed
|
|
9
|
+
* - setContext() refCount increment when called twice for the same chat
|
|
10
|
+
* - clearContext() with a numeric string chatId ("123" → parsed via Number())
|
|
11
|
+
* - clearContext() with a non-numeric string chatId (Teams-style: findContextByStringId path)
|
|
12
|
+
* - clearContext() refCount floor at 0 (Math.max guard)
|
|
13
|
+
* - clearContext() for an unknown stringId (no-op)
|
|
14
|
+
* - setContext() with a stringId persists the string for lookup
|
|
15
|
+
* - getPort() returns 0 before start() is called
|
|
16
|
+
* - stop() when server is null is a no-op (resolves immediately)
|
|
17
|
+
* - setFrontendHandler() stores the handler (observable via HTTP dispatch)
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
21
|
+
|
|
22
|
+
// ── Mocks (must precede all imports) ─────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
vi.mock("../util/log.js", () => ({
|
|
25
|
+
log: vi.fn(),
|
|
26
|
+
logError: vi.fn(),
|
|
27
|
+
logWarn: vi.fn(),
|
|
28
|
+
logDebug: vi.fn(),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
vi.mock("../core/gateway-actions.js", () => ({
|
|
32
|
+
handleSharedAction: vi.fn(async () => null),
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
vi.mock("../core/plugin.js", () => ({
|
|
36
|
+
handlePluginAction: vi.fn(async () => null),
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
vi.mock("../core/dispatcher.js", () => ({
|
|
40
|
+
getActiveCount: vi.fn(() => 0),
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
vi.mock("../util/watchdog.js", () => ({
|
|
44
|
+
getHealthStatus: vi.fn(() => ({
|
|
45
|
+
healthy: true,
|
|
46
|
+
totalMessagesProcessed: 0,
|
|
47
|
+
recentErrorCount: 0,
|
|
48
|
+
msSinceLastMessage: 0,
|
|
49
|
+
})),
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
vi.mock("../storage/sessions.js", () => ({
|
|
53
|
+
getActiveSessionCount: vi.fn(() => 0),
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
// ── Subject under test ────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
import { Gateway } from "../core/gateway.js";
|
|
59
|
+
|
|
60
|
+
// ── Test suite ────────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
describe("Gateway — extended method coverage", () => {
|
|
63
|
+
let gateway: Gateway;
|
|
64
|
+
|
|
65
|
+
beforeEach(() => {
|
|
66
|
+
gateway = new Gateway();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// ── isChatBusy ─────────────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
describe("isChatBusy()", () => {
|
|
72
|
+
it("returns true when a context has been set for the chat", () => {
|
|
73
|
+
gateway.setContext(1001);
|
|
74
|
+
expect(gateway.isChatBusy(1001)).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("returns false when no context has ever been set", () => {
|
|
78
|
+
expect(gateway.isChatBusy(9999)).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("returns false after the context is cleared", () => {
|
|
82
|
+
gateway.setContext(1002);
|
|
83
|
+
gateway.clearContext(1002);
|
|
84
|
+
expect(gateway.isChatBusy(1002)).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("returns true while refCount > 0 after two setContext calls", () => {
|
|
88
|
+
gateway.setContext(1003);
|
|
89
|
+
gateway.setContext(1003); // refCount = 2
|
|
90
|
+
gateway.clearContext(1003); // refCount = 1 — still busy
|
|
91
|
+
expect(gateway.isChatBusy(1003)).toBe(true);
|
|
92
|
+
gateway.clearContext(1003); // refCount = 0 — released
|
|
93
|
+
expect(gateway.isChatBusy(1003)).toBe(false);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// ── incrementMessages / getMessageCount ────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
describe("incrementMessages() + getMessageCount()", () => {
|
|
100
|
+
it("starts at 0 after context is set", () => {
|
|
101
|
+
gateway.setContext(2001);
|
|
102
|
+
expect(gateway.getMessageCount(2001)).toBe(0);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("increments message count by 1 each call", () => {
|
|
106
|
+
gateway.setContext(2002);
|
|
107
|
+
gateway.incrementMessages(2002);
|
|
108
|
+
expect(gateway.getMessageCount(2002)).toBe(1);
|
|
109
|
+
gateway.incrementMessages(2002);
|
|
110
|
+
gateway.incrementMessages(2002);
|
|
111
|
+
expect(gateway.getMessageCount(2002)).toBe(3);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("incrementMessages on a chat with no context is a no-op (no throw)", () => {
|
|
115
|
+
expect(() => gateway.incrementMessages(9999)).not.toThrow();
|
|
116
|
+
expect(gateway.getMessageCount(9999)).toBe(0);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("message counts are independent per chat", () => {
|
|
120
|
+
gateway.setContext(2010);
|
|
121
|
+
gateway.setContext(2011);
|
|
122
|
+
gateway.incrementMessages(2010);
|
|
123
|
+
gateway.incrementMessages(2010);
|
|
124
|
+
gateway.incrementMessages(2011);
|
|
125
|
+
expect(gateway.getMessageCount(2010)).toBe(2);
|
|
126
|
+
expect(gateway.getMessageCount(2011)).toBe(1);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("getMessageCount returns 0 for a chat that has never had a context", () => {
|
|
130
|
+
expect(gateway.getMessageCount(8888)).toBe(0);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// ── getActiveChats ─────────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
describe("getActiveChats()", () => {
|
|
137
|
+
it("returns 0 for a fresh gateway", () => {
|
|
138
|
+
expect(gateway.getActiveChats()).toBe(0);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("increments as contexts are added", () => {
|
|
142
|
+
gateway.setContext(3001);
|
|
143
|
+
expect(gateway.getActiveChats()).toBe(1);
|
|
144
|
+
gateway.setContext(3002);
|
|
145
|
+
expect(gateway.getActiveChats()).toBe(2);
|
|
146
|
+
gateway.setContext(3003);
|
|
147
|
+
expect(gateway.getActiveChats()).toBe(3);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("does NOT increment when setContext is called twice for the same chat (refCount only)", () => {
|
|
151
|
+
gateway.setContext(3010);
|
|
152
|
+
gateway.setContext(3010); // ref++, not a new map entry
|
|
153
|
+
expect(gateway.getActiveChats()).toBe(1);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("decrements when a context is fully released", () => {
|
|
157
|
+
gateway.setContext(3020);
|
|
158
|
+
gateway.setContext(3021);
|
|
159
|
+
gateway.clearContext(3020);
|
|
160
|
+
expect(gateway.getActiveChats()).toBe(1);
|
|
161
|
+
gateway.clearContext(3021);
|
|
162
|
+
expect(gateway.getActiveChats()).toBe(0);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// ── setContext refCount ────────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
describe("setContext() refCount behaviour", () => {
|
|
169
|
+
it("first call creates context with refCount 1", () => {
|
|
170
|
+
gateway.setContext(4001);
|
|
171
|
+
expect(gateway.isChatBusy(4001)).toBe(true);
|
|
172
|
+
expect(gateway.getActiveChats()).toBe(1);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("second call for the same chat increments refCount (still 1 active chat)", () => {
|
|
176
|
+
gateway.setContext(4002);
|
|
177
|
+
gateway.setContext(4002);
|
|
178
|
+
expect(gateway.getActiveChats()).toBe(1);
|
|
179
|
+
// Needs two clears to fully release
|
|
180
|
+
gateway.clearContext(4002);
|
|
181
|
+
expect(gateway.isChatBusy(4002)).toBe(true);
|
|
182
|
+
gateway.clearContext(4002);
|
|
183
|
+
expect(gateway.isChatBusy(4002)).toBe(false);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("setContext with a stringId stores it for string-based lookup", () => {
|
|
187
|
+
// Assign a stringId; the context should be findable by it later
|
|
188
|
+
gateway.setContext(5001, "teams_chat_19:abc");
|
|
189
|
+
expect(gateway.isChatBusy(5001)).toBe(true);
|
|
190
|
+
// Clear by the stringId (non-numeric path) — should release the context
|
|
191
|
+
gateway.clearContext("teams_chat_19:abc");
|
|
192
|
+
expect(gateway.isChatBusy(5001)).toBe(false);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// ── clearContext — numeric string path ─────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
describe("clearContext() with numeric string chatId", () => {
|
|
199
|
+
it("accepts a numeric string and resolves it as a number", () => {
|
|
200
|
+
gateway.setContext(6001);
|
|
201
|
+
// "6001" → Number("6001") = 6001 — valid numeric path
|
|
202
|
+
gateway.clearContext("6001");
|
|
203
|
+
expect(gateway.isChatBusy(6001)).toBe(false);
|
|
204
|
+
expect(gateway.getActiveChats()).toBe(0);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("numeric string decrements refCount correctly", () => {
|
|
208
|
+
gateway.setContext(6002);
|
|
209
|
+
gateway.setContext(6002); // refCount = 2
|
|
210
|
+
gateway.clearContext("6002"); // refCount = 1
|
|
211
|
+
expect(gateway.isChatBusy(6002)).toBe(true);
|
|
212
|
+
gateway.clearContext("6002"); // refCount = 0
|
|
213
|
+
expect(gateway.isChatBusy(6002)).toBe(false);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// ── clearContext — non-numeric string (Teams-style) ────────────────────────
|
|
218
|
+
|
|
219
|
+
describe("clearContext() with non-numeric string chatId (findContextByStringId path)", () => {
|
|
220
|
+
it("clears by string ID when the context was set with a matching stringId", () => {
|
|
221
|
+
gateway.setContext(7001, "19:threadABC@thread.v2");
|
|
222
|
+
// Non-numeric → Number("19:threadABC@thread.v2") = NaN → fallback to findContextByStringId
|
|
223
|
+
gateway.clearContext("19:threadABC@thread.v2");
|
|
224
|
+
expect(gateway.isChatBusy(7001)).toBe(false);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it("is a no-op when the non-numeric string ID does not match any context", () => {
|
|
228
|
+
gateway.setContext(7002, "known-string-id");
|
|
229
|
+
// Unknown string ID — no context should be modified
|
|
230
|
+
gateway.clearContext("completely-unknown-id");
|
|
231
|
+
expect(gateway.isChatBusy(7002)).toBe(true); // original context untouched
|
|
232
|
+
gateway.clearContext(7002); // cleanup
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("refCount is decremented correctly through string ID", () => {
|
|
236
|
+
gateway.setContext(7003, "teams-thread-xyz");
|
|
237
|
+
gateway.setContext(7003); // refCount = 2 (no stringId on second call — same map entry)
|
|
238
|
+
gateway.clearContext("teams-thread-xyz"); // refCount = 1
|
|
239
|
+
expect(gateway.isChatBusy(7003)).toBe(true);
|
|
240
|
+
gateway.clearContext(7003); // refCount = 0
|
|
241
|
+
expect(gateway.isChatBusy(7003)).toBe(false);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// ── clearContext edge cases ────────────────────────────────────────────────
|
|
246
|
+
|
|
247
|
+
describe("clearContext() edge cases", () => {
|
|
248
|
+
it("does not go below refCount 0 (Math.max guard) — extra clear is safe", () => {
|
|
249
|
+
gateway.setContext(8001);
|
|
250
|
+
gateway.clearContext(8001); // refCount → 0, entry deleted
|
|
251
|
+
// Extra clear — context no longer exists, should be a no-op
|
|
252
|
+
expect(() => gateway.clearContext(8001)).not.toThrow();
|
|
253
|
+
expect(gateway.isChatBusy(8001)).toBe(false);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it("clearContext with undefined chatId is a no-op", () => {
|
|
257
|
+
gateway.setContext(8002);
|
|
258
|
+
gateway.clearContext(undefined);
|
|
259
|
+
expect(gateway.isChatBusy(8002)).toBe(true);
|
|
260
|
+
gateway.clearContext(8002); // cleanup
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("clearContext on an unknown numeric chatId is a no-op", () => {
|
|
264
|
+
expect(() => gateway.clearContext(99999)).not.toThrow();
|
|
265
|
+
expect(gateway.getActiveChats()).toBe(0);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// ── getPort before start ───────────────────────────────────────────────────
|
|
270
|
+
|
|
271
|
+
describe("getPort()", () => {
|
|
272
|
+
it("returns 0 before start() is called", () => {
|
|
273
|
+
expect(gateway.getPort()).toBe(0);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// ── stop() when server is null ─────────────────────────────────────────────
|
|
278
|
+
|
|
279
|
+
describe("stop()", () => {
|
|
280
|
+
it("resolves immediately when the server was never started", async () => {
|
|
281
|
+
// server is null — stop() should resolve without error
|
|
282
|
+
await expect(gateway.stop()).resolves.toBeUndefined();
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it("calling stop() twice after no start is safe", async () => {
|
|
286
|
+
await expect(gateway.stop()).resolves.toBeUndefined();
|
|
287
|
+
await expect(gateway.stop()).resolves.toBeUndefined();
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// ── setFrontendHandler ────────────────────────────────────────────────────
|
|
292
|
+
|
|
293
|
+
describe("setFrontendHandler()", () => {
|
|
294
|
+
it("accepts a handler function without throwing", () => {
|
|
295
|
+
const handler = vi.fn(async () => ({ ok: true }));
|
|
296
|
+
expect(() => gateway.setFrontendHandler(handler)).not.toThrow();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("replaces a previously set handler", () => {
|
|
300
|
+
const firstHandler = vi.fn(async () => ({ ok: true, source: "first" }));
|
|
301
|
+
const secondHandler = vi.fn(async () => ({ ok: true, source: "second" }));
|
|
302
|
+
gateway.setFrontendHandler(firstHandler);
|
|
303
|
+
gateway.setFrontendHandler(secondHandler);
|
|
304
|
+
// Both registrations should succeed without error; the second wins
|
|
305
|
+
expect(firstHandler).not.toHaveBeenCalled();
|
|
306
|
+
expect(secondHandler).not.toHaveBeenCalled();
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// ── combined workflow ─────────────────────────────────────────────────────
|
|
311
|
+
|
|
312
|
+
describe("combined workflow", () => {
|
|
313
|
+
it("full lifecycle: set → increment → clear → verify reset", () => {
|
|
314
|
+
gateway.setContext(9001);
|
|
315
|
+
gateway.incrementMessages(9001);
|
|
316
|
+
gateway.incrementMessages(9001);
|
|
317
|
+
expect(gateway.getMessageCount(9001)).toBe(2);
|
|
318
|
+
expect(gateway.isChatBusy(9001)).toBe(true);
|
|
319
|
+
|
|
320
|
+
gateway.clearContext(9001);
|
|
321
|
+
expect(gateway.isChatBusy(9001)).toBe(false);
|
|
322
|
+
// Message count for a cleared context returns 0 (map entry deleted)
|
|
323
|
+
expect(gateway.getMessageCount(9001)).toBe(0);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it("three chats, selective removal preserves others", () => {
|
|
327
|
+
gateway.setContext(9010);
|
|
328
|
+
gateway.setContext(9011);
|
|
329
|
+
gateway.setContext(9012);
|
|
330
|
+
expect(gateway.getActiveChats()).toBe(3);
|
|
331
|
+
|
|
332
|
+
gateway.clearContext(9011);
|
|
333
|
+
expect(gateway.getActiveChats()).toBe(2);
|
|
334
|
+
expect(gateway.isChatBusy(9010)).toBe(true);
|
|
335
|
+
expect(gateway.isChatBusy(9011)).toBe(false);
|
|
336
|
+
expect(gateway.isChatBusy(9012)).toBe(true);
|
|
337
|
+
|
|
338
|
+
gateway.clearContext(9010);
|
|
339
|
+
gateway.clearContext(9012);
|
|
340
|
+
expect(gateway.getActiveChats()).toBe(0);
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
});
|