Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/client/test/handler.test.ts
1028 views
1
import ICoreRequestPayload from '@secret-agent/interfaces/ICoreRequestPayload';
2
import { Helpers } from '@secret-agent/testing/index';
3
import ExecuteJsPlugin from '@secret-agent/execute-js-plugin'; // eslint-disable-line import/no-extraneous-dependencies
4
import { Handler } from '../index';
5
import ConnectionToCore from '../connections/ConnectionToCore';
6
7
const outgoing = jest.fn();
8
9
class Piper extends ConnectionToCore {
10
async internalSendRequest(payload: ICoreRequestPayload): Promise<void> {
11
const response = await outgoing(payload);
12
this.onMessage({
13
responseId: payload.messageId,
14
data: response?.data,
15
...(response ?? {}),
16
});
17
}
18
19
protected createConnection = () => Promise.resolve(null);
20
protected destroyConnection = () => Promise.resolve(null);
21
}
22
23
afterAll(Helpers.afterAll);
24
afterEach(Helpers.afterEach);
25
26
describe('Handler', () => {
27
it('allows you to run concurrent dispatched tasks', async () => {
28
let counter = 0;
29
outgoing.mockImplementation(({ command }) => {
30
if (command === 'Session.create') {
31
return {
32
data: {
33
tabId: 'tab-id',
34
sessionId: `${(counter += 1)}`,
35
sessionsDataLocation: '',
36
},
37
};
38
}
39
if (command === 'Session.addEventListener') {
40
return {
41
data: { listenerId: 1 },
42
};
43
}
44
if (command === 'Session.close') {
45
return {
46
data: {},
47
};
48
}
49
});
50
const concurrency = 6;
51
const handler = new Handler(
52
new Piper({
53
maxConcurrency: concurrency,
54
}),
55
);
56
Helpers.needsClosing.push(handler);
57
58
const sessionsRunning = new Map<string, boolean>();
59
const runningAtSameTime: string[][] = [];
60
const expectedCalls: string[] = [];
61
62
const runFn = async (agent): Promise<any> => {
63
const sessionId = await agent.sessionId;
64
sessionsRunning.set(sessionId, true);
65
const concurrent: string[] = [];
66
for (const [session, isRunning] of sessionsRunning) {
67
if (isRunning) concurrent.push(session);
68
}
69
runningAtSameTime.push(concurrent);
70
await new Promise<void>(resolve => setTimeout(resolve, Math.random() * 25));
71
sessionsRunning.set(sessionId, false);
72
};
73
for (let i = 0; i < 100; i += 1) {
74
handler.dispatchAgent(runFn, { input: i });
75
expectedCalls.push('Session.create', 'Session.close');
76
}
77
78
await handler.waitForAllDispatches();
79
80
expect(runningAtSameTime.filter(x => x.length > concurrency)).toHaveLength(0);
81
expect(runningAtSameTime.filter(x => x.length >= concurrency).length).toBeGreaterThanOrEqual(1);
82
83
await handler.close();
84
85
const outgoingCommands = outgoing.mock.calls;
86
expect(outgoingCommands.map(c => c[0].command).sort()).toMatchObject(
87
['Core.connect', ...expectedCalls, 'Core.disconnect'].sort(),
88
);
89
});
90
91
it('has a max concurrency for "created" agents', async () => {
92
let counter = 0;
93
let listenerId = 0;
94
outgoing.mockImplementation(({ command }) => {
95
if (command === 'Session.create') {
96
return {
97
data: {
98
tabId: 'tab-id',
99
sessionId: `${(counter += 1)}`,
100
sessionsDataLocation: '',
101
},
102
};
103
}
104
if (command === 'Session.addEventListener') {
105
return {
106
data: { listenerId: (listenerId += 1).toString() },
107
};
108
}
109
if (command === 'close') {
110
return {
111
data: {},
112
};
113
}
114
});
115
const connection = new Piper({
116
maxConcurrency: 2,
117
});
118
const handler = new Handler(connection);
119
Helpers.needsClosing.push(handler);
120
121
const agent1 = await handler.createAgent();
122
const agent2 = await handler.createAgent();
123
await expect(agent1.sessionId).resolves.toBe('1');
124
await expect(agent2.sessionId).resolves.toBe('2');
125
const agent3 = handler.createAgent();
126
127
async function isAgent3Available(): Promise<boolean> {
128
const result = await Promise.race([
129
agent3,
130
new Promise(resolve => setTimeout(() => resolve('not avail'), 100)),
131
]);
132
return result !== 'not avail';
133
}
134
135
await expect(isAgent3Available()).resolves.toBe(false);
136
137
await agent1.close();
138
connection.onMessage({ listenerId: '1', meta: { sessionId: '1' }, eventArgs: [] });
139
await new Promise(setImmediate);
140
await expect(isAgent3Available()).resolves.toBe(true);
141
});
142
143
it('can use plugins in handlers', async () => {
144
let counter = 0;
145
let listenerId = 0;
146
let createSessionArgs: any;
147
outgoing.mockImplementation(message => {
148
const { command } = message;
149
if (command === 'Session.create') {
150
createSessionArgs = message.args[0];
151
return {
152
data: {
153
tabId: 'tab-id',
154
sessionId: `${(counter += 1)}`,
155
sessionsDataLocation: '',
156
},
157
};
158
}
159
if (command === 'Session.addEventListener') {
160
return {
161
data: { listenerId: (listenerId += 1).toString() },
162
};
163
}
164
if (command === 'close') {
165
return {
166
data: {},
167
};
168
}
169
});
170
const connection = new Piper({
171
maxConcurrency: 2,
172
});
173
const handler = new Handler(connection);
174
Helpers.needsClosing.push(handler);
175
176
handler.dispatchAgent(async agent => {
177
agent.use(ExecuteJsPlugin);
178
await agent.sessionId;
179
});
180
181
await handler.waitForAllDispatches();
182
183
expect(createSessionArgs.dependencyMap[ExecuteJsPlugin.ClientPlugin.id]).toBeTruthy();
184
});
185
});
186
187