@loadmill/executer 0.1.50 → 0.1.53
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/extraction-combiner.js +6 -6
- package/dist/extraction-combiner.js.map +1 -1
- package/dist/mill-info.d.ts +4 -0
- package/dist/mill-version.js +1 -1
- package/dist/post-script/console-log.d.ts +7 -0
- package/dist/post-script/console-log.js +31 -0
- package/dist/post-script/console-log.js.map +1 -0
- package/dist/post-script/post-script-executor.js +7 -4
- package/dist/post-script/post-script-executor.js.map +1 -1
- package/dist/request-sequence-result.d.ts +1 -0
- package/dist/sequence.d.ts +1 -1
- package/dist/sequence.js +18 -8
- package/dist/sequence.js.map +1 -1
- package/dist/single-runner.d.ts +1 -0
- package/dist/single-runner.js +1 -1
- package/dist/single-runner.js.map +1 -1
- package/package.json +3 -3
- package/src/asserter.ts +0 -137
- package/src/errors.ts +0 -10
- package/src/extraction-combiner.ts +0 -110
- package/src/failures.ts +0 -79
- package/src/message-creators.ts +0 -44
- package/src/mill-info.ts +0 -76
- package/src/mill-version.ts +0 -7
- package/src/post-script/ast-walker/index.ts +0 -160
- package/src/post-script/ast-walker/type-guard.ts +0 -73
- package/src/post-script/ast-walker/types.ts +0 -35
- package/src/post-script/parser/acorn-js-parser.ts +0 -8
- package/src/post-script/parser/js-parser.ts +0 -22
- package/src/post-script/parser/parser.ts +0 -5
- package/src/post-script/post-script-executor.ts +0 -89
- package/src/post-script/virtual-machine/virtual-machine.ts +0 -15
- package/src/post-script/virtual-machine/vm2-virtual-machine.ts +0 -45
- package/src/report-types.ts +0 -127
- package/src/request-sequence-result.ts +0 -63
- package/src/request-stats.ts +0 -20
- package/src/res-keeper.ts +0 -53
- package/src/sampler.ts +0 -133
- package/src/sequence.ts +0 -1107
- package/src/single-runner.ts +0 -66
- package/src/test-run-event-emitter.ts +0 -25
- package/src/utils.ts +0 -8
- package/src/work.ts +0 -17
- package/src/ws.ts +0 -286
- package/test/post-script-executor.spec.ts +0 -677
- package/tsconfig.json +0 -9
package/src/single-runner.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import superagent from 'superagent';
|
|
2
|
-
import { sequence } from './sequence';
|
|
3
|
-
import * as envUtils from '@loadmill/universal/dist/env-utils';
|
|
4
|
-
import log from '@loadmill/universal/dist/log';
|
|
5
|
-
import { AuthConf, LoadmillRequest, CachePenetration } from '@loadmill/core/dist/request';
|
|
6
|
-
import { Parameters } from '@loadmill/core/dist/parameters';
|
|
7
|
-
import {
|
|
8
|
-
ExtendedSequenceResult,
|
|
9
|
-
extendSequenceResult,
|
|
10
|
-
RequestSequenceResult
|
|
11
|
-
} from './request-sequence-result';
|
|
12
|
-
|
|
13
|
-
export async function runSingleIterationAndMergeResolved(
|
|
14
|
-
conf: ExecutableConf,
|
|
15
|
-
): Promise<ExtendedSequenceResult> {
|
|
16
|
-
const result = await runSingleIteration(conf);
|
|
17
|
-
return extendSequenceResult(result, conf.requests);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function runSingleIteration(
|
|
21
|
-
conf: ExecutableConf,
|
|
22
|
-
): Promise<RequestSequenceResult> {
|
|
23
|
-
let httpAgent;
|
|
24
|
-
if (conf.useCookies && !envUtils.isBrowser()) {
|
|
25
|
-
log.debug('Enabling cookies for node client.');
|
|
26
|
-
|
|
27
|
-
// todo itay: make leak intentionally possible?
|
|
28
|
-
// Unlike in the browser, cookies cannot leak from iteration to iteration:
|
|
29
|
-
httpAgent = superagent.agent();
|
|
30
|
-
} else {
|
|
31
|
-
httpAgent = superagent;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return sequence.execute(
|
|
35
|
-
httpAgent,
|
|
36
|
-
extendRequests(conf),
|
|
37
|
-
conf.parameters,
|
|
38
|
-
conf.domainsWhiteList,
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function extendRequests({
|
|
43
|
-
requests,
|
|
44
|
-
auth,
|
|
45
|
-
useCookies,
|
|
46
|
-
cachePenetration,
|
|
47
|
-
}: ExecutableConf) {
|
|
48
|
-
return requests.map((req) => ({
|
|
49
|
-
...req,
|
|
50
|
-
auth,
|
|
51
|
-
useCookies,
|
|
52
|
-
cachePenetration,
|
|
53
|
-
}));
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface ExecutableConf {
|
|
57
|
-
parameters: Parameters[];
|
|
58
|
-
domainsWhiteList: string[];
|
|
59
|
-
requests: LoadmillRequest[];
|
|
60
|
-
cachePenetration: CachePenetration;
|
|
61
|
-
|
|
62
|
-
auth?: AuthConf;
|
|
63
|
-
useCookies?: boolean;
|
|
64
|
-
useProxy?: boolean;
|
|
65
|
-
useStaticIp?: boolean;
|
|
66
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import log from '@loadmill/universal/dist/log';
|
|
2
|
-
import { Socket } from 'socket.io-client';
|
|
3
|
-
|
|
4
|
-
class TestRunEventsEmitter {
|
|
5
|
-
postScript: EventsEmitter;
|
|
6
|
-
constructor() {
|
|
7
|
-
const warningMsg = ' event emitter callback not assigned';
|
|
8
|
-
this.postScript = {
|
|
9
|
-
started: () => log.warn('postScript' + warningMsg),
|
|
10
|
-
finished: () => log.warn('postScript' + warningMsg),
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
setPostScriptHandlers = (socket: Socket, token: string) => {
|
|
15
|
-
this.postScript.started = () => socket.emit(`postscript-started:${token}`);
|
|
16
|
-
this.postScript.finished = () => socket.emit(`postscript-finished:${token}`);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const testRunEventsEmitter = new TestRunEventsEmitter();
|
|
21
|
-
export { testRunEventsEmitter };
|
|
22
|
-
|
|
23
|
-
type EventsEmitter = {
|
|
24
|
-
[funcName: string]: () => void | Socket;
|
|
25
|
-
}
|
package/src/utils.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MAX_API_REQUEST_BODY_LENGTH,
|
|
3
|
-
MAX_LOAD_REQUEST_BODY_LENGTH
|
|
4
|
-
} from '@loadmill/core/dist/conf/extrema';
|
|
5
|
-
import * as envUtils from '@loadmill/universal/dist/env-utils';
|
|
6
|
-
|
|
7
|
-
export const getMaxRequestBodySize = () =>
|
|
8
|
-
envUtils.isBrowser() ? MAX_LOAD_REQUEST_BODY_LENGTH : MAX_API_REQUEST_BODY_LENGTH;
|
package/src/work.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { AuthConf, LoadmillRequest, CachePenetration } from '@loadmill/core/dist/request';
|
|
2
|
-
import { TestConfLike } from '@loadmill/core/dist/conf';
|
|
3
|
-
import { Parameters } from '@loadmill/core/dist/parameters';
|
|
4
|
-
|
|
5
|
-
export interface Work extends TestConfLike {
|
|
6
|
-
loadId: string;
|
|
7
|
-
duration: number;
|
|
8
|
-
iterations: number;
|
|
9
|
-
iterationDelay: number;
|
|
10
|
-
parameters: Parameters[];
|
|
11
|
-
domainsWhiteList: string[];
|
|
12
|
-
requests: LoadmillRequest[];
|
|
13
|
-
cachePenetration: CachePenetration;
|
|
14
|
-
|
|
15
|
-
auth?: AuthConf;
|
|
16
|
-
useCookies?: boolean;
|
|
17
|
-
}
|
package/src/ws.ts
DELETED
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
import WebSocket from 'ws'; // todo should be picked with other option (socketio or non in browser)
|
|
2
|
-
import { delay } from '@loadmill/universal/dist/promise-utils';
|
|
3
|
-
import log from '@loadmill/universal/dist/log';
|
|
4
|
-
import { HttpResponseStatus, LoadmillHeaders } from '@loadmill/core/dist/request';
|
|
5
|
-
import { RequestFailuresError } from './errors';
|
|
6
|
-
import { ClientRequest, IncomingMessage, IncomingHttpHeaders } from 'http';
|
|
7
|
-
import { Socket } from 'net';
|
|
8
|
-
declare class WS extends WebSocket {
|
|
9
|
-
_req: WSClientRequest | null;
|
|
10
|
-
}
|
|
11
|
-
declare class WSClientRequest extends ClientRequest {
|
|
12
|
-
socket: Socket & { parser: ParserIncomingMessage };
|
|
13
|
-
}
|
|
14
|
-
declare class ParserIncomingMessage {
|
|
15
|
-
incoming: IncomingMessage;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
enum WSState {
|
|
20
|
-
CONNECTING,
|
|
21
|
-
OPEN,
|
|
22
|
-
CLOSING,
|
|
23
|
-
CLOSED,
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const SWITCHING_PROTOCOLS = 'Switching Protocols';
|
|
27
|
-
const WS_CONNECTION_TIMEOUT_MS = 10000;
|
|
28
|
-
|
|
29
|
-
export class WSRequest {
|
|
30
|
-
private hasErrorOccured?: boolean; // need this otherwise we have race condition between verifyConnectedAndOpen and onError
|
|
31
|
-
private ws: WebSocket;
|
|
32
|
-
public url: string; // need this for isExpectedStatus function
|
|
33
|
-
public expectedStatus: HttpResponseStatus; // need this for isExpectedStatus function
|
|
34
|
-
|
|
35
|
-
constructor(
|
|
36
|
-
private readonly wsRequestArgs: WSRequestArguments,
|
|
37
|
-
private readonly wsHandler: WSSequenceHandler,
|
|
38
|
-
private readonly onError: (e: Error) => void,
|
|
39
|
-
) {
|
|
40
|
-
this.url = wsRequestArgs.url;
|
|
41
|
-
this.expectedStatus = wsRequestArgs.expectedStatus;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* This function is executed when we are ready to send the ws request
|
|
46
|
-
* @param cb This callback is being executed after we successfully connected to ws and sent a ws message if there was any
|
|
47
|
-
*/
|
|
48
|
-
async ok(cb: (response: WSResponse) => boolean) {
|
|
49
|
-
const response: WSResponse = {
|
|
50
|
-
status: undefined,
|
|
51
|
-
res: {
|
|
52
|
-
statusMessage: undefined,
|
|
53
|
-
},
|
|
54
|
-
header: {},
|
|
55
|
-
req: {},
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const existingWS = this.wsHandler.getConnection(this.wsRequestArgs.url);
|
|
59
|
-
|
|
60
|
-
existingWS && log.debug(`Existing Connection state ${getConnectionState(existingWS)}`);
|
|
61
|
-
if (!existingWS || (existingWS && existingWS.readyState !== WSState.OPEN)) {
|
|
62
|
-
await this.addConnection(response);
|
|
63
|
-
} else {
|
|
64
|
-
this.ws = existingWS;
|
|
65
|
-
log.debug('Reusing existing ws connection', this.ws.url);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
this.sendMessage();
|
|
70
|
-
} catch (e) {
|
|
71
|
-
log.error('Failed to send a ws message', e);
|
|
72
|
-
}
|
|
73
|
-
cb(response);
|
|
74
|
-
return { };
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
private async addConnection(response: WSResponse) {
|
|
78
|
-
this.ws = new WebSocket(this.wsRequestArgs.url, undefined, { headers: this.wsRequestArgs.headers });
|
|
79
|
-
this.addEventListeners(response);
|
|
80
|
-
!this.hasErrorOccured && await this.verifyOpen();
|
|
81
|
-
this.wsHandler.storeConnection(this.wsRequestArgs.url, this.ws);
|
|
82
|
-
// todo add some debug details for the user to know we created a new connection in this request
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
private async addEventListeners(response: WSResponse) {
|
|
86
|
-
this.addOnErrorListener(response);
|
|
87
|
-
this.addOnUpgradeListener(response);
|
|
88
|
-
this.addOnOpenListener();
|
|
89
|
-
this.addOnMessageListener();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
private addOnErrorListener(response: WSResponse) {
|
|
93
|
-
this.ws.addEventListener('error', event => {
|
|
94
|
-
log.debug('inside addEventListener error');
|
|
95
|
-
try {
|
|
96
|
-
const target = event.target as WS;
|
|
97
|
-
this.updateResponseOnError(target, response);
|
|
98
|
-
} catch (e) {
|
|
99
|
-
log.error('Got an error inside addEventListener error', e);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
this.ws.on('error', (e) => {
|
|
104
|
-
try {
|
|
105
|
-
log.debug('inside on error', e);
|
|
106
|
-
this.onError(e);
|
|
107
|
-
this.hasErrorOccured = true;
|
|
108
|
-
} catch (e) {
|
|
109
|
-
log.warn('Failed to handle a ws error', e);
|
|
110
|
-
}
|
|
111
|
-
try {
|
|
112
|
-
log.debug('closing ws');
|
|
113
|
-
this.ws.close();
|
|
114
|
-
} catch (e) {
|
|
115
|
-
log.warn('Failed to close a ws connection', e);
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
private updateResponseOnError(ws: WS, response: WSResponse) {
|
|
121
|
-
if (ws._req) {
|
|
122
|
-
const { statusCode, statusMessage, headers } = ws._req.socket.parser.incoming;
|
|
123
|
-
log.debug('Got incoming incoming request', { statusCode, statusMessage, headers });
|
|
124
|
-
response.status = statusCode;
|
|
125
|
-
response.res.statusMessage = statusMessage;
|
|
126
|
-
response.header = headers;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
private addOnUpgradeListener(response: WSResponse) {
|
|
131
|
-
this.ws.on('upgrade', (event) => {
|
|
132
|
-
try {
|
|
133
|
-
const { statusCode, statusMessage, headers } = event;
|
|
134
|
-
log.debug('inside upgrade event', { statusCode, statusMessage, headers });
|
|
135
|
-
response.status = statusCode || 101;
|
|
136
|
-
response.res.statusMessage = statusMessage || SWITCHING_PROTOCOLS;
|
|
137
|
-
response.header = headers as LoadmillHeaders;
|
|
138
|
-
} catch (e) {
|
|
139
|
-
log.debug('upgrade event err', e);
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
private addOnOpenListener() {
|
|
145
|
-
this.ws.on('open', () => {
|
|
146
|
-
log.debug('inside on open, currently doing nothing here.');
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
private addOnMessageListener() {
|
|
151
|
-
this.ws.on('message', (message: any) => {
|
|
152
|
-
try {
|
|
153
|
-
|
|
154
|
-
const target = Array.isArray(message) ? decode(message) : message;
|
|
155
|
-
log.debug('got incoming message', target);
|
|
156
|
-
|
|
157
|
-
this.wsHandler.addMessage(target);
|
|
158
|
-
} catch (e) {
|
|
159
|
-
log.debug('error getting message', e);
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
async verifyOpen() {
|
|
165
|
-
const readyState = await this.waitForConnectedState(this.wsRequestArgs.timeout);
|
|
166
|
-
const connectionDebugMsg = `Connection ${WSState[readyState]}`;
|
|
167
|
-
log.debug(connectionDebugMsg);
|
|
168
|
-
if (readyState !== WebSocket.OPEN && !this.hasErrorOccured) {
|
|
169
|
-
let msg = 'Could not connect to WebSocket address: ';
|
|
170
|
-
log.debug(msg, { connectionState: WSState[readyState], url: this.ws.url });
|
|
171
|
-
if (readyState === WSState.CONNECTING) {
|
|
172
|
-
msg += 'request timeout';
|
|
173
|
-
} else { // CLOSING or CLOSED state
|
|
174
|
-
msg += connectionDebugMsg;
|
|
175
|
-
}
|
|
176
|
-
throw new RequestFailuresError(msg);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Waiting for WS connection to go out of CONNECTING state
|
|
182
|
-
* @param socket The ws connection object
|
|
183
|
-
* @param timeout The timeout in milliseconds for the waiting procedure
|
|
184
|
-
* @returns WSState
|
|
185
|
-
*/
|
|
186
|
-
waitForConnectedState = async (timeout: number = WS_CONNECTION_TIMEOUT_MS): Promise<WSState> => {
|
|
187
|
-
const WS_CONNECTION_INTERVAL_MS = 100;
|
|
188
|
-
if (this.ws.readyState != null && this.ws.readyState !== WebSocket.CONNECTING) {
|
|
189
|
-
return this.ws.readyState;
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
const maxIterations = timeout / WS_CONNECTION_INTERVAL_MS;
|
|
193
|
-
let i = 0;
|
|
194
|
-
while (this.ws.readyState === WebSocket.CONNECTING && i < maxIterations) {
|
|
195
|
-
await delay(WS_CONNECTION_INTERVAL_MS);
|
|
196
|
-
i++;
|
|
197
|
-
}
|
|
198
|
-
return this.ws.readyState;
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
private sendMessage() {
|
|
203
|
-
log.debug('about to send message', { readyState: WSState[this.ws.readyState] } );
|
|
204
|
-
|
|
205
|
-
const { message, mimeType } = this.wsRequestArgs;
|
|
206
|
-
|
|
207
|
-
if (this.ws.readyState === WSState.OPEN && message) {
|
|
208
|
-
this.ws.send(message, { binary: mimeType === 'binary' });
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// need this because the SequenceExecutor expectes a request obj with on func prop
|
|
214
|
-
on(_eventName: string, _cb: Function) {
|
|
215
|
-
//do nothing
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
export class WSSequenceHandler {
|
|
220
|
-
private _connections: { [url: string]: WebSocket };
|
|
221
|
-
messages: string[];
|
|
222
|
-
constructor() {
|
|
223
|
-
this._connections = {};
|
|
224
|
-
this.clearMessages();
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
getConnection(url: string): WebSocket | undefined {
|
|
228
|
-
return this._connections[url];
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
storeConnection(url: string, wsConnection: WebSocket) {
|
|
232
|
-
this._connections[url] = wsConnection;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
async closeAllConnections() {
|
|
236
|
-
try {
|
|
237
|
-
log.debug('Closing all ws connections');
|
|
238
|
-
for (const connection of Object.values(this._connections)) {
|
|
239
|
-
connection.close();
|
|
240
|
-
}
|
|
241
|
-
} catch (e) {
|
|
242
|
-
log.warn('Failed to close all connections', e, { connections: this._connections });
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
getMessages() {
|
|
247
|
-
return this.messages;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
addMessage(message: string) {
|
|
251
|
-
this.messages.push(message);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
clearMessages() {
|
|
255
|
-
this.messages = [];
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function getConnectionState(existingWS?: WebSocket) {
|
|
260
|
-
return existingWS ? WSState[existingWS.readyState] : null;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function decode(buffer, encoding = 'utf-8') {
|
|
264
|
-
const enc = new TextDecoder(encoding);
|
|
265
|
-
return enc.decode(new Uint8Array(buffer));
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
export type WSMimeType = 'binary' | 'text';
|
|
269
|
-
|
|
270
|
-
export type WSRequestArguments = {
|
|
271
|
-
expectedStatus: HttpResponseStatus;
|
|
272
|
-
headers: LoadmillHeaders
|
|
273
|
-
message: string;
|
|
274
|
-
mimeType: WSMimeType;
|
|
275
|
-
timeout: number;
|
|
276
|
-
url: string;
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
export type WSResponse = {
|
|
280
|
-
status?: number;
|
|
281
|
-
res: {
|
|
282
|
-
statusMessage?: string;
|
|
283
|
-
};
|
|
284
|
-
header: LoadmillHeaders | IncomingHttpHeaders;
|
|
285
|
-
req: {};
|
|
286
|
-
}
|