@eggjs/mock 7.0.0-beta.18 → 7.0.0-beta.20
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/agent-BKYkjoCx.js +111 -0
- package/dist/agent-CHCe8tnW.d.ts +24 -0
- package/dist/agent-DZ_fHoxJ.js +48 -0
- package/dist/agent_handler-DeUtoRVT.js +26 -0
- package/dist/app/extend/agent.d.ts +1 -1
- package/dist/app/extend/agent.js +3 -46
- package/dist/app/extend/application.d.ts +4 -174
- package/dist/app/extend/application.js +7 -383
- package/dist/app-6Bn3F5Uw.js +252 -0
- package/dist/app-CoypamK1.js +99 -0
- package/dist/app-l47a-gqT.d.ts +7 -0
- package/dist/app_handler-CkiK6fyC.js +55 -0
- package/dist/app_handler-Gp5J9X-v.d.ts +9 -0
- package/dist/application-BPpvw5dg.d.ts +175 -0
- package/dist/application-LrKwuOKn.js +385 -0
- package/dist/bootstrap.d.ts +9 -3
- package/dist/bootstrap.js +20 -3
- package/dist/cluster-CA_hr9eV.js +283 -0
- package/dist/cluster-RTfUwmUd.d.ts +126 -0
- package/dist/context-BIduDy1M.js +10 -0
- package/dist/format_options-LRnReKde.js +65 -0
- package/dist/index-BtBMDZg8.d.ts +78 -0
- package/dist/index.d.ts +10 -81
- package/dist/index.js +20 -45
- package/dist/inject_context-BxpcF-ds.js +106 -0
- package/dist/inject_mocha-BVf8v6El.js +38 -0
- package/dist/inject_mocha-Daf1Aj8M.d.ts +1 -0
- package/dist/inject_mocha.d.ts +1 -1
- package/dist/inject_mocha.js +21 -32
- package/dist/lib/agent_handler.d.ts +4 -1
- package/dist/lib/agent_handler.js +7 -24
- package/dist/lib/app.d.ts +5 -6
- package/dist/lib/app.js +12 -252
- package/dist/lib/app_handler.d.ts +5 -8
- package/dist/lib/app_handler.js +19 -54
- package/dist/lib/cluster.d.ts +5 -125
- package/dist/lib/cluster.js +5 -281
- package/dist/lib/context.js +1 -8
- package/dist/lib/format_options.d.ts +2 -2
- package/dist/lib/format_options.js +2 -63
- package/dist/lib/inject_context.js +20 -104
- package/dist/lib/mock_agent.js +1 -43
- package/dist/lib/mock_custom_loader.js +1 -33
- package/dist/lib/mock_http_server.js +1 -15
- package/dist/lib/mock_httpclient.d.ts +1 -39
- package/dist/lib/mock_httpclient.js +2 -108
- package/dist/lib/parallel/agent.d.ts +4 -23
- package/dist/lib/parallel/agent.js +6 -109
- package/dist/lib/parallel/app.d.ts +3 -2
- package/dist/lib/parallel/app.js +7 -97
- package/dist/lib/parallel/util.d.ts +1 -5
- package/dist/lib/parallel/util.js +2 -57
- package/dist/lib/restore.d.ts +1 -3
- package/dist/lib/restore.js +7 -14
- package/dist/lib/supertest.d.ts +1 -15
- package/dist/lib/supertest.js +3 -37
- package/dist/lib/types.d.ts +3 -76
- package/dist/lib/types.js +2 -0
- package/dist/lib/utils.js +1 -38
- package/dist/mock_agent-CxT7Q0_N.js +45 -0
- package/dist/mock_custom_loader-f_tHRojG.js +35 -0
- package/dist/mock_http_server-De0r07gr.js +17 -0
- package/dist/mock_httpclient-1EgQkMxi.d.ts +40 -0
- package/dist/mock_httpclient-B6UVI7GS.js +110 -0
- package/dist/register.d.ts +2 -0
- package/dist/register.js +22 -4
- package/dist/restore-BL0cE0KG.d.ts +4 -0
- package/dist/restore-CVQYXquh.js +16 -0
- package/dist/src-D22EjdGo.js +47 -0
- package/dist/supertest-BIds28yL.d.ts +16 -0
- package/dist/supertest-Cg412Los.js +39 -0
- package/dist/types-CPNMyF89.js +1 -0
- package/dist/types-CjZ-JeCo.d.ts +75 -0
- package/dist/util-1xYBaaoh.d.ts +6 -0
- package/dist/util-BpppqTXv.js +59 -0
- package/dist/utils-MhP_Krx1.js +40 -0
- package/package.json +7 -9
- package/dist/_virtual/rolldown_runtime.js +0 -7
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { request } from "./supertest-Cg412Los.js";
|
|
2
|
+
import { getMockAgent, restoreMockAgent } from "./mock_agent-CxT7Q0_N.js";
|
|
3
|
+
import { createMockHttpClient } from "./mock_httpclient-B6UVI7GS.js";
|
|
4
|
+
import { Application } from "egg";
|
|
5
|
+
import assert from "node:assert";
|
|
6
|
+
import { mock, restore } from "mm";
|
|
7
|
+
import { debuglog } from "node:util";
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import http, { IncomingMessage } from "node:http";
|
|
10
|
+
import mergeDescriptors from "merge-descriptors";
|
|
11
|
+
import { isAsyncFunction, isObject } from "is-type-of";
|
|
12
|
+
import { Logger, Transport } from "egg-logger";
|
|
13
|
+
|
|
14
|
+
//#region src/app/extend/application.ts
|
|
15
|
+
const debug = debuglog("egg/mock/app/extend/application");
|
|
16
|
+
const ORIGIN_TYPES = Symbol("@eggjs/mock originTypes");
|
|
17
|
+
const BACKGROUND_TASKS = Symbol("Application#backgroundTasks");
|
|
18
|
+
const REUSED_CTX = Symbol("Context#reusedInSuite");
|
|
19
|
+
var ApplicationUnittest = class extends Application {
|
|
20
|
+
_mockHttpClient;
|
|
21
|
+
/**
|
|
22
|
+
* mock Context
|
|
23
|
+
* @function App#mockContext
|
|
24
|
+
* @param {Object} data - ctx data
|
|
25
|
+
* @param {Object} [options] - mock ctx options
|
|
26
|
+
* @example
|
|
27
|
+
* ```js
|
|
28
|
+
* const ctx = app.mockContext({
|
|
29
|
+
* user: {
|
|
30
|
+
* name: 'Jason'
|
|
31
|
+
* }
|
|
32
|
+
* });
|
|
33
|
+
* console.log(ctx.user.name); // Jason
|
|
34
|
+
*
|
|
35
|
+
* // controller
|
|
36
|
+
* module.exports = function*() {
|
|
37
|
+
* this.body = this.user.name;
|
|
38
|
+
* };
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
mockContext(data, options) {
|
|
42
|
+
data = data ?? {};
|
|
43
|
+
function mockRequest(req$1) {
|
|
44
|
+
for (const key in data?.headers) {
|
|
45
|
+
mock(req$1.headers, key, data.headers[key]);
|
|
46
|
+
mock(req$1.headers, key.toLowerCase(), data.headers[key]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const mockCtxStorage = this.options.mockCtxStorage ?? true;
|
|
50
|
+
options = Object.assign({ mockCtxStorage }, options);
|
|
51
|
+
if ("_customMockContext" in this && typeof this._customMockContext === "function") this._customMockContext(data);
|
|
52
|
+
for (const key in data) mock(this.context, key, data[key]);
|
|
53
|
+
const req = this.mockRequest(data);
|
|
54
|
+
const res = new http.ServerResponse(req);
|
|
55
|
+
if (options.reuseCtxStorage !== false) {
|
|
56
|
+
if (this.currentContext && !this.currentContext[REUSED_CTX]) {
|
|
57
|
+
mockRequest(this.currentContext.request.req);
|
|
58
|
+
this.currentContext[REUSED_CTX] = true;
|
|
59
|
+
return this.currentContext;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const ctx = this.createContext(req, res);
|
|
63
|
+
if (options.mockCtxStorage) mock(this.ctxStorage, "getStore", () => ctx);
|
|
64
|
+
return ctx;
|
|
65
|
+
}
|
|
66
|
+
async mockContextScope(fn, data) {
|
|
67
|
+
const ctx = this.mockContext(data, {
|
|
68
|
+
mockCtxStorage: false,
|
|
69
|
+
reuseCtxStorage: false
|
|
70
|
+
});
|
|
71
|
+
return await this.ctxStorage.run(ctx, async () => {
|
|
72
|
+
return await fn(ctx);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* mock cookie session
|
|
77
|
+
* @function App#mockSession
|
|
78
|
+
* @param {Object} data - session object
|
|
79
|
+
*/
|
|
80
|
+
mockSession(data) {
|
|
81
|
+
if (!data) return this;
|
|
82
|
+
if (isObject(data) && !("save" in data)) Object.defineProperty(data, "save", {
|
|
83
|
+
value: () => {},
|
|
84
|
+
enumerable: false
|
|
85
|
+
});
|
|
86
|
+
mock(this.context, "session", data);
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Mock service
|
|
91
|
+
* @function App#mockService
|
|
92
|
+
* @param {String} service - name
|
|
93
|
+
* @param {String} methodName - method
|
|
94
|
+
* @param {Object|Function|Error} fn - mock you data
|
|
95
|
+
*/
|
|
96
|
+
mockService(service, methodName, fn) {
|
|
97
|
+
if (typeof service === "string") {
|
|
98
|
+
const splits = service.split(".");
|
|
99
|
+
service = this.serviceClasses;
|
|
100
|
+
for (const key of splits) service = service[key];
|
|
101
|
+
service = service.prototype || service;
|
|
102
|
+
}
|
|
103
|
+
this._mockFn(service, methodName, fn);
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* mock service that return error
|
|
108
|
+
* @function App#mockServiceError
|
|
109
|
+
* @param {String} service - name
|
|
110
|
+
* @param {String} methodName - method
|
|
111
|
+
* @param {Error} [err] - error information
|
|
112
|
+
*/
|
|
113
|
+
mockServiceError(service, methodName, err) {
|
|
114
|
+
if (typeof err === "string") err = new Error(err);
|
|
115
|
+
if (!err) err = /* @__PURE__ */ new Error(`mock ${methodName} error`);
|
|
116
|
+
this.mockService(service, methodName, err);
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
_mockFn(obj, name, data) {
|
|
120
|
+
const origin = obj[name];
|
|
121
|
+
assert(typeof origin === "function", `property ${name} in original object must be function`);
|
|
122
|
+
if (!obj[ORIGIN_TYPES]) obj[ORIGIN_TYPES] = {};
|
|
123
|
+
let type = obj[ORIGIN_TYPES][name];
|
|
124
|
+
if (!type) type = obj[ORIGIN_TYPES][name] = isAsyncFunction(origin) ? "async" : "sync";
|
|
125
|
+
if (typeof data === "function") {
|
|
126
|
+
const fn = data;
|
|
127
|
+
if (type === "async" && !isAsyncFunction(fn)) {
|
|
128
|
+
mock(obj, name, function(...args) {
|
|
129
|
+
return new Promise((resolve) => {
|
|
130
|
+
resolve(fn.apply(this, args));
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
mock(obj, name, fn);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (type === "async") {
|
|
139
|
+
mock(obj, name, () => {
|
|
140
|
+
return new Promise((resolve, reject) => {
|
|
141
|
+
if (data instanceof Error) return reject(data);
|
|
142
|
+
resolve(data);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
mock(obj, name, () => {
|
|
148
|
+
if (data instanceof Error) throw data;
|
|
149
|
+
return data;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* mock request
|
|
154
|
+
* @function App#mockRequest
|
|
155
|
+
* @param {Request} req - mock request
|
|
156
|
+
*/
|
|
157
|
+
mockRequest(req) {
|
|
158
|
+
req = { ...req };
|
|
159
|
+
const headers = req.headers ?? {};
|
|
160
|
+
for (const key in req.headers) headers[key.toLowerCase()] = req.headers[key];
|
|
161
|
+
if (!headers["x-forwarded-for"]) headers["x-forwarded-for"] = "127.0.0.1";
|
|
162
|
+
headers["x-mock-request-from"] = "@eggjs/mock";
|
|
163
|
+
req.headers = headers;
|
|
164
|
+
mergeDescriptors(req, {
|
|
165
|
+
query: {},
|
|
166
|
+
querystring: "",
|
|
167
|
+
host: "127.0.0.1",
|
|
168
|
+
hostname: "127.0.0.1",
|
|
169
|
+
protocol: "http",
|
|
170
|
+
secure: "false",
|
|
171
|
+
method: "GET",
|
|
172
|
+
url: "/",
|
|
173
|
+
path: "/",
|
|
174
|
+
socket: {
|
|
175
|
+
remoteAddress: "127.0.0.1",
|
|
176
|
+
remotePort: 7001
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
return req;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* mock cookies
|
|
183
|
+
* @function App#mockCookies
|
|
184
|
+
*/
|
|
185
|
+
mockCookies(cookies) {
|
|
186
|
+
if (!cookies) return this;
|
|
187
|
+
const createContext = this.createContext;
|
|
188
|
+
mock(this, "createContext", function(req, res) {
|
|
189
|
+
const ctx = createContext.call(this, req, res);
|
|
190
|
+
const getCookie = ctx.cookies.get;
|
|
191
|
+
mock(ctx.cookies, "get", function(key, opts) {
|
|
192
|
+
if (cookies[key]) return cookies[key];
|
|
193
|
+
return getCookie.call(this, key, opts);
|
|
194
|
+
});
|
|
195
|
+
return ctx;
|
|
196
|
+
});
|
|
197
|
+
return this;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* mock header
|
|
201
|
+
* @function App#mockHeaders
|
|
202
|
+
*/
|
|
203
|
+
mockHeaders(headers) {
|
|
204
|
+
if (!headers) return this;
|
|
205
|
+
const getHeader = this.request.get;
|
|
206
|
+
mock(this.request, "get", function(field) {
|
|
207
|
+
const value = findHeaders(headers, field);
|
|
208
|
+
if (value) return value;
|
|
209
|
+
return getHeader.call(this, field);
|
|
210
|
+
});
|
|
211
|
+
return this;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* mock csrf
|
|
215
|
+
* @function App#mockCsrf
|
|
216
|
+
* @since 1.11
|
|
217
|
+
*/
|
|
218
|
+
mockCsrf() {
|
|
219
|
+
mock(this.context, "assertCSRF", () => {});
|
|
220
|
+
mock(this.context, "assertCsrf", () => {});
|
|
221
|
+
return this;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* mock httpclient
|
|
225
|
+
* @alias mockHttpClient
|
|
226
|
+
* @function App#mockHttpclient
|
|
227
|
+
*/
|
|
228
|
+
mockHttpclient(mockUrl, mockMethod, mockResult) {
|
|
229
|
+
return this.mockHttpClient(mockUrl, mockMethod, mockResult);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* mock httpclient
|
|
233
|
+
* @function App#mockHttpClient
|
|
234
|
+
*/
|
|
235
|
+
mockHttpClient(mockUrl, mockMethod, mockResult) {
|
|
236
|
+
if (!this._mockHttpClient) this._mockHttpClient = createMockHttpClient(this);
|
|
237
|
+
this._mockHttpClient(mockUrl, mockMethod, mockResult);
|
|
238
|
+
return this;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* @deprecated Please use app.mockHttpClient instead of app.mockUrllib
|
|
242
|
+
*/
|
|
243
|
+
mockUrllib(mockUrl, mockMethod, mockResult) {
|
|
244
|
+
this.deprecate("[@eggjs/mock] Please use app.mockHttpClient instead of app.mockUrllib");
|
|
245
|
+
return this.mockHttpClient(mockUrl, mockMethod, mockResult);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* get mock httpclient agent
|
|
249
|
+
* @function App#mockHttpclientAgent
|
|
250
|
+
*/
|
|
251
|
+
mockAgent() {
|
|
252
|
+
return getMockAgent(this);
|
|
253
|
+
}
|
|
254
|
+
async mockAgentRestore() {
|
|
255
|
+
await restoreMockAgent();
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* @see mm#restore
|
|
259
|
+
* @function App#mockRestore
|
|
260
|
+
*/
|
|
261
|
+
async mockRestore() {
|
|
262
|
+
await this.mockAgentRestore();
|
|
263
|
+
restore();
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* @see mm
|
|
267
|
+
* @function App#mm
|
|
268
|
+
*/
|
|
269
|
+
get mm() {
|
|
270
|
+
return mock;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* override loadAgent
|
|
274
|
+
* @function App#loadAgent
|
|
275
|
+
*/
|
|
276
|
+
loadAgent() {}
|
|
277
|
+
/**
|
|
278
|
+
* mock serverEnv
|
|
279
|
+
* @function App#mockEnv
|
|
280
|
+
* @param {String} env - serverEnv
|
|
281
|
+
*/
|
|
282
|
+
mockEnv(env) {
|
|
283
|
+
mock(this.config, "env", env);
|
|
284
|
+
mock(this.config, "serverEnv", env);
|
|
285
|
+
debug("mock env: %o", env);
|
|
286
|
+
return this;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* http request helper
|
|
290
|
+
* @function App#httpRequest
|
|
291
|
+
* @return {SupertestRequest} req - supertest request
|
|
292
|
+
* @see https://github.com/visionmedia/supertest
|
|
293
|
+
*/
|
|
294
|
+
httpRequest() {
|
|
295
|
+
return request(this);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* collection logger message, then can be use on `expectLog()`
|
|
299
|
+
* @param {String|Logger} [logger] - logger instance, default is `app.logger`
|
|
300
|
+
* @function App#mockLog
|
|
301
|
+
*/
|
|
302
|
+
mockLog(logger) {
|
|
303
|
+
logger = logger ?? this.logger;
|
|
304
|
+
if (typeof logger === "string") logger = this.getLogger(logger);
|
|
305
|
+
if ("_mockLogs" in logger && logger._mockLogs) return;
|
|
306
|
+
const transport = new Transport(logger.options);
|
|
307
|
+
const log = logger.log;
|
|
308
|
+
const mockLogs = [];
|
|
309
|
+
mock(logger, "_mockLogs", mockLogs);
|
|
310
|
+
mock(logger, "log", (level, args, meta) => {
|
|
311
|
+
const message = transport.log(level, args, meta);
|
|
312
|
+
mockLogs.push(message);
|
|
313
|
+
log.apply(logger, [
|
|
314
|
+
level,
|
|
315
|
+
args,
|
|
316
|
+
meta
|
|
317
|
+
]);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
__checkExpectLog(expectOrNot, str, logger) {
|
|
321
|
+
logger = logger || this.logger;
|
|
322
|
+
if (typeof logger === "string") logger = this.getLogger(logger);
|
|
323
|
+
const filepath = logger.options.file;
|
|
324
|
+
let content;
|
|
325
|
+
if ("_mockLogs" in logger && logger._mockLogs) content = logger._mockLogs.join("\n");
|
|
326
|
+
else content = fs.readFileSync(filepath, "utf8");
|
|
327
|
+
let match;
|
|
328
|
+
let type;
|
|
329
|
+
if (str instanceof RegExp) {
|
|
330
|
+
match = str.test(content);
|
|
331
|
+
type = "RegExp";
|
|
332
|
+
} else {
|
|
333
|
+
match = content.includes(String(str));
|
|
334
|
+
type = "String";
|
|
335
|
+
}
|
|
336
|
+
if (expectOrNot) assert(match, `Can't find ${type}:"${str}" in ${filepath}, log content: ...${content.substring(content.length - 500)}`);
|
|
337
|
+
else assert(!match, `Find ${type}:"${str}" in ${filepath}, log content: ...${content.substring(content.length - 500)}`);
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* expect str/regexp in the logger, if your server disk is slow, please call `mockLog()` first.
|
|
341
|
+
* @param {String|RegExp} str - test str or regexp
|
|
342
|
+
* @param {String|Logger} [logger] - logger instance, default is `ctx.logger`
|
|
343
|
+
* @function App#expectLog
|
|
344
|
+
*/
|
|
345
|
+
expectLog(str, logger) {
|
|
346
|
+
this.__checkExpectLog(true, str, logger);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* not expect str/regexp in the logger, if your server disk is slow, please call `mockLog()` first.
|
|
350
|
+
* @param {String|RegExp} str - test str or regexp
|
|
351
|
+
* @param {String|Logger} [logger] - logger instance, default is `ctx.logger`
|
|
352
|
+
* @function App#notExpectLog
|
|
353
|
+
*/
|
|
354
|
+
notExpectLog(str, logger) {
|
|
355
|
+
this.__checkExpectLog(false, str, logger);
|
|
356
|
+
}
|
|
357
|
+
async backgroundTasksFinished() {
|
|
358
|
+
const tasks = this._backgroundTasks;
|
|
359
|
+
debug("waiting %d background tasks", tasks.length);
|
|
360
|
+
if (tasks.length === 0) return;
|
|
361
|
+
this._backgroundTasks = [];
|
|
362
|
+
await Promise.all(tasks);
|
|
363
|
+
debug("finished %d background tasks", tasks.length);
|
|
364
|
+
if (this._backgroundTasks.length) {
|
|
365
|
+
debug("new background tasks created: %s", this._backgroundTasks.length);
|
|
366
|
+
await this.backgroundTasksFinished();
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
get _backgroundTasks() {
|
|
370
|
+
if (!this[BACKGROUND_TASKS]) this[BACKGROUND_TASKS] = [];
|
|
371
|
+
return this[BACKGROUND_TASKS];
|
|
372
|
+
}
|
|
373
|
+
set _backgroundTasks(tasks) {
|
|
374
|
+
this[BACKGROUND_TASKS] = tasks;
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
function findHeaders(headers, key) {
|
|
378
|
+
if (!headers || !key) return null;
|
|
379
|
+
key = key.toLowerCase();
|
|
380
|
+
for (const headerKey in headers) if (key === headerKey.toLowerCase()) return headers[headerKey];
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
//#endregion
|
|
385
|
+
export { ApplicationUnittest };
|
package/dist/bootstrap.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
import "./mock_httpclient-1EgQkMxi.js";
|
|
2
|
+
import "./supertest-BIds28yL.js";
|
|
3
|
+
import "./types-CjZ-JeCo.js";
|
|
4
|
+
import { ApplicationUnittest } from "./application-BPpvw5dg.js";
|
|
5
|
+
import "./cluster-RTfUwmUd.js";
|
|
6
|
+
import "./app-l47a-gqT.js";
|
|
7
|
+
import "./restore-BL0cE0KG.js";
|
|
8
|
+
import { getBootstrapApp } from "./app_handler-Gp5J9X-v.js";
|
|
9
|
+
import { proxyMock } from "./index-BtBMDZg8.js";
|
|
4
10
|
import { strict as assert } from "node:assert";
|
|
5
11
|
|
|
6
12
|
//#region src/bootstrap.d.ts
|
package/dist/bootstrap.js
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import "./mock_http_server-De0r07gr.js";
|
|
2
|
+
import { getEggOptions } from "./utils-MhP_Krx1.js";
|
|
3
|
+
import "./supertest-Cg412Los.js";
|
|
4
|
+
import "./format_options-LRnReKde.js";
|
|
5
|
+
import "./cluster-CA_hr9eV.js";
|
|
6
|
+
import "./context-BIduDy1M.js";
|
|
7
|
+
import "./mock_custom_loader-f_tHRojG.js";
|
|
8
|
+
import "./mock_agent-CxT7Q0_N.js";
|
|
9
|
+
import "./mock_httpclient-B6UVI7GS.js";
|
|
10
|
+
import "./types-CPNMyF89.js";
|
|
11
|
+
import "./application-LrKwuOKn.js";
|
|
12
|
+
import "./agent-DZ_fHoxJ.js";
|
|
13
|
+
import "./app-6Bn3F5Uw.js";
|
|
14
|
+
import "./restore-CVQYXquh.js";
|
|
15
|
+
import "./util-BpppqTXv.js";
|
|
16
|
+
import "./app-CoypamK1.js";
|
|
17
|
+
import "./agent-BKYkjoCx.js";
|
|
18
|
+
import "./agent_handler-DeUtoRVT.js";
|
|
19
|
+
import { getBootstrapApp, setupApp } from "./app_handler-CkiK6fyC.js";
|
|
20
|
+
import { proxyMock, src_default } from "./src-D22EjdGo.js";
|
|
4
21
|
import { strict as assert } from "node:assert";
|
|
5
22
|
import path from "node:path";
|
|
6
23
|
import { readJSONSync } from "utility";
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { rimrafSync, sleep } from "./utils-MhP_Krx1.js";
|
|
2
|
+
import { request } from "./supertest-Cg412Los.js";
|
|
3
|
+
import { formatOptions } from "./format_options-LRnReKde.js";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { debuglog } from "node:util";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import childProcess from "node:child_process";
|
|
8
|
+
import { once } from "node:events";
|
|
9
|
+
import { existsSync } from "node:fs";
|
|
10
|
+
import { Coffee } from "coffee";
|
|
11
|
+
import { Ready } from "get-ready";
|
|
12
|
+
|
|
13
|
+
//#region src/lib/cluster.ts
|
|
14
|
+
const debug = debuglog("egg/mock/lib/cluster");
|
|
15
|
+
const clusters = /* @__PURE__ */ new Map();
|
|
16
|
+
globalThis.eggMockMasterPort = 17e3 + process.pid % 1e3;
|
|
17
|
+
let serverBin = path.join(import.meta.dirname, "start-cluster.js");
|
|
18
|
+
if (!existsSync(serverBin)) serverBin = path.join(import.meta.dirname, "start-cluster.ts");
|
|
19
|
+
let requestCallFunctionFile = path.join(import.meta.dirname, "request_call_function.js");
|
|
20
|
+
if (!existsSync(requestCallFunctionFile)) requestCallFunctionFile = path.join(import.meta.dirname, "request_call_function.ts");
|
|
21
|
+
/**
|
|
22
|
+
* A cluster version of egg.Application, you can test with supertest
|
|
23
|
+
* @example
|
|
24
|
+
* ```js
|
|
25
|
+
* const mm = require('mm');
|
|
26
|
+
* const request = require('supertest');
|
|
27
|
+
*
|
|
28
|
+
* describe('ClusterApplication', () => {
|
|
29
|
+
* let app;
|
|
30
|
+
* before(function (done) {
|
|
31
|
+
* app = mm.cluster({ baseDir });
|
|
32
|
+
* app.ready(done);
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* after(function () {
|
|
36
|
+
* app.close();
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* it('should 200', function (done) {
|
|
40
|
+
* request(app.callback())
|
|
41
|
+
* .get('/')
|
|
42
|
+
* .expect(200, done);
|
|
43
|
+
* });
|
|
44
|
+
* });
|
|
45
|
+
*/
|
|
46
|
+
var ClusterApplication = class extends Coffee {
|
|
47
|
+
options;
|
|
48
|
+
port;
|
|
49
|
+
baseDir;
|
|
50
|
+
closed;
|
|
51
|
+
_address;
|
|
52
|
+
/**
|
|
53
|
+
* @class
|
|
54
|
+
* @param {Object} options
|
|
55
|
+
* - {String} baseDir - The directory of the application
|
|
56
|
+
* - {Object} plugins - Custom you plugins
|
|
57
|
+
* - {String} framework - The directory of the egg framework
|
|
58
|
+
* - {Boolean} [cache=true] - Cache application based on baseDir
|
|
59
|
+
* - {Boolean} [coverage=true] - Switch on process coverage, but it'll be slower
|
|
60
|
+
* - {Boolean} [clean=true] - Remove $baseDir/logs
|
|
61
|
+
* - {Object} [opt] - opt pass to coffee, such as { execArgv: ['--debug'] }
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
constructor(options) {
|
|
65
|
+
const opt = options.opt;
|
|
66
|
+
delete options.opt;
|
|
67
|
+
options.port = options.port ?? ++globalThis.eggMockMasterPort;
|
|
68
|
+
if (!options.workers) options.workers = 1;
|
|
69
|
+
const args = [JSON.stringify(options)];
|
|
70
|
+
debug("fork %s, args: %s, opt: %j", serverBin, args.join(" "), opt);
|
|
71
|
+
super({
|
|
72
|
+
method: "fork",
|
|
73
|
+
cmd: serverBin,
|
|
74
|
+
args,
|
|
75
|
+
opt
|
|
76
|
+
});
|
|
77
|
+
Ready.mixin(this);
|
|
78
|
+
this.port = options.port;
|
|
79
|
+
this.baseDir = options.baseDir;
|
|
80
|
+
this.debug(process.env.DEBUG ? 0 : 2);
|
|
81
|
+
if (options.coverage === false) this.coverage(false);
|
|
82
|
+
process.nextTick(() => {
|
|
83
|
+
this.proc.on("message", (msg) => {
|
|
84
|
+
switch (msg && msg.action ? msg.action : msg) {
|
|
85
|
+
case "egg-ready":
|
|
86
|
+
debug("on message egg-ready %o", msg);
|
|
87
|
+
this._address = msg.data.address;
|
|
88
|
+
this.emit("close", 0);
|
|
89
|
+
break;
|
|
90
|
+
case "app-worker-died":
|
|
91
|
+
case "agent-worker-died":
|
|
92
|
+
this.emit("close", 1);
|
|
93
|
+
break;
|
|
94
|
+
default: break;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
this.end(() => this.ready(true));
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* the process that forked
|
|
102
|
+
* @member {ChildProcess}
|
|
103
|
+
*/
|
|
104
|
+
get process() {
|
|
105
|
+
return this.proc;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Compatible API for supertest
|
|
109
|
+
*/
|
|
110
|
+
callback() {
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Compatible API for supertest
|
|
115
|
+
* @member {String} url
|
|
116
|
+
* @private
|
|
117
|
+
*/
|
|
118
|
+
get url() {
|
|
119
|
+
if (this._address) return this._address;
|
|
120
|
+
return "http://127.0.0.1:" + this.port;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Compatible API for supertest
|
|
124
|
+
*/
|
|
125
|
+
address() {
|
|
126
|
+
return {
|
|
127
|
+
port: this.port,
|
|
128
|
+
address: this._address
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Compatible API for supertest
|
|
133
|
+
*/
|
|
134
|
+
listen() {
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* kill the process
|
|
139
|
+
*/
|
|
140
|
+
async close() {
|
|
141
|
+
this.closed = true;
|
|
142
|
+
const proc = this.proc;
|
|
143
|
+
const baseDir = this.baseDir;
|
|
144
|
+
if (proc?.connected) {
|
|
145
|
+
proc.kill("SIGTERM");
|
|
146
|
+
await once(proc, "exit");
|
|
147
|
+
}
|
|
148
|
+
clusters.delete(baseDir);
|
|
149
|
+
debug("delete cluster cache %s, remain %s", baseDir, [...clusters.keys()]);
|
|
150
|
+
if (os.platform() === "win32") await sleep(1e3);
|
|
151
|
+
}
|
|
152
|
+
get isClosed() {
|
|
153
|
+
return this.closed;
|
|
154
|
+
}
|
|
155
|
+
get router() {
|
|
156
|
+
const self = this;
|
|
157
|
+
return { pathFor(url) {
|
|
158
|
+
return self._callFunctionOnAppWorker("pathFor", [url], "router", true);
|
|
159
|
+
} };
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* get app[property] value in app worker
|
|
163
|
+
*/
|
|
164
|
+
getAppInstanceProperty(property) {
|
|
165
|
+
return this._callFunctionOnAppWorker("__getter__", [], property, true);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* collection logger message, then can be use on `expectLog()`
|
|
169
|
+
* it's different from `app.expectLog()`, only support string params.
|
|
170
|
+
*
|
|
171
|
+
* @param {String} [logger] - logger instance name, default is `logger`
|
|
172
|
+
* @function ClusterApplication#expectLog
|
|
173
|
+
*/
|
|
174
|
+
mockLog(logger) {
|
|
175
|
+
logger = logger ?? "logger";
|
|
176
|
+
this._callFunctionOnAppWorker("mockLog", [logger], null, true);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* expect str in the logger
|
|
180
|
+
* it's different from `app.expectLog()`, only support string params.
|
|
181
|
+
*
|
|
182
|
+
* @param {String} str - test str
|
|
183
|
+
* @param {String} [logger] - logger instance name, default is `logger`
|
|
184
|
+
* @function ClusterApplication#expectLog
|
|
185
|
+
*/
|
|
186
|
+
expectLog(str, logger) {
|
|
187
|
+
logger = logger ?? "logger";
|
|
188
|
+
this._callFunctionOnAppWorker("expectLog", [str, logger], null, true);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* not expect str in the logger
|
|
192
|
+
* it's different from `app.notExpectLog()`, only support string params.
|
|
193
|
+
*
|
|
194
|
+
* @param {String} str - test str
|
|
195
|
+
* @param {String} [logger] - logger instance name, default is `logger`
|
|
196
|
+
* @function ClusterApplication#notExpectLog
|
|
197
|
+
*/
|
|
198
|
+
notExpectLog(str, logger) {
|
|
199
|
+
logger = logger ?? "logger";
|
|
200
|
+
this._callFunctionOnAppWorker("notExpectLog", [str, logger], null, true);
|
|
201
|
+
}
|
|
202
|
+
httpRequest() {
|
|
203
|
+
return request(this);
|
|
204
|
+
}
|
|
205
|
+
_callFunctionOnAppWorker(method, args = [], property = void 0, needResult = false) {
|
|
206
|
+
for (let i = 0; i < args.length; i++) {
|
|
207
|
+
const arg = args[i];
|
|
208
|
+
if (typeof arg === "function") args[i] = {
|
|
209
|
+
__egg_mock_type: "function",
|
|
210
|
+
value: arg.toString()
|
|
211
|
+
};
|
|
212
|
+
else if (arg instanceof Error) {
|
|
213
|
+
const errObject = {
|
|
214
|
+
__egg_mock_type: "error",
|
|
215
|
+
name: arg.name,
|
|
216
|
+
message: arg.message,
|
|
217
|
+
stack: arg.stack
|
|
218
|
+
};
|
|
219
|
+
for (const key in arg) if (key !== "name" && key !== "message" && key !== "stack") errObject[key] = arg[key];
|
|
220
|
+
args[i] = errObject;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const data = {
|
|
224
|
+
port: this.port,
|
|
225
|
+
method,
|
|
226
|
+
args,
|
|
227
|
+
property,
|
|
228
|
+
needResult
|
|
229
|
+
};
|
|
230
|
+
const child = childProcess.spawnSync(process.execPath, [requestCallFunctionFile, JSON.stringify(data)], { stdio: "pipe" });
|
|
231
|
+
let result;
|
|
232
|
+
if (child.stdout && child.stdout.length > 0) if (needResult) result = JSON.parse(child.stdout.toString());
|
|
233
|
+
else console.error(child.stdout.toString());
|
|
234
|
+
if (child.status !== 0) throw new Error(child.stderr.toString());
|
|
235
|
+
if (child.error) throw child.error;
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
function createCluster(initOptions) {
|
|
240
|
+
const options = formatOptions(initOptions);
|
|
241
|
+
if (options.cache && clusters.has(options.baseDir)) {
|
|
242
|
+
const clusterApp$1 = clusters.get(options.baseDir);
|
|
243
|
+
if (!clusterApp$1.isClosed) return clusterApp$1;
|
|
244
|
+
clusters.delete(options.baseDir);
|
|
245
|
+
}
|
|
246
|
+
if (options.clean !== false) {
|
|
247
|
+
const logDir = path.join(options.baseDir, "logs");
|
|
248
|
+
try {
|
|
249
|
+
rimrafSync(logDir);
|
|
250
|
+
} catch (err) {
|
|
251
|
+
console.error(`remove log dir ${logDir} failed: ${err.stack}`);
|
|
252
|
+
}
|
|
253
|
+
const runDir = path.join(options.baseDir, "run");
|
|
254
|
+
try {
|
|
255
|
+
rimrafSync(runDir);
|
|
256
|
+
} catch (err) {
|
|
257
|
+
console.error(`remove run dir ${runDir} failed: ${err.stack}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
let clusterApp = new ClusterApplication(options);
|
|
261
|
+
clusterApp = new Proxy(clusterApp, { get(target, prop) {
|
|
262
|
+
debug("proxy handler.get %s", prop);
|
|
263
|
+
const method = prop;
|
|
264
|
+
if (typeof method === "string" && /^mock\w+$/.test(method) && target[method] === void 0) return function mockProxy(...args) {
|
|
265
|
+
return target._callFunctionOnAppWorker(method, args, null, true);
|
|
266
|
+
};
|
|
267
|
+
return target[prop];
|
|
268
|
+
} });
|
|
269
|
+
clusters.set(options.baseDir, clusterApp);
|
|
270
|
+
return clusterApp;
|
|
271
|
+
}
|
|
272
|
+
async function restore() {
|
|
273
|
+
for (const clusterApp of clusters.values()) await clusterApp.mockRestore();
|
|
274
|
+
}
|
|
275
|
+
process.on("exit", () => {
|
|
276
|
+
for (const clusterApp of clusters.values()) {
|
|
277
|
+
debug("on exit close clusterApp, port: %s", clusterApp.port);
|
|
278
|
+
clusterApp.close();
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
//#endregion
|
|
283
|
+
export { ClusterApplication, createCluster, restore };
|