@eggjs/mock 6.0.0-beta.3
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 +547 -0
- package/README.zh_CN.md +512 -0
- package/dist/commonjs/app/extend/agent.d.ts +33 -0
- package/dist/commonjs/app/extend/agent.js +49 -0
- package/dist/commonjs/app/extend/application.d.ts +175 -0
- package/dist/commonjs/app/extend/application.js +448 -0
- package/dist/commonjs/app/middleware/cluster_app_mock.d.ts +3 -0
- package/dist/commonjs/app/middleware/cluster_app_mock.js +100 -0
- package/dist/commonjs/app.d.ts +6 -0
- package/dist/commonjs/app.js +20 -0
- package/dist/commonjs/bootstrap.d.ts +4 -0
- package/dist/commonjs/bootstrap.js +58 -0
- package/dist/commonjs/index.d.ts +76 -0
- package/dist/commonjs/index.js +99 -0
- package/dist/commonjs/index.test-d.d.ts +1 -0
- package/dist/commonjs/index.test-d.js +43 -0
- package/dist/commonjs/lib/agent_handler.d.ts +3 -0
- package/dist/commonjs/lib/agent_handler.js +28 -0
- package/dist/commonjs/lib/app.d.ts +28 -0
- package/dist/commonjs/lib/app.js +303 -0
- package/dist/commonjs/lib/app_handler.d.ts +5 -0
- package/dist/commonjs/lib/app_handler.js +67 -0
- package/dist/commonjs/lib/cluster.d.ts +114 -0
- package/dist/commonjs/lib/cluster.js +337 -0
- package/dist/commonjs/lib/context.d.ts +1 -0
- package/dist/commonjs/lib/context.js +16 -0
- package/dist/commonjs/lib/format_options.d.ts +5 -0
- package/dist/commonjs/lib/format_options.js +100 -0
- package/dist/commonjs/lib/inject_context.d.ts +6 -0
- package/dist/commonjs/lib/inject_context.js +132 -0
- package/dist/commonjs/lib/mock_agent.d.ts +5 -0
- package/dist/commonjs/lib/mock_agent.js +49 -0
- package/dist/commonjs/lib/mock_custom_loader.d.ts +1 -0
- package/dist/commonjs/lib/mock_custom_loader.js +37 -0
- package/dist/commonjs/lib/mock_http_server.d.ts +2 -0
- package/dist/commonjs/lib/mock_http_server.js +24 -0
- package/dist/commonjs/lib/mock_httpclient.d.ts +35 -0
- package/dist/commonjs/lib/mock_httpclient.js +147 -0
- package/dist/commonjs/lib/parallel/agent.d.ts +20 -0
- package/dist/commonjs/lib/parallel/agent.js +125 -0
- package/dist/commonjs/lib/parallel/app.d.ts +20 -0
- package/dist/commonjs/lib/parallel/app.js +115 -0
- package/dist/commonjs/lib/parallel/util.d.ts +3 -0
- package/dist/commonjs/lib/parallel/util.js +77 -0
- package/dist/commonjs/lib/prerequire.d.ts +1 -0
- package/dist/commonjs/lib/prerequire.js +26 -0
- package/dist/commonjs/lib/request_call_function.d.ts +1 -0
- package/dist/commonjs/lib/request_call_function.js +52 -0
- package/dist/commonjs/lib/restore.d.ts +1 -0
- package/dist/commonjs/lib/restore.js +16 -0
- package/dist/commonjs/lib/start-cluster.d.ts +2 -0
- package/dist/commonjs/lib/start-cluster.js +23 -0
- package/dist/commonjs/lib/supertest.d.ts +11 -0
- package/dist/commonjs/lib/supertest.js +48 -0
- package/dist/commonjs/lib/tmp/empty.d.ts +1 -0
- package/dist/commonjs/lib/tmp/empty.js +3 -0
- package/dist/commonjs/lib/types.d.ts +60 -0
- package/dist/commonjs/lib/types.js +3 -0
- package/dist/commonjs/lib/utils.d.ts +9 -0
- package/dist/commonjs/lib/utils.js +80 -0
- package/dist/commonjs/package.json +3 -0
- package/dist/commonjs/register.d.ts +8 -0
- package/dist/commonjs/register.js +80 -0
- package/dist/esm/app/extend/agent.d.ts +33 -0
- package/dist/esm/app/extend/agent.js +46 -0
- package/dist/esm/app/extend/application.d.ts +175 -0
- package/dist/esm/app/extend/application.js +442 -0
- package/dist/esm/app/middleware/cluster_app_mock.d.ts +3 -0
- package/dist/esm/app/middleware/cluster_app_mock.js +98 -0
- package/dist/esm/app.d.ts +6 -0
- package/dist/esm/app.js +17 -0
- package/dist/esm/bootstrap.d.ts +4 -0
- package/dist/esm/bootstrap.js +16 -0
- package/dist/esm/index.d.ts +76 -0
- package/dist/esm/index.js +90 -0
- package/dist/esm/index.test-d.d.ts +1 -0
- package/dist/esm/index.test-d.js +42 -0
- package/dist/esm/lib/agent_handler.d.ts +3 -0
- package/dist/esm/lib/agent_handler.js +24 -0
- package/dist/esm/lib/app.d.ts +28 -0
- package/dist/esm/lib/app.js +295 -0
- package/dist/esm/lib/app_handler.d.ts +5 -0
- package/dist/esm/lib/app_handler.js +61 -0
- package/dist/esm/lib/cluster.d.ts +114 -0
- package/dist/esm/lib/cluster.js +328 -0
- package/dist/esm/lib/context.d.ts +1 -0
- package/dist/esm/lib/context.js +13 -0
- package/dist/esm/lib/format_options.d.ts +5 -0
- package/dist/esm/lib/format_options.js +94 -0
- package/dist/esm/lib/inject_context.d.ts +6 -0
- package/dist/esm/lib/inject_context.js +126 -0
- package/dist/esm/lib/mock_agent.d.ts +5 -0
- package/dist/esm/lib/mock_agent.js +45 -0
- package/dist/esm/lib/mock_custom_loader.d.ts +1 -0
- package/dist/esm/lib/mock_custom_loader.js +34 -0
- package/dist/esm/lib/mock_http_server.d.ts +2 -0
- package/dist/esm/lib/mock_http_server.js +18 -0
- package/dist/esm/lib/mock_httpclient.d.ts +35 -0
- package/dist/esm/lib/mock_httpclient.js +144 -0
- package/dist/esm/lib/parallel/agent.d.ts +20 -0
- package/dist/esm/lib/parallel/agent.js +117 -0
- package/dist/esm/lib/parallel/app.d.ts +20 -0
- package/dist/esm/lib/parallel/app.js +110 -0
- package/dist/esm/lib/parallel/util.d.ts +3 -0
- package/dist/esm/lib/parallel/util.js +73 -0
- package/dist/esm/lib/prerequire.d.ts +1 -0
- package/dist/esm/lib/prerequire.js +25 -0
- package/dist/esm/lib/request_call_function.d.ts +1 -0
- package/dist/esm/lib/request_call_function.js +47 -0
- package/dist/esm/lib/restore.d.ts +1 -0
- package/dist/esm/lib/restore.js +13 -0
- package/dist/esm/lib/start-cluster.d.ts +2 -0
- package/dist/esm/lib/start-cluster.js +18 -0
- package/dist/esm/lib/supertest.d.ts +11 -0
- package/dist/esm/lib/supertest.js +40 -0
- package/dist/esm/lib/tmp/empty.d.ts +1 -0
- package/dist/esm/lib/tmp/empty.js +2 -0
- package/dist/esm/lib/types.d.ts +60 -0
- package/dist/esm/lib/types.js +2 -0
- package/dist/esm/lib/utils.d.ts +9 -0
- package/dist/esm/lib/utils.js +69 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/register.d.ts +8 -0
- package/dist/esm/register.js +75 -0
- package/dist/package.json +4 -0
- package/package.json +131 -0
- package/src/app/extend/agent.ts +56 -0
- package/src/app/extend/application.ts +512 -0
- package/src/app/middleware/cluster_app_mock.ts +101 -0
- package/src/app.ts +18 -0
- package/src/bootstrap.ts +25 -0
- package/src/index.d.ts +193 -0
- package/src/index.test-d.ts +47 -0
- package/src/index.ts +110 -0
- package/src/lib/agent_handler.ts +28 -0
- package/src/lib/app.ts +313 -0
- package/src/lib/app_handler.ts +69 -0
- package/src/lib/cluster.ts +363 -0
- package/src/lib/context.ts +14 -0
- package/src/lib/format_options.ts +103 -0
- package/src/lib/inject_context.ts +134 -0
- package/src/lib/mock_agent.ts +57 -0
- package/src/lib/mock_custom_loader.ts +36 -0
- package/src/lib/mock_http_server.ts +19 -0
- package/src/lib/mock_httpclient.ts +181 -0
- package/src/lib/parallel/agent.ts +128 -0
- package/src/lib/parallel/app.ts +123 -0
- package/src/lib/parallel/util.ts +66 -0
- package/src/lib/prerequire.ts +25 -0
- package/src/lib/request_call_function.ts +49 -0
- package/src/lib/restore.ts +14 -0
- package/src/lib/start-cluster.ts +23 -0
- package/src/lib/supertest.ts +45 -0
- package/src/lib/tmp/.gitkeep +0 -0
- package/src/lib/tmp/empty.ts +0 -0
- package/src/lib/types.ts +72 -0
- package/src/lib/utils.ts +82 -0
- package/src/register.ts +80 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { IncomingMessage } from 'node:http';
|
|
2
|
+
import { mock, restore } from 'mm';
|
|
3
|
+
import type { HttpClient } from 'urllib';
|
|
4
|
+
import { Logger } from 'egg-logger';
|
|
5
|
+
import { EggCore, EggCoreOptions, ContextDelegation } from '@eggjs/core';
|
|
6
|
+
import { MockResultFunction, MockResultOptions, MockHttpClientMethod } from '../../lib/mock_httpclient.js';
|
|
7
|
+
import { EggTestRequest } from '../../lib/supertest.js';
|
|
8
|
+
import { MockOptions } from '../../lib/types.js';
|
|
9
|
+
export interface MockContextOptions {
|
|
10
|
+
/**
|
|
11
|
+
* mock ctxStorage or not, default is `true`
|
|
12
|
+
*/
|
|
13
|
+
mockCtxStorage?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* reuse ctxStorage or not, default is `true`
|
|
16
|
+
*/
|
|
17
|
+
reuseCtxStorage?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface MockContextData {
|
|
20
|
+
headers?: Record<string, string | string[]>;
|
|
21
|
+
[key: string]: any;
|
|
22
|
+
}
|
|
23
|
+
export interface MockContextDelegation extends ContextDelegation {
|
|
24
|
+
service: any;
|
|
25
|
+
}
|
|
26
|
+
export default abstract class ApplicationUnittest extends EggCore {
|
|
27
|
+
[key: string]: any;
|
|
28
|
+
options: MockOptions & EggCoreOptions;
|
|
29
|
+
_mockHttpClient: MockHttpClientMethod;
|
|
30
|
+
logger: Logger;
|
|
31
|
+
coreLogger: Logger;
|
|
32
|
+
abstract getLogger(name: string): Logger;
|
|
33
|
+
httpClient: HttpClient;
|
|
34
|
+
httpclient: HttpClient;
|
|
35
|
+
/**
|
|
36
|
+
* mock Context
|
|
37
|
+
* @function App#mockContext
|
|
38
|
+
* @param {Object} data - ctx data
|
|
39
|
+
* @param {Object} [options] - mock ctx options
|
|
40
|
+
* @example
|
|
41
|
+
* ```js
|
|
42
|
+
* const ctx = app.mockContext({
|
|
43
|
+
* user: {
|
|
44
|
+
* name: 'Jason'
|
|
45
|
+
* }
|
|
46
|
+
* });
|
|
47
|
+
* console.log(ctx.user.name); // Jason
|
|
48
|
+
*
|
|
49
|
+
* // controller
|
|
50
|
+
* module.exports = function*() {
|
|
51
|
+
* this.body = this.user.name;
|
|
52
|
+
* };
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
mockContext(data?: MockContextData, options?: MockContextOptions): MockContextDelegation;
|
|
56
|
+
mockContextScope(fn: (ctx?: MockContextDelegation) => Promise<any>, data?: MockContextData): Promise<any>;
|
|
57
|
+
/**
|
|
58
|
+
* mock cookie session
|
|
59
|
+
* @function App#mockSession
|
|
60
|
+
* @param {Object} data - session object
|
|
61
|
+
*/
|
|
62
|
+
mockSession(data: any): this;
|
|
63
|
+
/**
|
|
64
|
+
* Mock service
|
|
65
|
+
* @function App#mockService
|
|
66
|
+
* @param {String} service - name
|
|
67
|
+
* @param {String} methodName - method
|
|
68
|
+
* @param {Object|Function|Error} fn - mock you data
|
|
69
|
+
*/
|
|
70
|
+
mockService(service: string | any, methodName: string, fn: any): this;
|
|
71
|
+
/**
|
|
72
|
+
* mock service that return error
|
|
73
|
+
* @function App#mockServiceError
|
|
74
|
+
* @param {String} service - name
|
|
75
|
+
* @param {String} methodName - method
|
|
76
|
+
* @param {Error} [err] - error information
|
|
77
|
+
*/
|
|
78
|
+
mockServiceError(service: string | any, methodName: string, err?: string | Error): this;
|
|
79
|
+
_mockFn(obj: any, name: string, data: any): void;
|
|
80
|
+
/**
|
|
81
|
+
* mock request
|
|
82
|
+
* @function App#mockRequest
|
|
83
|
+
* @param {Request} req - mock request
|
|
84
|
+
*/
|
|
85
|
+
mockRequest(req: MockContextData): IncomingMessage;
|
|
86
|
+
/**
|
|
87
|
+
* mock cookies
|
|
88
|
+
* @function App#mockCookies
|
|
89
|
+
*/
|
|
90
|
+
mockCookies(cookies: Record<string, string | string[]>): this;
|
|
91
|
+
/**
|
|
92
|
+
* mock header
|
|
93
|
+
* @function App#mockHeaders
|
|
94
|
+
*/
|
|
95
|
+
mockHeaders(headers: Record<string, string | string[]>): this;
|
|
96
|
+
/**
|
|
97
|
+
* mock csrf
|
|
98
|
+
* @function App#mockCsrf
|
|
99
|
+
* @since 1.11
|
|
100
|
+
*/
|
|
101
|
+
mockCsrf(): this;
|
|
102
|
+
/**
|
|
103
|
+
* mock httpclient
|
|
104
|
+
* @alias mockHttpClient
|
|
105
|
+
* @function App#mockHttpclient
|
|
106
|
+
*/
|
|
107
|
+
mockHttpclient(mockUrl: string | RegExp, mockMethod: string | string[] | MockResultOptions | MockResultFunction, mockResult?: MockResultOptions | MockResultFunction | string): void;
|
|
108
|
+
/**
|
|
109
|
+
* mock httpclient
|
|
110
|
+
* @function App#mockHttpClient
|
|
111
|
+
*/
|
|
112
|
+
mockHttpClient(mockUrl: string | RegExp, mockMethod: string | string[] | MockResultOptions | MockResultFunction, mockResult?: MockResultOptions | MockResultFunction | string): void;
|
|
113
|
+
/**
|
|
114
|
+
* @deprecated Please use app.mockHttpClient instead of app.mockUrllib
|
|
115
|
+
*/
|
|
116
|
+
mockUrllib(mockUrl: string | RegExp, mockMethod: string | string[] | MockResultOptions | MockResultFunction, mockResult?: MockResultOptions | MockResultFunction | string): void;
|
|
117
|
+
/**
|
|
118
|
+
* get mock httpclient agent
|
|
119
|
+
* @function App#mockHttpclientAgent
|
|
120
|
+
*/
|
|
121
|
+
mockAgent(): import("urllib").MockAgent<import("urllib").MockAgent.Options>;
|
|
122
|
+
mockAgentRestore(): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* @see mm#restore
|
|
125
|
+
* @function App#mockRestore
|
|
126
|
+
*/
|
|
127
|
+
get mockRestore(): typeof restore;
|
|
128
|
+
/**
|
|
129
|
+
* @see mm
|
|
130
|
+
* @function App#mm
|
|
131
|
+
*/
|
|
132
|
+
get mm(): typeof mock;
|
|
133
|
+
/**
|
|
134
|
+
* override loadAgent
|
|
135
|
+
* @function App#loadAgent
|
|
136
|
+
*/
|
|
137
|
+
loadAgent(): void;
|
|
138
|
+
/**
|
|
139
|
+
* mock serverEnv
|
|
140
|
+
* @function App#mockEnv
|
|
141
|
+
* @param {String} env - serverEnv
|
|
142
|
+
*/
|
|
143
|
+
mockEnv(env: string): this;
|
|
144
|
+
/**
|
|
145
|
+
* http request helper
|
|
146
|
+
* @function App#httpRequest
|
|
147
|
+
* @return {SupertestRequest} req - supertest request
|
|
148
|
+
* @see https://github.com/visionmedia/supertest
|
|
149
|
+
*/
|
|
150
|
+
httpRequest(): EggTestRequest;
|
|
151
|
+
/**
|
|
152
|
+
* collection logger message, then can be use on `expectLog()`
|
|
153
|
+
* @param {String|Logger} [logger] - logger instance, default is `app.logger`
|
|
154
|
+
* @function App#mockLog
|
|
155
|
+
*/
|
|
156
|
+
mockLog(logger?: string | Logger): void;
|
|
157
|
+
__checkExpectLog(expectOrNot: boolean, str: string | RegExp, logger?: string | Logger): void;
|
|
158
|
+
/**
|
|
159
|
+
* expect str/regexp in the logger, if your server disk is slow, please call `mockLog()` first.
|
|
160
|
+
* @param {String|RegExp} str - test str or regexp
|
|
161
|
+
* @param {String|Logger} [logger] - logger instance, default is `ctx.logger`
|
|
162
|
+
* @function App#expectLog
|
|
163
|
+
*/
|
|
164
|
+
expectLog(str: string | RegExp, logger?: string | Logger): void;
|
|
165
|
+
/**
|
|
166
|
+
* not expect str/regexp in the logger, if your server disk is slow, please call `mockLog()` first.
|
|
167
|
+
* @param {String|RegExp} str - test str or regexp
|
|
168
|
+
* @param {String|Logger} [logger] - logger instance, default is `ctx.logger`
|
|
169
|
+
* @function App#notExpectLog
|
|
170
|
+
*/
|
|
171
|
+
notExpectLog(str: string | RegExp, logger?: string | Logger): void;
|
|
172
|
+
backgroundTasksFinished(): Promise<void>;
|
|
173
|
+
get _backgroundTasks(): Promise<any>[];
|
|
174
|
+
set _backgroundTasks(tasks: Promise<any>[]);
|
|
175
|
+
}
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import { debuglog } from 'node:util';
|
|
2
|
+
import http from 'node:http';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import assert from 'node:assert';
|
|
5
|
+
import mergeDescriptors from 'merge-descriptors';
|
|
6
|
+
import { isAsyncFunction, isObject } from 'is-type-of';
|
|
7
|
+
import { mock, restore } from 'mm';
|
|
8
|
+
import { Transport } from 'egg-logger';
|
|
9
|
+
import { EggCore } from '@eggjs/core';
|
|
10
|
+
import { getMockAgent, restoreMockAgent } from '../../lib/mock_agent.js';
|
|
11
|
+
import { createMockHttpClient, } from '../../lib/mock_httpclient.js';
|
|
12
|
+
import { request as supertestRequest } from '../../lib/supertest.js';
|
|
13
|
+
const debug = debuglog('@eggjs/mock/app/extend/application');
|
|
14
|
+
const ORIGIN_TYPES = Symbol('@eggjs/mock originTypes');
|
|
15
|
+
const BACKGROUND_TASKS = Symbol('Application#backgroundTasks');
|
|
16
|
+
const REUSED_CTX = Symbol('Context#reusedInSuite');
|
|
17
|
+
export default class ApplicationUnittest extends EggCore {
|
|
18
|
+
_mockHttpClient;
|
|
19
|
+
/**
|
|
20
|
+
* mock Context
|
|
21
|
+
* @function App#mockContext
|
|
22
|
+
* @param {Object} data - ctx data
|
|
23
|
+
* @param {Object} [options] - mock ctx options
|
|
24
|
+
* @example
|
|
25
|
+
* ```js
|
|
26
|
+
* const ctx = app.mockContext({
|
|
27
|
+
* user: {
|
|
28
|
+
* name: 'Jason'
|
|
29
|
+
* }
|
|
30
|
+
* });
|
|
31
|
+
* console.log(ctx.user.name); // Jason
|
|
32
|
+
*
|
|
33
|
+
* // controller
|
|
34
|
+
* module.exports = function*() {
|
|
35
|
+
* this.body = this.user.name;
|
|
36
|
+
* };
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
mockContext(data, options) {
|
|
40
|
+
data = data ?? {};
|
|
41
|
+
function mockRequest(req) {
|
|
42
|
+
for (const key in data?.headers) {
|
|
43
|
+
mock(req.headers, key, data.headers[key]);
|
|
44
|
+
mock(req.headers, key.toLowerCase(), data.headers[key]);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// try to use app.options.mockCtxStorage first
|
|
48
|
+
const mockCtxStorage = this.options.mockCtxStorage ?? true;
|
|
49
|
+
options = Object.assign({ mockCtxStorage }, options);
|
|
50
|
+
if ('_customMockContext' in this && typeof this._customMockContext === 'function') {
|
|
51
|
+
this._customMockContext(data);
|
|
52
|
+
}
|
|
53
|
+
// 使用者自定义mock,可以覆盖上面的 mock
|
|
54
|
+
for (const key in data) {
|
|
55
|
+
mock(this.context, key, data[key]);
|
|
56
|
+
}
|
|
57
|
+
const req = this.mockRequest(data);
|
|
58
|
+
const res = new http.ServerResponse(req);
|
|
59
|
+
if (options.reuseCtxStorage !== false) {
|
|
60
|
+
if (this.currentContext && !this.currentContext[REUSED_CTX]) {
|
|
61
|
+
mockRequest(this.currentContext.request.req);
|
|
62
|
+
this.currentContext[REUSED_CTX] = true;
|
|
63
|
+
return this.currentContext;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const ctx = this.createContext(req, res);
|
|
67
|
+
if (options.mockCtxStorage) {
|
|
68
|
+
mock(this.ctxStorage, 'getStore', () => ctx);
|
|
69
|
+
}
|
|
70
|
+
return ctx;
|
|
71
|
+
}
|
|
72
|
+
async mockContextScope(fn, data) {
|
|
73
|
+
const ctx = this.mockContext(data, {
|
|
74
|
+
mockCtxStorage: false,
|
|
75
|
+
reuseCtxStorage: false,
|
|
76
|
+
});
|
|
77
|
+
return await this.ctxStorage.run(ctx, async () => {
|
|
78
|
+
return await fn(ctx);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* mock cookie session
|
|
83
|
+
* @function App#mockSession
|
|
84
|
+
* @param {Object} data - session object
|
|
85
|
+
*/
|
|
86
|
+
mockSession(data) {
|
|
87
|
+
if (!data) {
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
if (isObject(data) && !('save' in data)) {
|
|
91
|
+
// keep session.save() work
|
|
92
|
+
Object.defineProperty(data, 'save', {
|
|
93
|
+
value: () => { },
|
|
94
|
+
enumerable: false,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
mock(this.context, 'session', data);
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Mock service
|
|
102
|
+
* @function App#mockService
|
|
103
|
+
* @param {String} service - name
|
|
104
|
+
* @param {String} methodName - method
|
|
105
|
+
* @param {Object|Function|Error} fn - mock you data
|
|
106
|
+
*/
|
|
107
|
+
mockService(service, methodName, fn) {
|
|
108
|
+
if (typeof service === 'string') {
|
|
109
|
+
const splits = service.split('.');
|
|
110
|
+
service = this.serviceClasses;
|
|
111
|
+
for (const key of splits) {
|
|
112
|
+
service = service[key];
|
|
113
|
+
}
|
|
114
|
+
service = service.prototype || service;
|
|
115
|
+
}
|
|
116
|
+
this._mockFn(service, methodName, fn);
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* mock service that return error
|
|
121
|
+
* @function App#mockServiceError
|
|
122
|
+
* @param {String} service - name
|
|
123
|
+
* @param {String} methodName - method
|
|
124
|
+
* @param {Error} [err] - error information
|
|
125
|
+
*/
|
|
126
|
+
mockServiceError(service, methodName, err) {
|
|
127
|
+
if (typeof err === 'string') {
|
|
128
|
+
err = new Error(err);
|
|
129
|
+
}
|
|
130
|
+
if (!err) {
|
|
131
|
+
// mockServiceError(service, methodName)
|
|
132
|
+
err = new Error(`mock ${methodName} error`);
|
|
133
|
+
}
|
|
134
|
+
this.mockService(service, methodName, err);
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
_mockFn(obj, name, data) {
|
|
138
|
+
const origin = obj[name];
|
|
139
|
+
assert(typeof origin === 'function', `property ${name} in original object must be function`);
|
|
140
|
+
// keep origin properties' type to support mock multi times
|
|
141
|
+
if (!obj[ORIGIN_TYPES])
|
|
142
|
+
obj[ORIGIN_TYPES] = {};
|
|
143
|
+
let type = obj[ORIGIN_TYPES][name];
|
|
144
|
+
if (!type) {
|
|
145
|
+
type = obj[ORIGIN_TYPES][name] = isAsyncFunction(origin) ? 'async' : 'sync';
|
|
146
|
+
}
|
|
147
|
+
if (typeof data === 'function') {
|
|
148
|
+
const fn = data;
|
|
149
|
+
// if original is async function
|
|
150
|
+
// but the mock function is normal function, need to change it return a promise
|
|
151
|
+
if (type === 'async' && !isAsyncFunction(fn)) {
|
|
152
|
+
mock(obj, name, function (...args) {
|
|
153
|
+
return new Promise(resolve => {
|
|
154
|
+
resolve(fn.apply(this, args));
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
mock(obj, name, fn);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (type === 'async') {
|
|
163
|
+
mock(obj, name, () => {
|
|
164
|
+
return new Promise((resolve, reject) => {
|
|
165
|
+
if (data instanceof Error)
|
|
166
|
+
return reject(data);
|
|
167
|
+
resolve(data);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
mock(obj, name, () => {
|
|
173
|
+
if (data instanceof Error) {
|
|
174
|
+
throw data;
|
|
175
|
+
}
|
|
176
|
+
return data;
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* mock request
|
|
181
|
+
* @function App#mockRequest
|
|
182
|
+
* @param {Request} req - mock request
|
|
183
|
+
*/
|
|
184
|
+
mockRequest(req) {
|
|
185
|
+
req = { ...req };
|
|
186
|
+
const headers = req.headers ?? {};
|
|
187
|
+
for (const key in req.headers) {
|
|
188
|
+
headers[key.toLowerCase()] = req.headers[key];
|
|
189
|
+
}
|
|
190
|
+
if (!headers['x-forwarded-for']) {
|
|
191
|
+
headers['x-forwarded-for'] = '127.0.0.1';
|
|
192
|
+
}
|
|
193
|
+
headers['x-mock-request-from'] = '@eggjs/mock';
|
|
194
|
+
req.headers = headers;
|
|
195
|
+
mergeDescriptors(req, {
|
|
196
|
+
query: {},
|
|
197
|
+
querystring: '',
|
|
198
|
+
host: '127.0.0.1',
|
|
199
|
+
hostname: '127.0.0.1',
|
|
200
|
+
protocol: 'http',
|
|
201
|
+
secure: 'false',
|
|
202
|
+
method: 'GET',
|
|
203
|
+
url: '/',
|
|
204
|
+
path: '/',
|
|
205
|
+
socket: {
|
|
206
|
+
remoteAddress: '127.0.0.1',
|
|
207
|
+
remotePort: 7001,
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
return req;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* mock cookies
|
|
214
|
+
* @function App#mockCookies
|
|
215
|
+
*/
|
|
216
|
+
mockCookies(cookies) {
|
|
217
|
+
if (!cookies) {
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
const createContext = this.createContext;
|
|
221
|
+
mock(this, 'createContext', function (req, res) {
|
|
222
|
+
const ctx = createContext.call(this, req, res);
|
|
223
|
+
const getCookie = ctx.cookies.get;
|
|
224
|
+
mock(ctx.cookies, 'get', function (key, opts) {
|
|
225
|
+
if (cookies[key]) {
|
|
226
|
+
return cookies[key];
|
|
227
|
+
}
|
|
228
|
+
return getCookie.call(this, key, opts);
|
|
229
|
+
});
|
|
230
|
+
return ctx;
|
|
231
|
+
});
|
|
232
|
+
return this;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* mock header
|
|
236
|
+
* @function App#mockHeaders
|
|
237
|
+
*/
|
|
238
|
+
mockHeaders(headers) {
|
|
239
|
+
if (!headers) {
|
|
240
|
+
return this;
|
|
241
|
+
}
|
|
242
|
+
const getHeader = this.request.get;
|
|
243
|
+
mock(this.request, 'get', function (field) {
|
|
244
|
+
const value = findHeaders(headers, field);
|
|
245
|
+
if (value)
|
|
246
|
+
return value;
|
|
247
|
+
return getHeader.call(this, field);
|
|
248
|
+
});
|
|
249
|
+
return this;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* mock csrf
|
|
253
|
+
* @function App#mockCsrf
|
|
254
|
+
* @since 1.11
|
|
255
|
+
*/
|
|
256
|
+
mockCsrf() {
|
|
257
|
+
mock(this.context, 'assertCSRF', () => { });
|
|
258
|
+
mock(this.context, 'assertCsrf', () => { });
|
|
259
|
+
return this;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* mock httpclient
|
|
263
|
+
* @alias mockHttpClient
|
|
264
|
+
* @function App#mockHttpclient
|
|
265
|
+
*/
|
|
266
|
+
mockHttpclient(mockUrl, mockMethod, mockResult) {
|
|
267
|
+
return this.mockHttpClient(mockUrl, mockMethod, mockResult);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* mock httpclient
|
|
271
|
+
* @function App#mockHttpClient
|
|
272
|
+
*/
|
|
273
|
+
mockHttpClient(mockUrl, mockMethod, mockResult) {
|
|
274
|
+
if (!this._mockHttpClient) {
|
|
275
|
+
this._mockHttpClient = createMockHttpClient(this);
|
|
276
|
+
}
|
|
277
|
+
return this._mockHttpClient(mockUrl, mockMethod, mockResult);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* @deprecated Please use app.mockHttpClient instead of app.mockUrllib
|
|
281
|
+
*/
|
|
282
|
+
mockUrllib(mockUrl, mockMethod, mockResult) {
|
|
283
|
+
this.deprecate('[@eggjs/mock] Please use app.mockHttpClient instead of app.mockUrllib');
|
|
284
|
+
return this.mockHttpClient(mockUrl, mockMethod, mockResult);
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* get mock httpclient agent
|
|
288
|
+
* @function App#mockHttpclientAgent
|
|
289
|
+
*/
|
|
290
|
+
mockAgent() {
|
|
291
|
+
return getMockAgent(this);
|
|
292
|
+
}
|
|
293
|
+
async mockAgentRestore() {
|
|
294
|
+
await restoreMockAgent();
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* @see mm#restore
|
|
298
|
+
* @function App#mockRestore
|
|
299
|
+
*/
|
|
300
|
+
get mockRestore() {
|
|
301
|
+
return restore;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* @see mm
|
|
305
|
+
* @function App#mm
|
|
306
|
+
*/
|
|
307
|
+
get mm() {
|
|
308
|
+
return mock;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* override loadAgent
|
|
312
|
+
* @function App#loadAgent
|
|
313
|
+
*/
|
|
314
|
+
loadAgent() { }
|
|
315
|
+
/**
|
|
316
|
+
* mock serverEnv
|
|
317
|
+
* @function App#mockEnv
|
|
318
|
+
* @param {String} env - serverEnv
|
|
319
|
+
*/
|
|
320
|
+
mockEnv(env) {
|
|
321
|
+
mock(this.config, 'env', env);
|
|
322
|
+
mock(this.config, 'serverEnv', env);
|
|
323
|
+
debug('mock env: %o', env);
|
|
324
|
+
return this;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* http request helper
|
|
328
|
+
* @function App#httpRequest
|
|
329
|
+
* @return {SupertestRequest} req - supertest request
|
|
330
|
+
* @see https://github.com/visionmedia/supertest
|
|
331
|
+
*/
|
|
332
|
+
httpRequest() {
|
|
333
|
+
return supertestRequest(this);
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* collection logger message, then can be use on `expectLog()`
|
|
337
|
+
* @param {String|Logger} [logger] - logger instance, default is `app.logger`
|
|
338
|
+
* @function App#mockLog
|
|
339
|
+
*/
|
|
340
|
+
mockLog(logger) {
|
|
341
|
+
logger = logger ?? this.logger;
|
|
342
|
+
if (typeof logger === 'string') {
|
|
343
|
+
logger = this.getLogger(logger);
|
|
344
|
+
}
|
|
345
|
+
// make sure mock once
|
|
346
|
+
if ('_mockLogs' in logger && logger._mockLogs)
|
|
347
|
+
return;
|
|
348
|
+
const transport = new Transport(logger.options);
|
|
349
|
+
// https://github.com/eggjs/egg-logger/blob/master/lib/logger.js#L64
|
|
350
|
+
const log = logger.log;
|
|
351
|
+
const mockLogs = [];
|
|
352
|
+
mock(logger, '_mockLogs', mockLogs);
|
|
353
|
+
mock(logger, 'log', (level, args, meta) => {
|
|
354
|
+
const message = transport.log(level, args, meta);
|
|
355
|
+
mockLogs.push(message);
|
|
356
|
+
log.apply(logger, [level, args, meta]);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
__checkExpectLog(expectOrNot, str, logger) {
|
|
360
|
+
logger = logger || this.logger;
|
|
361
|
+
if (typeof logger === 'string') {
|
|
362
|
+
logger = this.getLogger(logger);
|
|
363
|
+
}
|
|
364
|
+
const filepath = logger.options.file;
|
|
365
|
+
let content;
|
|
366
|
+
if ('_mockLogs' in logger && logger._mockLogs) {
|
|
367
|
+
content = logger._mockLogs.join('\n');
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
content = fs.readFileSync(filepath, 'utf8');
|
|
371
|
+
}
|
|
372
|
+
let match;
|
|
373
|
+
let type;
|
|
374
|
+
if (str instanceof RegExp) {
|
|
375
|
+
match = str.test(content);
|
|
376
|
+
type = 'RegExp';
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
match = content.includes(String(str));
|
|
380
|
+
type = 'String';
|
|
381
|
+
}
|
|
382
|
+
if (expectOrNot) {
|
|
383
|
+
assert(match, `Can't find ${type}:"${str}" in ${filepath}, log content: ...${content.substring(content.length - 500)}`);
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
assert(!match, `Find ${type}:"${str}" in ${filepath}, log content: ...${content.substring(content.length - 500)}`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* expect str/regexp in the logger, if your server disk is slow, please call `mockLog()` first.
|
|
391
|
+
* @param {String|RegExp} str - test str or regexp
|
|
392
|
+
* @param {String|Logger} [logger] - logger instance, default is `ctx.logger`
|
|
393
|
+
* @function App#expectLog
|
|
394
|
+
*/
|
|
395
|
+
expectLog(str, logger) {
|
|
396
|
+
this.__checkExpectLog(true, str, logger);
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* not expect str/regexp in the logger, if your server disk is slow, please call `mockLog()` first.
|
|
400
|
+
* @param {String|RegExp} str - test str or regexp
|
|
401
|
+
* @param {String|Logger} [logger] - logger instance, default is `ctx.logger`
|
|
402
|
+
* @function App#notExpectLog
|
|
403
|
+
*/
|
|
404
|
+
notExpectLog(str, logger) {
|
|
405
|
+
this.__checkExpectLog(false, str, logger);
|
|
406
|
+
}
|
|
407
|
+
async backgroundTasksFinished() {
|
|
408
|
+
const tasks = this._backgroundTasks;
|
|
409
|
+
debug('waiting %d background tasks', tasks.length);
|
|
410
|
+
if (tasks.length === 0)
|
|
411
|
+
return;
|
|
412
|
+
this._backgroundTasks = [];
|
|
413
|
+
await Promise.all(tasks);
|
|
414
|
+
debug('finished %d background tasks', tasks.length);
|
|
415
|
+
if (this._backgroundTasks.length) {
|
|
416
|
+
debug('new background tasks created: %s', this._backgroundTasks.length);
|
|
417
|
+
await this.backgroundTasksFinished();
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
get _backgroundTasks() {
|
|
421
|
+
if (!this[BACKGROUND_TASKS]) {
|
|
422
|
+
this[BACKGROUND_TASKS] = [];
|
|
423
|
+
}
|
|
424
|
+
return this[BACKGROUND_TASKS];
|
|
425
|
+
}
|
|
426
|
+
set _backgroundTasks(tasks) {
|
|
427
|
+
this[BACKGROUND_TASKS] = tasks;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
function findHeaders(headers, key) {
|
|
431
|
+
if (!headers || !key) {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
key = key.toLowerCase();
|
|
435
|
+
for (const headerKey in headers) {
|
|
436
|
+
if (key === headerKey.toLowerCase()) {
|
|
437
|
+
return headers[headerKey];
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwbGljYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYXBwL2V4dGVuZC9hcHBsaWNhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3JDLE9BQU8sSUFBeUIsTUFBTSxXQUFXLENBQUM7QUFDbEQsT0FBTyxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ3pCLE9BQU8sTUFBTSxNQUFNLGFBQWEsQ0FBQztBQUNqQyxPQUFPLGdCQUFnQixNQUFNLG1CQUFtQixDQUFDO0FBQ2pELE9BQU8sRUFBRSxlQUFlLEVBQUUsUUFBUSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQ3ZELE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBRW5DLE9BQU8sRUFBRSxTQUFTLEVBQW1DLE1BQU0sWUFBWSxDQUFDO0FBQ3hFLE9BQU8sRUFBRSxPQUFPLEVBQXFDLE1BQU0sYUFBYSxDQUFDO0FBQ3pFLE9BQU8sRUFBRSxZQUFZLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN6RSxPQUFPLEVBQ0wsb0JBQW9CLEdBR3JCLE1BQU0sOEJBQThCLENBQUM7QUFDdEMsT0FBTyxFQUFFLE9BQU8sSUFBSSxnQkFBZ0IsRUFBa0IsTUFBTSx3QkFBd0IsQ0FBQztBQUdyRixNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsb0NBQW9DLENBQUMsQ0FBQztBQUU3RCxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMseUJBQXlCLENBQUMsQ0FBQztBQUN2RCxNQUFNLGdCQUFnQixHQUFHLE1BQU0sQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDO0FBQy9ELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDO0FBc0JuRCxNQUFNLENBQUMsT0FBTyxPQUFnQixtQkFBb0IsU0FBUSxPQUFPO0lBRy9ELGVBQWUsQ0FBdUI7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
|