├── .eslintignore ├── .eslintrc ├── .github └── workflows │ ├── nodejs.yml │ ├── pkg.pr.new.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README.zh_CN.md ├── package.json ├── src ├── app.ts ├── app │ ├── extend │ │ ├── agent.ts │ │ └── application.ts │ └── middleware │ │ └── cluster_app_mock.ts ├── bootstrap.ts ├── index.ts ├── lib │ ├── agent_handler.ts │ ├── app.ts │ ├── app_handler.ts │ ├── cluster.ts │ ├── context.ts │ ├── format_options.ts │ ├── inject_context.ts │ ├── mock_agent.ts │ ├── mock_custom_loader.ts │ ├── mock_http_server.ts │ ├── mock_httpclient.ts │ ├── parallel │ │ ├── agent.ts │ │ ├── app.ts │ │ └── util.ts │ ├── prerequire.ts │ ├── request_call_function.ts │ ├── restore.ts │ ├── start-cluster.ts │ ├── supertest.ts │ ├── tmp │ │ ├── .gitkeep │ │ └── empty.ts │ ├── types.ts │ └── utils.ts ├── register.ts └── typings │ └── index.d.ts ├── test ├── agent.test.ts ├── app.test.ts ├── app │ └── middleware │ │ └── cluster_app_mock.test.ts ├── app_event.test.ts ├── app_proxy.test.ts ├── bootstrap-plugin.test.ts ├── bootstrap.test.ts ├── cluster.test.ts ├── ctx.test.ts ├── fixtures │ ├── agent-boot-error │ │ ├── agent.js │ │ └── package.json │ ├── agent-boot-ready-error │ │ ├── agent.js │ │ └── package.json │ ├── agent │ │ ├── agent.js │ │ ├── app.js │ │ ├── client.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.js │ │ ├── app │ │ │ └── router.js │ │ ├── config │ │ │ └── config.default.js │ │ └── package.json │ ├── app-fail │ │ ├── app │ │ │ └── router.js │ │ ├── config │ │ │ └── config.default.js │ │ └── package.json │ ├── app-proxy-ready │ │ ├── agent.js │ │ ├── app.js │ │ ├── config │ │ │ └── config.default.js │ │ └── package.json │ ├── app-proxy │ │ ├── app │ │ │ └── extend │ │ │ │ └── application.js │ │ ├── config │ │ │ └── config.default.js │ │ └── package.json │ ├── app-ready-failed │ │ ├── app.js │ │ ├── config │ │ │ └── config.default.js │ │ ├── package.json │ │ └── test │ │ │ └── index.test.js │ ├── app │ │ ├── app.js │ │ ├── app │ │ │ └── router.js │ │ ├── config │ │ │ └── config.default.js │ │ └── package.json │ ├── 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 │ │ ├── helloworld │ │ │ ├── app │ │ │ │ └── router.js │ │ │ ├── config │ │ │ │ └── config.default.js │ │ │ ├── package.json │ │ │ └── test │ │ │ │ └── helloworld.test.js │ │ ├── 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 │ │ │ ├── node_modules │ │ │ └── egg │ │ │ ├── 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-async │ │ ├── app.js │ │ ├── app │ │ │ ├── controller │ │ │ │ └── home.js │ │ │ ├── router.js │ │ │ └── service │ │ │ │ ├── bar │ │ │ │ └── foo.js │ │ │ │ ├── foo.js │ │ │ │ └── third │ │ │ │ └── bar │ │ │ │ └── foo.js │ │ ├── config │ │ │ └── config.js │ │ ├── mocks_data │ │ │ └── service │ │ │ │ ├── bar │ │ │ │ └── foo │ │ │ │ │ └── get │ │ │ │ │ └── foobar.js │ │ │ │ └── foo │ │ │ │ └── get │ │ │ │ └── foobar.js │ │ └── package.json │ ├── demo │ │ ├── app.js │ │ ├── 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 │ │ ├── config │ │ │ ├── config.js │ │ │ └── plugin.js │ │ ├── mocks_data │ │ │ └── service │ │ │ │ ├── bar │ │ │ │ └── foo │ │ │ │ │ └── get │ │ │ │ │ └── foobar.js │ │ │ │ └── foo │ │ │ │ └── get │ │ │ │ └── foobar.js │ │ └── package.json │ ├── demo_next │ │ ├── app.js │ │ ├── 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 │ │ ├── config │ │ │ └── config.js │ │ ├── mocks_data │ │ │ └── service │ │ │ │ ├── bar │ │ │ │ └── foo │ │ │ │ │ └── get │ │ │ │ │ └── foobar.js │ │ │ │ └── foo │ │ │ │ └── get │ │ │ │ └── foobar.js │ │ └── package.json │ ├── demo_next_h2 │ │ ├── app.js │ │ ├── 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 │ │ ├── config │ │ │ └── config.js │ │ ├── mocks_data │ │ │ └── service │ │ │ │ ├── bar │ │ │ │ └── foo │ │ │ │ │ └── get │ │ │ │ │ └── foobar.js │ │ │ │ └── foo │ │ │ │ └── get │ │ │ │ └── foobar.js │ │ └── package.json │ ├── disable-security │ │ ├── app.js │ │ ├── app │ │ │ ├── context.js │ │ │ ├── controller │ │ │ │ ├── home.js │ │ │ │ └── session.js │ │ │ ├── router.js │ │ │ └── service │ │ │ │ ├── bar │ │ │ │ └── foo.js │ │ │ │ ├── foo.js │ │ │ │ ├── old.js │ │ │ │ └── third │ │ │ │ └── bar │ │ │ │ └── foo.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-bootstrap │ │ ├── package.json │ │ └── test.js │ ├── plugin │ │ └── package.json │ ├── 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-esm │ │ ├── app.js │ │ ├── app │ │ │ └── modules │ │ │ │ └── foo │ │ │ │ ├── LogService.ts │ │ │ │ └── package.json │ │ ├── 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 │ ├── tegg-app │ │ ├── app.js │ │ ├── app │ │ │ └── modules │ │ │ │ └── foo │ │ │ │ ├── LogService.ts │ │ │ │ └── package.json │ │ ├── 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 │ │ ├── node_modules │ │ │ └── yadan │ │ │ │ ├── config │ │ │ │ └── config.default.js │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ └── package.json │ └── yadan_app_fail │ │ ├── config │ │ └── config.default.js │ │ ├── node_modules │ │ ├── .package-lock.json │ │ └── yadan │ │ │ ├── config │ │ │ └── config.default.js │ │ │ ├── index.js │ │ │ └── package.json │ │ └── package.json ├── format_options.test.ts ├── helper.ts ├── index.test-d.ts ├── index.test-skip.ts ├── inject_ctx.test.ts ├── mm.test.ts ├── mock_agent_httpclient.test.ts ├── mock_cluster_extend.test.ts ├── mock_cluster_restore.test.ts ├── mock_cluster_without_security_plugin.test.ts ├── mock_context.test.ts ├── mock_cookies.test.ts ├── mock_csrf.test.ts ├── mock_custom_loader.test.ts ├── mock_env.test.ts ├── mock_headers.test.ts ├── mock_httpclient_next.test.ts ├── mock_httpclient_next_h2.test.ts ├── mock_request.test.ts ├── mock_service.test.ts ├── mock_service_async.test.ts ├── mock_service_cluster.test.ts ├── mock_session.test.ts ├── parallel.test.ts ├── parallel_hook.test.ts └── tsd.test.ts └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | index.test-d.ts 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint-config-egg/typescript", 4 | "eslint-config-egg/lib/rules/enforce-node-prefix" 5 | ], 6 | "globals": { 7 | "beforeAll": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | Job: 11 | name: Node.js 12 | uses: node-modules/github-actions/.github/workflows/node-test.yml@master 13 | with: 14 | os: 'ubuntu-latest, macos-latest' 15 | version: '18.19.0, 18, 20, 22' 16 | secrets: 17 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 18 | -------------------------------------------------------------------------------- /.github/workflows/pkg.pr.new.yml: -------------------------------------------------------------------------------- 1 | name: Publish Any Commit 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | 8 | steps: 9 | - name: Checkout code 10 | uses: actions/checkout@v4 11 | 12 | - run: corepack enable 13 | - uses: actions/setup-node@v4 14 | with: 15 | node-version: 20 16 | 17 | - name: Install dependencies 18 | run: npm install 19 | 20 | - name: Build 21 | run: npm run prepublishOnly --if-present 22 | 23 | - run: npx pkg-pr-new publish 24 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | jobs: 8 | release: 9 | name: Node.js 10 | uses: eggjs/github-actions/.github/workflows/node-release.yml@master 11 | secrets: 12 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 13 | GIT_TOKEN: ${{ secrets.GIT_TOKEN }} 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | logs/ 4 | !test/fixtures/apps/app-not-clean/logs/keep 5 | !test/fixtures/yadan/node_modules 6 | !test/fixtures/yadan_*/node_modules 7 | run/ 8 | .vscode 9 | .idea 10 | .nyc_output 11 | package-lock.json 12 | .package-lock.json 13 | .tshy* 14 | .eslintcache 15 | dist 16 | .egg 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-present Alibaba Group Holding Limited and other contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@eggjs/mock", 3 | "version": "6.0.7", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "eggPlugin": { 8 | "name": "egg-mock", 9 | "exports": { 10 | "import": "./dist/esm", 11 | "require": "./dist/commonjs" 12 | } 13 | }, 14 | "description": "mock server plugin for egg", 15 | "homepage": "https://github.com/eggjs/mock", 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/eggjs/mock.git" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/eggjs/egg/issues" 22 | }, 23 | "keywords": [ 24 | "egg", 25 | "mock" 26 | ], 27 | "author": "popomore ", 28 | "engines": { 29 | "node": ">= 18.19.0" 30 | }, 31 | "dependencies": { 32 | "@eggjs/core": "^6.2.11", 33 | "@eggjs/supertest": "^8.1.0", 34 | "@eggjs/utils": "^4.0.3", 35 | "coffee": "^5.2.1", 36 | "detect-port": "^2.1.0", 37 | "egg-logger": "^3.5.0", 38 | "extend2": "^4.0.0", 39 | "get-ready": "^3.4.0", 40 | "globby": "^11.1.0", 41 | "is-type-of": "^2.2.0", 42 | "merge-descriptors": "^2.0.0", 43 | "mm": "^4.0.1", 44 | "sdk-base": "^5.0.0", 45 | "urllib": "^4.6.11", 46 | "utility": "^2.3.0" 47 | }, 48 | "peerDependencies": { 49 | "mocha": "^10 || ^11" 50 | }, 51 | "devDependencies": { 52 | "@arethetypeswrong/cli": "^0.17.1", 53 | "@eggjs/bin": "^7.0.0", 54 | "@eggjs/tegg": "^3.2.2", 55 | "@eggjs/tegg-config": "^3.2.2", 56 | "@eggjs/tegg-controller-plugin": "^3.2.2", 57 | "@eggjs/tegg-plugin": "^3.2.2", 58 | "@eggjs/tsconfig": "1", 59 | "@types/methods": "^1.1.4", 60 | "@types/mocha": "10", 61 | "@types/node": "22", 62 | "egg": "^4.0.8", 63 | "egg-errors": "^2.2.1", 64 | "egg-tracer": "^2.0.0", 65 | "eslint": "8", 66 | "eslint-config-egg": "14", 67 | "mocha": "^11.0.1", 68 | "pedding": "^2.0.0", 69 | "rimraf": "6", 70 | "tsd": "^0.31.2", 71 | "tshy": "3", 72 | "tshy-after": "^1.3.1", 73 | "typescript": "5" 74 | }, 75 | "scripts": { 76 | "clean": "rimraf dist", 77 | "lint": "eslint --cache src test --ext .ts", 78 | "pretest": "npm run clean && npm run lint -- --fix && npm run prepublishOnly", 79 | "test": "egg-bin test", 80 | "posttest": "npm run clean", 81 | "test-local": "egg-bin test", 82 | "preci": "npm run clean && npm run lint && npm run prepublishOnly", 83 | "ci": "egg-bin test", 84 | "postci": "npm run clean", 85 | "prepublishOnly": "tshy && tshy-after && attw --pack --profile node16" 86 | }, 87 | "type": "module", 88 | "tshy": { 89 | "exports": { 90 | ".": "./src/index.ts", 91 | "./bootstrap": "./src/bootstrap.ts", 92 | "./register": "./src/register.ts", 93 | "./package.json": "./package.json" 94 | } 95 | }, 96 | "exports": { 97 | ".": { 98 | "import": { 99 | "types": "./dist/esm/index.d.ts", 100 | "default": "./dist/esm/index.js" 101 | }, 102 | "require": { 103 | "types": "./dist/commonjs/index.d.ts", 104 | "default": "./dist/commonjs/index.js" 105 | } 106 | }, 107 | "./bootstrap": { 108 | "import": { 109 | "types": "./dist/esm/bootstrap.d.ts", 110 | "default": "./dist/esm/bootstrap.js" 111 | }, 112 | "require": { 113 | "types": "./dist/commonjs/bootstrap.d.ts", 114 | "default": "./dist/commonjs/bootstrap.js" 115 | } 116 | }, 117 | "./register": { 118 | "import": { 119 | "types": "./dist/esm/register.d.ts", 120 | "default": "./dist/esm/register.js" 121 | }, 122 | "require": { 123 | "types": "./dist/commonjs/register.d.ts", 124 | "default": "./dist/commonjs/register.js" 125 | } 126 | }, 127 | "./package.json": "./package.json" 128 | }, 129 | "files": [ 130 | "dist", 131 | "src" 132 | ], 133 | "types": "./dist/commonjs/index.d.ts", 134 | "main": "./dist/commonjs/index.js", 135 | "module": "./dist/esm/index.js" 136 | } 137 | -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | import { ILifecycleBoot, EggCore } from '@eggjs/core'; 2 | 3 | export default class Boot implements ILifecycleBoot { 4 | #app: EggCore; 5 | constructor(app: EggCore) { 6 | this.#app = app; 7 | } 8 | 9 | configWillLoad() { 10 | // make sure clusterAppMock position before securities 11 | const index = this.#app.config.coreMiddleware.indexOf('securities'); 12 | if (index >= 0) { 13 | this.#app.config.coreMiddleware.splice(index, 0, 'clusterAppMock'); 14 | } else { 15 | this.#app.config.coreMiddleware.push('clusterAppMock'); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app/extend/agent.ts: -------------------------------------------------------------------------------- 1 | import { mock, restore } from 'mm'; 2 | import { EggCore } from '@eggjs/core'; 3 | import { 4 | createMockHttpClient, MockResultFunction, 5 | MockResultOptions, 6 | MockHttpClientMethod, 7 | } from '../../lib/mock_httpclient.js'; 8 | import { getMockAgent, restoreMockAgent } from '../../lib/mock_agent.js'; 9 | 10 | export default abstract class AgentUnittest extends EggCore { 11 | [key: string]: any; 12 | _mockHttpClient: MockHttpClientMethod; 13 | 14 | /** 15 | * mock httpclient 16 | * @alias mockHttpClient 17 | * @function App#mockHttpclient 18 | */ 19 | mockHttpclient(mockUrl: string | RegExp, mockMethod: string | string[] | MockResultOptions | MockResultFunction, mockResult?: MockResultOptions | MockResultFunction | string) { 20 | return this.mockHttpClient(mockUrl, mockMethod, mockResult); 21 | } 22 | 23 | /** 24 | * mock httpclient 25 | * @function App#mockHttpClient 26 | */ 27 | mockHttpClient(mockUrl: string | RegExp, mockMethod: string | string[] | MockResultOptions | MockResultFunction, mockResult?: MockResultOptions | MockResultFunction | string) { 28 | if (!this._mockHttpClient) { 29 | this._mockHttpClient = createMockHttpClient(this); 30 | } 31 | return this._mockHttpClient(mockUrl, mockMethod, mockResult); 32 | } 33 | 34 | /** 35 | * get mock httpclient agent 36 | * @function Agent#mockHttpclientAgent 37 | */ 38 | mockAgent() { 39 | return getMockAgent(this as any); 40 | } 41 | 42 | async mockAgentRestore() { 43 | await restoreMockAgent(); 44 | } 45 | 46 | /** 47 | * @see mm#restore 48 | * @function Agent#mockRestore 49 | */ 50 | mockRestore = restore; 51 | 52 | /** 53 | * @see mm 54 | * @function Agent#mm 55 | */ 56 | mm = mock; 57 | } 58 | -------------------------------------------------------------------------------- /src/app/middleware/cluster_app_mock.ts: -------------------------------------------------------------------------------- 1 | import { debuglog } from 'node:util'; 2 | import { Context, Next } from '@eggjs/core'; 3 | 4 | const debug = debuglog('@eggjs/mock/app/middleware/cluster_app_mock'); 5 | 6 | export default () => { 7 | return async function clusterAppMock(ctx: Context, next: Next) { 8 | // use originalUrl to make sure other middlewares can't change request url 9 | if (ctx.originalUrl !== '/__egg_mock_call_function') { 10 | return next(); 11 | } 12 | const body = (ctx.request as any).body; 13 | debug('%s %s, body: %j', ctx.method, ctx.url, body); 14 | const { method, property, args, needResult } = body; 15 | if (!method) { 16 | ctx.status = 422; 17 | ctx.body = { 18 | success: false, 19 | error: 'Missing method', 20 | }; 21 | return; 22 | } 23 | if (args && !Array.isArray(args)) { 24 | ctx.status = 422; 25 | ctx.body = { 26 | success: false, 27 | error: 'args should be an Array instance', 28 | }; 29 | return; 30 | } 31 | if (property) { 32 | // method: '__getter__' and property: 'config' 33 | if (method === '__getter__') { 34 | if (!ctx.app[property]) { 35 | debug('property %s not exists on app', property); 36 | ctx.status = 422; 37 | ctx.body = { 38 | success: false, 39 | error: `property "${property}" not exists on app`, 40 | }; 41 | return; 42 | } 43 | ctx.body = { success: true, result: ctx.app[property] }; 44 | return; 45 | } 46 | 47 | if (!ctx.app[property] || typeof (ctx.app as any)[property][method] !== 'function') { 48 | debug('property %s.%s not exists on app', property, method); 49 | ctx.status = 422; 50 | ctx.body = { 51 | success: false, 52 | error: `method "${method}" not exists on app.${property}`, 53 | }; 54 | return; 55 | } 56 | } else { 57 | if (typeof ctx.app[method] !== 'function') { 58 | debug('method %s not exists on app', method); 59 | ctx.status = 422; 60 | ctx.body = { 61 | success: false, 62 | error: `method "${method}" not exists on app`, 63 | }; 64 | return; 65 | } 66 | } 67 | 68 | debug('call %s with %j', method, args); 69 | 70 | for (let i = 0; i < args.length; i++) { 71 | const arg = args[i]; 72 | if (arg && typeof arg === 'object') { 73 | // convert __egg_mock_type back to function 74 | if (arg.__egg_mock_type === 'function') { 75 | // eslint-disable-next-line 76 | args[i] = eval(`(function() { return ${arg.value} })()`); 77 | } else if (arg.__egg_mock_type === 'error') { 78 | const err: any = new Error(arg.message); 79 | err.name = arg.name; 80 | err.stack = arg.stack; 81 | for (const key in arg) { 82 | if (key !== 'name' && key !== 'message' && key !== 'stack' && key !== '__egg_mock_type') { 83 | err[key] = arg[key]; 84 | } 85 | } 86 | args[i] = err; 87 | } 88 | } 89 | } 90 | 91 | const target: any = property ? ctx.app[property] : ctx.app; 92 | const fn = target[method]; 93 | try { 94 | Promise.resolve(fn.call(target, ...args)).then(result => { 95 | ctx.body = needResult ? { success: true, result } : { success: true }; 96 | }); 97 | } catch (err: any) { 98 | ctx.status = 500; 99 | ctx.body = { success: false, error: err.message }; 100 | } 101 | }; 102 | }; 103 | -------------------------------------------------------------------------------- /src/bootstrap.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import path from 'node:path'; 3 | import { readJSONSync } from 'utility'; 4 | import mm, { mock, MockApplication } from './index.js'; 5 | import { getBootstrapApp, setupApp } from './lib/app_handler.js'; 6 | import { getEggOptions } from './lib/utils.js'; 7 | 8 | const options = getEggOptions(); 9 | 10 | // throw error when an egg plugin test is using bootstrap 11 | const pkgInfo = readJSONSync(path.join(options.baseDir || process.cwd(), 'package.json')); 12 | if (pkgInfo.eggPlugin) { 13 | throw new Error('DO NOT USE bootstrap to test plugin'); 14 | } 15 | 16 | const app = setupApp(); 17 | 18 | export { 19 | assert, 20 | getBootstrapApp, 21 | app, 22 | mm, 23 | mock, 24 | MockApplication, 25 | }; 26 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import mm from 'mm'; 2 | import { mock as _mock } from 'mm'; 3 | import { createCluster } from './lib/cluster.js'; 4 | import { createApp } from './lib/app.js'; 5 | // import { getMockAgent } from './lib/mock_agent.js'; 6 | import { restore } from './lib/restore.js'; 7 | import { setGetAppCallback } from './lib/app_handler.js'; 8 | import ApplicationUnittest from './app/extend/application.js'; 9 | 10 | export * from './lib/types.js'; 11 | 12 | // egg-bin will set this flag to require files for instrument 13 | // if (process.env.EGG_BIN_PREREQUIRE) { 14 | // require('./lib/prerequire'); 15 | // } 16 | 17 | // inherit & extends mm 18 | const mock = { 19 | ...mm, 20 | restore, 21 | 22 | /** 23 | * Create a egg mocked application 24 | * @function mm#app 25 | * @param {Object} [options] 26 | * - {String} baseDir - The directory of the application 27 | * - {Object} plugins - Custom you plugins 28 | * - {String} framework - The directory of the egg framework 29 | * - {Boolean} [true] cache - Cache application based on baseDir 30 | * - {Boolean} [true] coverage - Switch on process coverage, but it'll be slower 31 | * - {Boolean} [true] clean - Remove $baseDir/logs 32 | * @return {App} return {@link Application} 33 | * @example 34 | * ```js 35 | * const app = mm.app(); 36 | * ``` 37 | */ 38 | app: createApp, 39 | 40 | /** 41 | * Create a egg mocked cluster application 42 | * @function mm#cluster 43 | * @see ClusterApplication 44 | */ 45 | cluster: createCluster, 46 | 47 | /** 48 | * mock the serverEnv of Egg 49 | * @member {Function} mm#env 50 | * @param {String} env - contain default, test, prod, local, unittest 51 | * @see https://github.com/eggjs/egg-core/blob/master/lib/loader/egg_loader.js#L78 52 | */ 53 | env(env: string) { 54 | _mock(process.env, 'EGG_MOCK_SERVER_ENV', env); 55 | _mock(process.env, 'EGG_SERVER_ENV', env); 56 | }, 57 | 58 | /** 59 | * mock console level 60 | * @param {String} level - logger level 61 | */ 62 | consoleLevel(level: string) { 63 | level = (level || '').toUpperCase(); 64 | _mock(process.env, 'EGG_LOG', level); 65 | }, 66 | 67 | home(homePath?: string) { 68 | if (homePath) { 69 | _mock(process.env, 'EGG_HOME', homePath); 70 | } 71 | }, 72 | 73 | setGetAppCallback, 74 | }; 75 | 76 | // import mm from '@eggjs/mock'; 77 | const proxyMock = new Proxy(_mock, { 78 | apply(target, _, args) { 79 | return target(args[0], args[1], args[2]); 80 | }, 81 | get(_target, property, receiver) { 82 | // import mm from '@eggjs/mock'; 83 | // mm.isMocked(foo, 'bar') 84 | return Reflect.get(mock, property, receiver); 85 | }, 86 | }) as unknown as ((target: any, property: PropertyKey, value?: any) => void) & typeof mock; 87 | 88 | export default proxyMock; 89 | 90 | export { 91 | proxyMock as mock, 92 | // alias to mm 93 | proxyMock as mm, 94 | ApplicationUnittest as MockApplication, 95 | setGetAppCallback, 96 | createApp, 97 | createCluster, 98 | }; 99 | 100 | process.setMaxListeners(100); 101 | 102 | process.once('SIGQUIT', () => { 103 | process.exit(0); 104 | }); 105 | 106 | process.once('SIGTERM', () => { 107 | process.exit(0); 108 | }); 109 | 110 | process.once('SIGINT', () => { 111 | process.exit(0); 112 | }); 113 | -------------------------------------------------------------------------------- /src/lib/agent_handler.ts: -------------------------------------------------------------------------------- 1 | import { debuglog } from 'node:util'; 2 | import { createAgent, MockAgent } from './parallel/agent.js'; 3 | import { getEggOptions } from './utils.js'; 4 | 5 | const debug = debuglog('@eggjs/mock/lib/agent_handler'); 6 | 7 | let agent: MockAgent; 8 | 9 | export async function setupAgent() { 10 | debug('setupAgent call, env.ENABLE_MOCHA_PARALLEL: %s, process.env.AUTO_AGENT: %s, agent: %s', 11 | process.env.ENABLE_MOCHA_PARALLEL, process.env.AUTO_AGENT, !!agent); 12 | if (agent) { 13 | await agent.ready(); 14 | return agent; 15 | } 16 | if (process.env.ENABLE_MOCHA_PARALLEL && process.env.AUTO_AGENT) { 17 | agent = createAgent(getEggOptions()); 18 | await agent.ready(); 19 | } 20 | return agent; 21 | } 22 | 23 | export async function closeAgent() { 24 | debug('setupAgent call, agent: %s', !!agent); 25 | if (agent) { 26 | await agent.close(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/lib/app_handler.ts: -------------------------------------------------------------------------------- 1 | import { debuglog } from 'node:util'; 2 | import { createApp as createParallelApp } from './parallel/app.js'; 3 | import { setupAgent } from './agent_handler.js'; 4 | import { createApp } from './app.js'; 5 | import { restore } from './restore.js'; 6 | import { getEggOptions } from './utils.js'; 7 | import ApplicationUnittest from '../app/extend/application.js'; 8 | 9 | const debug = debuglog('@eggjs/mock/lib/app_handler'); 10 | 11 | declare namespace globalThis { 12 | let __eggMockAppInstance: ApplicationUnittest | null; 13 | } 14 | 15 | globalThis.__eggMockAppInstance = null; 16 | 17 | export function setupApp() { 18 | let app = globalThis.__eggMockAppInstance!; 19 | if (app) { 20 | debug('return exists app'); 21 | return app; 22 | } 23 | 24 | const options = getEggOptions(); 25 | debug('env.ENABLE_MOCHA_PARALLEL: %s, process.env.AUTO_AGENT: %s', 26 | process.env.ENABLE_MOCHA_PARALLEL, process.env.AUTO_AGENT); 27 | if (process.env.ENABLE_MOCHA_PARALLEL && process.env.AUTO_AGENT) { 28 | // setup agent first 29 | app = createParallelApp({ 30 | ...options, 31 | beforeInit: async parallelApp => { 32 | const agent = await setupAgent(); 33 | parallelApp.options.clusterPort = agent.options.clusterPort; 34 | debug('mockParallelApp beforeInit get clusterPort: %s', parallelApp.options.clusterPort); 35 | }, 36 | }); 37 | debug('mockParallelApp app: %s', !!app); 38 | } else { 39 | app = createApp(options) as unknown as ApplicationUnittest; 40 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 41 | // @ts-ignore 42 | if (typeof beforeAll === 'function') { 43 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 44 | // @ts-ignore 45 | // jest 46 | beforeAll(() => app.ready()); 47 | } 48 | if (typeof afterEach === 'function') { 49 | // mocha and jest 50 | afterEach(() => app.backgroundTasksFinished()); 51 | afterEach(restore); 52 | } 53 | } 54 | globalThis.__eggMockAppInstance = app; 55 | return app; 56 | } 57 | 58 | let getAppCallback: (suite: unknown, test?: unknown) => any; 59 | 60 | export function setGetAppCallback(cb: (suite: unknown, test?: unknown) => any) { 61 | getAppCallback = cb; 62 | } 63 | 64 | export async function getApp(suite?: unknown, test?: unknown) { 65 | if (getAppCallback) { 66 | return getAppCallback(suite, test); 67 | } 68 | const app = globalThis.__eggMockAppInstance!; 69 | if (app) { 70 | await app.ready(); 71 | } 72 | return app; 73 | } 74 | 75 | export function getBootstrapApp() { 76 | return globalThis.__eggMockAppInstance!; 77 | } 78 | -------------------------------------------------------------------------------- /src/lib/context.ts: -------------------------------------------------------------------------------- 1 | import { utils } from '@eggjs/core'; 2 | 3 | export const context = { 4 | runInBackground(scope: any) { 5 | /* istanbul ignore next */ 6 | const taskName = scope._name || scope.name || utils.getCalleeFromStack(true); 7 | if (taskName) { 8 | scope._name = taskName; 9 | } 10 | 11 | const promise = this._runInBackground(scope); 12 | this.app._backgroundTasks.push(promise); 13 | }, 14 | } as any; 15 | -------------------------------------------------------------------------------- /src/lib/format_options.ts: -------------------------------------------------------------------------------- 1 | import { debuglog } from 'node:util'; 2 | import path from 'node:path'; 3 | import { mm, isMocked } from 'mm'; 4 | import { getFrameworkPath } from '@eggjs/utils'; 5 | import { readJSONSync } from 'utility'; 6 | import { MockOptions, MockApplicationOptions } from './types.js'; 7 | import { getSourceDirname } from './utils.js'; 8 | 9 | const debug = debuglog('@eggjs/mock/lib/format_options'); 10 | 11 | /** 12 | * format the options 13 | */ 14 | export function formatOptions(initOptions?: MockOptions) { 15 | const options = { 16 | baseDir: process.cwd(), 17 | cache: true, 18 | coverage: true, 19 | clean: true, 20 | ...initOptions, 21 | } as MockApplicationOptions; 22 | 23 | // relative path to test/fixtures 24 | // ```js 25 | // formatOptions({ baseDir: 'app' }); // baseDir => $PWD/test/fixtures/app 26 | // ``` 27 | if (!path.isAbsolute(options.baseDir)) { 28 | options.baseDir = path.join(process.cwd(), 'test/fixtures', options.baseDir); 29 | } 30 | 31 | let framework = initOptions?.framework ?? initOptions?.customEgg; 32 | // test for framework 33 | if (framework === true) { 34 | framework = process.cwd(); 35 | // disable plugin test when framework test 36 | options.plugin = false; 37 | } else { 38 | if (!framework) { 39 | framework = ''; 40 | } 41 | // it will throw when framework is not found 42 | framework = getFrameworkPath({ framework, baseDir: options.baseDir }); 43 | } 44 | options.framework = options.customEgg = framework; 45 | 46 | const plugins = options.plugins = options.plugins || {}; 47 | 48 | // add self as a plugin 49 | let pluginPath = path.join(getSourceDirname(), '..'); 50 | // for dist directory 51 | // convert `/eggjs/mock/dist` to `/eggjs/mock` 52 | if (pluginPath.endsWith('/dist') || pluginPath.endsWith('\\dist')) { 53 | pluginPath = path.join(pluginPath, '..'); 54 | } 55 | plugins['egg-mock'] = { 56 | enable: true, 57 | path: pluginPath, 58 | }; 59 | 60 | // test for plugin 61 | if (options.plugin !== false) { 62 | // add self to plugin list 63 | const pluginPath = process.cwd(); 64 | const pkgPath = path.join(pluginPath, 'package.json'); 65 | const pluginName = getPluginName(pkgPath); 66 | if (options.plugin && !pluginName) { 67 | throw new Error(`should set "eggPlugin" property in ${pkgPath}`); 68 | } 69 | if (pluginName) { 70 | plugins[pluginName] = { 71 | enable: true, 72 | path: pluginPath, 73 | }; 74 | } 75 | } 76 | 77 | // mock HOME as baseDir, but ignore if it has been mocked 78 | const env = process.env.EGG_SERVER_ENV; 79 | if (!isMocked(process.env, 'HOME') && 80 | (env === 'default' || env === 'test' || env === 'prod')) { 81 | mm(process.env, 'HOME', options.baseDir); 82 | } 83 | 84 | // disable cache after call mm.env(), 85 | // otherwise it will use cache and won't load again. 86 | if (process.env.EGG_MOCK_SERVER_ENV) { 87 | options.cache = false; 88 | } 89 | 90 | debug('format options: %j', options); 91 | return options; 92 | } 93 | 94 | function getPluginName(pkgPath: string): string | undefined { 95 | try { 96 | const pkg = readJSONSync(pkgPath); 97 | if (pkg.eggPlugin?.name) { 98 | return pkg.eggPlugin.name; 99 | } 100 | } catch (_) { 101 | // ignore 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/lib/inject_context.ts: -------------------------------------------------------------------------------- 1 | import { debuglog } from 'node:util'; 2 | import assert from 'node:assert'; 3 | import { getApp } from './app_handler.js'; 4 | 5 | const MOCHA_SUITE_APP = Symbol.for('mocha#suite#app'); 6 | 7 | const debug = debuglog('@eggjs/mock/lib/inject_context'); 8 | 9 | /** 10 | * Monkey patch the mocha instance with egg context. 11 | * 12 | * @param {Function} mocha - the module of mocha 13 | */ 14 | export function injectContext(mocha: any) { 15 | if (mocha._injectContextLoaded) { 16 | debug('mocha already injected context, skip it'); 17 | return; 18 | } 19 | const { Runner } = mocha; 20 | const runSuite = Runner.prototype.runSuite; 21 | const runTests = Runner.prototype.runTests; 22 | 23 | function getTestTitle(suite: any, test: any) { 24 | const suiteTitle = suite.root ? 'root suite' : suite.title; 25 | if (!test) { 26 | return `"${suiteTitle}"`; 27 | } 28 | return `"${suiteTitle} - ${test.title}"`; 29 | } 30 | 31 | // Inject ctx for before/after. 32 | Runner.prototype.runSuite = async function(suite: any, fn: any) { 33 | debug('run suite: %s', suite.title); 34 | let app; 35 | const self = this; 36 | try { 37 | app = await getApp(suite); 38 | debug('get app: %s', !!app); 39 | await app.ready(); 40 | } catch { 41 | // 可能 app.ready 时报错,不使用失败的 app 42 | app = null; 43 | } 44 | if (!app) { 45 | // app 不存在,直接跳过,在 beforeEach 的 hook 中会报错 46 | // 确保不打乱 mocha 的顺序,防止 mocha 内部状态错误 47 | return runSuite.call(self, suite, fn); 48 | } 49 | let errSuite; 50 | try { 51 | suite.ctx[MOCHA_SUITE_APP] = app; 52 | const mockContextFun = app.mockModuleContextScope || app.mockContextScope; 53 | await mockContextFun.call(app, async function() { 54 | await new Promise(resolve => { 55 | runSuite.call(self, suite, (aErrSuite: Error) => { 56 | errSuite = aErrSuite; 57 | resolve(); 58 | }); 59 | }); 60 | }); 61 | } catch (err) { 62 | // mockContext 失败后动态注册一个 beforeAll hook 63 | // 快速失败,直接阻塞后续用例 64 | suite.beforeAll('egg-mock-mock-ctx-failed', async () => { 65 | throw err; 66 | }); 67 | return runSuite.call(self, suite, (aErrSuite: Error) => { 68 | return fn(aErrSuite); 69 | }); 70 | } 71 | return fn(errSuite); 72 | }; 73 | 74 | // Inject ctx for beforeEach/it/afterEach. 75 | // And ctx with before/after is not same as beforeEach/it/afterEach. 76 | Runner.prototype.runTests = async function(suite: any, fn: any) { 77 | const tests = suite.tests.slice(); 78 | if (!tests.length) { 79 | return runTests.call(this, suite, fn); 80 | } 81 | 82 | const app = suite.ctx[MOCHA_SUITE_APP]; 83 | 84 | const self = this; 85 | if (!app) { 86 | return runTests.call(self, suite, fn); 87 | } 88 | 89 | function done(errSuite?: Error) { 90 | suite.tests = tests; 91 | return fn(errSuite); 92 | } 93 | 94 | async function next(i: number) { 95 | const test = tests[i]; 96 | if (!test) { 97 | return done(); 98 | } 99 | suite.tests = [ test ]; 100 | 101 | let app; 102 | try { 103 | app = await getApp(suite, test); 104 | assert(app, `not found app for test ${getTestTitle(suite, test)}`); 105 | await app.ready(); 106 | } catch (err) { 107 | self.fail(test, err); 108 | return next(i + 1); 109 | } 110 | 111 | try { 112 | const mockContextFun = app.mockModuleContextScope || app.mockContextScope; 113 | await mockContextFun.call(app, async function() { 114 | return await new Promise(resolve => { 115 | runTests.call(self, suite, () => { 116 | return resolve(); 117 | }); 118 | }); 119 | }); 120 | } catch (err) { 121 | self.fail(test, err); 122 | return next(i + 1); 123 | } 124 | return next(i + 1); 125 | } 126 | next(0).catch(err => { 127 | self.fail(suite, err); 128 | done(suite); 129 | }); 130 | }; 131 | 132 | mocha._injectContextLoaded = true; 133 | debug('inject context success'); 134 | } 135 | -------------------------------------------------------------------------------- /src/lib/mock_agent.ts: -------------------------------------------------------------------------------- 1 | import { debuglog } from 'node:util'; 2 | import { 3 | MockAgent, setGlobalDispatcher, getGlobalDispatcher, Dispatcher, 4 | HttpClient, 5 | } from 'urllib'; 6 | 7 | const debug = debuglog('@eggjs/mock/lib/mock_agent'); 8 | 9 | declare namespace globalThis { 10 | let __mockAgent: MockAgent | null; 11 | let __globalDispatcher: Dispatcher; 12 | let __httpClientDispatchers: Map; 13 | } 14 | 15 | globalThis.__mockAgent = null; 16 | globalThis.__httpClientDispatchers = new Map(); 17 | 18 | export function getMockAgent(app?: { httpClient?: HttpClient }) { 19 | debug('getMockAgent'); 20 | if (!globalThis.__globalDispatcher) { 21 | globalThis.__globalDispatcher = getGlobalDispatcher(); 22 | debug('create global dispatcher'); 23 | } 24 | if (app?.httpClient && !globalThis.__httpClientDispatchers.has(app.httpClient)) { 25 | globalThis.__httpClientDispatchers.set(app.httpClient, app.httpClient.getDispatcher()); 26 | debug('add new httpClient, size: %d', globalThis.__httpClientDispatchers.size); 27 | } 28 | if (!globalThis.__mockAgent) { 29 | globalThis.__mockAgent = new MockAgent(); 30 | setGlobalDispatcher(globalThis.__mockAgent); 31 | if (typeof app?.httpClient?.setDispatcher === 'function') { 32 | app.httpClient.setDispatcher(globalThis.__mockAgent); 33 | } 34 | debug('create new mockAgent'); 35 | } 36 | return globalThis.__mockAgent; 37 | } 38 | 39 | export async function restoreMockAgent() { 40 | debug('restoreMockAgent start'); 41 | if (globalThis.__globalDispatcher) { 42 | setGlobalDispatcher(globalThis.__globalDispatcher); 43 | debug('restore global dispatcher'); 44 | } 45 | debug('restore httpClient, size: %d', globalThis.__httpClientDispatchers.size); 46 | for (const [ httpClient, dispatcher ] of globalThis.__httpClientDispatchers) { 47 | httpClient.setDispatcher(dispatcher); 48 | } 49 | globalThis.__httpClientDispatchers.clear(); 50 | if (globalThis.__mockAgent) { 51 | const agent = globalThis.__mockAgent; 52 | globalThis.__mockAgent = null; 53 | await agent.close(); 54 | debug('close mockAgent'); 55 | } 56 | debug('restoreMockAgent end'); 57 | } 58 | -------------------------------------------------------------------------------- /src/lib/mock_custom_loader.ts: -------------------------------------------------------------------------------- 1 | import { debuglog } from 'node:util'; 2 | 3 | const debug = debuglog('@eggjs/mock/lib/mock_custom_loader'); 4 | 5 | export function setCustomLoader(app: any) { 6 | const customLoader = app.config.customLoader; 7 | if (!customLoader) return; 8 | 9 | for (const field of Object.keys(customLoader)) { 10 | const loaderConfig = Object.assign({}, customLoader[field]); 11 | loaderConfig.field = field; 12 | addMethod(loaderConfig); 13 | } 14 | 15 | function addMethod(loaderConfig: any) { 16 | const field = loaderConfig.field as string; 17 | const appMethodName = 'mock' + field.replace(/^[a-z]/i, s => s.toUpperCase()); 18 | if (app[appMethodName]) { 19 | app.coreLogger.warn('Can\'t override app.%s', appMethodName); 20 | return; 21 | } 22 | debug('[addMethod] %s => %j', appMethodName, loaderConfig); 23 | app[appMethodName] = function(service: any, methodName: string, fn: any) { 24 | if (typeof service === 'string') { 25 | const arr = service.split('.'); 26 | service = loaderConfig.inject === 'ctx' ? this[field + 'Classes'] : this[field]; 27 | for (const key of arr) { 28 | service = service[key]; 29 | } 30 | service = service.prototype || service; 31 | } 32 | this._mockFn(service, methodName, fn); 33 | return this; 34 | }; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/lib/mock_http_server.ts: -------------------------------------------------------------------------------- 1 | import http, { Server } from 'node:http'; 2 | 3 | const SERVER = Symbol('http_server'); 4 | 5 | export function createServer(app: any): Server { 6 | let server = app[SERVER] || app.callback(); 7 | if (typeof server === 'function') { 8 | server = http.createServer(server); 9 | // cache server, avoid create many times 10 | app[SERVER] = server; 11 | if (!app.server) { 12 | app.server = server; 13 | } 14 | // emit server event just like egg-cluster does 15 | // https://github.com/eggjs/egg-cluster/blob/master/lib/app_worker.js#L52 16 | app.emit('server', server); 17 | } 18 | return server; 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/parallel/agent.ts: -------------------------------------------------------------------------------- 1 | import { debuglog } from 'node:util'; 2 | import path from 'node:path'; 3 | import { Base } from 'sdk-base'; 4 | import { detectPort } from 'detect-port'; 5 | import { importModule } from '@eggjs/utils'; 6 | import type { EggCore } from '@eggjs/core'; 7 | import { context } from '../context.js'; 8 | import { formatOptions } from '../format_options.js'; 9 | import { MockOptions, MockApplicationOptions } from '../types.js'; 10 | import { sleep, rimraf } from '../utils.js'; 11 | import { setCustomLoader } from '../mock_custom_loader.js'; 12 | import { APP_INIT } from './util.js'; 13 | 14 | const debug = debuglog('@eggjs/mock/lib/parallel/agent'); 15 | 16 | export class MockAgent extends Base { 17 | declare options: MockApplicationOptions; 18 | baseDir: string; 19 | [APP_INIT] = false; 20 | #initOnListeners = new Set(); 21 | #initOnceListeners = new Set(); 22 | _instance: EggCore; 23 | 24 | constructor(options: MockApplicationOptions) { 25 | super({ initMethod: '_init' }); 26 | this.options = options; 27 | this.baseDir = this.options.baseDir; 28 | } 29 | 30 | async _init() { 31 | if (this.options.beforeInit) { 32 | await this.options.beforeInit(this); 33 | delete this.options.beforeInit; 34 | } 35 | if (this.options.clean !== false) { 36 | const logDir = path.join(this.options.baseDir, 'logs'); 37 | try { 38 | await rimraf(logDir); 39 | } catch (err: any) { 40 | console.error(`remove log dir ${logDir} failed: ${err.stack}`); 41 | } 42 | const runDir = path.join(this.options.baseDir, 'run'); 43 | try { 44 | await rimraf(runDir); 45 | } catch (err: any) { 46 | console.error(`remove run dir ${runDir} failed: ${err.stack}`); 47 | } 48 | } 49 | 50 | this.options.clusterPort = await detectPort(); 51 | process.env.CLUSTER_PORT = String(this.options.clusterPort); 52 | debug('get clusterPort %s', this.options.clusterPort); 53 | const { Agent }: { Agent: typeof EggCore } = await importModule(this.options.framework); 54 | 55 | const agent = this._instance = new Agent({ ...this.options }); 56 | 57 | // egg-mock plugin need to override egg context 58 | Object.assign(agent.context, context); 59 | setCustomLoader(agent); 60 | 61 | debug('agent instantiate'); 62 | this[APP_INIT] = true; 63 | debug('this[APP_INIT] = true'); 64 | this.#bindEvents(); 65 | await agent.ready(); 66 | 67 | const msg = { 68 | action: 'egg-ready', 69 | data: this.options, 70 | }; 71 | (agent as any).messenger.onMessage(msg); 72 | debug('agent ready'); 73 | } 74 | 75 | #bindEvents() { 76 | debug('bind cache events to agent'); 77 | for (const args of this.#initOnListeners) { 78 | debug('on(%s), use cache and pass to agent', args); 79 | this._instance.on(args[0], args[1]); 80 | this.removeListener(args[0], args[1]); 81 | } 82 | for (const args of this.#initOnceListeners) { 83 | debug('once(%s), use cache and pass to agent', args); 84 | this._instance.once(args[0], args[1]); 85 | this.removeListener(args[0], args[1]); 86 | } 87 | } 88 | 89 | on(...args: any[]) { 90 | if (this[APP_INIT]) { 91 | debug('on(%s), pass to agent', args); 92 | this._instance.on(args[0], args[1]); 93 | } else { 94 | debug('on(%s), cache it because agent has not init', args); 95 | this.#initOnListeners.add(args); 96 | super.on(args[0], args[1]); 97 | } 98 | return this; 99 | } 100 | 101 | once(...args: any[]) { 102 | if (this[APP_INIT]) { 103 | debug('once(%s), pass to agent', args); 104 | this._instance.once(args[0], args[1]); 105 | } else { 106 | debug('once(%s), cache it because agent has not init', args); 107 | this.#initOnceListeners.add(args); 108 | super.on(args[0], args[1]); 109 | } 110 | return this; 111 | } 112 | 113 | /** 114 | * close agent 115 | */ 116 | async _close() { 117 | if (this._instance) { 118 | await this._instance.close(); 119 | } else { 120 | // when agent init throws an exception, must wait for agent quit gracefully 121 | await sleep(200); 122 | } 123 | } 124 | } 125 | 126 | export function createAgent(options: MockOptions) { 127 | return new MockAgent(formatOptions(options)); 128 | } 129 | -------------------------------------------------------------------------------- /src/lib/parallel/app.ts: -------------------------------------------------------------------------------- 1 | import { debuglog } from 'node:util'; 2 | import { Base } from 'sdk-base'; 3 | import { importModule } from '@eggjs/utils'; 4 | import type { EggCore } from '@eggjs/core'; 5 | import { context } from '../context.js'; 6 | import { formatOptions } from '../format_options.js'; 7 | import { MockOptions, MockApplicationOptions } from '../types.js'; 8 | import { sleep } from '../utils.js'; 9 | import { setCustomLoader } from '../mock_custom_loader.js'; 10 | import { createServer } from '../mock_http_server.js'; 11 | import { proxyApp, APP_INIT } from './util.js'; 12 | 13 | const debug = debuglog('@eggjs/mock/lib/parallel/app'); 14 | 15 | export class MockParallelApplication extends Base { 16 | declare options: MockApplicationOptions; 17 | baseDir: string; 18 | [APP_INIT] = false; 19 | #initOnListeners = new Set(); 20 | #initOnceListeners = new Set(); 21 | _instance: EggCore; 22 | 23 | constructor(options: MockApplicationOptions) { 24 | super({ initMethod: '_init' }); 25 | this.options = options; 26 | this.baseDir = options.baseDir; 27 | } 28 | 29 | async _init() { 30 | if (this.options.beforeInit) { 31 | await this.options.beforeInit(this); 32 | delete this.options.beforeInit; 33 | } 34 | 35 | if (!this.options.clusterPort) { 36 | this.options.clusterPort = parseInt(process.env.CLUSTER_PORT!); 37 | } 38 | if (!this.options.clusterPort) { 39 | throw new Error('cannot get env.CLUSTER_PORT, parallel run fail'); 40 | } 41 | debug('get clusterPort %s', this.options.clusterPort); 42 | const { Application }: { Application: typeof EggCore } = await importModule(this.options.framework); 43 | 44 | const app = this._instance = new Application({ ...this.options }); 45 | 46 | // egg-mock plugin need to override egg context 47 | Object.assign(app.context, context); 48 | setCustomLoader(app); 49 | 50 | debug('app instantiate'); 51 | this[APP_INIT] = true; 52 | debug('this[APP_INIT] = true'); 53 | this.#bindEvents(); 54 | debug('http server instantiate'); 55 | createServer(app); 56 | await app.ready(); 57 | 58 | const msg = { 59 | action: 'egg-ready', 60 | data: this.options, 61 | }; 62 | (app as any).messenger.onMessage(msg); 63 | debug('app ready'); 64 | } 65 | 66 | #bindEvents() { 67 | for (const args of this.#initOnListeners) { 68 | debug('on(%s), use cache and pass to app', args); 69 | this._instance.on(args[0], args[1]); 70 | this.removeListener(args[0], args[1]); 71 | } 72 | for (const args of this.#initOnceListeners) { 73 | debug('once(%s), use cache and pass to app', args); 74 | this._instance.once(args[0], args[1]); 75 | this.removeListener(args[0], args[1]); 76 | } 77 | } 78 | 79 | on(...args: any[]) { 80 | if (this[APP_INIT]) { 81 | debug('on(%s), pass to app', args); 82 | this._instance.on(args[0], args[1]); 83 | } else { 84 | debug('on(%s), cache it because app has not init', args); 85 | if (this.#initOnListeners) { 86 | this.#initOnListeners.add(args); 87 | } 88 | super.on(args[0], args[1]); 89 | } 90 | return this; 91 | } 92 | 93 | once(...args: any[]) { 94 | if (this[APP_INIT]) { 95 | debug('once(%s), pass to app', args); 96 | this._instance.once(args[0], args[1]); 97 | } else { 98 | debug('once(%s), cache it because app has not init', args); 99 | if (this.#initOnceListeners) { 100 | this.#initOnceListeners.add(args); 101 | } 102 | super.on(args[0], args[1]); 103 | } 104 | return this; 105 | } 106 | 107 | /** 108 | * close app 109 | */ 110 | async _close() { 111 | if (this._instance) { 112 | await this._instance.close(); 113 | } else { 114 | // when app init throws an exception, must wait for app quit gracefully 115 | await sleep(200); 116 | } 117 | } 118 | } 119 | 120 | export function createApp(initOptions: MockOptions) { 121 | const app = new MockParallelApplication(formatOptions(initOptions)); 122 | return proxyApp(app); 123 | } 124 | -------------------------------------------------------------------------------- /src/lib/parallel/util.ts: -------------------------------------------------------------------------------- 1 | import { debuglog } from 'node:util'; 2 | import { getProperty } from '../utils.js'; 3 | 4 | const debug = debuglog('@eggjs/mock/lib/parallel/util'); 5 | 6 | export const MOCK_APP_METHOD = [ 7 | 'ready', 8 | 'isClosed', 9 | 'closed', 10 | 'close', 11 | 'on', 12 | 'once', 13 | ]; 14 | 15 | export const APP_INIT = Symbol('appInit'); 16 | 17 | export function proxyApp(app: any) { 18 | const proxyApp = new Proxy(app, { 19 | get(target, prop: string) { 20 | // don't delegate properties on MockAgent 21 | if (MOCK_APP_METHOD.includes(prop)) { 22 | return getProperty(target, prop); 23 | } 24 | if (!target[APP_INIT]) throw new Error(`can't get ${prop} before ready`); 25 | // it's asynchronous when agent and app are loading, 26 | // so should get the properties after loader ready 27 | debug('proxy handler.get %s', prop); 28 | return target._instance[prop]; 29 | }, 30 | set(target, prop: string, value) { 31 | if (MOCK_APP_METHOD.includes(prop)) return true; 32 | if (!target[APP_INIT]) throw new Error(`can't set ${prop} before ready`); 33 | debug('proxy handler.set %s', prop); 34 | target._instance[prop] = value; 35 | return true; 36 | }, 37 | defineProperty(target, prop: string, descriptor) { 38 | // can't define properties on MockAgent 39 | if (MOCK_APP_METHOD.includes(prop)) return true; 40 | if (!target[APP_INIT]) throw new Error(`can't defineProperty ${prop} before ready`); 41 | debug('proxy handler.defineProperty %s', prop); 42 | Object.defineProperty(target._instance, prop, descriptor); 43 | return true; 44 | }, 45 | deleteProperty(target, prop: string) { 46 | // can't delete properties on MockAgent 47 | if (MOCK_APP_METHOD.includes(prop)) return true; 48 | if (!target[APP_INIT]) throw new Error(`can't delete ${prop} before ready`); 49 | debug('proxy handler.deleteProperty %s', prop); 50 | delete target._instance[prop]; 51 | return true; 52 | }, 53 | getOwnPropertyDescriptor(target, prop: string) { 54 | if (MOCK_APP_METHOD.includes(prop)) return Object.getOwnPropertyDescriptor(target, prop); 55 | if (!target[APP_INIT]) throw new Error(`can't getOwnPropertyDescriptor ${prop} before ready`); 56 | debug('proxy handler.getOwnPropertyDescriptor %s', prop); 57 | return Object.getOwnPropertyDescriptor(target._instance, prop); 58 | }, 59 | getPrototypeOf(target) { 60 | if (!target[APP_INIT]) throw new Error('can\'t getPrototypeOf before ready'); 61 | debug('proxy handler.getPrototypeOf %s'); 62 | return Object.getPrototypeOf(target._instance); 63 | }, 64 | }); 65 | return proxyApp; 66 | } 67 | -------------------------------------------------------------------------------- /src/lib/prerequire.ts: -------------------------------------------------------------------------------- 1 | // const debug = require('util').debuglog('egg-mock/prerequire'); 2 | // const path = require('path'); 3 | // const { existsSync } = require('fs'); 4 | // const globby = require('globby'); 5 | 6 | // const cwd = process.cwd(); 7 | // const dirs = []; 8 | // if (existsSync(path.join(cwd, 'app'))) { 9 | // dirs.push('app/**/*.js'); 10 | // } 11 | // // avoid Error: ENOENT: no such file or directory, scandir 12 | // if (existsSync(path.join(cwd, 'config'))) { 13 | // dirs.push('config/**/*.js'); 14 | // } 15 | // const files = globby.sync(dirs, { cwd }); 16 | 17 | // for (const file of files) { 18 | // const filepath = path.join(cwd, file); 19 | // try { 20 | // debug('%s prerequire %s', process.pid, filepath); 21 | // require(filepath); 22 | // } catch (err) { 23 | // debug('prerequire error %s', err.message); 24 | // } 25 | // } 26 | -------------------------------------------------------------------------------- /src/lib/request_call_function.ts: -------------------------------------------------------------------------------- 1 | import httpClient from 'urllib'; 2 | 3 | const { port, method, args, property, needResult } = JSON.parse(process.argv[2]); 4 | const url = `http://127.0.0.1:${port}/__egg_mock_call_function`; 5 | 6 | httpClient.request(url, { 7 | method: 'POST', 8 | data: { 9 | method, 10 | args, 11 | property, 12 | needResult, 13 | }, 14 | contentType: 'json', 15 | dataType: 'json', 16 | }).then(({ data }) => { 17 | if (!data.success) { 18 | // console.log('POST %s error, method: %s, args: %j, result data: %j', 19 | // url, method, args, data); 20 | if (data.error) { 21 | console.error(data.error); 22 | } else if (data.message) { 23 | const err = new Error(data.message); 24 | err.stack = data.stack; 25 | console.error(err); 26 | } 27 | process.exit(2); 28 | } 29 | 30 | if (data.result) { 31 | console.log('%j', data.result); 32 | } 33 | process.exit(0); 34 | }).catch(err => { 35 | // ignore ECONNREFUSED error on mockRestore 36 | if (method === 'mockRestore' && err.message.includes('ECONNREFUSED')) { 37 | process.exit(0); 38 | } 39 | 40 | console.error('POST %s error, method: %s, args: %j', url, method, args); 41 | console.error(err.stack); 42 | 43 | // ignore all error on mockRestore 44 | if (method === 'mockRestore') { 45 | process.exit(0); 46 | } else { 47 | process.exit(1); 48 | } 49 | }); 50 | -------------------------------------------------------------------------------- /src/lib/restore.ts: -------------------------------------------------------------------------------- 1 | import { debuglog } from 'node:util'; 2 | import { restore as mmRestore } from 'mm'; 3 | import { restoreMockAgent } from './mock_agent.js'; 4 | import { restore as clusterRestore } from './cluster.js'; 5 | 6 | const debug = debuglog('@eggjs/mock/lib/restore'); 7 | 8 | export async function restore() { 9 | // keep mm.restore execute in the current event loop 10 | mmRestore(); 11 | await clusterRestore(); 12 | await restoreMockAgent(); 13 | debug('restore all'); 14 | } 15 | -------------------------------------------------------------------------------- /src/lib/start-cluster.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import assert from 'node:assert'; 4 | import { debuglog } from 'node:util'; 5 | import { importModule } from '@eggjs/utils'; 6 | import { isAsyncFunction } from 'is-type-of'; 7 | 8 | const debug = debuglog('@eggjs/mock/lib/start-cluster'); 9 | 10 | // if (process.env.EGG_BIN_PREREQUIRE) { 11 | // require('./prerequire'); 12 | // } 13 | 14 | async function main() { 15 | const options = JSON.parse(process.argv[2]); 16 | debug('startCluster with options: %o', options); 17 | const { startCluster } = await importModule(options.framework); 18 | assert(isAsyncFunction(startCluster), 19 | `framework(${options.framework}) should export startCluster as an async function`); 20 | await startCluster(options); 21 | } 22 | 23 | main(); 24 | -------------------------------------------------------------------------------- /src/lib/supertest.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { readJSONSync } from 'utility'; 3 | import { Request, Test } from '@eggjs/supertest'; 4 | import { createServer } from './mock_http_server.js'; 5 | import { getSourceDirname } from './utils.js'; 6 | 7 | // patch from https://github.com/visionmedia/supertest/blob/199506d8dbfe0bb1434fc07c38cdcd1ab4c7c926/index.js#L19 8 | 9 | let pkgVersion = ''; 10 | 11 | /** 12 | * Test against the given `app`, 13 | * returning a new `Test`. 14 | */ 15 | export class EggTestRequest extends Request { 16 | #app: any; 17 | 18 | constructor(app: any) { 19 | super(createServer(app)); 20 | this.#app = app; 21 | } 22 | 23 | protected _testRequest(method: string, url: string): Test { 24 | // support pathFor(url) 25 | if (url[0] !== '/') { 26 | const realUrl = this.#app.router.pathFor(url); 27 | if (!realUrl) { 28 | throw new Error(`Can\'t find router:${url}, please check your \'app/router.js\'`); 29 | } 30 | url = realUrl; 31 | } 32 | const test = super._testRequest(method, url); 33 | if (!pkgVersion) { 34 | const pkgFile = path.join(getSourceDirname(), '../package.json'); 35 | const pkg = readJSONSync(pkgFile); 36 | pkgVersion = pkg.version; 37 | } 38 | test.set('User-Agent', `@eggjs/mock/${pkgVersion} Node.js/${process.version}`); 39 | return test; 40 | } 41 | } 42 | 43 | export function request(app: any) { 44 | return new EggTestRequest(app); 45 | } 46 | -------------------------------------------------------------------------------- /src/lib/tmp/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggjs/mock/b8e927c40bdde0a576a23a359cb9ecea9569ee3b/src/lib/tmp/.gitkeep -------------------------------------------------------------------------------- /src/lib/tmp/empty.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggjs/mock/b8e927c40bdde0a576a23a359cb9ecea9569ee3b/src/lib/tmp/empty.ts -------------------------------------------------------------------------------- /src/lib/types.ts: -------------------------------------------------------------------------------- 1 | export interface MockOptions { 2 | /** 3 | * The directory of the application 4 | */ 5 | baseDir?: string; 6 | 7 | /** 8 | * Custom you plugins 9 | */ 10 | plugins?: any; 11 | 12 | /** 13 | * The directory of the egg framework 14 | * 15 | * Set to `true` to use the current directory as framework directory 16 | */ 17 | framework?: string | boolean; 18 | 19 | /** 20 | * current test on plugin 21 | */ 22 | plugin?: boolean; 23 | 24 | /** 25 | * @deprecated please use framework instead 26 | */ 27 | customEgg?: string | boolean; 28 | 29 | /** 30 | * Cache application based on baseDir 31 | */ 32 | cache?: boolean; 33 | 34 | /** 35 | * Switch on process coverage, but it'll be slower 36 | */ 37 | coverage?: boolean; 38 | 39 | /** 40 | * Remove $baseDir/logs and $baseDir/run before start, default is `true` 41 | */ 42 | clean?: boolean; 43 | 44 | /** 45 | * default options.mockCtxStorage value on each mockContext 46 | */ 47 | mockCtxStorage?: boolean; 48 | 49 | beforeInit?: (app: any) => Promise; 50 | } 51 | 52 | export interface MockClusterOptions extends MockOptions { 53 | workers?: number | string; 54 | cache?: boolean; 55 | port?: number; 56 | /** 57 | * opt pass to coffee, such as { execArgv: ['--debug'] } 58 | */ 59 | opt?: object; 60 | startMode?: 'process' | 'worker_threads'; 61 | } 62 | 63 | /** 64 | * @deprecated please use MockOptions instead 65 | * keep this for compatible 66 | */ 67 | export type MockOption = MockOptions; 68 | 69 | export interface MockApplicationOptions extends MockOptions { 70 | baseDir: string; 71 | framework: string; 72 | clusterPort?: number; 73 | } 74 | 75 | export interface MockClusterApplicationOptions extends MockClusterOptions { 76 | baseDir: string; 77 | framework: string; 78 | port: number; 79 | } 80 | 81 | export type { 82 | MockResultOptions, ResultObject, 83 | MockResponseCallbackOptions, MockResultFunction, 84 | MockHttpClientMethod, 85 | } from './mock_httpclient.js'; 86 | 87 | export type { 88 | MockAgent, 89 | } from 'urllib'; 90 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { rm } from 'node:fs/promises'; 2 | import { rmSync } from 'node:fs'; 3 | import path from 'node:path'; 4 | import { fileURLToPath } from 'node:url'; 5 | import { scheduler } from 'node:timers/promises'; 6 | 7 | export function getSourceDirname() { 8 | if (typeof __dirname !== 'undefined') { 9 | return path.dirname(__dirname); 10 | } 11 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 12 | // @ts-ignore 13 | return path.dirname(path.dirname(fileURLToPath(import.meta.url))); 14 | } 15 | 16 | export async function sleep(delay: number) { 17 | await scheduler.wait(delay); 18 | } 19 | 20 | export async function rimraf(filepath: string) { 21 | await rm(filepath, { force: true, recursive: true }); 22 | } 23 | 24 | export function rimrafSync(filepath: string) { 25 | rmSync(filepath, { force: true, recursive: true }); 26 | } 27 | 28 | export function getProperty(target: any, prop: PropertyKey) { 29 | const member = target[prop]; 30 | if (typeof member === 'function') { 31 | return member.bind(target); 32 | } 33 | return member; 34 | } 35 | 36 | export function getEggOptions() { 37 | const options = { 38 | baseDir: process.env.EGG_BASE_DIR ?? process.cwd(), 39 | framework: process.env.EGG_FRAMEWORK, 40 | }; 41 | return options; 42 | } 43 | 44 | // const hasOwnProperty = Object.prototype.hasOwnProperty; 45 | 46 | // /** 47 | // * Merge the property descriptors of `src` into `dest` 48 | // * 49 | // * @param {object} dest Object to add descriptors to 50 | // * @param {object} src Object to clone descriptors from 51 | // * @param {boolean} [redefine=true] Redefine `dest` properties with `src` properties 52 | // * @return {object} Reference to dest 53 | // * @public 54 | // */ 55 | 56 | // function merge(dest, src, redefine) { 57 | // if (!dest) { 58 | // throw new TypeError('argument dest is required'); 59 | // } 60 | 61 | // if (!src) { 62 | // throw new TypeError('argument src is required'); 63 | // } 64 | 65 | // if (redefine === undefined) { 66 | // // Default to true 67 | // redefine = true; 68 | // } 69 | 70 | // Object.getOwnPropertyNames(src).forEach(function forEachOwnPropertyName(name) { 71 | // if (!redefine && hasOwnProperty.call(dest, name)) { 72 | // // Skip descriptor 73 | // return; 74 | // } 75 | 76 | // // Copy descriptor 77 | // const descriptor = Object.getOwnPropertyDescriptor(src, name); 78 | // Object.defineProperty(dest, name, descriptor); 79 | // }); 80 | 81 | // return dest; 82 | // } 83 | -------------------------------------------------------------------------------- /src/register.ts: -------------------------------------------------------------------------------- 1 | import { debuglog } from 'node:util'; 2 | import { createRequire } from 'node:module'; 3 | import { mock } from './index.js'; 4 | import { setupAgent, closeAgent } from './lib/agent_handler.js'; 5 | import { getApp } from './lib/app_handler.js'; 6 | import { injectContext } from './lib/inject_context.js'; 7 | 8 | const debug = debuglog('@eggjs/mock/register'); 9 | 10 | export async function mochaGlobalSetup() { 11 | debug('mochaGlobalSetup, agent.setupAgent() start'); 12 | await setupAgent(); 13 | debug('mochaGlobalSetup, agent.setupAgent() end'); 14 | } 15 | 16 | export async function mochaGlobalTeardown() { 17 | debug('mochaGlobalTeardown, agent.closeAgent() start'); 18 | await closeAgent(); 19 | debug('mochaGlobalTeardown, agent.closeAgent() end'); 20 | } 21 | 22 | export const mochaHooks = { 23 | async beforeAll() { 24 | const app = await getApp(); 25 | debug('mochaHooks.beforeAll call, _app: %s', app); 26 | if (app) { 27 | await app.ready(); 28 | } 29 | }, 30 | async afterEach() { 31 | const app = await getApp(); 32 | debug('mochaHooks.afterEach call, _app: %s', app); 33 | if (app) { 34 | await app.backgroundTasksFinished(); 35 | } 36 | await mock.restore(); 37 | }, 38 | async afterAll() { 39 | // skip auto app close on parallel 40 | if (process.env.ENABLE_MOCHA_PARALLEL) return; 41 | const app = await getApp(); 42 | debug('mochaHooks.afterAll call, _app: %s', app); 43 | if (app) { 44 | await app.close(); 45 | } 46 | }, 47 | }; 48 | 49 | /** 50 | * Find active node mocha instances. 51 | */ 52 | function findNodeJSMocha() { 53 | let children: any; 54 | if (typeof require === 'function') { 55 | children = require.cache || {}; 56 | } else { 57 | // FIXME: not work on ESM 58 | children = createRequire(process.cwd()).cache || {}; 59 | debug('createRequire on esm'); 60 | } 61 | 62 | return Object.keys(children) 63 | .filter(function(child) { 64 | const val = children[child].exports; 65 | return typeof val === 'function' && val.name === 'Mocha'; 66 | }) 67 | .map(function(child) { 68 | return children[child].exports; 69 | }); 70 | } 71 | 72 | import 'mocha'; 73 | 74 | const modules = findNodeJSMocha(); 75 | // console.error('modules length: %s', modules.length); 76 | 77 | for (const module of modules) { 78 | if (!module) continue; 79 | injectContext(module); 80 | } 81 | -------------------------------------------------------------------------------- /src/typings/index.d.ts: -------------------------------------------------------------------------------- 1 | // make sure to import egg typings and let typescript know about it 2 | // @see https://github.com/whxaxes/blog/issues/11 3 | // and https://www.typescriptlang.org/docs/handbook/declaration-merging.html 4 | import 'egg'; 5 | -------------------------------------------------------------------------------- /test/agent.test.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import { strict as assert } from 'node:assert'; 4 | import mm, { MockApplication } from '../src/index.js'; 5 | import { rimraf } from '../src/lib/utils.js'; 6 | import { getFixtures } from './helper.js'; 7 | 8 | const baseDir = getFixtures('agent'); 9 | 10 | describe('test/agent.test.ts', () => { 11 | let app: MockApplication; 12 | afterEach(() => app.close()); 13 | afterEach(mm.restore); 14 | 15 | it('mock agent ok', async () => { 16 | const filepath = path.join(baseDir, 'run/test.txt'); 17 | await rimraf(filepath); 18 | 19 | app = mm.app({ 20 | baseDir, 21 | }); 22 | 23 | await app.ready(); 24 | assert(fs.readFileSync(filepath, 'utf8') === '123'); 25 | }); 26 | 27 | it('mock agent again ok', done => { 28 | app = mm.app({ 29 | baseDir, 30 | }); 31 | app.ready(done); 32 | }); 33 | 34 | it('should cluster-client work', done => { 35 | app = mm.app({ baseDir }); 36 | app.ready(() => { 37 | app._agent.client.subscribe('agent sub', (data: string) => { 38 | assert(data === 'agent sub'); 39 | 40 | app.client.subscribe('app sub', (data: string) => { 41 | assert(data === 'app sub'); 42 | done(); 43 | }); 44 | }); 45 | }); 46 | }); 47 | 48 | it('should agent work ok after ready', async function() { 49 | app = mm.app({ baseDir }); 50 | await app.ready(); 51 | assert(app._agent.type === 'agent'); 52 | }); 53 | 54 | it.skip('should FrameworkErrorformater work during agent boot (configWillLoad)', async function() { 55 | // let logMsg = ''; 56 | let catchErr: any; 57 | // mm(process.stderr, 'write', (msg: string) => { 58 | // logMsg = msg; 59 | // }); 60 | try { 61 | app = mm.app({ baseDir: getFixtures('agent-boot-error') }); 62 | await app.ready(); 63 | } catch (err) { 64 | catchErr = err; 65 | } 66 | 67 | assert(catchErr.code === 'customPlugin_99'); 68 | // console.log(logMsg); 69 | // assert(/framework\.CustomError\: mock error \[ https\:\/\/eggjs\.org\/zh-cn\/faq\/customPlugin_99 \]/.test(logMsg)); 70 | }); 71 | 72 | it('should FrameworkErrorformater work during agent boot ready (didLoad)', async function() { 73 | let logMsg = ''; 74 | let catchErr: any; 75 | mm(process.stderr, 'write', (msg: string) => { 76 | logMsg = msg; 77 | }); 78 | app = mm.app({ baseDir: getFixtures('agent-boot-ready-error') }); 79 | try { 80 | await app.ready(); 81 | } catch (err) { 82 | catchErr = err; 83 | } 84 | 85 | assert(catchErr.code === 'customPlugin_99'); 86 | assert(/framework\.CustomError\: mock error \[ https\:\/\/eggjs\.org\/zh-cn\/faq\/customPlugin_99 \]/.test(logMsg)); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /test/app/middleware/cluster_app_mock.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import mm, { MockApplication } from '../../../src/index.js'; 3 | 4 | describe('test/app/middleware/cluster_app_mock.test.ts', () => { 5 | let app: MockApplication; 6 | before(() => { 7 | app = mm.app({ 8 | baseDir: 'demo', 9 | }); 10 | return app.ready(); 11 | }); 12 | after(() => app.close()); 13 | 14 | afterEach(mm.restore); 15 | 16 | it('should return 422 when method missing', () => { 17 | return app.httpRequest() 18 | .post('/__egg_mock_call_function') 19 | .send({}) 20 | .expect(422) 21 | .expect({ 22 | success: false, 23 | error: 'Missing method', 24 | }); 25 | }); 26 | 27 | it('should return 422 when args is not Array', () => { 28 | return app.httpRequest() 29 | .post('/__egg_mock_call_function') 30 | .send({ method: 'foo', args: 'hi' }) 31 | .expect(422) 32 | .expect({ 33 | success: false, 34 | error: 'args should be an Array instance', 35 | }); 36 | }); 37 | 38 | it('should return 422 when method is not exists on app', () => { 39 | return app.httpRequest() 40 | .post('/__egg_mock_call_function') 41 | .send({ method: 'not_exists_method', args: [] }) 42 | .expect(422) 43 | .expect({ 44 | success: false, 45 | error: 'method "not_exists_method" not exists on app', 46 | }); 47 | }); 48 | 49 | it('should recover error instance', async () => { 50 | let called = false; 51 | let callError: any; 52 | mm(app, 'foo', (_a: any, err: Error) => { called = true; callError = err; }); 53 | 54 | const err = { 55 | __egg_mock_type: 'error', 56 | name: 'FooError', 57 | message: 'foo error fire', 58 | stack: 'error stack', 59 | foo: 'bar', 60 | }; 61 | await app.httpRequest() 62 | .post('/__egg_mock_call_function') 63 | .send({ method: 'foo', args: [ 1, err ] }) 64 | .expect(200) 65 | .expect({ 66 | success: true, 67 | }); 68 | 69 | assert(called); 70 | assert(callError.name === 'FooError'); 71 | assert(callError.stack === 'error stack'); 72 | assert(callError.message === 'foo error fire'); 73 | assert(callError.foo === 'bar'); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /test/app_event.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert'; 2 | import { scheduler } from 'node:timers/promises'; 3 | import { pending } from 'pedding'; 4 | import mm, { MockApplication } from '../src/index.js'; 5 | import { getFixtures } from './helper.js'; 6 | 7 | const baseDir = getFixtures('app-event'); 8 | 9 | describe('test/app_event.test.ts', () => { 10 | afterEach(mm.restore); 11 | 12 | describe('after ready', () => { 13 | let app: MockApplication; 14 | before(() => { 15 | app = mm.app({ 16 | baseDir, 17 | cache: false, 18 | }); 19 | return app.ready(); 20 | }); 21 | after(async () => { 22 | await app.close(); 23 | }); 24 | 25 | it('should listen by eventByRequest', done => { 26 | done = pending(3, done); 27 | app.once('eventByRequest', done); 28 | app.on('eventByRequest', done); 29 | 30 | app.httpRequest() 31 | .get('/event') 32 | .expect(200) 33 | .expect('done', done); 34 | }); 35 | }); 36 | 37 | describe('before ready', () => { 38 | let app: MockApplication; 39 | beforeEach(() => { 40 | app = mm.app({ 41 | baseDir, 42 | cache: false, 43 | }); 44 | }); 45 | afterEach(() => app.ready()); 46 | afterEach(() => app.close()); 47 | 48 | it('should listen after app ready', done => { 49 | done = pending(2, done); 50 | app.once('appReady', done); 51 | app.on('appReady', done); 52 | }); 53 | 54 | it('should listen after app instantiate', done => { 55 | done = pending(2, done); 56 | app.once('appInstantiated', done); 57 | app.on('appInstantiated', done); 58 | }); 59 | }); 60 | 61 | describe('throw before app init', () => { 62 | let app: MockApplication; 63 | beforeEach(() => { 64 | const baseDir = getFixtures('app'); 65 | const customEgg = getFixtures('error-framework'); 66 | app = mm.app({ 67 | baseDir, 68 | customEgg, 69 | cache: false, 70 | }); 71 | }); 72 | afterEach(() => app.close()); 73 | 74 | it('should listen using app.on', done => { 75 | app.on('error', err => { 76 | assert.equal(err.message, 'start error'); 77 | done(); 78 | }); 79 | }); 80 | 81 | it('should listen using app.once', done => { 82 | app.once('error', err => { 83 | assert(err.message === 'start error'); 84 | done(); 85 | }); 86 | }); 87 | 88 | it('should throw error from ready', async () => { 89 | try { 90 | await app.ready(); 91 | } catch (err: any) { 92 | assert(err.message === 'start error'); 93 | } 94 | }); 95 | 96 | it('should close when app init failed', async () => { 97 | app.once('error', () => {}); 98 | await scheduler.wait(1000); 99 | // app._app is undefined 100 | await app.close(); 101 | }); 102 | }); 103 | }); 104 | -------------------------------------------------------------------------------- /test/bootstrap-plugin.test.ts: -------------------------------------------------------------------------------- 1 | import coffee from 'coffee'; 2 | import { mock, restore } from 'mm'; 3 | import { getFixtures } from './helper.js'; 4 | 5 | describe('test/bootstrap-plugin.test.ts', () => { 6 | after(() => restore()); 7 | 8 | it('should throw error on plugin project', () => { 9 | mock(process.env, 'EGG_BASE_DIR', getFixtures('plugin-bootstrap')); 10 | const testFile = getFixtures('plugin-bootstrap/test.js'); 11 | 12 | return coffee.fork(testFile) 13 | .debug() 14 | .expect('stderr', /DO NOT USE bootstrap to test plugin/) 15 | .expect('code', 1) 16 | .end(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/bootstrap.test.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import coffee from 'coffee'; 3 | import { importResolve } from '@eggjs/utils'; 4 | import { getFixtures } from './helper.js'; 5 | 6 | describe('test/bootstrap.test.ts', () => { 7 | describe('normal app in ESM', () => { 8 | it('should work', async () => { 9 | const eggBinFile = path.join(importResolve('@eggjs/bin/package.json'), '../bin/run.js'); 10 | await coffee.fork(eggBinFile, [ 11 | 'test', 12 | '--no-typescript', 13 | '-r', getFixtures('../../dist/esm/register.js'), 14 | ], { 15 | cwd: getFixtures('apps/helloworld'), 16 | }) 17 | .debug() 18 | .expect('code', 0) 19 | .expect('stdout', /\d+ passing/) 20 | .end(); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/ctx.test.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { strict as assert } from 'node:assert'; 3 | import { getFixtures } from './helper.js'; 4 | import mm, { MockApplication } from '../src/index.js'; 5 | 6 | const fixtures = getFixtures(''); 7 | 8 | describe('test/ctx.test.ts', () => { 9 | afterEach(mm.restore); 10 | 11 | let app: MockApplication; 12 | before(done => { 13 | app = mm.app({ 14 | baseDir: path.join(fixtures, 'demo'), 15 | }); 16 | app.ready(done); 17 | }); 18 | after(() => app.close()); 19 | 20 | it('should has logger, app, request', () => { 21 | const ctx = app.mockContext(); 22 | assert(ctx.app instanceof Object); 23 | assert(ctx.logger instanceof Object); 24 | assert(ctx.coreLogger instanceof Object); 25 | assert(ctx.request.url === '/'); 26 | assert(ctx.request.ip === '127.0.0.1'); 27 | }); 28 | 29 | it('should ctx.ip work', () => { 30 | const ctx = app.mockContext(); 31 | ctx.request.headers['x-forwarded-for'] = ''; 32 | assert.equal(ctx.request.ip, '127.0.0.1'); 33 | }); 34 | 35 | it('should has services', async () => { 36 | const ctx = app.mockContext(); 37 | const data = await ctx.service.foo.get('foo'); 38 | assert.equal(data, 'bar'); 39 | }); 40 | 41 | it('should not override mockData', async () => { 42 | const mockData: any = { user: 'popomore' }; 43 | app.mockContext(mockData); 44 | app.mockContext(mockData); 45 | assert(!mockData.headers); 46 | assert(!mockData.method); 47 | }); 48 | 49 | describe('mockContextScope', () => { 50 | it('should not conflict with nest call', async () => { 51 | await app.mockContextScope(async (ctx: any) => { 52 | const currentStore = app.ctxStorage.getStore(); 53 | assert(ctx === currentStore); 54 | 55 | await app.mockContextScope(async (nestCtx: any) => { 56 | const currentStore = app.ctxStorage.getStore(); 57 | assert(nestCtx === currentStore); 58 | }); 59 | }); 60 | 61 | await app.mockContextScope(async () => { 62 | const ctx = app.ctxStorage.getStore(); 63 | await app.mockContextScope(async (newCtx: any) => { 64 | const currentStore = app.ctxStorage.getStore(); 65 | assert.equal(newCtx, currentStore); 66 | assert.notEqual(ctx, currentStore); 67 | }); 68 | }); 69 | }); 70 | 71 | it('should not conflict with concurrent call', async () => { 72 | await Promise.all([ 73 | await app.mockContextScope(async (ctx: any) => { 74 | const currentStore = app.ctxStorage.getStore(); 75 | assert(ctx === currentStore); 76 | }), 77 | await app.mockContextScope(async (ctx: any) => { 78 | const currentStore = app.ctxStorage.getStore(); 79 | assert(ctx === currentStore); 80 | }), 81 | ]); 82 | }); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /test/fixtures/agent-boot-error/agent.js: -------------------------------------------------------------------------------- 1 | const { FrameworkBaseError } = require('egg-errors'); 2 | 3 | class CustomError extends FrameworkBaseError { 4 | get module() { 5 | return 'customPlugin'; 6 | } 7 | } 8 | 9 | module.exports = class Boot { 10 | async configWillLoad() { 11 | console.error('mock error'); 12 | throw new CustomError('mock error', 99); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /test/fixtures/agent-boot-error/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "agent-boot-error" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/agent-boot-ready-error/agent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { FrameworkBaseError } = require('egg-errors'); 4 | class CustomError extends FrameworkBaseError { 5 | get module() { 6 | return 'customPlugin'; 7 | } 8 | } 9 | 10 | module.exports = class { 11 | async didLoad() { 12 | throw new CustomError('mock error', 99); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /test/fixtures/agent-boot-ready-error/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "agent-boot-ready-error" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/agent/agent.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const Client = require('./client'); 4 | 5 | module.exports = agent => { 6 | const done = agent.readyCallback('agent:agent'); 7 | const p = path.join(__dirname, 'run/test.txt'); 8 | fs.mkdirSync(path.join(__dirname, 'run'), { recursive: true }); 9 | fs.writeFile(p, '123', done); 10 | 11 | agent.client = agent.cluster(Client).create(); 12 | }; 13 | -------------------------------------------------------------------------------- /test/fixtures/agent/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const Client = require('./client'); 6 | 7 | module.exports = function(app) { 8 | const done = app.readyCallback('agent:app'); 9 | const p = path.join(__dirname, 'run/test.txt'); 10 | setTimeout(() => { 11 | fs.readFile(p, 'utf-8', done); 12 | }, 1000); 13 | 14 | app.client = app.cluster(Client).create(); 15 | }; 16 | -------------------------------------------------------------------------------- /test/fixtures/agent/client.js: -------------------------------------------------------------------------------- 1 | const { Base } = require('sdk-base'); 2 | 3 | class Client extends Base { 4 | constructor() { 5 | super(); 6 | this.ready(true); 7 | } 8 | 9 | subscribe(topic, listener) { 10 | setTimeout(() => listener(topic), 10); 11 | } 12 | } 13 | 14 | module.exports = Client; 15 | -------------------------------------------------------------------------------- /test/fixtures/agent/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: '123', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/agent/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/app-boot-error/app.js: -------------------------------------------------------------------------------- 1 | const { FrameworkBaseError } = require('egg-errors'); 2 | 3 | class CustomError extends FrameworkBaseError { 4 | get module() { 5 | return 'customPlugin'; 6 | } 7 | } 8 | 9 | module.exports = class AppBootHook { 10 | configWillLoad() { 11 | throw new CustomError('mock error', 99); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /test/fixtures/app-boot-error/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-boot-error" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/app-boot-ready-error/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { FrameworkBaseError } = require('egg-errors'); 4 | class CustomError extends FrameworkBaseError { 5 | get module() { 6 | return 'customPlugin'; 7 | } 8 | } 9 | 10 | module.exports = class { 11 | async didLoad() { 12 | throw new CustomError('mock error', 99); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /test/fixtures/app-boot-ready-error/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-boot-ready-error" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/app-event/agent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.ready(() => { 5 | app.emit('agentInstantiated'); 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/app-event/app.js: -------------------------------------------------------------------------------- 1 | const { scheduler } = require('node:timers/promises'); 2 | 3 | module.exports = app => { 4 | app.ready(() => { 5 | // after ready 6 | console.log('emit appReady event in app.js'); 7 | app.emit('appReady'); 8 | }); 9 | console.log('register ready event in app.js'); 10 | 11 | process.nextTick(() => { 12 | // before ready, after app instantiate 13 | app.emit('appInstantiated'); 14 | }); 15 | 16 | app.beforeStart(async function() { 17 | await scheduler.wait(1000); 18 | }); 19 | }; 20 | -------------------------------------------------------------------------------- /test/fixtures/app-event/app/router.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | app.get('/event', async function() { 3 | this.app.emit('eventByRequest'); 4 | this.body = 'done'; 5 | }); 6 | }; 7 | -------------------------------------------------------------------------------- /test/fixtures/app-event/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: '123', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/app-event/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-event" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/app-fail/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | throw new Error('load error'); 4 | -------------------------------------------------------------------------------- /test/fixtures/app-fail/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: '123', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/app-fail/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "egg-mock" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/app-proxy-ready/agent.js: -------------------------------------------------------------------------------- 1 | const { scheduler } = require('node:timers/promises'); 2 | 3 | module.exports = app => { 4 | // set timeout let testcase ran before ready 5 | app.beforeStart(async function() { 6 | await scheduler.wait(1000); 7 | }); 8 | }; 9 | -------------------------------------------------------------------------------- /test/fixtures/app-proxy-ready/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.emit('eventOnReady'); 5 | app.emit('eventOnReady'); 6 | app.emit('eventOnceReady'); 7 | app.emit('eventOnceReady'); 8 | }; 9 | -------------------------------------------------------------------------------- /test/fixtures/app-proxy-ready/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: '123', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/app-proxy-ready/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-proxy-ready" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/app-proxy/app/extend/application.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | get getter() { 5 | return 'getter'; 6 | }, 7 | method() { 8 | return 'method'; 9 | }, 10 | prop: 1, 11 | shouldBeDelete: true, 12 | 13 | get a() { 14 | return 'a'; 15 | }, 16 | set a(x) { 17 | this._a = x; 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /test/fixtures/app-proxy/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: '123', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/app-proxy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-proxy" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/app-ready-failed/app.js: -------------------------------------------------------------------------------- 1 | module.exports = class AppHook { 2 | 3 | async didLoad() { 4 | throw new Error('mock app ready failed'); 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /test/fixtures/app-ready-failed/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: '123', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/app-ready-failed/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/app-ready-failed/test/index.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const { app } = require('../../../../dist/commonjs/bootstrap'); 3 | 4 | describe('test for app ready failed', () => { 5 | it('should not print', () => { 6 | // ... 7 | assert(app); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /test/fixtures/app/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.on('server', server => { 5 | app.serverKeepAliveTimeout = server.keepAliveTimeout || 5000; 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/app/app/router.js: -------------------------------------------------------------------------------- 1 | const { scheduler } = require('node:timers/promises'); 2 | 3 | module.exports = app => { 4 | app.get('/', async ctx => { 5 | ctx.body = 'foo'; 6 | }); 7 | 8 | app.get('/keepAliveTimeout', async ctx => { 9 | ctx.body = { 10 | keepAliveTimeout: ctx.app.serverKeepAliveTimeout, 11 | }; 12 | }); 13 | 14 | app.get('/ua', async ctx => { 15 | ctx.body = ctx.get('user-agent'); 16 | }); 17 | 18 | app.get('/logger', async ctx => { 19 | ctx.logger.info('[app.expectLog() test] ok'); 20 | ctx.coreLogger.info('[app.expectLog(coreLogger) test] ok'); 21 | ctx.body = { ok: true }; 22 | }); 23 | 24 | 25 | let counter = 0; 26 | app.get('/counter', async ctx => { 27 | ctx.body = { counter }; 28 | }); 29 | 30 | app.get('/counter/plus', async ctx => { 31 | ctx.runInBackground(async ctx => { 32 | // mock io delay 33 | await scheduler.wait(10); 34 | if (ctx.superMan) { 35 | counter += 10; 36 | return; 37 | } 38 | counter++; 39 | }); 40 | ctx.body = { counter }; 41 | }); 42 | 43 | app.get('/counter/minus', async ctx => { 44 | ctx.runInBackground(async () => { 45 | await scheduler.wait(10); 46 | counter--; 47 | }); 48 | ctx.body = { counter }; 49 | }); 50 | 51 | app.get('/counter/plusplus', async ctx => { 52 | ctx.runInBackground(async ctx => { 53 | // mock io delay 54 | await scheduler.wait(10); 55 | if (ctx.superMan) { 56 | counter += 10; 57 | } else { 58 | counter++; 59 | } 60 | ctx.runInBackground(async ctx => { 61 | // mock io delay 62 | await scheduler.wait(10); 63 | if (ctx.superMan) { 64 | counter += 10; 65 | } else { 66 | counter++; 67 | } 68 | }); 69 | }); 70 | ctx.body = { counter }; 71 | }); 72 | }; 73 | -------------------------------------------------------------------------------- /test/fixtures/app/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: '123', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/app-not-clean/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: '123', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/apps/app-not-clean/logs/keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggjs/mock/b8e927c40bdde0a576a23a359cb9ecea9569ee3b/test/fixtures/apps/app-not-clean/logs/keep -------------------------------------------------------------------------------- /test/fixtures/apps/app-not-clean/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-not-clean" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/app-throw/app/router.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | app.get('/throw', async function() { 3 | this.body = 'foo'; 4 | setTimeout(() => { 5 | /* eslint-disable-next-line */ 6 | a.b = c; 7 | }, 1); 8 | }); 9 | 10 | app.get('/throw-unhandledRejection', async function() { 11 | this.body = 'foo'; 12 | new Promise((resolve, reject) => { 13 | reject(new Error('foo reject error')); 14 | }); 15 | }); 16 | 17 | app.get('/throw-unhandledRejection-string', async function() { 18 | this.body = 'foo'; 19 | new Promise((resolve, reject) => { 20 | reject(new Error('foo reject string error')); 21 | }); 22 | }); 23 | 24 | app.get('/throw-unhandledRejection-obj', async function() { 25 | this.body = 'foo'; 26 | new Promise((resolve, reject) => { 27 | const err = { 28 | name: 'TypeError', 29 | message: 'foo reject obj error', 30 | stack: new Error().stack, 31 | toString() { 32 | return this.name + ': ' + this.message; 33 | }, 34 | }; 35 | reject(err); 36 | }); 37 | }); 38 | }; 39 | -------------------------------------------------------------------------------- /test/fixtures/apps/app-throw/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'test key'; 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/app-throw/config/config.unittest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.logger = { 4 | consoleLevel: 'NONE', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/apps/app-throw/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-throw" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/barapp/app/controller/home.js: -------------------------------------------------------------------------------- 1 | module.exports = async function() { 2 | this.body = { 3 | foo: this.app.config.foo, 4 | foobar: this.app.config.foobar, 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /test/fixtures/apps/barapp/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app) { 4 | app.get('/', app.controller.home); 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/apps/barapp/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.foo = 'bar'; 4 | 5 | exports.keys = '123'; 6 | -------------------------------------------------------------------------------- /test/fixtures/apps/barapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "barapp" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/env-app/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.fakeplugin = { 4 | foo: 'bar-default', 5 | }; 6 | 7 | exports.logger = { 8 | consoleLevel: 'NONE', 9 | }; 10 | 11 | exports.development = { 12 | fastReady: false, 13 | }; 14 | 15 | exports.keys = '123'; 16 | -------------------------------------------------------------------------------- /test/fixtures/apps/env-app/config/config.local.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.development = { 4 | fastReady: false, 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/apps/env-app/config/config.prod.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.fakeplugin = { 4 | foo: 'bar-prod', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/apps/env-app/config/config.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.fakeplugin = { 4 | foo: 'bar-test', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/apps/env-app/config/config.unittest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.fakeplugin = { 4 | foo: 'bar-unittest', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/apps/env-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "env-app" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/foo/app/router.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | app.get('/', async function() { 3 | this.body = { 4 | fooPlugin: app.fooPlugin, 5 | }; 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/apps/foo/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = '123'; 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/foo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "foo" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/helloworld/app/router.js: -------------------------------------------------------------------------------- 1 | export default app => { 2 | app.get('/', async ctx => { 3 | ctx.body = { 4 | hello: 'world', 5 | }; 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/apps/helloworld/config/config.default.js: -------------------------------------------------------------------------------- 1 | export default { 2 | keys: '123456', 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/helloworld/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "egg": { 4 | "typescript": false 5 | }, 6 | "type": "module", 7 | "devDependencies": { 8 | "@eggjs/mock": "6" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/apps/helloworld/test/helloworld.test.js: -------------------------------------------------------------------------------- 1 | import { app } from '../../../../../dist/esm/bootstrap.js'; 2 | 3 | describe('bootstrap test', () => { 4 | it('should GET /', () => { 5 | return app.httpRequest() 6 | .get('/') 7 | .expect({ 8 | hello: 'world', 9 | }); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /test/fixtures/apps/mock_cookies/app/router.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | app.get('/', async function() { 3 | this.body = { 4 | cookieValue: this.cookies.get('foo', { signed: false }) || undefined, 5 | cookiesValue: this.cookies.get('foo', { signed: false }) || undefined, 6 | }; 7 | }); 8 | }; 9 | -------------------------------------------------------------------------------- /test/fixtures/apps/mock_cookies/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = '123'; 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/mock_cookies/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mockCookies" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/mockhome/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = '123'; 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/mockhome/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mockhome" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/no-framework/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | module.exports = { 6 | a: { 7 | enable: true, 8 | path: path.join(__dirname, '../plugin/a'), 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /test/fixtures/apps/no-framework/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "no-framework" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/no-framework/plugin/a/app/extend/application.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mockEnv() { 3 | this.config.env = 'mocked by plugin'; 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/apps/no-framework/plugin/a/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "eggPlugin": { 3 | "name": "a", 4 | "dependencies": [ 5 | "egg-mock" 6 | ] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/apps/parallel-test/node_modules/egg: -------------------------------------------------------------------------------- 1 | ../../../../../node_modules/egg -------------------------------------------------------------------------------- /test/fixtures/apps/parallel-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "foo" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/apps/parallel-test/test/a.test.js: -------------------------------------------------------------------------------- 1 | const mm = require('../../../../../'); 2 | const assert = require('assert'); 3 | 4 | describe('test/parallel_a.test.js', () => { 5 | it('should work', () => { 6 | mm(global, 'test', '233'); 7 | assert(global.test === '233'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /test/fixtures/apps/parallel-test/test/b.test.js: -------------------------------------------------------------------------------- 1 | const mm = require('../../../../../'); 2 | const assert = require('assert'); 3 | 4 | describe('test/parallel_b.test.js', () => { 5 | it('should work', () => { 6 | mm(global, 'test', '244'); 7 | assert(global.test === '244'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /test/fixtures/apps/parallel-test/test/c.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('test/parallel_a.test.js', () => { 4 | it('should work', () => { 5 | assert(!global.test); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /test/fixtures/bar/config/config.default.js: -------------------------------------------------------------------------------- 1 | exports.foobar = 'bar'; 2 | 3 | exports.keys = '123'; 4 | -------------------------------------------------------------------------------- /test/fixtures/bar/index.js: -------------------------------------------------------------------------------- 1 | const egg = require('egg'); 2 | 3 | const EGG_PATH = Symbol.for('egg#eggPath'); 4 | 5 | class BarApplication extends egg.Application { 6 | get [EGG_PATH]() { 7 | return __dirname; 8 | } 9 | } 10 | 11 | exports.Agent = egg.Agent; 12 | exports.Application = BarApplication; 13 | exports.startCluster = egg.startCluster; 14 | -------------------------------------------------------------------------------- /test/fixtures/bar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bar", 3 | "version": "1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/cache/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = '123'; 4 | -------------------------------------------------------------------------------- /test/fixtures/cache/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cache" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/chair/index.js: -------------------------------------------------------------------------------- 1 | const egg = require('egg'); 2 | 3 | async function startCluster(options) { 4 | // print for the testcase that will assert stdout 5 | console.log(options.eggPath); 6 | delete options.eggPath; 7 | await egg.startCluster(options); 8 | } 9 | 10 | exports.startCluster = startCluster; 11 | exports.Application = egg.Application; 12 | exports.Agent = egg.Agent; 13 | -------------------------------------------------------------------------------- /test/fixtures/chair/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chair" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/create-context-failed/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: '123', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/create-context-failed/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/create-context-failed/test/index.test.js: -------------------------------------------------------------------------------- 1 | const { setGetAppCallback } = require('../../../..'); 2 | 3 | setGetAppCallback(() => { 4 | return { 5 | ready: async () => { 6 | // ... 7 | }, 8 | mockContextScope: async () => { 9 | throw new Error('mock create context failed'); 10 | }, 11 | close: async () => { 12 | // ... 13 | }, 14 | backgroundTasksFinished: async () => { 15 | // ... 16 | }, 17 | }; 18 | }); 19 | 20 | describe('test case create context error', () => { 21 | it('should not print', () => { 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/fixtures/custom-loader/app/adapter/docker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class DockerAdapter { 4 | constructor(app) { 5 | this.app = app; 6 | } 7 | 8 | async inspectDocker() { 9 | return 'docker'; 10 | } 11 | 12 | } 13 | 14 | module.exports = DockerAdapter; 15 | -------------------------------------------------------------------------------- /test/fixtures/custom-loader/app/controller/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class UserController { 4 | constructor(ctx) { 5 | this.ctx = ctx; 6 | this.app = ctx.app; 7 | } 8 | 9 | async get() { 10 | this.ctx.body = { 11 | adapter: await this.app.adapter.docker.inspectDocker(), 12 | repository: await this.ctx.repository.user.get(), 13 | }; 14 | } 15 | } 16 | 17 | module.exports = UserController; 18 | -------------------------------------------------------------------------------- /test/fixtures/custom-loader/app/repository/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class UserRepository { 4 | constructor(ctx) { 5 | this.ctx = ctx; 6 | } 7 | 8 | async get() { 9 | return this.ctx.params.name; 10 | } 11 | 12 | } 13 | 14 | module.exports = UserRepository; 15 | -------------------------------------------------------------------------------- /test/fixtures/custom-loader/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = app => { 4 | app.router.get('/users/:name', app.controller.user.get); 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/custom-loader/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: '123', 5 | customLoader: { 6 | adapter: { 7 | directory: 'app/adapter', 8 | inject: 'app', 9 | }, 10 | repository: { 11 | directory: 'app/repository', 12 | inject: 'ctx', 13 | }, 14 | env: { 15 | directory: 'app/env', 16 | inject: 'ctx', 17 | }, 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /test/fixtures/custom-loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom-loader" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/custom_egg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/demo-async/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function() { 4 | 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/demo-async/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app) { 4 | class Home extends app.Controller { 5 | async testService() { 6 | this.ctx.body = { 7 | foo1: await this.service.foo.get(), 8 | foo2: await this.service.bar.foo.get(), 9 | foo3: this.service.foo.getSync(), 10 | thirdService: await this.service.third.bar.foo.get(), 11 | }; 12 | } 13 | } 14 | 15 | return Home; 16 | }; 17 | -------------------------------------------------------------------------------- /test/fixtures/demo-async/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app) { 4 | app.get('/service', app.controller.home.testService); 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/demo-async/app/service/bar/foo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app) { 4 | class Foo extends app.Service { 5 | async get() { 6 | return 'bar'; 7 | } 8 | } 9 | 10 | return Foo; 11 | }; 12 | -------------------------------------------------------------------------------- /test/fixtures/demo-async/app/service/foo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app) { 4 | class Foo extends app.Service { 5 | async get() { 6 | return 'bar'; 7 | } 8 | 9 | getSync() { 10 | return 'bar'; 11 | } 12 | } 13 | 14 | return Foo; 15 | }; 16 | -------------------------------------------------------------------------------- /test/fixtures/demo-async/app/service/third/bar/foo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app) { 4 | class Main extends app.Service { 5 | async get() { 6 | return 'third'; 7 | } 8 | } 9 | 10 | return Main; 11 | }; 12 | -------------------------------------------------------------------------------- /test/fixtures/demo-async/config/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | urllib: { 5 | keepAlive: false, 6 | }, 7 | logger: { 8 | consoleLevel: 'NONE', 9 | }, 10 | keys: '123', 11 | }; 12 | -------------------------------------------------------------------------------- /test/fixtures/demo-async/mocks_data/service/bar/foo/get/foobar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = 'foobar'; 4 | -------------------------------------------------------------------------------- /test/fixtures/demo-async/mocks_data/service/foo/get/foobar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = 'foobar'; 4 | -------------------------------------------------------------------------------- /test/fixtures/demo-async/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo-async" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/demo/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function() { 4 | 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/demo/app/context.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | getResult(result) { 5 | return { 6 | body: result, 7 | }; 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/demo/app/controller/file.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | module.exports = app => { 4 | return async function file(ctx) { 5 | const ctxFromStorage = app.ctxStorage.getStore(); 6 | assert(ctxFromStorage !== ctx); 7 | const stream = await ctx.getFileStream(); 8 | const fields = stream.fields; 9 | ctx.body = { 10 | fields, 11 | filename: stream.filename, 12 | user: ctx.user, 13 | traceId: ctx.traceId, 14 | ctxFromStorageUser: ctxFromStorage.user, 15 | ctxFromStorageTraceId: ctxFromStorage.traceId, 16 | }; 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /test/fixtures/demo/app/controller/home.js: -------------------------------------------------------------------------------- 1 | exports.get = function () { 2 | this.body = { 3 | cookieValue: this.getCookie('foo') || undefined, 4 | cookiesValue: this.cookies.get('foo') || undefined, 5 | sessionValue: this.session.foo, 6 | }; 7 | }; 8 | 9 | exports.post = function () { 10 | this.body = 'done'; 11 | }; 12 | 13 | exports.hello = function () { 14 | this.body = 'hi'; 15 | }; 16 | 17 | exports.service = async ctx => { 18 | ctx.body = { 19 | foo1: await ctx.service.foo.get(), 20 | foo2: await ctx.service.bar.foo.get(), 21 | foo3: ctx.service.foo.getSync(), 22 | thirdService: await ctx.service.third.bar.foo.get(), 23 | }; 24 | }; 25 | 26 | exports.serviceOld = async function () { 27 | this.body = await this.service.old.test(); 28 | }; 29 | 30 | exports.header = function () { 31 | this.body = { 32 | header: this.get('customheader'), 33 | }; 34 | }; 35 | 36 | exports.urllib = async function () { 37 | const url = 'http://' + this.host; 38 | const method = this.query.method || 'request'; 39 | const data = this.query.data ? JSON.parse(this.query.data) : undefined; 40 | const dataType = this.query.dataType; 41 | let r = this.app.httpclient[method](url + '/mock_url', { 42 | dataType, 43 | data, 44 | }); 45 | if (method === 'request') r = r.then(d => d); 46 | const r1 = await r; 47 | const r2 = await this.app.httpclient[method](url + '/mock_url', { 48 | method: 'POST', 49 | dataType, 50 | data, 51 | headers: { 52 | 'x-custom': 'custom', 53 | }, 54 | }); 55 | this.body = { 56 | get: Buffer.isBuffer(r1.data) ? r1.data.toString() : r1.data, 57 | post: Buffer.isBuffer(r2.data) ? r2.data.toString() : r2.data, 58 | }; 59 | }; 60 | 61 | exports.mockUrlGet = function () { 62 | this.body = 'url get'; 63 | }; 64 | 65 | exports.mockUrlPost = async function () { 66 | this.body = 'url post'; 67 | }; 68 | 69 | exports.mockUrllibHeaders = async function () { 70 | const url = 'http://' + this.host; 71 | const method = this.query.method || 'request'; 72 | const res = await this.app.httpclient[method](url + '/mock_url'); 73 | this.body = res.headers; 74 | }; 75 | 76 | exports.dataType = async function () { 77 | const url = 'http://' + this.host; 78 | const res = await this.app.httpclient.request(url + '/mock_url', { 79 | dataType: 'json', 80 | }); 81 | this.body = res.data; 82 | }; 83 | -------------------------------------------------------------------------------- /test/fixtures/demo/app/controller/session.js: -------------------------------------------------------------------------------- 1 | module.exports = async function () { 2 | this.session.save(); 3 | this.body = this.session; 4 | }; 5 | -------------------------------------------------------------------------------- /test/fixtures/demo/app/controller/user.js: -------------------------------------------------------------------------------- 1 | exports.get = function () { 2 | this.set('x-request-url', this.url); 3 | this.body = this.user; 4 | }; 5 | 6 | exports.post = function () { 7 | this.body = { 8 | user: this.user, 9 | params: this.request.body, 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /test/fixtures/demo/app/extend/application.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mockDevice(obj) { 3 | obj.mock = true; 4 | return obj; 5 | }, 6 | 7 | async mockGenerator(obj) { 8 | obj.mock = true; 9 | return obj; 10 | }, 11 | 12 | mockPromise(obj) { 13 | obj.mock = true; 14 | return Promise.resolve(obj); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /test/fixtures/demo/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app) { 4 | app.get('home', '/', app.controller.home.get); 5 | app.get('/hello', app.controller.home.hello); 6 | app.get('/service', app.controller.home.service); 7 | app.get('/service/old', app.controller.home.serviceOld); 8 | app.get('/header', app.controller.home.header); 9 | app.get('/urllib', app.controller.home.urllib); 10 | app.get('/mock_url', app.controller.home.mockUrlGet); 11 | app.post('/mock_url', app.controller.home.mockUrlPost); 12 | app.get('/mock_urllib', app.controller.home.mockUrllibHeaders); 13 | app.get('/data_type', app.controller.home.dataType); 14 | app.get('session', '/session', app.controller.session); 15 | 16 | app.post('/', app.controller.home.post); 17 | 18 | app.get('/user', app.controller.user.get); 19 | app.post('/user', app.controller.user.post); 20 | app.post('/file', app.controller.file); 21 | }; 22 | -------------------------------------------------------------------------------- /test/fixtures/demo/app/service/bar/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | class Foo extends app.Service { 3 | async get() { 4 | return 'bar'; 5 | } 6 | } 7 | 8 | return Foo; 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/demo/app/service/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | class Foo extends app.Service { 3 | async get() { 4 | return 'bar'; 5 | } 6 | 7 | getSync() { 8 | return 'bar'; 9 | } 10 | } 11 | 12 | return Foo; 13 | }; 14 | -------------------------------------------------------------------------------- /test/fixtures/demo/app/service/old.js: -------------------------------------------------------------------------------- 1 | module.exports = () => { 2 | exports.test = async function() { 3 | return 'hello'; 4 | }; 5 | 6 | return exports; 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/demo/app/service/third/bar/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | class Main extends app.Service { 3 | async get() { 4 | return 'third'; 5 | } 6 | } 7 | 8 | return Main; 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/demo/config/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | urllib: { 5 | keepAlive: false, 6 | }, 7 | logger: { 8 | consoleLevel: 'NONE', 9 | }, 10 | keys: '123', 11 | }; 12 | -------------------------------------------------------------------------------- /test/fixtures/demo/config/plugin.js: -------------------------------------------------------------------------------- 1 | exports.schedule = false; 2 | exports.logrotator = false; 3 | -------------------------------------------------------------------------------- /test/fixtures/demo/mocks_data/service/bar/foo/get/foobar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = 'foobar'; 4 | -------------------------------------------------------------------------------- /test/fixtures/demo/mocks_data/service/foo/get/foobar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = 'foobar'; 4 | -------------------------------------------------------------------------------- /test/fixtures/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function() { 4 | 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/app/context.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | getResult(result) { 3 | return { 4 | body: result, 5 | }; 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/app/controller/file.js: -------------------------------------------------------------------------------- 1 | module.exports = async function() { 2 | const stream = await this.getFileStream(); 3 | const fields = stream.fields; 4 | this.body = { 5 | fields, 6 | filename: stream.filename, 7 | user: this.user, 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/app/controller/home.js: -------------------------------------------------------------------------------- 1 | exports.get = async function() { 2 | this.body = { 3 | cookieValue: this.getCookie('foo') || undefined, 4 | cookiesValue: this.cookies.get('foo') || undefined, 5 | sessionValue: this.session.foo, 6 | }; 7 | }; 8 | 9 | exports.post = async function() { 10 | this.body = 'done'; 11 | }; 12 | 13 | exports.hello = async function() { 14 | this.body = 'hi'; 15 | }; 16 | 17 | exports.service = async function() { 18 | this.body = { 19 | foo1: await this.service.foo.get(), 20 | foo2: await this.service.bar.foo.get(), 21 | foo3: this.service.foo.getSync(), 22 | thirdService: await this.service.third.bar.foo.get(), 23 | }; 24 | }; 25 | 26 | exports.serviceOld = async function() { 27 | this.body = await this.service.old.test(); 28 | }; 29 | 30 | exports.header = async function() { 31 | this.body = { 32 | header: this.get('customheader'), 33 | }; 34 | }; 35 | 36 | exports.urllib = async function() { 37 | const url = 'http://' + this.host; 38 | const method = this.query.method || 'request'; 39 | const data = this.query.data ? JSON.parse(this.query.data) : undefined; 40 | const dataType = this.query.dataType; 41 | const foo = this.query.foo; 42 | let requestUrl = url + (this.query.mock_url || '/mock_url'); 43 | if (foo) { 44 | requestUrl = `${requestUrl}?foo=${foo}`; 45 | } 46 | let r = this.app.httpclient[method](requestUrl, { 47 | dataType, 48 | data, 49 | }); 50 | if (method === 'request') r = r.then(d => d); 51 | const r1 = await r; 52 | const r2 = await this.app.httpclient[method](requestUrl, { 53 | method: 'POST', 54 | dataType, 55 | data, 56 | headers: { 57 | 'x-custom': 'custom', 58 | }, 59 | }); 60 | this.body = { 61 | get: Buffer.isBuffer(r1.data) ? r1.data.toString() : r1.data, 62 | post: Buffer.isBuffer(r2.data) ? r2.data.toString() : r2.data, 63 | }; 64 | }; 65 | 66 | exports.streaming = async ctx => { 67 | const url = 'http://' + ctx.host; 68 | const response = await ctx.httpclient.request(url + '/mock_url', { 69 | method: 'GET', 70 | streaming: true, 71 | }); 72 | ctx.status = response.status; 73 | ctx.body = response.res; 74 | }; 75 | 76 | exports.mockUrlGet = async function() { 77 | const foo = this.query.foo; 78 | if (foo) { 79 | this.body = `url get with foo: ${foo}`; 80 | return; 81 | } 82 | this.body = 'url get'; 83 | }; 84 | 85 | exports.mockUrlPost = async function() { 86 | this.body = 'url post'; 87 | }; 88 | 89 | exports.mockUrllibHeaders = async function() { 90 | const url = 'http://' + this.host; 91 | const method = this.query.method || 'request'; 92 | const res = await this.app.httpclient[method](url + '/mock_url'); 93 | this.body = res.headers; 94 | }; 95 | 96 | exports.dataType = async function() { 97 | const url = 'http://' + this.host; 98 | const res = await this.app.httpclient.request(url + '/mock_url', { 99 | dataType: 'json', 100 | }); 101 | this.body = res.data; 102 | }; 103 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/app/controller/session.js: -------------------------------------------------------------------------------- 1 | module.exports = async function() { 2 | this.session.save(); 3 | this.body = this.session; 4 | }; 5 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/app/controller/user.js: -------------------------------------------------------------------------------- 1 | exports.get = async function() { 2 | this.body = this.user; 3 | }; 4 | 5 | exports.post = async function() { 6 | this.body = { 7 | user: this.user, 8 | params: this.request.body, 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/app/extend/application.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mockDevice(obj) { 3 | obj.mock = true; 4 | return obj; 5 | }, 6 | 7 | async mockGenerator(obj) { 8 | obj.mock = true; 9 | return obj; 10 | }, 11 | 12 | mockPromise(obj) { 13 | obj.mock = true; 14 | return Promise.resolve(obj); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/app/router.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | app.get('home', '/', app.controller.home.get); 3 | app.get('/hello', app.controller.home.hello); 4 | app.get('/service', app.controller.home.service); 5 | app.get('/service/old', app.controller.home.serviceOld); 6 | app.get('/header', app.controller.home.header); 7 | app.get('/urllib', app.controller.home.urllib); 8 | app.get('/mock_url', app.controller.home.mockUrlGet); 9 | app.get('/mock_url2', app.controller.home.mockUrlGet); 10 | app.post('/mock_url', app.controller.home.mockUrlPost); 11 | app.post('/mock_url2', app.controller.home.mockUrlPost); 12 | app.get('/mock_urllib', app.controller.home.mockUrllibHeaders); 13 | app.get('/data_type', app.controller.home.dataType); 14 | app.get('session', '/session', app.controller.session); 15 | 16 | app.post('/', app.controller.home.post); 17 | 18 | app.get('/user', app.controller.user.get); 19 | app.post('/user', app.controller.user.post); 20 | app.post('/file', app.controller.file); 21 | 22 | app.get('/streaming', 'home.streaming'); 23 | }; 24 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/app/service/bar/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | class Foo extends app.Service { 3 | async get() { 4 | return 'bar'; 5 | } 6 | } 7 | 8 | return Foo; 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/app/service/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | class Foo extends app.Service { 3 | async get() { 4 | return 'bar'; 5 | } 6 | 7 | getSync() { 8 | return 'bar'; 9 | } 10 | } 11 | 12 | return Foo; 13 | }; 14 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/app/service/old.js: -------------------------------------------------------------------------------- 1 | module.exports = () => { 2 | exports.test = async function() { 3 | return 'hello'; 4 | }; 5 | 6 | return exports; 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/app/service/third/bar/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | class Main extends app.Service { 3 | async get() { 4 | return 'third'; 5 | } 6 | } 7 | 8 | return Main; 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/config/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | httpclient: { 3 | useHttpClientNext: true, 4 | allowH2: false, 5 | request: { 6 | timing: true, 7 | }, 8 | }, 9 | logger: { 10 | consoleLevel: 'NONE', 11 | }, 12 | keys: '123', 13 | }; 14 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/mocks_data/service/bar/foo/get/foobar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = 'foobar'; 4 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/mocks_data/service/foo/get/foobar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = 'foobar'; 4 | -------------------------------------------------------------------------------- /test/fixtures/demo_next/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function() { 4 | 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/app/context.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | getResult(result) { 3 | return { 4 | body: result, 5 | }; 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/app/controller/file.js: -------------------------------------------------------------------------------- 1 | module.exports = async function() { 2 | const stream = await this.getFileStream(); 3 | const fields = stream.fields; 4 | this.body = { 5 | fields, 6 | filename: stream.filename, 7 | user: this.user, 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/app/controller/home.js: -------------------------------------------------------------------------------- 1 | exports.get = async function() { 2 | this.body = { 3 | cookieValue: this.getCookie('foo') || undefined, 4 | cookiesValue: this.cookies.get('foo') || undefined, 5 | sessionValue: this.session.foo, 6 | }; 7 | }; 8 | 9 | exports.post = async function() { 10 | this.body = 'done'; 11 | }; 12 | 13 | exports.hello = async function() { 14 | this.body = 'hi'; 15 | }; 16 | 17 | exports.service = async function() { 18 | this.body = { 19 | foo1: await this.service.foo.get(), 20 | foo2: await this.service.bar.foo.get(), 21 | foo3: this.service.foo.getSync(), 22 | thirdService: await this.service.third.bar.foo.get(), 23 | }; 24 | }; 25 | 26 | exports.serviceOld = async function() { 27 | this.body = await this.service.old.test(); 28 | }; 29 | 30 | exports.header = async function() { 31 | this.body = { 32 | header: this.get('customheader'), 33 | }; 34 | }; 35 | 36 | exports.urllib = async function() { 37 | const url = 'http://' + this.host; 38 | const method = this.query.method || 'request'; 39 | const data = this.query.data ? JSON.parse(this.query.data) : undefined; 40 | const dataType = this.query.dataType; 41 | const foo = this.query.foo; 42 | let requestUrl = url + (this.query.mock_url || '/mock_url'); 43 | if (foo) { 44 | requestUrl = `${requestUrl}?foo=${foo}`; 45 | } 46 | let r = this.app.httpclient[method](requestUrl, { 47 | dataType, 48 | data, 49 | }); 50 | if (method === 'request') r = r.then(d => d); 51 | const r1 = await r; 52 | const r2 = await this.app.httpclient[method](requestUrl, { 53 | method: 'POST', 54 | dataType, 55 | data, 56 | headers: { 57 | 'x-custom': 'custom', 58 | }, 59 | }); 60 | this.body = { 61 | get: Buffer.isBuffer(r1.data) ? r1.data.toString() : r1.data, 62 | post: Buffer.isBuffer(r2.data) ? r2.data.toString() : r2.data, 63 | }; 64 | }; 65 | 66 | exports.streaming = async ctx => { 67 | const url = 'http://' + ctx.host; 68 | const response = await ctx.httpclient.request(url + '/mock_url', { 69 | method: 'GET', 70 | streaming: true, 71 | }); 72 | ctx.status = response.status; 73 | ctx.body = response.res; 74 | }; 75 | 76 | exports.mockUrlGet = async function() { 77 | const foo = this.query.foo; 78 | if (foo) { 79 | this.body = `url get with foo: ${foo}`; 80 | return; 81 | } 82 | this.body = 'url get'; 83 | }; 84 | 85 | exports.mockUrlPost = async function() { 86 | this.body = 'url post'; 87 | }; 88 | 89 | exports.mockUrllibHeaders = async function() { 90 | const url = 'http://' + this.host; 91 | const method = this.query.method || 'request'; 92 | const res = await this.app.httpclient[method](url + '/mock_url'); 93 | this.body = res.headers; 94 | }; 95 | 96 | exports.dataType = async function() { 97 | const url = 'http://' + this.host; 98 | const res = await this.app.httpclient.request(url + '/mock_url', { 99 | dataType: 'json', 100 | }); 101 | this.body = res.data; 102 | }; 103 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/app/controller/session.js: -------------------------------------------------------------------------------- 1 | module.exports = async function() { 2 | this.session.save(); 3 | this.body = this.session; 4 | }; 5 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/app/controller/user.js: -------------------------------------------------------------------------------- 1 | exports.get = async function() { 2 | this.body = this.user; 3 | }; 4 | 5 | exports.post = async function() { 6 | this.body = { 7 | user: this.user, 8 | params: this.request.body, 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/app/extend/application.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mockDevice(obj) { 3 | obj.mock = true; 4 | return obj; 5 | }, 6 | 7 | async mockGenerator(obj) { 8 | obj.mock = true; 9 | return obj; 10 | }, 11 | 12 | mockPromise(obj) { 13 | obj.mock = true; 14 | return Promise.resolve(obj); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/app/router.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | app.get('home', '/', app.controller.home.get); 3 | app.get('/hello', app.controller.home.hello); 4 | app.get('/service', app.controller.home.service); 5 | app.get('/service/old', app.controller.home.serviceOld); 6 | app.get('/header', app.controller.home.header); 7 | app.get('/urllib', app.controller.home.urllib); 8 | app.get('/mock_url', app.controller.home.mockUrlGet); 9 | app.get('/mock_url2', app.controller.home.mockUrlGet); 10 | app.post('/mock_url', app.controller.home.mockUrlPost); 11 | app.post('/mock_url2', app.controller.home.mockUrlPost); 12 | app.get('/mock_urllib', app.controller.home.mockUrllibHeaders); 13 | app.get('/data_type', app.controller.home.dataType); 14 | app.get('session', '/session', app.controller.session); 15 | 16 | app.post('/', app.controller.home.post); 17 | 18 | app.get('/user', app.controller.user.get); 19 | app.post('/user', app.controller.user.post); 20 | app.post('/file', app.controller.file); 21 | 22 | app.get('/streaming', 'home.streaming'); 23 | }; 24 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/app/service/bar/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | class Foo extends app.Service { 3 | async get() { 4 | return 'bar'; 5 | } 6 | } 7 | 8 | return Foo; 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/app/service/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | class Foo extends app.Service { 3 | async get() { 4 | return 'bar'; 5 | } 6 | 7 | getSync() { 8 | return 'bar'; 9 | } 10 | } 11 | 12 | return Foo; 13 | }; 14 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/app/service/old.js: -------------------------------------------------------------------------------- 1 | module.exports = () => { 2 | exports.test = async function() { 3 | return 'hello'; 4 | }; 5 | 6 | return exports; 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/app/service/third/bar/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | class Main extends app.Service { 3 | async get() { 4 | return 'third'; 5 | } 6 | } 7 | 8 | return Main; 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/config/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | httpclient: { 3 | useHttpClientNext: true, 4 | allowH2: true, 5 | request: { 6 | timing: true, 7 | }, 8 | }, 9 | logger: { 10 | consoleLevel: 'NONE', 11 | }, 12 | keys: '123', 13 | }; 14 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/mocks_data/service/bar/foo/get/foobar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = 'foobar'; 4 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/mocks_data/service/foo/get/foobar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = 'foobar'; 4 | -------------------------------------------------------------------------------- /test/fixtures/demo_next_h2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/disable-security/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function() { 4 | 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/disable-security/app/context.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | getResult(result) { 3 | return { 4 | body: result, 5 | }; 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/disable-security/app/controller/home.js: -------------------------------------------------------------------------------- 1 | exports.get = async function() { 2 | this.body = { 3 | cookieValue: this.getCookie('foo') || undefined, 4 | cookiesValue: this.cookies.get('foo') || undefined, 5 | sessionValue: this.session.foo, 6 | }; 7 | }; 8 | 9 | exports.post = async function() { 10 | this.body = 'done'; 11 | }; 12 | 13 | exports.hello = async function() { 14 | this.body = 'hi'; 15 | }; 16 | 17 | exports.service = async function() { 18 | this.body = { 19 | foo1: await this.service.foo.get(), 20 | foo2: await this.service.bar.foo.get(), 21 | foo3: this.service.foo.getSync(), 22 | thirdService: await this.service.third.bar.foo.get(), 23 | }; 24 | }; 25 | 26 | exports.serviceOld = async function() { 27 | this.body = await this.service.old.test(); 28 | }; 29 | 30 | exports.header = async function() { 31 | this.body = { 32 | header: this.get('customheader'), 33 | }; 34 | }; 35 | 36 | exports.urllib = async function() { 37 | const url = 'http://' + this.host; 38 | const method = this.query.method || 'request'; 39 | const dataType = this.query.dataType; 40 | let r = this.app.httpclient[method](url + '/mock_url', { 41 | dataType, 42 | }); 43 | if (method === 'request') r = r.then(d => d); 44 | const r1 = await r; 45 | const r2 = await this.app.httpclient[method](url + '/mock_url', { 46 | method: 'POST', 47 | dataType, 48 | }); 49 | this.body = { 50 | get: Buffer.isBuffer(r1.data) ? r1.data.toString() : r1.data, 51 | post: Buffer.isBuffer(r2.data) ? r2.data.toString() : r2.data, 52 | }; 53 | }; 54 | 55 | exports.mockUrlGet = async function() { 56 | this.body = 'url get'; 57 | }; 58 | 59 | exports.mockUrlPost = async function() { 60 | this.body = 'url post'; 61 | }; 62 | 63 | exports.mockUrllibHeaders = async function() { 64 | const url = 'http://' + this.host; 65 | const method = this.query.method || 'request'; 66 | const res = await this.app.httpclient[method](url + '/mock_url'); 67 | this.body = res.headers; 68 | }; 69 | -------------------------------------------------------------------------------- /test/fixtures/disable-security/app/controller/session.js: -------------------------------------------------------------------------------- 1 | module.exports = async function() { 2 | this.body = this.session; 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixtures/disable-security/app/router.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | app.get('/', app.controller.home.get); 3 | app.get('/hello', app.controller.home.hello); 4 | app.get('/service', app.controller.home.service); 5 | app.get('/service/old', app.controller.home.serviceOld); 6 | app.get('/header', app.controller.home.header); 7 | app.get('/urllib', app.controller.home.urllib); 8 | app.get('/mock_url', app.controller.home.mockUrlGet); 9 | app.post('/mock_url', app.controller.home.mockUrlPost); 10 | app.get('/mock_urllib', app.controller.home.mockUrllibHeaders); 11 | app.get('/session', app.controller.session); 12 | 13 | app.post('/', app.controller.home.post); 14 | }; 15 | -------------------------------------------------------------------------------- /test/fixtures/disable-security/app/service/bar/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | class Foo extends app.Service { 3 | async get() { 4 | return 'bar'; 5 | } 6 | } 7 | 8 | return Foo; 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/disable-security/app/service/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | class Foo extends app.Service { 3 | async get() { 4 | return 'bar'; 5 | } 6 | 7 | getSync() { 8 | return 'bar'; 9 | } 10 | } 11 | 12 | return Foo; 13 | }; 14 | -------------------------------------------------------------------------------- /test/fixtures/disable-security/app/service/old.js: -------------------------------------------------------------------------------- 1 | module.exports = () => { 2 | exports.test = async function() { 3 | return 'hello'; 4 | }; 5 | 6 | return exports; 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/disable-security/app/service/third/bar/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = function(app) { 2 | class Main extends app.Service { 3 | async get() { 4 | return 'third'; 5 | } 6 | } 7 | 8 | return Main; 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/disable-security/config/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | urllib: { 5 | keepAlive: false, 6 | }, 7 | logger: { 8 | consoleLevel: 'NONE', 9 | }, 10 | keys: '123', 11 | }; 12 | -------------------------------------------------------------------------------- /test/fixtures/disable-security/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.security = false; 4 | -------------------------------------------------------------------------------- /test/fixtures/disable-security/mocks_data/service/bar/foo/get/foobar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = 'foobar'; 4 | -------------------------------------------------------------------------------- /test/fixtures/disable-security/mocks_data/service/foo/get/foobar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = 'foobar'; 4 | -------------------------------------------------------------------------------- /test/fixtures/disable-security/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/error-framework/index.js: -------------------------------------------------------------------------------- 1 | const egg = require('egg'); 2 | 3 | class Application extends egg.Application { 4 | constructor() { 5 | throw new Error('start error'); 6 | } 7 | } 8 | 9 | exports.Application = Application; 10 | exports.Agent = egg.Agent; 11 | exports.startCluster = egg.startCluster; 12 | -------------------------------------------------------------------------------- /test/fixtures/error-framework/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "error-framework" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/failed-app/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: '123', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/failed-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/failed-app/test/index.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('test/index.test.ts', () => { 4 | describe('before error', () => { 5 | before(() => { 6 | throw new Error('before error'); 7 | }); 8 | 9 | it('should not print', () => { 10 | assert.fail('never arrive'); 11 | }); 12 | }); 13 | 14 | describe('after error', () => { 15 | after(() => { 16 | throw new Error('after error'); 17 | }); 18 | 19 | it('should print', () => { 20 | console.log('after error test case should print'); 21 | }); 22 | }); 23 | 24 | describe('beforeEach error', () => { 25 | beforeEach(() => { 26 | throw new Error('beforeEach error'); 27 | }); 28 | 29 | it('should not print', () => { 30 | assert.fail('never arrive'); 31 | }); 32 | }); 33 | 34 | describe('afterEach error', () => { 35 | afterEach(() => { 36 | throw new Error('afterEach error'); 37 | }); 38 | 39 | it('should print', () => { 40 | console.log('afterEach error test case should print'); 41 | }); 42 | }); 43 | 44 | describe('case error', () => { 45 | it('should failed', () => { 46 | assert.fail('should fail'); 47 | }); 48 | 49 | it('should work', () => { 50 | // ... 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /test/fixtures/fooPlugin/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(app) { 4 | app.fooPlugin = true; 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/fooPlugin/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = '123'; 4 | -------------------------------------------------------------------------------- /test/fixtures/fooPlugin/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.fooPlugin = { 4 | 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/fooPlugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fooPlugin", 3 | "eggPlugin": { 4 | "name": "fooPlugin" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/get-app-failed/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: '123', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/get-app-failed/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/get-app-failed/test/index.test.js: -------------------------------------------------------------------------------- 1 | const { setGetAppCallback } = require('../../../..'); 2 | 3 | setGetAppCallback(() => { 4 | throw new Error('mock get app failed'); 5 | }); 6 | 7 | describe('test case create context error', () => { 8 | it('should not print', () => { 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /test/fixtures/messenger-binding/agent.js: -------------------------------------------------------------------------------- 1 | module.exports = class Boot { 2 | constructor(agent) { 3 | this.agent = agent; 4 | } 5 | 6 | async willReady() { 7 | const agent = this.agent; 8 | agent.received = []; 9 | agent.messenger.on('action', data => { 10 | agent.received.push(data); 11 | console.error('agent.js received action data: %o', data); 12 | }); 13 | 14 | agent.messenger.on('broadcast-action', () => { 15 | agent.recievedBroadcastAction = true; 16 | agent.messenger.sendToApp('agent-recieved-broadcast-action'); 17 | }); 18 | 19 | agent.messenger.sendToApp('action', 'send data to app when agent starting'); 20 | } 21 | 22 | async didReady() { 23 | const agent = this.agent; 24 | agent.eggReady = true; 25 | agent.messenger.sendToApp('action', 'send data to app when agent started'); 26 | } 27 | 28 | async serverDidReady() { 29 | console.error('agent.js serverDidReady start'); 30 | const agent = this.agent; 31 | // only can send message to app when server started 32 | agent.messenger.sendToApp('action', 'send data to app when server started'); 33 | agent.messenger.sendRandom('action', 'send data to a random app'); 34 | agent.serverReady = true; 35 | console.error('agent.js serverDidReady end'); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /test/fixtures/messenger-binding/app.js: -------------------------------------------------------------------------------- 1 | module.exports = class Boot { 2 | constructor(app) { 3 | this.app = app; 4 | } 5 | 6 | async willReady() { 7 | const app = this.app; 8 | app.received = []; 9 | app.messenger.on('action', data => { 10 | app.received.push(data); 11 | console.error('app.js received action data: %o', data); 12 | }); 13 | 14 | app.messenger.on('app-action', () => { 15 | app.recievedAppAction = true; 16 | }); 17 | 18 | app.messenger.on('broadcast-action', () => { 19 | app.recievedBroadcastAction = true; 20 | }); 21 | 22 | app.messenger.on('agent-recieved-broadcast-action', () => { 23 | app.recievedAgentRecievedAction = true; 24 | }); 25 | 26 | app.messenger.sendToAgent('action', 'send data to agent when app starting'); 27 | 28 | // app.ready(() => { 29 | // app.messenger.sendToAgent('action', 'send data when app started'); 30 | // app.messenger.sendRandom('action', 'send data to a random agent'); 31 | // app.messenger.broadcast('broadcast-action', 'broadcast action'); 32 | // app.messenger.sendToApp('app-action', 'send action to app'); 33 | // }); 34 | // app.messenger.on('egg-ready', data => { 35 | // app.eggReady = true; 36 | // app.eggReadyData = data; 37 | // }); 38 | } 39 | 40 | async didReady() { 41 | const app = this.app; 42 | app.eggReady = true; 43 | app.messenger.sendToAgent('action', 'send data to agent when app started'); 44 | } 45 | 46 | async serverDidReady() { 47 | const app = this.app; 48 | // only can send message to agent when server started 49 | app.messenger.sendRandom('action', 'send data to a random agent'); 50 | app.messenger.broadcast('broadcast-action', 'broadcast action'); 51 | app.messenger.sendToApp('app-action', 'send action to app'); 52 | app.messenger.sendToApp('action', 'send action to all app'); 53 | app.serverReady = true; 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /test/fixtures/messenger-binding/config/config.default.js: -------------------------------------------------------------------------------- 1 | exports.keys = '123'; 2 | -------------------------------------------------------------------------------- /test/fixtures/messenger-binding/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "messenger-binding" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/plugin-bootstrap/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "eggPlugin": { 3 | "name": "plugin1" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/plugin-bootstrap/test.js: -------------------------------------------------------------------------------- 1 | require('../../../dist/commonjs/bootstrap'); 2 | -------------------------------------------------------------------------------- /test/fixtures/plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "eggPlugin": { 3 | "name": "plugin1" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/plugin_throw/package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/fixtures/request/app/router.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | app.get('home', '/', async function() { 3 | this.body = 'hello world'; 4 | }); 5 | 6 | app.get('session', '/session', async function() { 7 | this.body = 'hello session'; 8 | }); 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/request/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = 'foo'; 4 | -------------------------------------------------------------------------------- /test/fixtures/request/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "request-demo" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/server/app.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | app.messenger.on('egg-ready', server => { 3 | app.emitServer = !!server; 4 | }); 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/setup-app/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: '123', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/setup-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/setup-app/test/.setup.js: -------------------------------------------------------------------------------- 1 | const mm = require('../../../../index'); 2 | const path = require('path'); 3 | before(async () => { 4 | global.app = mm.app({ 5 | baseDir: path.join(__dirname, '../'), 6 | framework: require.resolve('egg'), 7 | }); 8 | mm.setGetAppCallback(() => { 9 | return global.app; 10 | }); 11 | await global.app.ready(); 12 | }); 13 | -------------------------------------------------------------------------------- /test/fixtures/setup-app/test/index.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('test/index.test.ts', () => { 4 | it('should work', () => { 5 | // eslint-disable-next-line no-undef 6 | assert(app.currentContext); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app-esm/app.js: -------------------------------------------------------------------------------- 1 | export default app => { 2 | app.on('server', server => { 3 | app.serverKeepAliveTimeout = server.keepAliveTimeout || 5000; 4 | }); 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app-esm/app/modules/foo/LogService.ts: -------------------------------------------------------------------------------- 1 | import { AccessLevel, Inject, SingletonProto } from '@eggjs/tegg'; 2 | 3 | interface Tracer { 4 | traceId: string; 5 | } 6 | 7 | @SingletonProto({ 8 | accessLevel: AccessLevel.PUBLIC, 9 | }) 10 | export class LogService { 11 | @Inject() 12 | private readonly tracer: Tracer; 13 | 14 | getTracerId() { 15 | return this.tracer.traceId; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app-esm/app/modules/foo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "foo", 3 | "eggModule": { 4 | "name": "foo" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app-esm/config/config.default.js: -------------------------------------------------------------------------------- 1 | export default { 2 | keys: '123', 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app-esm/config/plugin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | teggConfig: { 3 | package: '@eggjs/tegg-config', 4 | enable: true, 5 | }, 6 | tegg: { 7 | package: '@eggjs/tegg-plugin', 8 | enable: true, 9 | }, 10 | teggController: { 11 | package: '@eggjs/tegg-controller-plugin', 12 | enable: true, 13 | }, 14 | tracer: { 15 | package: 'egg-tracer', 16 | enable: true, 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app-esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "egg": { 4 | "typescript": true 5 | }, 6 | "type": "module" 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app-esm/test/hooks.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert'; 2 | import { Context } from 'egg'; 3 | // import { app } from '../../../../src/bootstrap.js'; 4 | import { app } from '../../../../dist/esm/bootstrap.js'; 5 | 6 | describe('test/hooks.test.ts', () => { 7 | let beforeCtx; 8 | let afterCtx; 9 | const beforeEachCtxList: Record = {}; 10 | const afterEachCtxList: Record = {}; 11 | const itCtxList: Record = {}; 12 | 13 | before(async () => { 14 | beforeCtx = app.currentContext; 15 | }); 16 | 17 | after(() => { 18 | afterCtx = app.currentContext; 19 | assert(beforeCtx); 20 | assert(beforeCtx !== itCtxList.foo); 21 | assert(itCtxList.foo !== itCtxList.bar); 22 | assert(afterCtx === beforeCtx); 23 | assert(beforeEachCtxList.foo === afterEachCtxList.foo); 24 | assert(beforeEachCtxList.foo === itCtxList.foo); 25 | }); 26 | 27 | describe('foo', () => { 28 | beforeEach(() => { 29 | beforeEachCtxList.foo = app.currentContext as Context; 30 | }); 31 | 32 | it('should work', () => { 33 | itCtxList.foo = app.currentContext as Context; 34 | }); 35 | 36 | afterEach(() => { 37 | afterEachCtxList.foo = app.currentContext as Context; 38 | }); 39 | }); 40 | 41 | describe('bar', () => { 42 | beforeEach(() => { 43 | beforeEachCtxList.bar = app.currentContext as Context; 44 | }); 45 | 46 | it('should work', () => { 47 | itCtxList.bar = app.currentContext as Context; 48 | }); 49 | 50 | afterEach(() => { 51 | afterEachCtxList.bar = app.currentContext as Context; 52 | }); 53 | }); 54 | 55 | describe('multi it', () => { 56 | const itCtxList: Array = []; 57 | 58 | it('should work 1', () => { 59 | itCtxList.push(app.currentContext as Context); 60 | }); 61 | 62 | it('should work 2', () => { 63 | itCtxList.push(app.currentContext as Context); 64 | }); 65 | 66 | after(() => { 67 | assert(itCtxList[0] !== itCtxList[1]); 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app-esm/test/multi_mock_context.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import { app } from '../../../../dist/commonjs/bootstrap'; 3 | 4 | describe('test/multi_mock_context.test.ts', () => { 5 | describe('mockContext', () => { 6 | it('should only reused once', async () => { 7 | const currentContext = app.currentContext; 8 | const ctx1 = app.mockContext(); 9 | const ctx2 = app.mockContext(); 10 | assert.strictEqual(currentContext, ctx1); 11 | assert.notStrictEqual(ctx2, ctx1); 12 | }); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app-esm/test/tegg.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | // import { app } from '../../../../src/bootstrap.js'; 3 | import { app } from '../../../../dist/commonjs/bootstrap'; 4 | import { LogService } from '../app/modules/foo/LogService'; 5 | 6 | describe('test/tegg.test.ts', () => { 7 | describe('async function', () => { 8 | it('should work', async () => { 9 | const logService = await app.getEggObject(LogService); 10 | assert(logService.getTracerId()); 11 | }); 12 | }); 13 | 14 | describe('callback function', () => { 15 | it('should work', done => { 16 | app.mockModuleContextScope(async () => { 17 | const logService = await app.getEggObject(LogService); 18 | assert(logService.getTracerId()); 19 | done(); 20 | }); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app-esm/test/tegg_context.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import { Context } from 'egg'; 3 | // import { app, mm } from '../../../../src/bootstrap.js'; 4 | import { app, mm } from '../../../../dist/commonjs/bootstrap'; 5 | import { LogService } from '../app/modules/foo/LogService'; 6 | 7 | describe('test/tegg_context.test.ts', () => { 8 | let ctx: Context; 9 | let logService: LogService; 10 | before(async () => { 11 | logService = await app.getEggObject(LogService); 12 | }); 13 | 14 | describe('mock ctx property', () => { 15 | it('should mock ctx work', async () => { 16 | ctx = await app.mockModuleContext(); 17 | mm(ctx.tracer, 'traceId', 'mockTraceId'); 18 | const traceId = logService.getTracerId(); 19 | assert.strictEqual(traceId, 'mockTraceId'); 20 | }); 21 | }); 22 | 23 | describe.skip('mockModuleContextWithData', () => { 24 | beforeEach(async () => { 25 | const ctx = await app.mockModuleContext({ 26 | tracer: { 27 | traceId: 'mock_with_data', 28 | }, 29 | headers: { 30 | 'user-agent': 'mock_agent', 31 | }, 32 | }); 33 | assert.strictEqual(ctx.tracer.traceId, 'mock_with_data'); 34 | assert.strictEqual(ctx.get('user-agent'), 'mock_agent'); 35 | }); 36 | 37 | it('should mock ctx work', () => { 38 | const traceId = logService.getTracerId(); 39 | assert.strictEqual(traceId, 'mock_with_data'); 40 | }); 41 | }); 42 | 43 | describe.skip('mockModuleContextWithHeaders', () => { 44 | beforeEach(async () => { 45 | await app.mockModuleContext({ 46 | headers: { 47 | 'user-agent': 'mock_agent', 48 | }, 49 | }); 50 | }); 51 | 52 | it('should mock ctx work', () => { 53 | assert.strictEqual(app.currentContext!.get('user-agent'), 'mock_agent'); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app-esm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@eggjs/tsconfig" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app-esm/typing.ts: -------------------------------------------------------------------------------- 1 | import 'egg'; 2 | import '@eggjs/tegg-plugin'; 3 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app/app.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | app.on('server', server => { 3 | app.serverKeepAliveTimeout = server.keepAliveTimeout || 5000; 4 | }); 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app/app/modules/foo/LogService.ts: -------------------------------------------------------------------------------- 1 | import { AccessLevel, Inject, SingletonProto } from '@eggjs/tegg'; 2 | 3 | interface Tracer { 4 | traceId: string; 5 | } 6 | 7 | @SingletonProto({ 8 | accessLevel: AccessLevel.PUBLIC, 9 | }) 10 | export class LogService { 11 | @Inject() 12 | private readonly tracer: Tracer; 13 | 14 | getTracerId() { 15 | return this.tracer.traceId; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app/app/modules/foo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "foo", 3 | "eggModule": { 4 | "name": "foo" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app/config/config.default.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | keys: '123', 3 | }; 4 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app/config/plugin.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | teggConfig: { 3 | package: '@eggjs/tegg-config', 4 | enable: true, 5 | }, 6 | tegg: { 7 | package: '@eggjs/tegg-plugin', 8 | enable: true, 9 | }, 10 | teggController: { 11 | package: '@eggjs/tegg-controller-plugin', 12 | enable: true, 13 | }, 14 | tracer: { 15 | package: 'egg-tracer', 16 | enable: true, 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "egg": { 4 | "typescript": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app/test/hooks.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert'; 2 | import { Context } from 'egg'; 3 | // import { app } from '../../../../src/bootstrap.js'; 4 | import { app } from '../../../../dist/commonjs/bootstrap'; 5 | 6 | describe('test/hooks.test.ts', () => { 7 | let beforeCtx; 8 | let afterCtx; 9 | const beforeEachCtxList: Record = {}; 10 | const afterEachCtxList: Record = {}; 11 | const itCtxList: Record = {}; 12 | 13 | before(async () => { 14 | beforeCtx = app.currentContext; 15 | }); 16 | 17 | after(() => { 18 | afterCtx = app.currentContext; 19 | assert(beforeCtx); 20 | assert(beforeCtx !== itCtxList.foo); 21 | assert(itCtxList.foo !== itCtxList.bar); 22 | assert(afterCtx === beforeCtx); 23 | assert(beforeEachCtxList.foo === afterEachCtxList.foo); 24 | assert(beforeEachCtxList.foo === itCtxList.foo); 25 | }); 26 | 27 | describe('foo', () => { 28 | beforeEach(() => { 29 | beforeEachCtxList.foo = app.currentContext as Context; 30 | }); 31 | 32 | it('should work', () => { 33 | itCtxList.foo = app.currentContext as Context; 34 | }); 35 | 36 | afterEach(() => { 37 | afterEachCtxList.foo = app.currentContext as Context; 38 | }); 39 | }); 40 | 41 | describe('bar', () => { 42 | beforeEach(() => { 43 | beforeEachCtxList.bar = app.currentContext as Context; 44 | }); 45 | 46 | it('should work', () => { 47 | itCtxList.bar = app.currentContext as Context; 48 | }); 49 | 50 | afterEach(() => { 51 | afterEachCtxList.bar = app.currentContext as Context; 52 | }); 53 | }); 54 | 55 | describe('multi it', () => { 56 | const itCtxList: Array = []; 57 | 58 | it('should work 1', () => { 59 | itCtxList.push(app.currentContext as Context); 60 | }); 61 | 62 | it('should work 2', () => { 63 | itCtxList.push(app.currentContext as Context); 64 | }); 65 | 66 | after(() => { 67 | assert(itCtxList[0] !== itCtxList[1]); 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app/test/multi_mock_context.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import { app } from '../../../../dist/commonjs/bootstrap'; 3 | 4 | describe('test/multi_mock_context.test.ts', () => { 5 | describe('mockContext', () => { 6 | it('should only reused once', async () => { 7 | const currentContext = app.currentContext; 8 | const ctx1 = app.mockContext(); 9 | const ctx2 = app.mockContext(); 10 | assert.strictEqual(currentContext, ctx1); 11 | assert.notStrictEqual(ctx2, ctx1); 12 | }); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app/test/tegg.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | // import { app } from '../../../../src/bootstrap.js'; 3 | import { app } from '../../../../dist/commonjs/bootstrap'; 4 | import { LogService } from '../app/modules/foo/LogService'; 5 | 6 | describe('test/tegg.test.ts', () => { 7 | describe('async function', () => { 8 | it('should work', async () => { 9 | const logService = await app.getEggObject(LogService); 10 | assert(logService.getTracerId()); 11 | }); 12 | }); 13 | 14 | describe('callback function', () => { 15 | it('should work', done => { 16 | app.mockModuleContextScope(async () => { 17 | const logService = await app.getEggObject(LogService); 18 | assert(logService.getTracerId()); 19 | done(); 20 | }); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app/test/tegg_context.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import { Context } from 'egg'; 3 | // import { app, mm } from '../../../../src/bootstrap.js'; 4 | import { app, mm } from '../../../../dist/commonjs/bootstrap'; 5 | import { LogService } from '../app/modules/foo/LogService'; 6 | 7 | describe('test/tegg_context.test.ts', () => { 8 | let ctx: Context; 9 | let logService: LogService; 10 | before(async () => { 11 | logService = await app.getEggObject(LogService); 12 | }); 13 | 14 | describe('mock ctx property', () => { 15 | it('should mock ctx work', async () => { 16 | ctx = await app.mockModuleContext(); 17 | mm(ctx.tracer, 'traceId', 'mockTraceId'); 18 | const traceId = logService.getTracerId(); 19 | assert.strictEqual(traceId, 'mockTraceId'); 20 | }); 21 | }); 22 | 23 | describe.skip('mockModuleContextWithData', () => { 24 | beforeEach(async () => { 25 | const ctx = await app.mockModuleContext({ 26 | tracer: { 27 | traceId: 'mock_with_data', 28 | }, 29 | headers: { 30 | 'user-agent': 'mock_agent', 31 | }, 32 | }); 33 | assert.strictEqual(ctx.tracer.traceId, 'mock_with_data'); 34 | assert.strictEqual(ctx.get('user-agent'), 'mock_agent'); 35 | }); 36 | 37 | it('should mock ctx work', () => { 38 | const traceId = logService.getTracerId(); 39 | assert.strictEqual(traceId, 'mock_with_data'); 40 | }); 41 | }); 42 | 43 | describe.skip('mockModuleContextWithHeaders', () => { 44 | beforeEach(async () => { 45 | await app.mockModuleContext({ 46 | headers: { 47 | 'user-agent': 'mock_agent', 48 | }, 49 | }); 50 | }); 51 | 52 | it('should mock ctx work', () => { 53 | assert.strictEqual(app.currentContext!.get('user-agent'), 'mock_agent'); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@eggjs/tsconfig" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/tegg-app/typing.ts: -------------------------------------------------------------------------------- 1 | import 'egg'; 2 | import '@eggjs/tegg-plugin'; 3 | -------------------------------------------------------------------------------- /test/fixtures/test-case-create-context-failed/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: '123', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/test-case-create-context-failed/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/test-case-create-context-failed/test/index.test.js: -------------------------------------------------------------------------------- 1 | const { setGetAppCallback } = require('../../../..'); 2 | 3 | setGetAppCallback((suite, test) => { 4 | return { 5 | ready: async () => { 6 | // ... 7 | }, 8 | mockContextScope: async scope => { 9 | if (!test) { 10 | await scope({}); 11 | } else { 12 | throw new Error('mock create context failed'); 13 | } 14 | }, 15 | backgroundTasksFinished: async () => { 16 | // ... 17 | }, 18 | close: async () => { 19 | // ... 20 | }, 21 | }; 22 | }); 23 | 24 | describe('test case create context error', function() { 25 | it('should not print', () => { 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/fixtures/test-case-get-app-failed/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | keys: '123', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/test-case-get-app-failed/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/test-case-get-app-failed/test/index.test.js: -------------------------------------------------------------------------------- 1 | const { setGetAppCallback } = require('../../../..'); 2 | 3 | setGetAppCallback((suite, test) => { 4 | if (test) { 5 | throw new Error('mock get app failed'); 6 | } 7 | return { 8 | ready: async () => { 9 | // ... 10 | }, 11 | mockContextScope: async scope => { 12 | await scope({}); 13 | }, 14 | backgroundTasksFinished: async () => { 15 | // ... 16 | }, 17 | close: async () => { 18 | // ... 19 | }, 20 | }; 21 | }); 22 | 23 | describe('test case get app error', () => { 24 | it('should not print', () => { 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/fixtures/yadan_app/config/config.default.js: -------------------------------------------------------------------------------- 1 | exports.keys = '123'; 2 | -------------------------------------------------------------------------------- /test/fixtures/yadan_app/node_modules/yadan/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.foobar = 'yadan'; 4 | -------------------------------------------------------------------------------- /test/fixtures/yadan_app/node_modules/yadan/index.js: -------------------------------------------------------------------------------- 1 | const egg = require('egg'); 2 | 3 | const EGG_PATH = Symbol.for('egg#eggPath'); 4 | 5 | class YadanApplication extends egg.Application { 6 | get [EGG_PATH]() { 7 | return __dirname; 8 | } 9 | } 10 | 11 | exports.Agent = egg.Agent; 12 | exports.Application = YadanApplication; 13 | exports.startCluster = egg.startCluster; 14 | -------------------------------------------------------------------------------- /test/fixtures/yadan_app/node_modules/yadan/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bar", 3 | "version": "1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/yadan_app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom", 3 | "egg": { 4 | "framework": "yadan" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/yadan_app_fail/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.keys = '123'; 4 | -------------------------------------------------------------------------------- /test/fixtures/yadan_app_fail/node_modules/.package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": {} 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/yadan_app_fail/node_modules/yadan/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.foobar = 'yadan'; 4 | -------------------------------------------------------------------------------- /test/fixtures/yadan_app_fail/node_modules/yadan/index.js: -------------------------------------------------------------------------------- 1 | const egg = require('egg'); 2 | 3 | const EGG_PATH = Symbol.for('egg#eggPath'); 4 | 5 | class YadanApplication extends egg.Application { 6 | get [EGG_PATH]() { 7 | return __dirname; 8 | } 9 | } 10 | 11 | exports.Application = YadanApplication; 12 | -------------------------------------------------------------------------------- /test/fixtures/yadan_app_fail/node_modules/yadan/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bar", 3 | "version": "1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/yadan_app_fail/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom", 3 | "egg": { 4 | "framework": "yadan" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/format_options.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import path from 'node:path'; 3 | import mm from '../src/index.js'; 4 | import { formatOptions } from '../src/lib/format_options.js'; 5 | import { getSourceDirname } from '../src/lib/utils.js'; 6 | import { getFixtures } from './helper.js'; 7 | 8 | describe('test/format_options.test.ts', () => { 9 | afterEach(mm.restore); 10 | 11 | it('should return the default options', () => { 12 | const options = formatOptions(); 13 | assert(options); 14 | assert(options.coverage === true); 15 | assert(options.cache === true); 16 | assert(options.baseDir === process.cwd()); 17 | assert.deepEqual(options.plugins['egg-mock'], { 18 | enable: true, 19 | path: path.join(getSourceDirname(), '..'), 20 | }); 21 | }); 22 | 23 | it('should return baseDir when on windows', () => { 24 | const baseDir = 'D:\\projectWorkSpace\\summer'; 25 | mm(path, 'isAbsolute', path.win32.isAbsolute); 26 | mm(process, 'cwd', () => baseDir); 27 | try { 28 | formatOptions(); 29 | throw new Error('should not run'); 30 | } catch (err: any) { 31 | assert(/D:[\\|\/]projectWorkSpace[\\|\/]summer/.test(err.message)); 32 | } 33 | }); 34 | 35 | it('should set cache', () => { 36 | const options = formatOptions({ cache: false }); 37 | assert(options); 38 | assert(options.cache === false); 39 | }); 40 | 41 | it('should disable cache when call mm.env', () => { 42 | mm.env('prod'); 43 | const options = formatOptions(); 44 | assert(options); 45 | assert(options.cache === false); 46 | }); 47 | 48 | it('should set coverage', () => { 49 | const options = formatOptions({ coverage: false }); 50 | assert(options); 51 | assert(options.coverage === false); 52 | }); 53 | 54 | it('should return options when set full baseDir', () => { 55 | const baseDir = getFixtures('app'); 56 | const options = formatOptions({ baseDir }); 57 | assert(options); 58 | assert(options.baseDir === baseDir); 59 | }); 60 | 61 | it('should return options when set short baseDir', () => { 62 | const options = formatOptions({ baseDir: 'apps/foo' }); 63 | assert(options); 64 | assert(options.baseDir === getFixtures('apps/foo')); 65 | }); 66 | 67 | it('should return options when set customEgg', () => { 68 | const customEgg = getFixtures('bar'); 69 | const options = formatOptions({ customEgg }); 70 | assert(options); 71 | assert(options.customEgg === customEgg); 72 | assert(options.framework === customEgg); 73 | }); 74 | 75 | it('should return options when set customEgg=true', () => { 76 | const baseDir = getFixtures('bar'); 77 | mm(process, 'cwd', () => { 78 | return baseDir; 79 | }); 80 | const options = formatOptions({ customEgg: true }); 81 | assert(options); 82 | assert.equal(options.framework, baseDir); 83 | assert.equal(options.customEgg, baseDir); 84 | }); 85 | 86 | it('should return options when set framework=true', () => { 87 | const baseDir = getFixtures('bar'); 88 | mm(process, 'cwd', () => { 89 | return baseDir; 90 | }); 91 | const options = formatOptions({ framework: true }); 92 | assert(options); 93 | assert.equal(options.framework, baseDir); 94 | assert.equal(options.customEgg, baseDir); 95 | }); 96 | 97 | it('should push plugins when in plugin dir', () => { 98 | const baseDir = getFixtures('plugin'); 99 | mm(process, 'cwd', () => { 100 | return baseDir; 101 | }); 102 | const options = formatOptions(); 103 | assert(options); 104 | assert.deepEqual(options.plugins.plugin1, { 105 | enable: true, 106 | path: baseDir, 107 | }); 108 | }); 109 | 110 | it('should not push plugins when in plugin dir but options.plugin = false', () => { 111 | const baseDir = getFixtures('plugin'); 112 | mm(process, 'cwd', () => { 113 | return baseDir; 114 | }); 115 | const options = formatOptions({ 116 | plugin: false, 117 | }); 118 | assert(options); 119 | assert(!options.plugins.plugin1); 120 | }); 121 | 122 | it('should not throw when no eggPlugin', () => { 123 | const baseDir = getFixtures('plugin_throw'); 124 | mm(process, 'cwd', () => { 125 | return baseDir; 126 | }); 127 | formatOptions(); 128 | }); 129 | 130 | it('should throw when no eggPlugin and options.plugin === true', () => { 131 | const baseDir = getFixtures('plugin_throw'); 132 | mm(process, 'cwd', () => { 133 | return baseDir; 134 | }); 135 | assert.throws(() => { 136 | formatOptions({ 137 | plugin: true, 138 | }); 139 | }, new RegExp(`should set "eggPlugin" property in ${baseDir}/package.json`)); 140 | }); 141 | 142 | it('should mock process.env.HOME when EGG_SERVER_ENV is default, test, prod', () => { 143 | const baseDir = process.cwd(); 144 | 145 | mm(process.env, 'EGG_SERVER_ENV', 'local'); 146 | assert.notEqual(process.env.HOME, baseDir); 147 | 148 | mm(process.env, 'EGG_SERVER_ENV', 'default'); 149 | formatOptions(); 150 | assert.equal(process.env.HOME, baseDir); 151 | 152 | mm(process.env, 'EGG_SERVER_ENV', 'test'); 153 | formatOptions(); 154 | assert.equal(process.env.HOME, baseDir); 155 | 156 | mm(process.env, 'EGG_SERVER_ENV', 'prod'); 157 | formatOptions(); 158 | assert.equal(process.env.HOME, baseDir); 159 | }); 160 | 161 | it('should not mock process.env.HOME when it has mocked', () => { 162 | const baseDir = process.cwd(); 163 | mm(process.env, 'HOME', '/mockpath'); 164 | mm(process.env, 'EGG_SERVER_ENV', 'default'); 165 | formatOptions(); 166 | assert.notEqual(process.env.HOME, baseDir); 167 | }); 168 | }); 169 | -------------------------------------------------------------------------------- /test/helper.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { fileURLToPath } from 'node:url'; 3 | 4 | const __filename = fileURLToPath(import.meta.url); 5 | export const __dirname = path.dirname(__filename); 6 | 7 | export function getFixtures(filename: string) { 8 | return path.join(__dirname, 'fixtures', filename); 9 | } 10 | -------------------------------------------------------------------------------- /test/index.test-d.ts: -------------------------------------------------------------------------------- 1 | import { expectType } from 'tsd'; 2 | import { Context } from 'egg'; 3 | import { MockApplication, MockAgent, ResultObject } from '../src/index.js'; 4 | import { getBootstrapApp, mock, mm } from '../src/bootstrap.js'; 5 | 6 | const app = getBootstrapApp(); 7 | expectType(app); 8 | expectType(app.currentContext as Context); 9 | expectType(app.ctxStorage.getStore() as Context); 10 | expectType(mock.app()); 11 | expectType(mm.app()); 12 | 13 | expectType(mm.app().mockAgent()); 14 | 15 | expectType(mm.app().mockHttpclient('url', 'post', { data: 'ok' })); 16 | expectType(mm.app().mockHttpclient('url', 'post', 'data')); 17 | expectType(mm.app().mockHttpclient('url', { 18 | data: 'mock response', 19 | repeats: 1, 20 | })); 21 | expectType(mm.app().mockHttpclient('url', (url) => { 22 | return url; 23 | })); 24 | expectType(mm.app().mockHttpclient('url', 'post', (url) => { 25 | return url; 26 | })); 27 | expectType(mm.app().mockHttpclient('url', 'get', { 28 | data: 'mock response', 29 | repeats: 1, 30 | })); 31 | 32 | expectType(app.mockLog()); 33 | expectType(app.mockLog('logger')); 34 | expectType(app.mockLog(app.logger)); 35 | expectType(app.expectLog('foo string')); 36 | expectType(app.expectLog('foo string', 'coreLogger')); 37 | expectType(app.expectLog('foo string', app.coreLogger)); 38 | expectType(app.expectLog(/foo string/)); 39 | expectType(app.expectLog(/foo string/, 'coreLogger')); 40 | expectType(app.expectLog(/foo string/, app.coreLogger)); 41 | expectType(mm.env('default')); 42 | expectType(mm.env('devserver')); 43 | 44 | expectType>(app.mockAgentRestore()); 45 | expectType>(app.mockRestore()); 46 | expectType>(app.mockContextScope(async () => {})); 47 | expectType>(app.mockContextScope(async _ctx => {})); 48 | 49 | expectType>(app.backgroundTasksFinished()); 50 | 51 | const result = {} as ResultObject; 52 | expectType(result.status!); 53 | -------------------------------------------------------------------------------- /test/index.test-skip.ts: -------------------------------------------------------------------------------- 1 | // import mm from '../index'; 2 | // import * as path from 'path'; 3 | // const fixtures = path.join(__dirname, 'fixtures'); 4 | 5 | // const app = mm.app({ 6 | // baseDir: path.join(fixtures, 'demo'), 7 | // }); 8 | // app.ready().then(() => { 9 | // app.httpRequest() 10 | // app.mockContext({ 11 | // user: { name: 'Jason' } 12 | // }); 13 | 14 | // app.mockCookies({ 15 | // foo: 'bar' 16 | // }); 17 | 18 | // app.mockSession({ 19 | // foo: 'bar' 20 | // }); 21 | 22 | // app.mockContext(); 23 | 24 | // app.mockService('foo', 'get', function* (ctx, methodName, args) { 25 | // return 'popomore'; 26 | // }); 27 | 28 | // app.mockServiceError('foo', 'get', new Error('mock error')); 29 | 30 | // app.mockCsrf(); 31 | 32 | // app.mockHttpclient('https://eggjs.org', 'mock egg'); 33 | // app.mockHttpclient('https://eggjs.org', { 34 | // data: 'mock egg', 35 | // status: 200, 36 | // }); 37 | 38 | // app.mockHttpclient('https://eggjs.org', [ 'get' , 'head' ], { data: 'foo' }); 39 | 40 | // app.mockHttpclient('https://eggjs.org', '*', { data: 'bar' }); 41 | 42 | // app.mockHttpclient('https://eggjs.org', () => ({ 43 | // data: 'mock egg', 44 | // status: 200, 45 | // })); 46 | 47 | // app.mockHttpclient('https://eggjs.org', function (url, opts) { 48 | // url.toLowerCase(); 49 | // opts.data; 50 | // return 'a'; 51 | // }); 52 | 53 | // app.httpRequest().post('/user').set('a', 'b').send().expect(200); 54 | 55 | // console.log(' ts run ok'); 56 | // app.close(); 57 | // mm.restore(); 58 | // process.exit(0); 59 | // }).catch(e => { 60 | // console.log(e); 61 | // process.exit(1); 62 | // }); 63 | 64 | // mm.consoleLevel('INFO'); 65 | 66 | // mm.home('a') 67 | 68 | // // mm.cluster(); 69 | 70 | // mm.env('test'); 71 | 72 | // // test for bootstrap 73 | // (global as any).before = () => {}; 74 | // (global as any).afterEach = () => {}; 75 | 76 | // import { mock, app as bootApp, assert } from '../bootstrap'; 77 | // bootApp.ready; 78 | // mock.restore; 79 | // assert(true); 80 | // mock.app; 81 | -------------------------------------------------------------------------------- /test/mm.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import path from 'node:path'; 3 | import fs from 'node:fs'; 4 | import mm, { MockApplication } from '../src/index.js'; 5 | import { getFixtures } from './helper.js'; 6 | 7 | const baseDir = getFixtures('apps/env-app'); 8 | 9 | describe('test/mm.test.ts', () => { 10 | afterEach(mm.restore); 11 | 12 | describe('mm.env()', () => { 13 | let app: MockApplication; 14 | beforeEach(() => { 15 | mm(process.env, 'EGG_HOME', baseDir); 16 | }); 17 | afterEach(() => app.close()); 18 | 19 | it('should mock unittest', async () => { 20 | app = mm.app({ baseDir: 'apps/env-app', cache: false }); 21 | await app.ready(); 22 | assert(app.config.fakeplugin.foo === 'bar-unittest'); 23 | assert(app.config.logger.dir === path.join(baseDir, 'logs/env-app')); 24 | }); 25 | 26 | it('should mock test', async () => { 27 | mm.env('test'); 28 | app = mm.app({ baseDir: 'apps/env-app', cache: false }); 29 | await app.ready(); 30 | assert(app.config.fakeplugin.foo === 'bar-test'); 31 | assert(app.config.logger.dir === path.join(baseDir, 'logs/env-app')); 32 | }); 33 | 34 | it('should mock prod', async () => { 35 | mm.env('prod'); 36 | app = mm.app({ baseDir: 'apps/env-app', cache: false }); 37 | await app.ready(); 38 | assert(app.config.fakeplugin.foo === 'bar-prod'); 39 | assert(app.config.logger.dir === path.join(baseDir, 'logs/env-app')); 40 | }); 41 | 42 | it('should mock default', async () => { 43 | mm.env('default'); 44 | app = mm.app({ baseDir: 'apps/env-app', cache: false }); 45 | await app.ready(); 46 | assert(app.config.fakeplugin.foo === 'bar-default'); 47 | assert(app.config.logger.dir === path.join(baseDir, 'logs/env-app')); 48 | }); 49 | 50 | it('should mock unittest', async () => { 51 | mm.env('unittest'); 52 | app = mm.app({ baseDir: 'apps/env-app', cache: false }); 53 | await app.ready(); 54 | assert(app.config.fakeplugin.foo === 'bar-unittest'); 55 | assert(app.config.logger.dir === path.join(baseDir, 'logs/env-app')); 56 | }); 57 | 58 | it('should mock local', async () => { 59 | mm.env('local'); 60 | app = mm.app({ baseDir: 'apps/env-app', cache: false }); 61 | await app.ready(); 62 | assert(app.config.fakeplugin.foo === 'bar-default'); 63 | assert(app.config.logger.dir === path.join(baseDir, 'logs/env-app')); 64 | }); 65 | }); 66 | 67 | describe('mm.app({ clean: false })', () => { 68 | let app: MockApplication; 69 | after(() => app.close()); 70 | 71 | it('keep log dir', async () => { 72 | app = mm.app({ baseDir: 'apps/app-not-clean', clean: false }); 73 | await app.ready(); 74 | assert(fs.existsSync(getFixtures('apps/app-not-clean/logs/keep'))); 75 | }); 76 | }); 77 | 78 | describe('mm.consoleLevel()', () => { 79 | it('should mock EGG_LOG', () => { 80 | mm.consoleLevel('none'); 81 | assert(process.env.EGG_LOG === 'NONE'); 82 | }); 83 | 84 | it('should not mock', () => { 85 | mm.consoleLevel(''); 86 | assert(!process.env.EGG_LOG); 87 | }); 88 | }); 89 | 90 | describe('mm.home', () => { 91 | let app: MockApplication; 92 | const baseDir = getFixtures('apps/mockhome'); 93 | before(() => { 94 | mm.home(baseDir); 95 | app = mm.app({ baseDir: 'apps/mockhome', clean: false }); 96 | return app.ready(); 97 | }); 98 | after(() => app.close()); 99 | 100 | it('should mock home', () => { 101 | assert(app.config.HOME === baseDir); 102 | }); 103 | 104 | it('should ignore when parameter is empty', () => { 105 | mm.home(); 106 | assert(!process.env.EGG_HOME); 107 | }); 108 | }); 109 | 110 | describe('egg-mock', () => { 111 | let app: MockApplication; 112 | before(() => { 113 | app = mm.app({ 114 | baseDir: 'apps/no-framework', 115 | }); 116 | return app.ready(); 117 | }); 118 | after(() => app.close()); 119 | 120 | it('should not be a framework', () => { 121 | app.mockEnv('prod test not work'); 122 | assert(app.config.env === 'mocked by plugin'); 123 | }); 124 | }); 125 | }); 126 | -------------------------------------------------------------------------------- /test/mock_cluster_extend.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import mm, { MockApplication } from '../src/index.js'; 3 | 4 | describe('test/mock_cluster_extend.test.ts', () => { 5 | let app: MockApplication; 6 | before(() => { 7 | app = mm.cluster({ 8 | baseDir: 'demo', 9 | coverage: false, 10 | }); 11 | return app.ready(); 12 | }); 13 | after(() => app.close()); 14 | 15 | afterEach(mm.restore); 16 | 17 | it('should mock cluster with result', async () => { 18 | let result = await app.mockDevice({ name: 'egg' }); 19 | assert.deepEqual(result, { name: 'egg', mock: true }); 20 | 21 | result = await app.mockGenerator({ name: 'egg generator' }); 22 | assert.deepEqual(result, { name: 'egg generator', mock: true }); 23 | 24 | result = await app.mockPromise({ name: 'egg promise' }); 25 | assert.deepEqual(result, { name: 'egg promise', mock: true }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/mock_cluster_restore.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import fs from 'node:fs'; 3 | import { scheduler } from 'node:timers/promises'; 4 | import mm, { MockApplication } from '../src/index.js'; 5 | import { getFixtures } from './helper.js'; 6 | 7 | describe('test/mock_cluster_restore.test.ts', () => { 8 | let app: MockApplication; 9 | before(() => { 10 | app = mm.cluster({ 11 | baseDir: 'demo', 12 | coverage: false, 13 | }); 14 | return app.ready(); 15 | }); 16 | after(() => app.close()); 17 | 18 | afterEach(mm.restore); 19 | 20 | it('should mock cluster restore work', async () => { 21 | app.mockSession({ 22 | user: { 23 | foo: 'bar', 24 | }, 25 | hello: 'egg mock session data', 26 | }); 27 | await app.httpRequest() 28 | .get('/session') 29 | .expect({ 30 | user: { 31 | foo: 'bar', 32 | }, 33 | hello: 'egg mock session data', 34 | }); 35 | 36 | mm.restore(); 37 | await app.httpRequest() 38 | .get('/session') 39 | .expect({}); 40 | }); 41 | 42 | describe('handle uncaughtException', () => { 43 | let app: MockApplication; 44 | before(() => { 45 | app = mm.cluster({ 46 | baseDir: 'apps/app-throw', 47 | coverage: false, 48 | }); 49 | return app.ready(); 50 | }); 51 | after(() => app.close()); 52 | 53 | it('should handle uncaughtException and log it', async () => { 54 | await app.httpRequest() 55 | .get('/throw') 56 | .expect('foo') 57 | .expect(200); 58 | 59 | await scheduler.wait(1100); 60 | const logFile = getFixtures('apps/app-throw/logs/app-throw/common-error.log'); 61 | const body = fs.readFileSync(logFile, 'utf8'); 62 | assert(body.includes('ReferenceError: a is not defined (uncaughtException throw')); 63 | }); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /test/mock_cluster_without_security_plugin.test.ts: -------------------------------------------------------------------------------- 1 | import mm, { MockApplication } from '../src/index.js'; 2 | 3 | describe('test/mock_cluster_without_security_plugin.test.ts', () => { 4 | let app: MockApplication; 5 | before(() => { 6 | app = mm.cluster({ 7 | baseDir: 'disable-security', 8 | coverage: false, 9 | }); 10 | return app.ready(); 11 | }); 12 | after(() => app.close()); 13 | 14 | afterEach(mm.restore); 15 | 16 | it('should mock cluster work', () => { 17 | app.mockSession({ 18 | user: { 19 | foo: 'bar', 20 | }, 21 | hello: 'egg mock session data', 22 | }); 23 | return app.httpRequest() 24 | .get('/session') 25 | .expect({ 26 | user: { 27 | foo: 'bar', 28 | }, 29 | hello: 'egg mock session data', 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/mock_context.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import mm, { MockApplication } from '../src/index.js'; 3 | import { getFixtures } from './helper.js'; 4 | 5 | describe('test/mock_context.test.ts', () => { 6 | let app: MockApplication; 7 | before(done => { 8 | app = mm.app({ 9 | baseDir: 'demo', 10 | }); 11 | app.ready(done); 12 | }); 13 | after(() => app.close()); 14 | afterEach(mm.restore); 15 | 16 | it('should work on GET with user login', () => { 17 | app.mockContext({ 18 | user: { 19 | foo: 'bar', 20 | }, 21 | tracer: { 22 | traceId: 'foo-traceId', 23 | }, 24 | }); 25 | 26 | // assert(ctx.user.foo === 'bar'); 27 | return app.httpRequest() 28 | .get('/user') 29 | .expect(200) 30 | .expect({ 31 | foo: 'bar', 32 | }) 33 | .expect('x-request-url', '/user'); 34 | }); 35 | 36 | it('should work on POST with user login', () => { 37 | app.mockContext({ 38 | user: { 39 | foo: 'bar', 40 | }, 41 | }); 42 | 43 | app.mockCsrf(); 44 | return app.httpRequest() 45 | .post('/user') 46 | .send({ 47 | hi: 'body', 48 | a: 123, 49 | }) 50 | .expect(200) 51 | .expect({ 52 | params: { 53 | hi: 'body', 54 | a: 123, 55 | }, 56 | user: { 57 | foo: 'bar', 58 | }, 59 | }); 60 | }); 61 | 62 | it('should work on POST file with user login', async () => { 63 | const ctx = app.mockContext({ 64 | user: { 65 | foo: 'bar', 66 | }, 67 | traceId: `trace-${Date.now}`, 68 | }); 69 | // assert(ctx.user.foo === 'bar'); 70 | const ctxFromStorage = app.ctxStorage.getStore(); 71 | assert(ctxFromStorage === ctx); 72 | assert(app.currentContext === ctx); 73 | 74 | app.mockCsrf(); 75 | await app.httpRequest() 76 | .post('/file') 77 | .field('title', 'file title') 78 | .attach('file', getFixtures('../../package.json')) 79 | .expect(200) 80 | .expect({ 81 | fields: { 82 | title: 'file title', 83 | }, 84 | filename: 'package.json', 85 | user: { 86 | foo: 'bar', 87 | }, 88 | traceId: ctx.traceId, 89 | ctxFromStorageUser: ctxFromStorage.user, 90 | ctxFromStorageTraceId: ctxFromStorage.traceId, 91 | }); 92 | const ctxFromStorage2 = app.ctxStorage.getStore(); 93 | assert(ctxFromStorage2 === ctx); 94 | mm.restore(); 95 | const ctxFromStorage3 = app.ctxStorage.getStore(); 96 | assert(!ctxFromStorage3); 97 | assert(!app.currentContext); 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /test/mock_cookies.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import mm, { MockApplication } from '../src/index.js'; 3 | import { getFixtures } from './helper.js'; 4 | 5 | describe('test/mock_cookies.test.ts', () => { 6 | let app: MockApplication; 7 | before(done => { 8 | app = mm.app({ 9 | baseDir: getFixtures('apps/mock_cookies'), 10 | }); 11 | app.ready(done); 12 | }); 13 | after(() => app.close()); 14 | afterEach(mm.restore); 15 | 16 | it('should not return when don\'t mock cookies', done => { 17 | const ctx = app.mockContext(); 18 | assert(!ctx.cookies.get('foo')); 19 | 20 | app.httpRequest() 21 | .get('/') 22 | .expect(function(res) { 23 | assert.deepEqual(res.body, {}); 24 | }) 25 | .expect(200, done); 26 | }); 27 | 28 | it('should mock cookies', done => { 29 | app.mockCookies({ 30 | foo: 'bar cookie', 31 | }); 32 | 33 | app.httpRequest() 34 | .get('/') 35 | .expect({ 36 | cookieValue: 'bar cookie', 37 | cookiesValue: 'bar cookie', 38 | }) 39 | .expect(200, done); 40 | }); 41 | 42 | it('should pass cookie opt', done => { 43 | app.mockCookies({}); 44 | 45 | app.httpRequest() 46 | .get('/') 47 | .set('cookie', 'foo=bar cookie') 48 | .expect({ 49 | cookieValue: 'bar cookie', 50 | cookiesValue: 'bar cookie', 51 | }) 52 | .expect(200, done); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /test/mock_csrf.test.ts: -------------------------------------------------------------------------------- 1 | import mm, { MockApplication } from '../src/index.js'; 2 | import { getFixtures } from './helper.js'; 3 | 4 | describe('test/mock_csrf.test.ts', () => { 5 | let app: MockApplication; 6 | before(() => { 7 | app = mm.app({ 8 | baseDir: getFixtures('demo'), 9 | }); 10 | return app.ready(); 11 | }); 12 | after(() => app.close()); 13 | afterEach(mm.restore); 14 | 15 | it('should pass', done => { 16 | app.mockCsrf(); 17 | app.httpRequest() 18 | .post('/') 19 | .expect(200) 20 | .expect('done', done); 21 | }); 22 | 23 | it('should 403 Forbidden', async () => { 24 | await app.httpRequest() 25 | .post('/') 26 | .expect(403) 27 | .expect(/ForbiddenError: missing csrf token/); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/mock_custom_loader.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | // import { importModule } from '@eggjs/utils'; 3 | import mm, { MockApplication } from '../src/index.js'; 4 | import { getFixtures } from './helper.js'; 5 | 6 | describe('test/mock_custom_loader.test.ts', () => { 7 | let app: MockApplication; 8 | before(async () => { 9 | app = mm.app({ 10 | baseDir: getFixtures('custom-loader'), 11 | }); 12 | await app.ready(); 13 | }); 14 | after(() => app.close()); 15 | afterEach(mm.restore); 16 | 17 | it('should return success', async () => { 18 | await app.httpRequest() 19 | .get('/users/popomore') 20 | .expect({ 21 | adapter: 'docker', 22 | repository: 'popomore', 23 | }) 24 | .expect(200); 25 | }); 26 | 27 | it('should return when mock with data', async () => { 28 | app.mockRepository('user', 'get', 'mock'); 29 | app.mockAdapter('docker', 'inspectDocker', 'mock'); 30 | await app.httpRequest() 31 | .get('/users/popomore') 32 | .expect({ 33 | adapter: 'mock', 34 | repository: 'mock', 35 | }) 36 | .expect(200); 37 | }); 38 | 39 | it('should return when mock the instance', async () => { 40 | app.mockAdapter(app.adapter.docker, 'inspectDocker', 'mock'); 41 | await app.httpRequest() 42 | .get('/users/popomore') 43 | .expect({ 44 | adapter: 'mock', 45 | repository: 'popomore', 46 | }) 47 | .expect(200); 48 | }); 49 | 50 | it('should not override the existing API', async () => { 51 | // const mod = await importModule(getFixtures('../../dist/commonjs/app/extend/application.js'), { 52 | // importDefaultOnly: true, 53 | // }); 54 | assert.equal(typeof app.mockEnv, 'function'); 55 | // assert.equal(app.mockEnv, mod.prototype.mockEnv); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/mock_env.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import { scheduler } from 'node:timers/promises'; 3 | import mm, { MockApplication } from '../src/index.js'; 4 | import { getFixtures } from './helper.js'; 5 | 6 | describe('test/mock_env.test.ts', () => { 7 | let app: MockApplication; 8 | before(() => { 9 | app = mm.app({ 10 | baseDir: getFixtures('demo'), 11 | }); 12 | return app.ready(); 13 | }); 14 | after(() => app.close()); 15 | afterEach(mm.restore); 16 | 17 | it('should mock env success', () => { 18 | assert.equal(app.config.env, 'unittest'); 19 | assert.equal(app.config.serverEnv, undefined); 20 | app.mockEnv('prod'); 21 | assert.equal(app.config.env, 'prod'); 22 | assert.equal(app.config.serverEnv, 'prod'); 23 | }); 24 | 25 | it('should keep mm.restore execute in the current event loop', async () => { 26 | mm.restore(); 27 | assert.equal(app.config.env, 'unittest'); 28 | assert.equal(app.config.serverEnv, undefined); 29 | 30 | app.mockEnv('prod'); 31 | assert.equal(app.config.env, 'prod'); 32 | assert.equal(app.config.serverEnv, 'prod'); 33 | 34 | await scheduler.wait(1); 35 | assert.equal(app.config.env, 'prod'); 36 | assert.equal(app.config.serverEnv, 'prod'); 37 | 38 | // sync restore should work 39 | mm.restore(); 40 | assert.equal(app.config.env, 'unittest'); 41 | assert.equal(app.config.serverEnv, undefined); 42 | }); 43 | }); 44 | 45 | -------------------------------------------------------------------------------- /test/mock_headers.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import { request } from '@eggjs/supertest'; 3 | import mm, { MockApplication } from '../src/index.js'; 4 | import { getFixtures } from './helper.js'; 5 | 6 | describe('test/mock_headers.test.ts', () => { 7 | let app: MockApplication; 8 | before(() => { 9 | app = mm.app({ 10 | baseDir: getFixtures('demo'), 11 | }); 12 | return app.ready(); 13 | }); 14 | after(() => app.close()); 15 | afterEach(mm.restore); 16 | 17 | it('should not exists without mock', done => { 18 | app.mockContext(); 19 | 20 | request(app.callback()) 21 | .get('/header') 22 | .expect(function(res) { 23 | assert(res.body.header === ''); 24 | }) 25 | .expect(200, done); 26 | }); 27 | 28 | it('should mock headers', done => { 29 | app.mockContext(); 30 | app.mockHeaders({ 31 | customheader: 'customheader', 32 | }); 33 | request(app.callback()) 34 | .get('/header') 35 | .expect(function(res) { 36 | assert(res.body.header === 'customheader'); 37 | }) 38 | .expect(200, done); 39 | }); 40 | 41 | it('should mock headers that is uppercase', done => { 42 | app.mockContext(); 43 | app.mockHeaders({ 44 | Customheader: 'customheader', 45 | }); 46 | request(app.callback()) 47 | .get('/header') 48 | .expect(function(res) { 49 | assert(res.body.header === 'customheader'); 50 | }) 51 | .expect(200, done); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /test/mock_service.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import mm, { MockApplication } from '../src/index.js'; 3 | import { getFixtures } from './helper.js'; 4 | 5 | describe('test/mock_service.test.ts', () => { 6 | let app: MockApplication; 7 | before(async () => { 8 | app = mm.app({ 9 | baseDir: getFixtures('demo'), 10 | }); 11 | await app.ready(); 12 | }); 13 | after(() => app.close()); 14 | afterEach(mm.restore); 15 | 16 | it('should return from service', () => { 17 | return app.httpRequest() 18 | .get('/service') 19 | .expect({ 20 | foo1: 'bar', 21 | foo2: 'bar', 22 | foo3: 'bar', 23 | thirdService: 'third', 24 | }); 25 | }); 26 | 27 | it('should return from service when mock with data', () => { 28 | app.mockService('foo', 'get', 'foo'); 29 | app.mockService('foo', 'getSync', 'foo'); 30 | app.mockService('bar.foo', 'get', 'foo'); 31 | return app.httpRequest() 32 | .get('/service') 33 | .expect({ 34 | foo1: 'foo', 35 | foo2: 'foo', 36 | foo3: 'foo', 37 | thirdService: 'third', 38 | }); 39 | }); 40 | 41 | it('mock repeat success', () => { 42 | app.mockService('foo', 'get', 'foo'); 43 | app.mockService('foo', 'get', 'foo'); 44 | app.mockService('foo', 'getSync', 'foo'); 45 | app.mockService('foo', 'getSync', 'foo'); 46 | app.mockService('bar.foo', 'get', 'foo'); 47 | app.mockService('bar.foo', 'get', 'foo'); 48 | return app.httpRequest() 49 | .get('/service') 50 | .expect({ 51 | foo1: 'foo', 52 | foo2: 'foo', 53 | foo3: 'foo', 54 | thirdService: 'third', 55 | }); 56 | }); 57 | 58 | it.skip('mock error success', async () => { 59 | app.mockService('foo', 'get', new Error('async error')); 60 | try { 61 | const ctx = app.mockContext(); 62 | await ctx.service.foo.get(); 63 | throw new Error('should not execute'); 64 | } catch (err: any) { 65 | assert(err.message === 'async error'); 66 | } 67 | }); 68 | 69 | it.skip('mock sync error success', async () => { 70 | app.mockService('foo', 'getSync', new Error('sync error')); 71 | try { 72 | const ctx = app.mockContext(); 73 | await ctx.service.foo.getSync(); 74 | throw new Error('should not execute'); 75 | } catch (err: any) { 76 | assert(err.message === 'sync error'); 77 | } 78 | }); 79 | 80 | it('should return from service when mock with normal function', () => { 81 | app.mockService('foo', 'get', () => 'foo'); 82 | app.mockService('foo', 'getSync', () => 'foo'); 83 | app.mockService('bar.foo', 'get', () => 'foo'); 84 | return app.httpRequest() 85 | .get('/service') 86 | .expect({ 87 | foo1: 'foo', 88 | foo2: 'foo', 89 | foo3: 'foo', 90 | thirdService: 'third', 91 | }); 92 | }); 93 | 94 | it('should support old service format', () => { 95 | app.mockService('old', 'test', 'test'); 96 | return app.httpRequest() 97 | .get('/service/old') 98 | .expect('test'); 99 | }); 100 | 101 | it('should throw', () => { 102 | assert.throws(() => { 103 | app.mockService('foo', 'not_exist', 'foo'); 104 | }, /property not_exist in original object must be function/); 105 | }); 106 | 107 | it('should return from service when mock with generator', () => { 108 | app.mockService('foo', 'get', async () => { 109 | return 'foo'; 110 | }); 111 | return app.httpRequest() 112 | .get('/service') 113 | .expect({ 114 | foo1: 'foo', 115 | foo2: 'bar', 116 | foo3: 'bar', 117 | thirdService: 'third', 118 | }); 119 | }); 120 | 121 | it('should return from service when mock with 3 level', () => { 122 | app.mockService('foo', 'get', '1 level service'); 123 | app.mockService('bar.foo', 'get', '2 level service'); 124 | app.mockService('third.bar.foo', 'get', '3 level service'); 125 | return app.httpRequest() 126 | .get('/service') 127 | .expect({ 128 | foo1: '1 level service', 129 | foo2: '2 level service', 130 | foo3: 'bar', 131 | thirdService: '3 level service', 132 | }); 133 | }); 134 | 135 | it('should return from service when mock with error', () => { 136 | app.mockService('foo', 'get', async () => { 137 | throw new Error('mock service foo.get error'); 138 | }); 139 | return app.httpRequest() 140 | .get('/service') 141 | .expect(/mock service foo\.get error/) 142 | .expect(500); 143 | }); 144 | 145 | describe('app.mockServiceError()', () => { 146 | it('should default mock error', () => { 147 | app.mockServiceError('foo', 'get'); 148 | return app.httpRequest() 149 | .get('/service') 150 | .expect(/mock get error/) 151 | .expect(500); 152 | }); 153 | 154 | it('should create custom mock error with string', () => { 155 | app.mockServiceError('foo', 'get', 'mock service foo.get error1'); 156 | return app.httpRequest() 157 | .get('/service') 158 | .expect(/mock service foo\.get error1/) 159 | .expect(500); 160 | }); 161 | 162 | it('should return custom mock error', () => { 163 | app.mockServiceError('foo', 'get', new Error('mock service foo.get error2')); 164 | return app.httpRequest() 165 | .get('/service') 166 | .expect(/mock service foo\.get error2/) 167 | .expect(500); 168 | }); 169 | }); 170 | }); 171 | -------------------------------------------------------------------------------- /test/mock_service_async.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import mm, { MockApplication } from '../src/index.js'; 3 | import { getFixtures } from './helper.js'; 4 | 5 | describe('test/mock_service_async.test.ts', () => { 6 | let app: MockApplication; 7 | before(async () => { 8 | app = mm.app({ 9 | baseDir: getFixtures('demo-async'), 10 | }); 11 | await app.ready(); 12 | }); 13 | after(() => app.close()); 14 | afterEach(mm.restore); 15 | 16 | it('should return from service', done => { 17 | app.httpRequest() 18 | .get('/service') 19 | .expect({ 20 | foo1: 'bar', 21 | foo2: 'bar', 22 | foo3: 'bar', 23 | thirdService: 'third', 24 | }, done); 25 | }); 26 | 27 | it('should return from service when mock with data', done => { 28 | app.mockService('foo', 'get', 'foo'); 29 | app.mockService('foo', 'getSync', 'foo'); 30 | app.mockService('bar.foo', 'get', 'foo'); 31 | app.httpRequest() 32 | .get('/service') 33 | .expect({ 34 | foo1: 'foo', 35 | foo2: 'foo', 36 | foo3: 'foo', 37 | thirdService: 'third', 38 | }, done); 39 | }); 40 | 41 | it('should return from service when mock with normal function', done => { 42 | app.mockService('foo', 'get', () => 'foo'); 43 | app.mockService('foo', 'getSync', () => 'foo'); 44 | app.mockService('bar.foo', 'get', () => 'foo'); 45 | app.httpRequest() 46 | .get('/service') 47 | .expect({ 48 | foo1: 'foo', 49 | foo2: 'foo', 50 | foo3: 'foo', 51 | thirdService: 'third', 52 | }, done); 53 | }); 54 | 55 | it('should throw', () => { 56 | assert.throws(() => { 57 | app.mockService('foo', 'not_exist', 'foo'); 58 | }, /property not_exist in original object must be function/); 59 | }); 60 | 61 | it('should return from service when mock with generator', done => { 62 | app.mockService('foo', 'get', async () => { 63 | return 'foo'; 64 | }); 65 | app.httpRequest() 66 | .get('/service') 67 | .expect({ 68 | foo1: 'foo', 69 | foo2: 'bar', 70 | foo3: 'bar', 71 | thirdService: 'third', 72 | }, done); 73 | }); 74 | 75 | it('should return from service when mock with 3 level', done => { 76 | app.mockService('foo', 'get', '1 level service'); 77 | app.mockService('bar.foo', 'get', '2 level service'); 78 | app.mockService('third.bar.foo', 'get', '3 level service'); 79 | app.httpRequest() 80 | .get('/service') 81 | .expect({ 82 | foo1: '1 level service', 83 | foo2: '2 level service', 84 | foo3: 'bar', 85 | thirdService: '3 level service', 86 | }, done); 87 | }); 88 | 89 | it('should return from service when mock with error', done => { 90 | app.mockService('foo', 'get', async () => { 91 | throw new Error('mock service foo.get error'); 92 | }); 93 | app.httpRequest() 94 | .get('/service') 95 | .expect(/mock service foo\.get error/) 96 | .expect(500, done); 97 | }); 98 | 99 | describe('app.mockServiceError()', () => { 100 | it('should default mock error', done => { 101 | app.mockServiceError('foo', 'get'); 102 | app.httpRequest() 103 | .get('/service') 104 | .expect(/mock get error/) 105 | .expect(500, done); 106 | }); 107 | 108 | it('should create custom mock error with string', done => { 109 | app.mockServiceError('foo', 'get', 'mock service foo.get error1'); 110 | app.httpRequest() 111 | .get('/service') 112 | .expect(/mock service foo\.get error1/) 113 | .expect(500, done); 114 | }); 115 | 116 | it('should return custom mock error', done => { 117 | app.mockServiceError('foo', 'get', new Error('mock service foo.get error2')); 118 | app.httpRequest() 119 | .get('/service') 120 | .expect(/mock service foo\.get error2/) 121 | .expect(500, done); 122 | }); 123 | }); 124 | }); 125 | -------------------------------------------------------------------------------- /test/mock_service_cluster.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import mm, { MockApplication } from '../src/index.js'; 3 | import { getFixtures } from './helper.js'; 4 | 5 | describe('test/mock_service_cluster.test.ts', () => { 6 | let app: MockApplication; 7 | before(() => { 8 | app = mm.cluster({ 9 | baseDir: getFixtures('demo'), 10 | }); 11 | return app.ready(); 12 | }); 13 | after(() => app.close()); 14 | 15 | afterEach(mm.restore); 16 | 17 | it('should return from service', () => { 18 | return app.httpRequest() 19 | .get('/service') 20 | .expect({ 21 | foo1: 'bar', 22 | foo2: 'bar', 23 | foo3: 'bar', 24 | thirdService: 'third', 25 | }); 26 | }); 27 | 28 | it('should return from service when mock with data', () => { 29 | app.mockService('foo', 'get', 'foo'); 30 | app.mockService('foo', 'getSync', 'foo'); 31 | app.mockService('bar.foo', 'get', 'foo'); 32 | return app.httpRequest() 33 | .get('/service') 34 | .expect({ 35 | foo1: 'foo', 36 | foo2: 'foo', 37 | foo3: 'foo', 38 | thirdService: 'third', 39 | }); 40 | }); 41 | 42 | it('should support old service format', () => { 43 | app.mockService('old', 'test', 'test'); 44 | return app.httpRequest() 45 | .get('/service/old') 46 | .expect('test'); 47 | }); 48 | 49 | it('should throw not exists service error', () => { 50 | assert.throws(() => { 51 | app.mockService('foo', 'not_exist', 'foo'); 52 | }, /property not_exist in original object must be function/); 53 | }); 54 | 55 | it('should return from service when mock with generator', () => { 56 | app.mockService('foo', 'get', async () => { 57 | return 'foo'; 58 | }); 59 | return app.httpRequest() 60 | .get('/service') 61 | .expect({ 62 | foo1: 'foo', 63 | foo2: 'bar', 64 | foo3: 'bar', 65 | thirdService: 'third', 66 | }); 67 | }); 68 | 69 | it('should return from service when mock with 3 level', () => { 70 | app.mockService('foo', 'get', '1 level service'); 71 | app.mockService('bar.foo', 'get', '2 level service'); 72 | app.mockService('third.bar.foo', 'get', '3 level service'); 73 | return app.httpRequest() 74 | .get('/service') 75 | .expect({ 76 | foo1: '1 level service', 77 | foo2: '2 level service', 78 | foo3: 'bar', 79 | thirdService: '3 level service', 80 | }); 81 | }); 82 | 83 | it('should return from service when mock with error', () => { 84 | app.mockService('foo', 'get', async () => { 85 | throw new Error('mock service foo.get error'); 86 | }); 87 | return app.httpRequest() 88 | .get('/service') 89 | .expect(/mock service foo\.get error/) 90 | .expect(500); 91 | }); 92 | 93 | describe('app.mockServiceError()', () => { 94 | it('should default mock error', () => { 95 | app.mockServiceError('foo', 'get'); 96 | return app.httpRequest() 97 | .get('/service') 98 | .expect(/mock get error/) 99 | .expect(500); 100 | }); 101 | 102 | it('should create custom mock error with string', () => { 103 | app.mockServiceError('foo', 'get', 'mock service foo.get error1'); 104 | return app.httpRequest() 105 | .get('/service') 106 | .expect(/mock service foo\.get error1/) 107 | .expect(500); 108 | }); 109 | 110 | it('should return custom mock error', () => { 111 | const err = new Error('mock service foo.get error2'); 112 | (err as any).foo = 'bar'; 113 | app.mockServiceError('foo', 'get', err); 114 | return app.httpRequest() 115 | .get('/service') 116 | .expect(/mock service foo\.get error2/) 117 | .expect(500); 118 | }); 119 | }); 120 | }); 121 | -------------------------------------------------------------------------------- /test/mock_session.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'node:assert'; 2 | import mm, { MockApplication } from '../src/index.js'; 3 | import { getFixtures } from './helper.js'; 4 | 5 | describe('test/mock_session.test.ts', () => { 6 | afterEach(mm.restore); 7 | 8 | describe('single process mode', () => { 9 | let app: MockApplication; 10 | before(() => { 11 | app = mm.app({ 12 | baseDir: getFixtures('demo'), 13 | }); 14 | return app.ready(); 15 | }); 16 | after(() => app.close()); 17 | 18 | it('should mock session', () => { 19 | const obj = { 20 | user: { 21 | foo: 'bar', 22 | }, 23 | hello: 'egg mock session data', 24 | }; 25 | 26 | // const ctx = app.mockContext(); 27 | app.mockSession(obj); 28 | // assert.deepEqual(ctx.session, obj); 29 | 30 | return app.httpRequest() 31 | .get('/session') 32 | .expect({ 33 | user: { 34 | foo: 'bar', 35 | }, 36 | hello: 'egg mock session data', 37 | }); 38 | }); 39 | 40 | it('should support mock session with plain type', () => { 41 | const ctx = app.mockContext(); 42 | (app as any).mockSession(); 43 | app.mockSession('123'); 44 | assert(ctx.session); 45 | assert(!(ctx as any).session.save); 46 | assert.equal(ctx.session, '123'); 47 | }); 48 | 49 | it('should mock restore', () => { 50 | return app.httpRequest() 51 | .get('/session') 52 | .expect({}); 53 | }); 54 | }); 55 | 56 | describe('cluster process mode', () => { 57 | let app: MockApplication; 58 | before(() => { 59 | app = mm.cluster({ 60 | baseDir: getFixtures('demo'), 61 | }); 62 | return app.ready(); 63 | }); 64 | after(() => app.close()); 65 | 66 | it('should mock session', () => { 67 | app.mockSession({ 68 | user: { 69 | foo: 'bar', 70 | }, 71 | hello: 'egg mock session data', 72 | }); 73 | return app.httpRequest() 74 | .get('/session') 75 | .expect({ 76 | user: { 77 | foo: 'bar', 78 | }, 79 | hello: 'egg mock session data', 80 | }); 81 | }); 82 | 83 | it('should mock restore', () => { 84 | return app.httpRequest() 85 | .get('/session') 86 | .expect({}); 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /test/parallel.test.ts: -------------------------------------------------------------------------------- 1 | import { getFixtures } from './helper.js'; 2 | 3 | // TBD: This test case is not working as expected. Need to investigate. 4 | describe.skip('test/parallel.test.ts', () => { 5 | before(async () => { 6 | const { mochaGlobalSetup } = await import('../src/register.js'); 7 | await mochaGlobalSetup(); 8 | process.env.ENABLE_MOCHA_PARAELLEL = 'true'; 9 | process.env.AUTO_AGENT = 'true'; 10 | process.env.EGG_BASE_DIR = getFixtures('apps/foo'); 11 | }); 12 | 13 | after(async () => { 14 | const { mochaGlobalTeardown } = await import('../src/register.js'); 15 | await mochaGlobalTeardown(); 16 | delete process.env.ENABLE_MOCHA_PARAELLEL; 17 | delete process.env.AUTO_AGENT; 18 | delete process.env.EGG_BASE_DIR; 19 | }); 20 | 21 | it('should work', async () => { 22 | const { getBootstrapApp } = await import('../src/bootstrap.js'); 23 | const app = getBootstrapApp(); 24 | await app.ready(); 25 | await app.httpRequest() 26 | .get('/') 27 | .expect(200) 28 | .expect('foo'); 29 | await app.close(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/parallel_hook.test.ts: -------------------------------------------------------------------------------- 1 | import coffee from 'coffee'; 2 | import mm from '../src/index.js'; 3 | import { importResolve } from '@eggjs/utils'; 4 | import { getFixtures } from './helper.js'; 5 | 6 | // TBD: This test case is not working as expected. Need to investigate. 7 | describe.skip('test/bootstrap-plugin.test.ts', () => { 8 | after(() => mm.restore()); 9 | 10 | it('should throw', async () => { 11 | return coffee.fork(importResolve('mocha/bin/mocha'), [ 12 | '-r', getFixtures('../lib/parallel/agent_register'), 13 | '--parallel', '--jobs', '2', '--exit', 14 | ], { 15 | cwd: getFixtures('apps/parallel-test'), 16 | }) 17 | // .debug() 18 | .expect('code', 0) 19 | .expect('stdout', /3 passing/) 20 | .end(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/tsd.test.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { execSync } from 'node:child_process'; 3 | import { __dirname } from './helper.js'; 4 | 5 | describe.skip('test/tsd.test.ts', () => { 6 | it('should tsd run success', () => { 7 | // will hang up, so skip it 8 | execSync('tsd', { 9 | cwd: path.join(__dirname, '..'), 10 | }); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@eggjs/tsconfig", 3 | "compilerOptions": { 4 | "strict": true, 5 | "noImplicitAny": true, 6 | "target": "ES2022", 7 | "module": "NodeNext", 8 | "moduleResolution": "NodeNext" 9 | } 10 | } 11 | --------------------------------------------------------------------------------