From 3a434bb6a90c98b957b6d92e059c09297bf7cf18 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Tue, 2 Jul 2024 14:17:05 +0800 Subject: [PATCH] fix: support httpclient mock on allowH2 = true (#168) --- .github/workflows/pkg.pr.new.yml | 23 + README.md | 13 +- README.zh_CN.md | 13 +- app/extend/agent.js | 4 +- app/extend/application.js | 4 +- index.js | 6 +- lib/mock_agent.js | 21 +- lib/mock_httpclient.js | 2 +- package.json | 4 +- test/fixtures/demo_next_h2/app.js | 5 + test/fixtures/demo_next_h2/app/context.js | 9 + .../demo_next_h2/app/controller/file.js | 11 + .../demo_next_h2/app/controller/home.js | 104 ++++ .../demo_next_h2/app/controller/session.js | 6 + .../demo_next_h2/app/controller/user.js | 12 + .../demo_next_h2/app/extend/application.js | 18 + test/fixtures/demo_next_h2/app/router.js | 23 + .../demo_next_h2/app/service/bar/foo.js | 11 + test/fixtures/demo_next_h2/app/service/foo.js | 15 + test/fixtures/demo_next_h2/app/service/old.js | 9 + .../demo_next_h2/app/service/third/bar/foo.js | 11 + test/fixtures/demo_next_h2/config/config.js | 13 + .../mocks_data/service/bar/foo/get/foobar.js | 3 + .../mocks_data/service/foo/get/foobar.js | 3 + test/fixtures/demo_next_h2/package.json | 3 + test/mock_httpclient_next_h2.test.js | 525 ++++++++++++++++++ 26 files changed, 838 insertions(+), 33 deletions(-) create mode 100644 .github/workflows/pkg.pr.new.yml create mode 100644 test/fixtures/demo_next_h2/app.js create mode 100644 test/fixtures/demo_next_h2/app/context.js create mode 100644 test/fixtures/demo_next_h2/app/controller/file.js create mode 100644 test/fixtures/demo_next_h2/app/controller/home.js create mode 100644 test/fixtures/demo_next_h2/app/controller/session.js create mode 100644 test/fixtures/demo_next_h2/app/controller/user.js create mode 100644 test/fixtures/demo_next_h2/app/extend/application.js create mode 100644 test/fixtures/demo_next_h2/app/router.js create mode 100644 test/fixtures/demo_next_h2/app/service/bar/foo.js create mode 100644 test/fixtures/demo_next_h2/app/service/foo.js create mode 100644 test/fixtures/demo_next_h2/app/service/old.js create mode 100644 test/fixtures/demo_next_h2/app/service/third/bar/foo.js create mode 100644 test/fixtures/demo_next_h2/config/config.js create mode 100644 test/fixtures/demo_next_h2/mocks_data/service/bar/foo/get/foobar.js create mode 100644 test/fixtures/demo_next_h2/mocks_data/service/foo/get/foobar.js create mode 100644 test/fixtures/demo_next_h2/package.json create mode 100644 test/mock_httpclient_next_h2.test.js diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml new file mode 100644 index 0000000..bac3fac --- /dev/null +++ b/.github/workflows/pkg.pr.new.yml @@ -0,0 +1,23 @@ +name: Publish Any Commit +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - run: corepack enable + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install dependencies + run: npm install + + - name: Build + run: npm run prepublishOnly --if-present + + - run: npx pkg-pr-new publish diff --git a/README.md b/README.md index 6e259a3..928acbd 100644 --- a/README.md +++ b/README.md @@ -523,6 +523,7 @@ it('should work', function() { ``` ### env for custom bootstrap + EGG_BASE_DIR: the base dir of egg app EGG_FRAMEWORK: the framework of egg app @@ -534,16 +535,8 @@ Please open an issue [here](https://github.com/eggjs/egg/issues). [MIT](LICENSE) - - ## Contributors -|[
popomore](https://github.com/popomore)
|[
fengmk2](https://github.com/fengmk2)
|[
atian25](https://github.com/atian25)
|[
dead-horse](https://github.com/dead-horse)
|[
shepherdwind](https://github.com/shepherdwind)
|[
shaoshuai0102](https://github.com/shaoshuai0102)
| -| :---: | :---: | :---: | :---: | :---: | :---: | -|[
mansonchor](https://github.com/mansonchor)
|[
whxaxes](https://github.com/whxaxes)
|[
brickyang](https://github.com/brickyang)
|[
zbinlin](https://github.com/zbinlin)
|[
GoodMeowing](https://github.com/GoodMeowing)
|[
ngot](https://github.com/ngot)
| -|[
geekdada](https://github.com/geekdada)
|[
shinux](https://github.com/shinux)
|[
zhang740](https://github.com/zhang740)
|[
caoer](https://github.com/caoer)
|[
lidianhao123](https://github.com/lidianhao123)
|[
limerickgds](https://github.com/limerickgds)
| -[
paranoidjk](https://github.com/paranoidjk)
|[
gxcsoccer](https://github.com/gxcsoccer)
|[
okoala](https://github.com/okoala)
|[
ImHype](https://github.com/ImHype)
|[
linjiajian999](https://github.com/linjiajian999)
- -This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Fri Apr 29 2022 22:49:14 GMT+0800`. +[![Contributors](https://contrib.rocks/image?repo=eggjs/egg-mock)](https://github.com/eggjs/egg-mock/graphs/contributors) - +Made with [contributors-img](https://contrib.rocks). diff --git a/README.zh_CN.md b/README.zh_CN.md index dc2c9c2..013d573 100644 --- a/README.zh_CN.md +++ b/README.zh_CN.md @@ -494,6 +494,7 @@ describe('test ctx', () => { ``` ### env for custom bootstrap + EGG_BASE_DIR: the base dir of egg app EGG_FRAMEWORK: the framework of egg app @@ -505,16 +506,8 @@ Please open an issue [here](https://github.com/eggjs/egg/issues). [MIT](LICENSE) - - ## Contributors -|[
popomore](https://github.com/popomore)
|[
fengmk2](https://github.com/fengmk2)
|[
atian25](https://github.com/atian25)
|[
dead-horse](https://github.com/dead-horse)
|[
shepherdwind](https://github.com/shepherdwind)
|[
shaoshuai0102](https://github.com/shaoshuai0102)
| -| :---: | :---: | :---: | :---: | :---: | :---: | -|[
mansonchor](https://github.com/mansonchor)
|[
whxaxes](https://github.com/whxaxes)
|[
brickyang](https://github.com/brickyang)
|[
zbinlin](https://github.com/zbinlin)
|[
GoodMeowing](https://github.com/GoodMeowing)
|[
ngot](https://github.com/ngot)
| -|[
geekdada](https://github.com/geekdada)
|[
shinux](https://github.com/shinux)
|[
zhang740](https://github.com/zhang740)
|[
caoer](https://github.com/caoer)
|[
lidianhao123](https://github.com/lidianhao123)
|[
limerickgds](https://github.com/limerickgds)
| -[
paranoidjk](https://github.com/paranoidjk)
|[
gxcsoccer](https://github.com/gxcsoccer)
|[
okoala](https://github.com/okoala)
|[
ImHype](https://github.com/ImHype)
|[
linjiajian999](https://github.com/linjiajian999)
- -This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Fri Apr 29 2022 22:49:14 GMT+0800`. +[![Contributors](https://contrib.rocks/image?repo=eggjs/egg-mock)](https://github.com/eggjs/egg-mock/graphs/contributors) - +Made with [contributors-img](https://contrib.rocks). diff --git a/app/extend/agent.js b/app/extend/agent.js index f211783..d85a5f7 100644 --- a/app/extend/agent.js +++ b/app/extend/agent.js @@ -22,11 +22,11 @@ module.exports = { * @return {MockAgent} agent */ mockAgent() { - return mockAgent.getAgent(); + return mockAgent.getAgent(this); }, mockAgentRestore() { - return mockAgent.restore(); + return mockAgent.restore(this); }, /** diff --git a/app/extend/application.js b/app/extend/application.js index 251c9ef..020f14a 100644 --- a/app/extend/application.js +++ b/app/extend/application.js @@ -303,11 +303,11 @@ module.exports = { * @return {MockAgent} agent */ mockAgent() { - return mockAgent.getAgent(); + return mockAgent.getAgent(this); }, mockAgentRestore() { - return mockAgent.restore(); + return mockAgent.restore(this); }, /** diff --git a/index.js b/index.js index a79b4b8..6382895 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,9 @@ const app = require('./lib/app'); const mockAgent = require('./lib/mock_agent'); // egg-bin will set this flag to require files for instrument -if (process.env.EGG_BIN_PREREQUIRE) require('./lib/prerequire'); +if (process.env.EGG_BIN_PREREQUIRE) { + require('./lib/prerequire'); +} /** * @namespace mm @@ -22,7 +24,7 @@ Object.assign(mock, mm, { cluster.restore(); mm.restore(); // return promise - return mockAgent.restore(); + return mockAgent.restore(app); }, /** diff --git a/lib/mock_agent.js b/lib/mock_agent.js index f5e6858..9eeba0d 100644 --- a/lib/mock_agent.js +++ b/lib/mock_agent.js @@ -2,21 +2,36 @@ const { MockAgent, setGlobalDispatcher, getGlobalDispatcher } = require('urllib' let _mockAgent; let _global; +const APP_HTTPCLIENT_AGENT = Symbol('app.httpclient.agent'); module.exports = { - getAgent() { + getAgent(app) { if (!_global) { _global = getGlobalDispatcher(); + if (typeof app?.httpclient?.getDispatcher === 'function') { + if (!app[APP_HTTPCLIENT_AGENT]) { + // save the raw dispatcher + app[APP_HTTPCLIENT_AGENT] = app.httpclient.getDispatcher(); + } + } } if (!_mockAgent) { _mockAgent = new MockAgent(); setGlobalDispatcher(_mockAgent); + if (typeof app?.httpclient?.setDispatcher === 'function') { + app.httpclient.setDispatcher(_mockAgent); + } } return _mockAgent; }, - async restore() { + async restore(app) { if (!_mockAgent) return; - if (_global) setGlobalDispatcher(_global); + if (_global) { + setGlobalDispatcher(_global); + if (app?.[APP_HTTPCLIENT_AGENT]) { + app.httpclient.setDispatcher(app[APP_HTTPCLIENT_AGENT]); + } + } const agent = _mockAgent; _mockAgent = null; await agent.close(); diff --git a/lib/mock_httpclient.js b/lib/mock_httpclient.js index d488f12..57515cf 100644 --- a/lib/mock_httpclient.js +++ b/lib/mock_httpclient.js @@ -114,7 +114,7 @@ module.exports = app => { mockConfigIndex = mockConfigs.length; mockConfigs.push({ mockUrl, mockResult, mockConfigIndex }); } - const mockPool = mockAgent.getAgent().get(origin); + const mockPool = mockAgent.getAgent(app).get(origin); // persist default is true const persist = typeof mockResult?.persist === 'boolean' ? mockResult?.persist : true; mockMethod.forEach(function(method) { diff --git a/package.json b/package.json index 90a6574..286d34c 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,7 @@ "test": "npm run lint && npm run tsd && npm run test-local", "test-local": "egg-bin test -r ./register.js --ts false", "cov": "egg-bin cov -r ./register.js --ts false", - "ci": "npm run lint && npm run tsd && npm run cov", - "contributors": "git-contributor" + "ci": "npm run lint && npm run tsd && npm run cov" }, "dependencies": { "@types/supertest": "^2.0.7", @@ -63,7 +62,6 @@ "egg-tracer": "^2.0.0", "eslint": "^8.24.0", "eslint-config-egg": "^12.0.0", - "git-contributor": "^2.0.0", "mocha": "^10.1.0", "pedding": "^1.1.0", "tsd": "^0.31.0", diff --git a/test/fixtures/demo_next_h2/app.js b/test/fixtures/demo_next_h2/app.js new file mode 100644 index 0000000..0c134c2 --- /dev/null +++ b/test/fixtures/demo_next_h2/app.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = function() { + +}; diff --git a/test/fixtures/demo_next_h2/app/context.js b/test/fixtures/demo_next_h2/app/context.js new file mode 100644 index 0000000..8422a63 --- /dev/null +++ b/test/fixtures/demo_next_h2/app/context.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = { + getResult(result) { + return { + body: result, + }; + }, +}; diff --git a/test/fixtures/demo_next_h2/app/controller/file.js b/test/fixtures/demo_next_h2/app/controller/file.js new file mode 100644 index 0000000..e98c443 --- /dev/null +++ b/test/fixtures/demo_next_h2/app/controller/file.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = function* () { + const stream = yield this.getFileStream(); + const fields = stream.fields; + this.body = { + fields, + filename: stream.filename, + user: this.user, + }; +}; diff --git a/test/fixtures/demo_next_h2/app/controller/home.js b/test/fixtures/demo_next_h2/app/controller/home.js new file mode 100644 index 0000000..395021b --- /dev/null +++ b/test/fixtures/demo_next_h2/app/controller/home.js @@ -0,0 +1,104 @@ +'use strict'; + +exports.get = function* () { + this.body = { + cookieValue: this.getCookie('foo') || undefined, + cookiesValue: this.cookies.get('foo') || undefined, + sessionValue: this.session.foo, + }; +}; + +exports.post = function* () { + this.body = 'done'; +}; + +exports.hello = function* () { + this.body = 'hi'; +}; + +exports.service = function* () { + this.body = { + foo1: yield this.service.foo.get(), + foo2: yield this.service.bar.foo.get(), + foo3: this.service.foo.getSync(), + thirdService: yield this.service.third.bar.foo.get(), + }; +}; + +exports.serviceOld = function* () { + this.body = yield this.service.old.test(); +}; + +exports.header = function* () { + this.body = { + header: this.get('customheader'), + }; +}; + +exports.urllib = function* () { + const url = 'http://' + this.host; + const method = this.query.method || 'request'; + const data = this.query.data ? JSON.parse(this.query.data) : undefined; + const dataType = this.query.dataType; + const foo = this.query.foo; + let requestUrl = url + (this.query.mock_url || '/mock_url'); + if (foo) { + requestUrl = `${requestUrl}?foo=${foo}`; + } + let r = this.app.httpclient[method](requestUrl, { + dataType, + data, + }); + if (method === 'request') r = r.then(d => d); + const r1 = yield r; + const r2 = yield this.app.httpclient[method](requestUrl, { + method: 'POST', + dataType, + data, + headers: { + 'x-custom': 'custom', + }, + }); + this.body = { + get: Buffer.isBuffer(r1.data) ? r1.data.toString() : r1.data, + post: Buffer.isBuffer(r2.data) ? r2.data.toString() : r2.data, + }; +}; + +exports.streaming = async ctx => { + const url = 'http://' + ctx.host; + const response = await ctx.httpclient.request(url + '/mock_url', { + method: 'GET', + streaming: true, + }); + ctx.status = response.status; + ctx.body = response.res; +}; + +exports.mockUrlGet = function* () { + const foo = this.query.foo; + if (foo) { + this.body = `url get with foo: ${foo}`; + return; + } + this.body = 'url get'; +}; + +exports.mockUrlPost = function* () { + this.body = 'url post'; +}; + +exports.mockUrllibHeaders = function* () { + const url = 'http://' + this.host; + const method = this.query.method || 'request'; + const res = yield this.app.httpclient[method](url + '/mock_url'); + this.body = res.headers; +}; + +exports.dataType = function* () { + const url = 'http://' + this.host; + const res = yield this.app.httpclient.request(url + '/mock_url', { + dataType: 'json', + }); + this.body = res.data; +}; diff --git a/test/fixtures/demo_next_h2/app/controller/session.js b/test/fixtures/demo_next_h2/app/controller/session.js new file mode 100644 index 0000000..5e9e198 --- /dev/null +++ b/test/fixtures/demo_next_h2/app/controller/session.js @@ -0,0 +1,6 @@ +'use strict'; + +module.exports = function* () { + this.session.save(); + this.body = this.session; +}; diff --git a/test/fixtures/demo_next_h2/app/controller/user.js b/test/fixtures/demo_next_h2/app/controller/user.js new file mode 100644 index 0000000..b789ef2 --- /dev/null +++ b/test/fixtures/demo_next_h2/app/controller/user.js @@ -0,0 +1,12 @@ +'use strict'; + +exports.get = function* () { + this.body = this.user; +}; + +exports.post = function* () { + this.body = { + user: this.user, + params: this.request.body, + }; +}; diff --git a/test/fixtures/demo_next_h2/app/extend/application.js b/test/fixtures/demo_next_h2/app/extend/application.js new file mode 100644 index 0000000..2810f72 --- /dev/null +++ b/test/fixtures/demo_next_h2/app/extend/application.js @@ -0,0 +1,18 @@ +'use strict'; + +module.exports = { + mockDevice(obj) { + obj.mock = true; + return obj; + }, + + * mockGenerator(obj) { + obj.mock = true; + return obj; + }, + + mockPromise(obj) { + obj.mock = true; + return Promise.resolve(obj); + }, +}; diff --git a/test/fixtures/demo_next_h2/app/router.js b/test/fixtures/demo_next_h2/app/router.js new file mode 100644 index 0000000..0af9e43 --- /dev/null +++ b/test/fixtures/demo_next_h2/app/router.js @@ -0,0 +1,23 @@ +module.exports = app => { + app.get('home', '/', app.controller.home.get); + app.get('/hello', app.controller.home.hello); + app.get('/service', app.controller.home.service); + app.get('/service/old', app.controller.home.serviceOld); + app.get('/header', app.controller.home.header); + app.get('/urllib', app.controller.home.urllib); + app.get('/mock_url', app.controller.home.mockUrlGet); + app.get('/mock_url2', app.controller.home.mockUrlGet); + app.post('/mock_url', app.controller.home.mockUrlPost); + app.post('/mock_url2', app.controller.home.mockUrlPost); + app.get('/mock_urllib', app.controller.home.mockUrllibHeaders); + app.get('/data_type', app.controller.home.dataType); + app.get('session', '/session', app.controller.session); + + app.post('/', app.controller.home.post); + + app.get('/user', app.controller.user.get); + app.post('/user', app.controller.user.post); + app.post('/file', app.controller.file); + + app.get('/streaming', 'home.streaming'); +}; diff --git a/test/fixtures/demo_next_h2/app/service/bar/foo.js b/test/fixtures/demo_next_h2/app/service/bar/foo.js new file mode 100644 index 0000000..8070a93 --- /dev/null +++ b/test/fixtures/demo_next_h2/app/service/bar/foo.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = function(app) { + class Foo extends app.Service { + * get() { + return 'bar'; + } + } + + return Foo; +}; diff --git a/test/fixtures/demo_next_h2/app/service/foo.js b/test/fixtures/demo_next_h2/app/service/foo.js new file mode 100644 index 0000000..467c747 --- /dev/null +++ b/test/fixtures/demo_next_h2/app/service/foo.js @@ -0,0 +1,15 @@ +'use strict'; + +module.exports = function(app) { + class Foo extends app.Service { + * get() { + return 'bar'; + } + + getSync() { + return 'bar'; + } + } + + return Foo; +}; diff --git a/test/fixtures/demo_next_h2/app/service/old.js b/test/fixtures/demo_next_h2/app/service/old.js new file mode 100644 index 0000000..29ea81e --- /dev/null +++ b/test/fixtures/demo_next_h2/app/service/old.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = () => { + exports.test = function* () { + return 'hello'; + }; + + return exports; +}; diff --git a/test/fixtures/demo_next_h2/app/service/third/bar/foo.js b/test/fixtures/demo_next_h2/app/service/third/bar/foo.js new file mode 100644 index 0000000..21c1807 --- /dev/null +++ b/test/fixtures/demo_next_h2/app/service/third/bar/foo.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = function(app) { + class Main extends app.Service { + * get() { + return 'third'; + } + } + + return Main; +}; diff --git a/test/fixtures/demo_next_h2/config/config.js b/test/fixtures/demo_next_h2/config/config.js new file mode 100644 index 0000000..05cad3f --- /dev/null +++ b/test/fixtures/demo_next_h2/config/config.js @@ -0,0 +1,13 @@ +module.exports = { + httpclient: { + useHttpClientNext: true, + allowH2: true, + request: { + timing: true, + }, + }, + logger: { + consoleLevel: 'NONE', + }, + keys: '123', +}; diff --git a/test/fixtures/demo_next_h2/mocks_data/service/bar/foo/get/foobar.js b/test/fixtures/demo_next_h2/mocks_data/service/bar/foo/get/foobar.js new file mode 100644 index 0000000..af79a2f --- /dev/null +++ b/test/fixtures/demo_next_h2/mocks_data/service/bar/foo/get/foobar.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = 'foobar'; diff --git a/test/fixtures/demo_next_h2/mocks_data/service/foo/get/foobar.js b/test/fixtures/demo_next_h2/mocks_data/service/foo/get/foobar.js new file mode 100644 index 0000000..af79a2f --- /dev/null +++ b/test/fixtures/demo_next_h2/mocks_data/service/foo/get/foobar.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = 'foobar'; diff --git a/test/fixtures/demo_next_h2/package.json b/test/fixtures/demo_next_h2/package.json new file mode 100644 index 0000000..e38ae2e --- /dev/null +++ b/test/fixtures/demo_next_h2/package.json @@ -0,0 +1,3 @@ +{ + "name": "demo" +} diff --git a/test/mock_httpclient_next_h2.test.js b/test/mock_httpclient_next_h2.test.js new file mode 100644 index 0000000..ab7b97c --- /dev/null +++ b/test/mock_httpclient_next_h2.test.js @@ -0,0 +1,525 @@ +const pedding = require('pedding'); +const path = require('path'); +const fs = require('fs'); +const request = require('supertest'); +const assert = require('assert'); +const mm = require('..'); + +const fixtures = path.join(__dirname, 'fixtures'); + +describe('test/mock_httpclient_next_h2.test.js', () => { + let app; + let server; + let url; + let url2; + before(() => { + app = mm.app({ + baseDir: path.join(fixtures, 'demo_next_h2'), + }); + return app.ready(); + }); + before(() => { + server = app.listen(); + url = `http://127.0.0.1:${server.address().port}/mock_url`; + url2 = `http://127.0.0.1:${server.address().port}/mock_url2`; + }); + after(() => app.close()); + afterEach(mm.restore); + + it('should mock url and get response event on urllib', done => { + done = pedding(2, done); + app.mockCsrf(); + app.mockHttpclient(url, { + data: Buffer.from('mock all response'), + }); + + request(server) + .get('/urllib') + .expect({ + get: 'mock all response', + post: 'mock all response', + }) + .expect(200, done); + + app.httpclient.once('response', result => { + assert('url' in result.req); + // assert('size' in result.req); + assert('options' in result.req); + + assert(result.res.status === 200); + assert(result.res.statusCode === 200); + assert.deepEqual(result.res.headers, {}); + assert(result.res.rt); + done(); + }); + + let count = 0; + app.httpclient.on('response', result => { + if (count === 0) { + const options = result.req.options; + assert(options.method === 'GET'); + } else if (count === 1) { + const options = result.req.options; + assert(options.method === 'POST'); + assert(options.headers['x-custom'] === 'custom'); + } + count++; + }); + }); + + it('should mock url using app.mockAgent().intercept()', async () => { + app.mockCsrf(); + app.mockAgent() + .get(new URL(url).origin) + .intercept({ + path: '/mock_url', + method: 'GET', + }) + .reply(200, 'mock GET response'); + app.mockAgent() + .get(new URL(url).origin) + .intercept({ + path: '/mock_url', + method: 'POST', + }) + .reply(200, 'mock POST response'); + + await request(server) + .get('/urllib') + .expect({ + get: 'mock GET response', + post: 'mock POST response', + }) + .expect(200); + }); + + it('should support on streaming', async () => { + app.mockHttpclient(url, 'get', { + data: fs.readFileSync(__filename), + }); + + const res = await request(server) + .get('/streaming') + .expect(200); + assert.match(res.body.toString(), /should support on streaming/); + assert.equal(res.body.toString(), fs.readFileSync(__filename, 'utf8')); + }); + + it('should mock url support multi method', async () => { + app.mockCsrf(); + app.mockHttpclient(url, [ 'get', 'post' ], { + data: Buffer.from('mock response'), + }); + + await request(server) + .get('/urllib') + .expect({ + get: 'mock response', + post: 'mock response', + }) + .expect(200); + }); + + it('should mockHttpclient call multi times work with Regex', async () => { + app.mockCsrf(); + app.mockHttpclient(/\/not\/match\//, { + data: Buffer.from('mock not match response'), + }); + app.mockHttpclient(/\/mock_url/, { + data: Buffer.from('mock 1 match response'), + }); + app.mockHttpclient(/\/mock_url/, { + data: Buffer.from('mock 2 match response'), + }); + + await request(server) + .get('/urllib') + .expect({ + get: 'mock 1 match response', + post: 'mock 1 match response', + }) + .expect(200); + }); + + it('should mockHttpclient call multi times work with url string', async () => { + app.mockCsrf(); + app.mockHttpclient(`${url}-not-match`, { + data: Buffer.from('mock not match response'), + }); + app.mockHttpclient(url, { + data: Buffer.from('mock 1 match response'), + }); + app.mockHttpclient(url, { + data: Buffer.from('mock 2 match response'), + }); + + await request(server) + .get('/urllib') + .expect({ + get: 'mock 1 match response', + post: 'mock 1 match response', + }) + .expect(200); + }); + + it('should mock url method support *', async () => { + app.mockCsrf(); + app.mockHttpclient(url, '*', { + data: Buffer.from('mock * response'), + }); + + await request(server) + .get('/urllib') + .expect({ + get: 'mock * response', + post: 'mock * response', + }) + .expect(200); + }); + + it('should mock url post', async () => { + app.mockCsrf(); + app.mockHttpclient(url, 'post', { + data: Buffer.from('mock url post'), + }); + + await request(server) + .get('/urllib') + .expect({ + get: 'url get', + post: 'mock url post', + }) + .expect(200); + }); + + it('should use first mock data on duplicate url mock', async () => { + app.mockCsrf(); + app.mockHttpclient(url, 'post', { + data: Buffer.from('mock url1 first post'), + }); + // should ignore this same url mock data, use the first mock data + app.mockHttpclient(url, 'post', { + data: Buffer.from('mock url1 second post'), + }); + app.mockHttpclient(url2, 'post', { + data: Buffer.from('mock url2 post'), + }); + + await request(server) + .get('/urllib') + .expect({ + get: 'url get', + post: 'mock url1 first post', + }) + .expect(200); + await request(server) + .get('/urllib') + .query({ + mock_url: '/mock_url2', + }) + .expect({ + get: 'url get', + post: 'mock url2 post', + }) + .expect(200); + }); + + it('should mock work on query', async () => { + app.mockCsrf(); + // mockHttpclient not support query, should use mockAgent instead + app.mockHttpclient(`${url}?foo=foo1`, 'get', { + data: Buffer.from('mock foo1'), + }); + app.mockHttpclient(`${url}?foo=foo2`, 'get', { + data: Buffer.from('mock foo1'), + }); + await request(server) + .get('/urllib') + .query({ foo: 'foo1' }) + .expect({ + get: 'mock foo1', + post: 'url post', + }) + .expect(200); + await request(server) + .get('/urllib') + .query({ foo: 'foo2' }) + .expect({ + get: 'mock foo1', + post: 'url post', + }) + .expect(200); + await app.mockAgentRestore(); + + app.mockAgent().get(new URL(url).origin) + .intercept({ + path: '/mock_url?foo=foo1', + method: 'GET', + }) + .reply(200, 'mock new foo1'); + app.mockAgent().get(new URL(url).origin) + .intercept({ + path: '/mock_url?foo=foo2', + method: 'GET', + }) + .reply(200, 'mock new foo2'); + await request(server) + .get('/urllib') + .query({ foo: 'foo1' }) + .expect({ + get: 'mock new foo1', + post: 'url post', + }) + .expect(200); + await request(server) + .get('/urllib') + .query({ foo: 'foo2' }) + .expect({ + get: 'mock new foo2', + post: 'url post', + }) + .expect(200); + }); + + it('should mock url get and post', async () => { + app.mockCsrf(); + app.mockHttpclient(url, 'post', { + data: 'mock url post', + }); + app.mockHttpclient(url, { + data: 'mock url get', + }); + + await request(server) + .get('/urllib') + .expect({ + get: 'mock url get', + post: 'mock url post', + }) + .expect(200); + }); + + it('should support request', async () => { + app.mockCsrf(); + app.mockHttpclient(url, 'post', { + data: 'mock url post', + }); + app.mockHttpclient(url, { + data: 'mock url get', + }); + + await request(server) + .get('/urllib?method=request') + .expect({ + get: 'mock url get', + post: 'mock url post', + }) + .expect(200); + }); + + it('should support persist = false', async () => { + app.mockCsrf(); + app.mockHttpclient(url, { + data: 'mock url', + persist: false, + }); + + await request(server) + .get('/urllib?method=request') + .expect({ + get: 'mock url', + post: 'url post', + }) + .expect(200); + }); + + it('should support persist = true and ignore repeats = 1', async () => { + app.mockCsrf(); + app.mockHttpclient(url, { + data: 'mock url', + persist: true, + repeats: 1, + }); + + await request(server) + .get('/urllib?method=request') + .expect({ + get: 'mock url', + post: 'mock url', + }) + .expect(200); + }); + + it('should support persist = false and repeats = 2', async () => { + app.mockCsrf(); + app.mockHttpclient(url, { + data: 'mock url', + delay: 100, + persist: false, + repeats: 2, + }); + + await request(server) + .get('/urllib?method=request') + .expect({ + get: 'mock url', + post: 'mock url', + }) + .expect(200); + + await request(server) + .get('/urllib?method=request') + .expect({ + get: 'url get', + post: 'url post', + }) + .expect(200); + }); + + it('should support curl', async () => { + app.mockCsrf(); + app.mockHttpclient(url, 'post', { + data: 'mock url post', + }); + app.mockHttpclient(url, { + data: 'mock url get', + }); + + await request(server) + .get('/urllib?method=curl') + .expect({ + get: 'mock url get', + post: 'mock url post', + }) + .expect(200); + }); + + it('should support json', async () => { + app.mockCsrf(); + app.mockHttpclient(url, 'get', { + data: { method: 'get' }, + }); + app.mockHttpclient(url, 'post', { + data: { method: 'post' }, + }); + + await request(server) + .get('/urllib?dataType=json') + .expect({ + get: { method: 'get' }, + post: { method: 'post' }, + }) + .expect(200); + }); + + it('should support text', async () => { + app.mockCsrf(); + app.mockHttpclient(url, 'post', { + data: 'mock url post', + }); + app.mockHttpclient(url, { + data: 'mock url get', + }); + + await request(server) + .get('/urllib?dataType=text') + .expect({ + get: 'mock url get', + post: 'mock url post', + }) + .expect(200); + }); + + it('should exits req headers', async () => { + app.mockCsrf(); + app.mockHttpclient(url, { + data: 'mock url test', + }); + await request(server) + .get('/mock_urllib') + .expect({}) + .expect(200); + }); + + it('should mock url path support RegExp', async () => { + app.mockCsrf(); + app.mockHttpclient(/\/mock_url$/, { + data: Buffer.from('mock response'), + }); + + await request(server) + .get('/urllib') + .expect({ + get: 'mock response', + post: 'mock response', + }) + .expect(200); + }); + + it('should mock full url support RegExp', async () => { + app.mockCsrf(); + app.mockHttpclient(/http:\/\/127\.0\.0\.1:\d+\/mock_url$/, [ 'get', 'post' ], { + data: Buffer.from('mock full 127 url response'), + }); + + await request(server) + .get('/urllib') + .expect({ + get: 'mock full 127 url response', + post: 'mock full 127 url response', + }) + .expect(200); + }); + + it('should use copy of mock data', async () => { + app.mockCsrf(); + app.mockHttpclient(/\/mock_url$/, { + data: { a: 1 }, + }); + + await request(server) + .get('/data_type') + .expect({ + a: 1, + }) + .expect(200); + + await request(server) + .get('/data_type') + .expect({ + a: 1, + }) + .expect(200); + }); + + it('should support fn', async () => { + app.mockCsrf(); + app.mockHttpclient(url, 'get', (url, opt) => { + return `mock ${url} with ${opt.path}`; + }); + app.mockHttpclient(url, 'post', 'mock url post'); + + await request(server) + .get('/urllib') + .query({ data: JSON.stringify({ a: 'b' }) }) + .expect({ + get: `mock ${url}?a=b with /mock_url?a=b`, + post: 'mock url post', + }) + .expect(200); + }); + + it('should mock fn with multi-request without error', async () => { + app.mockCsrf(); + let i = 0; + app.mockHttpclient(url, 'post', () => { + i++; + return {}; + }); + + await request(server).get('/urllib').expect(200); + await request(server).get('/urllib').expect(200); + await request(server).get('/urllib').expect(200); + assert(i === 3); + }); +});