Repository: eggjs/egg-mock Branch: 5.x Commit: 5ced81620f84 Files: 283 Total size: 291.6 KB Directory structure: gitextract_75m1jveo/ ├── .eslintrc ├── .github/ │ └── workflows/ │ ├── nodejs.yml │ ├── pkg.pr.new.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README.zh_CN.md ├── app/ │ ├── extend/ │ │ ├── agent.js │ │ └── application.js │ └── middleware/ │ └── cluster_app_mock.js ├── app.js ├── bootstrap.d.ts ├── bootstrap.js ├── index.d.ts ├── index.js ├── index.test-d.ts ├── lib/ │ ├── agent_handler.js │ ├── app.js │ ├── app_handler.js │ ├── cluster.js │ ├── context.js │ ├── format_options.js │ ├── http_test.js │ ├── inject_context.js │ ├── mock_agent.js │ ├── mock_custom_loader.js │ ├── mock_http_server.js │ ├── mock_httpclient.js │ ├── parallel/ │ │ ├── agent.js │ │ ├── agent_register.js │ │ ├── app.js │ │ └── util.js │ ├── prerequire.js │ ├── request_call_function.js │ ├── start-cluster │ ├── supertest.js │ ├── tmp/ │ │ └── .gitkeep │ └── utils.js ├── package.json ├── register.js └── test/ ├── agent.test.js ├── app/ │ └── middleware/ │ └── cluster_app_mock.test.js ├── app.test.js ├── app_event.test.js ├── app_proxy.test.js ├── bootstrap-plugin.test.js ├── bootstrap.test.js ├── cluster.test.js ├── ctx.test.js ├── fixtures/ │ ├── agent/ │ │ ├── agent.js │ │ ├── app.js │ │ ├── client.js │ │ ├── config/ │ │ │ └── config.default.js │ │ └── package.json │ ├── agent-boot-error/ │ │ ├── agent.js │ │ └── package.json │ ├── agent-boot-ready-error/ │ │ ├── agent.js │ │ └── package.json │ ├── app/ │ │ ├── app/ │ │ │ └── router.js │ │ ├── app.js │ │ ├── config/ │ │ │ └── config.default.js │ │ └── package.json │ ├── app-boot-error/ │ │ ├── app.js │ │ └── package.json │ ├── app-boot-ready-error/ │ │ ├── app.js │ │ └── package.json │ ├── app-event/ │ │ ├── agent.js │ │ ├── app/ │ │ │ └── router.js │ │ ├── app.js │ │ ├── config/ │ │ │ └── config.default.js │ │ └── package.json │ ├── app-fail/ │ │ ├── app/ │ │ │ └── router.js │ │ ├── config/ │ │ │ └── config.default.js │ │ └── package.json │ ├── app-proxy/ │ │ ├── app/ │ │ │ └── extend/ │ │ │ └── application.js │ │ ├── config/ │ │ │ └── config.default.js │ │ └── package.json │ ├── app-proxy-ready/ │ │ ├── agent.js │ │ ├── app.js │ │ ├── config/ │ │ │ └── config.default.js │ │ └── package.json │ ├── app-ready-failed/ │ │ ├── app.js │ │ ├── config/ │ │ │ └── config.default.js │ │ ├── package.json │ │ └── test/ │ │ └── index.test.js │ ├── apps/ │ │ ├── app-not-clean/ │ │ │ ├── config/ │ │ │ │ └── config.default.js │ │ │ ├── logs/ │ │ │ │ └── keep │ │ │ └── package.json │ │ ├── app-throw/ │ │ │ ├── app/ │ │ │ │ └── router.js │ │ │ ├── config/ │ │ │ │ ├── config.default.js │ │ │ │ └── config.unittest.js │ │ │ └── package.json │ │ ├── barapp/ │ │ │ ├── app/ │ │ │ │ ├── controller/ │ │ │ │ │ └── home.js │ │ │ │ └── router.js │ │ │ ├── config/ │ │ │ │ └── config.default.js │ │ │ └── package.json │ │ ├── env-app/ │ │ │ ├── config/ │ │ │ │ ├── config.default.js │ │ │ │ ├── config.local.js │ │ │ │ ├── config.prod.js │ │ │ │ ├── config.test.js │ │ │ │ └── config.unittest.js │ │ │ └── package.json │ │ ├── foo/ │ │ │ ├── app/ │ │ │ │ └── router.js │ │ │ ├── config/ │ │ │ │ └── config.default.js │ │ │ └── package.json │ │ ├── mock_cookies/ │ │ │ ├── app/ │ │ │ │ └── router.js │ │ │ ├── config/ │ │ │ │ └── config.default.js │ │ │ └── package.json │ │ ├── mockhome/ │ │ │ ├── config/ │ │ │ │ └── config.default.js │ │ │ └── package.json │ │ ├── no-framework/ │ │ │ ├── config/ │ │ │ │ └── plugin.js │ │ │ ├── package.json │ │ │ └── plugin/ │ │ │ └── a/ │ │ │ ├── app/ │ │ │ │ └── extend/ │ │ │ │ └── application.js │ │ │ └── package.json │ │ └── parallel-test/ │ │ ├── package.json │ │ └── test/ │ │ ├── a.test.js │ │ ├── b.test.js │ │ └── c.test.js │ ├── bar/ │ │ ├── config/ │ │ │ └── config.default.js │ │ ├── index.js │ │ └── package.json │ ├── cache/ │ │ ├── config.default.js │ │ └── package.json │ ├── chair/ │ │ ├── index.js │ │ └── package.json │ ├── create-context-failed/ │ │ ├── config/ │ │ │ └── config.default.js │ │ ├── package.json │ │ └── test/ │ │ └── index.test.js │ ├── custom-loader/ │ │ ├── app/ │ │ │ ├── adapter/ │ │ │ │ └── docker.js │ │ │ ├── controller/ │ │ │ │ └── user.js │ │ │ ├── repository/ │ │ │ │ └── user.js │ │ │ └── router.js │ │ ├── config/ │ │ │ └── config.default.js │ │ └── package.json │ ├── custom_egg/ │ │ └── package.json │ ├── demo/ │ │ ├── app/ │ │ │ ├── context.js │ │ │ ├── controller/ │ │ │ │ ├── file.js │ │ │ │ ├── home.js │ │ │ │ ├── session.js │ │ │ │ └── user.js │ │ │ ├── extend/ │ │ │ │ └── application.js │ │ │ ├── router.js │ │ │ └── service/ │ │ │ ├── bar/ │ │ │ │ └── foo.js │ │ │ ├── foo.js │ │ │ ├── old.js │ │ │ └── third/ │ │ │ └── bar/ │ │ │ └── foo.js │ │ ├── app.js │ │ ├── config/ │ │ │ └── config.js │ │ ├── mocks_data/ │ │ │ └── service/ │ │ │ ├── bar/ │ │ │ │ └── foo/ │ │ │ │ └── get/ │ │ │ │ └── foobar.js │ │ │ └── foo/ │ │ │ └── get/ │ │ │ └── foobar.js │ │ └── package.json │ ├── demo-async/ │ │ ├── app/ │ │ │ ├── controller/ │ │ │ │ └── home.js │ │ │ ├── router.js │ │ │ └── service/ │ │ │ ├── bar/ │ │ │ │ └── foo.js │ │ │ ├── foo.js │ │ │ └── third/ │ │ │ └── bar/ │ │ │ └── foo.js │ │ ├── app.js │ │ ├── config/ │ │ │ └── config.js │ │ ├── mocks_data/ │ │ │ └── service/ │ │ │ ├── bar/ │ │ │ │ └── foo/ │ │ │ │ └── get/ │ │ │ │ └── foobar.js │ │ │ └── foo/ │ │ │ └── get/ │ │ │ └── foobar.js │ │ └── package.json │ ├── demo_next/ │ │ ├── app/ │ │ │ ├── context.js │ │ │ ├── controller/ │ │ │ │ ├── file.js │ │ │ │ ├── home.js │ │ │ │ ├── session.js │ │ │ │ └── user.js │ │ │ ├── extend/ │ │ │ │ └── application.js │ │ │ ├── router.js │ │ │ └── service/ │ │ │ ├── bar/ │ │ │ │ └── foo.js │ │ │ ├── foo.js │ │ │ ├── old.js │ │ │ └── third/ │ │ │ └── bar/ │ │ │ └── foo.js │ │ ├── app.js │ │ ├── config/ │ │ │ └── config.js │ │ ├── mocks_data/ │ │ │ └── service/ │ │ │ ├── bar/ │ │ │ │ └── foo/ │ │ │ │ └── get/ │ │ │ │ └── foobar.js │ │ │ └── foo/ │ │ │ └── get/ │ │ │ └── foobar.js │ │ └── package.json │ ├── demo_next_h2/ │ │ ├── app/ │ │ │ ├── context.js │ │ │ ├── controller/ │ │ │ │ ├── file.js │ │ │ │ ├── home.js │ │ │ │ ├── session.js │ │ │ │ └── user.js │ │ │ ├── extend/ │ │ │ │ └── application.js │ │ │ ├── router.js │ │ │ └── service/ │ │ │ ├── bar/ │ │ │ │ └── foo.js │ │ │ ├── foo.js │ │ │ ├── old.js │ │ │ └── third/ │ │ │ └── bar/ │ │ │ └── foo.js │ │ ├── app.js │ │ ├── config/ │ │ │ └── config.js │ │ ├── mocks_data/ │ │ │ └── service/ │ │ │ ├── bar/ │ │ │ │ └── foo/ │ │ │ │ └── get/ │ │ │ │ └── foobar.js │ │ │ └── foo/ │ │ │ └── get/ │ │ │ └── foobar.js │ │ └── package.json │ ├── disable-security/ │ │ ├── app/ │ │ │ ├── context.js │ │ │ ├── controller/ │ │ │ │ ├── home.js │ │ │ │ └── session.js │ │ │ ├── router.js │ │ │ └── service/ │ │ │ ├── bar/ │ │ │ │ └── foo.js │ │ │ ├── foo.js │ │ │ ├── old.js │ │ │ └── third/ │ │ │ └── bar/ │ │ │ └── foo.js │ │ ├── app.js │ │ ├── config/ │ │ │ ├── config.js │ │ │ └── plugin.js │ │ ├── mocks_data/ │ │ │ └── service/ │ │ │ ├── bar/ │ │ │ │ └── foo/ │ │ │ │ └── get/ │ │ │ │ └── foobar.js │ │ │ └── foo/ │ │ │ └── get/ │ │ │ └── foobar.js │ │ └── package.json │ ├── error-framework/ │ │ ├── index.js │ │ └── package.json │ ├── failed-app/ │ │ ├── config/ │ │ │ └── config.default.js │ │ ├── package.json │ │ └── test/ │ │ └── index.test.js │ ├── fooPlugin/ │ │ ├── app.js │ │ ├── config/ │ │ │ ├── config.default.js │ │ │ └── plugin.js │ │ └── package.json │ ├── get-app-failed/ │ │ ├── config/ │ │ │ └── config.default.js │ │ ├── package.json │ │ └── test/ │ │ └── index.test.js │ ├── messenger-binding/ │ │ ├── agent.js │ │ ├── app.js │ │ ├── config/ │ │ │ └── config.default.js │ │ └── package.json │ ├── plugin/ │ │ └── package.json │ ├── plugin-bootstrap/ │ │ ├── package.json │ │ └── test.js │ ├── plugin_throw/ │ │ └── package.json │ ├── request/ │ │ ├── app/ │ │ │ └── router.js │ │ ├── config/ │ │ │ └── config.default.js │ │ └── package.json │ ├── server/ │ │ ├── app.js │ │ └── package.json │ ├── setup-app/ │ │ ├── config/ │ │ │ └── config.default.js │ │ ├── package.json │ │ └── test/ │ │ ├── .setup.js │ │ └── index.test.js │ ├── tegg-app/ │ │ ├── app/ │ │ │ └── modules/ │ │ │ └── foo/ │ │ │ ├── LogService.ts │ │ │ └── package.json │ │ ├── app.js │ │ ├── config/ │ │ │ ├── config.default.js │ │ │ └── plugin.js │ │ ├── package.json │ │ ├── test/ │ │ │ ├── hooks.test.ts │ │ │ ├── multi_mock_context.test.ts │ │ │ ├── tegg.test.ts │ │ │ └── tegg_context.test.ts │ │ ├── tsconfig.json │ │ └── typing.ts │ ├── test-case-create-context-failed/ │ │ ├── config/ │ │ │ └── config.default.js │ │ ├── package.json │ │ └── test/ │ │ └── index.test.js │ ├── test-case-get-app-failed/ │ │ ├── config/ │ │ │ └── config.default.js │ │ ├── package.json │ │ └── test/ │ │ └── index.test.js │ └── yadan_app/ │ ├── config/ │ │ └── config.default.js │ └── package.json ├── format_options.test.js ├── index.test.ts ├── inject_ctx.test.js ├── mm.test.js ├── mock_agent_httpclient.test.js ├── mock_cluster_extend.test.js ├── mock_cluster_restore.test.js ├── mock_cluster_without_security_plugin.test.js ├── mock_context.test.js ├── mock_cookies.test.js ├── mock_csrf.test.js ├── mock_custom_loader.test.js ├── mock_env.test.js ├── mock_headers.test.js ├── mock_httpclient.test.js ├── mock_httpclient_next.test.js ├── mock_httpclient_next_h2.test.js ├── mock_request.test.js ├── mock_service.test.js ├── mock_service_async.test.js ├── mock_service_cluster.test.js ├── mock_session.test.js ├── parallel.test.js ├── parallel_hook.test.js └── tsd.test.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintrc ================================================ { "extends": "eslint-config-egg", "globals": { "beforeAll": true } } ================================================ FILE: .github/workflows/nodejs.yml ================================================ name: CI on: push: branches: [ master ] pull_request: branches: [ master ] jobs: Job: name: Node.js uses: node-modules/github-actions/.github/workflows/node-test.yml@master with: os: 'ubuntu-latest' version: '14.18.0, 14, 16, 18, 20, 22' secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} ================================================ FILE: .github/workflows/pkg.pr.new.yml ================================================ 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 ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: push: branches: [ master ] jobs: release: name: Node.js uses: eggjs/github-actions/.github/workflows/node-release.yml@master secrets: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} GIT_TOKEN: ${{ secrets.GIT_TOKEN }} ================================================ FILE: .gitignore ================================================ node_modules/ coverage/ logs/ !test/fixtures/apps/app-not-clean/logs/keep !test/fixtures/yadan/node_modules run/ .vscode .idea .nyc_output package-lock.json ================================================ FILE: CHANGELOG.md ================================================ # Changelog ## [5.15.1](https://github.com/eggjs/egg-mock/compare/v5.15.0...v5.15.1) (2024-12-12) ### Bug Fixes * try to use urllib4 ([#177](https://github.com/eggjs/egg-mock/issues/177)) ([c8551b9](https://github.com/eggjs/egg-mock/commit/c8551b98f044c7315a67f579cc2c1b4f5dde73c4)) ## [5.15.0](https://github.com/eggjs/egg-mock/compare/v5.14.0...v5.15.0) (2024-12-10) ### Features * use @eggjs/utils ([#176](https://github.com/eggjs/egg-mock/issues/176)) ([48000cb](https://github.com/eggjs/egg-mock/commit/48000cb468fd25ac258d728ecc9afca21a8f6284)) ## [5.14.0](https://github.com/eggjs/egg-mock/compare/v5.13.0...v5.14.0) (2024-12-09) ### Features * detect-port@2, is-type-of@2, get-ready@3 ([#175](https://github.com/eggjs/egg-mock/issues/175)) ([0364af8](https://github.com/eggjs/egg-mock/commit/0364af8d87237bc6c08e2f9306945daa1d9144c9)) ## [5.13.0](https://github.com/eggjs/egg-mock/compare/v5.12.5...v5.13.0) (2024-12-07) ### Features * mv urllib to peerDependencies ([#174](https://github.com/eggjs/egg-mock/issues/174)) ([df7dc48](https://github.com/eggjs/egg-mock/commit/df7dc48be38f7f323f91213aa3ce3bd5e7879a58)) ## [5.12.5](https://github.com/eggjs/egg-mock/compare/v5.12.4...v5.12.5) (2024-07-05) ### Bug Fixes * should run restore in the current event loop ([#172](https://github.com/eggjs/egg-mock/issues/172)) ([1f9fc01](https://github.com/eggjs/egg-mock/commit/1f9fc0179ac524cbeb0532b92783233c0274ce71)) ## [5.12.4](https://github.com/eggjs/egg-mock/compare/v5.12.3...v5.12.4) (2024-07-04) ### Bug Fixes * should reset agent on restore ([#171](https://github.com/eggjs/egg-mock/issues/171)) ([088ac41](https://github.com/eggjs/egg-mock/commit/088ac41d96daecf62c544ca954812d3721ac06cc)) ## [5.12.3](https://github.com/eggjs/egg-mock/compare/v5.12.2...v5.12.3) (2024-07-02) ### Bug Fixes * don't close used mock agent ([#170](https://github.com/eggjs/egg-mock/issues/170)) ([3e7a504](https://github.com/eggjs/egg-mock/commit/3e7a5044e3a687c8accbfc9f8a60c469bf4fdad6)) ## [5.12.2](https://github.com/eggjs/egg-mock/compare/v5.12.1...v5.12.2) (2024-07-02) ### Bug Fixes * export mockHttpClient types ([#169](https://github.com/eggjs/egg-mock/issues/169)) ([ddab25f](https://github.com/eggjs/egg-mock/commit/ddab25f1b06e1e513c2dd523bf7d773970d37031)) ## [5.12.1](https://github.com/eggjs/egg-mock/compare/v5.12.0...v5.12.1) (2024-07-02) ### Bug Fixes * support httpclient mock on allowH2 = true ([#168](https://github.com/eggjs/egg-mock/issues/168)) ([3a434bb](https://github.com/eggjs/egg-mock/commit/3a434bb6a90c98b957b6d92e059c09297bf7cf18)) ## [5.12.0](https://github.com/eggjs/egg-mock/compare/v5.11.0...v5.12.0) (2024-06-02) ### Features * change backgroundTasksFinished method from private to public ([#163](https://github.com/eggjs/egg-mock/issues/163)) ([86b4d45](https://github.com/eggjs/egg-mock/commit/86b4d452f0e5e851e8fd550a74f244c91de5c093)) ## [5.11.0](https://github.com/eggjs/egg-mock/compare/v5.10.9...v5.11.0) (2024-06-02) ### Features * use egg-logger@3 and sdk-base@4 ([#167](https://github.com/eggjs/egg-mock/issues/167)) ([e02e72e](https://github.com/eggjs/egg-mock/commit/e02e72ef561ae029be3671ebf2a8e37ad06dc2d1)) ## [5.10.9](https://github.com/eggjs/egg-mock/compare/v5.10.8...v5.10.9) (2023-11-08) ### Bug Fixes * allow to call mockHttpclient multi times ([#165](https://github.com/eggjs/egg-mock/issues/165)) ([370e42d](https://github.com/eggjs/egg-mock/commit/370e42d777d89dcfde1773db6404284439b38725)) ## [5.10.8](https://github.com/eggjs/egg-mock/compare/v5.10.7...v5.10.8) (2023-06-21) ### Bug Fixes * fix mocha failed title for inject_ctx ([#162](https://github.com/eggjs/egg-mock/issues/162)) ([f6f59ac](https://github.com/eggjs/egg-mock/commit/f6f59ac48789fb2c81dfd3025b626c2ed4090a36)) ## [5.10.7](https://github.com/eggjs/egg-mock/compare/v5.10.6...v5.10.7) (2023-05-29) ### Bug Fixes * set types to index.d.ts ([#161](https://github.com/eggjs/egg-mock/issues/161)) ([7de47a1](https://github.com/eggjs/egg-mock/commit/7de47a1e6bb8d4d003b93c130d1e08c7ac0c8bf7)) ## [5.10.6](https://github.com/eggjs/egg-mock/compare/v5.10.5...v5.10.6) (2023-02-22) ### Bug Fixes * disable app close on parallel ([#160](https://github.com/eggjs/egg-mock/issues/160)) ([5a08d33](https://github.com/eggjs/egg-mock/commit/5a08d33ffcbc047ef374a5ab99bee00ad5675808)) ## [5.10.5](https://github.com/eggjs/egg-mock/compare/v5.10.4...v5.10.5) (2023-02-16) ### Bug Fixes * should close app on afterAll hook ([#158](https://github.com/eggjs/egg-mock/issues/158)) ([081b157](https://github.com/eggjs/egg-mock/commit/081b1574c31cfe274083b21a1e11bc81304ab1d5)) ## [5.10.4](https://github.com/eggjs/egg-mock/compare/v5.10.3...v5.10.4) (2023-01-31) ### Bug Fixes * use symbol to access suite app ([#156](https://github.com/eggjs/egg-mock/issues/156)) ([a130dd3](https://github.com/eggjs/egg-mock/commit/a130dd307258df540b65b6d445b56681f69d9232)) ## [5.10.3](https://github.com/eggjs/egg-mock/compare/v5.10.2...v5.10.3) (2023-01-30) ### Bug Fixes * inject failed should make suite/test failed ([#154](https://github.com/eggjs/egg-mock/issues/154)) ([f9f2d4c](https://github.com/eggjs/egg-mock/commit/f9f2d4c5184b2fd2a60485d9333bfefb2c42fabb)) ## [5.10.2](https://github.com/eggjs/egg-mock/compare/v5.10.1...v5.10.2) (2023-01-30) ### Bug Fixes * make sure app ready on parallel mode ([#155](https://github.com/eggjs/egg-mock/issues/155)) ([83c600e](https://github.com/eggjs/egg-mock/commit/83c600e8cb8139a232f1066c2925562574102dbe)) ## [5.10.1](https://github.com/eggjs/egg-mock/compare/v5.10.0...v5.10.1) (2023-01-29) ### Bug Fixes * fix mockContext with headers ([#153](https://github.com/eggjs/egg-mock/issues/153)) ([5e3e816](https://github.com/eggjs/egg-mock/commit/5e3e816395d8be37548d4bb72c3e3dc2d098bee1)) ## [5.10.0](https://github.com/eggjs/egg-mock/compare/v5.9.4...v5.10.0) (2023-01-28) ### Features * add default ua egg-mock/${version} ([#151](https://github.com/eggjs/egg-mock/issues/151)) ([ccb28f5](https://github.com/eggjs/egg-mock/commit/ccb28f5a99148528459f1f8b120254f3feb64561)) * impl setGetAppCallback ([#152](https://github.com/eggjs/egg-mock/issues/152)) ([b7d902c](https://github.com/eggjs/egg-mock/commit/b7d902cf990ee90181f24e9722f42560858118eb)) ## [5.9.4](https://github.com/eggjs/egg-mock/compare/v5.9.3...v5.9.4) (2023-01-18) ### Bug Fixes * use originalUrl to check mock call function request ([#149](https://github.com/eggjs/egg-mock/issues/149)) ([abe1c07](https://github.com/eggjs/egg-mock/commit/abe1c07a2abb4b98c37a34725e4917caafe6dc7e)) ## [5.9.3](https://github.com/eggjs/egg-mock/compare/v5.9.2...v5.9.3) (2023-01-18) ### Bug Fixes * every it should has self ctx ([#150](https://github.com/eggjs/egg-mock/issues/150)) ([bf33c1c](https://github.com/eggjs/egg-mock/commit/bf33c1cbde00b0f10d49184bda305c2c98a58401)) ## [5.9.2](https://github.com/eggjs/egg-mock/compare/v5.9.1...v5.9.2) (2023-01-17) ### Bug Fixes * mocha should be peer deps ([#148](https://github.com/eggjs/egg-mock/issues/148)) ([9a5fcca](https://github.com/eggjs/egg-mock/commit/9a5fcca1ff19f2bc7d74a8122becc1bf0ddfe89d)) ## [5.8.4](https://github.com/eggjs/egg-mock/compare/v5.8.3...v5.8.4) (2023-01-12) ### Bug Fixes * only await app ready when app exists ([#145](https://github.com/eggjs/egg-mock/issues/145)) ([f4ed458](https://github.com/eggjs/egg-mock/commit/f4ed458441449da70fd4108747377b7890dc08fb)) ## [5.8.3](https://github.com/eggjs/egg-mock/compare/v5.8.2...v5.8.3) (2023-01-11) ### Bug Fixes * app should wait for agent ready on parallel mode ([#144](https://github.com/eggjs/egg-mock/issues/144)) ([205e836](https://github.com/eggjs/egg-mock/commit/205e836ba11a869da208c441a9b90edd6e61b3b1)) ## [5.8.2](https://github.com/eggjs/egg-mock/compare/v5.8.1...v5.8.2) (2023-01-10) ### Bug Fixes * ignore bootstrap error on non egg project ([#142](https://github.com/eggjs/egg-mock/issues/142)) ([880c282](https://github.com/eggjs/egg-mock/commit/880c282481024005456074a123b4b1f185971f4c)) ## [5.8.1](https://github.com/eggjs/egg-mock/compare/v5.8.0...v5.8.1) (2023-01-09) ### Bug Fixes * add register.js to files ([#141](https://github.com/eggjs/egg-mock/issues/141)) ([a87e801](https://github.com/eggjs/egg-mock/commit/a87e8019724dbb2401bd41ccf95be26c571e8e8f)) ## [5.8.0](https://github.com/eggjs/egg-mock/compare/v5.7.1...v5.8.0) (2023-01-09) ### Features * use mocha global hook to register before/after ([#140](https://github.com/eggjs/egg-mock/issues/140)) ([9ef65de](https://github.com/eggjs/egg-mock/commit/9ef65de3382c15c5fdeff2e8d0b14eed8144a41b)) ## [5.7.1](https://github.com/eggjs/egg-mock/compare/v5.7.0...v5.7.1) (2023-01-07) ### Bug Fixes * should add egg to peerDependencies ([#139](https://github.com/eggjs/egg-mock/issues/139)) ([2134fc7](https://github.com/eggjs/egg-mock/commit/2134fc73661e4c2de433aceda2ea45811c8bff8b)) ## [5.7.0](https://github.com/eggjs/egg-mock/compare/v5.6.0...v5.7.0) (2023-01-03) ### Features * remove power-assert ([#138](https://github.com/eggjs/egg-mock/issues/138)) ([0c9fad2](https://github.com/eggjs/egg-mock/commit/0c9fad2f7a6f739080f2b996d8f6bb98852af12a)) ## [5.6.0](https://github.com/eggjs/egg-mock/compare/v5.5.0...v5.6.0) (2023-01-03) ### Features * upgrade globby to v11 ([#137](https://github.com/eggjs/egg-mock/issues/137)) ([b0af3eb](https://github.com/eggjs/egg-mock/commit/b0af3eb471a394448236e4cb18863e60218e0d2a)) ## [5.5.0](https://github.com/eggjs/egg-mock/compare/v5.4.0...v5.5.0) (2022-12-28) ### Features * add mockContextScope ([#136](https://github.com/eggjs/egg-mock/issues/136)) ([9db9afa](https://github.com/eggjs/egg-mock/commit/9db9afaadfb73a58d03b3cbdbd0c8c6515e6578a)) --- 5.4.0 / 2022-12-14 ================== **features** * [[`0bd71bc`](http://github.com/eggjs/egg-mock/commit/0bd71bc732b430f679854cceb5017238aca67f38)] - 📦 NEW: Allow restore mockAgent only (#134) (fengmk2 <>) 5.3.0 / 2022-12-09 ================== **features** * [[`6608f01`](http://github.com/eggjs/egg-mock/commit/6608f01983b6891fad2eea758e426bb205dff117)] - 📦 NEW: mock context support ctxStorage (#133) (fengmk2 <>) 5.2.1 / 2022-11-11 ================== **fixes** * [[`18c366d`](http://github.com/eggjs/egg-mock/commit/18c366d4d8f110e8b5e166bb4109be6659f59dd2)] - fix: use global hook for register global hook (#132) (killa <>) 5.2.0 / 2022-11-08 ================== **features** * [[`209c921`](http://github.com/eggjs/egg-mock/commit/209c921cd07eeb541793d5eb28a4982c891ef337)] - feat: add EGG_FRAMEWORK for bootstrap custom framework (#131) (killa <>) 5.1.0 / 2022-11-04 ================== **features** * [[`22f508c`](http://github.com/eggjs/egg-mock/commit/22f508cba2f6330172db89efde3a678ed35c5258)] - feat: impl parallel app for mocha parallel mode (#130) (killa <>) 5.0.2 / 2022-10-09 ================== **fixes** * [[`299f7ec`](http://github.com/eggjs/egg-mock/commit/299f7ecc7314737c182c1745fce0d32c61cd657d)] - 🐛 FIX: Should use urllib-next package (#129) (fengmk2 <>) **others** * [[`17a6713`](http://github.com/eggjs/egg-mock/commit/17a6713663555f55832c12fa12c68803a59aa925)] - 🤖 TEST: Add httpclient streaming mocking (#128) (fengmk2 <>) 5.0.1 / 2022-09-29 ================== **features** * [[`ae766ff`](http://github.com/eggjs/egg-mock/commit/ae766ff582a3e14a28b97e15bad5a56efcb542bc)] - 👌 IMPROVE: Mock httpclient support delay ms and repeat times (#127) (fengmk2 <>) 5.0.0 / 2022-09-29 ================== **features** * [[`60658ec`](http://github.com/eggjs/egg-mock/commit/60658ecae4a16ac1e52dc3238c279a61262f4620)] - 📦 NEW: [BREAKING] Support egg 3.0 (#126) (fengmk2 <>) 4.2.1 / 2022-05-17 ================== **features** * [[`983e610`](http://github.com/eggjs/egg-mock/commit/983e6106b3047f3c0b8d1871fecf4b851cb3000a)] - feat: allow other envtype (#125) (吖猩 <>) **others** * [[`bb9cb79`](http://github.com/eggjs/egg-mock/commit/bb9cb79dbd3d8999cc887d1f0ea952b5df449086)] - 📖 DOC: Change ci status badge (fengmk2 <>) * [[`cd1f2db`](http://github.com/eggjs/egg-mock/commit/cd1f2dbd08683e60b31b83e3406c32a823124704)] - 📖 DOC: Update contributors (fengmk2 <>) * [[`74c0d8f`](http://github.com/eggjs/egg-mock/commit/74c0d8f5a9532fa9e3b3f1c29ad27b59d46ea984)] - Create codeql-analysis.yml (fengmk2 <>) * [[`6bcc866`](http://github.com/eggjs/egg-mock/commit/6bcc8660a94c761dbdcee0a09bcd6d06e8441f49)] - 🤖 TEST: Fix assert cases (#124) (fengmk2 <>) 4.2.0 / 2021-12-17 ================== **features** * [[`b45ad40`](http://github.com/eggjs/egg-mock/commit/b45ad40dd572977986cd86220f090d28c1268b70)] - feat: add mockLog, expectLog to type define (#121) (fengmk2 <>) 4.1.0 / 2021-04-05 ================== **features** * [[`ce6ecde`](https://github.com/eggjs/egg-mock.git/commit/ce6ecde700a81ce986347834dbd2dc2d48217f88)] - feat: add consoleLogger error when mockApp init error (mansonchor.github.com <>) **others** * [[`e7c73e3`](https://github.com/eggjs/egg-mock.git/commit/e7c73e37814b6c9041f37bbcd92b3bb7c35758b3)] - build: remove node@6 (dead-horse <>) 4.0.1 / 2020-08-19 ================== **features** * [[`d5e584e`](http://github.com/eggjs/egg-mock/commit/d5e584e769dd348bc87a99a90f0a6003dfc7a4cf)] - feat: httpclient support mock async function (#117) (Yiyu He <>) 4.0.0 / 2020-03-01 ================== **features** * [[`c39109f`](http://github.com/eggjs/egg-mock/commit/c39109faa0eff01d8597c3988c04459c3520f309)] - feat: upgrade mm@3 (#116) (Yiyu He <>) 3.25.1 / 2020-01-17 ================== **fixes** * [[`51ef091`](http://github.com/eggjs/egg-mock/commit/51ef091cbb06ae74ff7f9591e3071db648ba5346)] - fix: backgroundTasksFinished ensure all tasks finished (#115) (Yiyu He <>) 3.25.0 / 2019-12-12 ================== **features** * [[`4c31c9e`](http://github.com/eggjs/egg-mock/commit/4c31c9e2917eea449e2afddf96fc8d2aabe6ad5e)] - feat: support init hook before mock app init (#109) (TZ | 天猪 <>) **fixes** * [[`cbab52a`](http://github.com/eggjs/egg-mock/commit/cbab52a697e6e47abd48ce45320b7c40a0463c12)] - fix: enable sendRandom() method in unittest (#114) (GoodMeowing <<36814673+GoodMeowing@users.noreply.github.com>>) 3.24.2 / 2019-11-07 ================== **fixes** * [[`3bf5ded`](http://github.com/eggjs/egg-mock/commit/3bf5ded501608f2b5b3199d8b3d0ca0329dd9df7)] - fix: mockLog don't read file (#113) (Yiyu He <>) 3.24.1 / 2019-09-30 ================== **fixes** * [[`bd305d2`](http://github.com/eggjs/egg-mock/commit/bd305d21bd54395e597f3fce06758fcbb99ba43f)] - fix: single mode will call app.agent.close (#108) (TZ | 天猪 <>) 3.24.0 / 2019-09-26 ================== **features** * [[`315e685`](http://github.com/eggjs/egg-mock/commit/315e685d2059ec61e62e9109da8b58f9bf5552cd)] - feat: support app.notExpectLog() (#107) (fengmk2 <>) 3.23.2 / 2019-09-10 ================== **fixes** * [[`e494325`](http://github.com/eggjs/egg-mock/commit/e494325562b84876a96062fd061ab4f8c7787a2e)] - fix: mockHttpclient with multi-request (#106) (吖猩 <>) * [[`d836536`](http://github.com/eggjs/egg-mock/commit/d8365368e2339f25874a7dfc1c573249ae841e8f)] - fix: fix httpRequest function signature (#105) (Colin Cheng <>) 3.23.1 / 2019-05-20 ================== **fixes** * [[`6be0c43`](http://github.com/eggjs/egg-mock/commit/6be0c431ee1fd651c4f0bb6f433d7c4444b74708)] - fix: rimraf (#104) (TZ | 天猪 <>) 3.23.0 / 2019-05-20 ================== **features** * [[`9ada7f0`](http://github.com/eggjs/egg-mock/commit/9ada7f004def359a0b17f3824cea946abe4ed1f2)] - feat: mockHttpclient support fn (#103) (TZ | 天猪 <>) 3.22.4 / 2019-05-06 ================== **fixes** * [[`478581a`](http://github.com/eggjs/egg-mock/commit/478581a7851d19286c4e689af421a70cae27d26d)] - fix: remove egg-core deps (#101) (TZ | 天猪 <>) 3.22.3 / 2019-05-06 ================== **fixes** * [[`6174f9b`](http://github.com/eggjs/egg-mock/commit/6174f9b37698399785b99e86f2f45630f78a084f)] - fix: throw error when an egg plugin test is using bootstrap (#100) (TZ | 天猪 <>) 3.22.2 / 2019-04-10 ================== **fixes** * [[`a68ca65`](http://github.com/eggjs/egg-mock/commit/a68ca6549428e6c4dc886231d7c6b7fbefab46c6)] - fix: should emit server (#98) (TZ | 天猪 <>) 3.22.1 / 2019-03-12 ================== **fixes** * [[`3f73bad`](http://github.com/eggjs/egg-mock/commit/3f73bad59aa8acbb14399a914d31b8eb348ff493)] - fix: d.ts typo (#97) (TZ | 天猪 <>) 3.22.0 / 2019-03-11 ================== **features** * [[`81ed542`](http://github.com/eggjs/egg-mock/commit/81ed5427853067d84901c1848e630a8002ecfcf0)] - feat: add mock API for customLoader (#95) (Haoliang Gao <>) **fixes** * [[`58d0b32`](http://github.com/eggjs/egg-mock/commit/58d0b32a5851e4cd31492fe0e85c0e81336b6d04)] - fix: remove nonexistent type and correct typing (#96) (Sinux <>) 3.21.0 / 2018-12-27 =================== **features** * [[`93f8009`](https://github.com/eggjs/egg-mock/commit/93f8009c2f4c7d7f24b361f4713e035a2f993134)] - feat: cluster mock support result (#92) (TZ <>) * [[`be3d146`](https://github.com/eggjs/egg-mock/commit/be3d1466bf438a379b85429c40c510d6be7ecc26)] - feat: bootstrap support run on jest env (#93) (fengmk2 <>) 3.20.1 / 2018-09-17 ================== **fixes** * [[`4b5dbb5`](http://github.com/eggjs/egg-mock/commit/4b5dbb512bf8f598d5ea5361c58ae9d40d528ff8)] - fix: add app.mockLog() to improve app.expectLog() more stable (#87) (fengmk2 <>) **others** * [[`a64db33`](http://github.com/eggjs/egg-mock/commit/a64db33d2ee68a76f7c41303e79e37099f33b373)] - deps: add egg-logger dependency (#88) (fengmk2 <>) 3.20.0 / 2018-08-30 ================== **features** * [[`283eef3`](http://github.com/eggjs/egg-mock/commit/283eef3a4f1b0bcd90cc0d6bcf6de9fe136d8503)] - feat: add `app.agent.mockHttpclient()` for agent (#82) (limerick <>) 3.19.7 / 2018-08-28 ================== **fixes** * [[`cc6b976`](http://github.com/eggjs/egg-mock/commit/cc6b976a66103dca44428e9ca4cf6e8d18b8323b)] - fix: app.messenger.broadcast send to self (君羽 <>) 3.19.6 / 2018-08-24 ================== **fixes** * [[`00fb82e`](http://github.com/eggjs/egg-mock/commit/00fb82eac8114f0be1a97421ea270947ea7b5efd)] - fix: fix declaration merging error (#86) (吖猩 <>) 3.19.5 / 2018-08-24 ================== **fixes** * [[`1635a90`](http://github.com/eggjs/egg-mock/commit/1635a9098d16df4ba4195d2e289476471bf96cb2)] - fix: show expectLog last 500 words on assert error (#85) (fengmk2 <>) 3.19.4 / 2018-08-24 =================== * feat: .d.ts 新增继承自 mm 的 api (#81) 3.19.3 / 2018-08-16 ================== **fixes** * [[`c91bf93`](http://github.com/eggjs/egg-mock/commit/c91bf93e792c788c4cdd7cf786a45fc2ecb4511d)] - fix: allow egg-core module missing (#83) (fengmk2 <>) 3.19.2 / 2018-08-07 ================== **fixes** * [[`1710f7f`](http://github.com/eggjs/egg-mock/commit/1710f7fcfdbd8709d6b4c50817ab0c214c525378)] - fix: put mock restore at the end (#80) (fengmk2 <>) 3.19.1 / 2018-08-07 ================== **fixes** * [[`db3cb11`](http://github.com/eggjs/egg-mock/commit/db3cb11a97ec6bdb3a70222a459241ffc3cc2c47)] - fix: make sure backgroundTasksFinished() return promise (#79) (fengmk2 <>) 3.19.0 / 2018-08-06 ================== **features** * [[`ab5a47e`](https://github.com/eggjs/egg-mock.git/commit/ab5a47e12f1fea4300a44ef19aa4ba300574d18a)] - feat: should wait for background task finish on afterEach (#78) (fengmk2 <>) 3.18.0 / 2018-08-03 ================== **features** * [[`f25c50a`](http://github.com/eggjs/egg-mock/commit/f25c50a24433e251e5c9f905170cea87e3ac93e6)] - feat: add `app.expectLog()` for app and cluster (#77) (fengmk2 <>) **others** * [[`ffb1187`](http://github.com/eggjs/egg-mock/commit/ffb1187aab11bc544c4bc6c5921ca0fba28e621f)] - chore: improve tsd and add bootstrap.d.ts (#76) (SuperEVO <>) 3.17.3 / 2018-07-14 =================== * types: add bootstrap.d.ts (#75) 3.17.2 / 2018-05-21 ================== **others** * [[`62c3dfa`](http://github.com/eggjs/egg-mock/commit/62c3dfa517b94c56c35fed8af8d9aad29e7c38d4)] - refactor: middleware use promise-based style (#74) (Haoliang Gao <>) 3.17.1 / 2018-04-21 =================== * fix: remove options.typescript support (#73) 3.17.0 / 2018-03-30 =================== * feat: support ts from env and pkg (#71) 3.16.0 / 2018-03-28 =================== * feat: support ts (#70) * fix: mockSession save should not be enumerable (#69) 3.15.1 / 2018-03-20 ================== **fixes** * [[`3fbf862`](http://github.com/eggjs/egg-mock/commit/3fbf86232ee3c8e4944c8072e127c0f1ede1d26b)] - fix: mockSession save (#68) (TZ | 天猪 <>) 3.15.0 / 2018-03-08 ================== **features** * [[`9857065`](http://github.com/eggjs/egg-mock/commit/985706518e9ab8be155f285490484e5a304833fc)] - feat: add unexpectHeader() and expectHeader() (#67) (fengmk2 <>) * [[`f1820d7`](http://github.com/eggjs/egg-mock/commit/f1820d70f2e266d4b18fb7062976b4c14952a16f)] - feat: mm.app() support server event (#65) (fengmk2 <>) 3.14.1 / 2018-02-28 ================== **fixes** * [[`d38d615`](http://github.com/eggjs/egg-mock/commit/d38d615c3f9bc79eb09c6864ab9d5833a50d029a)] - fix: mockUrl accepts RegExp (#64) (Brick <>) **others** * [[`23c1075`](http://github.com/eggjs/egg-mock/commit/23c1075f5aaaa866b0243061d0eadf21ce67d382)] - test: add post with multipart file test cases (#63) (fengmk2 <>) 3.14.0 / 2017-12-12 ================== **others** * [[`be9bcd2`](http://github.com/eggjs/egg-mock/commit/be9bcd22c91044b0efdbc3db6b8109cf625002b1)] - refactor: modify d.ts and support bootstrap (Eward Song <>) 3.13.1 / 2017-10-17 ================== **fixes** * [[`9d071b2`](http://github.com/eggjs/egg-mock/commit/9d071b28c5ef341ee63ccb06f00f724922c698b2)] - fix: support mock the same property multiple times (#61) (Yiyu He <>) 3.13.0 / 2017-10-10 ================== **features** * [[`30ca0c9`](http://github.com/eggjs/egg-mock/commit/30ca0c980f3ee8b1f60f5213f0768fe5eeaaf49a)] - feat: port can be customized (#60) (Haoliang Gao <>) 3.12.2 / 2017-09-22 ================== **fixes** * [[`5935564`](http://github.com/eggjs/egg-mock/commit/5935564d1e649f8702c0f3f79e67efde10717542)] - fix: missing methods package (dainli <>) **others** * [[`e7f518a`](http://github.com/eggjs/egg-mock/commit/e7f518a92e1686973bea557eb0a21f1d293ab0b4)] - fix(mockHttpclient): should use the copy of mockResult (#58) (Haoliang Gao <>) * [new tag] 3.12.1 -> 3.12.1 3.12.1 / 2017-09-13 ================== **others** * [[`e7f518a`](http://github.com/eggjs/egg-mock/commit/e7f518a92e1686973bea557eb0a21f1d293ab0b4)] - fix(mockHttpclient): should use the copy of mockResult (#58) (Haoliang Gao <>) 3.12.0 / 2017-09-12 ================== **others** * [[`25a0e28`](http://github.com/eggjs/egg-mock/commit/25a0e28e85209ec08a593b38cd434ed389ef8887)] - feat(mockHttpclient): use Regular Expression for matching url (#57) (Haoliang Gao <>) 3.11.0 / 2017-09-11 ================== **features** * [[`f1a08a6`](http://github.com/eggjs/egg-mock/commit/f1a08a654a08313c0848828ee9051f8bf174fc6a)] - feat: support httpRequest().get(routerName) (#56) (fengmk2 <>) 3.10.0 / 2017-08-30 ================== **features** * [[`f3654df`](http://github.com/eggjs/egg-mock/commit/f3654df99d4bee2ea0ee1ef580af7af66f21255d)] - feat: base promise to support async function (#55) (Yiyu He <>) 3.9.1 / 2017-08-14 ================== **fixes** * [[`d6cafaa`](http://github.com/eggjs/egg-mock/commit/d6cafaa531d9bbcc0fc987e7d6fdefd6a515e785)] - fix: fix agent type after ready (#54) (zōng yǔ <>) 3.9.0 / 2017-08-02 ================== **features** * [[`9e1642c`](http://github.com/eggjs/egg-mock/commit/9e1642c7fc569d3cc4a73c9ede6511a18cca6fc5)] - feat: add bootstrap (#53) (Yiyu He <>) 3.8.0 / 2017-06-21 ================== * deps: upgrade dependencies (#51) * test: disable coverage when mm.cluster (#50) 3.7.2 / 2017-06-07 ================== * fix(httpclient): miss headers on options when emit response (#49) 3.7.1 / 2017-06-01 ================== * fix: detect prop object type can be non string (#48) 3.7.0 / 2017-05-18 =================== * feat: support prerequire files (#46) 3.6.1 / 2017-05-11 ================== * fix: ignore all error on cluster mock restore (#45) 3.6.0 / 2017-05-10 ================== * chore: add tsd (#43) * feat: support mock function on cluster mode (#44) * deps: upgrade dependencies (#42) 3.5.0 / 2017-04-25 ================== * feat: mockUrllib support async function (#41) 3.4.0 / 2017-04-17 ================== * feat: should pass when emit egg-ready (#39) 3.3.0 / 2017-04-15 ================== * feat: add app.httpRequest() test helper (#38) 3.2.0 / 2017-03-14 ================== * feat: mockHttpClient support mock multi methods (#35) * test: remove userrole (#34) 3.1.2 / 2017-03-05 ================== * fix: should pass all arguments when mockCookies (#33) 3.1.1 / 2017-03-04 ================== * fix: egg-mock is not a framework (#32) 3.1.0 / 2017-03-02 ================== * feat: use framework instead of customEgg (#31) 3.0.1 / 2017-02-22 ================== * fix: app.close in right order (#30) 3.0.0 / 2017-02-13 ================== * deps: upgrade egg (#29) * fix: bind messenger with app and agent (#28) * feat: [BREAKING_CHANGE] can get error from .ready() (#27) * test: remove unuse codes (#26) 2.4.0 / 2017-02-08 ================== * feat: listen error that thrown when app init (#25) 2.3.1 / 2017-01-26 ================== * fix: improve proxy handler and event listener (#24) 2.3.0 / 2017-01-25 ================== * feat: cluster-client support for mm.app (#23) 2.2.0 / 2017-01-25 ================== * feat: reimplement mm.app (#22) 2.1.0 / 2017-01-16 ================== * feat: support read framework from package.json (#20) 2.0.0 / 2017-01-12 ================== * refactor: use mockHttpclient instead of mockUrllib (#19) 1.3.0 (deprecated) / 2017-01-12 ================== * refactor: use mockHttpclient instead of mockUrllib (#19) 1.2.1 / 2017-01-09 ================== * fix: can't override data when mockContext(data) (#18) * fix: replace the internal link into an github link in the env comment. (#17) 1.2.0 / 2016-11-11 ================== * feat: try to lookup egg that will be the default customEgg (#16) * fix: don't use cache when app from cache is closed (#15) 1.1.0 / 2016-11-02 ================== * feat: add mm.home (#14) 1.0.0 / 2016-11-01 ================== * test: add testcase (#10) 0.0.8 / 2016-10-25 ================== * feat: wait 10ms to close app (#13) 0.0.7 / 2016-10-25 ================== * feat: should close agent when app close (#12) 0.0.6 / 2016-10-24 ================== * feat: cluster should wait process exit (#11) * docs:update readme (#9) * docs: update readme 0.0.5 / 2016-10-11 ================== * feat: pass opt to coffee (#7) 0.0.4 / 2016-08-16 ================== * fix: add eggPath for new egg (#5) ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017-present Alibaba Group Holding Limited and other contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # egg-mock [![NPM version][npm-image]][npm-url] [![Node.js CI](https://github.com/eggjs/egg-mock/actions/workflows/nodejs.yml/badge.svg)](https://github.com/eggjs/egg-mock/actions/workflows/nodejs.yml) [![Test coverage][codecov-image]][codecov-url] [![npm download][download-image]][download-url] [npm-image]: https://img.shields.io/npm/v/egg-mock.svg?style=flat-square [npm-url]: https://npmjs.org/package/egg-mock [codecov-image]: https://codecov.io/github/eggjs/egg-mock/coverage.svg?branch=master [codecov-url]: https://codecov.io/github/eggjs/egg-mock?branch=master [download-image]: https://img.shields.io/npm/dm/egg-mock.svg?style=flat-square [download-url]: https://npmjs.org/package/egg-mock Mock library for testing Egg applications, plugins and custom Egg frameworks with ease. `egg-mock` inherits all APIs from [node_modules/mm](https://github.com/node-modules/mm), offering more flexibility. ## Install ```bash $ npm i egg-mock --save-dev ``` ## Usage ### Create testcase Launch a mock server with `mm.app` ```js // test/index.test.js const path = require('path'); const mm = require('egg-mock'); describe('some test', () => { let app; before(() => { app = mm.app({ baseDir: 'apps/foo' }); return app.ready(); }) after(() => app.close()); it('should request /', () => { return app.httpRequest() .get('/') .expect(200); }); }); ``` Retrieve Agent instance through `app.agent` after `mm.app` started. Using `mm.cluster` launch cluster server, you can use the same API as `mm.app`; ### Test Application `baseDir` is optional that is `process.cwd()` by default. ```js before(() => { app = mm.app(); return app.ready(); }); ``` ### Test Framework framework is optional, it's `node_modules/egg` by default. ```js before(() => { app = mm.app({ baseDir: 'apps/demo', framework: true, }); return app.ready(); }); ``` ### Test Plugin If `eggPlugin.name` is defined in `package.json`, it's a plugin that will be loaded to plugin list automatically. ```js before(() => { app = mm.app({ baseDir: 'apps/demo', }); return app.ready(); }); ``` You can also test the plugin in different framework, e.g. test [aliyun-egg](https://github.com/eggjs/aliyun-egg) and framework-b in one plugin. ```js describe('aliyun-egg', () => { let app; before(() => { app = mm.app({ baseDir: 'apps/demo', framework: path.join(__dirname, 'node_modules/aliyun-egg'), }); return app.ready(); }); }); describe('framework-b', () => { let app; before(() => { app = mm.app({ baseDir: 'apps/demo', framework: path.join(__dirname, 'node_modules/framework-b'), }); return app.ready(); }); }); ``` If it's detected as an plugin, but you don't want it to be, you can use `plugin = false`. ```js before(() => { app = mm.app({ baseDir: 'apps/demo', plugin: false, }); return app.ready(); }); ``` ## API ### mm.app(options) Create a mock application. ### mm.cluster(options) Create a mock cluster server, but you can't use API in application, you should test using `supertest`. ```js const mm = require('egg-mock'); describe('test/app.js', () => { let app, config; before(() => { app = mm.cluster(); return app.ready(); }); after(() => app.close()); it('some test', () => { return app.httpRequest() .get('/config') .expect(200) }); }); ``` You can disable coverage, because it's slow. ```js mm.cluster({ coverage: false, }); ``` ### mm.env(env) Mock env when starting ```js // production environment mm.env('prod'); mm.app({ cache: false, }); ``` Environment list ### mm.consoleLevel(level) Mock level that print to stdout/stderr ```js // DON'T log to terminal mm.consoleLevel('NONE'); ``` level list: `DEBUG`, `INFO`, `WARN`, `ERROR`, `NONE` ### mm.home(homePath) mock home directory ### mm.restore() restore all mock data, e.g. `afterEach(mm.restore)` ### options Options for `mm.app` and `mm.cluster` #### baseDir {String} The directory of application, default is `process.cwd()`. ```js mm.app({ baseDir: path.join(__dirname, 'fixtures/apps/demo'), }) ``` You can use a string based on `$CWD/test/fixtures` for short ```js mm.app({ baseDir: 'apps/demo', }) ``` #### framework {String/Boolean} The directory of framework ```js mm.app({ baseDir: 'apps/demo', framework: path.join(__dirname, 'fixtures/egg'), }) ``` It can be true when test an framework #### plugin The directory of plugin, it's detected automatically. ```js mm.app({ baseDir: 'apps/demo', }) ``` #### plugins {Object} Define a list of plugins #### cache {Boolean} Determine whether enable cache. it's cached by baseDir. #### clean {Boolean} Clean all logs directory, default is true. If you are using `ava`, disable it. ### app.mockLog([logger]) and app.expectLog(str[, logger]), app.notExpectLog(str[, logger]) Assert some string value in the logger instance. It is recommended to pair `app.mockLog()` with `app.expectLog()` or `app.notExpectLog()`. Using `app.expectLog()` or `app.notExpectLog()` alone requires dependency on the write speed of the log. When the server disk is high IO, unstable results will occur. ```js it('should work', async () => { app.mockLog(); await app.httpRequest() .get('/') .expect('hello world') .expect(200); app.expectLog('foo in logger'); app.expectLog('foo in coreLogger', 'coreLogger'); app.expectLog('foo in myCustomLogger', 'myCustomLogger'); app.notExpectLog('bar in logger'); app.notExpectLog('bar in coreLogger', 'coreLogger'); app.notExpectLog('bar in myCustomLogger', 'myCustomLogger'); }); ``` ### app.httpRequest() Request current app http server. ```js it('should work', () => { return app.httpRequest() .get('/') .expect('hello world') .expect(200); }); ``` See [supertest](https://github.com/visionmedia/supertest) to get more APIs. #### .unexpectHeader(name) Assert current response not contains the specified header ```js it('should work', () => { return app.httpRequest() .get('/') .unexpectHeader('set-cookie') .expect(200); }); ``` #### .expectHeader(name) Assert current response contains the specified header ```js it('should work', () => { return app.httpRequest() .get('/') .expectHeader('set-cookie') .expect(200); }); ``` ### app.mockContext(options) ```js const ctx = app.mockContext({ user: { name: 'Jason' } }); console.log(ctx.user.name); // Jason ``` ### app.mockContextScope(fn, options) ```js await app.mockContextScope(async ctx => { console.log(ctx.user.name); // Jason }, { user: { name: 'Jason' } }); ``` ### app.mockCookies(data) ```js app.mockCookies({ foo: 'bar' }); const ctx = app.mockContext(); console.log(ctx.getCookie('foo')); ``` ### app.mockHeaders(data) Mock request header ### app.mockSession(data) ```js app.mockSession({ foo: 'bar' }); const ctx = app.mockContext(); console.log(ctx.session.foo); ``` ### app.mockService(service, methodName, fn) ```js it('should mock user name', function* () { app.mockService('user', 'getName', function* (ctx, methodName, args) { return 'popomore'; }); const ctx = app.mockContext(); yield ctx.service.user.getName(); }); ``` ### app.mockServiceError(service, methodName, error) You can mock an error for service ```js app.mockServiceError('user', 'home', new Error('mock error')); ``` ### app.mockCsrf() ```js app.mockCsrf(); return app.httpRequest() .post('/login') .expect(302); ``` ### app.mockHttpclient(url, method, data) Mock httpclient request, e.g.: `ctx.curl` ```js app.get('/', function*() { const ret = yield this.curl('https://eggjs.org'); this.body = ret.data.toString(); }); app.mockHttpclient('https://eggjs.org', { // can be buffer / string / json / function // will auto convert to buffer // follow options.dataType to convert data: 'mock egg', }); // app.mockHttpclient('https://eggjs.org', 'get', mockResponse); // mock get // app.mockHttpclient('https://eggjs.org', [ 'get' , 'head' ], mockResponse); // mock get and head // app.mockHttpclient('https://eggjs.org', '*', mockResponse); // mock all methods // app.mockHttpclient('https://eggjs.org', mockResponse); // mock all methods by default // app.mockHttpclient('https://eggjs.org', 'get', function(url, opt) { return 'xxx' }); // support fn return app.httpRequest() .post('/') .expect('mock egg'); ``` You can also use Regular Expression for matching url. ```js app.mockHttpclient(/\/users\/[a-z]$/i, { data: { name: 'egg', }, }); ``` You can alse mock agent.httpclient ```js app.agent.mockHttpclient('https://eggjs.org', { data: { name: 'egg', }, }); ``` ## Bootstrap We also provide a bootstrap file for applications' unit test to reduce duplicated code: ```js const { app, mock, assert } = require('egg-mock/bootstrap'); describe('test app', () => { it('should request success', () => { // mock data will be restored each case mock.data(app, 'method', { foo: 'bar' }); return app.httpRequest() .get('/foo') .expect(res => { assert(!res.headers.foo); }) .expect(/bar/); }); }); describe('test ctx', () => { it('can use ctx', async function() { const res = await this.ctx.service.foo(); assert(res === 'foo'); }); }); ``` We inject ctx to every test case, so you can use `app.currentContext` in your test case. and the first call of `app.mockContext` will reuse `app.currentContext`. ```js const { app, mock, assert } = require('egg-mock/bootstrap'); describe('test ctx', () => { it('should can use ctx', () => { const ctx = app.currentContext; assert(ctx); }); it('should reuse ctx', () => { const ctx = app.currentContext; // first call will reuse app.currentContext const mockCtx = app.mockContext(); assert(ctx === mockCtx); // next call will create a new context // multi call app.mockContext will get wrong context with app.currentContext // so we recommend to use app.mockContextScope const mockCtx2 = app.mockContext(); assert(ctx !== mockCtx); }); }); ``` And if you use mm.app to bootstrap app, you can manually call setGetAppCallback, then egg-mock will inject ctx for each test case. ```js // test/.setup.js const mm = require('egg-mock'); const path = require('path'); before(async function() { const app = this.app = mm.app(); mm.setGetAppCallback(() => { return app; }); await app.ready(); }); // test/index.test.js it('should work', function() { // eslint-disable-next-line no-undef assert(this.app.currentContext); }); ``` ### env for custom bootstrap EGG_BASE_DIR: the base dir of egg app EGG_FRAMEWORK: the framework of egg app ## Questions & Suggestions Please open an issue [here](https://github.com/eggjs/egg/issues). ## License [MIT](LICENSE) ## Contributors [![Contributors](https://contrib.rocks/image?repo=eggjs/egg-mock)](https://github.com/eggjs/egg-mock/graphs/contributors) Made with [contributors-img](https://contrib.rocks). ================================================ FILE: README.zh_CN.md ================================================ # egg-mock [![NPM version][npm-image]][npm-url] [![Node.js CI](https://github.com/eggjs/egg-mock/actions/workflows/nodejs.yml/badge.svg)](https://github.com/eggjs/egg-mock/actions/workflows/nodejs.yml) [![Test coverage][codecov-image]][codecov-url] [![npm download][download-image]][download-url] [npm-image]: https://img.shields.io/npm/v/egg-mock.svg?style=flat-square [npm-url]: https://npmjs.org/package/egg-mock [codecov-image]: https://codecov.io/github/eggjs/egg-mock/coverage.svg?branch=master [codecov-url]: https://codecov.io/github/eggjs/egg-mock?branch=master [download-image]: https://img.shields.io/npm/dm/egg-mock.svg?style=flat-square [download-url]: https://npmjs.org/package/egg-mock 一个数据模拟的库,更方便地测试 Egg 应用、插件及自定义 Egg 框架。`egg-mock` 拓展自 [node_modules/mm](https://github.com/node-modules/mm),你可以使用所有 `mm` 包含的 API。 ## Install ```bash $ npm i egg-mock --save-dev ``` ## Usage ### 创建测试用例 通过 `mm.app` 启动应用,可以使用 App 的 API 模拟数据 ```js // test/index.test.js const path = require('path'); const mm = require('egg-mock'); describe('some test', () => { let app; before(() => { app = mm.app({ baseDir: 'apps/foo' customEgg: path.join(__dirname, '../node_modules/egg'), }); return app.ready(); }) after(() => app.close()); it('should request /', () => { return app.httpRequest() .get('/') .expect(200); }); }); ``` 使用 `mm.app` 启动后可以通过 `app.agent` 访问到 agent 对象。 使用 `mm.cluster` 启动多进程测试,API 与 `mm.app` 一致。 ### 应用开发者 应用开发者不需要传入 baseDir,其为当前路径 ```js before(() => { app = mm.app({ customEgg: path.join(__dirname, '../node_modules/egg'), }); return app.ready(); }); ``` ### 框架开发者 框架开发者需要指定 customEgg,会将当前路径指定为框架入口 ```js before(() => { app = mm.app({ baseDir: 'apps/demo', customEgg: true, }); return app.ready(); }); ``` ### 插件开发者 在插件目录下执行测试用例时,只要 `package.json` 中有 `eggPlugin.name` 字段,就会自动把当前目录加到插件列表中。 ```js before(() => { app = mm.app({ baseDir: 'apps/demo', customEgg: path.join(__dirname, '../node_modules/egg'), }); return app.ready(); }); ``` 也可以通过 customEgg 指定其他框架,比如希望在 aliyun-egg 和 framework-b 同时测试此插件。 ```js describe('aliyun-egg', () => { let app; before(() => { app = mm.app({ baseDir: 'apps/demo', customEgg: path.join(__dirname, 'node_modules/aliyun-egg'), }); return app.ready(); }); }); describe('framework-b', () => { let app; before(() => { app = mm.app({ baseDir: 'apps/demo', customEgg: path.join(__dirname, 'node_modules/framework-b'), }); return app.ready(); }); }); ``` 如果当前目录确实是一个 egg 插件,但是又不想当它是一个插件来测试,可以通过 `options.plugin` 选项来关闭: ```js before(() => { app = mm.app({ baseDir: 'apps/demo', customEgg: path.join(__dirname, 'node_modules/egg'), plugin: false, }); return app.ready(); }); ``` ## API ### mm.app(options) 创建一个 mock 的应用。 ### mm.cluster(options) 创建一个多进程应用,因为是多进程应用,无法获取 worker 的属性,只能通过 supertest 请求。 ```js const mm = require('egg-mock'); describe('test/app.js', () => { let app, config; before(() => { app = mm.cluster(); return app.ready(); }); after(() => app.close()); it('some test', () => { return app.httpRequest() .get('/config') .expect(200) }); }); ``` 默认会启用覆盖率,因为覆盖率比较慢,可以设置 coverage 关闭 ```js mm.cluster({ coverage: false, }); ``` ### mm.env(env) 设置环境变量,主要用于启动阶段,运行阶段可以使用 app.mockEnv。 ```js // 模拟生成环境 mm.env('prod'); mm.app({ cache: false, }); ``` 具体值见 ### mm.consoleLevel(level) mock 终端日志打印级别 ```js // 不输出到终端 mm.consoleLevel('NONE'); ``` 可选 level 为 `DEBUG`, `INFO`, `WARN`, `ERROR`, `NONE` ### mm.home(homePath) 模拟操作系统用户目录 ### mm.restore 还原所有 mock 数据,一般需要结合 `afterEach(mm.restore)` 使用 ### options mm.app 和 mm.cluster 的配置参数 #### baseDir {String} 当前应用的目录,如果是应用本身的测试可以不填默认为 $CWD。 指定完整路径 ```js mm.app({ baseDir: path.join(__dirname, 'fixtures/apps/demo'), }) ``` 也支持缩写,找 test/fixtures 目录下的 ```js mm.app({ baseDir: 'apps/demo', }) ``` #### customEgg {String/Boolean} 指定框架路径 ```js mm.app({ baseDir: 'apps/demo', customEgg: path.join(__dirname, 'fixtures/egg'), }) ``` 对于框架的测试用例,可以指定 true,会自动加载当前路径。 #### plugin 指定插件的路径,只用于插件测试。设置为 true 会将当前路径设置到插件列表。 ```js mm.app({ baseDir: 'apps/demo', plugin: true, }) ``` #### plugins {Object} 传入插件列表,可以自定义多个插件 #### cache {Boolean} 是否需要缓存,默认开启。 是通过 baseDir 缓存的,如果不需要可以关闭,但速度会慢。 #### clean {Boolean} 是否需要清理 log 目录,默认开启。 如果是通过 ava 等并行测试框架进行测试,需要手动在执行测试前进行统一的日志清理,不能通过 mm 来处理,设置 `clean` 为 `false`。 ### app.mockLog([logger]) and app.expectLog(str[, logger]), app.notExpectLog(str[, logger]) 断言指定的字符串记录在指定的日志中。 建议 `app.mockLog()` 和 `app.expectLog()` 或者 `app.notExpectLog()` 配对使用。 单独使用 `app.expectLog()` 或者 `app.notExpectLog()` 需要依赖日志的写入速度,在服务器磁盘高 IO 的时候,会出现不稳定的结果。 ```js it('should work', async () => { // 将日志记录到内存,用于下面的 expectLog app.mockLog(); await app.httpRequest() .get('/') .expect('hello world') .expect(200); app.expectLog('foo in logger'); app.expectLog('foo in coreLogger', 'coreLogger'); app.expectLog('foo in myCustomLogger', 'myCustomLogger'); app.notExpectLog('bar in logger'); app.notExpectLog('bar in coreLogger', 'coreLogger'); app.notExpectLog('bar in myCustomLogger', 'myCustomLogger'); }); ``` ### app.httpRequest() 请求当前应用 http 服务的辅助工具。 ```js it('should work', () => { return app.httpRequest() .get('/') .expect('hello world') .expect(200); }); ``` 更多信息请查看 [supertest](https://github.com/visionmedia/supertest) 的 API 说明。 #### .unexpectHeader(name) 断言当前请求响应不包含指定 header ```js it('should work', () => { return app.httpRequest() .get('/') .unexpectHeader('set-cookie') .expect(200); }); ``` #### .expectHeader(name) 断言当前请求响应包含指定 header ```js it('should work', () => { return app.httpRequest() .get('/') .expectHeader('set-cookie') .expect(200); }); ``` ### app.mockContext(options) 模拟上下文数据 ```js const ctx = app.mockContext({ user: { name: 'Jason' } }); console.log(ctx.user.name); // Jason ``` ### app.mockContextScope(fn, options) 安全的模拟上下文数据,同一用例用多次调用 mockContext 可能会造成 AsyncLocalStorage 污染 ```js await app.mockContextScope(async ctx => { console.log(ctx.user.name); // Jason }, { user: { name: 'Jason' } }); ``` ### app.mockCookies(data) ```js app.mockCookies({ foo: 'bar' }); const ctx = app.mockContext(); console.log(ctx.getCookie('foo')); ``` ### app.mockHeaders(data) 模拟请求头 ### app.mockSession(data) ```js app.mockSession({ foo: 'bar' }); const ctx = app.mockContext(); console.log(ctx.session.foo); ``` ### app.mockService(service, methodName, fn) ```js it('should mock user name', function* () { app.mockService('user', 'getName', function* (ctx, methodName, args) { return 'popomore'; }); const ctx = app.mockContext(); yield ctx.service.user.getName(); }); ``` ### app.mockServiceError(service, methodName, error) 可以模拟一个错误 ```js app.mockServiceError('user', 'home', new Error('mock error')); ``` ### app.mockCsrf() 模拟 csrf,不用传递 token ```js app.mockCsrf(); return app.httpRequest() .post('/login') .expect(302); ``` ### app.mockHttpclient(url, method, data) 模拟 httpclient 的请求,例如 `ctx.curl` ```js app.get('/', async ctx => { const ret = await ctx.curl('https://eggjs.org'); this.body = ret.data.toString(); }); app.mockHttpclient('https://eggjs.org', { // 模拟的参数,可以是 buffer / string / json / function // 都会转换成 buffer // 按照请求时的 options.dataType 来做对应的转换 data: 'mock egg', }); return app.httpRequest() .post('/') .expect('mock egg'); ``` ## Bootstrap 我们提供了一个 bootstrap 来减少单测中的重复代码: ```js const { app, mock, assert } = require('egg-mock/bootstrap'); describe('test app', () => { it('should request success', () => { // mock data will be restored each case mock.data(app, 'method', { foo: 'bar' }); return app.httpRequest() .get('/foo') .expect(res => { assert(!res.headers.foo); }) .expect(/bar/); }); }); describe('test ctx', () => { it('can use ctx', async function() { const res = await this.ctx.service.foo(); assert(res === 'foo'); }); }); ``` 我们将会在每个 case 中自定注入 ctx, 可以通过 `app.currentContext` 来获取当前的 ctx。 并且第一次使用 `app.mockContext` 会自动复用当前 case 的上下文。 ```js const { app, mock, assert } = require('egg-mock/bootstrap'); describe('test ctx', () => { it('should can use ctx', () => { const ctx = app.currentContext; assert(ctx); }); it('should reuse ctx', () => { const ctx = app.currentContext; // 第一次调用会复用上下文 const mockCtx = app.mockContext(); assert(ctx === mockCtx); // 后续调用会新建上下文 // 极不建议多次调用 app.mockContext // 这会导致上下文污染 // 建议使用 app.mockContextScope const mockCtx2 = app.mockContext(); assert(ctx !== mockCtx); }); }); ``` ### env for custom bootstrap EGG_BASE_DIR: the base dir of egg app EGG_FRAMEWORK: the framework of egg app ## Questions & Suggestions Please open an issue [here](https://github.com/eggjs/egg/issues). ## License [MIT](LICENSE) ## Contributors [![Contributors](https://contrib.rocks/image?repo=eggjs/egg-mock)](https://github.com/eggjs/egg-mock/graphs/contributors) Made with [contributors-img](https://contrib.rocks). ================================================ FILE: app/extend/agent.js ================================================ const mm = require('mm'); const mockHttpclient = require('../../lib/mock_httpclient'); const mockAgent = require('../../lib/mock_agent'); module.exports = { /** * mock httpclient * @function Agent#mockHttpclient * @param {...any} args - args * @return {Context} this */ mockHttpclient(...args) { if (!this._mockHttpclient) { this._mockHttpclient = mockHttpclient(this); } return this._mockHttpclient(...args); }, /** * get mock httpclient agent * @function Agent#mockHttpclientAgent * @return {MockAgent} agent */ mockAgent() { return mockAgent.getAgent(this); }, async mockAgentRestore() { await mockAgent.restore(); }, /** * @see mm#restore * @function Agent#mockRestore */ mockRestore: mm.restore, /** * @see mm * @function Agent#mm */ mm, }; ================================================ FILE: app/extend/application.js ================================================ const debug = require('util').debuglog('egg-mock:application'); const mm = require('mm'); const http = require('http'); const fs = require('fs'); const merge = require('merge-descriptors'); const is = require('is-type-of'); const assert = require('assert'); const Transport = require('egg-logger').Transport; const mockAgent = require('../../lib/mock_agent'); const mockHttpclient = require('../../lib/mock_httpclient'); const supertestRequest = require('../../lib/supertest'); const ORIGIN_TYPES = Symbol('egg-mock:originTypes'); const BACKGROUND_TASKS = Symbol('Application#backgroundTasks'); const REUSED_CTX = Symbol('Context#reusedInSuite'); module.exports = { /** * mock Context * @function App#mockContext * @param {Object} data - ctx data * @param {Object} [options] - mock ctx options * @return {Context} ctx * @example * ```js * const ctx = app.mockContext({ * user: { * name: 'Jason' * } * }); * console.log(ctx.user.name); // Jason * * // controller * module.exports = function*() { * this.body = this.user.name; * }; * ``` */ mockContext(data, options) { function mockRequest(req) { for (const key in (data.headers) || {}) { mm(req.headers, key, data.headers[key]); mm(req.headers, key.toLowerCase(), data.headers[key]); } } options = Object.assign({ mockCtxStorage: true }, options); data = data || {}; if (this._customMockContext) { this._customMockContext(data); } // 使用者自定义mock,可以覆盖上面的 mock for (const key in data) { mm(this.context, key, data[key]); } const req = this.mockRequest(data); const res = new http.ServerResponse(req); if (options.reuseCtxStorage !== false) { if (this.currentContext && !this.currentContext[REUSED_CTX]) { mockRequest(this.currentContext.request.req); this.currentContext[REUSED_CTX] = true; return this.currentContext; } } const ctx = this.createContext(req, res); if (options.mockCtxStorage) { mm(this.ctxStorage, 'getStore', () => ctx); } return ctx; }, async mockContextScope(fn, data) { const ctx = this.mockContext(data, { mockCtxStorage: false, reuseCtxStorage: false, }); return await this.ctxStorage.run(ctx, fn, ctx); }, /** * mock cookie session * @function App#mockSession * @param {Object} data - session object * @return {App} this */ mockSession(data) { if (!data) { return this; } if (is.object(data) && !data.save) { Object.defineProperty(data, 'save', { value: () => {}, enumerable: false, }); } mm(this.context, 'session', data); return this; }, /** * Mock service * @function App#mockService * @param {String} service - name * @param {String} methodName - method * @param {Object/Function/Error} fn - mock you data * @return {App} this */ mockService(service, methodName, fn) { if (typeof service === 'string') { const arr = service.split('.'); service = this.serviceClasses; for (const key of arr) { service = service[key]; } service = service.prototype || service; } this._mockFn(service, methodName, fn); return this; }, /** * mock service that return error * @function App#mockServiceError * @param {String} service - name * @param {String} methodName - method * @param {Error} [err] - error infomation * @return {App} this */ mockServiceError(service, methodName, err) { if (typeof err === 'string') { err = new Error(err); } else if (!err) { // mockServiceError(service, methodName) err = new Error('mock ' + methodName + ' error'); } this.mockService(service, methodName, err); return this; }, _mockFn(obj, name, data) { const origin = obj[name]; assert(is.function(origin), `property ${name} in original object must be function`); // keep origin properties' type to support mock multitimes if (!obj[ORIGIN_TYPES]) obj[ORIGIN_TYPES] = {}; let type = obj[ORIGIN_TYPES][name]; if (!type) { type = obj[ORIGIN_TYPES][name] = is.generatorFunction(origin) || is.asyncFunction(origin) ? 'async' : 'sync'; } if (is.function(data)) { const fn = data; // if original is generator function or async function // but the mock function is normal function, need to change it return a promise if (type === 'async' && (!is.generatorFunction(fn) && !is.asyncFunction(fn))) { mm(obj, name, function(...args) { return new Promise(resolve => { resolve(fn.apply(this, args)); }); }); return; } mm(obj, name, fn); return; } if (type === 'async') { mm(obj, name, () => { return new Promise((resolve, reject) => { if (data instanceof Error) return reject(data); resolve(data); }); }); return; } mm(obj, name, () => { if (data instanceof Error) { throw data; } return data; }); }, /** * mock request * @function App#mockRequest * @param {Request} req - mock request * @return {Request} req */ mockRequest(req) { req = Object.assign({}, req); const headers = req.headers || {}; for (const key in req.headers) { headers[key.toLowerCase()] = req.headers[key]; } if (!headers['x-forwarded-for']) { headers['x-forwarded-for'] = '127.0.0.1'; } req.headers = headers; merge(req, { query: {}, querystring: '', host: '127.0.0.1', hostname: '127.0.0.1', protocol: 'http', secure: 'false', method: 'GET', url: '/', path: '/', socket: { remoteAddress: '127.0.0.1', remotePort: 7001, }, }); return req; }, /** * mock cookies * @function App#mockCookies * @param {Object} cookies - cookie * @return {Context} this */ mockCookies(cookies) { if (!cookies) { return this; } const createContext = this.createContext; mm(this, 'createContext', function(req, res) { const ctx = createContext.call(this, req, res); const getCookie = ctx.cookies.get; mm(ctx.cookies, 'get', function(key, opts) { if (cookies[key]) { return cookies[key]; } return getCookie.call(this, key, opts); }); return ctx; }); return this; }, /** * mock header * @function App#mockHeaders * @param {Object} headers - header 对象 * @return {Context} this */ mockHeaders(headers) { if (!headers) { return this; } const getHeader = this.request.get; mm(this.request, 'get', function(field) { const header = findHeaders(headers, field); if (header) return header; return getHeader.call(this, field); }); return this; }, /** * mock csrf * @function App#mockCsrf * @return {App} this * @since 1.11 */ mockCsrf() { mm(this.context, 'assertCSRF', () => {}); mm(this.context, 'assertCsrf', () => {}); return this; }, /** * mock httpclient * @function App#mockHttpclient * @param {...any} args - args * @return {Context} this */ mockHttpclient(...args) { if (!this._mockHttpclient) { this._mockHttpclient = mockHttpclient(this); } return this._mockHttpclient(...args); }, mockUrllib(...args) { this.deprecate('[egg-mock] Please use app.mockHttpclient instead of app.mockUrllib'); return this.mockHttpclient(...args); }, /** * get mock httpclient agent * @function App#mockHttpclientAgent * @return {MockAgent} agent */ mockAgent() { return mockAgent.getAgent(this); }, async mockAgentRestore() { await mockAgent.restore(); }, /** * @see mm#restore * @function App#mockRestore */ mockRestore: mm.restore, /** * @see mm * @function App#mm */ mm, /** * override loadAgent * @function App#loadAgent */ loadAgent() {}, /** * mock serverEnv * @function App#mockEnv * @param {String} env - serverEnv * @return {App} this */ mockEnv(env) { mm(this.config, 'env', env); mm(this.config, 'serverEnv', env); return this; }, /** * http request helper * @function App#httpRequest * @return {SupertestRequest} req - supertest request * @see https://github.com/visionmedia/supertest */ httpRequest() { return supertestRequest(this); }, /** * collection logger message, then can be use on `expectLog()` * @param {String|Logger} [logger] - logger instance, default is `ctx.logger` * @function App#mockLog */ mockLog(logger) { logger = logger || this.logger; if (typeof logger === 'string') { logger = this.getLogger(logger); } // make sure mock once if (logger._mockLogs) return; const transport = new Transport(logger.options); // https://github.com/eggjs/egg-logger/blob/master/lib/logger.js#L64 const log = logger.log; mm(logger, '_mockLogs', []); mm(logger, 'log', (level, args, meta) => { const message = transport.log(level, args, meta); logger._mockLogs.push(message); log.apply(logger, [ level, args, meta ]); }); }, __checkExpectLog(expectOrNot, str, logger) { logger = logger || this.logger; if (typeof logger === 'string') { logger = this.getLogger(logger); } const filepath = logger.options.file; let content; if (logger._mockLogs) { content = logger._mockLogs.join('\n'); } else { content = fs.readFileSync(filepath, 'utf8'); } let match; let type; if (str instanceof RegExp) { match = str.test(content); type = 'RegExp'; } else { match = content.includes(String(str)); type = 'String'; } if (expectOrNot) { assert(match, `Can't find ${type}:"${str}" in ${filepath}, log content: ...${content.substring(content.length - 500)}`); } else { assert(!match, `Find ${type}:"${str}" in ${filepath}, log content: ...${content.substring(content.length - 500)}`); } }, /** * expect str/regexp in the logger, if your server disk is slow, please call `mockLog()` first. * @param {String|RegExp} str - test str or regexp * @param {String|Logger} [logger] - logger instance, default is `ctx.logger` * @function App#expectLog */ expectLog(str, logger) { this.__checkExpectLog(true, str, logger); }, /** * not expect str/regexp in the logger, if your server disk is slow, please call `mockLog()` first. * @param {String|RegExp} str - test str or regexp * @param {String|Logger} [logger] - logger instance, default is `ctx.logger` * @function App#notExpectLog */ notExpectLog(str, logger) { this.__checkExpectLog(false, str, logger); }, backgroundTasksFinished() { const tasks = this._backgroundTasks; debug('waiting %d background tasks', tasks.length); if (tasks.length === 0) return Promise.resolve(); this._backgroundTasks = []; return Promise.all(tasks).then(() => { debug('finished %d background tasks', tasks.length); if (this._backgroundTasks.length) { debug('new background tasks created: %s', this._backgroundTasks.length); return this.backgroundTasksFinished(); } }); }, get _backgroundTasks() { if (!this[BACKGROUND_TASKS]) { this[BACKGROUND_TASKS] = []; } return this[BACKGROUND_TASKS]; }, set _backgroundTasks(tasks) { this[BACKGROUND_TASKS] = tasks; }, }; function findHeaders(headers, key) { if (!headers || !key) { return null; } key = key.toLowerCase(); for (const headerKey in headers) { if (key === headerKey.toLowerCase()) { return headers[headerKey]; } } return null; } ================================================ FILE: app/middleware/cluster_app_mock.js ================================================ const debug = require('util').debuglog('egg-mock:middleware:cluster_app_mock'); const is = require('is-type-of'); const co = require('co'); module.exports = () => { return function clusterAppMock(ctx, next) { // use originalUrl to make sure other middlewares can't change request url if (ctx.originalUrl !== '/__egg_mock_call_function') return next(); debug('%s %s, body: %j', ctx.method, ctx.url, ctx.request.body); const { method, property, args, needResult } = ctx.request.body; if (!method) { ctx.status = 422; ctx.body = { success: false, error: 'Missing method', }; return; } if (args && !Array.isArray(args)) { ctx.status = 422; ctx.body = { success: false, error: 'args should be an Array instance', }; return; } if (property) { if (!ctx.app[property] || typeof ctx.app[property][method] !== 'function') { ctx.status = 422; ctx.body = { success: false, error: `method "${method}" not exists on app.${property}`, }; return; } } else { if (typeof ctx.app[method] !== 'function') { ctx.status = 422; ctx.body = { success: false, error: `method "${method}" not exists on app`, }; return; } } debug('call %s with %j', method, args); for (let i = 0; i < args.length; i++) { const arg = args[i]; if (arg && typeof arg === 'object') { // convert __egg_mock_type back to function if (arg.__egg_mock_type === 'function') { // eslint-disable-next-line args[i] = eval(`(function() { return ${arg.value} })()`); } else if (arg.__egg_mock_type === 'error') { const err = new Error(arg.message); err.name = arg.name; err.stack = arg.stack; for (const key in arg) { if (key !== 'name' && key !== 'message' && key !== 'stack' && key !== '__egg_mock_type') { err[key] = arg[key]; } } args[i] = err; } } } const target = property ? ctx.app[property] : ctx.app; let fn = target[method]; if (is.generatorFunction(fn)) fn = co.wrap(fn); try { Promise.resolve(fn.call(target, ...args)).then(result => { ctx.body = needResult ? { success: true, result } : { success: true }; }); } catch (err) { ctx.status = 500; ctx.body = { success: false, error: err.message }; } }; }; ================================================ FILE: app.js ================================================ 'use strict'; module.exports = app => { // make sure clusterAppMock position before securities const index = app.config.coreMiddleware.indexOf('securities'); if (index >= 0) { app.config.coreMiddleware.splice(index, 0, 'clusterAppMock'); } else { app.config.coreMiddleware.push('clusterAppMock'); } }; ================================================ FILE: bootstrap.d.ts ================================================ import * as assert from 'assert'; import { MockApplication, EggMock } from './'; export { assert } export declare const app: MockApplication; export declare const mock: EggMock; export declare const mm: EggMock; ================================================ FILE: bootstrap.js ================================================ const assert = require('assert'); const path = require('path'); const mock = require('./index').default; const appHandler = require('./lib/app_handler'); const { getEggOptions } = require('./lib/utils'); const options = getEggOptions(); // throw error when an egg plugin test is using bootstrap const pkgInfo = require(path.join(options.baseDir || process.cwd(), 'package.json')); if (pkgInfo.eggPlugin) throw new Error('DO NOT USE bootstrap to test plugin'); appHandler.setupApp(); module.exports = { assert, get app() { return appHandler.getBootstrapApp(); }, mock, mm: mock, }; ================================================ FILE: index.d.ts ================================================ import { Application, Context, EggLogger } from 'egg'; import { MockMate } from 'mm'; import { Test } from 'supertest'; import { MockAgent } from 'urllib'; import { Suite } from 'mocha'; export { MockAgent }; export interface EggTest extends Test { unexpectHeader(name: string, b?: Function): EggTest; expectHeader(name: string, b?: Function): EggTest; } export type Methods = 'get' | 'post' | 'delete' | 'del' | 'put' | 'head' | 'options' | 'patch' | 'trace' | 'connect'; export interface BaseMockApplication extends Application { ready(): Promise; close(): Promise; callback(): any; /** * mock Context */ mockContext(data?: any): C; /** * mock Context */ mockContextScope(fn: (ctx: C) => Promise, data?: any): Promise; /** * mock cookie session */ mockSession(data: any): T; mockCookies(cookies: any): T; mockHeaders(headers: any): T; /** * Mock service */ mockService(service: string, methodName: string, fn: any): T; /** * mock service that return error */ mockServiceError(service: string, methodName: string, err?: Error): T; mockHttpclient(mockUrl: string | RegExp, mockMethod: string | string[], mockResult: MockHttpClientResult): Application; mockHttpclient(mockUrl: string | RegExp, mockResult: MockHttpClientResult): Application; mockAgent(): MockAgent; mockAgentRestore(): Promise; mockRestore(): Promise; /** * mock csrf */ mockCsrf(): T; /** * http request helper */ httpRequest(): { [key in Methods]: (url: string) => EggTest; } & { [key: string]: (url: string) => EggTest; }; /** * mock logger */ mockLog(logger?: EggLogger | string): void; expectLog(expected: string | RegExp, logger?: EggLogger | string): void; notExpectLog(expected: string | RegExp, logger?: EggLogger | string): void; /** * background task */ backgroundTasksFinished(): Promise; } export interface ResultObject { data?: string | object | Buffer; status?: number; headers?: any; delay?: number; persist?: boolean; repeats?: number; } export type ResultFunction = (url?: string, opts?: any) => ResultObject | string | void; export type MockHttpClientResult = ResultObject | ResultFunction | string; export interface MockOption { /** * The directory of the application */ baseDir?: string; /** * Custom you plugins */ plugins?: any; /** * The directory of the egg framework */ framework?: string; /** * Cache application based on baseDir */ cache?: boolean; /** * Swtich on process coverage, but it'll be slower */ coverage?: boolean; /** * Remove $baseDir/logs */ clean?: boolean; } export type EnvType = 'default' | 'test' | 'prod' | 'local' | 'unittest' | string & {}; export type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'NONE'; export interface MockApplication extends BaseMockApplication { } export interface EggMock extends MockMate { /** * Create a egg mocked application */ app: (option?: MockOption) => MockApplication; /** * Create a mock cluster server, but you can't use API in application, you should test using supertest */ cluster: (option?: MockOption) => MockApplication; /** * mock the serverEnv of Egg */ env: (env: EnvType) => void; /** * mock console level */ consoleLevel: (level: LogLevel) => void; /** * set EGG_HOME path */ home: (homePath: string) => void; /** * restore mock */ restore: () => any; /** * If you use mm.app instead of egg-mock/bootstrap to bootstrap app. * Should manually call setGetAppCallback, * then egg-mock will inject ctx for each test case * @param cb */ setGetAppCallback: (cb: (suite: Suite) => Promise ) => void; } declare const mm: EggMock; export default mm; ================================================ FILE: index.js ================================================ const mm = require('mm'); const cluster = require('./lib/cluster'); 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'); } /** * @namespace mm */ function mock(...args) { return mm(...args); } module.exports = mock; module.exports.default = mock; // inherit & extends mm Object.assign(mock, mm, { async restore() { cluster.restore(); await Promise.all([ mockAgent.restore(), mm.restore() ]); }, /** * Create a egg mocked application * @function mm#app * @param {Object} [options] * - {String} baseDir - The directory of the application * - {Object} plugins - Tustom you plugins * - {String} framework - The directory of the egg framework * - {Boolean} [true] cache - Cache application based on baseDir * - {Boolean} [true] coverage - Swtich on process coverage, but it'll be slower * - {Boolean} [true] clean - Remove $baseDir/logs * @return {App} return {@link Application} * @example * ```js * var app = mm.app(); * ``` */ app, /** * Create a egg mocked cluster application * @function mm#cluster * @see ClusterApplication */ cluster, /** * mock the serverEnv of Egg * @member {Function} mm#env * @param {String} env - contain default, test, prod, local, unittest * @see https://github.com/eggjs/egg-core/blob/master/lib/loader/egg_loader.js#L78 */ env(env) { mm(process.env, 'EGG_MOCK_SERVER_ENV', env); mm(process.env, 'EGG_SERVER_ENV', env); }, /** * mock console level * @param {String} level - logger level */ consoleLevel(level) { level = (level || '').toUpperCase(); mm(process.env, 'EGG_LOG', level); }, home(homePath) { if (homePath) { mm(process.env, 'EGG_HOME', homePath); } }, setGetAppCallback: require('./lib/app_handler').setGetAppCallback, }); process.setMaxListeners(100); process.once('SIGQUIT', () => { process.exit(0); }); process.once('SIGTERM', () => { process.exit(0); }); process.once('SIGINT', () => { process.exit(0); }); ================================================ FILE: index.test-d.ts ================================================ import { expectType } from 'tsd'; import { Application, Context } from 'egg'; import { MockApplication, MockAgent, ResultObject } from '.'; import { app, mock, mm } from './bootstrap'; expectType(app); expectType(app.currentContext); expectType(app.ctxStorage.getStore()); expectType(mock.app()); expectType(mm.app()); expectType(mm.app().mockAgent()); expectType(mm.app().mockHttpclient('url', 'post', { data: 'ok' })); expectType(mm.app().mockHttpclient('url', 'post', 'data')); expectType(mm.app().mockHttpclient('url', { data: 'mock response', repeats: 1, })); expectType(mm.app().mockHttpclient('url', () => {})); expectType(mm.app().mockHttpclient('url', 'post', () => {})); expectType(mm.app().mockHttpclient('url', 'get', { data: 'mock response', repeats: 1, })); expectType(app.mockLog()); expectType(app.mockLog('logger')); expectType(app.mockLog(app.logger)); expectType(app.expectLog('foo string')); expectType(app.expectLog('foo string', 'coreLogger')); expectType(app.expectLog('foo string', app.coreLogger)); expectType(app.expectLog(/foo string/)); expectType(app.expectLog(/foo string/, 'coreLogger')); expectType(app.expectLog(/foo string/, app.coreLogger)); expectType(mm.env('default')); expectType(mm.env('devserver')); expectType>(app.mockAgentRestore()); expectType>(app.mockRestore()); expectType>(app.mockContextScope(async () => {})); expectType>(app.mockContextScope(async (ctx) => {})); expectType>(app.backgroundTasksFinished()); const result: ResultObject = {}; expectType(result.status!); ================================================ FILE: lib/agent_handler.js ================================================ const debug = require('util').debuglog('egg-mock:lib:agent'); const Agent = require('./parallel/agent'); const { getEggOptions } = require('./utils'); let agent; exports.setupAgent = async () => { debug('setupAgent call, env.ENABLE_MOCHA_PARALLEL: %s, process.env.AUTO_AGENT: %s, agent: %s', process.env.ENABLE_MOCHA_PARALLEL, process.env.AUTO_AGENT, !!agent); if (agent) { await agent.ready(); return agent; } if (process.env.ENABLE_MOCHA_PARALLEL && process.env.AUTO_AGENT) { agent = Agent(getEggOptions()); await agent.ready(); } return agent; }; exports.closeAgent = async () => { debug('setupAgent call, agent: %s', !!agent); if (agent) { await agent.close(); } }; ================================================ FILE: lib/app.js ================================================ const debug = require('util').debuglog('egg-mock:lib:app'); const os = require('os'); const path = require('path'); const EventEmitter = require('events'); const co = require('co'); const is = require('is-type-of'); const { Ready } = require('get-ready'); const { detectPort } = require('detect-port'); const ConsoleLogger = require('egg-logger').EggConsoleLogger; const { sleep, rimraf } = require('./utils'); const formatOptions = require('./format_options'); const context = require('./context'); const mockCustomLoader = require('./mock_custom_loader'); const mockHttpServer = require('./mock_http_server'); const consoleLogger = new ConsoleLogger({ level: 'INFO' }); const apps = new Map(); const INIT = Symbol('init'); const APP_INIT = Symbol('appInit'); const BIND_EVENT = Symbol('bindEvent'); const INIT_ON_LISTENER = Symbol('initOnListener'); const INIT_ONCE_LISTENER = Symbol('initOnceListener'); const MESSENGER = Symbol('messenger'); const MOCK_APP_METHOD = [ 'ready', 'closed', 'close', '_agent', '_app', 'on', 'once', 'then', ]; class MockApplication extends EventEmitter { constructor(options) { super(); this.options = options; this.baseDir = options.baseDir; this.closed = false; this[APP_INIT] = false; this[INIT_ON_LISTENER] = new Set(); this[INIT_ONCE_LISTENER] = new Set(); Ready.mixin(this); // listen once, otherwise will throw exception when emit error without listenr this.once('error', () => {}); co(this[INIT].bind(this)) .then(() => this.ready(true)) .catch(err => { if (!this[APP_INIT]) { this.emit('error', err); } consoleLogger.error(err); this.ready(err); }); } * [INIT]() { if (this.options.beforeInit) { yield this.options.beforeInit(this); delete this.options.beforeInit; } if (this.options.clean !== false) { const logDir = path.join(this.options.baseDir, 'logs'); try { /* istanbul ignore if */ if (os.platform() === 'win32') yield sleep(1000); yield rimraf(logDir); } catch (err) { /* istanbul ignore next */ console.error(`remove log dir ${logDir} failed: ${err.stack}`); } } this.options.clusterPort = yield detectPort(); debug('get clusterPort %s', this.options.clusterPort); const egg = require(this.options.framework); const Agent = egg.Agent; const agent = this._agent = new Agent(Object.assign({}, this.options)); debug('agent instantiate'); yield agent.ready(); debug('agent ready'); const Application = bindMessenger(egg.Application, agent); const app = this._app = new Application(Object.assign({}, this.options)); // https://github.com/eggjs/egg/blob/8bb7c7e7d59d6aeca4b2ed1eb580368dcb731a4d/lib/egg.js#L125 // egg single mode mount this at start(), so egg-mock should impel it. app.agent = agent; agent.app = app; // egg-mock plugin need to override egg context Object.assign(app.context, context); mockCustomLoader(app); debug('app instantiate'); this[APP_INIT] = true; debug('this[APP_INIT] = true'); this[BIND_EVENT](); debug('http server instantiate'); mockHttpServer(app); yield app.ready(); const msg = { action: 'egg-ready', data: this.options, }; app.messenger._onMessage(msg); agent.messenger._onMessage(msg); debug('app ready'); } [BIND_EVENT]() { for (const args of this[INIT_ON_LISTENER]) { debug('on(%s), use cache and pass to app', args); this._app.on(...args); this.removeListener(...args); } for (const args of this[INIT_ONCE_LISTENER]) { debug('once(%s), use cache and pass to app', args); this._app.on(...args); this.removeListener(...args); } } on(...args) { if (this[APP_INIT]) { debug('on(%s), pass to app', args); this._app.on(...args); } else { debug('on(%s), cache it because app has not init', args); this[INIT_ON_LISTENER].add(args); super.on(...args); } } once(...args) { if (this[APP_INIT]) { debug('once(%s), pass to app', args); this._app.once(...args); } else { debug('once(%s), cache it because app has not init', args); this[INIT_ONCE_LISTENER].add(args); super.on(...args); } } /** * close app * @return {Promise} promise */ close() { this.closed = true; const self = this; const baseDir = this.baseDir; return co(function* () { if (self._app) { yield self._app.close(); } else { // when app init throws an exception, must wait for app quit gracefully yield sleep(200); } if (self._agent) yield self._agent.close(); apps.delete(baseDir); debug('delete app cache %s, remain %s', baseDir, [ ...apps.keys() ]); /* istanbul ignore if */ if (os.platform() === 'win32') yield sleep(1000); }); } } module.exports = function(options) { options = formatOptions(options); if (options.cache && apps.has(options.baseDir)) { const app = apps.get(options.baseDir); // return cache when it hasn't been killed if (!app.closed) { return app; } // delete the cache when it's closed apps.delete(options.baseDir); } let app = new MockApplication(options); app = new Proxy(app, { get(target, prop) { // don't delegate properties on MockApplication if (MOCK_APP_METHOD.includes(prop)) return getProperty(target, prop); if (!target[APP_INIT]) throw new Error(`can't get ${prop} before ready`); // it's asynchronous when agent and app are loading, // so should get the properties after loader ready debug('proxy handler.get %s', prop); return target._app[prop]; }, set(target, prop, value) { if (MOCK_APP_METHOD.includes(prop)) return true; if (!target[APP_INIT]) throw new Error(`can't set ${prop} before ready`); debug('proxy handler.set %s', prop); target._app[prop] = value; return true; }, defineProperty(target, prop, descriptor) { // can't define properties on MockApplication if (MOCK_APP_METHOD.includes(prop)) return true; if (!target[APP_INIT]) throw new Error(`can't defineProperty ${prop} before ready`); debug('proxy handler.defineProperty %s', prop); Object.defineProperty(target._app, prop, descriptor); return true; }, deleteProperty(target, prop) { // can't delete properties on MockApplication if (MOCK_APP_METHOD.includes(prop)) return true; if (!target[APP_INIT]) throw new Error(`can't delete ${prop} before ready`); debug('proxy handler.deleteProperty %s', prop); delete target._app[prop]; return true; }, getOwnPropertyDescriptor(target, prop) { if (MOCK_APP_METHOD.includes(prop)) return Object.getOwnPropertyDescriptor(target, prop); if (!target[APP_INIT]) throw new Error(`can't getOwnPropertyDescriptor ${prop} before ready`); debug('proxy handler.getOwnPropertyDescriptor %s', prop); return Object.getOwnPropertyDescriptor(target._app, prop); }, getPrototypeOf(target) { if (!target[APP_INIT]) throw new Error('can\'t getPrototypeOf before ready'); debug('proxy handler.getPrototypeOf %s'); return Object.getPrototypeOf(target._app); }, }); apps.set(options.baseDir, app); return app; }; function getProperty(target, prop) { const member = target[prop]; if (is.function(member)) { return member.bind(target); } return member; } function bindMessenger(Application, agent) { const agentMessenger = agent.messenger; return class MessengerApplication extends Application { constructor(options) { super(options); // enable app to send to a random agent this.messenger.sendRandom = (action, data) => { this.messenger.sendToAgent(action, data); }; // enable agent to send to a random app agentMessenger.on('egg-ready', () => { agentMessenger.sendRandom = (action, data) => { agentMessenger.sendToApp(action, data); }; }); agentMessenger.send = new Proxy(agentMessenger.send, { apply: this._sendMessage.bind(this), }); } _sendMessage(target, thisArg, [ action, data, to ]) { const appMessenger = this.messenger; setImmediate(() => { if (to === 'app') { appMessenger._onMessage({ action, data }); } else { agentMessenger._onMessage({ action, data }); } }); } get messenger() { return this[MESSENGER]; } set messenger(m) { m.send = new Proxy(m.send, { apply: this._sendMessage.bind(this), }); this[MESSENGER] = m; } get [Symbol.for('egg#eggPath')]() { return path.join(__dirname, 'tmp'); } }; } ================================================ FILE: lib/app_handler.js ================================================ const debug = require('util').debuglog('egg-mock:bootstrap:app_handler'); const mockParallelApp = require('./parallel/app'); const { setupAgent } = require('./agent_handler'); const mock = require('../index').default; const { getEggOptions } = require('./utils'); let app; exports.setupApp = () => { if (app) { debug('return exists app'); return app; } const options = getEggOptions(); debug('env.ENABLE_MOCHA_PARALLEL: %s, process.env.AUTO_AGENT: %s', process.env.ENABLE_MOCHA_PARALLEL, process.env.AUTO_AGENT); if (process.env.ENABLE_MOCHA_PARALLEL && process.env.AUTO_AGENT) { // setup agent first app = mockParallelApp({ ...options, beforeInit: async _app => { const agent = await setupAgent(); _app.options.clusterPort = agent.options.clusterPort; debug('mockParallelApp beforeInit get clusterPort: %s', _app.options.clusterPort); }, }); debug('mockParallelApp app: %s', !!app); } else { app = mock.app(options); if (typeof beforeAll === 'function') { // jest beforeAll(() => app.ready()); afterEach(() => app.backgroundTasksFinished()); afterEach(mock.restore); } } }; let getAppCallback; exports.setGetAppCallback = cb => { getAppCallback = cb; }; exports.getApp = async (suite, test) => { if (getAppCallback) { return getAppCallback(suite, test); } if (app) { await app.ready(); } return app; }; exports.getBootstrapApp = () => { return app; }; ================================================ FILE: lib/cluster.js ================================================ const debug = require('util').debuglog('egg-mock:cluster'); const path = require('path'); const os = require('os'); const childProcess = require('child_process'); const Coffee = require('coffee').Coffee; const { Ready } = require('get-ready'); const co = require('co'); const awaitEvent = require('await-event'); const supertestRequest = require('./supertest'); const { sleep, rimrafSync } = require('./utils'); const formatOptions = require('./format_options'); const clusters = new Map(); const serverBin = path.join(__dirname, 'start-cluster'); const requestCallFunctionFile = path.join(__dirname, 'request_call_function.js'); let masterPort = 17000; /** * A cluster version of egg.Application, you can test with supertest * @example * ```js * const mm = require('mm'); * const request = require('supertest'); * * describe('ClusterApplication', () => { * let app; * before(function (done) { * app = mm.cluster({ baseDir }); * app.ready(done); * }); * * after(function () { * app.close(); * }); * * it('should 200', function (done) { * request(app.callback()) * .get('/') * .expect(200, done); * }); * }); */ class ClusterApplication extends Coffee { /** * @class * @param {Object} options * - {String} baseDir - The directory of the application * - {Object} plugins - Tustom you plugins * - {String} framework - The directory of the egg framework * - {Boolean} [cache=true] - Cache application based on baseDir * - {Boolean} [coverage=true] - Swtich on process coverage, but it'll be slower * - {Boolean} [clean=true] - Remove $baseDir/logs * - {Object} [opt] - opt pass to coffee, such as { execArgv: ['--debug'] } * ``` */ constructor(options) { const opt = options.opt; delete options.opt; // incremental port options.port = options.port || ++masterPort; // Set 1 worker when test if (!options.workers) options.workers = 1; const args = [ JSON.stringify(options) ]; debug('fork %s, args: %s, opt: %j', serverBin, args.join(' '), opt); super({ method: 'fork', cmd: serverBin, args, opt, }); Ready.mixin(this); this.port = options.port; this.baseDir = options.baseDir; // print stdout and stderr when DEBUG, otherwise stderr. this.debug(process.env.DEBUG ? 0 : 2); // disable coverage if (options.coverage === false) { this.coverage(false); } process.nextTick(() => { this.proc.on('message', msg => { // 'egg-ready' and { action: 'egg-ready' } const action = msg && msg.action ? msg.action : msg; switch (action) { case 'egg-ready': this.emit('close', 0); break; case 'app-worker-died': case 'agent-worker-died': this.emit('close', 1); break; default: // ignore it break; } }); }); this.end(() => this.ready(true)); } /** * the process that forked * @member {ChildProcess} */ get process() { return this.proc; } /** * Compatible API for supertest * @return {ClusterApplication} return the instance */ callback() { return this; } /** * Compatible API for supertest * @member {String} url * @private */ get url() { return 'http://127.0.0.1:' + this.port; } /** * Compatible API for supertest * @return {Object} * - {Number} port * @private */ address() { return { port: this.port, }; } /** * Compatible API for supertest * @return {ClusterApplication} return the instance * @private */ listen() { return this; } /** * kill the process * @return {Promise} promise */ close() { this.closed = true; const proc = this.proc; const baseDir = this.baseDir; return co(function* () { if (proc.connected) { proc.kill('SIGTERM'); yield awaitEvent.call(proc, 'exit'); } clusters.delete(baseDir); debug('delete cluster cache %s, remain %s', baseDir, [ ...clusters.keys() ]); /* istanbul ignore if */ if (os.platform() === 'win32') yield sleep(1000); }); } // mock app.router.pathFor(name) api get router() { const that = this; return { pathFor(url) { return that._callFunctionOnAppWorker('pathFor', [ url ], 'router', true); }, }; } /** * collection logger message, then can be use on `expectLog()` * it's different from `app.expectLog()`, only support string params. * * @param {String} [logger] - logger instance name, default is `logger` * @function ClusterApplication#expectLog */ mockLog(logger) { logger = logger || 'logger'; this._callFunctionOnAppWorker('mockLog', [ logger ], null, true); } /** * expect str in the logger * it's different from `app.expectLog()`, only support string params. * * @param {String} str - test str * @param {String} [logger] - logger instance name, default is `logger` * @function ClusterApplication#expectLog */ expectLog(str, logger) { logger = logger || 'logger'; this._callFunctionOnAppWorker('expectLog', [ str, logger ], null, true); } /** * not expect str in the logger * it's different from `app.notExpectLog()`, only support string params. * * @param {String} str - test str * @param {String} [logger] - logger instance name, default is `logger` * @function ClusterApplication#notExpectLog */ notExpectLog(str, logger) { logger = logger || 'logger'; this._callFunctionOnAppWorker('notExpectLog', [ str, logger ], null, true); } httpRequest() { return supertestRequest(this); } _callFunctionOnAppWorker(method, args = [], property = undefined, needResult = false) { for (let i = 0; i < args.length; i++) { const arg = args[i]; if (typeof arg === 'function') { args[i] = { __egg_mock_type: 'function', value: arg.toString(), }; } else if (arg instanceof Error) { const errObject = { __egg_mock_type: 'error', name: arg.name, message: arg.message, stack: arg.stack, }; for (const key in arg) { if (key !== 'name' && key !== 'message' && key !== 'stack') { errObject[key] = arg[key]; } } args[i] = errObject; } } const data = { port: this.port, method, args, property, needResult, }; const child = childProcess.spawnSync(process.execPath, [ requestCallFunctionFile, JSON.stringify(data), ], { stdio: 'pipe', }); if (child.stderr && child.stderr.length > 0) { console.error(child.stderr.toString()); } let result; if (child.stdout && child.stdout.length > 0) { if (needResult) { result = JSON.parse(child.stdout.toString()); } else { console.error(child.stdout.toString()); } } if (child.status !== 0) { throw new Error(child.stderr.toString()); } if (child.error) { throw child.error; } return result; } } module.exports = options => { options = formatOptions(options); if (options.cache && clusters.has(options.baseDir)) { const clusterApp = clusters.get(options.baseDir); // return cache when it hasn't been killed if (!clusterApp.closed) { return clusterApp; } // delete the cache when it's closed clusters.delete(options.baseDir); } if (options.clean !== false) { const logDir = path.join(options.baseDir, 'logs'); try { rimrafSync(logDir); } catch (err) { /* istanbul ignore next */ console.error(`remove log dir ${logDir} failed: ${err.stack}`); } } let clusterApp = new ClusterApplication(options); clusterApp = new Proxy(clusterApp, { get(target, prop) { debug('proxy handler.get %s', prop); // proxy mockXXX function to app worker const method = prop; if (typeof method === 'string' && /^mock\w+$/.test(method) && target[method] === undefined) { return function mockProxy(...args) { return target._callFunctionOnAppWorker(method, args, null, true); }; } return target[prop]; }, }); clusters.set(options.baseDir, clusterApp); return clusterApp; }; // export to let mm.restore() worked module.exports.restore = () => { for (const clusterApp of clusters.values()) { clusterApp.mockRestore(); } }; // ensure to close App process on test exit. process.on('exit', () => { for (const clusterApp of clusters.values()) { clusterApp.close(); } }); ================================================ FILE: lib/context.js ================================================ 'use strict'; // try to use eggUtils.getCalleeFromStack // ignore it if egg-core module not found let eggUtils; try { eggUtils = require('egg-core').utils; if (!eggUtils) { // try to support egg-core@3 eggUtils = require('egg-core/lib/utils'); } } catch (_) { // ignore eggUtils } module.exports = { runInBackground(scope) { /* istanbul ignore next */ const taskName = scope._name || scope.name || (eggUtils && eggUtils.getCalleeFromStack(true)); if (taskName) { scope._name = taskName; } const promise = this._runInBackground(scope); this.app._backgroundTasks.push(promise); }, }; ================================================ FILE: lib/format_options.js ================================================ const debug = require('util').debuglog('mm'); const path = require('path'); const mm = require('mm'); const utils = require('@eggjs/utils'); /** * format the options * @param {Object} options - options * @return {Object} options */ module.exports = function formatOptions(options) { const defaults = { baseDir: process.cwd(), cache: true, coverage: true, clean: true, }; options = Object.assign({}, defaults, options); // relative path to test/fixtures // ```js // formatOptions({ baseDir: 'app' }); // baseDir => $PWD/test/fixtures/app // ``` if (!path.isAbsolute(options.baseDir)) { options.baseDir = path.join(process.cwd(), 'test/fixtures', options.baseDir); } let framework = options.framework || options.customEgg; // test for framework if (framework === true) { framework = process.cwd(); // disable plugin test when framework test options.plugin = false; } else { // it will throw when framework is not found framework = utils.getFrameworkPath({ framework, baseDir: options.baseDir }); } options.customEgg = options.framework = framework; const plugins = options.plugins = options.plugins || {}; // add self as a plugin plugins['egg-mock'] = { enable: true, path: path.join(__dirname, '..'), }; // test for plugin if (options.plugin !== false) { // add self to plugin list const pkgPath = path.join(process.cwd(), 'package.json'); const pluginName = getPluginName(pkgPath); if (options.plugin && !pluginName) { throw new Error(`should set eggPlugin in ${pkgPath}`); } if (pluginName) { plugins[pluginName] = { enable: true, path: process.cwd(), }; } } // mock HOME as baseDir, but ignore if it has been mocked const env = process.env.EGG_SERVER_ENV; if (!mm.isMocked(process.env, 'HOME') && (env === 'default' || env === 'test' || env === 'prod')) { mm(process.env, 'HOME', options.baseDir); } // disable cache after call mm.env(), // otherwise it will use cache and won't load again. if (process.env.EGG_MOCK_SERVER_ENV) { options.cache = false; } debug('format options: %j', options); return options; }; function getPluginName(pkgPath) { try { const pkg = require(pkgPath); if (pkg.eggPlugin && pkg.eggPlugin.name) { return pkg.eggPlugin.name; } } catch (_) { // ignore } } ================================================ FILE: lib/http_test.js ================================================ 'use strict'; const Test = require('supertest').Test; class EggTest extends Test { /** * Unexpectations: * * .unexpectHeader('Content-Type') * .unexpectHeader('Content-Type', fn) * * @return {EggTest} * @public */ unexpectHeader(name, b) { if (typeof b === 'function') { this.end(b); } // header if (typeof name === 'string') { this._asserts.push(this._unexpectHeader.bind(this, name)); } return this; } /** * Expectations: * * .expectHeader('Content-Type') * .expectHeader('Content-Type', fn) * * @return {EggTest} * @public */ expectHeader(name, b) { if (typeof b === 'function') { this.end(b); } // header if (typeof name === 'string') { this._asserts.push(this._expectHeader.bind(this, name)); } return this; } _unexpectHeader(name, res) { const actual = res.headers[name.toLowerCase()]; if (actual) { return new Error('unexpected "' + name + '" header field, got "' + actual + '"'); } } _expectHeader(name, res) { const actual = res.headers[name.toLowerCase()]; if (!actual) { return new Error('expected "' + name + '" header field'); } } } module.exports = EggTest; ================================================ FILE: lib/inject_context.js ================================================ const assert = require('assert'); const debug = require('util').debuglog('egg-mock:inject_context'); const MOCHA_SUITE_APP = Symbol.for('mocha#suite#app'); const appHandler = require('./app_handler'); /** * Monkey patch the mocha instance with egg context. * * @param {Function} mocha - */ function injectContext(mocha) { if (!mocha || mocha._injectContextLoaded) { return; } const { Runner } = mocha; const runSuite = Runner.prototype.runSuite; const runTests = Runner.prototype.runTests; function getTestTitle(suite, test) { const suiteTitle = suite.root ? 'root suite' : suite.title; if (!test) { return `"${suiteTitle}"`; } return `"${suiteTitle} - ${test.title}"`; } // Inject ctx for before/after. Runner.prototype.runSuite = async function(suite, fn) { debug('run suite: %s', suite.title); let app; const self = this; try { app = await appHandler.getApp(suite); debug('get app: %s', !!app); await app.ready(); } catch { // 可能 app.ready 时报错,不使用失败的 app app = null; } if (!app) { // app 不存在,直接跳过,在 beforeEach 的 hook 中会报错 // 确保不打乱 mocha 的顺序,防止 mocha 内部状态错误 return runSuite.call(self, suite, fn); } let errSuite; try { suite.ctx[MOCHA_SUITE_APP] = app; const mockContextFun = app.mockModuleContextScope || app.mockContextScope; await mockContextFun.call(app, async function() { await new Promise(resolve => { runSuite.call(self, suite, aErrSuite => { errSuite = aErrSuite; resolve(); }); }); }); } catch (err) { // mockContext 失败后动态注册一个 beforeAll hook // 快速失败,直接阻塞后续用例 suite.beforeAll('egg-mock-mock-ctx-failed', async () => { throw err; }); return runSuite.call(self, suite, aErrSuite => { return fn(aErrSuite); }); } return fn(errSuite); }; // Inject ctx for beforeEach/it/afterEach. // And ctx with before/after is not same as beforeEach/it/afterEach. Runner.prototype.runTests = async function(suite, fn) { const tests = suite.tests.slice(); if (!tests.length) { return runTests.call(this, suite, fn); } const app = suite.ctx[MOCHA_SUITE_APP]; const self = this; if (!app) { return runTests.call(self, suite, fn); } function done(errSuite) { suite.tests = tests; return fn(errSuite); } async function next(i) { const test = tests[i]; if (!test) { return done(); } suite.tests = [ test ]; let app; try { app = await appHandler.getApp(suite, test); assert(app, `not found app for test ${getTestTitle(suite, test)}`); await app.ready(); } catch (err) { self.fail(test, err); return next(i + 1); } try { const mockContextFun = app.mockModuleContextScope || app.mockContextScope; await mockContextFun.call(app, async function() { return await new Promise(resolve => { runTests.call(self, suite, () => { return resolve(); }); }); }); } catch (err) { self.fail(test, err); return next(i + 1); } return next(i + 1); } next(0).catch(err => { self.fail(suite, err); done(suite); }); }; mocha._injectContextLoaded = true; } module.exports = injectContext; ================================================ FILE: lib/mock_agent.js ================================================ const { debuglog } = require('util'); let { MockAgent, setGlobalDispatcher, getGlobalDispatcher } = require('urllib'); if (typeof getGlobalDispatcher === 'undefined') { let urllibNext; // https://github.com/eggjs/egg/blob/3.x/package.json#L59 try { // try to use urllib4 urllibNext = require('urllib4'); } catch { // try to use urllib-next try { urllibNext = require('urllib-next'); } catch { throw new Error('Please install urllib@4'); } } MockAgent = urllibNext.MockAgent; setGlobalDispatcher = urllibNext.setGlobalDispatcher; getGlobalDispatcher = urllibNext.getGlobalDispatcher; } const debug = debuglog('egg-mock:lib:mock_agent'); let _mockAgent; let _global; const APP_HTTPCLIENT_AGENT = Symbol('app.httpclient.agent'); const httpClients = []; module.exports = { getAgent(app) { if (!_global) { _global = getGlobalDispatcher(); } if (!app?.httpclient?.[APP_HTTPCLIENT_AGENT] && typeof app?.httpclient?.getDispatcher === 'function') { // save the raw dispatcher app.httpclient[APP_HTTPCLIENT_AGENT] = app.httpclient.getDispatcher(); httpClients.push(app.httpclient); debug('add new httpClient, size: %d', httpClients.length); } if (!_mockAgent) { _mockAgent = new MockAgent(); setGlobalDispatcher(_mockAgent); if (typeof app?.httpclient?.setDispatcher === 'function') { app.httpclient.setDispatcher(_mockAgent); } } return _mockAgent; }, async restore() { if (!_mockAgent) return; if (_global) { setGlobalDispatcher(_global); } for (const httpClient of httpClients) { httpClient.setDispatcher(httpClient[APP_HTTPCLIENT_AGENT]); } debug('restore httpClient, size: %d', httpClients.length); const agent = _mockAgent; _mockAgent = null; await agent.close(); }, }; ================================================ FILE: lib/mock_custom_loader.js ================================================ 'use strict'; module.exports = app => { const customLoader = app.config.customLoader; if (!customLoader) return; for (const field of Object.keys(customLoader)) { const loaderConfig = Object.assign({}, customLoader[field]); loaderConfig.field = field; addMethod(loaderConfig); } function addMethod(loaderConfig) { const field = loaderConfig.field; const appMethodName = 'mock' + field.replace(/^[a-z]/i, s => s.toUpperCase()); if (app[appMethodName]) { app.coreLogger.warn('Can\'t override app.%s', appMethodName); return; } app[appMethodName] = function(service, methodName, fn) { if (typeof service === 'string') { const arr = service.split('.'); service = loaderConfig.inject === 'ctx' ? this[field + 'Classes'] : this[field]; for (const key of arr) { service = service[key]; } service = service.prototype || service; } this._mockFn(service, methodName, fn); return this; }; } }; ================================================ FILE: lib/mock_http_server.js ================================================ 'use strict'; const http = require('http'); const SERVER = Symbol('http_server'); module.exports = app => { let server = app[SERVER] || app.callback(); if (typeof server === 'function') { server = http.createServer(server); // cache server, avoid create many times app[SERVER] = server; // emit server event just like egg-cluster does // https://github.com/eggjs/egg-cluster/blob/master/lib/app_worker.js#L52 app.emit('server', server); } return server; }; ================================================ FILE: lib/mock_httpclient.js ================================================ const mm = require('mm'); const extend = require('extend2'); const is = require('is-type-of'); const mockAgent = require('./mock_agent'); function matchMethod(mockMethods, method) { return mockMethods.some(m => m === '*' || m === method); } function matchUrl(mockUrl, url) { if (url === mockUrl) return true; if (mockUrl instanceof RegExp && url.match(mockUrl)) return true; return false; } function normalizeResult(result) { if (is.string(result)) { result = { data: result }; } if (!result.status) { result.status = 200; } result.data = result.data || ''; if (Buffer.isBuffer(result.data)) { // do nothing } else if (typeof result.data === 'object') { // json result.data = Buffer.from(JSON.stringify(result.data)); } else if (typeof result.data === 'string') { // string result.data = Buffer.from(result.data); } else { throw new Error('`mockResult.data` must be buffer, string or json'); } if (!result.res) { result.res = { status: result.status, }; } result.responseSize = result.responseSize || 0; if (result.data) { result.responseSize = result.data.length; } result.headers = result.headers || {}; return result; } module.exports = app => { /** * mock httpclient * @function mockHttpclient * @param {String} mockUrl - url * @param {String|Array} mockMethod - http method, default is '*' * @param {Object|Function} mockResult - you data * - data - buffer / string / json * - status - http status * - headers - response header * - delay - delay the associated reply by a set amount in ms. * - persist - any matching request will always reply with the defined response indefinitely, default is true * - repeats - number, any matching request will reply with the defined response a fixed amount of times * @return {Context} this */ const MOCK_CONFIGS = Symbol('MOCK_CONFIGS'); const MOCK_CONFIG_INDEX = Symbol('MOCK_CONFIG_INDEX'); return function mockHttpclient(mockUrl, mockMethod, mockResult) { if (!mockResult) { // app.mockHttpclient(mockUrl, mockResult) mockResult = mockMethod; mockMethod = '*'; } if (!Array.isArray(mockMethod)) mockMethod = [ mockMethod ]; mockMethod = mockMethod.map(method => (method || 'GET').toUpperCase()); if (app.config.httpclient.useHttpClientNext) { // use MockAgent on undici let mockConfigs = app[MOCK_CONFIGS]; if (!mockConfigs) { mockConfigs = []; mm(app, MOCK_CONFIGS, mockConfigs); } let mockConfigIndex = -1; let origin = mockUrl; let pathname = mockUrl; if (typeof mockUrl === 'string') { const urlObject = new URL(mockUrl); origin = urlObject.origin; const orginalPathname = urlObject.pathname; pathname = path => { if (path === orginalPathname) return true; // should match /foo?a=1 including query if (path.includes('?')) return path.startsWith(orginalPathname); return false; }; } else if (mockUrl instanceof RegExp) { let requestOrigin = ''; origin = value => { requestOrigin = value; return true; }; pathname = path => { for (const config of mockConfigs) { if (config.mockUrl.test(`${requestOrigin}${path}`)) { mm(app, MOCK_CONFIG_INDEX, config.mockConfigIndex); return true; } } return false; }; mockConfigIndex = mockConfigs.length; mockConfigs.push({ mockUrl, mockResult, mockConfigIndex }); } const mockPool = mockAgent.getAgent(app).get(origin); // persist default is true const persist = typeof mockResult?.persist === 'boolean' ? mockResult?.persist : true; mockMethod.forEach(function(method) { if (method === '*') { method = () => true; } const mockScope = mockPool.intercept({ path: pathname, method, }).reply(options => { // not support mockResult as an async function const requestUrl = `${options.origin}${options.path}`; let mockRequestResult; if (mockConfigIndex >= 0) { mockResult = mockConfigs[app[MOCK_CONFIG_INDEX]].mockResult; mockRequestResult = is.function(mockResult) ? mockResult(requestUrl, options) : mockResult; } else { mockRequestResult = is.function(mockResult) ? mockResult(requestUrl, options) : mockResult; } const result = extend(true, {}, normalizeResult(mockRequestResult)); return { statusCode: result.status, data: result.data, responseOptions: { headers: result.headers, }, }; }); if (mockResult?.delay > 0) { mockScope.delay(mockResult.delay); } if (persist) { mockScope.persist(); } else if (mockResult?.repeats > 0) { mockScope.times(mockResult.repeats); } }); return; } const httpclient = app.httpclient; const rawRequest = httpclient.request; mm(httpclient, 'requestThunk', _request); mm(httpclient, 'request', _request); mm(httpclient, 'curl', _request); return app; // support generator rather than callback and promise async function _request(url, opt) { opt = opt || {}; opt.method = (opt.method || 'GET').toUpperCase(); opt.headers = opt.headers || {}; if (matchUrl(mockUrl, url) && matchMethod(mockMethod, opt.method)) { let mockRequestResult = is.function(mockResult) ? mockResult(url, opt) : mockResult; // support mockResult as an async function if (is.promise(mockRequestResult)) mockRequestResult = await mockRequestResult; const result = extend(true, {}, normalizeResult(mockRequestResult)); const response = { status: result.status, statusCode: result.status, headers: result.headers, size: result.responseSize, aborted: false, rt: 1, keepAliveSocket: result.keepAliveSocket || false, }; httpclient.emit('request', { reqId: Date.now(), url, args: opt, ctx: opt.ctx, }); httpclient.emit('response', { error: null, ctx: opt.ctx, req: { url, options: opt, size: result.requestSize, }, res: response, }); if (opt.dataType === 'json') { try { result.data = JSON.parse(result.data); } catch (err) { err.name = 'JSONResponseFormatError'; throw err; } } else if (opt.dataType === 'text') { result.data = result.data.toString(); } return result; } return rawRequest.call(httpclient, url, opt); } }; }; ================================================ FILE: lib/parallel/agent.js ================================================ const debug = require('util').debuglog('egg-mock:lib:parallel:agent'); const path = require('path'); const Base = require('sdk-base'); const { detectPort } = require('detect-port'); const context = require('../context'); const formatOptions = require('../format_options'); const { sleep, rimraf } = require('../utils'); const mockCustomLoader = require('../mock_custom_loader'); const { APP_INIT, INIT_ONCE_LISTENER, INIT_ON_LISTENER, BIND_EVENT, consoleLogger, } = require('./util'); class MockAgent extends Base { constructor(options) { super({ initMethod: '_init' }); this.options = options; this.baseDir = options.baseDir; this.closed = false; this[APP_INIT] = false; this[INIT_ON_LISTENER] = new Set(); this[INIT_ONCE_LISTENER] = new Set(); // listen once, otherwise will throw exception when emit error without listenr this.once('error', err => { consoleLogger.error(err); }); } async _init() { if (this.options.beforeInit) { await this.options.beforeInit(this); delete this.options.beforeInit; } if (this.options.clean !== false) { const logDir = path.join(this.options.baseDir, 'logs'); try { await rimraf(logDir); } catch (err) { /* istanbul ignore next */ console.error(`remove log dir ${logDir} failed: ${err.stack}`); } } this.options.clusterPort = process.env.CLUSTER_PORT = await detectPort(); debug('get clusterPort %s', this.options.clusterPort); const { Agent } = require(this.options.framework); const agent = this._instance = new Agent(Object.assign({}, this.options)); // egg-mock plugin need to override egg context Object.assign(agent.context, context); mockCustomLoader(agent); debug('agent instantiate'); this[APP_INIT] = true; debug('this[APP_INIT] = true'); this[BIND_EVENT](); await agent.ready(); const msg = { action: 'egg-ready', data: this.options, }; agent.messenger._onMessage(msg); debug('agent ready'); } [BIND_EVENT]() { for (const args of this[INIT_ON_LISTENER]) { debug('on(%s), use cache and pass to app', args); this._instance.on(...args); this.removeListener(...args); } for (const args of this[INIT_ONCE_LISTENER]) { debug('once(%s), use cache and pass to app', args); this._instance.on(...args); this.removeListener(...args); } } on(...args) { if (this[APP_INIT]) { debug('on(%s), pass to app', args); this._instance.on(...args); } else { debug('on(%s), cache it because app has not init', args); if (this[INIT_ON_LISTENER]) { this[INIT_ON_LISTENER].add(args); } super.on(...args); } } once(...args) { if (this[APP_INIT]) { debug('once(%s), pass to app', args); this._instance.once(...args); } else { debug('once(%s), cache it because app has not init', args); if (this[INIT_ONCE_LISTENER]) { this[INIT_ONCE_LISTENER].add(args); } super.on(...args); } } /** * close app * @return {Promise} promise */ async close() { this.closed = true; const self = this; if (self._instance) { await self._instance.close(); } else { // when app init throws an exception, must wait for app quit gracefully await sleep(200); } } } module.exports = function(options) { options = formatOptions(options); return new MockAgent(options); }; ================================================ FILE: lib/parallel/agent_register.js ================================================ console.warn('[deprecated] use `egg-mock/register.js` instead of `egg-mock/lib/parallel/agent_register.js`'); module.exports = require('../../register'); ================================================ FILE: lib/parallel/app.js ================================================ const debug = require('util').debuglog('egg-mock:lib:parallel:app'); const Base = require('sdk-base'); const context = require('../context'); const formatOptions = require('../format_options'); const { sleep } = require('../utils'); const mockCustomLoader = require('../mock_custom_loader'); const mockHttpServer = require('../mock_http_server'); const { proxyApp, APP_INIT, INIT_ONCE_LISTENER, INIT_ON_LISTENER, BIND_EVENT, consoleLogger, } = require('./util'); class MockApplication extends Base { constructor(options) { super({ initMethod: '_init' }); this.options = options; this.baseDir = options.baseDir; this.closed = false; this[APP_INIT] = false; this[INIT_ON_LISTENER] = new Set(); this[INIT_ONCE_LISTENER] = new Set(); // listen once, otherwise will throw exception when emit error without listenr this.once('error', err => { consoleLogger.error(err); }); } async _init() { if (this.options.beforeInit) { await this.options.beforeInit(this); delete this.options.beforeInit; } this.options.clusterPort = this.options.clusterPort || process.env.CLUSTER_PORT; if (!this.options.clusterPort) { throw new Error('cannot get env.CLUSTER_PORT, parallel run fail'); } debug('get clusterPort %s', this.options.clusterPort); const { Application } = require(this.options.framework); const app = this._instance = new Application(Object.assign({}, this.options)); // egg-mock plugin need to override egg context Object.assign(app.context, context); mockCustomLoader(app); debug('app instantiate'); this[APP_INIT] = true; debug('this[APP_INIT] = true'); this[BIND_EVENT](); debug('http server instantiate'); mockHttpServer(app); await app.ready(); const msg = { action: 'egg-ready', data: this.options, }; app.messenger._onMessage(msg); debug('app ready'); } [BIND_EVENT]() { for (const args of this[INIT_ON_LISTENER]) { debug('on(%s), use cache and pass to app', args); this._instance.on(...args); this.removeListener(...args); } for (const args of this[INIT_ONCE_LISTENER]) { debug('once(%s), use cache and pass to app', args); this._instance.on(...args); this.removeListener(...args); } } on(...args) { if (this[APP_INIT]) { debug('on(%s), pass to app', args); this._instance.on(...args); } else { debug('on(%s), cache it because app has not init', args); if (this[INIT_ON_LISTENER]) { this[INIT_ON_LISTENER].add(args); } super.on(...args); } } once(...args) { if (this[APP_INIT]) { debug('once(%s), pass to app', args); this._instance.once(...args); } else { debug('once(%s), cache it because app has not init', args); if (this[INIT_ONCE_LISTENER]) { this[INIT_ONCE_LISTENER].add(args); } super.on(...args); } } /** * close app * @return {Promise} promise */ async close() { this.closed = true; const self = this; if (self._instance) { await self._instance.close(); } else { // when app init throws an exception, must wait for app quit gracefully await sleep(200); } } } module.exports = function(options) { options = formatOptions(options); const app = new MockApplication(options); return proxyApp(app, options); }; ================================================ FILE: lib/parallel/util.js ================================================ const debug = require('util').debuglog('egg-mock:lib:parallel:util'); const ConsoleLogger = require('egg-logger').EggConsoleLogger; const { getProperty } = require('../utils'); const consoleLogger = new ConsoleLogger({ level: 'INFO' }); const MOCK_APP_METHOD = [ 'ready', 'closed', 'close', 'on', 'once', ]; const INIT = Symbol('init'); const APP_INIT = Symbol('appInit'); const BIND_EVENT = Symbol('bindEvent'); const INIT_ON_LISTENER = Symbol('initOnListener'); const INIT_ONCE_LISTENER = Symbol('initOnceListener'); function proxyApp(app) { const proxyApp = new Proxy(app, { get(target, prop) { // don't delegate properties on MockAgent if (MOCK_APP_METHOD.includes(prop)) return getProperty(target, prop); if (!target[APP_INIT]) throw new Error(`can't get ${prop} before ready`); // it's asyncrounus when agent and app are loading, // so should get the properties after loader ready debug('proxy handler.get %s', prop); return target._instance[prop]; }, set(target, prop, value) { if (MOCK_APP_METHOD.includes(prop)) return true; if (!target[APP_INIT]) throw new Error(`can't set ${prop} before ready`); debug('proxy handler.set %s', prop); target._instance[prop] = value; return true; }, defineProperty(target, prop, descriptor) { // can't define properties on MockAgent if (MOCK_APP_METHOD.includes(prop)) return true; if (!target[APP_INIT]) throw new Error(`can't defineProperty ${prop} before ready`); debug('proxy handler.defineProperty %s', prop); Object.defineProperty(target._instance, prop, descriptor); return true; }, deleteProperty(target, prop) { // can't delete properties on MockAgent if (MOCK_APP_METHOD.includes(prop)) return true; if (!target[APP_INIT]) throw new Error(`can't delete ${prop} before ready`); debug('proxy handler.deleteProperty %s', prop); delete target._instance[prop]; return true; }, getOwnPropertyDescriptor(target, prop) { if (MOCK_APP_METHOD.includes(prop)) return Object.getOwnPropertyDescriptor(target, prop); if (!target[APP_INIT]) throw new Error(`can't getOwnPropertyDescriptor ${prop} before ready`); debug('proxy handler.getOwnPropertyDescriptor %s', prop); return Object.getOwnPropertyDescriptor(target._instance, prop); }, getPrototypeOf(target) { if (!target[APP_INIT]) throw new Error('can\'t getPrototypeOf before ready'); debug('proxy handler.getPrototypeOf %s'); return Object.getPrototypeOf(target._instance); }, }); return proxyApp; } module.exports = { MOCK_APP_METHOD, INIT, APP_INIT, BIND_EVENT, INIT_ON_LISTENER, INIT_ONCE_LISTENER, proxyApp, consoleLogger, }; ================================================ FILE: lib/prerequire.js ================================================ const debug = require('util').debuglog('egg-mock:prerequire'); const path = require('path'); const { existsSync } = require('fs'); const globby = require('globby'); const cwd = process.cwd(); const dirs = []; if (existsSync(path.join(cwd, 'app'))) { dirs.push('app/**/*.js'); } // avoid Error: ENOENT: no such file or directory, scandir if (existsSync(path.join(cwd, 'config'))) { dirs.push('config/**/*.js'); } const files = globby.sync(dirs, { cwd }); for (const file of files) { const filepath = path.join(cwd, file); try { debug('%s prerequire %s', process.pid, filepath); require(filepath); } catch (err) { debug('prerequire error %s', err.message); } } ================================================ FILE: lib/request_call_function.js ================================================ const httpclient = require('urllib'); const { port, method, args, property, needResult } = JSON.parse(process.argv[2]); const url = `http://127.0.0.1:${port}/__egg_mock_call_function`; httpclient.request(url, { method: 'POST', data: { method, args, property, needResult, }, contentType: 'json', dataType: 'json', }).then(({ data }) => { if (!data.success) { console.error('POST %s error, method: %s, args: %j', url, method, args); if (data.error) { console.error(data.error); } else if (data.message) { const err = new Error(data.message); err.stack = data.stack; console.error(err); } process.exit(2); } if (data.result) { console.log('%j', data.result); } process.exit(0); }).catch(err => { // ignore ECONNREFUSED error on mockRestore if (method === 'mockRestore' && err.message.includes('ECONNREFUSED')) { process.exit(0); } console.error('POST %s error, method: %s, args: %j', url, method, args); console.error(err.stack); // ignore all error on mockRestore if (method === 'mockRestore') { process.exit(0); } else { process.exit(1); } }); ================================================ FILE: lib/start-cluster ================================================ #!/usr/bin/env node if (process.env.EGG_BIN_PREREQUIRE) require('./prerequire'); const options = JSON.parse(process.argv.slice(2)); require(options.framework).startCluster(options); ================================================ FILE: lib/supertest.js ================================================ 'use strict'; const methods = require('methods'); const EggTest = require('./http_test'); const mockHttpServer = require('./mock_http_server'); const pkg = require('../package.json'); // patch from https://github.com/visionmedia/supertest/blob/199506d8dbfe0bb1434fc07c38cdcd1ab4c7c926/index.js#L19 /** * Test against the given `app`, * returning a new `Test`. * * @param {Application} app * @return {Test} * @public */ module.exports = app => { const server = mockHttpServer(app); const obj = {}; for (const method of methods) { obj[method] = url => { // support pathFor(url) if (url[0] !== '/') { const realUrl = app.router.pathFor(url); if (!realUrl) throw new Error(`Can\'t find router:${url}, please check your \'app/router.js\'`); url = realUrl; } const eggTest = new EggTest(server, method, url); eggTest.set('user-agent', `egg-mock/${pkg.version}`); return eggTest; }; } obj.del = obj.delete; return obj; }; ================================================ FILE: lib/tmp/.gitkeep ================================================ ================================================ FILE: lib/utils.js ================================================ const util = require('util'); const { rm } = require('fs/promises'); const { rmSync } = require('fs'); const is = require('is-type-of'); const setTimeoutPromise = util.promisify(setTimeout); module.exports = { async sleep(delay) { await setTimeoutPromise(delay); }, async rimraf(filepath) { await rm(filepath, { force: true, recursive: true }); }, rimrafSync(filepath) { rmSync(filepath, { force: true, recursive: true }); }, getProperty(target, prop) { const member = target[prop]; if (is.function(member)) { return member.bind(target); } return member; }, getEggOptions() { const options = {}; if (process.env.EGG_BASE_DIR) { options.baseDir = process.env.EGG_BASE_DIR; } else { options.baseDir = process.cwd(); } if (process.env.EGG_FRAMEWORK) { options.framework = process.env.EGG_FRAMEWORK; } return options; }, }; ================================================ FILE: package.json ================================================ { "name": "egg-mock", "version": "5.15.2", "eggPlugin": { "name": "egg-mock" }, "description": "mock server for egg", "types": "index.d.ts", "main": "index.js", "files": [ "app.js", "index.js", "index.d.ts", "bootstrap.js", "bootstrap.d.ts", "app", "lib", "register.js" ], "scripts": { "lint": "eslint lib app index.js test/*.test.js", "tsd": "tsd", "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" }, "dependencies": { "@eggjs/utils": "^4.0.2", "@types/supertest": "^2.0.7", "await-event": "^2.1.0", "co": "^4.6.0", "coffee": "^5.2.1", "detect-port": "^2.0.1", "egg-logger": "^3.5.0", "extend2": "^1.0.0", "get-ready": "^3.1.0", "globby": "^11.1.0", "is-type-of": "^2.2.0", "merge-descriptors": "^1.0.1", "methods": "^1.1.2", "mm": "^3.0.2", "sdk-base": "^4.2.1", "supertest": "^6.2.4", "urllib": "3" }, "peerDependencies": { "egg": "^3.12.0", "mocha": "^10.2.0", "urllib": "^3 || ^4" }, "devDependencies": { "@eggjs/tegg": "^3.2.2", "@eggjs/tegg-config": "^3.2.2", "@eggjs/tegg-controller-plugin": "^3.2.2", "@eggjs/tegg-plugin": "^3.2.2", "@eggjs/tsconfig": "^1.1.0", "@types/mocha": "^10.0.1", "@types/node": "20", "egg": "^3.12.0", "egg-bin": "^6.0.0", "egg-errors": "^2.2.1", "egg-tracer": "^2.0.0", "eslint": "^8.24.0", "eslint-config-egg": "^12.0.0", "mocha": "^10.1.0", "pedding": "^1.1.0", "tsd": "^0.31.0", "typescript": "5" }, "homepage": "https://github.com/eggjs/egg-mock", "repository": { "type": "git", "url": "git+https://github.com/eggjs/egg-mock.git" }, "bugs": { "url": "https://github.com/eggjs/egg/issues" }, "keywords": [ "egg", "mock" ], "engines": { "node": ">= 14.18.0" }, "author": "popomore " } ================================================ FILE: register.js ================================================ const debug = require('util').debuglog('egg-mock:register'); const mock = require('./index').default; const agentHandler = require('./lib/agent_handler'); const appHandler = require('./lib/app_handler'); const injectContext = require('./lib/inject_context'); exports.mochaGlobalSetup = async () => { debug('mochaGlobalSetup, agent.setupAgent() start'); await agentHandler.setupAgent(); debug('mochaGlobalSetup, agent.setupAgent() end'); }; exports.mochaGlobalTeardown = async () => { debug('mochaGlobalTeardown, agent.closeAgent() start'); await agentHandler.closeAgent(); debug('mochaGlobalTeardown, agent.closeAgent() end'); }; exports.mochaHooks = { async beforeAll() { const app = await appHandler.getApp(); debug('mochaHooks.beforeAll call, _app: %s', app); if (app) { await app.ready(); } }, async afterEach() { const app = await appHandler.getApp(); debug('mochaHooks.afterEach call, _app: %s', app); if (app) { await app.backgroundTasksFinished(); } await mock.restore(); }, async afterAll() { // skip auto app close on parallel if (process.env.ENABLE_MOCHA_PARALLEL) return; const app = await appHandler.getApp(); debug('mochaHooks.afterAll call, _app: %s', app); if (app) { await app.close(); } }, }; /** * Find active node mocha instances. * * @return {Array} */ function findNodeJSMocha() { const children = require.cache || {}; return Object.keys(children) .filter(function(child) { const val = children[child].exports; return typeof val === 'function' && val.name === 'Mocha'; }) .map(function(child) { return children[child].exports; }); } require('mocha'); const modules = findNodeJSMocha(); for (const module of modules) { injectContext(module); } ================================================ FILE: test/agent.test.js ================================================ 'use strict'; const mm = require('..'); const fs = require('fs'); const path = require('path'); const { rimraf } = require('../lib/utils'); const assert = require('assert'); const fixtures = path.join(__dirname, 'fixtures'); const baseDir = path.join(fixtures, 'agent'); describe('test/agent.test.js', () => { let app; afterEach(() => app.close()); afterEach(mm.restore); it('mock agent ok', async () => { const filepath = path.join(baseDir, 'run/test.txt'); await rimraf(filepath); app = mm.app({ baseDir, }); await app.ready(); assert(fs.readFileSync(filepath, 'utf8') === '123'); }); it('mock agent again ok', done => { app = mm.app({ baseDir, }); app.ready(done); }); it('should cluster-client work', done => { app = mm.app({ baseDir }); app.ready(() => { app._agent.client.subscribe('agent sub', data => { assert(data === 'agent sub'); app.client.subscribe('app sub', data => { assert(data === 'app sub'); done(); }); }); }); }); it('should agent work ok after ready', function* () { app = mm.app({ baseDir }); yield app.ready(); assert(app._agent.type === 'agent'); }); it('should FrameworkErrorformater work during agent boot', function* () { let logMsg; let catchErr; mm(process.stderr, 'write', msg => { logMsg = msg; }); app = mm.app({ baseDir: path.join(fixtures, 'agent-boot-error') }); try { yield app.ready(); } catch (err) { catchErr = err; } assert(catchErr.code === 'customPlugin_99'); assert(/framework\.CustomError\: mock error \[ https\:\/\/eggjs\.org\/zh-cn\/faq\/customPlugin_99 \]/.test(logMsg)); }); it('should FrameworkErrorformater work during agent boot ready', function* () { let logMsg; let catchErr; mm(process.stderr, 'write', msg => { logMsg = msg; }); app = mm.app({ baseDir: path.join(fixtures, 'agent-boot-ready-error') }); try { yield app.ready(); } catch (err) { catchErr = err; } assert(catchErr.code === 'customPlugin_99'); assert(/framework\.CustomError\: mock error \[ https\:\/\/eggjs\.org\/zh-cn\/faq\/customPlugin_99 \]/.test(logMsg)); }); }); ================================================ FILE: test/app/middleware/cluster_app_mock.test.js ================================================ 'use strict'; const assert = require('assert'); const mm = require('../../..'); describe('test/app/middleware/cluster_app_mock.test.js', () => { let app; before(() => { app = mm.app({ baseDir: 'demo', }); return app.ready(); }); after(() => app.close()); afterEach(mm.restore); it('should return 422 when method missing', () => { return app.httpRequest() .post('/__egg_mock_call_function') .send({}) .expect(422) .expect({ success: false, error: 'Missing method', }); }); it('should return 422 when args is not Array', () => { return app.httpRequest() .post('/__egg_mock_call_function') .send({ method: 'foo', args: 'hi' }) .expect(422) .expect({ success: false, error: 'args should be an Array instance', }); }); it('should return 422 when method is not exists on app', () => { return app.httpRequest() .post('/__egg_mock_call_function') .send({ method: 'not_exists_method', args: [] }) .expect(422) .expect({ success: false, error: 'method "not_exists_method" not exists on app', }); }); it('should recover error instance', function* () { let called; let callError; mm(app, 'foo', (a, err) => { called = true; callError = err; }); const err = { __egg_mock_type: 'error', name: 'FooError', message: 'foo error fire', stack: 'error stack', foo: 'bar', }; yield app.httpRequest() .post('/__egg_mock_call_function') .send({ method: 'foo', args: [ 1, err ] }) .expect(200) .expect({ success: true, }); assert(called); assert(callError.name === 'FooError'); assert(callError.stack === 'error stack'); assert(callError.message === 'foo error fire'); assert(callError.foo === 'bar'); }); }); ================================================ FILE: test/app.test.js ================================================ 'use strict'; const request = require('supertest'); const path = require('path'); const assert = require('assert'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); describe('test/app.test.js', () => { afterEach(mm.restore); // test mm.app call('app'); // test mm.cluster call('cluster'); it('should alias app.agent to app._agent', async () => { const baseDir = path.join(fixtures, 'app'); const app = mm.app({ baseDir, customEgg: path.join(__dirname, '../node_modules/egg'), }); await app.ready(); assert(app.agent === app._agent); assert(app.agent.app === app._app); }); it('should not use cache when app is closed', async () => { const baseDir = path.join(fixtures, 'app'); const app1 = mm.app({ baseDir, customEgg: path.join(__dirname, '../node_modules/egg'), }); await app1.ready(); await app1.close(); const app2 = mm.app({ baseDir, customEgg: path.join(__dirname, '../node_modules/egg'), }); await app2.ready(); await app2.close(); assert(app1 !== app2); }); it('should auto find framework when egg.framework exists on package.json', async () => { const baseDir = path.join(fixtures, 'yadan_app'); const app = mm.app({ baseDir, }); await app.ready(); assert(app.config.foobar === 'yadan'); await app.close(); }); it('should emit server event on app without superTest', async () => { const baseDir = path.join(fixtures, 'server'); const app = mm.app({ baseDir, }); await app.ready(); assert(app.server); assert(app.emitServer); await app.close(); }); it('support options.beforeInit', async () => { const baseDir = path.join(fixtures, 'app'); const app = mm.app({ baseDir, customEgg: path.join(__dirname, '../node_modules/egg'), cache: false, beforeInit(instance) { return new Promise(resolve => { setTimeout(() => { instance.options.test = 'abc'; resolve(); }, 100); }); }, }); await app.ready(); assert(!app.options.beforeInit); assert(app.options.test === 'abc'); }); it('should emit error when load Application fail', done => { const baseDir = path.join(fixtures, 'app-fail'); const app = mm.app({ baseDir, cache: false }); app.once('error', err => { assert(/load error/.test(err.message)); done(); }); }); it('should FrameworkErrorformater work during app boot', async () => { let logMsg; let catchErr; mm(process.stderr, 'write', msg => { logMsg = msg; }); const app = mm.app({ baseDir: path.join(fixtures, 'app-boot-error') }); try { await app.ready(); } catch (err) { catchErr = err; } assert(catchErr.code === 'customPlugin_99'); assert(/framework\.CustomError\: mock error \[ https\:\/\/eggjs\.org\/zh-cn\/faq\/customPlugin_99 \]/.test(logMsg)); }); it('should FrameworkErrorformater work during app boot ready', async () => { let logMsg; let catchErr; mm(process.stderr, 'write', msg => { logMsg = msg; }); const app = mm.app({ baseDir: path.join(fixtures, 'app-boot-ready-error') }); try { await app.ready(); } catch (err) { catchErr = err; } assert(catchErr.code === 'customPlugin_99'); assert(/framework\.CustomError\: mock error \[ https\:\/\/eggjs\.org\/zh-cn\/faq\/customPlugin_99 \]/.test(logMsg)); }); }); function call(method) { let app; describe(`mm.${method}()`, () => { before(done => { const baseDir = path.join(fixtures, 'app'); mm(process, 'cwd', () => baseDir); app = mm[method]({ cache: false, coverage: false, }); app.ready(done); }); after(() => app.close()); it('should work', done => { request(app.callback()) .get('/') .expect('foo') .expect(200, done); }); it('should emit server event on app', () => { return app.httpRequest() .get('/keepAliveTimeout') .expect(200) .expect({ keepAliveTimeout: 5000, }); }); it('should app.expectLog(), app.notExpectLog() work', async () => { await app.httpRequest() .get('/logger') .expect(200) .expect({ ok: true, }); app.expectLog('[app.expectLog() test] ok'); app.expectLog('[app.expectLog() test] ok', 'logger'); app.expectLog('[app.expectLog(coreLogger) test] ok', 'coreLogger'); app.notExpectLog('[app.notExpectLog() test] fail'); app.notExpectLog('[app.notExpectLog() test] fail', 'logger'); app.notExpectLog('[app.notExpectLog(coreLogger) test] fail', 'coreLogger'); if (method === 'app') { app.expectLog(/\[app\.expectLog\(\) test\] ok/); app.expectLog(/\[app\.expectLog\(\) test\] ok/, app.logger); app.expectLog('[app.expectLog(coreLogger) test] ok', app.coreLogger); app.expectLog(/\[app\.expectLog\(coreLogger\) test\] ok/, 'coreLogger'); app.notExpectLog(/\[app\.notExpectLog\(\) test\] fail/); app.notExpectLog(/\[app\.notExpectLog\(\) test\] fail/, app.logger); app.notExpectLog('[app.notExpectLog(coreLogger) test] fail', app.coreLogger); app.notExpectLog(/\[app\.notExpectLog\(coreLogger\) test\] fail/, 'coreLogger'); } try { app.expectLog('[app.expectLog(coreLogger) test] ok'); throw new Error('should not run this'); } catch (err) { assert(err.message.includes('Can\'t find String:"[app.expectLog(coreLogger) test] ok" in ')); assert(err.message.includes('app-web.log')); } try { app.notExpectLog('[app.expectLog() test] ok'); throw new Error('should not run this'); } catch (err) { assert(err.message.includes('Find String:"[app.expectLog() test] ok" in ')); assert(err.message.includes('app-web.log')); } }); it('should app.mockLog() then app.expectLog() work', async () => { app.mockLog(); app.mockLog('logger'); app.mockLog('coreLogger'); await app.httpRequest() .get('/logger') .expect(200) .expect({ ok: true, }); app.expectLog('[app.expectLog() test] ok'); app.expectLog('[app.expectLog() test] ok', 'logger'); app.expectLog('[app.expectLog(coreLogger) test] ok', 'coreLogger'); app.notExpectLog('[app.notExpectLog() test] fail'); app.notExpectLog('[app.notExpectLog() test] fail', 'logger'); app.notExpectLog('[app.notExpectLog(coreLogger) test] fail', 'coreLogger'); if (method === 'app') { app.expectLog(/\[app\.expectLog\(\) test\] ok/); app.expectLog(/\[app\.expectLog\(\) test\] ok/, app.logger); app.expectLog('[app.expectLog(coreLogger) test] ok', app.coreLogger); app.expectLog(/\[app\.expectLog\(coreLogger\) test\] ok/, 'coreLogger'); app.notExpectLog(/\[app\.notExpectLog\(\) test\] fail/); app.notExpectLog(/\[app\.notExpectLog\(\) test\] fail/, app.logger); app.notExpectLog('[app.notExpectLog(coreLogger) test] fail', app.coreLogger); app.notExpectLog(/\[app\.notExpectLog\(coreLogger\) test\] fail/, 'coreLogger'); } try { app.expectLog('[app.expectLog(coreLogger) test] ok'); throw new Error('should not run this'); } catch (err) { assert(err.message.includes('Can\'t find String:"[app.expectLog(coreLogger) test] ok" in ')); assert(err.message.includes('app-web.log')); } try { app.notExpectLog('[app.expectLog() test] ok'); throw new Error('should not run this'); } catch (err) { assert(err.message.includes('Find String:"[app.expectLog() test] ok" in ')); assert(err.message.includes('app-web.log')); } if (method === 'app') { assert(app.logger._mockLogs); assert(app.coreLogger._mockLogs); await mm.restore(); assert(!app.logger._mockLogs); assert(!app.coreLogger._mockLogs); } }); it('should app.mockLog() don\'t read from file', async () => { await app.httpRequest() .get('/logger') .expect(200) .expect({ ok: true, }); app.expectLog('INFO'); app.mockLog(); app.notExpectLog('INFO'); }); it('should request with ua', async () => { await app.httpRequest() .get('/ua') .expect(200) .expect(/egg-mock\//); }); }); describe(`mm.${method}({ baseDir, plugin=string })`, () => { const pluginDir = path.join(fixtures, 'fooPlugin'); before(done => { mm(process, 'cwd', () => pluginDir); app = mm[method]({ baseDir: path.join(__dirname, 'fixtures/apps/foo'), plugin: 'fooPlugin', cache: false, coverage: false, }); app.ready(done); }); after(() => app.close()); it('should work', done => { request(app.callback()) .get('/') .expect({ fooPlugin: true, }) .expect(200, done); }); }); describe(`mm.${method}({ baseDir, plugin=true })`, () => { const pluginDir = path.join(fixtures, 'fooPlugin'); before(done => { mm(process, 'cwd', () => pluginDir); app = mm[method]({ baseDir: path.join(__dirname, 'fixtures/apps/foo'), plugin: true, cache: false, coverage: false, }); app.ready(done); }); after(() => app.close()); it('should work', done => { request(app.callback()) .get('/') .expect({ fooPlugin: true, }) .expect(200, done); }); }); describe(`mm.${method}({ baseDir, plugins })`, () => { before(done => { app = mm[method]({ baseDir: path.join(__dirname, 'fixtures/apps/foo'), plugins: { fooPlugin: { enable: true, path: path.join(fixtures, 'fooPlugin'), }, }, cache: false, coverage: false, }); app.ready(done); }); after(() => app.close()); it('should work', done => { request(app.callback()) .get('/') .expect({ fooPlugin: true, }) .expect(200, done); }); }); describe(`mm.${method}({ baseDir, customEgg=fullpath})`, () => { before(done => { app = mm[method]({ baseDir: 'apps/barapp', customEgg: path.join(fixtures, 'bar'), cache: false, coverage: false, }); app.ready(done); }); after(() => app.close()); it('should work', done => { request(app.callback()) .get('/') .expect({ foo: 'bar', foobar: 'bar', }) .expect(200, done); }); }); describe(`mm.${method}({ baseDir, customEgg=true})`, () => { before(done => { mm(process, 'cwd', () => { return path.join(fixtures, 'bar'); }); app = mm[method]({ baseDir: path.join(fixtures, 'apps/barapp'), customEgg: true, cache: false, coverage: false, }); app.ready(done); }); after(() => app.close()); it('should work', done => { request(app.callback()) .get('/') .expect({ foo: 'bar', foobar: 'bar', }) .expect(200, done); }); }); describe(`mm.${method}({ baseDir, cache=true })`, () => { let app1; let app2; before(done => { app1 = mm[method]({ baseDir: 'cache', coverage: false, }); app1.ready(done); }); before(done => { app2 = mm[method]({ baseDir: 'cache', coverage: false, }); app2.ready(done); }); after(async () => { await app1.close(); await app2.close(); }); it('should equal', () => { assert(app1 === app2); }); }); } ================================================ FILE: test/app_event.test.js ================================================ const path = require('path'); const request = require('supertest'); const pedding = require('pedding'); const assert = require('assert'); const mm = require('..'); const { sleep } = require('../lib/utils'); const fixtures = path.join(__dirname, 'fixtures'); const baseDir = path.join(fixtures, 'app-event'); describe('test/app_event.test.js', () => { afterEach(mm.restore); describe('after ready', () => { let app; before(() => { app = mm.app({ baseDir, cache: false, }); return app.ready(); }); after(() => app.close()); it('should listen by eventByRequest', done => { done = pedding(3, done); app.once('eventByRequest', done); app.on('eventByRequest', done); request(app.callback()) .get('/event') .expect(200) .expect('done', done); }); }); describe('before ready', () => { let app; beforeEach(() => { app = mm.app({ baseDir, cache: false, }); }); afterEach(() => app.ready()); afterEach(() => app.close()); it('should listen after app ready', done => { done = pedding(2, done); app.once('appReady', done); app.on('appReady', done); }); it('should listen after app instantiate', done => { done = pedding(2, done); app.once('appInstantiated', done); app.on('appInstantiated', done); }); }); describe('throw before app init', () => { let app; beforeEach(() => { const baseDir = path.join(fixtures, 'app'); const customEgg = path.join(fixtures, 'error-framework'); app = mm.app({ baseDir, customEgg, cache: false, }); }); afterEach(() => app.close()); it('should listen using app.on', done => { app.on('error', err => { assert(err.message === 'start error'); done(); }); }); it('should listen using app.once', done => { app.once('error', err => { assert(err.message === 'start error'); done(); }); }); it('should throw error from ready', function* () { try { yield app.ready(); } catch (err) { assert(err.message === 'start error'); } }); it('should close when app init failed', function* () { app.once('error', () => {}); yield sleep(1000); // app._app is undefined yield app.close(); }); }); }); ================================================ FILE: test/app_proxy.test.js ================================================ 'use strict'; const path = require('path'); const assert = require('assert'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); const baseDir = path.join(fixtures, 'app-proxy'); describe('test/app_proxy.test.js', () => { afterEach(mm.restore); describe('when before ready', () => { let app; const baseDir = path.join(fixtures, 'app-proxy-ready'); before(() => { app = mm.app({ baseDir, cache: false, }); }); after(function* () { yield app.ready(); yield app.close(); }); it('should not get property', function* () { assert.throws(() => { app.config; }, /can't get config before ready/); }); it('should not set property', function* () { assert.throws(() => { app.curl = function* mockCurl() { return 'mock'; }; }, /can't set curl before ready/); }); it('should not define property', function* () { assert.throws(() => { Object.defineProperty(app, 'config', { value: {}, }); }, /can't defineProperty config before ready/); }); it('should not delete property', function* () { assert.throws(() => { delete app.config; }, /can't delete config before ready/); }); it('should not getOwnPropertyDescriptor property', function* () { assert.throws(() => { Object.getOwnPropertyDescriptor(app, 'config'); }, /can't getOwnPropertyDescriptor config before ready/); }); it('should not getPrototypeOf property', function* () { assert.throws(() => { Object.getPrototypeOf(app); }, /can't getPrototypeOf before ready/); }); }); describe('handler.get', () => { let app; before(() => { app = mm.app({ baseDir, cache: false, }); return app.ready(); }); after(() => app.close()); it('should get property', () => { assert(app.getter === 'getter'); assert(app.method() === 'method'); }); it('should ignore when get property on MockApplication', function* () { assert(app.closed === false); yield app.close(); assert(app.closed === true); }); }); describe('handler.set', () => { let app; before(() => { app = mm.app({ baseDir, cache: false, }); return app.ready(); }); after(() => app.close()); it('should override property with setter', function* () { app.curl = function* mockCurl() { return 'mock'; }; const data = yield app.curl(); assert(data === 'mock'); }); it('should ignore when set property on MockApplication', function* () { app.closed = true; assert(app.closed === false); yield app.close(); }); }); describe('handler.defineProperty', () => { let app; before(() => { app = mm.app({ baseDir, cache: false, }); return app.ready(); }); after(() => app.close()); it('should defineProperty', function* () { assert(app.prop === 1); Object.defineProperty(app, 'prop', { get() { if (!this._prop) { this._prop = 0; } return this._prop++; }, set(prop) { if (this._prop) { this._prop = this._prop + prop; } }, }); assert(app.prop === 0); assert(app.prop === 1); app.prop = 2; assert(app.prop === 4); app.prop = 2; assert(app.prop === 7); }); it('should ignore when defineProperty on MockApplication', function* () { assert(app.closed === false); Object.defineProperty(app, 'closed', { value: true, }); assert(app.closed === false); assert(!app._app.closed); }); }); describe('handler.deleteProperty', () => { let app; before(() => { app = mm.app({ baseDir, cache: false, }); return app.ready(); }); after(() => app.close()); it('should delete property', () => { assert(app.shouldBeDelete === true); delete app.shouldBeDelete; assert(app.shouldBeDelete === undefined); }); it('should ignore when delete property on MockApplication', function* () { assert(!app._app.closed); assert(app.closed === false); delete app.closed; assert(!app._app.closed); assert(app.closed === false); }); }); describe('handler.getOwnPropertyDescriptor', () => { let app; before(() => { app = mm.app({ baseDir, cache: false, }); return app.ready(); }); after(() => app.close()); it('should getOwnPropertyDescriptor', () => { const d = Object.getOwnPropertyDescriptor(app, 'a'); assert(typeof d.get === 'function'); assert(typeof d.set === 'function'); }); it('should ignore when getOwnPropertyDescriptor on MockApplication', function* () { const d = Object.getOwnPropertyDescriptor(app, 'closed'); assert(d.value === false); }); }); describe('handler.getPrototypeOf', () => { let app; before(() => { app = mm.app({ baseDir, cache: false, }); return app.ready(); }); after(() => app.close()); it('should getPrototypeOf', () => { assert(Object.getPrototypeOf(app) === Object.getPrototypeOf(app._app)); }); }); describe('MOCK_APP_METHOD', () => { let app; before(() => { app = mm.app({ baseDir, cache: false, }); return app.ready(); }); after(() => app.close()); it('should be used on MockApplication', () => { const MOCK_APP_METHOD = [ 'ready', 'closed', 'close', '_agent', '_app', 'on', 'once', ]; for (const key of MOCK_APP_METHOD) { assert(app[key] !== app._app[key]); } }); }); describe('messenger binding', () => { let app; const baseDir = path.join(fixtures, 'messenger-binding'); before(() => { app = mm.app({ baseDir, cache: false, }); return app.ready(); }); after(() => app.close()); it('should send message from app to agent', () => { assert.deepEqual(app._agent.received, [ 'send data when app starting', 'send data when app started', 'send data to a random agent', ]); }); it('should send message from agent to app', () => { process.nextTick(() => { assert.deepEqual(app._app.received, [ 'send data when server started', 'send data to a random app', ]); }); }); it('should receive egg-ready', () => { assert(app._app.eggReady === true); assert(app._agent.eggReady === true); assert(app._agent.eggReadyData.baseDir === baseDir); assert(app._app.eggReadyData.baseDir === baseDir); }); it('should broadcast message successfully', () => { assert(app._app.recievedBroadcastAction === true); assert(app._agent.recievedBroadcastAction === true); assert(app._app.recievedAgentRecievedAction === true); }); it('should send message from app to app', () => { assert(app._app.recievedAppAction === true); }); }); }); ================================================ FILE: test/bootstrap-plugin.test.js ================================================ 'use strict'; const path = require('path'); const coffee = require('coffee'); const mock = require('mm'); describe('test/bootstrap-plugin.test.js', () => { after(() => mock.restore()); it('should throw', () => { mock(process.env, 'EGG_BASE_DIR', path.join(__dirname, './fixtures/plugin-bootstrap')); const testFile = path.join(__dirname, './fixtures/plugin-bootstrap/test.js'); return coffee.fork(testFile) // .debug() .expect('stderr', /DO NOT USE bootstrap to test plugin/) .expect('code', 1) .end(); }); }); ================================================ FILE: test/bootstrap.test.js ================================================ 'use strict'; const path = require('path'); const baseDir = process.env.EGG_BASE_DIR = path.join(__dirname, './fixtures/app'); const { app, assert, mm, mock } = require('../bootstrap'); describe('test/bootstrap.test.js', () => { it('should create app success', () => { assert(app.baseDir === baseDir); }); it('should mock and mm success', () => { assert(app.baseDir === baseDir); mm(app, 'baseDir', 'foo'); assert(app.baseDir === 'foo'); mock(app, 'baseDir', 'bar'); assert(app.baseDir === 'bar'); }); it('should afterEach(mm.restore) success', () => { assert(app.baseDir === baseDir); }); it('should assert success', done => { try { assert(app.baseDir !== baseDir); } catch (err) { done(); } }); describe('backgroundTasksFinished()', () => { it('should wait for background task 1 finished', function* () { yield app.httpRequest() .get('/counter') .expect(200) .expect({ counter: 0 }); yield app.httpRequest() .get('/counter/plus') .expect(200) .expect({ counter: 0 }); }); it('should wait for background task 2 finished', function* () { yield app.httpRequest() .get('/counter') .expect(200) .expect({ counter: 1 }); yield app.httpRequest() .get('/counter/minus') .expect(200) .expect({ counter: 1 }); }); it('should wait for background task 3 finished', function* () { yield app.httpRequest() .get('/counter') .expect(200) .expect({ counter: 0 }); app.mockContext({ superMan: true }); yield app.httpRequest() .get('/counter/plus') .expect(200) .expect({ counter: 0 }); }); it('should wait for background task 4 finished', function* () { yield app.httpRequest() .get('/counter') .expect(200) .expect({ counter: 10 }); yield app.httpRequest() .get('/counter/plusplus') .expect(200) .expect({ counter: 10 }); }); it('should wait for background task 5 finished', function* () { yield app.httpRequest() .get('/counter') .expect(200) .expect({ counter: 12 }); app.mockContext({ superMan: true }); yield app.httpRequest() .get('/counter/plusplus') .expect(200) .expect({ counter: 12 }); }); it('should all reset', function* () { yield app.httpRequest() .get('/counter') .expect(200) .expect({ counter: 32 }); }); it('should always return promise instance', () => { let p = app.backgroundTasksFinished(); assert(p.then); p = app.backgroundTasksFinished(); assert(p.then); p = app.backgroundTasksFinished(); assert(p.then); }); }); }); ================================================ FILE: test/cluster.test.js ================================================ 'use strict'; const path = require('path'); const assert = require('assert'); const request = require('supertest'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); describe('test/cluster.test.js', () => { afterEach(mm.restore); describe('normal', () => { let app; before(() => { app = mm.cluster({ baseDir: path.join(fixtures, 'demo'), cache: false, coverage: false, }); // app.debug(); return app.ready(); }); after(() => app.close()); it('should have members', async () => { assert(app.callback() === app); assert(app.listen() === app); await app.ready(); assert(app.process); }); it('should throw error when mock function not exists', () => { assert.throws(() => { app.mockNotExists(); }, /method "mockNotExists" not exists on app/); }); it('should listen on port', () => { app.expect('stdout', /egg started on http:\/\/127.0.0.1:17\d{3}/); }); }); describe('cluster with fullpath baseDir', () => { let app; before(done => { app = mm.cluster({ baseDir: path.join(fixtures, 'demo'), cache: false, coverage: false, }); app.ready(done); }); after(() => app.close()); it('should work', done => { request(app.callback()) .get('/hello') .expect('hi') .expect(200, done); }); }); describe('cluster with shortpath baseDir', () => { let app; before(done => { app = mm.cluster({ baseDir: 'demo', cache: false, coverage: false, }); app.ready(done); }); after(() => app.close()); it('should work', done => { request(app.callback()) .get('/hello') .expect('hi') .expect(200, done); }); }); describe('cluster with customEgg=string', () => { let app; before(done => { app = mm.cluster({ baseDir: 'apps/barapp', customEgg: path.join(fixtures, 'bar'), cache: false, coverage: false, }); app.ready(done); }); after(() => app.close()); it('should work', done => { request(app.callback()) .get('/') .expect({ foo: 'bar', foobar: 'bar', }) .expect(200, done); }); }); describe('cluster with customEgg=true', () => { let app; before(done => { mm(process, 'cwd', () => { return path.join(fixtures, 'bar'); }); app = mm.cluster({ baseDir: path.join(fixtures, 'apps/barapp'), customEgg: true, cache: false, coverage: false, }); app.ready(done); }); after(() => app.close()); it('should work', done => { request(app.callback()) .get('/') .expect({ foo: 'bar', foobar: 'bar', }) .expect(200, done); }); }); describe('cluster with cache', () => { let app1; let app2; afterEach(() => { const promises = []; app1 && promises.push(app1.close()); app2 && promises.push(app2.close()); return Promise.all(promises); }); it('should return cached cluster app', async () => { app1 = mm.cluster({ baseDir: 'demo', coverage: false, }); await app1.ready(); app2 = mm.cluster({ baseDir: 'demo', coverage: false, }); await app2.ready(); assert(app1 === app2); }); it('should return new app if cached app has been closed', async () => { app1 = mm.cluster({ baseDir: 'demo', coverage: false, }); await app1.ready(); await app1.close(); app2 = mm.cluster({ baseDir: 'demo', coverage: false, }); await app2.ready(); assert(app2 !== app1); }); }); describe('cluster with eggPath', () => { let app; after(() => app.close()); it('should get eggPath', async () => { app = mm.cluster({ baseDir: 'demo', customEgg: path.join(__dirname, 'fixtures/chair'), eggPath: '/path/to/eggPath', cache: false, coverage: false, }); await app .debug() .expect('stdout', /\/path\/to\/eggPath/) .end(); }); }); describe('cluster with workers', () => { let app; after(() => app.close()); it('should get 2 workers', async () => { app = mm.cluster({ baseDir: 'demo', customEgg: path.join(__dirname, 'fixtures/chair'), workers: 2, cache: false, coverage: false, }); app.debug(); await app.expect('stdout', /app_worker#1:/) .expect('stdout', /app_worker#2:/) .end(); }); }); describe('cluster with opts.customEgg', () => { let app; after(() => app.close()); it('should pass execArgv', async () => { app = mm.cluster({ baseDir: 'custom_egg', customEgg: path.join(__dirname, 'fixtures/bar'), workers: 1, cache: false, coverage: false, opt: { execArgv: [ '--inspect' ], }, }); // app.debug(); await app.expect('stdout', /app_worker#1:/) .expect('stderr', /Debugger listening/) .end(); }); }); describe('cluster with egg.framework=yadan', () => { let app; after(() => app.close()); it('should pass execArgv', async () => { app = mm.cluster({ baseDir: 'yadan_app', workers: 1, cache: false, coverage: false, }); await app.expect('stdout', /app_worker#1:/) .end(); }); }); describe('prerequire', () => { let app; after(() => app.close()); it('should load files', async () => { mm(process.env, 'EGG_BIN_PREREQUIRE', 'true'); mm(process.env, 'NODE_DEBUG', 'egg-mock:prerequire'); app = mm.cluster({ baseDir: 'yadan_app', workers: 1, cache: false, coverage: false, }); await app .expect('stderr', /prerequire .+?\/app\/extend\/application.js/) .expect('code', 0) .end(); }); }); describe('custom port', () => { let app; after(() => app.close()); it('should use it', async () => { app = mm.cluster({ baseDir: path.join(fixtures, 'demo'), cache: false, coverage: false, port: 5566, }); // app.debug(); await app.ready(); app.expect('stdout', /egg started on http:\/\/127.0.0.1:5566/); }); }); }); ================================================ FILE: test/ctx.test.js ================================================ 'use strict'; const path = require('path'); const assert = require('assert'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); describe('test/ctx.test.js', () => { afterEach(mm.restore); let app; before(done => { app = mm.app({ baseDir: path.join(fixtures, 'demo'), }); app.ready(done); }); after(() => app.close()); it('should has logger, app, request', () => { const ctx = app.mockContext(); assert(ctx.app instanceof Object); assert(ctx.logger instanceof Object); assert(ctx.coreLogger instanceof Object); assert(ctx.request.url === '/'); assert(ctx.request.ip === '127.0.0.1'); }); it('should ctx.ip work', () => { const ctx = app.mockContext(); ctx.request.headers['x-forwarded-for'] = ''; assert(ctx.request.ip === '127.0.0.1'); }); it('should has services', function* () { const ctx = app.mockContext(); const data = yield ctx.service.foo.get('foo'); assert(data === 'bar'); }); it('should not override mockData', function* () { const mockData = { user: 'popomore' }; app.mockContext(mockData); app.mockContext(mockData); assert(!mockData.headers); assert(!mockData.method); }); describe('mockContextScope', () => { it('should not conflict with nest call', async () => { await app.mockContextScope(async ctx => { const currentStore = app.ctxStorage.getStore(); assert(ctx === currentStore); await app.mockContextScope(async nestCtx => { const currentStore = app.ctxStorage.getStore(); assert(nestCtx === currentStore); }); }); }); it('should not conflict with concurrent call', async () => { await Promise.all([ await app.mockContextScope(async ctx => { const currentStore = app.ctxStorage.getStore(); assert(ctx === currentStore); }), await app.mockContextScope(async ctx => { const currentStore = app.ctxStorage.getStore(); assert(ctx === currentStore); }), ]); }); }); }); ================================================ FILE: test/fixtures/agent/agent.js ================================================ const fs = require('fs'); const path = require('path'); const Client = require('./client'); module.exports = agent => { const done = agent.readyCallback('agent:agent'); const p = path.join(__dirname, 'run/test.txt'); fs.mkdirSync(path.join(__dirname, 'run'), { recursive: true }); fs.writeFile(p, '123', done); agent.client = agent.cluster(Client).create(); }; ================================================ FILE: test/fixtures/agent/app.js ================================================ 'use strict'; const fs = require('fs'); const path = require('path'); const Client = require('./client'); module.exports = function(app) { const done = app.readyCallback('agent:app'); const p = path.join(__dirname, 'run/test.txt'); setTimeout(() => { fs.readFile(p, 'utf-8', done); }, 1000); app.client = app.cluster(Client).create(); }; ================================================ FILE: test/fixtures/agent/client.js ================================================ 'use strict'; const Base = require('sdk-base'); class Client extends Base { constructor() { super(); this.ready(true); } subscribe(topic, listener) { setTimeout(() => listener(topic), 10); } } module.exports = Client; ================================================ FILE: test/fixtures/agent/config/config.default.js ================================================ 'use strict'; module.exports = { keys: '123', }; ================================================ FILE: test/fixtures/agent/package.json ================================================ { "name": "demo" } ================================================ FILE: test/fixtures/agent-boot-error/agent.js ================================================ 'use strict'; const { FrameworkBaseError } = require('egg-errors'); class CustomError extends FrameworkBaseError { get module() { return 'customPlugin'; } } module.exports = class { configWillLoad() { throw new CustomError('mock error', 99); } }; ================================================ FILE: test/fixtures/agent-boot-error/package.json ================================================ { "name": "agent-boot-error" } ================================================ FILE: test/fixtures/agent-boot-ready-error/agent.js ================================================ 'use strict'; const { FrameworkBaseError } = require('egg-errors'); class CustomError extends FrameworkBaseError { get module() { return 'customPlugin'; } } module.exports = class { async didLoad() { throw new CustomError('mock error', 99); } }; ================================================ FILE: test/fixtures/agent-boot-ready-error/package.json ================================================ { "name": "agent-boot-ready-error" } ================================================ FILE: test/fixtures/app/app/router.js ================================================ const { sleep } = require('../../../../lib/utils'); module.exports = app => { app.get('/', async ctx => { ctx.body = 'foo'; }); app.get('/keepAliveTimeout', async ctx => { ctx.body = { keepAliveTimeout: ctx.app.serverKeepAliveTimeout, }; }); app.get('/ua', async ctx => { ctx.body = ctx.get('user-agent'); }); app.get('/logger', async ctx => { ctx.logger.info('[app.expectLog() test] ok'); ctx.coreLogger.info('[app.expectLog(coreLogger) test] ok'); ctx.body = { ok: true }; }); let counter = 0; app.get('/counter', async ctx => { ctx.body = { counter }; }); app.get('/counter/plus', async ctx => { ctx.runInBackground(async ctx => { // mock io delay await sleep(10); if (ctx.superMan) { counter += 10; return; } counter++; }); ctx.body = { counter }; }); app.get('/counter/minus', async ctx => { ctx.runInBackground(async () => { await sleep(10); counter--; }); ctx.body = { counter }; }); app.get('/counter/plusplus', async ctx => { ctx.runInBackground(async ctx => { // mock io delay await sleep(10); if (ctx.superMan) { counter += 10; } else { counter++; } ctx.runInBackground(async ctx => { // mock io delay await sleep(10); if (ctx.superMan) { counter += 10; } else { counter++; } }); }); ctx.body = { counter }; }); }; ================================================ FILE: test/fixtures/app/app.js ================================================ 'use strict'; module.exports = app => { app.on('server', server => { app.serverKeepAliveTimeout = server.keepAliveTimeout || 5000; }); }; ================================================ FILE: test/fixtures/app/config/config.default.js ================================================ 'use strict'; module.exports = { keys: '123', }; ================================================ FILE: test/fixtures/app/package.json ================================================ { "name": "app" } ================================================ FILE: test/fixtures/app-boot-error/app.js ================================================ 'use strict'; const { FrameworkBaseError } = require('egg-errors'); class CustomError extends FrameworkBaseError { get module() { return 'customPlugin'; } } module.exports = class { configWillLoad() { throw new CustomError('mock error', 99); } }; ================================================ FILE: test/fixtures/app-boot-error/package.json ================================================ { "name": "app-boot-error" } ================================================ FILE: test/fixtures/app-boot-ready-error/app.js ================================================ 'use strict'; const { FrameworkBaseError } = require('egg-errors'); class CustomError extends FrameworkBaseError { get module() { return 'customPlugin'; } } module.exports = class { async didLoad() { throw new CustomError('mock error', 99); } }; ================================================ FILE: test/fixtures/app-boot-ready-error/package.json ================================================ { "name": "app-boot-ready-error" } ================================================ FILE: test/fixtures/app-event/agent.js ================================================ 'use strict'; module.exports = app => { app.ready(() => { app.emit('agentInstantiated'); }); }; ================================================ FILE: test/fixtures/app-event/app/router.js ================================================ 'use strict'; module.exports = function(app) { app.get('/event', function* () { this.app.emit('eventByRequest'); this.body = 'done'; }); }; ================================================ FILE: test/fixtures/app-event/app.js ================================================ 'use strict'; const { sleep } = require('../../../lib/utils'); module.exports = app => { app.ready(() => { // after ready app.emit('appReady'); }); process.nextTick(() => { // before ready, after app instantiate app.emit('appInstantiated'); }); app.beforeStart(function* () { yield sleep(1000); }); }; ================================================ FILE: test/fixtures/app-event/config/config.default.js ================================================ 'use strict'; module.exports = { keys: '123', }; ================================================ FILE: test/fixtures/app-event/package.json ================================================ { "name": "app-event" } ================================================ FILE: test/fixtures/app-fail/app/router.js ================================================ 'use strict'; throw new Error('load error'); ================================================ FILE: test/fixtures/app-fail/config/config.default.js ================================================ 'use strict'; module.exports = { keys: '123', }; ================================================ FILE: test/fixtures/app-fail/package.json ================================================ { "name": "egg-mock" } ================================================ FILE: test/fixtures/app-proxy/app/extend/application.js ================================================ 'use strict'; module.exports = { get getter() { return 'getter'; }, method() { return 'method'; }, prop: 1, shouldBeDelete: true, get a() { return 'a'; }, set a(x) { this._a = x; }, }; ================================================ FILE: test/fixtures/app-proxy/config/config.default.js ================================================ 'use strict'; module.exports = { keys: '123', }; ================================================ FILE: test/fixtures/app-proxy/package.json ================================================ { "name": "app-proxy" } ================================================ FILE: test/fixtures/app-proxy-ready/agent.js ================================================ 'use strict'; const { sleep } = require('../../../lib/utils'); module.exports = app => { // set timeout let testcase ran before ready app.beforeStart(function* () { yield sleep(1000); }); }; ================================================ FILE: test/fixtures/app-proxy-ready/app.js ================================================ 'use strict'; module.exports = app => { app.emit('eventOnReady'); app.emit('eventOnReady'); app.emit('eventOnceReady'); app.emit('eventOnceReady'); }; ================================================ FILE: test/fixtures/app-proxy-ready/config/config.default.js ================================================ 'use strict'; module.exports = { keys: '123', }; ================================================ FILE: test/fixtures/app-proxy-ready/package.json ================================================ { "name": "app-proxy-ready" } ================================================ FILE: test/fixtures/app-ready-failed/app.js ================================================ module.exports = class AppHook { async didLoad() { throw new Error('mock app ready failed'); } }; ================================================ FILE: test/fixtures/app-ready-failed/config/config.default.js ================================================ 'use strict'; module.exports = { keys: '123', }; ================================================ FILE: test/fixtures/app-ready-failed/package.json ================================================ { "name": "app" } ================================================ FILE: test/fixtures/app-ready-failed/test/index.test.js ================================================ const assert = require('assert'); const { app } = require('../../../../bootstrap'); describe('test for app ready failed', () => { it('should not print', () => { // ... assert(app); }); }); ================================================ FILE: test/fixtures/apps/app-not-clean/config/config.default.js ================================================ 'use strict'; module.exports = { keys: '123', }; ================================================ FILE: test/fixtures/apps/app-not-clean/logs/keep ================================================ ================================================ FILE: test/fixtures/apps/app-not-clean/package.json ================================================ { "name": "app-not-clean" } ================================================ FILE: test/fixtures/apps/app-throw/app/router.js ================================================ 'use strict'; module.exports = app => { app.get('/throw', function* () { this.body = 'foo'; setTimeout(() => { /* eslint-disable-next-line */ a.b = c; }, 1); }); app.get('/throw-unhandledRejection', function* () { this.body = 'foo'; new Promise((resolve, reject) => { reject(new Error('foo reject error')); }); }); app.get('/throw-unhandledRejection-string', function* () { this.body = 'foo'; new Promise((resolve, reject) => { reject(new Error('foo reject string error')); }); }); app.get('/throw-unhandledRejection-obj', function* () { this.body = 'foo'; new Promise((resolve, reject) => { const err = { name: 'TypeError', message: 'foo reject obj error', stack: new Error().stack, toString() { return this.name + ': ' + this.message; }, }; reject(err); }); }); }; ================================================ FILE: test/fixtures/apps/app-throw/config/config.default.js ================================================ 'use strict'; exports.keys = 'test key'; ================================================ FILE: test/fixtures/apps/app-throw/config/config.unittest.js ================================================ 'use strict'; exports.logger = { consoleLevel: 'NONE', }; ================================================ FILE: test/fixtures/apps/app-throw/package.json ================================================ { "name": "app-throw" } ================================================ FILE: test/fixtures/apps/barapp/app/controller/home.js ================================================ 'use strict'; module.exports = function* () { this.body = { foo: this.app.config.foo, foobar: this.app.config.foobar, }; }; ================================================ FILE: test/fixtures/apps/barapp/app/router.js ================================================ 'use strict'; module.exports = function(app) { app.get('/', app.controller.home); }; ================================================ FILE: test/fixtures/apps/barapp/config/config.default.js ================================================ 'use strict'; exports.foo = 'bar'; exports.keys = '123'; ================================================ FILE: test/fixtures/apps/barapp/package.json ================================================ { "name": "barapp" } ================================================ FILE: test/fixtures/apps/env-app/config/config.default.js ================================================ 'use strict'; exports.fakeplugin = { foo: 'bar-default', }; exports.logger = { consoleLevel: 'NONE', }; exports.development = { fastReady: false, }; exports.keys = '123'; ================================================ FILE: test/fixtures/apps/env-app/config/config.local.js ================================================ 'use strict'; exports.development = { fastReady: false, }; ================================================ FILE: test/fixtures/apps/env-app/config/config.prod.js ================================================ 'use strict'; exports.fakeplugin = { foo: 'bar-prod', }; ================================================ FILE: test/fixtures/apps/env-app/config/config.test.js ================================================ 'use strict'; exports.fakeplugin = { foo: 'bar-test', }; ================================================ FILE: test/fixtures/apps/env-app/config/config.unittest.js ================================================ 'use strict'; exports.fakeplugin = { foo: 'bar-unittest', }; ================================================ FILE: test/fixtures/apps/env-app/package.json ================================================ { "name": "env-app" } ================================================ FILE: test/fixtures/apps/foo/app/router.js ================================================ 'use strict'; module.exports = function(app) { app.get('/', function* () { this.body = { fooPlugin: app.fooPlugin, }; }); }; ================================================ FILE: test/fixtures/apps/foo/config/config.default.js ================================================ 'use strict'; exports.keys = '123'; ================================================ FILE: test/fixtures/apps/foo/package.json ================================================ { "name": "foo" } ================================================ FILE: test/fixtures/apps/mock_cookies/app/router.js ================================================ 'use strict'; module.exports = function(app) { app.get('/', function* () { this.body = { cookieValue: this.cookies.get('foo', { signed: false }) || undefined, cookiesValue: this.cookies.get('foo', { signed: false }) || undefined, }; }); }; ================================================ FILE: test/fixtures/apps/mock_cookies/config/config.default.js ================================================ 'use strict'; exports.keys = '123'; ================================================ FILE: test/fixtures/apps/mock_cookies/package.json ================================================ { "name": "mockCookies" } ================================================ FILE: test/fixtures/apps/mockhome/config/config.default.js ================================================ 'use strict'; exports.keys = '123'; ================================================ FILE: test/fixtures/apps/mockhome/package.json ================================================ { "name": "mockhome" } ================================================ FILE: test/fixtures/apps/no-framework/config/plugin.js ================================================ 'use strict'; const path = require('path'); module.exports = { a: { enable: true, path: path.join(__dirname, '../plugin/a'), }, }; ================================================ FILE: test/fixtures/apps/no-framework/package.json ================================================ { "name": "no-framework" } ================================================ FILE: test/fixtures/apps/no-framework/plugin/a/app/extend/application.js ================================================ 'use strict'; module.exports = { mockEnv() { this.config.env = 'mocked by plugin'; }, }; ================================================ FILE: test/fixtures/apps/no-framework/plugin/a/package.json ================================================ { "eggPlugin": { "name": "a", "dependencies": [ "egg-mock" ] } } ================================================ FILE: test/fixtures/apps/parallel-test/package.json ================================================ { "name": "foo" } ================================================ FILE: test/fixtures/apps/parallel-test/test/a.test.js ================================================ const mm = require('../../../../../'); const assert = require('assert'); describe('test/parallel_a.test.js', () => { it('should work', () => { mm(global, 'test', '233'); assert(global.test === '233'); }); }); ================================================ FILE: test/fixtures/apps/parallel-test/test/b.test.js ================================================ const mm = require('../../../../../'); const assert = require('assert'); describe('test/parallel_b.test.js', () => { it('should work', () => { mm(global, 'test', '244'); assert(global.test === '244'); }); }); ================================================ FILE: test/fixtures/apps/parallel-test/test/c.test.js ================================================ const assert = require('assert'); describe('test/parallel_a.test.js', () => { it('should work', () => { assert(!global.test); }); }); ================================================ FILE: test/fixtures/bar/config/config.default.js ================================================ 'use strict'; exports.foobar = 'bar'; exports.keys = '123'; ================================================ FILE: test/fixtures/bar/index.js ================================================ 'use strict'; const egg = require('egg'); const EGG_PATH = Symbol.for('egg#eggPath'); class BarApplication extends egg.Application { get [EGG_PATH]() { return __dirname; } } Object.assign(exports, egg); exports.Application = BarApplication; ================================================ FILE: test/fixtures/bar/package.json ================================================ { "name": "bar", "version": "1.0.0" } ================================================ FILE: test/fixtures/cache/config.default.js ================================================ 'use strict'; exports.keys = '123'; ================================================ FILE: test/fixtures/cache/package.json ================================================ { "name": "cache" } ================================================ FILE: test/fixtures/chair/index.js ================================================ 'use strict'; const egg = require('egg'); function startCluster(options) { // print for the testcase that will assert stdout console.log(options.eggPath); delete options.eggPath; egg.startCluster(options); } Object.assign(exports, egg, { startCluster }); ================================================ FILE: test/fixtures/chair/package.json ================================================ { "name": "chair" } ================================================ FILE: test/fixtures/create-context-failed/config/config.default.js ================================================ 'use strict'; module.exports = { keys: '123', }; ================================================ FILE: test/fixtures/create-context-failed/package.json ================================================ { "name": "app" } ================================================ FILE: test/fixtures/create-context-failed/test/index.test.js ================================================ const { setGetAppCallback } = require('../../../..'); setGetAppCallback(() => { return { ready: async () => { // ... }, mockContextScope: async () => { throw new Error('mock create context failed'); }, close: async () => { // ... }, backgroundTasksFinished: async () => { // ... }, }; }); describe('test case create context error', () => { it('should not print', () => { }); }); ================================================ FILE: test/fixtures/custom-loader/app/adapter/docker.js ================================================ 'use strict'; class DockerAdapter { constructor(app) { this.app = app; } async inspectDocker() { return 'docker'; } } module.exports = DockerAdapter; ================================================ FILE: test/fixtures/custom-loader/app/controller/user.js ================================================ 'use strict'; class UserController { constructor(ctx) { this.ctx = ctx; this.app = ctx.app; } async get() { this.ctx.body = { adapter: await this.app.adapter.docker.inspectDocker(), repository: await this.ctx.repository.user.get(), }; } } module.exports = UserController; ================================================ FILE: test/fixtures/custom-loader/app/repository/user.js ================================================ 'use strict'; class UserRepository { constructor(ctx) { this.ctx = ctx; } async get() { return this.ctx.params.name; } } module.exports = UserRepository; ================================================ FILE: test/fixtures/custom-loader/app/router.js ================================================ 'use strict'; module.exports = app => { app.router.get('/users/:name', app.controller.user.get); }; ================================================ FILE: test/fixtures/custom-loader/config/config.default.js ================================================ 'use strict'; module.exports = { keys: '123', customLoader: { adapter: { directory: 'app/adapter', inject: 'app', }, repository: { directory: 'app/repository', inject: 'ctx', }, env: { directory: 'app/env', inject: 'ctx', }, }, }; ================================================ FILE: test/fixtures/custom-loader/package.json ================================================ { "name": "custom-loader" } ================================================ FILE: test/fixtures/custom_egg/package.json ================================================ { "name": "custom" } ================================================ FILE: test/fixtures/demo/app/context.js ================================================ 'use strict'; module.exports = { getResult(result) { return { body: result, }; }, }; ================================================ FILE: test/fixtures/demo/app/controller/file.js ================================================ const assert = require('assert'); module.exports = app => { return async function file(ctx) { const ctxFromStorage = app.ctxStorage.getStore(); assert(ctxFromStorage !== ctx); const stream = await ctx.getFileStream(); const fields = stream.fields; ctx.body = { fields, filename: stream.filename, user: ctx.user, traceId: ctx.traceId, ctxFromStorageUser: ctxFromStorage.user, ctxFromStorageTraceId: ctxFromStorage.traceId, }; }; }; ================================================ FILE: test/fixtures/demo/app/controller/home.js ================================================ '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; let r = this.app.httpclient[method](url + '/mock_url', { dataType, data, }); if (method === 'request') r = r.then(d => d); const r1 = yield r; const r2 = yield this.app.httpclient[method](url + '/mock_url', { 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.mockUrlGet = function* () { 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; }; ================================================ FILE: test/fixtures/demo/app/controller/session.js ================================================ 'use strict'; module.exports = function* () { this.session.save(); this.body = this.session; }; ================================================ FILE: test/fixtures/demo/app/controller/user.js ================================================ exports.get = function* () { this.set('x-request-url', this.url); this.body = this.user; }; exports.post = function* () { this.body = { user: this.user, params: this.request.body, }; }; ================================================ FILE: test/fixtures/demo/app/extend/application.js ================================================ '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); }, }; ================================================ FILE: test/fixtures/demo/app/router.js ================================================ 'use strict'; module.exports = function(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.post('/mock_url', 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); }; ================================================ FILE: test/fixtures/demo/app/service/bar/foo.js ================================================ 'use strict'; module.exports = function(app) { class Foo extends app.Service { * get() { return 'bar'; } } return Foo; }; ================================================ FILE: test/fixtures/demo/app/service/foo.js ================================================ 'use strict'; module.exports = function(app) { class Foo extends app.Service { * get() { return 'bar'; } getSync() { return 'bar'; } } return Foo; }; ================================================ FILE: test/fixtures/demo/app/service/old.js ================================================ 'use strict'; module.exports = () => { exports.test = function* () { return 'hello'; }; return exports; }; ================================================ FILE: test/fixtures/demo/app/service/third/bar/foo.js ================================================ 'use strict'; module.exports = function(app) { class Main extends app.Service { * get() { return 'third'; } } return Main; }; ================================================ FILE: test/fixtures/demo/app.js ================================================ 'use strict'; module.exports = function() { }; ================================================ FILE: test/fixtures/demo/config/config.js ================================================ 'use strict'; module.exports = { urllib: { keepAlive: false, }, logger: { consoleLevel: 'NONE', }, keys: '123', }; ================================================ FILE: test/fixtures/demo/mocks_data/service/bar/foo/get/foobar.js ================================================ 'use strict'; module.exports = 'foobar'; ================================================ FILE: test/fixtures/demo/mocks_data/service/foo/get/foobar.js ================================================ 'use strict'; module.exports = 'foobar'; ================================================ FILE: test/fixtures/demo/package.json ================================================ { "name": "demo" } ================================================ FILE: test/fixtures/demo-async/app/controller/home.js ================================================ 'use strict'; module.exports = function(app) { class Home extends app.Controller { async testService() { this.ctx.body = { foo1: await this.service.foo.get(), foo2: await this.service.bar.foo.get(), foo3: this.service.foo.getSync(), thirdService: await this.service.third.bar.foo.get(), }; } } return Home; }; ================================================ FILE: test/fixtures/demo-async/app/router.js ================================================ 'use strict'; module.exports = function(app) { app.get('/service', app.controller.home.testService); }; ================================================ FILE: test/fixtures/demo-async/app/service/bar/foo.js ================================================ 'use strict'; module.exports = function(app) { class Foo extends app.Service { async get() { return 'bar'; } } return Foo; }; ================================================ FILE: test/fixtures/demo-async/app/service/foo.js ================================================ 'use strict'; module.exports = function(app) { class Foo extends app.Service { async get() { return 'bar'; } getSync() { return 'bar'; } } return Foo; }; ================================================ FILE: test/fixtures/demo-async/app/service/third/bar/foo.js ================================================ 'use strict'; module.exports = function(app) { class Main extends app.Service { async get() { return 'third'; } } return Main; }; ================================================ FILE: test/fixtures/demo-async/app.js ================================================ 'use strict'; module.exports = function() { }; ================================================ FILE: test/fixtures/demo-async/config/config.js ================================================ 'use strict'; module.exports = { urllib: { keepAlive: false, }, logger: { consoleLevel: 'NONE', }, keys: '123', }; ================================================ FILE: test/fixtures/demo-async/mocks_data/service/bar/foo/get/foobar.js ================================================ 'use strict'; module.exports = 'foobar'; ================================================ FILE: test/fixtures/demo-async/mocks_data/service/foo/get/foobar.js ================================================ 'use strict'; module.exports = 'foobar'; ================================================ FILE: test/fixtures/demo-async/package.json ================================================ { "name": "demo-async" } ================================================ FILE: test/fixtures/demo_next/app/context.js ================================================ 'use strict'; module.exports = { getResult(result) { return { body: result, }; }, }; ================================================ FILE: test/fixtures/demo_next/app/controller/file.js ================================================ 'use strict'; module.exports = function* () { const stream = yield this.getFileStream(); const fields = stream.fields; this.body = { fields, filename: stream.filename, user: this.user, }; }; ================================================ FILE: test/fixtures/demo_next/app/controller/home.js ================================================ '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; }; ================================================ FILE: test/fixtures/demo_next/app/controller/session.js ================================================ 'use strict'; module.exports = function* () { this.session.save(); this.body = this.session; }; ================================================ FILE: test/fixtures/demo_next/app/controller/user.js ================================================ 'use strict'; exports.get = function* () { this.body = this.user; }; exports.post = function* () { this.body = { user: this.user, params: this.request.body, }; }; ================================================ FILE: test/fixtures/demo_next/app/extend/application.js ================================================ '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); }, }; ================================================ FILE: test/fixtures/demo_next/app/router.js ================================================ 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'); }; ================================================ FILE: test/fixtures/demo_next/app/service/bar/foo.js ================================================ 'use strict'; module.exports = function(app) { class Foo extends app.Service { * get() { return 'bar'; } } return Foo; }; ================================================ FILE: test/fixtures/demo_next/app/service/foo.js ================================================ 'use strict'; module.exports = function(app) { class Foo extends app.Service { * get() { return 'bar'; } getSync() { return 'bar'; } } return Foo; }; ================================================ FILE: test/fixtures/demo_next/app/service/old.js ================================================ 'use strict'; module.exports = () => { exports.test = function* () { return 'hello'; }; return exports; }; ================================================ FILE: test/fixtures/demo_next/app/service/third/bar/foo.js ================================================ 'use strict'; module.exports = function(app) { class Main extends app.Service { * get() { return 'third'; } } return Main; }; ================================================ FILE: test/fixtures/demo_next/app.js ================================================ 'use strict'; module.exports = function() { }; ================================================ FILE: test/fixtures/demo_next/config/config.js ================================================ module.exports = { httpclient: { useHttpClientNext: true, request: { timing: true, }, }, logger: { consoleLevel: 'NONE', }, keys: '123', }; ================================================ FILE: test/fixtures/demo_next/mocks_data/service/bar/foo/get/foobar.js ================================================ 'use strict'; module.exports = 'foobar'; ================================================ FILE: test/fixtures/demo_next/mocks_data/service/foo/get/foobar.js ================================================ 'use strict'; module.exports = 'foobar'; ================================================ FILE: test/fixtures/demo_next/package.json ================================================ { "name": "demo" } ================================================ FILE: test/fixtures/demo_next_h2/app/context.js ================================================ 'use strict'; module.exports = { getResult(result) { return { body: result, }; }, }; ================================================ FILE: test/fixtures/demo_next_h2/app/controller/file.js ================================================ 'use strict'; module.exports = function* () { const stream = yield this.getFileStream(); const fields = stream.fields; this.body = { fields, filename: stream.filename, user: this.user, }; }; ================================================ FILE: test/fixtures/demo_next_h2/app/controller/home.js ================================================ '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; }; ================================================ FILE: test/fixtures/demo_next_h2/app/controller/session.js ================================================ 'use strict'; module.exports = function* () { this.session.save(); this.body = this.session; }; ================================================ FILE: test/fixtures/demo_next_h2/app/controller/user.js ================================================ 'use strict'; exports.get = function* () { this.body = this.user; }; exports.post = function* () { this.body = { user: this.user, params: this.request.body, }; }; ================================================ FILE: test/fixtures/demo_next_h2/app/extend/application.js ================================================ '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); }, }; ================================================ FILE: test/fixtures/demo_next_h2/app/router.js ================================================ 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'); }; ================================================ FILE: test/fixtures/demo_next_h2/app/service/bar/foo.js ================================================ 'use strict'; module.exports = function(app) { class Foo extends app.Service { * get() { return 'bar'; } } return Foo; }; ================================================ FILE: test/fixtures/demo_next_h2/app/service/foo.js ================================================ 'use strict'; module.exports = function(app) { class Foo extends app.Service { * get() { return 'bar'; } getSync() { return 'bar'; } } return Foo; }; ================================================ FILE: test/fixtures/demo_next_h2/app/service/old.js ================================================ 'use strict'; module.exports = () => { exports.test = function* () { return 'hello'; }; return exports; }; ================================================ FILE: test/fixtures/demo_next_h2/app/service/third/bar/foo.js ================================================ 'use strict'; module.exports = function(app) { class Main extends app.Service { * get() { return 'third'; } } return Main; }; ================================================ FILE: test/fixtures/demo_next_h2/app.js ================================================ 'use strict'; module.exports = function() { }; ================================================ FILE: test/fixtures/demo_next_h2/config/config.js ================================================ module.exports = { httpclient: { useHttpClientNext: true, allowH2: true, request: { timing: true, }, }, logger: { consoleLevel: 'NONE', }, keys: '123', }; ================================================ FILE: test/fixtures/demo_next_h2/mocks_data/service/bar/foo/get/foobar.js ================================================ 'use strict'; module.exports = 'foobar'; ================================================ FILE: test/fixtures/demo_next_h2/mocks_data/service/foo/get/foobar.js ================================================ 'use strict'; module.exports = 'foobar'; ================================================ FILE: test/fixtures/demo_next_h2/package.json ================================================ { "name": "demo" } ================================================ FILE: test/fixtures/disable-security/app/context.js ================================================ 'use strict'; module.exports = { getResult(result) { return { body: result, }; }, }; ================================================ FILE: test/fixtures/disable-security/app/controller/home.js ================================================ '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 dataType = this.query.dataType; let r = this.app.httpclient[method](url + '/mock_url', { dataType, }); if (method === 'request') r = r.then(d => d); const r1 = yield r; const r2 = yield this.app.httpclient[method](url + '/mock_url', { method: 'POST', dataType, }); this.body = { get: Buffer.isBuffer(r1.data) ? r1.data.toString() : r1.data, post: Buffer.isBuffer(r2.data) ? r2.data.toString() : r2.data, }; }; exports.mockUrlGet = function* () { 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; }; ================================================ FILE: test/fixtures/disable-security/app/controller/session.js ================================================ 'use strict'; module.exports = function* () { this.body = this.session; }; ================================================ FILE: test/fixtures/disable-security/app/router.js ================================================ 'use strict'; module.exports = function(app) { app.get('/', 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.post('/mock_url', app.controller.home.mockUrlPost); app.get('/mock_urllib', app.controller.home.mockUrllibHeaders); app.get('/session', app.controller.session); app.post('/', app.controller.home.post); }; ================================================ FILE: test/fixtures/disable-security/app/service/bar/foo.js ================================================ 'use strict'; module.exports = function(app) { class Foo extends app.Service { * get() { return 'bar'; } } return Foo; }; ================================================ FILE: test/fixtures/disable-security/app/service/foo.js ================================================ 'use strict'; module.exports = function(app) { class Foo extends app.Service { * get() { return 'bar'; } getSync() { return 'bar'; } } return Foo; }; ================================================ FILE: test/fixtures/disable-security/app/service/old.js ================================================ 'use strict'; module.exports = () => { exports.test = function* () { return 'hello'; }; return exports; }; ================================================ FILE: test/fixtures/disable-security/app/service/third/bar/foo.js ================================================ 'use strict'; module.exports = function(app) { class Main extends app.Service { * get() { return 'third'; } } return Main; }; ================================================ FILE: test/fixtures/disable-security/app.js ================================================ 'use strict'; module.exports = function() { }; ================================================ FILE: test/fixtures/disable-security/config/config.js ================================================ 'use strict'; module.exports = { urllib: { keepAlive: false, }, logger: { consoleLevel: 'NONE', }, keys: '123', }; ================================================ FILE: test/fixtures/disable-security/config/plugin.js ================================================ 'use strict'; exports.security = false; ================================================ FILE: test/fixtures/disable-security/mocks_data/service/bar/foo/get/foobar.js ================================================ 'use strict'; module.exports = 'foobar'; ================================================ FILE: test/fixtures/disable-security/mocks_data/service/foo/get/foobar.js ================================================ 'use strict'; module.exports = 'foobar'; ================================================ FILE: test/fixtures/disable-security/package.json ================================================ { "name": "demo" } ================================================ FILE: test/fixtures/error-framework/index.js ================================================ 'use strict'; const egg = require('egg'); class Application extends egg.Application { constructor() { throw new Error('start error'); } } Object.assign(exports, egg, { Application }); ================================================ FILE: test/fixtures/error-framework/package.json ================================================ { "name": "error-framework" } ================================================ FILE: test/fixtures/failed-app/config/config.default.js ================================================ 'use strict'; module.exports = { keys: '123', }; ================================================ FILE: test/fixtures/failed-app/package.json ================================================ { "name": "app" } ================================================ FILE: test/fixtures/failed-app/test/index.test.js ================================================ const assert = require('assert'); describe('test/index.test.ts', () => { describe('before error', () => { before(() => { throw new Error('before error'); }); it('should not print', () => { assert.fail('never arrive'); }); }); describe('after error', () => { after(() => { throw new Error('after error'); }); it('should print', () => { console.log('after error test case should print'); }); }); describe('beforeEach error', () => { beforeEach(() => { throw new Error('beforeEach error'); }); it('should not print', () => { assert.fail('never arrive'); }); }); describe('afterEach error', () => { afterEach(() => { throw new Error('afterEach error'); }); it('should print', () => { console.log('afterEach error test case should print'); }); }); describe('case error', () => { it('should failed', () => { assert.fail('should fail'); }); it('should work', () => { // ... }); }); }); ================================================ FILE: test/fixtures/fooPlugin/app.js ================================================ 'use strict'; module.exports = function(app) { app.fooPlugin = true; }; ================================================ FILE: test/fixtures/fooPlugin/config/config.default.js ================================================ 'use strict'; exports.keys = '123'; ================================================ FILE: test/fixtures/fooPlugin/config/plugin.js ================================================ 'use strict'; exports.fooPlugin = { }; ================================================ FILE: test/fixtures/fooPlugin/package.json ================================================ { "name": "fooPlugin", "eggPlugin": { "name": "fooPlugin" } } ================================================ FILE: test/fixtures/get-app-failed/config/config.default.js ================================================ 'use strict'; module.exports = { keys: '123', }; ================================================ FILE: test/fixtures/get-app-failed/package.json ================================================ { "name": "app" } ================================================ FILE: test/fixtures/get-app-failed/test/index.test.js ================================================ const { setGetAppCallback } = require('../../../..'); setGetAppCallback(() => { throw new Error('mock get app failed'); }); describe('test case create context error', () => { it('should not print', () => { }); }); ================================================ FILE: test/fixtures/messenger-binding/agent.js ================================================ 'use strict'; module.exports = agent => { agent.received = []; agent.messenger.on('action', data => agent.received.push(data)); agent.messenger.on('broadcast-action', () => { agent.recievedBroadcastAction = true; agent.messenger.sendToApp('agent-recieved-broadcast-action'); }); agent.messenger.sendToApp('action', 'send data when agent starting'); agent.ready(() => { agent.messenger.sendToApp('action', 'send data when agent started'); }); agent.messenger.on('egg-ready', () => { agent.messenger.sendToApp('action', 'send data when server started'); process.nextTick(() => agent.messenger.sendRandom('action', 'send data to a random app')); }); agent.messenger.on('egg-ready', data => { agent.eggReady = true; agent.eggReadyData = data; }); }; ================================================ FILE: test/fixtures/messenger-binding/app.js ================================================ 'use strict'; module.exports = app => { app.received = []; app.messenger.on('action', data => app.received.push(data)); app.messenger.on('app-action', () => { app.recievedAppAction = true; }); app.messenger.on('broadcast-action', () => { app.recievedBroadcastAction = true; }); app.messenger.on('agent-recieved-broadcast-action', () => { app.recievedAgentRecievedAction = true; }); app.messenger.sendToAgent('action', 'send data when app starting'); app.ready(() => { app.messenger.sendToAgent('action', 'send data when app started'); app.messenger.sendRandom('action', 'send data to a random agent') app.messenger.broadcast('broadcast-action', 'broadcast action'); app.messenger.sendToApp('app-action', 'send action to app'); }); app.messenger.on('egg-ready', data => { app.eggReady = true; app.eggReadyData = data; }); }; ================================================ FILE: test/fixtures/messenger-binding/config/config.default.js ================================================ 'use strict'; exports.keys = '123'; ================================================ FILE: test/fixtures/messenger-binding/package.json ================================================ { "name": "messenger-binding" } ================================================ FILE: test/fixtures/plugin/package.json ================================================ { "eggPlugin": { "name": "plugin1" } } ================================================ FILE: test/fixtures/plugin-bootstrap/package.json ================================================ { "eggPlugin": { "name": "plugin1" } } ================================================ FILE: test/fixtures/plugin-bootstrap/test.js ================================================ 'use strict'; require('../../../bootstrap'); ================================================ FILE: test/fixtures/plugin_throw/package.json ================================================ {} ================================================ FILE: test/fixtures/request/app/router.js ================================================ 'use strict'; module.exports = app => { app.get('home', '/', function* () { this.body = 'hello world'; }); app.get('session', '/session', function* () { this.body = 'hello session'; }); }; ================================================ FILE: test/fixtures/request/config/config.default.js ================================================ 'use strict'; exports.keys = 'foo'; ================================================ FILE: test/fixtures/request/package.json ================================================ { "name": "request-demo" } ================================================ FILE: test/fixtures/server/app.js ================================================ 'use strict'; module.exports = app => { app.messenger.on('egg-ready', server => { app.emitServer = !!server; }); }; ================================================ FILE: test/fixtures/server/package.json ================================================ { "name": "server", "version": "1.0.0" } ================================================ FILE: test/fixtures/setup-app/config/config.default.js ================================================ 'use strict'; module.exports = { keys: '123', }; ================================================ FILE: test/fixtures/setup-app/package.json ================================================ { "name": "app" } ================================================ FILE: test/fixtures/setup-app/test/.setup.js ================================================ const mm = require('../../../../index'); const path = require('path'); before(async () => { global.app = mm.app({ baseDir: path.join(__dirname, '../'), framework: require.resolve('egg'), }); mm.setGetAppCallback(() => { return global.app; }); await global.app.ready(); }); ================================================ FILE: test/fixtures/setup-app/test/index.test.js ================================================ const assert = require('assert'); describe('test/index.test.ts', () => { it('should work', () => { // eslint-disable-next-line no-undef assert(app.currentContext); }); }); ================================================ FILE: test/fixtures/tegg-app/app/modules/foo/LogService.ts ================================================ import { AccessLevel, Inject, SingletonProto } from '@eggjs/tegg'; interface Tracer { traceId: string; } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class LogService { @Inject() private readonly tracer: Tracer; getTracerId() { return this.tracer.traceId; } } ================================================ FILE: test/fixtures/tegg-app/app/modules/foo/package.json ================================================ { "name": "foo", "eggModule": { "name": "foo" } } ================================================ FILE: test/fixtures/tegg-app/app.js ================================================ module.exports = app => { app.on('server', server => { app.serverKeepAliveTimeout = server.keepAliveTimeout || 5000; }); }; ================================================ FILE: test/fixtures/tegg-app/config/config.default.js ================================================ module.exports = { keys: '123', }; ================================================ FILE: test/fixtures/tegg-app/config/plugin.js ================================================ module.exports = { teggConfig: { package: '@eggjs/tegg-config', enable: true, }, tegg: { package: '@eggjs/tegg-plugin', enable: true, }, teggController: { package: '@eggjs/tegg-controller-plugin', enable: true, }, tracer: { package: 'egg-tracer', enable: true, }, }; ================================================ FILE: test/fixtures/tegg-app/package.json ================================================ { "name": "app", "egg": { "typescript": true } } ================================================ FILE: test/fixtures/tegg-app/test/hooks.test.ts ================================================ import assert from 'assert'; import { Context } from 'egg'; import { app } from '../../../../bootstrap'; describe('test/hooks.test.ts', () => { let beforeCtx; let afterCtx; let beforeEachCtxList: Record = {}; let afterEachCtxList: Record = {}; let itCtxList: Record = {}; before(async () => { beforeCtx = app.currentContext; }); after(() => { afterCtx = app.currentContext; assert(beforeCtx); assert(beforeCtx !== itCtxList['foo']); assert(itCtxList['foo'] !== itCtxList['bar']); assert(afterCtx === beforeCtx); assert(beforeEachCtxList['foo'] === afterEachCtxList['foo']); assert(beforeEachCtxList['foo'] === itCtxList['foo']); }); describe('foo', () => { beforeEach(() => { beforeEachCtxList['foo'] = app.currentContext; }); it('should work', () => { itCtxList['foo'] = app.currentContext; }); afterEach(() => { afterEachCtxList['foo'] = app.currentContext; }); }); describe('bar', () => { beforeEach(() => { beforeEachCtxList['bar'] = app.currentContext; }); it('should work', () => { itCtxList['bar'] = app.currentContext; }); afterEach(() => { afterEachCtxList['bar'] = app.currentContext; }); }); describe('multi it', () => { const itCtxList: Array = []; it('should work 1', () => { itCtxList.push(app.currentContext); }); it('should work 2', () => { itCtxList.push(app.currentContext); }); after(() => { assert(itCtxList[0] !== itCtxList[1]); }) }); }); ================================================ FILE: test/fixtures/tegg-app/test/multi_mock_context.test.ts ================================================ import assert from 'assert'; import { app } from '../../../../bootstrap'; describe('test/multi_mock_context.test.ts', () => { describe('mockContext', () => { it('should only reused once', async () => { const currentContext = app.currentContext; const ctx1 = app.mockContext(); const ctx2 = app.mockContext(); assert.strictEqual(currentContext, ctx1); assert.notStrictEqual(ctx2, ctx1); }); }); }); ================================================ FILE: test/fixtures/tegg-app/test/tegg.test.ts ================================================ import assert from 'assert'; import { app } from '../../../../bootstrap'; import { LogService } from '../app/modules/foo/LogService'; describe('test/tegg.test.ts', () => { describe('async function', () => { it('should work', async () => { const logService = await app.getEggObject(LogService); assert(logService.getTracerId()); }); }); describe('callback function', () => { it('should work', (done) => { app.mockModuleContextScope(async () => { const logService = await app.getEggObject(LogService); assert(logService.getTracerId()); done(); }); }); }); }); ================================================ FILE: test/fixtures/tegg-app/test/tegg_context.test.ts ================================================ import assert from 'assert'; import { Context } from 'egg'; import { app, mm } from '../../../../bootstrap'; import { LogService } from '../app/modules/foo/LogService'; describe('test/tegg_context.test.ts', () => { let ctx: Context; let logService: LogService; before(async () => { logService = await app.getEggObject(LogService); }); describe('mock ctx property', () => { it('should mock ctx work', async () => { ctx = await app.mockModuleContext(); mm(ctx.tracer, 'traceId', 'mockTraceId'); const traceId = logService.getTracerId(); assert.strictEqual(traceId, 'mockTraceId'); }); }); describe.skip('mockModuleContextWithData', () => { let ctx: Context; beforeEach(async () => { ctx = await app.mockModuleContext({ tracer: { traceId: 'mock_with_data', }, headers: { 'user-agent': 'mock_agent', }, }); assert.strictEqual(ctx.tracer.traceId, 'mock_with_data'); assert.strictEqual(ctx.get('user-agent'), 'mock_agent'); }); it('should mock ctx work', () => { const traceId = logService.getTracerId(); assert.strictEqual(traceId, 'mock_with_data'); }); }); describe.skip('mockModuleContextWithHeaders', () => { beforeEach(async () => { await app.mockModuleContext({ headers: { 'user-agent': 'mock_agent', }, }); }); it('should mock ctx work', () => { assert.strictEqual(app.currentContext.get('user-agent'), 'mock_agent'); }); }); }); ================================================ FILE: test/fixtures/tegg-app/tsconfig.json ================================================ { "extends": "@eggjs/tsconfig" } ================================================ FILE: test/fixtures/tegg-app/typing.ts ================================================ import "egg"; import "@eggjs/tegg-plugin"; ================================================ FILE: test/fixtures/test-case-create-context-failed/config/config.default.js ================================================ 'use strict'; module.exports = { keys: '123', }; ================================================ FILE: test/fixtures/test-case-create-context-failed/package.json ================================================ { "name": "app" } ================================================ FILE: test/fixtures/test-case-create-context-failed/test/index.test.js ================================================ const { setGetAppCallback } = require('../../../..'); setGetAppCallback((suite, test) => { return { ready: async () => { // ... }, mockContextScope: async scope => { if (!test) { await scope({}); } else { throw new Error('mock create context failed'); } }, backgroundTasksFinished: async () => { // ... }, close: async () => { // ... }, }; }); describe('test case create context error', function() { it('should not print', () => { }); }); ================================================ FILE: test/fixtures/test-case-get-app-failed/config/config.default.js ================================================ 'use strict'; module.exports = { keys: '123', }; ================================================ FILE: test/fixtures/test-case-get-app-failed/package.json ================================================ { "name": "app" } ================================================ FILE: test/fixtures/test-case-get-app-failed/test/index.test.js ================================================ const { setGetAppCallback } = require('../../../..'); setGetAppCallback((suite, test) => { if (test) { throw new Error('mock get app failed'); } return { ready: async () => { // ... }, mockContextScope: async scope => { await scope({}); }, backgroundTasksFinished: async () => { // ... }, close: async () => { // ... }, }; }); describe('test case get app error', () => { it('should not print', () => { }); }); ================================================ FILE: test/fixtures/yadan_app/config/config.default.js ================================================ 'use strict'; exports.keys = '123'; ================================================ FILE: test/fixtures/yadan_app/package.json ================================================ { "name": "custom", "egg": { "framework": "yadan" } } ================================================ FILE: test/format_options.test.js ================================================ 'use strict'; const path = require('path'); const assert = require('assert'); const mm = require('..'); const formatOptions = require('../lib/format_options'); describe('test/format_options.test.js', () => { afterEach(mm.restore); it('should return the default options', () => { const options = formatOptions(); assert(options); assert(options.coverage === true); assert(options.cache === true); assert(options.baseDir === process.cwd()); assert.deepEqual(options.plugins['egg-mock'], { enable: true, path: path.join(__dirname, '..'), }); }); it('should return baseDir when on windows', () => { const baseDir = 'D:\\projectWorkSpace\\summer'; mm(path, 'isAbsolute', path.win32.isAbsolute); mm(process, 'cwd', () => baseDir); try { formatOptions(); throw new Error('should not run'); } catch (err) { assert(/D:[\\|\/]projectWorkSpace[\\|\/]summer/.test(err.message)); } }); it('should set cache', () => { const options = formatOptions({ cache: false }); assert(options); assert(options.cache === false); }); it('should disable cache when call mm.env', () => { mm.env('prod'); const options = formatOptions(); assert(options); assert(options.cache === false); }); it('should set coverage', () => { const options = formatOptions({ coverage: false }); assert(options); assert(options.coverage === false); }); it('should return options when set full baseDir', () => { const baseDir = path.join(__dirname, 'fixtures/app'); const options = formatOptions({ baseDir }); assert(options); assert(options.baseDir === baseDir); }); it('should return options when set short baseDir', () => { const options = formatOptions({ baseDir: 'apps/foo' }); assert(options); assert(options.baseDir === path.join(__dirname, 'fixtures/apps/foo')); }); it('should return options when set customEgg', () => { const customEgg = path.join(__dirname, 'fixtures/bar'); const options = formatOptions({ customEgg }); assert(options); assert(options.customEgg === customEgg); }); it('should return options when set customEgg=true', () => { const baseDir = path.join(__dirname, 'fixtures/bar'); mm(process, 'cwd', () => { return baseDir; }); const options = formatOptions({ customEgg: true }); assert(options); assert(options.customEgg === baseDir); }); it('should push plugins when in plugin dir', () => { const baseDir = path.join(__dirname, 'fixtures/plugin'); mm(process, 'cwd', () => { return baseDir; }); const options = formatOptions(); assert(options); assert.deepEqual(options.plugins.plugin1, { enable: true, path: baseDir, }); }); it('should not push pluings when in plugin dir but options.plugin = false', () => { const baseDir = path.join(__dirname, 'fixtures/plugin'); mm(process, 'cwd', () => { return baseDir; }); const options = formatOptions({ plugin: false, }); assert(options); assert(!options.plugins.plugin1); }); it('should not throw when no eggPlugin', () => { const baseDir = path.join(__dirname, 'fixtures/plugin_throw'); mm(process, 'cwd', () => { return baseDir; }); formatOptions(); }); it('should throw when no eggPlugin and options.plugin === true', () => { const baseDir = path.join(__dirname, 'fixtures/plugin_throw'); mm(process, 'cwd', () => { return baseDir; }); assert.throws(() => { formatOptions({ plugin: true, }); }, new RegExp(`should set eggPlugin in ${baseDir}/package.json`)); }); it('should mock process.env.HOME when EGG_SERVER_ENV is default, test, prod', () => { const baseDir = process.cwd(); mm(process.env, 'EGG_SERVER_ENV', 'local'); assert.notEqual(process.env.HOME, baseDir); mm(process.env, 'EGG_SERVER_ENV', 'default'); formatOptions(); assert.equal(process.env.HOME, baseDir); mm(process.env, 'EGG_SERVER_ENV', 'test'); formatOptions(); assert.equal(process.env.HOME, baseDir); mm(process.env, 'EGG_SERVER_ENV', 'prod'); formatOptions(); assert.equal(process.env.HOME, baseDir); }); it('should not mock process.env.HOME when it has mocked', () => { const baseDir = process.cwd(); mm(process.env, 'HOME', '/mockpath'); mm(process.env, 'EGG_SERVER_ENV', 'default'); formatOptions(); assert.notEqual(process.env.HOME, baseDir); }); }); ================================================ FILE: test/index.test.ts ================================================ import mm from '../index'; import * as path from 'path'; const fixtures = path.join(__dirname, 'fixtures'); const app = mm.app({ baseDir: path.join(fixtures, 'demo'), }); app.ready().then(() => { app.httpRequest() app.mockContext({ user: { name: 'Jason' } }); app.mockCookies({ foo: 'bar' }); app.mockSession({ foo: 'bar' }); app.mockContext(); app.mockService('foo', 'get', function* (ctx, methodName, args) { return 'popomore'; }); app.mockServiceError('foo', 'get', new Error('mock error')); app.mockCsrf(); app.mockHttpclient('https://eggjs.org', 'mock egg'); app.mockHttpclient('https://eggjs.org', { data: 'mock egg', status: 200, }); app.mockHttpclient('https://eggjs.org', [ 'get' , 'head' ], { data: 'foo' }); app.mockHttpclient('https://eggjs.org', '*', { data: 'bar' }); app.mockHttpclient('https://eggjs.org', () => ({ data: 'mock egg', status: 200, })); app.mockHttpclient('https://eggjs.org', function (url, opts) { url.toLowerCase(); opts.data; return 'a'; }); app.httpRequest().post('/user').set('a', 'b').send().expect(200); console.log(' ts run ok'); app.close(); mm.restore(); process.exit(0); }).catch(e => { console.log(e); process.exit(1); }); mm.consoleLevel('INFO'); mm.home('a') // mm.cluster(); mm.env('test'); // test for bootstrap (global as any).before = () => {}; (global as any).afterEach = () => {}; import { mock, app as bootApp, assert } from '../bootstrap'; bootApp.ready; mock.restore; assert(true); mock.app; ================================================ FILE: test/inject_ctx.test.js ================================================ const coffee = require('coffee'); const path = require('path'); describe('test/inject_ctx.test.js', () => { const eggBinFile = require.resolve('egg-bin//dist/bin/cli'); it('should inject ctx to runner', async () => { const fixture = path.join(__dirname, 'fixtures/tegg-app'); await coffee.fork(eggBinFile, [ 'test', '-r', require.resolve('../register'), ], { cwd: fixture, env: { EGG_FRAMEWORK: require.resolve('egg'), }, }) // .debug() .expect('code', 0) .expect('stdout', /\d+ passing/) .end(); }); it('should inject ctx to runner with setGetAppCallback', async () => { const fixture = path.join(__dirname, 'fixtures/setup-app'); await coffee.fork(eggBinFile, [ 'test', '-r', require.resolve('../register'), ], { cwd: fixture, }) // .debug() .expect('code', 0) // .expect('stdout', /9 passing/) .end(); }); it('hook/case error should failed', async () => { const fixture = path.join(__dirname, 'fixtures/failed-app'); await coffee.fork(eggBinFile, [ 'test', '-r', require.resolve('../register'), ], { cwd: fixture, env: { EGG_FRAMEWORK: require.resolve('egg'), }, }) // .debug() .expect('stdout', /after error test case should print/) .expect('stdout', /afterEach error test case should print/) .expect('stdout', /"before all" hook for "should not print"/) .expect('stdout', /"after all" hook for "should print"/) .expect('stdout', /"before each" hook for "should not print"/) .expect('stdout', /"after each" hook for "should print"/) .expect('stdout', /3 passing/) // 1 after + 1 afterEach + 1 before + 1 beforeEach + 1 test case .expect('stdout', /5 failing/) .expect('code', 1) .end(); }); describe('run suite', () => { // test/inject_ctx.test.js // run suite // 1) "before all" hook: beforeAll in "{root}" // 2) "after all" hook: afterAll in "{root}" // 0 passing (7ms) // 2 failing // 1) "before all" hook: beforeAll in "{root}": // Error: mock get app failed // 2) "after all" hook: afterAll in "{root}": // Error: mock get app failed it('get app error should failed', async () => { const fixture = path.join(__dirname, 'fixtures/get-app-failed'); await coffee.fork(eggBinFile, [ 'test', '-r', require.resolve('../register'), ], { cwd: fixture, env: { EGG_FRAMEWORK: require.resolve('egg'), }, }) // .debug() .expect('code', 1) .expect('stdout', /"before all" hook: beforeAll in "{root}"/) .end(); }); // test/inject_ctx.test.js // run suite // 1) "before all" hook: egg-mock-mock-ctx-failed in "{root}" // 0 passing (4ms) // 1 failing // 1) "before all" hook: egg-mock-mock-ctx-failed in "{root}": // Error: mock create context failed it('create context error should failed', async () => { const fixture = path.join(__dirname, 'fixtures/create-context-failed'); await coffee.fork(eggBinFile, [ 'test', '-r', require.resolve('../register'), ], { cwd: fixture, env: { EGG_FRAMEWORK: require.resolve('egg'), }, }) // .debug() .expect('code', 1) .expect('stdout', /Error: mock create context failed/) .end(); }); // 1) "before all" hook: beforeAll in "{root}" // 2) "after all" hook: afterAll in "{root}" // 0 passing (432ms) // 2 failing // 1) "before all" hook: beforeAll in "{root}": // Error: mock app ready failed // 2) "after all" hook: afterAll in "{root}": // Error: mock app ready failed it('app.ready error should failed', async () => { const fixture = path.join(__dirname, 'fixtures/app-ready-failed'); await coffee.fork(eggBinFile, [ 'test', '-r', require.resolve('../register'), ], { cwd: fixture, env: { EGG_FRAMEWORK: require.resolve('egg'), }, }) // .debug() .expect('code', 1) .expect('stdout', /mock app ready failed/) .end(); }); }); describe('run test', () => { // test case get app error // 1) should not print // 0 passing (5ms) // 1 failing // 1) test case get app error // should not print: // Error: mock get app failed it('get app error should failed', async () => { const fixture = path.join(__dirname, 'fixtures/test-case-get-app-failed'); await coffee.fork(eggBinFile, [ 'test', '-r', require.resolve('../register'), ], { cwd: fixture, env: { EGG_FRAMEWORK: require.resolve('egg'), }, }) // .debug() .expect('code', 1) .expect('stdout', /Error: mock get app failed/) .end(); }); // test case create context error // 1) should not print // 0 passing (7ms) // 1 failing // 1) test case create context error // should not print: // Error: mock create context failed // at Object.mockContextScope (test/index.test.js:12:15) // at next (/Users/killa/workspace/egg-mock/lib/inject_context.js:107:30) it('create context error should failed', async () => { const fixture = path.join(__dirname, 'fixtures/test-case-create-context-failed'); await coffee.fork(eggBinFile, [ 'test', '-r', require.resolve('../register'), ], { cwd: fixture, env: { EGG_FRAMEWORK: require.resolve('egg'), }, }) // .debug() .expect('code', 1) .expect('stdout', /Error: mock create context failed/) .end(); }); }); }); ================================================ FILE: test/mm.test.js ================================================ 'use strict'; const path = require('path'); const fs = require('fs'); const assert = require('assert'); const mm = require('..'); const baseDir = path.join(__dirname, 'fixtures/apps/env-app'); describe('test/mm.test.js', () => { afterEach(mm.restore); describe('mm.env()', () => { let app; beforeEach(() => { mm(process.env, 'EGG_HOME', baseDir); }); afterEach(() => app.close()); it('should mock unittest', async () => { app = mm.app({ baseDir: 'apps/env-app', cache: false }); await app.ready(); assert(app.config.fakeplugin.foo === 'bar-unittest'); assert(app.config.logger.dir === path.join(baseDir, 'logs/env-app')); }); it('should mock test', async () => { mm.env('test'); app = mm.app({ baseDir: 'apps/env-app', cache: false }); await app.ready(); assert(app.config.fakeplugin.foo === 'bar-test'); assert(app.config.logger.dir === path.join(baseDir, 'logs/env-app')); }); it('should mock prod', async () => { mm.env('prod'); app = mm.app({ baseDir: 'apps/env-app', cache: false }); await app.ready(); assert(app.config.fakeplugin.foo === 'bar-prod'); assert(app.config.logger.dir === path.join(baseDir, 'logs/env-app')); }); it('should mock default', async () => { mm.env('default'); app = mm.app({ baseDir: 'apps/env-app', cache: false }); await app.ready(); assert(app.config.fakeplugin.foo === 'bar-default'); assert(app.config.logger.dir === path.join(baseDir, 'logs/env-app')); }); it('should mock unittest', async () => { mm.env('unittest'); app = mm.app({ baseDir: 'apps/env-app', cache: false }); await app.ready(); assert(app.config.fakeplugin.foo === 'bar-unittest'); assert(app.config.logger.dir === path.join(baseDir, 'logs/env-app')); }); it('should mock local', async () => { mm.env('local'); app = mm.app({ baseDir: 'apps/env-app', cache: false }); await app.ready(); assert(app.config.fakeplugin.foo === 'bar-default'); assert(app.config.logger.dir === path.join(baseDir, 'logs/env-app')); }); }); describe('mm.app({ clean: false })', () => { let app; after(() => app.close()); it('keep log dir', async () => { app = mm.app({ baseDir: 'apps/app-not-clean', clean: false }); await app.ready(); assert(fs.existsSync(path.join(__dirname, 'fixtures/apps/app-not-clean/logs/keep'))); }); }); describe('mm.consoleLevel()', () => { it('shoud mock EGG_LOG', () => { mm.consoleLevel('none'); assert(process.env.EGG_LOG === 'NONE'); }); it('shoud not mock', () => { mm.consoleLevel(''); assert(!process.env.EGG_LOG); }); }); describe('mm.home', () => { let app; const baseDir = path.join(__dirname, 'fixtures/apps/mockhome'); before(() => { mm.home(baseDir); app = mm.app({ baseDir: 'apps/mockhome', clean: false }); return app.ready(); }); after(() => app.close()); it('should mock home', () => { assert(app.config.HOME === baseDir); }); it('should ignore when parameter is empty', () => { mm.home(); assert(!process.env.EGG_HOME); }); }); describe('egg-mock', () => { let app; before(() => { app = mm.app({ baseDir: 'apps/no-framework', }); return app.ready(); }); after(() => app.close()); it('should not be a framework', () => { app.mockEnv(); assert(app.config.env === 'mocked by plugin'); }); }); }); ================================================ FILE: test/mock_agent_httpclient.test.js ================================================ 'use strict'; const pedding = require('pedding'); const path = require('path'); const assert = require('assert'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); const url = 'http://127.0.0.1:9989/mock_url'; describe('test/mock_agent_httpclient.test.js', () => { let app; let agent; let httpclient; before(() => { app = mm.app({ baseDir: path.join(fixtures, 'demo'), }); return app.ready(); }); before(() => { agent = app.agent; httpclient = crtHttpclient(agent); }); after(() => app.agent.close()); afterEach(mm.restore); it('should mock url and get reponse event on urllib', done => { done = pedding(3, done); agent.mockHttpclient(url, { data: Buffer.from('mock response'), }); agent.httpclient.once('request', function(meta) { assert('url' in meta); assert('args' in meta); done(); }); agent.httpclient.once('response', function(result) { assert('url' in result.req); assert('size' in result.req); assert('options' in result.req); assert.deepEqual(result.res, { status: 200, statusCode: 200, headers: {}, size: 13, aborted: false, rt: 1, keepAliveSocket: false, }); done(); }); let count = 0; agent.httpclient.on('response', function(result) { if (count === 0) { assert.deepEqual(result.req.options, { dataType: undefined, method: 'GET', headers: {}, }); } else if (count === 1) { assert.deepEqual(result.req.options, { dataType: undefined, method: 'POST', headers: { 'x-custom': 'custom', }, }); } count++; }); httpclient() .then(data => { assert.deepEqual(data, { get: 'mock response', post: 'mock response', }); done(); }); }); it('should mock url support multi method', done => { done = pedding(2, done); agent.mockHttpclient(url, [ 'get', 'post' ], { data: Buffer.from('mock response'), }); agent.httpclient.once('response', function(result) { assert.deepEqual(result.res, { status: 200, statusCode: 200, headers: {}, size: 13, aborted: false, rt: 1, keepAliveSocket: false, }); done(); }); httpclient() .then(data => { assert.deepEqual(data, { get: 'mock response', post: 'mock response', }); done(); }); }); it('should mock url method support *', done => { done = pedding(2, done); agent.mockHttpclient(url, '*', { data: Buffer.from('mock response'), }); agent.httpclient.once('response', function(result) { assert.deepEqual(result.res, { status: 200, statusCode: 200, headers: {}, size: 13, aborted: false, rt: 1, keepAliveSocket: false, }); done(); }); httpclient() .then(data => { assert.deepEqual(data, { get: 'mock response', post: 'mock response', }); done(); }); }); it('should mock url get and post', done => { agent.mockHttpclient(url, { data: 'mock url get', }); agent.mockHttpclient(url, 'post', { data: 'mock url post', }); httpclient() .then(data => { assert.deepEqual(data, { get: 'mock url get', post: 'mock url post', }); done(); }); }); it('should support request', done => { agent.mockHttpclient(url, { data: 'mock url get', }); agent.mockHttpclient(url, 'post', { data: 'mock url post', }); httpclient('request') .then(data => { assert.deepEqual(data, { get: 'mock url get', post: 'mock url post', }); done(); }); }); it('should support curl', done => { agent.mockHttpclient(url, { data: 'mock url get', }); agent.mockHttpclient(url, 'post', { data: 'mock url post', }); httpclient('curl') .then(data => { assert.deepEqual(data, { get: 'mock url get', post: 'mock url post', }); done(); }); }); it('should support json', done => { agent.mockHttpclient(url, { data: { method: 'get' }, }); agent.mockHttpclient(url, 'post', { data: { method: 'post' }, }); httpclient('request', 'json') .then(data => { assert.deepEqual(data, { get: { method: 'get' }, post: { method: 'post' }, }); done(); }); }); it('should support text', done => { agent.mockHttpclient(url, { data: 'mock url get', }); agent.mockHttpclient(url, 'post', { data: 'mock url post', }); httpclient('request', 'text') .then(data => { assert.deepEqual(data, { get: 'mock url get', post: 'mock url post', }); done(); }); }); it('should mock url and get reponse event on urllib', done => { agent.mockHttpclient(url, { data: Buffer.from('mock response'), }); httpclient() .then(data => { assert.deepEqual(data, { get: 'mock response', post: 'mock response', }); done(); }); }); }); function crtHttpclient(app) { return (method = 'request', dataType) => { const r1 = app.httpclient[method](url, { dataType, }); const r2 = app.httpclient[method](url, { method: 'POST', dataType, headers: { 'x-custom': 'custom', }, }); return Promise.all([ r1, r2 ]).then(([ r1, r2 ]) => { return { get: Buffer.isBuffer(r1.data) ? r1.data.toString() : r1.data, post: Buffer.isBuffer(r2.data) ? r2.data.toString() : r2.data, }; }); }; } ================================================ FILE: test/mock_cluster_extend.test.js ================================================ 'use strict'; const assert = require('assert'); const mm = require('..'); describe('test/mock_cluster_extend.test.js', () => { let app; before(() => { app = mm.cluster({ baseDir: 'demo', coverage: false, }); return app.ready(); }); after(() => app.close()); afterEach(mm.restore); it('should mock cluster with result', function* () { let result = yield app.mockDevice({ name: 'egg' }); assert.deepEqual(result, { name: 'egg', mock: true }); result = yield app.mockGenerator({ name: 'egg generator' }); assert.deepEqual(result, { name: 'egg generator', mock: true }); result = yield app.mockPromise({ name: 'egg promise' }); assert.deepEqual(result, { name: 'egg promise', mock: true }); }); }); ================================================ FILE: test/mock_cluster_restore.test.js ================================================ 'use strict'; const assert = require('assert'); const fs = require('fs'); const path = require('path'); const { sleep } = require('../lib/utils'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); describe('test/mock_cluster_restore.test.js', () => { let app; before(() => { app = mm.cluster({ baseDir: 'demo', coverage: false, }); return app.ready(); }); after(() => app.close()); afterEach(mm.restore); it('should mock cluster restore work', function* () { app.mockSession({ user: { foo: 'bar', }, hello: 'egg mock session data', }); yield app.httpRequest() .get('/session') .expect({ user: { foo: 'bar', }, hello: 'egg mock session data', }); mm.restore(); yield app.httpRequest() .get('/session') .expect({}); }); describe('handle uncaughtException', () => { let app; before(() => { app = mm.cluster({ baseDir: 'apps/app-throw', coverage: false, }); return app.ready(); }); after(() => app.close()); it('should handle uncaughtException and log it', function* () { yield app.httpRequest() .get('/throw') .expect('foo') .expect(200); yield sleep(1100); const logfile = path.join(fixtures, 'apps/app-throw/logs/app-throw/common-error.log'); const body = fs.readFileSync(logfile, 'utf8'); assert(body.includes('ReferenceError: a is not defined (uncaughtException throw')); }); }); }); ================================================ FILE: test/mock_cluster_without_security_plugin.test.js ================================================ 'use strict'; const mm = require('..'); describe('test/mock_cluster_without_security.test.js', () => { let app; before(() => { app = mm.cluster({ baseDir: 'disable-security', coverage: false, }); return app.ready(); }); after(() => app.close()); afterEach(mm.restore); it('should mock cluster work', () => { app.mockSession({ user: { foo: 'bar', }, hello: 'egg mock session data', }); return app.httpRequest() .get('/session') .expect({ user: { foo: 'bar', }, hello: 'egg mock session data', }); }); }); ================================================ FILE: test/mock_context.test.js ================================================ const assert = require('assert'); const mm = require('..'); describe('test/mock_context.test.js', () => { let app; before(done => { app = mm.app({ baseDir: 'demo', }); app.ready(done); }); after(() => app.close()); afterEach(mm.restore); it('should work on GET with user login', () => { app.mockContext({ user: { foo: 'bar', }, tracer: { traceId: 'foo-traceId', }, }); // assert(ctx.user.foo === 'bar'); return app.httpRequest() .get('/user') .expect(200) .expect({ foo: 'bar', }) .expect('x-request-url', '/user'); }); it('should work on POST with user login', () => { app.mockContext({ user: { foo: 'bar', }, }); app.mockCsrf(); return app.httpRequest() .post('/user') .send({ hi: 'body', a: 123, }) .expect(200) .expect({ params: { hi: 'body', a: 123, }, user: { foo: 'bar', }, }); }); it.skip('should work on POST file with user login', async () => { const ctx = app.mockContext({ user: { foo: 'bar', }, traceId: `trace-${Date.now}`, }); // assert(ctx.user.foo === 'bar'); const ctxFromStorage = app.ctxStorage.getStore(); assert(ctxFromStorage === ctx); assert(app.currentContext === ctx); app.mockCsrf(); await app.httpRequest() .post('/file') .field('title', 'file title') .attach('file', __filename) .expect(200) .expect({ fields: { title: 'file title', }, filename: 'mock_context.test.js', user: { foo: 'bar', }, traceId: ctx.traceId, ctxFromStorageUser: ctxFromStorage.user, ctxFromStorageTraceId: ctxFromStorage.traceId, }); const ctxFromStorage2 = app.ctxStorage.getStore(); assert(ctxFromStorage2 === ctx); mm.restore(); const ctxFromStorage3 = app.ctxStorage.getStore(); assert(!ctxFromStorage3); assert(!app.currentContext); }); }); ================================================ FILE: test/mock_cookies.test.js ================================================ 'use strict'; const request = require('supertest'); const path = require('path'); const assert = require('assert'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); describe('test/mock_cookies.test.js', () => { let app; before(done => { app = mm.app({ baseDir: path.join(fixtures, 'apps/mock_cookies'), }); app.ready(done); }); after(() => app.close()); afterEach(mm.restore); it('should not return when don\'t mock cookies', done => { const ctx = app.mockContext(); assert(!ctx.cookies.get('foo')); request(app.callback()) .get('/') .expect(function(res) { assert.deepEqual(res.body, {}); }) .expect(200, done); }); it('should mock cookies', done => { app.mockCookies({ foo: 'bar cookie', }); // const ctx = app.mockContext(); // assert(ctx.cookies.get('foo') === 'bar cookie'); request(app.callback()) .get('/') .expect({ cookieValue: 'bar cookie', cookiesValue: 'bar cookie', }) .expect(200, done); }); it('should pass cookie opt', done => { app.mockCookies({}); request(app.callback()) .get('/') .set('cookie', 'foo=bar cookie') .expect({ cookieValue: 'bar cookie', cookiesValue: 'bar cookie', }) .expect(200, done); }); }); ================================================ FILE: test/mock_csrf.test.js ================================================ 'use strict'; const path = require('path'); const request = require('supertest'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); describe('test/mock_csrf.test.js', () => { let app; before(() => { app = mm.app({ baseDir: path.join(fixtures, 'demo'), }); return app.ready(); }); after(() => app.close()); afterEach(mm.restore); it('should work', done => { app.mockCsrf(); request(app.callback()) .post('/') .expect(200) .expect('done', done); }); }); ================================================ FILE: test/mock_custom_loader.test.js ================================================ 'use strict'; const path = require('path'); const assert = require('assert'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); describe('test/mock_custom_loader.test.js', () => { let app; before(async () => { app = mm.app({ baseDir: path.join(fixtures, 'custom-loader'), }); await app.ready(); }); after(() => app.close()); afterEach(mm.restore); it('should return success', async () => { await app.httpRequest() .get('/users/popomore') .expect({ adapter: 'docker', repository: 'popomore', }) .expect(200); }); it('should return when mock with data', async () => { app.mockRepository('user', 'get', 'mock'); app.mockAdapter('docker', 'inspectDocker', 'mock'); await app.httpRequest() .get('/users/popomore') .expect({ adapter: 'mock', repository: 'mock', }) .expect(200); }); it('should return when mock the instance', async () => { app.mockAdapter(app.adapter.docker, 'inspectDocker', 'mock'); await app.httpRequest() .get('/users/popomore') .expect({ adapter: 'mock', repository: 'popomore', }) .expect(200); }); it('should not override the existing API', () => { assert(app.mockEnv === require('../app/extend/application.js').mockEnv); }); }); ================================================ FILE: test/mock_env.test.js ================================================ const path = require('node:path'); const { strict: assert } = require('node:assert'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); describe('test/mock_env.test.js', () => { let app; before(() => { app = mm.app({ baseDir: path.join(fixtures, 'demo'), }); return app.ready(); }); after(() => app.close()); afterEach(mm.restore); it('should mock env success', () => { app.mockEnv('prod'); assert.equal(app.config.env, 'prod'); assert.equal(app.config.serverEnv, 'prod'); }); it('should keep mm.restore execute in the current event loop', async () => { mm.restore(); assert.equal(app.config.env, 'unittest'); assert.equal(app.config.serverEnv, undefined); app.mockEnv('prod'); assert.equal(app.config.env, 'prod'); assert.equal(app.config.serverEnv, 'prod'); await sleep(1); assert.equal(app.config.env, 'prod'); assert.equal(app.config.serverEnv, 'prod'); mm.restore(); assert.equal(app.config.env, 'unittest'); assert.equal(app.config.serverEnv, undefined); }); }); function sleep(ms) { return new Promise(resolve => { setTimeout(resolve, ms); }); } ================================================ FILE: test/mock_headers.test.js ================================================ 'use strict'; const request = require('supertest'); const path = require('path'); const assert = require('assert'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); describe('test/mock_headers.test.js', () => { afterEach(mm.restore); let app; before(() => { app = mm.app({ baseDir: path.join(fixtures, 'demo'), }); return app.ready(); }); after(() => app.close()); afterEach(mm.restore); it('should not exists without mock', done => { app.mockContext(); request(app.callback()) .get('/header') .expect(function(res) { assert(res.body.header === ''); }) .expect(200, done); }); it('should mock headers', done => { app.mockContext(); app.mockHeaders({ customheader: 'customheader', }); request(app.callback()) .get('/header') .expect(function(res) { assert(res.body.header === 'customheader'); }) .expect(200, done); }); it('should mock headers that is uppercase', done => { app.mockContext(); app.mockHeaders({ Customheader: 'customheader', }); request(app.callback()) .get('/header') .expect(function(res) { assert(res.body.header === 'customheader'); }) .expect(200, done); }); }); ================================================ FILE: test/mock_httpclient.test.js ================================================ 'use strict'; const pedding = require('pedding'); const path = require('path'); const request = require('supertest'); const assert = require('assert'); const { sleep } = require('../lib/utils'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); describe('test/mock_httpclient.test.js', () => { let app; let server; let url; before(() => { app = mm.app({ baseDir: path.join(fixtures, 'demo'), }); return app.ready(); }); before(() => { server = app.listen(); url = `http://127.0.0.1:${server.address().port}/mock_url`; }); after(() => app.close()); afterEach(mm.restore); it('should mock url and get reponse event on urllib', done => { done = pedding(2, done); app.mockCsrf(); app.mockHttpclient(url, { data: Buffer.from('mock response'), }); request(server) .get('/urllib') .expect({ get: 'mock response', post: 'mock response', }) .expect(200, done); app.httpclient.once('response', function(result) { assert('url' in result.req); assert('size' in result.req); assert('options' in result.req); assert.deepEqual(result.res, { status: 200, statusCode: 200, headers: {}, size: 13, aborted: false, rt: 1, keepAliveSocket: false, }); done(); }); let count = 0; app.httpclient.on('response', function(result) { if (count === 0) { assert.deepEqual(result.req.options, { data: undefined, dataType: undefined, method: 'GET', headers: {}, }); } else if (count === 1) { assert.deepEqual(result.req.options, { data: undefined, dataType: undefined, method: 'POST', headers: { 'x-custom': 'custom', }, }); } count++; }); }); it('should mock url support multi method', done => { done = pedding(2, done); app.mockCsrf(); app.mockHttpclient(url, [ 'get', 'post' ], { data: Buffer.from('mock response'), }); request(server) .get('/urllib') .expect({ get: 'mock response', post: 'mock response', }) .expect(200, done); app.httpclient.once('response', function(result) { assert.deepEqual(result.res, { status: 200, statusCode: 200, headers: {}, size: 13, aborted: false, rt: 1, keepAliveSocket: false, }); done(); }); }); it('should mock url method support *', done => { done = pedding(2, done); app.mockCsrf(); app.mockHttpclient(url, '*', { data: Buffer.from('mock response'), }); request(server) .get('/urllib') .expect({ get: 'mock response', post: 'mock response', }) .expect(200, done); app.httpclient.once('response', function(result) { assert.deepEqual(result.res, { status: 200, statusCode: 200, headers: {}, size: 13, aborted: false, rt: 1, keepAliveSocket: false, }); done(); }); }); it('should mock url post', done => { app.mockCsrf(); app.mockHttpclient(url, 'post', { data: Buffer.from('mock url post'), }); request(server) .get('/urllib') .expect({ get: 'url get', post: 'mock url post', }) .expect(200, done); }); it('should mock url get and post', done => { app.mockCsrf(); app.mockHttpclient(url, { data: 'mock url get', }); app.mockHttpclient(url, 'post', { data: 'mock url post', }); request(server) .get('/urllib') .expect({ get: 'mock url get', post: 'mock url post', }) .expect(200, done); }); it('should support request', done => { app.mockCsrf(); app.mockHttpclient(url, { data: 'mock url get', }); app.mockHttpclient(url, 'post', { data: 'mock url post', }); request(server) .get('/urllib?method=request') .expect({ get: 'mock url get', post: 'mock url post', }) .expect(200, done); }); it('should support curl', done => { app.mockCsrf(); app.mockHttpclient(url, { data: 'mock url get', }); app.mockHttpclient(url, 'post', { data: 'mock url post', }); request(server) .get('/urllib?method=curl') .expect({ get: 'mock url get', post: 'mock url post', }) .expect(200, done); }); it('should support json', done => { app.mockCsrf(); app.mockHttpclient(url, { data: { method: 'get' }, }); app.mockHttpclient(url, 'post', { data: { method: 'post' }, }); request(server) .get('/urllib?dataType=json') .expect({ get: { method: 'get' }, post: { method: 'post' }, }) .expect(200, done); }); it('should support text', done => { app.mockCsrf(); app.mockHttpclient(url, { data: 'mock url get', }); app.mockHttpclient(url, 'post', { data: 'mock url post', }); request(server) .get('/urllib?dataType=text') .expect({ get: 'mock url get', post: 'mock url post', }) .expect(200, done); }); it('should exits req headers', done => { app.mockCsrf(); app.mockHttpclient(url, { data: 'mock url test', }); request(server) .get('/mock_urllib') .expect({}) .expect(200, done); }); it('should deprecate mockUrllib', done => { app.mockCsrf(); app.mockUrllib(url, { data: 'mock url test', }); request(server) .get('/mock_urllib') .expect({}) .expect(200, done); }); it('should mock url and get reponse event on urllib', done => { app.mockCsrf(); app.mockHttpclient(/\/mock_url$/, { data: Buffer.from('mock response'), }); request(server) .get('/urllib') .expect({ get: 'mock response', post: 'mock response', }) .expect(200, done); }); it('should use copy of mock data', function* () { app.mockCsrf(); app.mockHttpclient(/\/mock_url$/, { data: { a: 1 }, }); yield request(server) .get('/data_type') .expect({ a: 1, }) .expect(200); yield request(server) .get('/data_type') .expect({ a: 1, }) .expect(200); }); it('should support fn', function* () { app.mockCsrf(); app.mockHttpclient(url, 'get', (url, opt) => { return `mock ${url} with ${opt.data.a}`; }); app.mockHttpclient(url, 'post', 'mock url post'); yield request(server) .get('/urllib') .query({ data: JSON.stringify({ a: 'b' }) }) .expect({ get: `mock ${url} with b`, post: 'mock url post', }) .expect(200); }); it('should support async function', function* () { app.mockCsrf(); app.mockHttpclient(url, 'get', async (url, opt) => { await sleep(100); return `mock ${url} with ${opt.data.a}`; }); app.mockHttpclient(url, 'post', 'mock url post'); yield request(server) .get('/urllib') .query({ data: JSON.stringify({ a: 'b' }) }) .expect({ get: `mock ${url} with b`, post: 'mock url post', }) .expect(200); }); it('should mock fn with multi-request without error', function* () { app.mockCsrf(); let i = 0; app.mockHttpclient(url, 'post', () => { i++; return {}; }); yield request(server).get('/urllib').expect(200); yield request(server).get('/urllib').expect(200); yield request(server).get('/urllib').expect(200); assert(i === 3); }); }); ================================================ FILE: test/mock_httpclient_next.test.js ================================================ const pedding = require('pedding'); const path = require('path'); const fs = require('fs'); const request = require('supertest'); const assert = require('assert'); const { sleep } = require('../lib/utils'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); describe('test/mock_httpclient_next.test.js', () => { let app; let server; let url; let url2; before(() => { app = mm.app({ baseDir: path.join(fixtures, 'demo_next'), }); 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 reponse 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); }); // not support in urllib3 it.skip('should support async function', async () => { app.mockCsrf(); app.mockHttpclient(url, 'get', async (url, opt) => { await sleep(100); return `mock ${url} with ${opt.data.a}`; }); app.mockHttpclient(url, 'post', 'mock url post'); await request(server) .get('/urllib') .query({ data: JSON.stringify({ a: 'b' }) }) .expect({ get: `mock ${url} with 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); }); }); ================================================ FILE: test/mock_httpclient_next_h2.test.js ================================================ 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 auto restore after each case', async () => { app.mockCsrf(); await request(server) .get('/urllib') .expect({ get: 'url get', post: '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); }); }); ================================================ FILE: test/mock_request.test.js ================================================ 'use strict'; const assert = require('assert'); const mm = require('..'); describe('test/mock_httpclient.test.js', () => { describe('app mode', () => { let app; before(() => { app = mm.app({ baseDir: 'request', }); return app.ready(); }); after(() => app.close()); afterEach(mm.restore); it('should test app with request', () => { return app.httpRequest() .get('/') .expect(200) .expect('hello world'); }); it('should test app with request on pathFor', () => { return app.httpRequest() .get('home') .expect(200) .expect('hello world'); }); it('should GET session path work', () => { return app.httpRequest() .get('session') .expect(200) .expect('hello session'); }); it('should GET wrong pathFor name throw error', () => { try { app.httpRequest() .get('session-404') .expect(200) .expect('hello world'); throw new Error('should not run this'); } catch (err) { assert(err); assert(err.message === 'Can\'t find router:session-404, please check your \'app/router.js\''); } }); it('should test with expectHeader(header) and unexpectHeader(header)', () => { return app.httpRequest() .get('/') .expect(200) .expect('hello world') .expectHeader('set-cookie') .unexpectHeader('cache-control'); }); it('should test with expectHeader(header) and unexpectHeader(header) throw error', function* () { try { yield app.httpRequest() .get('/') .expect(200) .expect('hello world') .expectHeader('set-cookie1'); } catch (err) { assert(err.message === 'expected "set-cookie1" header field'); } try { yield app.httpRequest() .get('/') .expect(200) .expect('hello world') .unexpectHeader('set-cookie'); } catch (err) { assert(err.message.startsWith('unexpected "set-cookie" header field, got \"')); } }); it('should test with expectHeader(header, done)', done => { app.httpRequest() .get('/') .expectHeader('set-cookie', done); }); it('should test with unexpectHeader(header, done)', done => { app.httpRequest() .get('/') .unexpectHeader('cache-control', done); }); }); describe('cluster mode', () => { let app; before(() => { app = mm.cluster({ baseDir: 'request', }); return app.ready(); }); after(() => app.close()); afterEach(mm.restore); it('should test app with request', () => { return app.httpRequest() .get('/') .expect(200) .expect('hello world'); }); it('should test app with request on pathFor', () => { return app.httpRequest() .get('home') .expect(200) .expect('hello world'); }); it('should GET session path work', () => { return app.httpRequest() .get('session') .expect(200) .expect('hello session'); }); it('should GET wrong pathFor name throw error', () => { try { app.httpRequest() .get('session-404') .expect(200) .expect('hello world'); throw new Error('should not run this'); } catch (err) { assert(err); assert(err.message === 'Can\'t find router:session-404, please check your \'app/router.js\''); } }); it('should test with expectHeader(header) and unexpectHeader(header)', () => { return app.httpRequest() .get('/') .expect(200) .expect('hello world') .expectHeader('set-cookie') .unexpectHeader('cache-control'); }); it('should test with expectHeader(header) and unexpectHeader(header) throw error', function* () { try { yield app.httpRequest() .get('/') .expect(200) .expect('hello world') .expectHeader('set-cookie1'); } catch (err) { assert(err.message === 'expected "set-cookie1" header field'); } try { yield app.httpRequest() .get('/') .expect(200) .expect('hello world') .unexpectHeader('set-cookie'); } catch (err) { assert(err.message.startsWith('unexpected "set-cookie" header field, got \"')); } }); it('should test with expectHeader(header, done)', done => { app.httpRequest() .get('/') .expectHeader('set-cookie', done); }); it('should test with unexpectHeader(header, done)', done => { app.httpRequest() .get('/') .unexpectHeader('cache-control', done); }); }); }); ================================================ FILE: test/mock_service.test.js ================================================ 'use strict'; const path = require('path'); const assert = require('assert'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); describe('test/mock_service.test.js', () => { let app; before(async () => { app = mm.app({ baseDir: path.join(fixtures, 'demo'), }); await app.ready(); }); after(() => app.close()); afterEach(mm.restore); it('should return from service', () => { return app.httpRequest() .get('/service') .expect({ foo1: 'bar', foo2: 'bar', foo3: 'bar', thirdService: 'third', }); }); it('should return from service when mock with data', () => { app.mockService('foo', 'get', 'foo'); app.mockService('foo', 'getSync', 'foo'); app.mockService('bar.foo', 'get', 'foo'); return app.httpRequest() .get('/service') .expect({ foo1: 'foo', foo2: 'foo', foo3: 'foo', thirdService: 'third', }); }); it('mock repeat succeess', () => { app.mockService('foo', 'get', 'foo'); app.mockService('foo', 'get', 'foo'); app.mockService('foo', 'getSync', 'foo'); app.mockService('foo', 'getSync', 'foo'); app.mockService('bar.foo', 'get', 'foo'); app.mockService('bar.foo', 'get', 'foo'); return app.httpRequest() .get('/service') .expect({ foo1: 'foo', foo2: 'foo', foo3: 'foo', thirdService: 'third', }); }); it.skip('mock error succeess', async () => { app.mockService('foo', 'get', new Error('async error')); try { const ctx = app.mockContext(); await ctx.service.foo.get(); throw new Error('should not execute'); } catch (err) { assert(err.message === 'async error'); } }); it.skip('mock sync error succeess', async () => { app.mockService('foo', 'getSync', new Error('sync error')); try { const ctx = app.mockContext(); await ctx.service.foo.getSync(); throw new Error('should not execute'); } catch (err) { assert(err.message === 'sync error'); } }); it('should return from service when mock with normal function', () => { app.mockService('foo', 'get', () => 'foo'); app.mockService('foo', 'getSync', () => 'foo'); app.mockService('bar.foo', 'get', () => 'foo'); return app.httpRequest() .get('/service') .expect({ foo1: 'foo', foo2: 'foo', foo3: 'foo', thirdService: 'third', }); }); it('should support old service format', () => { app.mockService('old', 'test', 'test'); return app.httpRequest() .get('/service/old') .expect('test'); }); it('should throw', () => { assert.throws(() => { app.mockService('foo', 'not_exist', 'foo'); }, /property not_exist in original object must be function/); }); it('should return from service when mock with generator', () => { app.mockService('foo', 'get', async () => { return 'foo'; }); return app.httpRequest() .get('/service') .expect({ foo1: 'foo', foo2: 'bar', foo3: 'bar', thirdService: 'third', }); }); it('should return from service when mock with 3 level', () => { app.mockService('foo', 'get', '1 level service'); app.mockService('bar.foo', 'get', '2 level service'); app.mockService('third.bar.foo', 'get', '3 level service'); return app.httpRequest() .get('/service') .expect({ foo1: '1 level service', foo2: '2 level service', foo3: 'bar', thirdService: '3 level service', }); }); it('should return from service when mock with error', () => { app.mockService('foo', 'get', async () => { throw new Error('mock service foo.get error'); }); return app.httpRequest() .get('/service') .expect(/mock service foo\.get error/) .expect(500); }); describe('app.mockServiceError()', () => { it('should default mock error', () => { app.mockServiceError('foo', 'get'); return app.httpRequest() .get('/service') .expect(/mock get error/) .expect(500); }); it('should create custom mock error with string', () => { app.mockServiceError('foo', 'get', 'mock service foo.get error1'); return app.httpRequest() .get('/service') .expect(/mock service foo\.get error1/) .expect(500); }); it('should return custom mock error', () => { app.mockServiceError('foo', 'get', new Error('mock service foo.get error2')); return app.httpRequest() .get('/service') .expect(/mock service foo\.get error2/) .expect(500); }); }); }); ================================================ FILE: test/mock_service_async.test.js ================================================ 'use strict'; const path = require('path'); const request = require('supertest'); const assert = require('assert'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); describe('test/mock_service_async.test.js', () => { let app; before(async () => { app = mm.app({ baseDir: path.join(fixtures, 'demo-async'), }); await app.ready(); }); after(() => app.close()); afterEach(mm.restore); it('should return from service', done => { request(app.callback()) .get('/service') .expect({ foo1: 'bar', foo2: 'bar', foo3: 'bar', thirdService: 'third', }, done); }); it('should return from service when mock with data', done => { app.mockService('foo', 'get', 'foo'); app.mockService('foo', 'getSync', 'foo'); app.mockService('bar.foo', 'get', 'foo'); request(app.callback()) .get('/service') .expect({ foo1: 'foo', foo2: 'foo', foo3: 'foo', thirdService: 'third', }, done); }); it('should return from service when mock with normal function', done => { app.mockService('foo', 'get', () => 'foo'); app.mockService('foo', 'getSync', () => 'foo'); app.mockService('bar.foo', 'get', () => 'foo'); request(app.callback()) .get('/service') .expect({ foo1: 'foo', foo2: 'foo', foo3: 'foo', thirdService: 'third', }, done); }); it('should throw', () => { assert.throws(() => { app.mockService('foo', 'not_exist', 'foo'); }, /property not_exist in original object must be function/); }); it('should return from service when mock with generator', done => { app.mockService('foo', 'get', async () => { return 'foo'; }); request(app.callback()) .get('/service') .expect({ foo1: 'foo', foo2: 'bar', foo3: 'bar', thirdService: 'third', }, done); }); it('should return from service when mock with 3 level', done => { app.mockService('foo', 'get', '1 level service'); app.mockService('bar.foo', 'get', '2 level service'); app.mockService('third.bar.foo', 'get', '3 level service'); request(app.callback()) .get('/service') .expect({ foo1: '1 level service', foo2: '2 level service', foo3: 'bar', thirdService: '3 level service', }, done); }); it('should return from service when mock with error', done => { app.mockService('foo', 'get', async () => { throw new Error('mock service foo.get error'); }); request(app.callback()) .get('/service') .expect(/mock service foo\.get error/) .expect(500, done); }); describe('app.mockServiceError()', () => { it('should default mock error', done => { app.mockServiceError('foo', 'get'); request(app.callback()) .get('/service') .expect(/mock get error/) .expect(500, done); }); it('should create custom mock error with string', done => { app.mockServiceError('foo', 'get', 'mock service foo.get error1'); request(app.callback()) .get('/service') .expect(/mock service foo\.get error1/) .expect(500, done); }); it('should return custom mock error', done => { app.mockServiceError('foo', 'get', new Error('mock service foo.get error2')); request(app.callback()) .get('/service') .expect(/mock service foo\.get error2/) .expect(500, done); }); }); }); ================================================ FILE: test/mock_service_cluster.test.js ================================================ 'use strict'; const path = require('path'); const assert = require('assert'); const mm = require('..'); const fixtures = path.join(__dirname, 'fixtures'); describe('test/mock_service_cluster.test.js', () => { let app; before(() => { app = mm.cluster({ baseDir: path.join(fixtures, 'demo'), }); return app.ready(); }); after(() => app.close()); afterEach(mm.restore); it('should return from service', () => { return app.httpRequest() .get('/service') .expect({ foo1: 'bar', foo2: 'bar', foo3: 'bar', thirdService: 'third', }); }); it('should return from service when mock with data', () => { app.mockService('foo', 'get', 'foo'); app.mockService('foo', 'getSync', 'foo'); app.mockService('bar.foo', 'get', 'foo'); return app.httpRequest() .get('/service') .expect({ foo1: 'foo', foo2: 'foo', foo3: 'foo', thirdService: 'third', }); }); it('should support old service format', () => { app.mockService('old', 'test', 'test'); return app.httpRequest() .get('/service/old') .expect('test'); }); it('should throw not exists service error', () => { assert.throws(() => { app.mockService('foo', 'not_exist', 'foo'); }, /property not_exist in original object must be function/); }); it('should return from service when mock with generator', () => { app.mockService('foo', 'get', function* () { return 'foo'; }); return app.httpRequest() .get('/service') .expect({ foo1: 'foo', foo2: 'bar', foo3: 'bar', thirdService: 'third', }); }); it('should return from service when mock with 3 level', () => { app.mockService('foo', 'get', '1 level service'); app.mockService('bar.foo', 'get', '2 level service'); app.mockService('third.bar.foo', 'get', '3 level service'); return app.httpRequest() .get('/service') .expect({ foo1: '1 level service', foo2: '2 level service', foo3: 'bar', thirdService: '3 level service', }); }); it('should return from service when mock with error', () => { app.mockService('foo', 'get', function* () { throw new Error('mock service foo.get error'); }); return app.httpRequest() .get('/service') .expect(/mock service foo\.get error/) .expect(500); }); describe('app.mockServiceError()', () => { it('should default mock error', () => { app.mockServiceError('foo', 'get'); return app.httpRequest() .get('/service') .expect(/mock get error/) .expect(500); }); it('should create custom mock error with string', () => { app.mockServiceError('foo', 'get', 'mock service foo.get error1'); return app.httpRequest() .get('/service') .expect(/mock service foo\.get error1/) .expect(500); }); it('should return custom mock error', () => { const err = new Error('mock service foo.get error2'); err.foo = 'bar'; app.mockServiceError('foo', 'get', err); return app.httpRequest() .get('/service') .expect(/mock service foo\.get error2/) .expect(500); }); }); }); ================================================ FILE: test/mock_session.test.js ================================================ 'use strict'; const mm = require('..'); const assert = require('assert'); describe('test/mock_session.test.js', () => { afterEach(mm.restore); describe('single process mode', () => { let app; before(() => { app = mm.app({ baseDir: 'demo', }); return app.ready(); }); after(() => app.close()); it('should mock session', () => { const obj = { user: { foo: 'bar', }, hello: 'egg mock session data', }; // const ctx = app.mockContext(); app.mockSession(obj); // assert.deepEqual(ctx.session, obj); return app.httpRequest() .get('/session') .expect({ user: { foo: 'bar', }, hello: 'egg mock session data', }); }); it.skip('should support mock session with plain type', () => { const ctx = app.mockContext(); app.mockSession(); app.mockSession('123'); assert(ctx.session === '123'); assert(!ctx.session.save); }); it('should mock restore', () => { return app.httpRequest() .get('/session') .expect({}); }); }); describe('cluster process mode', () => { let app; before(() => { app = mm.cluster({ baseDir: 'demo', }); return app.ready(); }); after(() => app.close()); it('should mock session', () => { app.mockSession({ user: { foo: 'bar', }, hello: 'egg mock session data', }); return app.httpRequest() .get('/session') .expect({ user: { foo: 'bar', }, hello: 'egg mock session data', }); }); it('should mock restore', () => { return app.httpRequest() .get('/session') .expect({}); }); }); }); ================================================ FILE: test/parallel.test.js ================================================ const path = require('path'); const agentRegister = require('../register'); describe('test/parallel.test.js', () => { before(async () => { await agentRegister.mochaGlobalSetup(); process.env.ENABLE_MOCHA_PARAELLEL = 'true'; process.env.AUTO_AGENT = 'true'; process.env.EGG_BASE_DIR = path.join(__dirname, './fixtures/apps/foo'); }); after(async () => { await agentRegister.mochaGlobalTeardown(); delete process.env.ENABLE_MOCHA_PARAELLEL; delete process.env.AUTO_AGENT; delete process.env.EGG_BASE_DIR; }); it('should work', async () => { const { app } = require('../bootstrap'); await app.ready(); await app.httpRequest() .get('/') .expect(200) .expect('foo'); await app.close(); }); }); ================================================ FILE: test/parallel_hook.test.js ================================================ 'use strict'; const path = require('path'); const coffee = require('coffee'); const mock = require('mm'); describe('test/bootstrap-plugin.test.js', () => { after(() => mock.restore()); it('should throw', async () => { return coffee.fork(require.resolve('mocha/bin/mocha'), [ '-r', path.join(__dirname, '../lib/parallel/agent_register'), '--parallel', '--jobs', '2', '--exit', ], { cwd: path.join(__dirname, './fixtures/apps/parallel-test'), }) // .debug() .expect('code', 0) .expect('stdout', /3 passing/) .end(); }); }); ================================================ FILE: test/tsd.test.js ================================================ 'use strict'; const path = require('path'); const { execSync } = require('child_process'); describe('test/tsd.test.js', () => { it('should tsd run success', () => { execSync('tsd', { cwd: path.dirname(__dirname), }); }); });