@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.
Files changed (78) hide show
  1. package/dist/agent-BKYkjoCx.js +111 -0
  2. package/dist/agent-CHCe8tnW.d.ts +24 -0
  3. package/dist/agent-DZ_fHoxJ.js +48 -0
  4. package/dist/agent_handler-DeUtoRVT.js +26 -0
  5. package/dist/app/extend/agent.d.ts +1 -1
  6. package/dist/app/extend/agent.js +3 -46
  7. package/dist/app/extend/application.d.ts +4 -174
  8. package/dist/app/extend/application.js +7 -383
  9. package/dist/app-6Bn3F5Uw.js +252 -0
  10. package/dist/app-CoypamK1.js +99 -0
  11. package/dist/app-l47a-gqT.d.ts +7 -0
  12. package/dist/app_handler-CkiK6fyC.js +55 -0
  13. package/dist/app_handler-Gp5J9X-v.d.ts +9 -0
  14. package/dist/application-BPpvw5dg.d.ts +175 -0
  15. package/dist/application-LrKwuOKn.js +385 -0
  16. package/dist/bootstrap.d.ts +9 -3
  17. package/dist/bootstrap.js +20 -3
  18. package/dist/cluster-CA_hr9eV.js +283 -0
  19. package/dist/cluster-RTfUwmUd.d.ts +126 -0
  20. package/dist/context-BIduDy1M.js +10 -0
  21. package/dist/format_options-LRnReKde.js +65 -0
  22. package/dist/index-BtBMDZg8.d.ts +78 -0
  23. package/dist/index.d.ts +10 -81
  24. package/dist/index.js +20 -45
  25. package/dist/inject_context-BxpcF-ds.js +106 -0
  26. package/dist/inject_mocha-BVf8v6El.js +38 -0
  27. package/dist/inject_mocha-Daf1Aj8M.d.ts +1 -0
  28. package/dist/inject_mocha.d.ts +1 -1
  29. package/dist/inject_mocha.js +21 -32
  30. package/dist/lib/agent_handler.d.ts +4 -1
  31. package/dist/lib/agent_handler.js +7 -24
  32. package/dist/lib/app.d.ts +5 -6
  33. package/dist/lib/app.js +12 -252
  34. package/dist/lib/app_handler.d.ts +5 -8
  35. package/dist/lib/app_handler.js +19 -54
  36. package/dist/lib/cluster.d.ts +5 -125
  37. package/dist/lib/cluster.js +5 -281
  38. package/dist/lib/context.js +1 -8
  39. package/dist/lib/format_options.d.ts +2 -2
  40. package/dist/lib/format_options.js +2 -63
  41. package/dist/lib/inject_context.js +20 -104
  42. package/dist/lib/mock_agent.js +1 -43
  43. package/dist/lib/mock_custom_loader.js +1 -33
  44. package/dist/lib/mock_http_server.js +1 -15
  45. package/dist/lib/mock_httpclient.d.ts +1 -39
  46. package/dist/lib/mock_httpclient.js +2 -108
  47. package/dist/lib/parallel/agent.d.ts +4 -23
  48. package/dist/lib/parallel/agent.js +6 -109
  49. package/dist/lib/parallel/app.d.ts +3 -2
  50. package/dist/lib/parallel/app.js +7 -97
  51. package/dist/lib/parallel/util.d.ts +1 -5
  52. package/dist/lib/parallel/util.js +2 -57
  53. package/dist/lib/restore.d.ts +1 -3
  54. package/dist/lib/restore.js +7 -14
  55. package/dist/lib/supertest.d.ts +1 -15
  56. package/dist/lib/supertest.js +3 -37
  57. package/dist/lib/types.d.ts +3 -76
  58. package/dist/lib/types.js +2 -0
  59. package/dist/lib/utils.js +1 -38
  60. package/dist/mock_agent-CxT7Q0_N.js +45 -0
  61. package/dist/mock_custom_loader-f_tHRojG.js +35 -0
  62. package/dist/mock_http_server-De0r07gr.js +17 -0
  63. package/dist/mock_httpclient-1EgQkMxi.d.ts +40 -0
  64. package/dist/mock_httpclient-B6UVI7GS.js +110 -0
  65. package/dist/register.d.ts +2 -0
  66. package/dist/register.js +22 -4
  67. package/dist/restore-BL0cE0KG.d.ts +4 -0
  68. package/dist/restore-CVQYXquh.js +16 -0
  69. package/dist/src-D22EjdGo.js +47 -0
  70. package/dist/supertest-BIds28yL.d.ts +16 -0
  71. package/dist/supertest-Cg412Los.js +39 -0
  72. package/dist/types-CPNMyF89.js +1 -0
  73. package/dist/types-CjZ-JeCo.d.ts +75 -0
  74. package/dist/util-1xYBaaoh.d.ts +6 -0
  75. package/dist/util-BpppqTXv.js +59 -0
  76. package/dist/utils-MhP_Krx1.js +40 -0
  77. package/package.json +7 -9
  78. 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 };
@@ -1,6 +1,12 @@
1
- import ApplicationUnittest from "./app/extend/application.js";
2
- import { getBootstrapApp } from "./lib/app_handler.js";
3
- import proxyMock from "./index.js";
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 { getEggOptions } from "./lib/utils.js";
2
- import { getBootstrapApp, setupApp } from "./lib/app_handler.js";
3
- import src_default, { mm as proxyMock } from "./index.js";
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 };