From 53a9bf4f2f043dcb2200ade4cc2205d6fefbfcfc Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Mon, 18 Jul 2016 21:16:35 +0800 Subject: [PATCH] test: add test codes (#20) closes #16 --- .eslintignore | 4 +- .gitignore | 2 + .travis.yml | 2 +- appveyor.yml | 2 +- docs/plugins.puml | 13 - examples/cookie/test/index.test.js | 47 +++ examples/cookie_session/test/index.test.js | 43 +++ examples/helloworld/test/index.test.js | 35 ++ examples/multipart/config/config.default.js | 3 + examples/multipart/test/index.test.js | 63 ++++ examples/static/app/router.js | 2 +- examples/static/config/config.default.js | 3 + examples/static/test/index.test.js | 35 ++ lib/core/app/middleware/meta.js | 12 +- lib/core/app/middleware/notfound.js | 2 +- lib/core/app/middleware/site_file.js | 4 +- lib/core/config/plugin.js | 38 +- package.json | 25 +- test/benchmark/assign.js | 54 +++ test/fixtures/apps/agent-app-sync/agent.js | 9 + test/fixtures/apps/agent-app-sync/app.js | 14 + .../apps/agent-app-sync/app/router.js | 7 + .../fixtures/apps/agent-app-sync/package.json | 3 + test/fixtures/apps/agent-app/app.js | 11 + test/fixtures/apps/agent-app/app/router.js | 25 ++ test/fixtures/apps/agent-app/config/plugin.js | 10 + test/fixtures/apps/agent-app/package.json | 3 + .../agent-app/plugins/mock-client/agent.js | 29 ++ .../apps/agent-app/plugins/mock-client/app.js | 28 ++ .../mock-client/config/config.default.js | 9 + .../plugins/mock-client/mock_client.js | 65 ++++ test/fixtures/apps/agent-die/agent.js | 4 + test/fixtures/apps/agent-die/package.json | 3 + test/fixtures/apps/agent-die/start.js | 13 + test/fixtures/apps/agent-instrument/agent.js | 10 + .../apps/agent-instrument/package.json | 3 + test/fixtures/apps/agent-restart/agent.js | 17 + test/fixtures/apps/agent-restart/app.js | 14 + test/fixtures/apps/agent-restart/client.js | 3 + test/fixtures/apps/agent-restart/package.json | 3 + test/fixtures/apps/agent-throw/agent.js | 7 + test/fixtures/apps/agent-throw/app/router.js | 8 + .../apps/agent-throw/config/config.default.js | 1 + test/fixtures/apps/agent-throw/package.json | 3 + .../aliyun-egg-app/app/controller/home.js | 9 + .../apps/aliyun-egg-app/app/router.js | 5 + .../aliyun-egg-app/config/config.default.js | 1 + .../fixtures/apps/aliyun-egg-app/package.json | 3 + test/fixtures/apps/aliyun-egg-biz/index.js | 3 + .../fixtures/apps/aliyun-egg-biz/package.json | 3 + test/fixtures/apps/aliyun-egg/index.js | 7 + test/fixtures/apps/aliyun-egg/lib/agent.js | 19 + .../apps/aliyun-egg/lib/aliyun-egg.js | 37 ++ .../apps/aliyun-egg/lib/core/agent.js | 5 + test/fixtures/apps/aliyun-egg/lib/core/app.js | 5 + .../apps/aliyun-egg/lib/core/config/plugin.js | 10 + .../aliyun-egg/lib/plugins/custom/agent.js | 7 + .../apps/aliyun-egg/lib/plugins/custom/app.js | 9 + test/fixtures/apps/aliyun-egg/package.json | 3 + test/fixtures/apps/app-die/app/router.js | 13 + .../apps/app-die/config/config.default.js | 1 + test/fixtures/apps/app-die/package.json | 3 + .../apps/app-router/app/controller/home.js | 3 + test/fixtures/apps/app-router/app/router.js | 4 + test/fixtures/apps/app-router/package.json | 3 + test/fixtures/apps/app-server/app.js | 7 + test/fixtures/apps/app-server/app/router.js | 5 + .../apps/app-server/config/config.default.js | 1 + test/fixtures/apps/app-server/package.json | 3 + .../apps/body_parser_testapp/app/router.js | 19 + .../config/config.default.js | 7 + .../apps/body_parser_testapp/package.json | 3 + .../config/config.default.js | 1 + .../close-watcher-logrotator/config/plugin.js | 2 + .../close-watcher-logrotator/package.json | 3 + .../cluster_mod_app/app/controller/home.js | 5 + .../apps/cluster_mod_app/app/router.js | 5 + .../cluster_mod_app/config/config.default.js | 1 + .../apps/cluster_mod_app/package.json | 3 + .../apps/context-config-app/app/context.js | 6 + .../context-config-app/app/controller/home.js | 18 + .../apps/context-config-app/app/router.js | 4 + .../apps/context-config-app/config/config.js | 5 + .../context-config-app/config/config.local.js | 5 + .../apps/context-config-app/package.json | 3 + test/fixtures/apps/cors/app/router.js | 13 + .../apps/cors/config/config.default.js | 3 + test/fixtures/apps/cors/config/plugin.js | 1 + test/fixtures/apps/cors/package.json | 3 + .../apps/custom-env-app/app/router.js | 9 + .../custom-env-app/config/config.default.js | 3 + .../fixtures/apps/custom-env-app/package.json | 3 + .../custom-logger/config/config.default.js | 14 + test/fixtures/apps/custom-logger/package.json | 3 + .../apps/demo/app/controller/hello.js | 6 + .../fixtures/apps/demo/app/controller/home.js | 5 + .../apps/demo/app/controller/logger.js | 17 + test/fixtures/apps/demo/app/router.js | 12 + .../apps/demo/config/config.default.js | 2 + test/fixtures/apps/demo/package.json | 3 + .../apps/development/app/public/foo.js | 1 + test/fixtures/apps/development/app/router.js | 11 + .../apps/development/config/config.default.js | 1 + .../apps/development/config/plugin.js | 1 + test/fixtures/apps/development/package.json | 3 + .../encrypt-cookies/app/controller/home.js | 17 + .../apps/encrypt-cookies/app/router.js | 3 + .../encrypt-cookies/config/config.default.js | 1 + .../apps/encrypt-cookies/package.json | 3 + .../apps/favicon/app/controller/home.js | 5 + test/fixtures/apps/favicon/app/router.js | 5 + .../apps/favicon/config/config.default.js | 5 + test/fixtures/apps/favicon/package.json | 3 + .../apps/helper/app/controller/home.js | 3 + test/fixtures/apps/helper/app/router.js | 33 ++ .../apps/helper/config/config.default.js | 11 + test/fixtures/apps/helper/package.json | 3 + .../fixtures/apps/i18n/app/controller/home.js | 9 + .../apps/i18n/app/controller/message.js | 17 + test/fixtures/apps/i18n/app/router.js | 6 + test/fixtures/apps/i18n/app/views/home.html | 3 + .../fixtures/apps/i18n/config/locales/de.json | 9 + test/fixtures/apps/i18n/config/locales/xx.txt | 1 + .../apps/i18n/config/locales/zh-CN.js | 4 + test/fixtures/apps/i18n/config/plugin.js | 9 + test/fixtures/apps/i18n/package.json | 3 + .../apps/koa-session/app/controller/clear.js | 5 + .../apps/koa-session/app/controller/home.js | 11 + test/fixtures/apps/koa-session/app/router.js | 5 + .../apps/koa-session/config/config.default.js | 1 + test/fixtures/apps/koa-session/package.json | 3 + .../config/plugin.js | 22 ++ .../loader-plugin-dep-missing/package.json | 3 + .../plugins/a/package.json | 5 + .../plugins/b/package.json | 5 + .../plugins/c/package.json | 5 + .../config/plugin.js | 22 ++ .../loader-plugin-dep-recursive/package.json | 3 + .../plugins/a/package.json | 5 + .../plugins/b/package.json | 5 + .../plugins/c/package.json | 5 + .../apps/loader-plugin-dep/config/plugin.js | 39 ++ .../apps/loader-plugin-dep/package.json | 3 + .../loader-plugin-dep/plugins/a/package.json | 6 + .../loader-plugin-dep/plugins/b/package.json | 6 + .../loader-plugin-dep/plugins/c/package.json | 6 + .../loader-plugin-dep/plugins/d/package.json | 5 + .../loader-plugin-dep/plugins/e/package.json | 5 + .../loader-plugin-dep/plugins/f/package.json | 5 + .../loader-plugin-noexist/config/plugin.js | 5 + .../apps/loader-plugin-noexist/package.json | 3 + test/fixtures/apps/loader-plugin/app.js | 6 + .../fixtures/apps/loader-plugin/app/router.js | 12 + .../apps/loader-plugin/app/service/Foo4.js | 9 + .../apps/loader-plugin/app/service/foo.js | 9 + .../apps/loader-plugin/app/service/foo2.js | 3 + .../loader-plugin/app/service/foo3/foo3.js | 3 + .../loader-plugin/app/service/fooDir/Foo5.js | 9 + .../loader-plugin/config/config.default.js | 3 + .../apps/loader-plugin/config/map.json | 1 + .../apps/loader-plugin/config/plugin.js | 42 +++ .../apps/loader-plugin/node_modules/a/app.js | 6 + .../node_modules/a/app/proxy/a.js | 0 .../node_modules/a/app/service/bar1.js | 0 .../node_modules/a/config/config.js | 1 + .../loader-plugin/node_modules/a/package.json | 5 + .../node_modules/a1/package.json | 7 + .../apps/loader-plugin/node_modules/b/app.js | 6 + .../node_modules/b/app/service/bar2.js | 9 + .../b/config/antx.default.properties | 4 + .../node_modules/b/config/antx.dev.properties | 1 + .../b/config/antx.prod.properties | 2 + .../b/config/antx.test.properties | 1 + .../b/config/antx.unittest.properties | 1 + .../loader-plugin/node_modules/b/package.json | 5 + .../apps/loader-plugin/node_modules/c/app.js | 6 + .../node_modules/c/config/config.js | 3 + .../loader-plugin/node_modules/c/package.json | 5 + .../loader-plugin/node_modules/d/.gitkeep | 0 .../node_modules/d/config/config.js | 5 + .../loader-plugin/node_modules/d/package.json | 5 + .../loader-plugin/node_modules/rds/.gitkeep | 0 .../node_modules/rds/package.json | 5 + test/fixtures/apps/loader-plugin/package.json | 3 + .../apps/loader-plugin/plugins/e/package.json | 6 + .../apps/loader-plugin/plugins/f/package.json | 6 + .../apps/loader-plugin/plugins/g/package.json | 6 + test/fixtures/apps/locals/app.js | 10 + test/fixtures/apps/locals/app/helper.js | 5 + test/fixtures/apps/locals/app/router.js | 129 +++++++ .../apps/locals/config/config.default.js | 1 + test/fixtures/apps/locals/config/plugin.js | 3 + test/fixtures/apps/locals/package.json | 3 + .../apps/logger-level-debug/app/router.js | 8 + .../config/config.default.js | 5 + .../apps/logger-level-debug/package.json | 3 + .../config/config.default.js | 5 + .../apps/logger-output-json/config/map.json | 1 + .../apps/logger-output-json/package.json | 3 + .../apps/logger-reload/app/controller/home.js | 8 + .../fixtures/apps/logger-reload/app/router.js | 3 + test/fixtures/apps/logger-reload/package.json | 3 + test/fixtures/apps/logger/agent.js | 6 + test/fixtures/apps/logger/app.js | 10 + .../apps/logger/config/config.default.js | 16 + .../apps/logger/config/config.local.js | 7 + test/fixtures/apps/logger/package.json | 3 + .../apps/logrotater-app/app/router.js | 7 + .../logrotater-app/config/config.default.js | 7 + .../fixtures/apps/logrotater-app/package.json | 3 + .../apps/master-worker-started/package.json | 3 + .../apps/messenger-app-agent/agent.js | 12 + test/fixtures/apps/messenger-app-agent/app.js | 11 + .../apps/messenger-app-agent/package.json | 3 + test/fixtures/apps/messenger-random/agent.js | 9 + test/fixtures/apps/messenger-random/app.js | 6 + .../apps/messenger-random/package.json | 3 + test/fixtures/apps/messenger/agent.js | 13 + test/fixtures/apps/messenger/app.js | 13 + test/fixtures/apps/messenger/package.json | 3 + .../apps/middlewares/app/controller/home.js | 5 + .../apps/middlewares/app/controller/trace.js | 26 ++ .../apps/middlewares/app/crossdomain.xml | 1 + test/fixtures/apps/middlewares/app/robots.txt | 5 + test/fixtures/apps/middlewares/app/router.js | 8 + .../apps/middlewares/config/config.js | 16 + test/fixtures/apps/middlewares/package.json | 3 + test/fixtures/apps/middlewares/server.conf | 1 + .../mock-dev-app/config/config.default.js | 1 + test/fixtures/apps/mock-dev-app/package.json | 3 + .../apps/mock-production-app/config/config.js | 12 + .../apps/mock-production-app/config/map.json | 3 + .../apps/mock-production-app/package.json | 3 + .../apps/nobuffer-logger/package.json | 3 + .../apps/notfound-custom-404/app/router.js | 5 + .../config/config.default.js | 5 + .../apps/notfound-custom-404/package.json | 3 + .../apps/notfound/config/config.default.js | 5 + test/fixtures/apps/notfound/package.json | 3 + test/fixtures/apps/notready/a/app.js | 5 + test/fixtures/apps/notready/a/package.json | 6 + test/fixtures/apps/notready/config/plugin.js | 8 + test/fixtures/apps/notready/package.json | 3 + .../apps/onerror/app/controller/home.js | 21 ++ .../apps/onerror/app/controller/user.js | 10 + test/fixtures/apps/onerror/app/router.js | 6 + .../apps/onerror/config/config.default.js | 7 + test/fixtures/apps/onerror/package.json | 3 + .../apps/override_method/app/router.js | 13 + .../override_method/config/config.default.js | 8 + .../apps/override_method/package.json | 3 + .../app/controller/home.js | 6 + .../apps/querystring-extended/app/router.js | 3 + .../config/config.default.js | 9 + .../apps/querystring-extended/package.json | 3 + .../apps/reload-worker/app/controller/home.js | 0 .../reload-worker/app/controller/home1.js | 1 + .../fixtures/apps/reload-worker/app/router.js | 5 + test/fixtures/apps/reload-worker/package.json | 3 + .../fixtures/apps/rest/app/apis/categories.js | 18 + test/fixtures/apps/rest/app/apis/posts.js | 7 + .../apps/rest/app/apis/posts/replies.js | 11 + .../apps/rest/app/apis/sites/channels.js | 3 + .../apps/rest/app/apis/sites/index.js | 3 + test/fixtures/apps/rest/app/apis/users.js | 94 +++++ .../apps/rest/app/apis/users/posts.js | 19 + .../apps/rest/app/apis/users/posts/replies.js | 3 + .../apps/rest/config/config.default.js | 13 + test/fixtures/apps/rest/config/plugin.js | 1 + test/fixtures/apps/rest/package.json | 3 + .../apps/router-app/app/controller/locals.js | 5 + .../apps/router-app/app/controller/members.js | 15 + .../apps/router-app/app/controller/posts.js | 29 ++ test/fixtures/apps/router-app/app/router.js | 6 + .../router-app/app/views/locals/router.html | 1 + .../apps/router-app/config/config.default.js | 1 + test/fixtures/apps/router-app/package.json | 3 + .../apps/schedule/app/schedule/sub/cron.js | 10 + test/fixtures/apps/schedule/package.json | 3 + .../apps/secure-app/app/controller/index.js | 34 ++ test/fixtures/apps/secure-app/app/router.js | 4 + .../apps/secure-app/config/config.default.js | 3 + test/fixtures/apps/secure-app/package.json | 3 + .../apps/service-app/app/controller/user.js | 5 + test/fixtures/apps/service-app/app/router.js | 3 + .../apps/service-app/app/service/user.js | 17 + test/fixtures/apps/service-app/package.json | 3 + .../services_loader_verify/app/service/foo.js | 17 + .../apps/services_loader_verify/package.json | 3 + test/fixtures/apps/singleton-demo/agent.js | 7 + test/fixtures/apps/singleton-demo/app.js | 7 + .../singleton-demo/config/config.default.js | 12 + test/fixtures/apps/singleton-demo/create.js | 23 ++ .../fixtures/apps/singleton-demo/package.json | 3 + .../apps/static-server/app/public/foo.js | 1 + .../static-server/config/config.default.js | 1 + .../apps/static-server/config/plugin.js | 1 + test/fixtures/apps/static-server/package.json | 6 + .../subdir-services/app/controller/home.js | 14 + .../apps/subdir-services/app/router.js | 3 + .../certify-personal/mobile-hi/do_certify.js | 19 + .../subdir-services/app/service/cif/user.js | 18 + .../subdir-services/app/service/foo/bar.js | 18 + .../app/service/foo/subdir/bar.js | 18 + .../app/service/foo/subdir1/subdir11/bar.js | 18 + .../app/service/foo/subdir2/sub2.js | 18 + .../apps/subdir-services/app/service/ok.js | 17 + .../subdir-services/app/service/old_style.js | 3 + .../apps/subdir-services/app/service/user.js | 17 + .../apps/subdir-services/package.json | 3 + .../apps/userrole/app/controller/admin.js | 5 + .../apps/userrole/app/controller/user.js | 5 + test/fixtures/apps/userrole/app/router.js | 8 + .../apps/userrole/config/config.default.js | 14 + test/fixtures/apps/userrole/config/role.js | 10 + test/fixtures/apps/userrole/package.json | 3 + .../apps/userservice/app/controller/home.js | 8 + test/fixtures/apps/userservice/app/router.js | 5 + .../apps/userservice/config/config.default.js | 18 + test/fixtures/apps/userservice/package.json | 3 + .../apps/validate_form/app/controller/user.js | 16 + .../fixtures/apps/validate_form/app/router.js | 5 + .../validate_form/config/config.default.js | 8 + test/fixtures/apps/validate_form/package.json | 3 + test/fixtures/apps/view-framework/index.js | 6 + test/fixtures/apps/view-framework/lib/view.js | 38 ++ .../fixtures/apps/view-framework/package.json | 3 + test/fixtures/apps/view-render/app/a.js | 1 + .../view-render/app/controller/context.js | 7 + .../apps/view-render/app/controller/csrf.js | 3 + .../apps/view-render/app/controller/empty.js | 3 + .../apps/view-render/app/controller/home.js | 3 + .../apps/view-render/app/controller/inject.js | 5 + .../apps/view-render/app/controller/locals.js | 7 + .../apps/view-render/app/controller/nonce.js | 3 + .../apps/view-render/app/controller/shtml.js | 6 + .../apps/view-render/app/controller/sjs.js | 6 + .../apps/view-render/app/controller/string.js | 7 + .../apps/view-render/app/controller/xss.js | 6 + test/fixtures/apps/view-render/app/helper.js | 7 + test/fixtures/apps/view-render/app/router.js | 14 + .../apps/view-render/app/views/a.html | 1 + .../apps/view-render/app/views/form_csrf.html | 6 + .../apps/view-render/app/views/index.html | 4 + .../apps/view-render/app/views/inject.html | 4 + .../apps/view-render/app/views/locals.html | 6 + .../apps/view-render/app/views/nonce.html | 8 + .../apps/view-render/app/views/shtml.html | 1 + .../apps/view-render/app/views/sjs.html | 1 + .../apps/view-render/app/views/xss.html | 4 + .../apps/view-render/config/config.default.js | 5 + .../apps/view-render/config/plugin.js | 6 + test/fixtures/apps/view-render/package.json | 3 + test/fixtures/apps/view/app/app.js | 5 + .../fixtures/apps/view/app/controller/home.js | 16 + test/fixtures/apps/view/app/helper.js | 3 + test/fixtures/apps/view/app/router.js | 6 + test/fixtures/apps/view/app/views/index.html | 2 + .../apps/view/config/config.default.js | 1 + test/fixtures/apps/view/package.json | 3 + .../apps/watcher-development-app/agent.js | 26 ++ .../watcher-development-app/app/router.js | 55 +++ .../config/config.unittest.js | 7 + .../apps/watcher-development-app/package.json | 3 + .../watcher-development-app/tmp-agent.txt | 1 + .../watcher-development-app/tmp-agent/tmp.txt | 0 .../apps/watcher-development-app/tmp.txt | 1 + .../apps/watcher-development-app/tmp/tmp.txt | 1 + .../config/config.unittest.js | 5 + .../apps/watcher-type-default/package.json | 3 + test/fixtures/apps/worker-die/app.js | 1 + test/fixtures/apps/worker-die/package.json | 3 + test/fixtures/custom-egg/index.js | 3 + test/fixtures/custom-egg/package.json | 3 + test/lib/agent.test.js | 88 +++++ test/lib/application.test.js | 93 +++++ test/lib/cluster/agent_worker.test.js | 86 +++++ test/lib/cluster/app_worker.test.js | 22 ++ test/lib/cluster/master.test.js | 206 +++++++++++ test/lib/core/agent_worker_client.test.js | 289 +++++++++++++++ test/lib/core/app/extend/agent.test.js | 54 +++ test/lib/core/app/extend/application.test.js | 113 ++++++ .../lib/core/app/extend/context.jsonp.test.js | 43 +++ test/lib/core/app/extend/context.test.js | 308 ++++++++++++++++ test/lib/core/app/extend/helper.test.js | 70 ++++ test/lib/core/app/extend/request.test.js | 334 ++++++++++++++++++ .../core/app/middleware/body_parser.test.js | 57 +++ test/lib/core/app/middleware/meta.test.js | 65 ++++ test/lib/core/app/middleware/notfound.test.js | 88 +++++ .../app/middleware/override_method.test.js | 37 ++ test/lib/core/app/middleware/security.test.js | 105 ++++++ .../lib/core/app/middleware/site_file.test.js | 98 +++++ test/lib/core/app_worker_client.test.js | 204 +++++++++++ test/lib/core/config/config.test.js | 19 + test/lib/core/cookies.test.js | 249 +++++++++++++ test/lib/core/loader/config_loader.test.js | 26 ++ test/lib/core/loader/load_app.test.js | 28 ++ test/lib/core/loader/load_plugin.test.js | 250 +++++++++++++ test/lib/core/loader/load_router.test.js | 27 ++ test/lib/core/loader/load_service.test.js | 108 ++++++ test/lib/core/logger.test.js | 267 ++++++++++++++ test/lib/core/messenger.test.js | 167 +++++++++ test/lib/core/router.test.js | 157 ++++++++ test/lib/core/singleton.test.js | 138 ++++++++ test/lib/core/urllib.test.js | 83 +++++ test/lib/core/util.test.js | 23 ++ test/lib/plugins/cors.test.js | 36 ++ test/lib/plugins/depd.test.js | 23 ++ test/lib/plugins/development.test.js | 100 ++++++ test/lib/plugins/empty.txt | 0 test/lib/plugins/i18n.test.js | 53 +++ test/lib/plugins/logrotater.test.js | 34 ++ test/lib/plugins/multipart.test.js | 53 +++ test/lib/plugins/onerror.test.js | 27 ++ test/lib/plugins/rest.test.js | 48 +++ test/lib/plugins/schedule.test.js | 34 ++ test/lib/plugins/session.test.js | 75 ++++ test/lib/plugins/static.test.js | 19 + test/lib/plugins/userrole.test.js | 27 ++ test/lib/plugins/userservice.test.js | 26 ++ test/lib/plugins/validate.test.js | 27 ++ test/lib/plugins/view/render.test.js | 44 +++ test/lib/plugins/watcher.test.js | 167 +++++++++ test/mocha.opts | 1 + test/utils.js | 87 +++++ 425 files changed, 7886 insertions(+), 56 deletions(-) create mode 100644 examples/cookie/test/index.test.js create mode 100644 examples/cookie_session/test/index.test.js create mode 100644 examples/helloworld/test/index.test.js create mode 100644 examples/multipart/config/config.default.js create mode 100644 examples/multipart/test/index.test.js create mode 100644 examples/static/config/config.default.js create mode 100644 examples/static/test/index.test.js create mode 100644 test/benchmark/assign.js create mode 100644 test/fixtures/apps/agent-app-sync/agent.js create mode 100644 test/fixtures/apps/agent-app-sync/app.js create mode 100644 test/fixtures/apps/agent-app-sync/app/router.js create mode 100644 test/fixtures/apps/agent-app-sync/package.json create mode 100644 test/fixtures/apps/agent-app/app.js create mode 100644 test/fixtures/apps/agent-app/app/router.js create mode 100644 test/fixtures/apps/agent-app/config/plugin.js create mode 100644 test/fixtures/apps/agent-app/package.json create mode 100644 test/fixtures/apps/agent-app/plugins/mock-client/agent.js create mode 100644 test/fixtures/apps/agent-app/plugins/mock-client/app.js create mode 100644 test/fixtures/apps/agent-app/plugins/mock-client/config/config.default.js create mode 100644 test/fixtures/apps/agent-app/plugins/mock-client/mock_client.js create mode 100644 test/fixtures/apps/agent-die/agent.js create mode 100644 test/fixtures/apps/agent-die/package.json create mode 100644 test/fixtures/apps/agent-die/start.js create mode 100644 test/fixtures/apps/agent-instrument/agent.js create mode 100644 test/fixtures/apps/agent-instrument/package.json create mode 100644 test/fixtures/apps/agent-restart/agent.js create mode 100644 test/fixtures/apps/agent-restart/app.js create mode 100644 test/fixtures/apps/agent-restart/client.js create mode 100644 test/fixtures/apps/agent-restart/package.json create mode 100644 test/fixtures/apps/agent-throw/agent.js create mode 100644 test/fixtures/apps/agent-throw/app/router.js create mode 100644 test/fixtures/apps/agent-throw/config/config.default.js create mode 100644 test/fixtures/apps/agent-throw/package.json create mode 100644 test/fixtures/apps/aliyun-egg-app/app/controller/home.js create mode 100644 test/fixtures/apps/aliyun-egg-app/app/router.js create mode 100644 test/fixtures/apps/aliyun-egg-app/config/config.default.js create mode 100644 test/fixtures/apps/aliyun-egg-app/package.json create mode 100644 test/fixtures/apps/aliyun-egg-biz/index.js create mode 100644 test/fixtures/apps/aliyun-egg-biz/package.json create mode 100644 test/fixtures/apps/aliyun-egg/index.js create mode 100644 test/fixtures/apps/aliyun-egg/lib/agent.js create mode 100644 test/fixtures/apps/aliyun-egg/lib/aliyun-egg.js create mode 100644 test/fixtures/apps/aliyun-egg/lib/core/agent.js create mode 100644 test/fixtures/apps/aliyun-egg/lib/core/app.js create mode 100644 test/fixtures/apps/aliyun-egg/lib/core/config/plugin.js create mode 100644 test/fixtures/apps/aliyun-egg/lib/plugins/custom/agent.js create mode 100644 test/fixtures/apps/aliyun-egg/lib/plugins/custom/app.js create mode 100644 test/fixtures/apps/aliyun-egg/package.json create mode 100644 test/fixtures/apps/app-die/app/router.js create mode 100644 test/fixtures/apps/app-die/config/config.default.js create mode 100644 test/fixtures/apps/app-die/package.json create mode 100644 test/fixtures/apps/app-router/app/controller/home.js create mode 100644 test/fixtures/apps/app-router/app/router.js create mode 100644 test/fixtures/apps/app-router/package.json create mode 100644 test/fixtures/apps/app-server/app.js create mode 100644 test/fixtures/apps/app-server/app/router.js create mode 100644 test/fixtures/apps/app-server/config/config.default.js create mode 100644 test/fixtures/apps/app-server/package.json create mode 100644 test/fixtures/apps/body_parser_testapp/app/router.js create mode 100644 test/fixtures/apps/body_parser_testapp/config/config.default.js create mode 100644 test/fixtures/apps/body_parser_testapp/package.json create mode 100644 test/fixtures/apps/close-watcher-logrotator/config/config.default.js create mode 100644 test/fixtures/apps/close-watcher-logrotator/config/plugin.js create mode 100644 test/fixtures/apps/close-watcher-logrotator/package.json create mode 100644 test/fixtures/apps/cluster_mod_app/app/controller/home.js create mode 100644 test/fixtures/apps/cluster_mod_app/app/router.js create mode 100644 test/fixtures/apps/cluster_mod_app/config/config.default.js create mode 100644 test/fixtures/apps/cluster_mod_app/package.json create mode 100644 test/fixtures/apps/context-config-app/app/context.js create mode 100644 test/fixtures/apps/context-config-app/app/controller/home.js create mode 100644 test/fixtures/apps/context-config-app/app/router.js create mode 100644 test/fixtures/apps/context-config-app/config/config.js create mode 100644 test/fixtures/apps/context-config-app/config/config.local.js create mode 100644 test/fixtures/apps/context-config-app/package.json create mode 100644 test/fixtures/apps/cors/app/router.js create mode 100644 test/fixtures/apps/cors/config/config.default.js create mode 100644 test/fixtures/apps/cors/config/plugin.js create mode 100644 test/fixtures/apps/cors/package.json create mode 100644 test/fixtures/apps/custom-env-app/app/router.js create mode 100644 test/fixtures/apps/custom-env-app/config/config.default.js create mode 100644 test/fixtures/apps/custom-env-app/package.json create mode 100644 test/fixtures/apps/custom-logger/config/config.default.js create mode 100644 test/fixtures/apps/custom-logger/package.json create mode 100644 test/fixtures/apps/demo/app/controller/hello.js create mode 100644 test/fixtures/apps/demo/app/controller/home.js create mode 100644 test/fixtures/apps/demo/app/controller/logger.js create mode 100644 test/fixtures/apps/demo/app/router.js create mode 100644 test/fixtures/apps/demo/config/config.default.js create mode 100644 test/fixtures/apps/demo/package.json create mode 100644 test/fixtures/apps/development/app/public/foo.js create mode 100644 test/fixtures/apps/development/app/router.js create mode 100644 test/fixtures/apps/development/config/config.default.js create mode 100644 test/fixtures/apps/development/config/plugin.js create mode 100644 test/fixtures/apps/development/package.json create mode 100644 test/fixtures/apps/encrypt-cookies/app/controller/home.js create mode 100644 test/fixtures/apps/encrypt-cookies/app/router.js create mode 100644 test/fixtures/apps/encrypt-cookies/config/config.default.js create mode 100644 test/fixtures/apps/encrypt-cookies/package.json create mode 100644 test/fixtures/apps/favicon/app/controller/home.js create mode 100644 test/fixtures/apps/favicon/app/router.js create mode 100644 test/fixtures/apps/favicon/config/config.default.js create mode 100644 test/fixtures/apps/favicon/package.json create mode 100644 test/fixtures/apps/helper/app/controller/home.js create mode 100644 test/fixtures/apps/helper/app/router.js create mode 100644 test/fixtures/apps/helper/config/config.default.js create mode 100644 test/fixtures/apps/helper/package.json create mode 100644 test/fixtures/apps/i18n/app/controller/home.js create mode 100644 test/fixtures/apps/i18n/app/controller/message.js create mode 100644 test/fixtures/apps/i18n/app/router.js create mode 100644 test/fixtures/apps/i18n/app/views/home.html create mode 100644 test/fixtures/apps/i18n/config/locales/de.json create mode 100644 test/fixtures/apps/i18n/config/locales/xx.txt create mode 100644 test/fixtures/apps/i18n/config/locales/zh-CN.js create mode 100644 test/fixtures/apps/i18n/config/plugin.js create mode 100644 test/fixtures/apps/i18n/package.json create mode 100644 test/fixtures/apps/koa-session/app/controller/clear.js create mode 100644 test/fixtures/apps/koa-session/app/controller/home.js create mode 100644 test/fixtures/apps/koa-session/app/router.js create mode 100644 test/fixtures/apps/koa-session/config/config.default.js create mode 100644 test/fixtures/apps/koa-session/package.json create mode 100644 test/fixtures/apps/loader-plugin-dep-missing/config/plugin.js create mode 100644 test/fixtures/apps/loader-plugin-dep-missing/package.json create mode 100644 test/fixtures/apps/loader-plugin-dep-missing/plugins/a/package.json create mode 100644 test/fixtures/apps/loader-plugin-dep-missing/plugins/b/package.json create mode 100644 test/fixtures/apps/loader-plugin-dep-missing/plugins/c/package.json create mode 100644 test/fixtures/apps/loader-plugin-dep-recursive/config/plugin.js create mode 100644 test/fixtures/apps/loader-plugin-dep-recursive/package.json create mode 100644 test/fixtures/apps/loader-plugin-dep-recursive/plugins/a/package.json create mode 100644 test/fixtures/apps/loader-plugin-dep-recursive/plugins/b/package.json create mode 100644 test/fixtures/apps/loader-plugin-dep-recursive/plugins/c/package.json create mode 100644 test/fixtures/apps/loader-plugin-dep/config/plugin.js create mode 100644 test/fixtures/apps/loader-plugin-dep/package.json create mode 100644 test/fixtures/apps/loader-plugin-dep/plugins/a/package.json create mode 100644 test/fixtures/apps/loader-plugin-dep/plugins/b/package.json create mode 100644 test/fixtures/apps/loader-plugin-dep/plugins/c/package.json create mode 100644 test/fixtures/apps/loader-plugin-dep/plugins/d/package.json create mode 100644 test/fixtures/apps/loader-plugin-dep/plugins/e/package.json create mode 100644 test/fixtures/apps/loader-plugin-dep/plugins/f/package.json create mode 100644 test/fixtures/apps/loader-plugin-noexist/config/plugin.js create mode 100644 test/fixtures/apps/loader-plugin-noexist/package.json create mode 100644 test/fixtures/apps/loader-plugin/app.js create mode 100644 test/fixtures/apps/loader-plugin/app/router.js create mode 100644 test/fixtures/apps/loader-plugin/app/service/Foo4.js create mode 100644 test/fixtures/apps/loader-plugin/app/service/foo.js create mode 100644 test/fixtures/apps/loader-plugin/app/service/foo2.js create mode 100644 test/fixtures/apps/loader-plugin/app/service/foo3/foo3.js create mode 100644 test/fixtures/apps/loader-plugin/app/service/fooDir/Foo5.js create mode 100644 test/fixtures/apps/loader-plugin/config/config.default.js create mode 100644 test/fixtures/apps/loader-plugin/config/map.json create mode 100644 test/fixtures/apps/loader-plugin/config/plugin.js create mode 100644 test/fixtures/apps/loader-plugin/node_modules/a/app.js create mode 100644 test/fixtures/apps/loader-plugin/node_modules/a/app/proxy/a.js create mode 100644 test/fixtures/apps/loader-plugin/node_modules/a/app/service/bar1.js create mode 100644 test/fixtures/apps/loader-plugin/node_modules/a/config/config.js create mode 100644 test/fixtures/apps/loader-plugin/node_modules/a/package.json create mode 100644 test/fixtures/apps/loader-plugin/node_modules/a1/package.json create mode 100644 test/fixtures/apps/loader-plugin/node_modules/b/app.js create mode 100644 test/fixtures/apps/loader-plugin/node_modules/b/app/service/bar2.js create mode 100644 test/fixtures/apps/loader-plugin/node_modules/b/config/antx.default.properties create mode 100644 test/fixtures/apps/loader-plugin/node_modules/b/config/antx.dev.properties create mode 100644 test/fixtures/apps/loader-plugin/node_modules/b/config/antx.prod.properties create mode 100644 test/fixtures/apps/loader-plugin/node_modules/b/config/antx.test.properties create mode 100644 test/fixtures/apps/loader-plugin/node_modules/b/config/antx.unittest.properties create mode 100644 test/fixtures/apps/loader-plugin/node_modules/b/package.json create mode 100644 test/fixtures/apps/loader-plugin/node_modules/c/app.js create mode 100644 test/fixtures/apps/loader-plugin/node_modules/c/config/config.js create mode 100644 test/fixtures/apps/loader-plugin/node_modules/c/package.json create mode 100644 test/fixtures/apps/loader-plugin/node_modules/d/.gitkeep create mode 100644 test/fixtures/apps/loader-plugin/node_modules/d/config/config.js create mode 100644 test/fixtures/apps/loader-plugin/node_modules/d/package.json create mode 100644 test/fixtures/apps/loader-plugin/node_modules/rds/.gitkeep create mode 100644 test/fixtures/apps/loader-plugin/node_modules/rds/package.json create mode 100644 test/fixtures/apps/loader-plugin/package.json create mode 100644 test/fixtures/apps/loader-plugin/plugins/e/package.json create mode 100644 test/fixtures/apps/loader-plugin/plugins/f/package.json create mode 100644 test/fixtures/apps/loader-plugin/plugins/g/package.json create mode 100644 test/fixtures/apps/locals/app.js create mode 100644 test/fixtures/apps/locals/app/helper.js create mode 100644 test/fixtures/apps/locals/app/router.js create mode 100644 test/fixtures/apps/locals/config/config.default.js create mode 100644 test/fixtures/apps/locals/config/plugin.js create mode 100644 test/fixtures/apps/locals/package.json create mode 100644 test/fixtures/apps/logger-level-debug/app/router.js create mode 100644 test/fixtures/apps/logger-level-debug/config/config.default.js create mode 100644 test/fixtures/apps/logger-level-debug/package.json create mode 100644 test/fixtures/apps/logger-output-json/config/config.default.js create mode 100644 test/fixtures/apps/logger-output-json/config/map.json create mode 100644 test/fixtures/apps/logger-output-json/package.json create mode 100644 test/fixtures/apps/logger-reload/app/controller/home.js create mode 100644 test/fixtures/apps/logger-reload/app/router.js create mode 100644 test/fixtures/apps/logger-reload/package.json create mode 100644 test/fixtures/apps/logger/agent.js create mode 100644 test/fixtures/apps/logger/app.js create mode 100644 test/fixtures/apps/logger/config/config.default.js create mode 100644 test/fixtures/apps/logger/config/config.local.js create mode 100644 test/fixtures/apps/logger/package.json create mode 100755 test/fixtures/apps/logrotater-app/app/router.js create mode 100755 test/fixtures/apps/logrotater-app/config/config.default.js create mode 100755 test/fixtures/apps/logrotater-app/package.json create mode 100644 test/fixtures/apps/master-worker-started/package.json create mode 100644 test/fixtures/apps/messenger-app-agent/agent.js create mode 100644 test/fixtures/apps/messenger-app-agent/app.js create mode 100644 test/fixtures/apps/messenger-app-agent/package.json create mode 100644 test/fixtures/apps/messenger-random/agent.js create mode 100644 test/fixtures/apps/messenger-random/app.js create mode 100644 test/fixtures/apps/messenger-random/package.json create mode 100644 test/fixtures/apps/messenger/agent.js create mode 100644 test/fixtures/apps/messenger/app.js create mode 100644 test/fixtures/apps/messenger/package.json create mode 100644 test/fixtures/apps/middlewares/app/controller/home.js create mode 100644 test/fixtures/apps/middlewares/app/controller/trace.js create mode 100644 test/fixtures/apps/middlewares/app/crossdomain.xml create mode 100644 test/fixtures/apps/middlewares/app/robots.txt create mode 100644 test/fixtures/apps/middlewares/app/router.js create mode 100644 test/fixtures/apps/middlewares/config/config.js create mode 100644 test/fixtures/apps/middlewares/package.json create mode 100644 test/fixtures/apps/middlewares/server.conf create mode 100644 test/fixtures/apps/mock-dev-app/config/config.default.js create mode 100644 test/fixtures/apps/mock-dev-app/package.json create mode 100644 test/fixtures/apps/mock-production-app/config/config.js create mode 100644 test/fixtures/apps/mock-production-app/config/map.json create mode 100644 test/fixtures/apps/mock-production-app/package.json create mode 100644 test/fixtures/apps/nobuffer-logger/package.json create mode 100644 test/fixtures/apps/notfound-custom-404/app/router.js create mode 100644 test/fixtures/apps/notfound-custom-404/config/config.default.js create mode 100644 test/fixtures/apps/notfound-custom-404/package.json create mode 100644 test/fixtures/apps/notfound/config/config.default.js create mode 100644 test/fixtures/apps/notfound/package.json create mode 100644 test/fixtures/apps/notready/a/app.js create mode 100644 test/fixtures/apps/notready/a/package.json create mode 100644 test/fixtures/apps/notready/config/plugin.js create mode 100644 test/fixtures/apps/notready/package.json create mode 100644 test/fixtures/apps/onerror/app/controller/home.js create mode 100644 test/fixtures/apps/onerror/app/controller/user.js create mode 100644 test/fixtures/apps/onerror/app/router.js create mode 100644 test/fixtures/apps/onerror/config/config.default.js create mode 100644 test/fixtures/apps/onerror/package.json create mode 100644 test/fixtures/apps/override_method/app/router.js create mode 100644 test/fixtures/apps/override_method/config/config.default.js create mode 100644 test/fixtures/apps/override_method/package.json create mode 100644 test/fixtures/apps/querystring-extended/app/controller/home.js create mode 100644 test/fixtures/apps/querystring-extended/app/router.js create mode 100644 test/fixtures/apps/querystring-extended/config/config.default.js create mode 100644 test/fixtures/apps/querystring-extended/package.json create mode 100644 test/fixtures/apps/reload-worker/app/controller/home.js create mode 100644 test/fixtures/apps/reload-worker/app/controller/home1.js create mode 100644 test/fixtures/apps/reload-worker/app/router.js create mode 100644 test/fixtures/apps/reload-worker/package.json create mode 100644 test/fixtures/apps/rest/app/apis/categories.js create mode 100644 test/fixtures/apps/rest/app/apis/posts.js create mode 100644 test/fixtures/apps/rest/app/apis/posts/replies.js create mode 100644 test/fixtures/apps/rest/app/apis/sites/channels.js create mode 100644 test/fixtures/apps/rest/app/apis/sites/index.js create mode 100644 test/fixtures/apps/rest/app/apis/users.js create mode 100644 test/fixtures/apps/rest/app/apis/users/posts.js create mode 100644 test/fixtures/apps/rest/app/apis/users/posts/replies.js create mode 100644 test/fixtures/apps/rest/config/config.default.js create mode 100644 test/fixtures/apps/rest/config/plugin.js create mode 100644 test/fixtures/apps/rest/package.json create mode 100644 test/fixtures/apps/router-app/app/controller/locals.js create mode 100644 test/fixtures/apps/router-app/app/controller/members.js create mode 100644 test/fixtures/apps/router-app/app/controller/posts.js create mode 100644 test/fixtures/apps/router-app/app/router.js create mode 100644 test/fixtures/apps/router-app/app/views/locals/router.html create mode 100644 test/fixtures/apps/router-app/config/config.default.js create mode 100644 test/fixtures/apps/router-app/package.json create mode 100644 test/fixtures/apps/schedule/app/schedule/sub/cron.js create mode 100644 test/fixtures/apps/schedule/package.json create mode 100644 test/fixtures/apps/secure-app/app/controller/index.js create mode 100644 test/fixtures/apps/secure-app/app/router.js create mode 100644 test/fixtures/apps/secure-app/config/config.default.js create mode 100644 test/fixtures/apps/secure-app/package.json create mode 100644 test/fixtures/apps/service-app/app/controller/user.js create mode 100644 test/fixtures/apps/service-app/app/router.js create mode 100644 test/fixtures/apps/service-app/app/service/user.js create mode 100644 test/fixtures/apps/service-app/package.json create mode 100644 test/fixtures/apps/services_loader_verify/app/service/foo.js create mode 100644 test/fixtures/apps/services_loader_verify/package.json create mode 100644 test/fixtures/apps/singleton-demo/agent.js create mode 100644 test/fixtures/apps/singleton-demo/app.js create mode 100644 test/fixtures/apps/singleton-demo/config/config.default.js create mode 100644 test/fixtures/apps/singleton-demo/create.js create mode 100644 test/fixtures/apps/singleton-demo/package.json create mode 100644 test/fixtures/apps/static-server/app/public/foo.js create mode 100644 test/fixtures/apps/static-server/config/config.default.js create mode 100644 test/fixtures/apps/static-server/config/plugin.js create mode 100644 test/fixtures/apps/static-server/package.json create mode 100644 test/fixtures/apps/subdir-services/app/controller/home.js create mode 100644 test/fixtures/apps/subdir-services/app/router.js create mode 100644 test/fixtures/apps/subdir-services/app/service/certify-personal/mobile-hi/do_certify.js create mode 100644 test/fixtures/apps/subdir-services/app/service/cif/user.js create mode 100644 test/fixtures/apps/subdir-services/app/service/foo/bar.js create mode 100644 test/fixtures/apps/subdir-services/app/service/foo/subdir/bar.js create mode 100644 test/fixtures/apps/subdir-services/app/service/foo/subdir1/subdir11/bar.js create mode 100644 test/fixtures/apps/subdir-services/app/service/foo/subdir2/sub2.js create mode 100644 test/fixtures/apps/subdir-services/app/service/ok.js create mode 100644 test/fixtures/apps/subdir-services/app/service/old_style.js create mode 100644 test/fixtures/apps/subdir-services/app/service/user.js create mode 100644 test/fixtures/apps/subdir-services/package.json create mode 100644 test/fixtures/apps/userrole/app/controller/admin.js create mode 100644 test/fixtures/apps/userrole/app/controller/user.js create mode 100644 test/fixtures/apps/userrole/app/router.js create mode 100644 test/fixtures/apps/userrole/config/config.default.js create mode 100644 test/fixtures/apps/userrole/config/role.js create mode 100644 test/fixtures/apps/userrole/package.json create mode 100644 test/fixtures/apps/userservice/app/controller/home.js create mode 100644 test/fixtures/apps/userservice/app/router.js create mode 100644 test/fixtures/apps/userservice/config/config.default.js create mode 100644 test/fixtures/apps/userservice/package.json create mode 100644 test/fixtures/apps/validate_form/app/controller/user.js create mode 100644 test/fixtures/apps/validate_form/app/router.js create mode 100644 test/fixtures/apps/validate_form/config/config.default.js create mode 100644 test/fixtures/apps/validate_form/package.json create mode 100644 test/fixtures/apps/view-framework/index.js create mode 100644 test/fixtures/apps/view-framework/lib/view.js create mode 100644 test/fixtures/apps/view-framework/package.json create mode 100644 test/fixtures/apps/view-render/app/a.js create mode 100644 test/fixtures/apps/view-render/app/controller/context.js create mode 100644 test/fixtures/apps/view-render/app/controller/csrf.js create mode 100644 test/fixtures/apps/view-render/app/controller/empty.js create mode 100644 test/fixtures/apps/view-render/app/controller/home.js create mode 100644 test/fixtures/apps/view-render/app/controller/inject.js create mode 100644 test/fixtures/apps/view-render/app/controller/locals.js create mode 100644 test/fixtures/apps/view-render/app/controller/nonce.js create mode 100644 test/fixtures/apps/view-render/app/controller/shtml.js create mode 100644 test/fixtures/apps/view-render/app/controller/sjs.js create mode 100644 test/fixtures/apps/view-render/app/controller/string.js create mode 100644 test/fixtures/apps/view-render/app/controller/xss.js create mode 100644 test/fixtures/apps/view-render/app/helper.js create mode 100644 test/fixtures/apps/view-render/app/router.js create mode 100644 test/fixtures/apps/view-render/app/views/a.html create mode 100644 test/fixtures/apps/view-render/app/views/form_csrf.html create mode 100644 test/fixtures/apps/view-render/app/views/index.html create mode 100644 test/fixtures/apps/view-render/app/views/inject.html create mode 100644 test/fixtures/apps/view-render/app/views/locals.html create mode 100644 test/fixtures/apps/view-render/app/views/nonce.html create mode 100644 test/fixtures/apps/view-render/app/views/shtml.html create mode 100644 test/fixtures/apps/view-render/app/views/sjs.html create mode 100644 test/fixtures/apps/view-render/app/views/xss.html create mode 100644 test/fixtures/apps/view-render/config/config.default.js create mode 100644 test/fixtures/apps/view-render/config/plugin.js create mode 100644 test/fixtures/apps/view-render/package.json create mode 100644 test/fixtures/apps/view/app/app.js create mode 100644 test/fixtures/apps/view/app/controller/home.js create mode 100644 test/fixtures/apps/view/app/helper.js create mode 100644 test/fixtures/apps/view/app/router.js create mode 100644 test/fixtures/apps/view/app/views/index.html create mode 100644 test/fixtures/apps/view/config/config.default.js create mode 100644 test/fixtures/apps/view/package.json create mode 100644 test/fixtures/apps/watcher-development-app/agent.js create mode 100644 test/fixtures/apps/watcher-development-app/app/router.js create mode 100644 test/fixtures/apps/watcher-development-app/config/config.unittest.js create mode 100644 test/fixtures/apps/watcher-development-app/package.json create mode 100644 test/fixtures/apps/watcher-development-app/tmp-agent.txt create mode 100644 test/fixtures/apps/watcher-development-app/tmp-agent/tmp.txt create mode 100644 test/fixtures/apps/watcher-development-app/tmp.txt create mode 100644 test/fixtures/apps/watcher-development-app/tmp/tmp.txt create mode 100644 test/fixtures/apps/watcher-type-default/config/config.unittest.js create mode 100644 test/fixtures/apps/watcher-type-default/package.json create mode 100644 test/fixtures/apps/worker-die/app.js create mode 100644 test/fixtures/apps/worker-die/package.json create mode 100644 test/fixtures/custom-egg/index.js create mode 100644 test/fixtures/custom-egg/package.json create mode 100644 test/lib/agent.test.js create mode 100644 test/lib/application.test.js create mode 100644 test/lib/cluster/agent_worker.test.js create mode 100644 test/lib/cluster/app_worker.test.js create mode 100644 test/lib/cluster/master.test.js create mode 100644 test/lib/core/agent_worker_client.test.js create mode 100644 test/lib/core/app/extend/agent.test.js create mode 100644 test/lib/core/app/extend/application.test.js create mode 100644 test/lib/core/app/extend/context.jsonp.test.js create mode 100644 test/lib/core/app/extend/context.test.js create mode 100644 test/lib/core/app/extend/helper.test.js create mode 100644 test/lib/core/app/extend/request.test.js create mode 100644 test/lib/core/app/middleware/body_parser.test.js create mode 100644 test/lib/core/app/middleware/meta.test.js create mode 100644 test/lib/core/app/middleware/notfound.test.js create mode 100644 test/lib/core/app/middleware/override_method.test.js create mode 100644 test/lib/core/app/middleware/security.test.js create mode 100644 test/lib/core/app/middleware/site_file.test.js create mode 100644 test/lib/core/app_worker_client.test.js create mode 100644 test/lib/core/config/config.test.js create mode 100644 test/lib/core/cookies.test.js create mode 100644 test/lib/core/loader/config_loader.test.js create mode 100644 test/lib/core/loader/load_app.test.js create mode 100644 test/lib/core/loader/load_plugin.test.js create mode 100644 test/lib/core/loader/load_router.test.js create mode 100644 test/lib/core/loader/load_service.test.js create mode 100644 test/lib/core/logger.test.js create mode 100644 test/lib/core/messenger.test.js create mode 100644 test/lib/core/router.test.js create mode 100644 test/lib/core/singleton.test.js create mode 100644 test/lib/core/urllib.test.js create mode 100644 test/lib/core/util.test.js create mode 100644 test/lib/plugins/cors.test.js create mode 100644 test/lib/plugins/depd.test.js create mode 100644 test/lib/plugins/development.test.js create mode 100644 test/lib/plugins/empty.txt create mode 100644 test/lib/plugins/i18n.test.js create mode 100644 test/lib/plugins/logrotater.test.js create mode 100644 test/lib/plugins/multipart.test.js create mode 100644 test/lib/plugins/onerror.test.js create mode 100644 test/lib/plugins/rest.test.js create mode 100644 test/lib/plugins/schedule.test.js create mode 100644 test/lib/plugins/session.test.js create mode 100644 test/lib/plugins/static.test.js create mode 100644 test/lib/plugins/userrole.test.js create mode 100644 test/lib/plugins/userservice.test.js create mode 100644 test/lib/plugins/validate.test.js create mode 100644 test/lib/plugins/view/render.test.js create mode 100644 test/lib/plugins/watcher.test.js create mode 100644 test/mocha.opts create mode 100644 test/utils.js diff --git a/.eslintignore b/.eslintignore index 5886583c02..eb984b683c 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,4 @@ test/fixtures -test/benchmark +examples/**/app/public +logs +run diff --git a/.gitignore b/.gitignore index 8f4403e745..e20c0b1535 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ run .tmp docs/CONTRIBUTING.md docs/README.md + +!test/fixtures/apps/loader-plugin/node_modules diff --git a/.travis.yml b/.travis.yml index 0fd64bfdc6..03736ab24c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,4 +8,4 @@ install: script: - npm run ci after_script: - - npm i codecov && codecov + - npminstall codecov && codecov diff --git a/appveyor.yml b/appveyor.yml index 7dff837c3d..e4893768a1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,7 +5,7 @@ environment: install: - ps: Install-Product node $env:nodejs_version - - npm i npminstall && npminstall + - npm i npminstall && node_modules\.bin\npminstall test_script: - node --version diff --git a/docs/plugins.puml b/docs/plugins.puml index 447d465877..a19b2ca8cd 100644 --- a/docs/plugins.puml +++ b/docs/plugins.puml @@ -2,19 +2,6 @@ @startuml digraph world { "onerror"; - "userservice"; - "userrole"; "session"; - "i18n"; - "validate"; - "watcher"; - "multipart"; - "security" -> "session"; - "development" -> "watcher"; - "rest"; - "static"; - "cors" -> "security"; - "logrotater"; - "schedule"; } @enduml diff --git a/examples/cookie/test/index.test.js b/examples/cookie/test/index.test.js new file mode 100644 index 0000000000..a8ffb6ba5c --- /dev/null +++ b/examples/cookie/test/index.test.js @@ -0,0 +1,47 @@ +'use strict'; + +const path = require('path'); +const request = require('supertest-as-promised'); +const mm = require('egg-mock'); + +describe('example cookie test', () => { + let app; + + before(() => { + const baseDir = path.dirname(__dirname); + const customEgg = path.join(baseDir, '../..'); + app = mm.app({ + baseDir, + customEgg, + }); + return app.ready(); + }); + + after(() => app.close()); + + it('should GET / show "remember me" checkbox when cookie.remember not exists', () => { + return request(app.callback()) + .get('/') + .expect(200) + .expect(/ remember me<\/label>/); + }); + + it('should POST /remember to set cookie.remember = 1', () => { + return request(app.callback()) + .post('/remember') + .send({ + remember: 'true', + }) + .expect(302) + .expect('Location', '/') + .expect('Set-Cookie', /^remember=1; path=\/; expires=[^;]+; httponly,remember\.sig=[^;]+; path=\/; expires=[^;]+; httponly$/); + }); + + it('should GET /forget to delete cookie.remember', () => { + return request(app.callback()) + .get('/forget') + .expect(302) + .expect('Location', '/') + .expect('Set-Cookie', /^remember=; path=\/; expires=[^;]+; httponly$/); + }); +}); diff --git a/examples/cookie_session/test/index.test.js b/examples/cookie_session/test/index.test.js new file mode 100644 index 0000000000..a6e1ae3c6a --- /dev/null +++ b/examples/cookie_session/test/index.test.js @@ -0,0 +1,43 @@ +'use strict'; + +const path = require('path'); +const request = require('supertest-as-promised'); +const mm = require('egg-mock'); + +describe('example cookie_session test', () => { + let app; + let cookie; + + before(() => { + const baseDir = path.dirname(__dirname); + const customEgg = path.join(baseDir, '../..'); + app = mm.app({ + baseDir, + customEgg, + }); + return app.ready(); + }); + + after(() => app.close()); + + it('should GET / first time', () => { + return request(app.callback()) + .get('/') + .expect(200) + .expect(/^1 times/) + .expect('Set-Cookie', /^EGG_SESS=[^;]+; path=\/; expires=[^;]+; httponly$/) + .expect(res => { + cookie = res.headers['set-cookie'][0].split(';')[0]; + }); + }); + + it('should GET / second time', () => { + return request(app.callback()) + .get('/') + .set('Cookie', cookie) + .expect(200) + .expect(/^2 times/) + // session.count change + .expect('Set-Cookie', /^EGG_SESS=[^;]+; path=\/; expires=[^;]+; httponly$/); + }); +}); diff --git a/examples/helloworld/test/index.test.js b/examples/helloworld/test/index.test.js new file mode 100644 index 0000000000..b56326ad15 --- /dev/null +++ b/examples/helloworld/test/index.test.js @@ -0,0 +1,35 @@ +'use strict'; + +const path = require('path'); +const request = require('supertest-as-promised'); +const mm = require('egg-mock'); + +describe('example helloworld test', () => { + let app; + + before(() => { + const baseDir = path.dirname(__dirname); + const customEgg = path.join(baseDir, '../..'); + app = mm.app({ + baseDir, + customEgg, + }); + return app.ready(); + }); + + after(() => app.close()); + + it('should GET / 200', () => { + return request(app.callback()) + .get('/') + .expect(200) + .expect('Hello World'); + }); + + it('should GET /foo', () => { + return request(app.callback()) + .get('/foo') + .expect(200) + .expect('Hello foo'); + }); +}); diff --git a/examples/multipart/config/config.default.js b/examples/multipart/config/config.default.js new file mode 100644 index 0000000000..6c4b8d15f5 --- /dev/null +++ b/examples/multipart/config/config.default.js @@ -0,0 +1,3 @@ +'use strict'; + +exports.keys = 'my keys'; diff --git a/examples/multipart/test/index.test.js b/examples/multipart/test/index.test.js new file mode 100644 index 0000000000..5da26c82f9 --- /dev/null +++ b/examples/multipart/test/index.test.js @@ -0,0 +1,63 @@ +'use strict'; + +const assert = require('assert'); +const path = require('path'); +const request = require('supertest-as-promised'); +const mm = require('egg-mock'); +const formstream = require('formstream'); +const urllib = require('urllib'); + +describe.skip('example multipart test', () => { + let app; + let csrfToken; + let cookies; + let host; + let server; + + before(() => { + const baseDir = path.dirname(__dirname); + const customEgg = path.join(baseDir, '../..'); + app = mm.app({ + baseDir, + customEgg, + }); + server = app.listen(); + }); + + after(() => app.close()); + + it('should GET / show upload form', () => { + return request(server) + .get('/') + .expect(200) + .expect(/

Image: <\/p>/) + .expect(res => { + console.log(res.headers, res.text); + csrfToken = res.headers['x-csrf']; + cookies = res.headers['set-cookie'].join(';'); + host = `http://127.0.0.1:${server.address().port}`; + }); + }); + + it('should POST /upload success', done => { + const form = formstream(); + form.file('file', __filename); + // other form fields + form.field('title', 'fengmk2 test title') + .field('love', 'egg'); + + const headers = form.headers(); + headers.Cookie = cookies; + urllib.request(`${host}/upload?_csrf=${csrfToken}`, { + method: 'POST', + headers, + stream: form, + dataType: 'json', + }, (err, data, res) => { + assert(!err, err && err.message); + assert.equal(res.statusCode, 200); + console.log(data); + done(); + }); + }); +}); diff --git a/examples/static/app/router.js b/examples/static/app/router.js index e28728a8a1..b3e62cf0b3 100644 --- a/examples/static/app/router.js +++ b/examples/static/app/router.js @@ -1,5 +1,5 @@ 'use strict'; module.exports = app => { - app.get('/', 'home'); + app.get('/', app.controller.home); }; diff --git a/examples/static/config/config.default.js b/examples/static/config/config.default.js new file mode 100644 index 0000000000..6c4b8d15f5 --- /dev/null +++ b/examples/static/config/config.default.js @@ -0,0 +1,3 @@ +'use strict'; + +exports.keys = 'my keys'; diff --git a/examples/static/test/index.test.js b/examples/static/test/index.test.js new file mode 100644 index 0000000000..0c0eccb629 --- /dev/null +++ b/examples/static/test/index.test.js @@ -0,0 +1,35 @@ +'use strict'; + +const path = require('path'); +const request = require('supertest-as-promised'); +const mm = require('egg-mock'); + +describe('example static test', () => { + let app; + + before(() => { + const baseDir = path.dirname(__dirname); + const customEgg = path.join(baseDir, '../..'); + app = mm.app({ + baseDir, + customEgg, + }); + return app.ready(); + }); + + after(() => app.close()); + + it('should GET / 200', () => { + return request(app.callback()) + .get('/') + .expect(200) + .expect(/

  • Download hi\.txt<\/a>\.<\/li>/); + }); + + it('should GET /public/hi.txt', () => { + return request(app.callback()) + .get('/public/hi.txt') + .expect(200) + .expect('hi egg.\n你好,蛋蛋。\n'); + }); +}); diff --git a/lib/core/app/middleware/meta.js b/lib/core/app/middleware/meta.js index 4a44e34997..f2047ca602 100644 --- a/lib/core/app/middleware/meta.js +++ b/lib/core/app/middleware/meta.js @@ -1,19 +1,19 @@ /** - * meta 中间件,放在最前面 + * meta middleware, should be the first middleware */ 'use strict'; -module.exports = function() { +module.exports = () => { let serverId = process.env.HOSTNAME || ''; if (serverId.indexOf('-') > 0) { - // appname-90-1 => 90-1 + // appname-1-1 => 1-1 serverId = serverId.split('-').slice(1).join('-'); } - return function* (next) { + return function* meta(next) { /** - * 开始处理当前请求的时间戳,单位 `ms`,方便做一些时间计算。 + * Request start time * @member {Number} Context#starttime */ this.starttime = Date.now(); @@ -28,7 +28,7 @@ module.exports = function() { this.set('X-Server-Id', serverId); } - // 设置一个 x-readtime 头, 供 nginx access log 使用, 也方便调试 + // total response time header this.set('X-Readtime', Date.now() - this.starttime); }; }; diff --git a/lib/core/app/middleware/notfound.js b/lib/core/app/middleware/notfound.js index 6080ec6827..1057931f81 100644 --- a/lib/core/app/middleware/notfound.js +++ b/lib/core/app/middleware/notfound.js @@ -1,6 +1,6 @@ 'use strict'; -module.exports = function(options) { +module.exports = options => { return function* notfound(next) { yield next; diff --git a/lib/core/app/middleware/site_file.js b/lib/core/app/middleware/site_file.js index 2d763a5e6a..ea8f4d607b 100644 --- a/lib/core/app/middleware/site_file.js +++ b/lib/core/app/middleware/site_file.js @@ -5,8 +5,8 @@ const path = require('path'); const MAX_AGE = 30 * 24 * 68 * 60; -module.exports = function fixture(options) { - return function* (next) { +module.exports = options => { + return function* siteFile(next) { if (this.method !== 'HEAD' && this.method !== 'GET') return yield next; if (!options.hasOwnProperty(this.path)) return yield next; diff --git a/lib/core/config/plugin.js b/lib/core/config/plugin.js index 4b87cb9504..0039123d76 100644 --- a/lib/core/config/plugin.js +++ b/lib/core/config/plugin.js @@ -27,13 +27,13 @@ module.exports = { /** * userrole * @member {Object} Plugin#userrole - * @property {Boolean} enable - 默认 true + * @property {Boolean} enable - `true` by default * @since 1.0.0 */ - // userrole: { - // enable: true, - // package: 'egg-userrole', - // }, + userrole: { + enable: true, + package: 'egg-userrole', + }, /** * session @@ -63,10 +63,10 @@ module.exports = { * @property {Boolean} enable - 默认 true * @since 1.0.0 */ - // validate: { - // enable: true, - // package: 'egg-validate', - // }, + validate: { + enable: true, + package: 'egg-validate', + }, /** * file and dir watcher @@ -87,7 +87,7 @@ module.exports = { */ // multipart: { // enable: true, - // package: '@ali/egg-multipart', + // package: 'egg-multipart', // }, /** @@ -128,13 +128,13 @@ module.exports = { /** * `app/public` dir static serve * @member {Object} Plugin#static - * @property {Boolean} enable - 默认 false + * @property {Boolean} enable - `false` by default * @since 1.0.0 */ - // static: { - // enable: false, - // package: 'egg-static', - // }, + static: { + enable: false, + package: 'egg-static', + }, /** * CORS @@ -142,10 +142,10 @@ module.exports = { * @property {Boolean} enable - 默认 false * @since 1.0.0 */ - // cors: { - // enable: false, - // package: 'egg-cors', - // }, + cors: { + enable: false, + package: 'egg-cors', + }, /** * logger file rotater diff --git a/package.json b/package.json index c5070e7a45..887b2b3de8 100644 --- a/package.json +++ b/package.json @@ -3,18 +3,22 @@ "version": "0.0.4", "description": "A web framework's framework for Node.js", "dependencies": { - "egg-cluster": "*", - "egg-loader": "*", "accepts": "^1.3.3", "agentkeepalive": "^2.1.1", "co": "^4.6.0", "debug": "^2.2.0", "delegates": "^1.0.0", "depd": "^1.1.0", + "egg-cluster": "*", "egg-cookies": "^1.0.0", + "egg-cors": "^0.0.2", + "egg-loader": "*", "egg-logger": "^1.0.1", - "egg-onerror": "~0.0.2", - "egg-session": "~0.0.2", + "egg-onerror": "*", + "egg-session": "*", + "egg-static": "*", + "egg-userrole": "*", + "egg-validate": "*", "graceful": "^1.0.0", "humanize-ms": "^1.2.0", "inflection": "^1.10.0", @@ -39,7 +43,8 @@ "co-sleep": "^0.0.1", "coffee": "^3.2.2", "egg-bin": "1", - "egg-ci": "^1.0.0", + "egg-ci": "1", + "egg-mock": "*", "egg-plugin-puml": "1", "eslint": "^3.0.0", "eslint-config-egg": "^3.1.0", @@ -48,7 +53,6 @@ "glob": "^7.0.3", "merge-descriptors": "^1.0.1", "moment": "^2.13.0", - "node-uuid": "^1.4.7", "once": "^1.3.3", "pedding": "^1.0.0", "rds": "^0.1.0", @@ -56,9 +60,7 @@ "should": "^6.0.3", "stream-wormhole": "^1.0.0", "supertest": "^1.2.0", - "supertest-as-promised": "^3.2.0", - "uuid": "^2.0.2", - "uuid-js": "^0.7.5" + "supertest-as-promised": "^3.2.0" }, "main": "index.js", "files": [ @@ -67,11 +69,12 @@ "index.js" ], "scripts": { - "lint": "eslint --fix lib test *.js", + "lint": "eslint --fix lib test examples *.js", "test": "npm run lint && npm run test-local", "test-local": "egg-bin test", + "test-examples": "TESTS=examples/**/test/**/*.test.js egg-bin test", "cov": "egg-bin cov", - "ci": "node -v && npm run lint && npm run cov", + "ci": "npm run lint && npm run test-examples && npm run cov", "autod": "autod", "doc": "./scripts/doc.sh", "puml": "puml . --dest ./docs", diff --git a/test/benchmark/assign.js b/test/benchmark/assign.js new file mode 100644 index 0000000000..ea60e750f5 --- /dev/null +++ b/test/benchmark/assign.js @@ -0,0 +1,54 @@ +'use strict'; + +const Benchmark = require('benchmark'); +const benchmarks = require('beautify-benchmark'); +const utils = require('../../lib/util'); + +new Benchmark.Suite() +.add('Object.assign', () => { + const a = {}; + const b = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + Object.assign(a, b); +}) +.add('for in', () => { + const a = {}; + const b = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + for (const key in b) { + a[key] = b[key]; + } +}) +.add('Object.keys', () => { + const a = {}; + const b = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + const keys = Object.keys(b); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + a[key] = b[key]; + } +}) +.add('utils.assign', () => { + const a = {}; + const b = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + utils.assign(a, b); +}) +.on('cycle', event => { + benchmarks.add(event.target); +}) +.on('start', () => { + console.log('\n node version: %s, date: %s\n Starting...', + process.version, Date()); +}) +.on('complete', () => { + benchmarks.log(); + process.exit(0); +}) +.run({ async: false }); + +// node version: v4.2.3, date: Tue Jan 26 2016 16:52:46 GMT+0800 (CST) +// Starting... +// 4 tests completed. +// +// Object.assign x 646,034 ops/sec ±1.61% (86 runs sampled) +// for in x 2,754,639 ops/sec ±1.21% (88 runs sampled) +// Object.keys x 3,590,226 ops/sec ±1.04% (93 runs sampled) +// utils.assign x 3,192,343 ops/sec ±0.69% (93 runs sampled) diff --git a/test/fixtures/apps/agent-app-sync/agent.js b/test/fixtures/apps/agent-app-sync/agent.js new file mode 100644 index 0000000000..ac817ef910 --- /dev/null +++ b/test/fixtures/apps/agent-app-sync/agent.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = agent => { + agent.startAgent({ + name: 'test', + client: { ready: cb => cb() }, + subscribe: (info, cb) => cb('test'), + }); +}; diff --git a/test/fixtures/apps/agent-app-sync/app.js b/test/fixtures/apps/agent-app-sync/app.js new file mode 100644 index 0000000000..17eef8182b --- /dev/null +++ b/test/fixtures/apps/agent-app-sync/app.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = app => { + const done = app.readyCallback(); + const test = app.createAppWorkerClient('test', { + listen(cb) { + this._subscribe('listening', cb); + }, + }); + test.listen(arg => { + app.arg = arg; + done(); + }) +}; diff --git a/test/fixtures/apps/agent-app-sync/app/router.js b/test/fixtures/apps/agent-app-sync/app/router.js new file mode 100644 index 0000000000..de9f8095a0 --- /dev/null +++ b/test/fixtures/apps/agent-app-sync/app/router.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = app => { + app.get('/', function*() { + this.body = this.app.arg; + }) +}; diff --git a/test/fixtures/apps/agent-app-sync/package.json b/test/fixtures/apps/agent-app-sync/package.json new file mode 100644 index 0000000000..06c4ed021a --- /dev/null +++ b/test/fixtures/apps/agent-app-sync/package.json @@ -0,0 +1,3 @@ +{ + "name": "agent-app-sync" +} diff --git a/test/fixtures/apps/agent-app/app.js b/test/fixtures/apps/agent-app/app.js new file mode 100644 index 0000000000..6f0ad1bc02 --- /dev/null +++ b/test/fixtures/apps/agent-app/app.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = app => { + const done = app.readyCallback('foo'); + app.mockClient.subscribe({ + id: 'foo' + }, value => { + app.foo = value; + done(); + }); +}; diff --git a/test/fixtures/apps/agent-app/app/router.js b/test/fixtures/apps/agent-app/app/router.js new file mode 100644 index 0000000000..efd9f79fbb --- /dev/null +++ b/test/fixtures/apps/agent-app/app/router.js @@ -0,0 +1,25 @@ +module.exports = app => { + app.get('/', function*() { + this.body = 'ok'; + }); + + app.get('/getData', function*() { + this.body = yield app.mockClient.getData(); + }); + + app.get('/getError', function*() { + try { + yield app.mockClient.getError(); + } catch (err) { + this.body = err.message; + } + }); + + app.get('/getDataGenerator', function* () { + this.body = yield app.mockClient.getDataGenerator(); + }) + + app.get('/sub', function*() { + this.body = app.foo; + }); +}; diff --git a/test/fixtures/apps/agent-app/config/plugin.js b/test/fixtures/apps/agent-app/config/plugin.js new file mode 100644 index 0000000000..98e5bed6c1 --- /dev/null +++ b/test/fixtures/apps/agent-app/config/plugin.js @@ -0,0 +1,10 @@ +'use strict'; + +const path = require('path'); + +module.exports = { + mock: { + enable: true, + path: path.join(__dirname, '../plugins/mock-client'), + }, +}; diff --git a/test/fixtures/apps/agent-app/package.json b/test/fixtures/apps/agent-app/package.json new file mode 100644 index 0000000000..34a8314b9c --- /dev/null +++ b/test/fixtures/apps/agent-app/package.json @@ -0,0 +1,3 @@ +{ + "name": "agent-app" +} diff --git a/test/fixtures/apps/agent-app/plugins/mock-client/agent.js b/test/fixtures/apps/agent-app/plugins/mock-client/agent.js new file mode 100644 index 0000000000..748a316bf6 --- /dev/null +++ b/test/fixtures/apps/agent-app/plugins/mock-client/agent.js @@ -0,0 +1,29 @@ +'use strict'; + +const MockClient = require('./mock_client'); + +module.exports = agent => { + const done = agent.readyCallback('agent_configclient'); + const options = agent.config.mock; + + agent.mockClient = new MockClient(); + + // 启动 agent 任务 + agent.startAgent({ + client: agent.mockClient, + name: 'mock', + subscribe: function(info, listener) { + agent.mockClient.on(info.id, listener); + if (info.id === 'foo') { + setTimeout(function() { + agent.mockClient.emit('foo', 'bar'); + }, 100); + } + }, + }); + + agent.mockClient.ready(() => { + agent.logger.info('[agent] %s started mockClient', agent.config.name); + done(); + }); +}; diff --git a/test/fixtures/apps/agent-app/plugins/mock-client/app.js b/test/fixtures/apps/agent-app/plugins/mock-client/app.js new file mode 100644 index 0000000000..bcf9655c72 --- /dev/null +++ b/test/fixtures/apps/agent-app/plugins/mock-client/app.js @@ -0,0 +1,28 @@ +'use strict'; + +module.exports = app => { + const options = app.config.mock; + + app.mockClient = app.createAppWorkerClient('mock', { + subscribe: function(reg, listner) { + this._subscribe(reg, listner); + return this; + }, + + * getData(id) { + return yield this._invoke('getData', [id]); + }, + + * getError() { + return yield this._invoke('getError', []); + }, + + * getDataGenerator(id) { + return yield this._invoke('getDataGenerator', [id]); + }, + }, options); + + app.mockClient.ready(app.readyCallback('worker_mock_client'), { + isWeakDep: app.config.runMode === 0, + }); +}; diff --git a/test/fixtures/apps/agent-app/plugins/mock-client/config/config.default.js b/test/fixtures/apps/agent-app/plugins/mock-client/config/config.default.js new file mode 100644 index 0000000000..8d01395106 --- /dev/null +++ b/test/fixtures/apps/agent-app/plugins/mock-client/config/config.default.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = () => { + return { + mock: { + name: 'mock', + }, + }; +} diff --git a/test/fixtures/apps/agent-app/plugins/mock-client/mock_client.js b/test/fixtures/apps/agent-app/plugins/mock-client/mock_client.js new file mode 100644 index 0000000000..0f74f147c4 --- /dev/null +++ b/test/fixtures/apps/agent-app/plugins/mock-client/mock_client.js @@ -0,0 +1,65 @@ +'use strict'; + +const EventEmitter = require('events').EventEmitter; +const sleep = require('co-sleep'); + +class MockClient extends EventEmitter { + constructor(options) { + super(); + + setImmediate(function() { + this.ready(true); + }.bind(this)); + } + + ready(flagOrFunction) { + this._ready = !!this._ready; + this._readyCallbacks = this._readyCallbacks || []; + + if (typeof flagOrFunction === 'function') { + this._readyCallbacks.push(flagOrFunction); + } else { + this._ready = !!flagOrFunction; + } + + if (this._ready) { + this._readyCallbacks.splice(0, Infinity).forEach(function(callback) { + process.nextTick(callback); + }); + } + return this; + } + + getCallback(id, callback) { + setTimeout(function() { + if (id === 'error') { + callback(new Error('mock error')); + } else { + callback(null, 'mock data'); + } + }, 100); + } + + getData() { + return new Promise(function(resolve, reject) { + setTimeout(function() { + resolve('mock data'); + }, 100); + }); + } + + * getDataGenerator() { + yield sleep(100); + return 'mock data'; + } + + getError() { + return new Promise(function(resolve, reject) { + setTimeout(function() { + reject(new Error('mock error')); + }, 100); + }); + } +} + +module.exports = MockClient; diff --git a/test/fixtures/apps/agent-die/agent.js b/test/fixtures/apps/agent-die/agent.js new file mode 100644 index 0000000000..609a3d444d --- /dev/null +++ b/test/fixtures/apps/agent-die/agent.js @@ -0,0 +1,4 @@ + +setTimeout(() => { + throw new Error('app worker throw'); +}, 5000); diff --git a/test/fixtures/apps/agent-die/package.json b/test/fixtures/apps/agent-die/package.json new file mode 100644 index 0000000000..a973c60f38 --- /dev/null +++ b/test/fixtures/apps/agent-die/package.json @@ -0,0 +1,3 @@ +{ + "name": "agent-die" +} diff --git a/test/fixtures/apps/agent-die/start.js b/test/fixtures/apps/agent-die/start.js new file mode 100644 index 0000000000..06fa6ce672 --- /dev/null +++ b/test/fixtures/apps/agent-die/start.js @@ -0,0 +1,13 @@ +'use strict'; + +const utils = require('../../../utils'); + +require('../../../../index').startCluster({ + baseDir: __dirname, + workers: 1 +}) + +setTimeout(() => { + process.exit(); +// coverage will be slow +}, 5000); diff --git a/test/fixtures/apps/agent-instrument/agent.js b/test/fixtures/apps/agent-instrument/agent.js new file mode 100644 index 0000000000..f3a9bd15a3 --- /dev/null +++ b/test/fixtures/apps/agent-instrument/agent.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = agent => { + const done = agent.readyCallback('custom-agent-ready') + const ins = agent.instrument('http', `/hello`); + setTimeout(() => { + ins.end(); + done(); + }, 500); +} diff --git a/test/fixtures/apps/agent-instrument/package.json b/test/fixtures/apps/agent-instrument/package.json new file mode 100644 index 0000000000..b342c35e61 --- /dev/null +++ b/test/fixtures/apps/agent-instrument/package.json @@ -0,0 +1,3 @@ +{ + "name": "agent-instrument" +} diff --git a/test/fixtures/apps/agent-restart/agent.js b/test/fixtures/apps/agent-restart/agent.js new file mode 100644 index 0000000000..775c2e68a0 --- /dev/null +++ b/test/fixtures/apps/agent-restart/agent.js @@ -0,0 +1,17 @@ +'use strict'; + +const client = require('./client'); + +module.exports = agent => { + agent.startAgent({ + name: 'mock', + client: client, + subscribe: function(reg, listener) { + console.log('agent subscribe', reg); + }, + }); + + agent.messenger.on('die', () => { + process.exit(1); + }); +}; diff --git a/test/fixtures/apps/agent-restart/app.js b/test/fixtures/apps/agent-restart/app.js new file mode 100644 index 0000000000..fd40dddc8e --- /dev/null +++ b/test/fixtures/apps/agent-restart/app.js @@ -0,0 +1,14 @@ +'use strict'; + +const client = require('./client'); + +module.exports = app => { + const mock = app.createAppWorkerClient('mock', { + subscribe: function(info, listener) { + this._subscribe(info, listener); + return this; + }, + }); + + mock.subscribe('aaa', data => console.log(data)); +}; diff --git a/test/fixtures/apps/agent-restart/client.js b/test/fixtures/apps/agent-restart/client.js new file mode 100644 index 0000000000..230c6a2157 --- /dev/null +++ b/test/fixtures/apps/agent-restart/client.js @@ -0,0 +1,3 @@ +module.exports = { + ready: cb => cb(), +}; diff --git a/test/fixtures/apps/agent-restart/package.json b/test/fixtures/apps/agent-restart/package.json new file mode 100644 index 0000000000..7b80f612cf --- /dev/null +++ b/test/fixtures/apps/agent-restart/package.json @@ -0,0 +1,3 @@ +{ + "name": "agent-restart" +} diff --git a/test/fixtures/apps/agent-throw/agent.js b/test/fixtures/apps/agent-throw/agent.js new file mode 100644 index 0000000000..e1857e7638 --- /dev/null +++ b/test/fixtures/apps/agent-throw/agent.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = agent => { + agent.messenger.on('agent-throw', () => { + throw new Error('agent error'); + }); +}; diff --git a/test/fixtures/apps/agent-throw/app/router.js b/test/fixtures/apps/agent-throw/app/router.js new file mode 100644 index 0000000000..7377d3f8f6 --- /dev/null +++ b/test/fixtures/apps/agent-throw/app/router.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = app => { + app.get('/agent-throw', function*() { + app.messenger.broadcast('agent-throw'); + this.body = 'done'; + }); +}; diff --git a/test/fixtures/apps/agent-throw/config/config.default.js b/test/fixtures/apps/agent-throw/config/config.default.js new file mode 100644 index 0000000000..b6cba897c8 --- /dev/null +++ b/test/fixtures/apps/agent-throw/config/config.default.js @@ -0,0 +1 @@ +exports.keys = 'foo'; diff --git a/test/fixtures/apps/agent-throw/package.json b/test/fixtures/apps/agent-throw/package.json new file mode 100644 index 0000000000..6c6327b13d --- /dev/null +++ b/test/fixtures/apps/agent-throw/package.json @@ -0,0 +1,3 @@ +{ + "name": "agent-throw" +} diff --git a/test/fixtures/apps/aliyun-egg-app/app/controller/home.js b/test/fixtures/apps/aliyun-egg-app/app/controller/home.js new file mode 100644 index 0000000000..5d71f1230f --- /dev/null +++ b/test/fixtures/apps/aliyun-egg-app/app/controller/home.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = function*() { + this.body = { + 'aliyun-egg-core': !!this.app['aliyun-egg'], + 'aliyun-egg-plugin': !!this.app.custom, + 'aliyun-egg-agent': !!this.app.agent, + } +}; diff --git a/test/fixtures/apps/aliyun-egg-app/app/router.js b/test/fixtures/apps/aliyun-egg-app/app/router.js new file mode 100644 index 0000000000..b3e62cf0b3 --- /dev/null +++ b/test/fixtures/apps/aliyun-egg-app/app/router.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = app => { + app.get('/', app.controller.home); +}; diff --git a/test/fixtures/apps/aliyun-egg-app/config/config.default.js b/test/fixtures/apps/aliyun-egg-app/config/config.default.js new file mode 100644 index 0000000000..b6cba897c8 --- /dev/null +++ b/test/fixtures/apps/aliyun-egg-app/config/config.default.js @@ -0,0 +1 @@ +exports.keys = 'foo'; diff --git a/test/fixtures/apps/aliyun-egg-app/package.json b/test/fixtures/apps/aliyun-egg-app/package.json new file mode 100644 index 0000000000..067fc6a132 --- /dev/null +++ b/test/fixtures/apps/aliyun-egg-app/package.json @@ -0,0 +1,3 @@ +{ + "name": "aliyun-egg-app" +} diff --git a/test/fixtures/apps/aliyun-egg-biz/index.js b/test/fixtures/apps/aliyun-egg-biz/index.js new file mode 100644 index 0000000000..b8fc326fdf --- /dev/null +++ b/test/fixtures/apps/aliyun-egg-biz/index.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('../aliyun-egg'); diff --git a/test/fixtures/apps/aliyun-egg-biz/package.json b/test/fixtures/apps/aliyun-egg-biz/package.json new file mode 100644 index 0000000000..6e4660adf5 --- /dev/null +++ b/test/fixtures/apps/aliyun-egg-biz/package.json @@ -0,0 +1,3 @@ +{ + "name": "aliyun-egg-biz" +} diff --git a/test/fixtures/apps/aliyun-egg/index.js b/test/fixtures/apps/aliyun-egg/index.js new file mode 100644 index 0000000000..4f5c71e9c8 --- /dev/null +++ b/test/fixtures/apps/aliyun-egg/index.js @@ -0,0 +1,7 @@ +'use strict'; + +const egg = require('../../../..'); + +module.exports = egg; +module.exports.Application = require('./lib/aliyun-egg'); +module.exports.Agent = require('./lib/agent'); diff --git a/test/fixtures/apps/aliyun-egg/lib/agent.js b/test/fixtures/apps/aliyun-egg/lib/agent.js new file mode 100644 index 0000000000..f42593b700 --- /dev/null +++ b/test/fixtures/apps/aliyun-egg/lib/agent.js @@ -0,0 +1,19 @@ +'use strict'; + +const path = require('path'); +const egg = require('../../../../..'); +const Agent = egg.Agent; +const AppWorkerLoader = egg.AppWorkerLoader; + +class MyAgent extends Agent { + + constructor(options) { + super(options); + } + + get [Symbol.for('egg#eggPath')]() { + return path.join(__dirname, '..'); + } +} + +module.exports = MyAgent; diff --git a/test/fixtures/apps/aliyun-egg/lib/aliyun-egg.js b/test/fixtures/apps/aliyun-egg/lib/aliyun-egg.js new file mode 100644 index 0000000000..efd2234bae --- /dev/null +++ b/test/fixtures/apps/aliyun-egg/lib/aliyun-egg.js @@ -0,0 +1,37 @@ +'use strict'; + +const path = require('path'); +const egg = require('../../../../..'); +const Application = egg.Application; +const AppWorkerLoader = egg.AppWorkerLoader; + +class Loader extends AppWorkerLoader { + + constructor(options) { + super(options); + } + + loadConfig() { + this.loadServerConf(); + super.loadConfig(); + } + + loadServerConf() {} +} + +class ChairApplication extends Application { + + constructor(options) { + super(options); + } + + get [Symbol.for('egg#eggPath')]() { + return path.join(__dirname, '..'); + } + + get [Symbol.for('egg#loader')]() { + return Loader; + } +} + +module.exports = ChairApplication; diff --git a/test/fixtures/apps/aliyun-egg/lib/core/agent.js b/test/fixtures/apps/aliyun-egg/lib/core/agent.js new file mode 100644 index 0000000000..ce90514512 --- /dev/null +++ b/test/fixtures/apps/aliyun-egg/lib/core/agent.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = agent => { + +}; diff --git a/test/fixtures/apps/aliyun-egg/lib/core/app.js b/test/fixtures/apps/aliyun-egg/lib/core/app.js new file mode 100644 index 0000000000..46ce926d94 --- /dev/null +++ b/test/fixtures/apps/aliyun-egg/lib/core/app.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = app => { + app['aliyun-egg'] = {}; +}; diff --git a/test/fixtures/apps/aliyun-egg/lib/core/config/plugin.js b/test/fixtures/apps/aliyun-egg/lib/core/config/plugin.js new file mode 100644 index 0000000000..f652a0dc33 --- /dev/null +++ b/test/fixtures/apps/aliyun-egg/lib/core/config/plugin.js @@ -0,0 +1,10 @@ +'use strict'; + +const path = require('path'); + +module.exports = { + custom: { + enable: true, + path: path.join(__dirname, '../../plugins/custom'), + }, +}; diff --git a/test/fixtures/apps/aliyun-egg/lib/plugins/custom/agent.js b/test/fixtures/apps/aliyun-egg/lib/plugins/custom/agent.js new file mode 100644 index 0000000000..ecfd8a6790 --- /dev/null +++ b/test/fixtures/apps/aliyun-egg/lib/plugins/custom/agent.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = agent => { + agent.messenger.on('custom-aliyun-egg-worker', data => { + agent.messenger.broadcast('custom-aliyun-egg-agent', data); + }) +}; diff --git a/test/fixtures/apps/aliyun-egg/lib/plugins/custom/app.js b/test/fixtures/apps/aliyun-egg/lib/plugins/custom/app.js new file mode 100644 index 0000000000..83a1f31d1c --- /dev/null +++ b/test/fixtures/apps/aliyun-egg/lib/plugins/custom/app.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = app => { + app.custom = {}; + app.messenger.broadcast('custom-aliyun-egg-worker', 123); + app.messenger.on('custom-aliyun-egg-agent', data => { + app.agent = data; + }) +}; diff --git a/test/fixtures/apps/aliyun-egg/package.json b/test/fixtures/apps/aliyun-egg/package.json new file mode 100644 index 0000000000..a0caaf7885 --- /dev/null +++ b/test/fixtures/apps/aliyun-egg/package.json @@ -0,0 +1,3 @@ +{ + "name": "aliyun-egg" +} diff --git a/test/fixtures/apps/app-die/app/router.js b/test/fixtures/apps/app-die/app/router.js new file mode 100644 index 0000000000..85642afa6a --- /dev/null +++ b/test/fixtures/apps/app-die/app/router.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = app => { + app.get('/exit', function*() { + process.exit(1); + }); + + app.get('/uncaughtException', function*() { + setTimeout(() => { + throw new Error('get uncaughtException'); + }, 100); + }); +}; diff --git a/test/fixtures/apps/app-die/config/config.default.js b/test/fixtures/apps/app-die/config/config.default.js new file mode 100644 index 0000000000..b6cba897c8 --- /dev/null +++ b/test/fixtures/apps/app-die/config/config.default.js @@ -0,0 +1 @@ +exports.keys = 'foo'; diff --git a/test/fixtures/apps/app-die/package.json b/test/fixtures/apps/app-die/package.json new file mode 100644 index 0000000000..bd967656fb --- /dev/null +++ b/test/fixtures/apps/app-die/package.json @@ -0,0 +1,3 @@ +{ + "name": "app-die" +} diff --git a/test/fixtures/apps/app-router/app/controller/home.js b/test/fixtures/apps/app-router/app/controller/home.js new file mode 100644 index 0000000000..24901f6eec --- /dev/null +++ b/test/fixtures/apps/app-router/app/controller/home.js @@ -0,0 +1,3 @@ +module.exports = function* () { + this.body = 'hello'; +}; diff --git a/test/fixtures/apps/app-router/app/router.js b/test/fixtures/apps/app-router/app/router.js new file mode 100644 index 0000000000..4f58faef63 --- /dev/null +++ b/test/fixtures/apps/app-router/app/router.js @@ -0,0 +1,4 @@ +module.exports = app => { + app.get('home', '/', 'home'); + app.get('/home', app.controller.home); +}; diff --git a/test/fixtures/apps/app-router/package.json b/test/fixtures/apps/app-router/package.json new file mode 100644 index 0000000000..bffa1e7bdd --- /dev/null +++ b/test/fixtures/apps/app-router/package.json @@ -0,0 +1,3 @@ +{ + "name": "app-router" +} diff --git a/test/fixtures/apps/app-server/app.js b/test/fixtures/apps/app-server/app.js new file mode 100644 index 0000000000..5344174c5e --- /dev/null +++ b/test/fixtures/apps/app-server/app.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = app => { + app.on('server', server => { + app.serverEmit = true; + }); +}; diff --git a/test/fixtures/apps/app-server/app/router.js b/test/fixtures/apps/app-server/app/router.js new file mode 100644 index 0000000000..2e7e66a3ed --- /dev/null +++ b/test/fixtures/apps/app-server/app/router.js @@ -0,0 +1,5 @@ +module.exports = app => { + app.get('/', function* () { + this.body = this.app.serverEmit; + }); +}; diff --git a/test/fixtures/apps/app-server/config/config.default.js b/test/fixtures/apps/app-server/config/config.default.js new file mode 100644 index 0000000000..ea93c9c13a --- /dev/null +++ b/test/fixtures/apps/app-server/config/config.default.js @@ -0,0 +1 @@ +exports.keys = 'my keys'; diff --git a/test/fixtures/apps/app-server/package.json b/test/fixtures/apps/app-server/package.json new file mode 100644 index 0000000000..bfdb7df8be --- /dev/null +++ b/test/fixtures/apps/app-server/package.json @@ -0,0 +1,3 @@ +{ + "name": "app-server" +} diff --git a/test/fixtures/apps/body_parser_testapp/app/router.js b/test/fixtures/apps/body_parser_testapp/app/router.js new file mode 100644 index 0000000000..276433c1f8 --- /dev/null +++ b/test/fixtures/apps/body_parser_testapp/app/router.js @@ -0,0 +1,19 @@ +module.exports = app => { + app.get('/test/body_parser/user', function* () { + this.body = { + url: this.url, + csrf: this.csrf + }; + }); + + app.post('/test/body_parser/user', function* () { + this.body = this.request.body; + }); + + app.post('/test/body_parser/foo.json', function* () { + this.body = this.request.body; + }); + app.post('/test/body_parser/form.json', function* () { + this.body = this.request.body; + }); +}; diff --git a/test/fixtures/apps/body_parser_testapp/config/config.default.js b/test/fixtures/apps/body_parser_testapp/config/config.default.js new file mode 100644 index 0000000000..2960d82751 --- /dev/null +++ b/test/fixtures/apps/body_parser_testapp/config/config.default.js @@ -0,0 +1,7 @@ +exports.bodyParser = { + queryString: { + arrayLimit: 5 + } +}; + +exports.keys = 'foo'; diff --git a/test/fixtures/apps/body_parser_testapp/package.json b/test/fixtures/apps/body_parser_testapp/package.json new file mode 100644 index 0000000000..6fa5c4721f --- /dev/null +++ b/test/fixtures/apps/body_parser_testapp/package.json @@ -0,0 +1,3 @@ +{ + "name": "body_parser_testapp" +} diff --git a/test/fixtures/apps/close-watcher-logrotator/config/config.default.js b/test/fixtures/apps/close-watcher-logrotator/config/config.default.js new file mode 100644 index 0000000000..b6cba897c8 --- /dev/null +++ b/test/fixtures/apps/close-watcher-logrotator/config/config.default.js @@ -0,0 +1 @@ +exports.keys = 'foo'; diff --git a/test/fixtures/apps/close-watcher-logrotator/config/plugin.js b/test/fixtures/apps/close-watcher-logrotator/config/plugin.js new file mode 100644 index 0000000000..01bdf67a1e --- /dev/null +++ b/test/fixtures/apps/close-watcher-logrotator/config/plugin.js @@ -0,0 +1,2 @@ +exports.logrotater = false; +exports.watcher = false; diff --git a/test/fixtures/apps/close-watcher-logrotator/package.json b/test/fixtures/apps/close-watcher-logrotator/package.json new file mode 100644 index 0000000000..d1d0107055 --- /dev/null +++ b/test/fixtures/apps/close-watcher-logrotator/package.json @@ -0,0 +1,3 @@ +{ + "name": "close-watcher-logrotator" +} diff --git a/test/fixtures/apps/cluster_mod_app/app/controller/home.js b/test/fixtures/apps/cluster_mod_app/app/controller/home.js new file mode 100644 index 0000000000..58a4ac160a --- /dev/null +++ b/test/fixtures/apps/cluster_mod_app/app/controller/home.js @@ -0,0 +1,5 @@ +'use strict'; + +exports.index = function* () { + this.body = 'hi cluster'; +}; diff --git a/test/fixtures/apps/cluster_mod_app/app/router.js b/test/fixtures/apps/cluster_mod_app/app/router.js new file mode 100644 index 0000000000..095958ec4f --- /dev/null +++ b/test/fixtures/apps/cluster_mod_app/app/router.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = app => { + app.get('/', app.controller.home.index); +}; diff --git a/test/fixtures/apps/cluster_mod_app/config/config.default.js b/test/fixtures/apps/cluster_mod_app/config/config.default.js new file mode 100644 index 0000000000..b6cba897c8 --- /dev/null +++ b/test/fixtures/apps/cluster_mod_app/config/config.default.js @@ -0,0 +1 @@ +exports.keys = 'foo'; diff --git a/test/fixtures/apps/cluster_mod_app/package.json b/test/fixtures/apps/cluster_mod_app/package.json new file mode 100644 index 0000000000..cf745b625a --- /dev/null +++ b/test/fixtures/apps/cluster_mod_app/package.json @@ -0,0 +1,3 @@ +{ + "name": "cluster_mod_app" +} diff --git a/test/fixtures/apps/context-config-app/app/context.js b/test/fixtures/apps/context-config-app/app/context.js new file mode 100644 index 0000000000..aca4298022 --- /dev/null +++ b/test/fixtures/apps/context-config-app/app/context.js @@ -0,0 +1,6 @@ +module.exports = { + foo: 1, + bar: function() { + return 2; + } +}; \ No newline at end of file diff --git a/test/fixtures/apps/context-config-app/app/controller/home.js b/test/fixtures/apps/context-config-app/app/controller/home.js new file mode 100644 index 0000000000..5a114e9013 --- /dev/null +++ b/test/fixtures/apps/context-config-app/app/controller/home.js @@ -0,0 +1,18 @@ +'use strict'; + +exports.index = function* () { + this.body = { + path: this.router.pathFor('home'), + foo: this.foo, + bar: this.bar() + }; +}; + +exports.runtime = function* () { + this.runtime.mysql = 10; + this.runtime.foo = 11; + this.body = { + mysql: this.runtime.mysql, + foo: this.runtime.foo + }; +}; diff --git a/test/fixtures/apps/context-config-app/app/router.js b/test/fixtures/apps/context-config-app/app/router.js new file mode 100644 index 0000000000..8deaf7cc89 --- /dev/null +++ b/test/fixtures/apps/context-config-app/app/router.js @@ -0,0 +1,4 @@ +module.exports = app => { + app.get('home', '/', 'home.index'); + app.get('runtime', '/runtime', 'home.runtime'); +}; diff --git a/test/fixtures/apps/context-config-app/config/config.js b/test/fixtures/apps/context-config-app/config/config.js new file mode 100644 index 0000000000..31f997dc14 --- /dev/null +++ b/test/fixtures/apps/context-config-app/config/config.js @@ -0,0 +1,5 @@ +exports.security = { + csrf: false +} + +exports.keys = 'foo'; diff --git a/test/fixtures/apps/context-config-app/config/config.local.js b/test/fixtures/apps/context-config-app/config/config.local.js new file mode 100644 index 0000000000..927ed4b05f --- /dev/null +++ b/test/fixtures/apps/context-config-app/config/config.local.js @@ -0,0 +1,5 @@ +'use stirct'; + +exports.logger = { + stdoutLevel: 'NONE', +}; diff --git a/test/fixtures/apps/context-config-app/package.json b/test/fixtures/apps/context-config-app/package.json new file mode 100644 index 0000000000..ef64023eba --- /dev/null +++ b/test/fixtures/apps/context-config-app/package.json @@ -0,0 +1,3 @@ +{ + "name": "context-config-app" +} diff --git a/test/fixtures/apps/cors/app/router.js b/test/fixtures/apps/cors/app/router.js new file mode 100644 index 0000000000..2222e47507 --- /dev/null +++ b/test/fixtures/apps/cors/app/router.js @@ -0,0 +1,13 @@ +module.exports = app => { + app.get('/', function*() { + this.body = { + foo: 'bar' + }; + }); + + app.post('/', function*() { + this.body = { + foo: 'bar' + }; + }); +}; diff --git a/test/fixtures/apps/cors/config/config.default.js b/test/fixtures/apps/cors/config/config.default.js new file mode 100644 index 0000000000..d9b93b9490 --- /dev/null +++ b/test/fixtures/apps/cors/config/config.default.js @@ -0,0 +1,3 @@ +exports.cors = { + credentials: true, +}; diff --git a/test/fixtures/apps/cors/config/plugin.js b/test/fixtures/apps/cors/config/plugin.js new file mode 100644 index 0000000000..5347247057 --- /dev/null +++ b/test/fixtures/apps/cors/config/plugin.js @@ -0,0 +1 @@ +exports.cors = true; diff --git a/test/fixtures/apps/cors/package.json b/test/fixtures/apps/cors/package.json new file mode 100644 index 0000000000..be8a359c74 --- /dev/null +++ b/test/fixtures/apps/cors/package.json @@ -0,0 +1,3 @@ +{ + "name": "cors" +} diff --git a/test/fixtures/apps/custom-env-app/app/router.js b/test/fixtures/apps/custom-env-app/app/router.js new file mode 100644 index 0000000000..a4e9705aea --- /dev/null +++ b/test/fixtures/apps/custom-env-app/app/router.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = app => { + app.get('/', function*() { + this.body = { + env: this.app.config.env, + }; + }); +}; diff --git a/test/fixtures/apps/custom-env-app/config/config.default.js b/test/fixtures/apps/custom-env-app/config/config.default.js new file mode 100644 index 0000000000..5365ab0bf2 --- /dev/null +++ b/test/fixtures/apps/custom-env-app/config/config.default.js @@ -0,0 +1,3 @@ +'use strict'; + +exports.keys = 'foo'; diff --git a/test/fixtures/apps/custom-env-app/package.json b/test/fixtures/apps/custom-env-app/package.json new file mode 100644 index 0000000000..2ba8c94568 --- /dev/null +++ b/test/fixtures/apps/custom-env-app/package.json @@ -0,0 +1,3 @@ +{ + "name": "custom-env-app" +} diff --git a/test/fixtures/apps/custom-logger/config/config.default.js b/test/fixtures/apps/custom-logger/config/config.default.js new file mode 100644 index 0000000000..77c1855b03 --- /dev/null +++ b/test/fixtures/apps/custom-logger/config/config.default.js @@ -0,0 +1,14 @@ +'use strict'; + +const path = require('path'); + +module.exports = info => { + return { + customLogger: { + myLogger: { + file: path.join(info.baseDir, 'logs/my.log'), + formatter: meta => meta.message, + }, + }, + }; +}; diff --git a/test/fixtures/apps/custom-logger/package.json b/test/fixtures/apps/custom-logger/package.json new file mode 100644 index 0000000000..aa5619cadc --- /dev/null +++ b/test/fixtures/apps/custom-logger/package.json @@ -0,0 +1,3 @@ +{ + "name": "custom-logger" +} diff --git a/test/fixtures/apps/demo/app/controller/hello.js b/test/fixtures/apps/demo/app/controller/hello.js new file mode 100644 index 0000000000..a8f3b30296 --- /dev/null +++ b/test/fixtures/apps/demo/app/controller/hello.js @@ -0,0 +1,6 @@ +'use strict'; + +module.exports = function*() { + this.setCookie('hi', 'foo'); + this.body = 'hello'; +}; diff --git a/test/fixtures/apps/demo/app/controller/home.js b/test/fixtures/apps/demo/app/controller/home.js new file mode 100644 index 0000000000..fb335b81ac --- /dev/null +++ b/test/fixtures/apps/demo/app/controller/home.js @@ -0,0 +1,5 @@ +module.exports = function* () { + this.body = { + workerTitle: process.title + }; +}; diff --git a/test/fixtures/apps/demo/app/controller/logger.js b/test/fixtures/apps/demo/app/controller/logger.js new file mode 100644 index 0000000000..d56ffddb83 --- /dev/null +++ b/test/fixtures/apps/demo/app/controller/logger.js @@ -0,0 +1,17 @@ +'use strict'; + +module.exports = function*() { + const message = this.query.message; + + this.logger.debug('debug %s', message); + this.logger.info('info %s', message); + this.logger.warn('warn %s', message); + this.logger.error(new Error('error ' + message)); + + this.coreLogger.debug('core debug %s', message); + this.coreLogger.info('core info %s', message); + this.coreLogger.warn('core warn %s', message); + this.coreLogger.error(new Error('core error ' + message)); + + this.body = 'logger'; +}; diff --git a/test/fixtures/apps/demo/app/router.js b/test/fixtures/apps/demo/app/router.js new file mode 100644 index 0000000000..ae6b825305 --- /dev/null +++ b/test/fixtures/apps/demo/app/router.js @@ -0,0 +1,12 @@ +module.exports = app => { + app.get('home', '/', 'home'); + app.get('/hello', app.controller.hello); + app.get('/logger', app.controller.logger); + app.get('/protocol', function*() { + this.body = this.protocol; + }); + + app.get('/user.json', function*() { + this.jsonp = { name: 'fengmk2' }; + }); +}; diff --git a/test/fixtures/apps/demo/config/config.default.js b/test/fixtures/apps/demo/config/config.default.js new file mode 100644 index 0000000000..d4fbaa981d --- /dev/null +++ b/test/fixtures/apps/demo/config/config.default.js @@ -0,0 +1,2 @@ +exports.keys = 'foo'; +exports.protocolHeaders = 'X-Forwarded-Proto'; diff --git a/test/fixtures/apps/demo/package.json b/test/fixtures/apps/demo/package.json new file mode 100644 index 0000000000..e38ae2ede5 --- /dev/null +++ b/test/fixtures/apps/demo/package.json @@ -0,0 +1,3 @@ +{ + "name": "demo" +} diff --git a/test/fixtures/apps/development/app/public/foo.js b/test/fixtures/apps/development/app/public/foo.js new file mode 100644 index 0000000000..fb1c35f957 --- /dev/null +++ b/test/fixtures/apps/development/app/public/foo.js @@ -0,0 +1 @@ +alert('bar'); diff --git a/test/fixtures/apps/development/app/router.js b/test/fixtures/apps/development/app/router.js new file mode 100644 index 0000000000..e0cefa5068 --- /dev/null +++ b/test/fixtures/apps/development/app/router.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = app => { + app.get('/foo.js', function* () { + this.body = 'foo.js'; + }); + + app.get('/foo', function* () { + this.body = 'foo'; + }); +}; diff --git a/test/fixtures/apps/development/config/config.default.js b/test/fixtures/apps/development/config/config.default.js new file mode 100644 index 0000000000..b6cba897c8 --- /dev/null +++ b/test/fixtures/apps/development/config/config.default.js @@ -0,0 +1 @@ +exports.keys = 'foo'; diff --git a/test/fixtures/apps/development/config/plugin.js b/test/fixtures/apps/development/config/plugin.js new file mode 100644 index 0000000000..fadc25284d --- /dev/null +++ b/test/fixtures/apps/development/config/plugin.js @@ -0,0 +1 @@ +exports.static = true; diff --git a/test/fixtures/apps/development/package.json b/test/fixtures/apps/development/package.json new file mode 100644 index 0000000000..64279c6306 --- /dev/null +++ b/test/fixtures/apps/development/package.json @@ -0,0 +1,3 @@ +{ + "name": "development" +} diff --git a/test/fixtures/apps/encrypt-cookies/app/controller/home.js b/test/fixtures/apps/encrypt-cookies/app/controller/home.js new file mode 100644 index 0000000000..3c687c3fa3 --- /dev/null +++ b/test/fixtures/apps/encrypt-cookies/app/controller/home.js @@ -0,0 +1,17 @@ +module.exports = function* () { + var encrypt = this.getCookie('foo', { + encrypt: true + }); + var encryptWrong = this.getCookie('foo'); + var plain = this.getCookie('plain'); + this.setCookie('foo', 'bar 中文', { + encrypt: true + }); + this.setCookie('plain', 'text ok'); + this.body = { + set: 'bar 中文', + encrypt: encrypt, + encryptWrong: encryptWrong, + plain: plain, + }; +}; diff --git a/test/fixtures/apps/encrypt-cookies/app/router.js b/test/fixtures/apps/encrypt-cookies/app/router.js new file mode 100644 index 0000000000..fa70390f6a --- /dev/null +++ b/test/fixtures/apps/encrypt-cookies/app/router.js @@ -0,0 +1,3 @@ +module.exports = app => { + app.get('/', app.controller.home); +}; diff --git a/test/fixtures/apps/encrypt-cookies/config/config.default.js b/test/fixtures/apps/encrypt-cookies/config/config.default.js new file mode 100644 index 0000000000..b6cba897c8 --- /dev/null +++ b/test/fixtures/apps/encrypt-cookies/config/config.default.js @@ -0,0 +1 @@ +exports.keys = 'foo'; diff --git a/test/fixtures/apps/encrypt-cookies/package.json b/test/fixtures/apps/encrypt-cookies/package.json new file mode 100644 index 0000000000..16a18b2476 --- /dev/null +++ b/test/fixtures/apps/encrypt-cookies/package.json @@ -0,0 +1,3 @@ +{ + "name": "encrypt-cookies" +} diff --git a/test/fixtures/apps/favicon/app/controller/home.js b/test/fixtures/apps/favicon/app/controller/home.js new file mode 100644 index 0000000000..ac412a98d8 --- /dev/null +++ b/test/fixtures/apps/favicon/app/controller/home.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = function*() { + this.body = 'home'; +}; diff --git a/test/fixtures/apps/favicon/app/router.js b/test/fixtures/apps/favicon/app/router.js new file mode 100644 index 0000000000..b3e62cf0b3 --- /dev/null +++ b/test/fixtures/apps/favicon/app/router.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = app => { + app.get('/', app.controller.home); +}; diff --git a/test/fixtures/apps/favicon/config/config.default.js b/test/fixtures/apps/favicon/config/config.default.js new file mode 100644 index 0000000000..c569bde7ed --- /dev/null +++ b/test/fixtures/apps/favicon/config/config.default.js @@ -0,0 +1,5 @@ +exports.siteFile = { + '/favicon.ico': 'https://eggjs.org/favicon.ico', +} + +exports.keys = 'foo'; diff --git a/test/fixtures/apps/favicon/package.json b/test/fixtures/apps/favicon/package.json new file mode 100644 index 0000000000..61f27eae24 --- /dev/null +++ b/test/fixtures/apps/favicon/package.json @@ -0,0 +1,3 @@ +{ + "name": "favicon" +} diff --git a/test/fixtures/apps/helper/app/controller/home.js b/test/fixtures/apps/helper/app/controller/home.js new file mode 100644 index 0000000000..62d57de545 --- /dev/null +++ b/test/fixtures/apps/helper/app/controller/home.js @@ -0,0 +1,3 @@ +module.exports = function* () { + this.body = 'hello home'; +}; diff --git a/test/fixtures/apps/helper/app/router.js b/test/fixtures/apps/helper/app/router.js new file mode 100644 index 0000000000..d9c4640fa9 --- /dev/null +++ b/test/fixtures/apps/helper/app/router.js @@ -0,0 +1,33 @@ +module.exports = app => { + app.get('home', '/home', 'home'); + + app.get('/pathFor', function* () { + this.body = this.helper.pathFor('home', this.query); + }); + + app.get('/urlFor', function* () { + this.body = this.helper.urlFor('home', this.query); + }); + + app.get('/escape', function* () { + this.body = this.helper.escape(' + + diff --git a/test/fixtures/apps/view-render/app/views/shtml.html b/test/fixtures/apps/view-render/app/views/shtml.html new file mode 100644 index 0000000000..69e5e488a3 --- /dev/null +++ b/test/fixtures/apps/view-render/app/views/shtml.html @@ -0,0 +1 @@ +{{helper.shtml(foo)}} diff --git a/test/fixtures/apps/view-render/app/views/sjs.html b/test/fixtures/apps/view-render/app/views/sjs.html new file mode 100644 index 0000000000..c790bd3c9b --- /dev/null +++ b/test/fixtures/apps/view-render/app/views/sjs.html @@ -0,0 +1 @@ +var foo = "{{ helper.sjs(foo) }}"; diff --git a/test/fixtures/apps/view-render/app/views/xss.html b/test/fixtures/apps/view-render/app/views/xss.html new file mode 100644 index 0000000000..0bc6a5eef3 --- /dev/null +++ b/test/fixtures/apps/view-render/app/views/xss.html @@ -0,0 +1,4 @@ +{{ url }} +{{ url | safe }} +{{ helper.surl(url) }} +{{ html }} diff --git a/test/fixtures/apps/view-render/config/config.default.js b/test/fixtures/apps/view-render/config/config.default.js new file mode 100644 index 0000000000..fbbbdfcbf8 --- /dev/null +++ b/test/fixtures/apps/view-render/config/config.default.js @@ -0,0 +1,5 @@ +exports.security = { + csp: { + enable: true, + }, +}; diff --git a/test/fixtures/apps/view-render/config/plugin.js b/test/fixtures/apps/view-render/config/plugin.js new file mode 100644 index 0000000000..d41f56e103 --- /dev/null +++ b/test/fixtures/apps/view-render/config/plugin.js @@ -0,0 +1,6 @@ +'use strict'; + +exports.view = { + enable: true, + package: 'egg-view-nunjucks', +}; diff --git a/test/fixtures/apps/view-render/package.json b/test/fixtures/apps/view-render/package.json new file mode 100644 index 0000000000..f87c58daea --- /dev/null +++ b/test/fixtures/apps/view-render/package.json @@ -0,0 +1,3 @@ +{ + "name": "view-render" +} diff --git a/test/fixtures/apps/view/app/app.js b/test/fixtures/apps/view/app/app.js new file mode 100644 index 0000000000..4449388f4a --- /dev/null +++ b/test/fixtures/apps/view/app/app.js @@ -0,0 +1,5 @@ +'use strict'; + +// view-framework -> view +module.exports = app => { +}; diff --git a/test/fixtures/apps/view/app/controller/home.js b/test/fixtures/apps/view/app/controller/home.js new file mode 100644 index 0000000000..d9c79475ea --- /dev/null +++ b/test/fixtures/apps/view/app/controller/home.js @@ -0,0 +1,16 @@ +'use strict'; + +exports.index = function *() { + this.locals = {b: 'b'}; + yield this.render('index.html', { + a: '111', + }); +}; + +exports.string = function *() { + this.locals = {b: 'b'}; + this.body = yield this.renderString('{{a}}', { + a: '111', + }); +}; + diff --git a/test/fixtures/apps/view/app/helper.js b/test/fixtures/apps/view/app/helper.js new file mode 100644 index 0000000000..7867d12722 --- /dev/null +++ b/test/fixtures/apps/view/app/helper.js @@ -0,0 +1,3 @@ +exports.testHelper = () => { + return 'testHelper'; +}; diff --git a/test/fixtures/apps/view/app/router.js b/test/fixtures/apps/view/app/router.js new file mode 100644 index 0000000000..e548171535 --- /dev/null +++ b/test/fixtures/apps/view/app/router.js @@ -0,0 +1,6 @@ +'use strict'; + +module.exports = app => { + app.get('/', app.controller.home.index); + app.get('/string', app.controller.home.string); +}; diff --git a/test/fixtures/apps/view/app/views/index.html b/test/fixtures/apps/view/app/views/index.html new file mode 100644 index 0000000000..1faa87a613 --- /dev/null +++ b/test/fixtures/apps/view/app/views/index.html @@ -0,0 +1,2 @@ +{{a}} +{{helper.testHelper()}} diff --git a/test/fixtures/apps/view/config/config.default.js b/test/fixtures/apps/view/config/config.default.js new file mode 100644 index 0000000000..b6cba897c8 --- /dev/null +++ b/test/fixtures/apps/view/config/config.default.js @@ -0,0 +1 @@ +exports.keys = 'foo'; diff --git a/test/fixtures/apps/view/package.json b/test/fixtures/apps/view/package.json new file mode 100644 index 0000000000..d03eee3ee5 --- /dev/null +++ b/test/fixtures/apps/view/package.json @@ -0,0 +1,3 @@ +{ + "name": "view" +} diff --git a/test/fixtures/apps/watcher-development-app/agent.js b/test/fixtures/apps/watcher-development-app/agent.js new file mode 100644 index 0000000000..796e70652f --- /dev/null +++ b/test/fixtures/apps/watcher-development-app/agent.js @@ -0,0 +1,26 @@ +'use strict'; + +const utils = require('../../../utils'); +const file_path1 = utils.getFilepath('apps/watcher-development-app/tmp-agent.txt'); +const dir_path = utils.getFilepath('apps/watcher-development-app/tmp-agent'); + +module.exports = function(agent) { + let count = 0; + function listener() { + count++; + } + + agent.messenger.on('i-want-agent-file-changed-count', function() { + agent.messenger.broadcast('agent-file-changed-count', count); + }); + + agent.messenger.on('agent-unwatch', function() { + agent.watcher.unwatch([file_path1, dir_path], listener); + agent.messenger.broadcast('agent-unwatch-success', 'agent unwatch success'); + }); + + agent.messenger.on('agent-watch', function() { + agent.watcher.watch([file_path1, dir_path], listener); + agent.messenger.broadcast('agent-watch-success', 'agent watch success'); + }); +}; diff --git a/test/fixtures/apps/watcher-development-app/app/router.js b/test/fixtures/apps/watcher-development-app/app/router.js new file mode 100644 index 0000000000..7722b41bec --- /dev/null +++ b/test/fixtures/apps/watcher-development-app/app/router.js @@ -0,0 +1,55 @@ +'use strict'; + +const utils = require('../../../../utils'); +const file_path1 = utils.getFilepath('apps/watcher-development-app/tmp.txt'); +// const file_path2 = utils.getFilePath('apps/watcher-development-app/tmp/tmp.txt'); +const dir_path = utils.getFilepath('apps/watcher-development-app/tmp'); + +module.exports = function(app) { + let fileChangeCount = 0; + + function callback() { + fileChangeCount++; + } + + app.get('/app-watch', function*() { + app.watcher.watch([file_path1, dir_path], callback); + this.body = 'app watch success'; + }); + + app.get('/app-unwatch', function*() { + app.watcher.unwatch([file_path1, dir_path], callback); + this.body = 'app unwatch success'; + }); + + app.get('/app-msg', function*() { + this.body = fileChangeCount; + }); + + app.get('/agent-watch', function*() { + app.messenger.broadcast('agent-watch'); + this.body = yield new Promise(function(resolve) { + app.messenger.on('agent-watch-success', function(msg) { + resolve(msg); + }); + }); + }); + + app.get('/agent-unwatch', function*() { + app.messenger.broadcast('agent-unwatch'); + this.body = yield new Promise(function(resolve) { + app.messenger.on('agent-unwatch-success', function(msg) { + resolve(msg); + }); + }); + }); + + app.get('/agent-msg', function*() { + app.messenger.broadcast('i-want-agent-file-changed-count'); + this.body = yield new Promise(function(resolve) { + app.messenger.on('agent-file-changed-count', function(msg) { + resolve(msg); + }); + }); + }); +}; diff --git a/test/fixtures/apps/watcher-development-app/config/config.unittest.js b/test/fixtures/apps/watcher-development-app/config/config.unittest.js new file mode 100644 index 0000000000..ed9616cfbf --- /dev/null +++ b/test/fixtures/apps/watcher-development-app/config/config.unittest.js @@ -0,0 +1,7 @@ +'use strict'; + +exports.env = 'local'; + +exports.watcher = { + type: 'development', +}; diff --git a/test/fixtures/apps/watcher-development-app/package.json b/test/fixtures/apps/watcher-development-app/package.json new file mode 100644 index 0000000000..ccab716e55 --- /dev/null +++ b/test/fixtures/apps/watcher-development-app/package.json @@ -0,0 +1,3 @@ +{ + "name": "watcher-app" +} \ No newline at end of file diff --git a/test/fixtures/apps/watcher-development-app/tmp-agent.txt b/test/fixtures/apps/watcher-development-app/tmp-agent.txt new file mode 100644 index 0000000000..01f02e32ce --- /dev/null +++ b/test/fixtures/apps/watcher-development-app/tmp-agent.txt @@ -0,0 +1 @@ +bbb \ No newline at end of file diff --git a/test/fixtures/apps/watcher-development-app/tmp-agent/tmp.txt b/test/fixtures/apps/watcher-development-app/tmp-agent/tmp.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/fixtures/apps/watcher-development-app/tmp.txt b/test/fixtures/apps/watcher-development-app/tmp.txt new file mode 100644 index 0000000000..7c4a013e52 --- /dev/null +++ b/test/fixtures/apps/watcher-development-app/tmp.txt @@ -0,0 +1 @@ +aaa \ No newline at end of file diff --git a/test/fixtures/apps/watcher-development-app/tmp/tmp.txt b/test/fixtures/apps/watcher-development-app/tmp/tmp.txt new file mode 100644 index 0000000000..7c4a013e52 --- /dev/null +++ b/test/fixtures/apps/watcher-development-app/tmp/tmp.txt @@ -0,0 +1 @@ +aaa \ No newline at end of file diff --git a/test/fixtures/apps/watcher-type-default/config/config.unittest.js b/test/fixtures/apps/watcher-type-default/config/config.unittest.js new file mode 100644 index 0000000000..bef6520082 --- /dev/null +++ b/test/fixtures/apps/watcher-type-default/config/config.unittest.js @@ -0,0 +1,5 @@ +'use strict'; + +exports.watcher = { + type: 'default', +}; diff --git a/test/fixtures/apps/watcher-type-default/package.json b/test/fixtures/apps/watcher-type-default/package.json new file mode 100644 index 0000000000..de0a2af5f7 --- /dev/null +++ b/test/fixtures/apps/watcher-type-default/package.json @@ -0,0 +1,3 @@ +{ + "name": "watcher-type-default" +} \ No newline at end of file diff --git a/test/fixtures/apps/worker-die/app.js b/test/fixtures/apps/worker-die/app.js new file mode 100644 index 0000000000..e66241423e --- /dev/null +++ b/test/fixtures/apps/worker-die/app.js @@ -0,0 +1 @@ +throw new Error('dddd'); \ No newline at end of file diff --git a/test/fixtures/apps/worker-die/package.json b/test/fixtures/apps/worker-die/package.json new file mode 100644 index 0000000000..475e7df300 --- /dev/null +++ b/test/fixtures/apps/worker-die/package.json @@ -0,0 +1,3 @@ +{ + "name": "worker-die" +} diff --git a/test/fixtures/custom-egg/index.js b/test/fixtures/custom-egg/index.js new file mode 100644 index 0000000000..8f3703b506 --- /dev/null +++ b/test/fixtures/custom-egg/index.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('../../../index'); diff --git a/test/fixtures/custom-egg/package.json b/test/fixtures/custom-egg/package.json new file mode 100644 index 0000000000..377e8c356a --- /dev/null +++ b/test/fixtures/custom-egg/package.json @@ -0,0 +1,3 @@ +{ + "name": "custom-egg" +} diff --git a/test/lib/agent.test.js b/test/lib/agent.test.js new file mode 100644 index 0000000000..c1b91dcf5c --- /dev/null +++ b/test/lib/agent.test.js @@ -0,0 +1,88 @@ +'use strict'; + +const should = require('should'); +const fs = require('fs'); +const path = require('path'); +const request = require('supertest'); +const execSync = require('child_process').execSync; +const mm = require('egg-mock'); +const Agent = require('../../lib/agent'); +const utils = require('../utils'); + +describe('test/lib/agent.test.js', () => { + + afterEach(mm.restore); + + describe('agent.dumpConfig()', () => { + it('should dump config and plugins', () => { + const baseDir = path.join(__dirname, '../fixtures/apps/demo'); + new Agent({ + baseDir, + }); + const json = require(path.join(baseDir, 'run/agent_config.json')); + json.plugins.onerror.version.should.match(/\d+\.\d+\.\d+/); + json.config.name.should.equal('demo'); + }); + }); + + describe('require agent', () => { + it('should exit normal', () => { + execSync(`${process.execPath} -e "require('./lib/agent')"`, { + timeout: 3000, + }); + }); + }); + + describe('close()', () => { + it('should close all listeners', function() { + const baseDir = path.join(__dirname, '../fixtures/apps/demo'); + const agent = new Agent({ + baseDir, + }); + process.listeners('unhandledRejection') + .indexOf(agent._unhandledRejectionHandler).should.not.equal(-1); + agent.close(); + process.listeners('unhandledRejection') + .indexOf(agent._unhandledRejectionHandler).should.equal(-1); + }); + + it('should emit close event before exit', () => { + const baseDir = path.join(__dirname, '../fixtures/apps/demo'); + const agent = new Agent({ + baseDir, + }); + let called = false; + agent.on('close', () => { + called = true; + }); + agent.close(); + called.should.equal(true); + }); + }); + + describe('agent throw', () => { + const baseDir = utils.getFilepath('apps/agent-throw'); + let app; + before(() => { + mm(process.env, 'EGG_LOG', 'none'); + app = utils.cluster('apps/agent-throw'); + return app.ready(); + }); + + after(() => app.close()); + + it('should catch exeption', done => { + request(app.callback()) + .get('/agent-throw') + .expect(200, err => { + should.not.exists(err); + setTimeout(() => { + const body = fs.readFileSync(path.join(baseDir, 'logs/agent-throw/common-error.log'), 'utf8'); + body.should.containEql('nodejs.unhandledExceptionError: agent error'); + app.notExpect(/nodejs.AgentWorkerDiedError/); + done(); + }, 1000); + }); + }); + }); +}); diff --git a/test/lib/application.test.js b/test/lib/application.test.js new file mode 100644 index 0000000000..0fb7d0aaf8 --- /dev/null +++ b/test/lib/application.test.js @@ -0,0 +1,93 @@ +'use strict'; + +const Application = require('../../lib/application'); +const path = require('path'); + +describe('test/lib/application.test.js', () => { + describe('create application', () => { + it('should throw options.baseDir required', () => { + (function() { + new Application({ + baseDir: 1, + }); + }).should.throw('options.baseDir required, and must be a string'); + }); + + it('should throw options.baseDir not exist', () => { + (function() { + new Application({ + baseDir: 'not-exist', + }); + }).should.throw('Directory not-exist not exists'); + }); + + it('should throw options.baseDir is not a directory', () => { + (function() { + new Application({ + baseDir: __filename, + }); + }).should.throw(`Directory ${__filename} is not a directory`); + }); + }); + + describe('application.deprecate', () => { + it('should get deprecate with namespace egg', () => { + const app = createApplication(); + const deprecate = app.deprecate; + deprecate._namespace.should.equal('egg'); + deprecate.should.equal(app.deprecate); + }); + }); + + describe('curl()', () => { + it('should curl success', function* () { + const app = createApplication(); + const res = yield app.curl('https://a.alipayobjects.com/aliBridge/1.0.0/aliBridge.min.js'); + res.status.should.equal(200); + }); + }); + + describe('dumpConfig()', () => { + it('should dump config and plugins', () => { + const baseDir = path.join(__dirname, '../fixtures/apps/demo'); + new Application({ + baseDir, + }); + const json = require(path.join(baseDir, 'run/application_config.json')); + json.plugins.onerror.version.should.match(/\d+\.\d+\.\d+/); + json.config.name.should.equal('demo'); + }); + }); + + describe('close()', () => { + it('should close all listeners', () => { + const baseDir = path.join(__dirname, '../fixtures/apps/demo'); + const application = new Application({ + baseDir, + }); + process.listeners('unhandledRejection') + .indexOf(application._unhandledRejectionHandler).should.not.equal(-1); + application.close(); + process.listeners('unhandledRejection') + .indexOf(application._unhandledRejectionHandler).should.equal(-1); + }); + it('should emit close event before exit', () => { + const baseDir = path.join(__dirname, '../fixtures/apps/demo'); + const application = new Application({ + baseDir, + }); + let called = false; + application.on('close', () => { + called = true; + }); + application.close(); + called.should.equal(true); + }); + }); +}); + +function createApplication(options) { + options = options || {}; + options.baseDir = options.baseDir || path.join(__dirname, '../fixtures/apps/demo'); + return new Application(options); +} diff --git a/test/lib/cluster/agent_worker.test.js b/test/lib/cluster/agent_worker.test.js new file mode 100644 index 0000000000..3814101b98 --- /dev/null +++ b/test/lib/cluster/agent_worker.test.js @@ -0,0 +1,86 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const should = require('should'); +const rimraf = require('rimraf'); +const mm = require('egg-mock'); +const glob = require('glob'); +const utils = require('../../utils'); +const Agent = require('../../..').Agent; + +const fixtures = path.join(__dirname, '../../fixtures'); + +describe.skip('test/lib/cluster/agent_worker.test.js', () => { + + afterEach(mm.restore); + + describe('agent custom loggers', () => { + let agent; + + before(() => { + rimraf.sync(path.join(fixtures, 'apps/custom-logger/logs')); + agent = new Agent({ + baseDir: path.join(fixtures, 'apps/custom-logger'), + }); + }); + + it('should support custom logger in agent', done => { + should.exist(agent.loggers); + should.exist(agent.loggers.myLogger); + + agent.loggers.myLogger.info('hello my logger!'); + + setTimeout(() => { + fs.readFileSync(path.join(fixtures, 'apps/custom-logger/logs/my.log'), 'utf8') + .should.equal('hello my logger!\n'); + done(); + }, 1500); + }); + + it('should reload log in agent', done => { + rimraf.sync(path.join(fixtures, 'apps/custom-logger/logs/my.log')); + + process.emit('message', { + action: 'test-reload-logger', + }); + + setTimeout(() => { + agent.loggers.myLogger.info('goodbye my logger!'); + + setTimeout(() => { + fs.readFileSync(path.join(fixtures, 'apps/custom-logger/logs/my.log'), 'utf8') + .should.equal('goodbye my logger!\n'); + done(); + }, 1500); + }, 200); + }); + }); + + describe('logrotater', () => { + let app; + before(done => { + mm(process.env, 'EGG_LOG', 'NONE'); + app = utils.cluster('apps/app-monitor'); + app.ready(done); + }); + + after(() => app.close()); + + it('should cut the log file', done => { + const baseDir = utils.getFilepath('apps/app-monitor'); + this.app.process.send({ + to: 'agent', + action: 'test-reload-logger', + }); + setTimeout(() => { + const files = glob.sync(path.join(baseDir, 'logs/app-monitor/*.log.*')); + files.length.should.above(0); + files.forEach(file => { + file.should.match(/log.\d{4}-\d{2}-\d{2}$/); + }); + done(); + }, 1000); + }); + }); +}); diff --git a/test/lib/cluster/app_worker.test.js b/test/lib/cluster/app_worker.test.js new file mode 100644 index 0000000000..6da5907395 --- /dev/null +++ b/test/lib/cluster/app_worker.test.js @@ -0,0 +1,22 @@ +'use strict'; + +const request = require('supertest-as-promised'); +const utils = require('../../utils'); + +describe('test/lib/cluster/app_worker.test.js', () => { + let app; + before(done => { + app = utils.cluster('apps/app-server', { + coverage: true, + }); + app.ready(done); + }); + + after(() => app.close()); + + it('should start cluster success and app worker emit `server` event', () => { + return request(app.callback()) + .get('/') + .expect('true'); + }); +}); diff --git a/test/lib/cluster/master.test.js b/test/lib/cluster/master.test.js new file mode 100644 index 0000000000..7f785723a5 --- /dev/null +++ b/test/lib/cluster/master.test.js @@ -0,0 +1,206 @@ +'use strict'; + +const fs = require('fs'); +const mm = require('egg-mock'); +const request = require('supertest-as-promised'); +const coffee = require('coffee'); +const utils = require('../../utils'); + +describe('test/lib/cluster/master.test.js', () => { + + afterEach(mm.restore); + + describe('app worker die', () => { + let app; + before(() => { + mm.env('default'); + app = utils.cluster('apps/app-die'); + // app.debug(); + app.coverage(false); + return app.ready(); + }); + after(() => app.close()); + + it('should restart after app worker exit', done => { + request(app.callback()) + .get('/exit') + // wait for app worker restart + .end(() => setTimeout(() => { + // test common-error.log + const errorFile = utils.getFilepath('apps/app-die/logs/app-die/common-error.log'); + const content = fs.readFileSync(errorFile, 'utf8'); + content.should.match(/nodejs\.AppWorkerDiedError: \[master\]/); + content.should.match(/App Worker#1:\d+ died/); + + // error pipe to console + app.expect('stdout', /App Worker#1:\d+ disconnect/); + app.expect('stderr', /nodejs\.AppWorkerDiedError: \[master\]/); + app.expect('stderr', /App Worker#1:\d+ died/); + app.expect('stdout', /App Worker#2:\d+ started/); + + done(); + // this will be slow on ci env + }, 5000)); + }); + + it('should restart when app worker throw uncaughtException', done => { + request(app.callback()) + .get('/uncaughtException') + // wait for app worker restart + .end(() => setTimeout(() => { + const errorFile = utils.getFilepath('apps/app-die/logs/app-die/common-error.log'); + const content = fs.readFileSync(errorFile, 'utf8'); + content.should.match(/nodejs\.Error: get uncaughtException \(uncaughtException throw 1 times on pid:\d+\)/); + + app.expect('stderr', /\[graceful:worker:\d+:uncaughtException\] throw error 1 times/); + app.expect('stdout', /App Worker#\d:\d+ started/); + done(); + }, 5000)); + }); + }); + + describe('Master start fail', () => { + let master; + + after(() => master.close()); + + it('should master exit with 1', done => { + mm(process.env, 'EGG_LOG', 'none'); + master = utils.cluster('apps/worker-die', { coverage: true }); + master.expect('code', 1).ready(done); + }); + }); + + describe('Master started log', () => { + let app; + + afterEach(() => app.close()); + + it('should dev env stdout message include "Egg started"', done => { + app = utils.cluster('apps/master-worker-started', { coverage: true }); + app.expect('stdout', /Egg started/).ready(done); + }); + + it('should production env stdout message include "Egg started"', done => { + mm.env('prod'); + mm(process.env, 'HOME', utils.getFilepath('apps/mock-production-app/config')); + app = utils.cluster('apps/mock-production-app', { coverage: true }); + app.expect('stdout', /Egg started/).ready(done); + }); + }); + + describe('--cluster', () => { + let app; + before(() => { + mm(process.env, 'EGG_LOG', 'none'); + app = utils.cluster('apps/cluster_mod_app', { coverage: true }); + return app.ready(); + }); + after(() => app.close()); + + it('should online cluster mode startup success', () => { + return request(app.callback()) + .get('/') + .expect('hi cluster') + .expect(200); + }); + }); + + describe('--dev', () => { + let app; + before(() => { + app = utils.cluster('apps/cluster_mod_app', { coverage: true }); + return app.ready(); + }); + after(() => app.close()); + + it('should dev cluster mode startup success', () => { + return request(app) + .get('/') + .expect('hi cluster') + .expect(200); + }); + }); + + describe('start app with custom env', () => { + describe('cluster mode, serverEnv: prod', () => { + let app; + before(() => { + mm.env('prod'); + mm(process.env, 'HOME', utils.getFilepath('apps/custom-env-app')); + app = utils.cluster('apps/custom-env-app'); + return app.ready(); + }); + after(() => app.close()); + + it('should start with prod env', () => { + return request(app.callback()) + .get('/') + .expect({ + env: 'prod', + }) + .expect(200); + }); + }); + }); + + describe('framework start', () => { + let app; + before(() => { + // dependencies relation: + // aliyun-egg-app -> aliyun-egg-biz -> aliyun-egg -> egg + mm(process.env, 'HOME', utils.getFilepath('apps/aliyun-egg-app')); + app = utils.cluster('apps/aliyun-egg-app', { + customEgg: utils.getFilepath('apps/aliyun-egg-biz'), + }); + return app.ready(); + }); + after(() => app.close()); + + it('should start success', () => { + return request(app.callback()) + .get('/') + .expect({ + 'aliyun-egg-core': true, + 'aliyun-egg-plugin': true, + 'aliyun-egg-agent': true, + }) + .expect(200); + }); + }); + + describe('spawn start', () => { + let app; + after(() => { + // make sure process exit + app.proc.kill('SIGTERM'); + }); + + it('should not cause master die when agent start error', done => { + app = coffee.spawn('node', [ utils.getFilepath('apps/agent-die/start.js') ]) + .coverage(false); + + // spawn can't comunication, so `end` event won't emit + setTimeout(() => { + app.emit('close', 0); + app.notExpect('stderr', /TypeError: process\.send is not a function/); + done(); + }, 10000); + }); + }); + + describe.skip('close watcher and logrotator both', () => { + let app; + before(done => { + mm.env('default'); + app = utils.cluster('apps/close-watcher-logrotator'); + app.ready(done); + }); + + after(() => app.close()); + + it('agent should exit normal', () => { + app.notExpect('stderr', /nodejs\.AgentWorkerDiedError/); + }); + }); +}); diff --git a/test/lib/core/agent_worker_client.test.js b/test/lib/core/agent_worker_client.test.js new file mode 100644 index 0000000000..6328a74d22 --- /dev/null +++ b/test/lib/core/agent_worker_client.test.js @@ -0,0 +1,289 @@ +'use strict'; + +const should = require('should'); +const mm = require('egg-mock'); +const request = require('supertest-as-promised'); +const sleep = require('co-sleep'); +const path = require('path'); +const EventEmitter = require('events').EventEmitter; +const utils = require('../../utils'); +const Agent = require('../../..').Agent; +const fs = require('fs'); + +const fixtures = path.join(__dirname, '../../fixtures'); + +describe('test/lib/core/agent_worker_client.test.js', () => { + describe('single process', () => { + let agent; + let client; + + before(done => { + agent = new Agent({ + baseDir: path.join(fixtures, 'apps/demo'), + }); + + const realClient = Object.assign(Object.create(EventEmitter.prototype), { + ready(flagOrFunction) { + this._ready = !!this._ready; + this._readyCallbacks = this._readyCallbacks || []; + + if (typeof flagOrFunction === 'function') { + this._readyCallbacks.push(flagOrFunction); + } else { + this._ready = !!flagOrFunction; + } + + if (this._ready) { + this._readyCallbacks.splice(0, Infinity).forEach(callback => process.nextTick(callback)); + } + return this; + }, + getCallback(id, callback) { + setTimeout(() => { + if (id === 'error') { + callback(new Error('mock error')); + } else { + callback(null, 'mock data'); + } + }, 100); + }, + getData() { + return new Promise(resolve => { + setTimeout(() => { + resolve('mock data'); + }, 100); + }); + }, + * getDataGenerator() { + yield sleep(100); + return 'mock data'; + }, + getError() { + return new Promise((_, reject) => { + setTimeout(() => { + reject(new Error('mock error')); + }, 100); + }); + }, + }); + + client = agent.startAgent({ + name: 'mock', + client: realClient, + subscribe(info, listener) { + realClient.on(info.id, listener); + }, + formatKey(info) { + return info.id; + }, + }); + client.ready(done); + realClient.ready(true); + }); + + afterEach(mm.restore); + + it('should ready', done => { + client.ready(done); + + setTimeout(() => { + client.innerClient.ready(true); + }, 100); + }); + + it('should not exit when dumpConfig error', () => { + const writeFileSync = fs.writeFileSync; + mm(fs, 'writeFileSync', function() { + if (arguments[0] && arguments[0].endsWith('config.json')) { + throw new Error('mock error'); + } + writeFileSync.apply(fs, arguments); + }); + new Agent({ + baseDir: path.join(fixtures, 'apps/demo'), + }); + }); + + it('should subscribe well', done => { + mm(client, '_broadcast', (command, info) => { + command.should.equal('mock_subscribe_changed'); + info.value.should.equal('mock data'); + done(); + }); + + client.messenger.emit('mock_subscribe_request', { + info: { + id: 'mockKey', + }, + pid: 123, + }); + + setImmediate(() => client.innerClient.emit('mockKey', 'mock data')); + }); + + it('should invoke callback well', done => { + mm(client, '_sendTo', (pid, action, data) => { + pid.should.equal(123); + action.should.equal('mock_invoke_response'); + data.success.should.equal(true); + data.data.should.equal('mock data'); + done(); + }); + + client.messenger.emit('mock_invoke_request', { + opaque: 1, + method: 'getCallback', + args: [ '123' ], + pid: 123, + }); + }); + + it('should invoke callback with error', done => { + mm(client, '_sendTo', (pid, action, data) => { + pid.should.equal(123); + action.should.equal('mock_invoke_response'); + data.success.should.equal(false); + should.exist(data.errorMessage); + data.errorMessage.should.equal('mock error'); + done(); + }); + + client.messenger.emit('mock_invoke_request', { + method: 'getCallback', + args: [ 'error' ], + pid: 123, + }); + }); + + it('should invoke promise well', done => { + mm(client, '_sendTo', (pid, action, data) => { + pid.should.equal(123); + action.should.equal('mock_invoke_response'); + data.success.should.equal(true); + data.data.should.equal('mock data'); + done(); + }); + + client.messenger.emit('mock_invoke_request', { + method: 'getData', + args: [ '123' ], + pid: 123, + }); + }); + + it('should invoke generatorFunction well', done => { + mm(client, '_sendTo', (pid, action, data) => { + pid.should.equal(123); + action.should.equal('mock_invoke_response'); + data.success.should.equal(true); + data.data.should.equal('mock data'); + done(); + }); + + client.messenger.emit('mock_invoke_request', { + method: 'getDataGenerator', + args: [ '123' ], + pid: 123, + }); + }); + + it('should invoke error', done => { + mm(client, '_sendTo', (pid, action, data) => { + pid.should.equal(123); + action.should.equal('mock_invoke_response'); + data.success.should.equal(false); + should.exist(data.errorMessage); + data.errorMessage.should.equal('mock error'); + done(); + }); + + client.messenger.emit('mock_invoke_request', { + method: 'getError', + args: [ '123' ], + pid: 123, + }); + }); + }); + + describe('cluster', () => { + let app; + + before(() => { + app = utils.cluster('apps/agent-app'); + return app.ready(); + }); + + after(() => app.close()); + + it('should request ok', () => { + return request(app.callback()) + .get('/') + .expect(200, 'ok'); + }); + + it('should request getData ok', () => { + return request(app.callback()) + .get('/getData') + .expect(200, 'mock data'); + }); + + it('should request getDataGenerator ok', () => { + return request(app.callback()) + .get('/getDataGenerator') + .expect(200, 'mock data'); + }); + + it('should request getError', () => { + return request(app.callback()) + .get('/getError') + .expect(200, 'mock error'); + }); + + it('should request sub ok', () => { + return request(app.callback()) + .get('/sub') + .expect(200, 'bar'); + }); + }); + + describe('agent sync callback', () => { + let app; + before(() => { + app = utils.app('apps/agent-app-sync'); + return app.ready(); + }); + after(() => app.close()); + + it('should call', () => { + return request(app.callback()) + .get('/') + .expect(200) + .expect('test'); + }); + }); + + describe('agent restart', () => { + let app; + before(() => { + app = utils.cluster('apps/agent-restart'); + // app.debug(); + app.expect('code', 0); + return app.ready(); + }); + + before(done => { + app.process.send({ + action: 'die', + to: 'agent', + }); + setTimeout(done, 8000); + }); + after(() => app.close()); + + it('should resend subscribe', () => { + const stdout = app.stdout; + stdout.match(/Agent Worker started/g).length.should.eql(2); + stdout.match(/agent subscribe aaa/g).length.should.eql(2); + }); + }); +}); diff --git a/test/lib/core/app/extend/agent.test.js b/test/lib/core/app/extend/agent.test.js new file mode 100644 index 0000000000..2a75b74ee1 --- /dev/null +++ b/test/lib/core/app/extend/agent.test.js @@ -0,0 +1,54 @@ +'use strict'; + +const fs = require('fs'); +const mm = require('egg-mock'); +const sleep = require('co-sleep'); +const utils = require('../../../../utils'); + +describe('test/lib/core/app/extend/agent.test.js', () => { + afterEach(mm.restore); + + describe('agent.addSingleton()', () => { + let app; + before(done => { + app = utils.app('apps/singleton-demo'); + app.ready(done); + }); + after(() => app.close()); + + it('should add singleton success', function* () { + let config = yield app.agent.dataService.get('second').getConfig(); + config.foo.should.equal('bar'); + config.foo2.should.equal('bar2'); + + const ds = yield app.agent.dataService.createInstance({ foo: 'barrr' }); + config = yield ds.getConfig(); + config.foo.should.equal('barrr'); + }); + }); + + describe('agent.instrument()', () => { + it.skip('should not log in unittest env', function* () { + mm.env('unittest'); + const app = utils.app('apps/agent-instrument'); + yield app.ready(); + yield sleep(1000); + // TODO: why egg-agent.log not exists? + const log = fs.readFileSync( + utils.getFilepath('apps/agent-instrument/logs/agent-instrument/egg-agent.log'), 'utf8'); + log.should.not.match(/\[http\] \/hello/); + app.close(); + }); + + it('should log in local env', function* () { + mm.env('local'); + const app = utils.app('apps/agent-instrument', { cache: false }); + yield app.ready(); + yield sleep(1000); + const log = fs.readFileSync( + utils.getFilepath('apps/agent-instrument/logs/agent-instrument/egg-agent.log'), 'utf8'); + log.should.match(/\[http\] \/hello/); + app.close(); + }); + }); +}); diff --git a/test/lib/core/app/extend/application.test.js b/test/lib/core/app/extend/application.test.js new file mode 100644 index 0000000000..ae7e6ab1e7 --- /dev/null +++ b/test/lib/core/app/extend/application.test.js @@ -0,0 +1,113 @@ +'use strict'; + +const request = require('supertest-as-promised'); +const mm = require('egg-mock'); +const utils = require('../../../../utils'); + +describe('test/lib/core/app/extend/application.test.js', () => { + describe('app.logger', () => { + let app; + before(() => { + app = utils.app('apps/demo'); + return app.ready(); + }); + after(() => app.close()); + + it('should alias app.logger => app.loggers.logger', () => { + app.logger.should.equal(app.loggers.logger); + }); + }); + + describe('app.inspect()', () => { + it('should inspect app properties', done => { + const app = utils.app('apps/demo'); + app.ready(() => { + app.inspect().should.have.properties([ + 'name', 'baseDir', + 'env', 'subdomainOffset', 'poweredBy', + 'controller', 'middlewares', 'serviceClasses', + 'config', 'urllib', 'loggers', + ]); + app.inspect().name.should.equal('demo'); + app.close(); + done(); + }); + }); + }); + + describe('app.readyCallback()', () => { + let app; + after(() => app.close()); + + it('should log info when plugin is not ready', done => { + app = utils.app('apps/notready'); + mm(app.console, 'warn', (message, a) => { + message.should.eql('[egg:core:ready_timeout] 10 seconds later %s was still unable to finish.'); + a.should.eql('a'); + done(); + }); + app.ready(() => { + throw new Error('should not be called'); + }); + }); + }); + + describe('app.locals', () => { + let app; + before(() => { + app = utils.app('apps/locals'); + return app.ready(); + }); + after(() => app.close()); + + it('should app.locals is same ref', () => { + return request(app.callback()) + .get('/app_same_ref') + .expect('true'); + }); + }); + + describe('app.createAnonymousContext()', () => { + let app; + before(() => { + app = utils.app('apps/demo'); + return app.ready(); + }); + after(() => app.close()); + + it('should get anonymous context object', function* () { + const ctx = app.createAnonymousContext({ + socket: { + remoteAddress: '10.0.0.1', + }, + headers: { + 'x-forwarded-for': '10.0.0.1', + }, + url: '/foobar?ok=1', + }); + ctx.ip.should.equal('10.0.0.1'); + ctx.url.should.equal('/foobar?ok=1'); + ctx.socket.remoteAddress.should.equal('10.0.0.1'); + ctx.socket.remotePort.should.equal(7001); + }); + }); + + describe('app.addSingleton()', () => { + let app; + before(() => { + app = utils.app('apps/singleton-demo'); + return app.ready(); + }); + after(() => app.close()); + + it('should add singleton success', function* () { + let config = yield app.dataService.get('first').getConfig(); + config.foo.should.equal('bar'); + config.foo1.should.equal('bar1'); + + const ds = yield app.dataService.createInstance({ foo: 'barrr' }); + config = yield ds.getConfig(); + config.foo.should.equal('barrr'); + }); + }); +}); diff --git a/test/lib/core/app/extend/context.jsonp.test.js b/test/lib/core/app/extend/context.jsonp.test.js new file mode 100644 index 0000000000..d543fd78ff --- /dev/null +++ b/test/lib/core/app/extend/context.jsonp.test.js @@ -0,0 +1,43 @@ +'use strict'; + +const mm = require('egg-mock'); +const request = require('supertest-as-promised'); +const utils = require('../../../../utils'); + +describe('test/lib/core/app/extend/context.jsonp.test.js', () => { + let app; + before(() => { + app = utils.app('apps/demo'); + return app.ready(); + }); + after(() => app.close()); + afterEach(mm.restore); + + it('should response jsonp', () => { + return request(app.callback()) + .get('/user.json?_callback=$jQuery110208780175377614796_1406016639408&ctoken=123') + .set('Cookie', 'ctoken=123') + .expect('Content-Type', 'application/javascript; charset=utf-8') + .expect('X-Content-Type-Options', 'nosniff') + .expect('/**/ typeof $jQuery110208780175377614796_1406016639408 === \'function\' && $jQuery110208780175377614796_1406016639408({"name":"fengmk2"});') + .expect(200); + }); + + it('should response json body when callback empty', () => { + return request(app.callback()) + .get('/user.json?_callback=&ctoken=123') + .set('Cookie', 'ctoken=123') + .expect('Content-Type', 'application/json; charset=utf-8') + .expect('{"name":"fengmk2"}') + .expect(200); + }); + + it('should response json body when callback missing', () => { + return request(app.callback()) + .get('/user.json?callback=&ctoken=123') + .set('Cookie', 'ctoken=123') + .expect('Content-Type', 'application/json; charset=utf-8') + .expect('{"name":"fengmk2"}') + .expect(200); + }); +}); diff --git a/test/lib/core/app/extend/context.test.js b/test/lib/core/app/extend/context.test.js new file mode 100644 index 0000000000..8fb6b4fdd6 --- /dev/null +++ b/test/lib/core/app/extend/context.test.js @@ -0,0 +1,308 @@ +'use strict'; + +const should = require('should'); +const fs = require('fs'); +const path = require('path'); +const mm = require('egg-mock'); +const request = require('supertest-as-promised'); +const rimraf = require('rimraf'); +const utils = require('../../../../utils'); + +describe('test/lib/core/app/extend/context.test.js', () => { + + afterEach(mm.restore); + + describe('ctx.logger', () => { + const baseDir = utils.getFilepath('apps/demo'); + + beforeEach(() => { + rimraf.sync(path.join(baseDir, 'logs')); + }); + + let app; + afterEach(() => app.close()); + + it('env=local: level => debug', done => { + mm.env('local'); + mm(process.env, 'EGG_LOG', 'none'); + app = utils.app('apps/demo'); + const logdir = app.config.logger.dir; + + request(app.callback()) + .get('/logger?message=foo') + .expect('logger', err => { + should.not.exists(err); + + const errorContent = fs.readFileSync(path.join(logdir, 'common-error.log'), 'utf8'); + errorContent.should.containEql('nodejs.Error: error foo'); + errorContent.should.containEql('nodejs.Error: core error foo'); + + const loggerContent = fs.readFileSync(path.join(logdir, 'demo-web.log'), 'utf8'); + loggerContent.should.containEql('debug foo'); + loggerContent.should.containEql('info foo'); + loggerContent.should.containEql('warn foo'); + + const coreLoggerContent = fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'); + coreLoggerContent.should.containEql('core debug foo'); + coreLoggerContent.should.containEql('core info foo'); + coreLoggerContent.should.containEql('core warn foo'); + done(); + }); + }); + + it('env=unittest: level => info', done => { + mm.env('unittest'); + app = utils.app('apps/demo'); + app.ready(() => { + const logdir = app.config.logger.dir; + + app.mockContext({ + userId: '123123', + tracer: { + traceId: '456456', + }, + }); + + request(app.callback()) + .get('/logger?message=foo') + .expect('logger', err => { + should.not.exists(err); + + const errorContent = fs.readFileSync(path.join(logdir, 'common-error.log'), 'utf8'); + errorContent.should.containEql('nodejs.Error: error foo'); + errorContent.should.containEql('nodejs.Error: core error foo'); + errorContent.should.match(/\[123123\/[\d\.]+\/456456\/\d+ms GET \/logger\?message=foo]/); + + const loggerContent = fs.readFileSync(path.join(logdir, 'demo-web.log'), 'utf8'); + loggerContent.should.not.containEql('debug foo'); + loggerContent.should.containEql('info foo'); + loggerContent.should.containEql('warn foo'); + + const coreLoggerContent = fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'); + coreLoggerContent.should.not.containEql('core debug foo'); + coreLoggerContent.should.containEql('core info foo'); + coreLoggerContent.should.containEql('core warn foo'); + + done(); + }); + }); + }); + + it('env=prod: level => info', done => { + mm.env('unittest'); + app = utils.app('apps/demo'); + const logdir = app.config.logger.dir; + + request(app.callback()) + .get('/logger?message=foo') + .expect('logger', err => { + should.not.exists(err); + + const errorContent = fs.readFileSync(path.join(logdir, 'common-error.log'), 'utf8'); + errorContent.should.containEql('nodejs.Error: error foo'); + errorContent.should.containEql('nodejs.Error: core error foo'); + + const loggerContent = fs.readFileSync(path.join(logdir, 'demo-web.log'), 'utf8'); + loggerContent.should.not.containEql('debug foo'); + loggerContent.should.containEql('info foo'); + loggerContent.should.containEql('warn foo'); + + const coreLoggerContent = fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8'); + coreLoggerContent.should.not.containEql('core debug foo'); + coreLoggerContent.should.containEql('core info foo'); + coreLoggerContent.should.containEql('core warn foo'); + + done(); + }); + }); + }); + + describe('properties', () => { + let app; + before(() => { + app = utils.app('apps/context-config-app'); + return app.ready(); + }); + after(() => app.close()); + + describe('ctx.router', () => { + it('should work', () => { + return request(app.callback()) + .get('/') + .expect(200) + .expect('{"path":"/","foo":1,"bar":2}'); + }); + }); + + describe('ctx.runtime', () => { + it('should work', () => { + return request(app.callback()) + .get('/runtime') + .expect(200) + .expect('{"mysql":10,"foo":11}'); + }); + }); + }); + + describe('ctx.instrument(event, action), app.instrument(event, action)', () => { + let app; + before(() => { + mm.env('local'); + app = utils.app('apps/context-config-app'); + return app.ready(); + }); + after(() => app.close()); + + it('should instrument whatever you want', done => { + const ctx = app.mockContext(); + mm(ctx.logger, 'info', msg => { + msg.should.match(/\[foo\] test action on ctx \d+ms/); + done(); + }); + const ins = ctx.instrument('foo', 'test action on ctx'); + ins.end(); + }); + + it('should app.instrument work', done => { + mm(app.logger, 'info', msg => { + msg.should.match(/\[foo\] test action on app \d+ms/); + done(); + }); + const ins = app.instrument('foo', 'test action on app'); + ins.end(); + }); + }); + + describe('ctx.view', () => { + let app; + before(() => { + app = utils.cluster({ + baseDir: 'apps/view', + customEgg: utils.getFilepath('apps/view-framework'), + }); + return app.ready(); + }); + after(() => app.close()); + + it('should render template', () => { + return request(app.callback()) + .get('/') + .expect(200) + .expect('name=index.html, a=111, b=b, c=testHelper'); + }); + + it('should render string', () => { + return request(app.callback()) + .get('/string') + .expect(200) + .expect('tpl={{a}}, a=111, b=b, c=testHelper'); + }); + }); + + describe('ctx.locals', () => { + let app; + before(() => { + app = utils.app('apps/locals'); + return app.ready(); + }); + after(() => app.close()); + + it('should same this.locals ref on every request ', () => { + return request(app.callback()) + .get('/ctx_same_ref') + .expect('true'); + }); + + it('should this.locals merge app.locals data', () => { + return request(app.callback()) + .get('/ctx_merge_app') + .expect({ + a: 1, + b: 1, + }); + }); + + it('should this.locals cover app.locals data', () => { + return request(app.callback()) + .get('/ctx_override_app') + .expect({ + a: 'ctx.a', + b: 'ctx.b', + }); + }); + + it('should not change this.locals data when app.locals change again', () => { + return request(app.callback()) + .get('/ctx_app_update_can_not_affect_ctx') + .expect({ + a: 'app.a', + b: 'app.b', + newPropertyExists: false, + }); + }); + + it('should locals only support object format', () => { + return request(app.callback()) + .get('/set_only_support_object') + .expect({ + 'ctx.locals.object': true, + 'app.locals.object': true, + 'app.locals.string': false, + 'app.locals.number': false, + 'app.locals.function': false, + 'app.locals.array': false, + 'ctx.locals.string': false, + 'ctx.locals.number': false, + 'ctx.locals.function': false, + 'ctx.locals.array': false, + }); + }); + }); + + describe('ctx.roleFailureHandler()', () => { + it('should detect ajax', function* () { + const context = yield utils.createContext({ isAjax: true }); + context.roleFailureHandler('admin'); + context.body.should.eql({ message: 'Forbidden, required role: admin', stat: 'deny' }); + }); + + it('should response message when is not ajax', function* () { + const context = yield utils.createContext(); + context.roleFailureHandler('admin'); + context.body.should.equal('Forbidden, required role: admin'); + }); + }); + + describe('ctx.curl()', () => { + it('should curl ok', function* () { + const context = yield utils.createContext(); + const res = yield context.curl('https://a.alipayobjects.com/aliBridge/1.0.0/aliBridge.min.js'); + res.status.should.equal(200); + }); + }); + + describe('ctx.realStatus', () => { + it('should get from status ok', function* () { + const context = yield utils.createContext(); + context.status = 200; + context.realStatus.should.equal(200); + }); + + it('should get from realStatus ok', () => { + const context = utils.createContext(); + context.status = 302; + context.realStatus = 500; + context.realStatus.should.equal(500); + }); + }); + + describe('ctx.state', () => { + it('should delegate ctx.locals', function* () { + const context = yield utils.createContext(); + context.locals = { a: 'a', b: 'b' }; + context.state = { a: 'aa', c: 'cc' }; + context.state.should.eql({ a: 'aa', b: 'b', c: 'cc' }); + context.state.should.equal(context.locals); + }); + }); +}); diff --git a/test/lib/core/app/extend/helper.test.js b/test/lib/core/app/extend/helper.test.js new file mode 100644 index 0000000000..bea7ad0b4b --- /dev/null +++ b/test/lib/core/app/extend/helper.test.js @@ -0,0 +1,70 @@ +'use strict'; + +const request = require('supertest-as-promised'); +const utils = require('../../../../utils'); + +describe('test/lib/core/app/extend/helper.test.js', () => { + let app; + before(() => { + app = utils.app('apps/helper'); + return app.ready(); + }); + + describe('pathFor()', () => { + it('should get home path url', () => { + return request(app.callback()) + .get('/pathFor') + .expect('/home') + .expect(200); + }); + + it('should get home path with params', () => { + return request(app.callback()) + .get('/pathFor?foo=bar') + .expect('/home?foo=bar') + .expect(200); + }); + }); + + describe('urlFor()', () => { + it('should get full home url', () => { + return request(app.callback()) + .get('/urlFor') + .expect(/^http:\/\/127\.0\.0\.1:\d+\/home$/) + .expect(200); + }); + + it('should get full home url with params', () => { + return request(app.callback()) + .get('/urlFor?foo=1') + .expect(/^http:\/\/127\.0\.0\.1:\d+\/home\?foo=1$/) + .expect(200); + }); + + }); + + describe.skip('escape()', () => { + it('should escape script', () => { + return request(app.callback()) + .get('/escape') + .expect('<script>') + .expect(200); + }); + }); + + describe.skip('shtml()', () => { + it('should ignore attribute if domain not in domainWhiteList', () => { + return request(app.callback()) + .get('/shtml-not-in-domain-whitelist') + .expect('true') + .expect(200); + }); + + it('should keep attribute if domain in default domainWhiteList', () => { + return request(app.callback()) + .get('/shtml-in-default-domain-whitelist') + .expect('true') + .expect(200); + }); + }); +}); diff --git a/test/lib/core/app/extend/request.test.js b/test/lib/core/app/extend/request.test.js new file mode 100644 index 0000000000..6cb50e2119 --- /dev/null +++ b/test/lib/core/app/extend/request.test.js @@ -0,0 +1,334 @@ +'use strict'; + +const mm = require('egg-mock'); +const should = require('should'); +const merge = require('merge-descriptors'); +const urllib = require('urllib'); +const request = require('supertest'); +const utils = require('../../../../utils'); +const requestExt = require('../../../../../lib/core/app/extend/request'); + +describe('test/lib/core/app/extend/request.test.js', () => { + afterEach(mm.restore); + + describe('req.host', () => { + it('should return host with port', function* () { + const req = yield utils.createRequest(); + req.header.host = 'foo.com:3000'; + req.hostname.should.equal('foo.com'); + }); + + it('should return "localhost" when no host present', function* () { + const req = yield utils.createRequest(); + req.host.should.be.a.String; + req.host.should.equal('localhost'); + }); + + it('should return host from X-Forwarded-Host header', function* () { + const req = yield utils.createRequest(); + req.header['x-forwarded-host'] = 'foo.com'; + req.host.should.be.a.String; + req.host.should.equal('foo.com'); + }); + }); + + describe('req.hostname', () => { + it('should return hostname with port', function* () { + const req = yield utils.createRequest(); + req.header.host = 'foo.com:3000'; + req.hostname.should.equal('foo.com'); + }); + + it('should return "localhost" when no host present', function* () { + const req = yield utils.createRequest(); + req.hostname.should.be.a.String; + req.hostname.should.equal('localhost'); + }); + }); + + describe('req.ip', () => { + it('should return ipv4', function* () { + const req = yield utils.createRequest(); + req.socket.remoteAddress = '::ffff:127.0.0.1'; + req.ip.should.equal('127.0.0.1'); + req.ip.should.equal('127.0.0.1'); + }); + }); + + describe('req.ips', () => { + it('should used x-forwarded-for', function* () { + const req = yield utils.createRequest(); + req.header['x-forwarded-for'] = '127.0.0.1,127.0.0.2'; + req.ips.should.eql([ '127.0.0.1', '127.0.0.2' ]); + }); + + it('should used x-real-ip', function* () { + const req = yield utils.createRequest(); + req.header['x-forwarded-for'] = ''; + req.header['x-real-ip'] = '127.0.0.1,127.0.0.2'; + req.ips.should.eql([ '127.0.0.1', '127.0.0.2' ]); + }); + + it('should return []', function* () { + const req = yield utils.createRequest(); + req.header['x-forwarded-for'] = ''; + req.header['x-real-ip'] = ''; + req.ips.should.eql([]); + }); + }); + + describe('req.protocol', () => { + let app; + beforeEach(() => { + app = utils.app('apps/demo'); + return app.ready(); + }); + afterEach(() => app.close()); + + it('should return http when it not config and no protocol header', () => { + mm(app.config, 'protocl', null); + return request(app.callback()) + .get('/protocol') + .expect('http'); + }); + + it('should return value of X-Custom-Proto', () => { + mm(app.config, 'protocolHeaders', 'X-Custom-Proto'); + return request(app.callback()) + .get('/protocol') + .set('X-Custom-Proto', 'https') + .expect('https'); + }); + + it('should ignore X-Client-Scheme', () => { + mm(app.config, 'protocolHeaders', 'X-Forwarded-Proto'); + return request(app.callback()) + .get('/protocol') + .set('X-Client-Scheme', 'https') + .expect('http'); + }); + + it('should return value of X-Forwarded-Proto', () => { + return request(app.callback()) + .get('/protocol') + .set('x-forwarded-proto', 'https') + .expect('https'); + }); + + it('should ignore X-Forwarded-Proto', () => { + mm(app.config, 'protocolHeaders', ''); + return request(app.callback()) + .get('/protocol') + .set('x-forwarded-proto', 'https') + .expect('http'); + }); + + it('should return value from config', () => { + mm(app.config, 'protocol', 'https'); + return request(app.callback()) + .get('/protocol') + .expect('https'); + }); + }); + + describe('this.query[key] => String', () => { + it('should get string value', () => { + createRequest('a=b').query.should.eql({ a: 'b' }); + createRequest('a=&').query.should.eql({ a: '' }); + createRequest('a=b&').query.should.eql({ a: 'b' }); + createRequest('a.=b').query.should.eql({ 'a.': 'b' }); + createRequest('a=b&a=c').query.should.eql({ a: 'b' }); + createRequest('a=&a=c').query.should.eql({ a: '' }); + createRequest('a=c&a=b').query.should.eql({ a: 'c' }); + createRequest('a=c&a=b&b=bb').query.should.eql({ a: 'c', b: 'bb' }); + createRequest('a[=c&a[=b').query.should.eql({ 'a[': 'c' }); + createRequest('a{=c&a{=b').query.should.eql({ 'a{': 'c' }); + createRequest('a[]=c&a[]=b').query.should.eql({ 'a[]': 'c' }); + createRequest('a[]=&a[]=b').query.should.eql({ 'a[]': '' }); + createRequest('a[foo]=c').query.should.eql({ 'a[foo]': 'c' }); + createRequest('a[foo][bar]=c').query.should.eql({ 'a[foo][bar]': 'c' }); + createRequest('a=').query.should.eql({ a: '' }); + createRequest('a[]=a&a=b&a=c').query.should.eql({ 'a[]': 'a', a: 'b' }); + }); + + it('should get undefined when key not exists', () => { + const request = createRequest('a=b'); + request.query.should.eql({ a: 'b' }); + should.not.exist(request.query.foo); + }); + }); + + describe('this.queries[key] => Array', () => { + it('should get array value', () => { + createRequest('').queries.should.eql({ }); + createRequest('a=').queries.should.eql({ a: [ '' ] }); + createRequest('a=&').queries.should.eql({ a: [ '' ] }); + createRequest('a=b&').queries.should.eql({ a: [ 'b' ] }); + createRequest('a.=').queries.should.eql({ 'a.': [ '' ] }); + createRequest('a=&a=&a=&a=&a=&a=&a=&a=').queries.should.eql({ a: [ '', '', '', '', '', '', '', '' ] }); + createRequest('a=&a=&a=&a=&a=&a=&a=&a=&').queries.should.eql({ a: [ '', '', '', '', '', '', '', '' ] }); + createRequest('a=&a=&a=&a=&a=&a=&a=&a=&&&&').queries.should.eql({ a: [ '', '', '', '', '', '', '', '' ] }); + createRequest('a=b').queries.should.eql({ a: [ 'b' ] }); + createRequest('a={}').queries.should.eql({ a: [ '{}' ] }); + createRequest('a=[]').queries.should.eql({ a: [ '[]' ] }); + createRequest('a[]=[]').queries.should.eql({ 'a[]': [ '[]' ], a: [ '[]' ] }); + createRequest('a[]=&a[]=').queries.should.eql({ 'a[]': [ '', '' ], a: [ '', '' ] }); + createRequest('a[]=[]&a[]=[]').queries.should.eql({ 'a[]': [ '[]', '[]' ], a: [ '[]', '[]' ] }); + createRequest('a=b&a=c').queries.should.eql({ a: [ 'b', 'c' ] }); + createRequest('a=&a=c').queries.should.eql({ a: [ '', 'c' ] }); + createRequest('a=c&a=b').queries.should.eql({ a: [ 'c', 'b' ] }); + createRequest('a=c&a=b&b=bb').queries.should.eql({ a: [ 'c', 'b' ], b: [ 'bb' ] }); + createRequest('a[=c&a[=b').queries.should.eql({ 'a[': [ 'c', 'b' ] }); + createRequest('a{=c&a{=b').queries.should.eql({ 'a{': [ 'c', 'b' ] }); + createRequest('a[]=c&a[]=b').queries.should.eql({ 'a[]': [ 'c', 'b' ], a: [ 'c', 'b' ] }); + createRequest('a[]=&a[]=b').queries.should.eql({ 'a[]': [ '', 'b' ], a: [ '', 'b' ] }); + createRequest('a[]=&a[]=b&a=foo').queries.should.eql({ 'a[]': [ '', 'b' ], a: [ 'foo' ] }); + createRequest('a=bar&a[]=&a[]=b&a=foo').queries.should.eql({ 'a[]': [ '', 'b' ], a: [ 'bar', 'foo' ] }); + + // a[][] 这种不支持自动变化为 a + createRequest('a[][]=&a[][]=b').queries.should.eql({ 'a[][]': [ '', 'b' ] }); + createRequest('a][]=&a][]=b').queries.should.eql({ 'a][]': [ '', 'b' ] }); + createRequest('a[[]=&a[[]=b').queries.should.eql({ 'a[[]': [ '', 'b' ] }); + createRequest('[]=&[]=b').queries.should.eql({ '[]': [ '', 'b' ] }); + + // a[], a 混搭的时候,只返回最后一个 a 的值 + createRequest('a[]=a&a=b&a=c').queries.should.eql({ 'a[]': [ 'a' ], a: [ 'b', 'c' ] }); + + // object + createRequest('a[foo]=c').queries.should.eql({ 'a[foo]': [ 'c' ] }); + createRequest('a[foo]=c&a=b').queries.should.eql({ 'a[foo]': [ 'c' ], a: [ 'b' ] }); + createRequest('a[foo]=c&a=b&b=bb&d=d1&d=d2').queries.should.eql({ + 'a[foo]': [ 'c' ], + a: [ 'b' ], + b: [ 'bb' ], + d: [ 'd1', 'd2' ], + }); + createRequest('a[foo]=c&a[]=b&a[]=d').queries.should.eql({ + 'a[foo]': [ 'c' ], + 'a[]': [ 'b', 'd' ], + a: [ 'b', 'd' ], + }); + createRequest('a[foo]=c&a[]=b&a[]=d&c=cc&c=c2&c=').queries.should.eql({ + 'a[foo]': [ 'c' ], + 'a[]': [ 'b', 'd' ], + a: [ 'b', 'd' ], + c: [ 'cc', 'c2', '' ], + }); + createRequest('a[foo][bar]=c').queries.should.eql({ + 'a[foo][bar]': [ 'c' ], + }); + }); + + it('should get undefined when key not exists', () => { + const request = createRequest('a=b'); + request.queries.should.eql({ a: [ 'b' ] }); + should.not.exist(request.queries.foo); + }); + }); + + describe('this.query = obj', () => { + it('should set query with object', () => { + const req = createRequest('a=c'); + req.query.should.eql({ a: 'c' }); + req.query = {}; + req.query.should.eql({}); + req.querystring.should.equal(''); + + req.query = { foo: 'bar' }; + req.query.should.eql({ foo: 'bar' }); + req.querystring.should.equal('foo=bar'); + + req.query = { array: [ 1, 2 ] }; + req.query.should.eql({ array: '1' }); + req.querystring.should.equal('array=1&array=2'); + }); + }); + + describe('request.acceptJSON', () => { + it('should true when isAjax', function* () { + const req = yield utils.createRequest(); + mm(req.req.headers, 'x-requested-with', 'XMLHttpRequest'); + req.acceptJSON.should.equal(true); + }); + + it('should true when response is json', function* () { + const context = yield utils.createContext({ + headers: { + accept: 'text/html', + }, + url: '/', + }); + context.res._headers = { + 'content-type': 'json', + }; + context.request.acceptJSON.should.equal(true); + }); + + it('should true when accept json', function* () { + const context = yield utils.createContext({ + headers: { + accept: 'application/json', + }, + url: '/', + }); + context.request.acceptJSON.should.equal(true); + }); + + it('should false when do not accept json', function* () { + const request = yield utils.createRequest({ + headers: { + accept: 'text/html', + }, + url: '/', + }); + request.acceptJSON.should.equal(false); + }); + }); + + describe('work with egg app', () => { + let app; + let host; + before(done => { + app = utils.app('apps/querystring-extended'); + app.listen(0, function() { + host = `http://127.0.0.1:${this.address().port}`; + done(); + }); + }); + + it('should return query and queries', done => { + urllib.request(`${host}/?p=a,b&p=b,c&a[foo]=bar`, { + dataType: 'json', + }, (err, body) => { + body.should.eql({ + query: { p: 'a,b', 'a[foo]': 'bar' }, + queries: { p: [ 'a,b', 'b,c' ], 'a[foo]': [ 'bar' ] }, + }); + done(err); + }); + }); + + it('should work with encodeURIComponent', done => { + urllib.request(`${host}/?p=a,b&p=b,c&${encodeURIComponent('a[foo]')}=bar`, { + dataType: 'json', + }, (err, body) => { + body.should.eql({ + query: { p: 'a,b', 'a[foo]': 'bar' }, + queries: { p: [ 'a,b', 'b,c' ], 'a[foo]': [ 'bar' ] }, + }); + done(err); + }); + }); + }); + + function createRequest(querystring) { + const app = { + context: {}, + request: { + querystring, + }, + }; + merge(app.request, requestExt); + return app.request; + } +}); diff --git a/test/lib/core/app/middleware/body_parser.test.js b/test/lib/core/app/middleware/body_parser.test.js new file mode 100644 index 0000000000..eb955702c3 --- /dev/null +++ b/test/lib/core/app/middleware/body_parser.test.js @@ -0,0 +1,57 @@ +'use strict'; + +const querystring = require('querystring'); +const should = require('should'); +const request = require('supertest'); +const utils = require('../../../../utils'); + +describe('test/lib/core/app/middleware/body_parser.test.js', () => { + let app; + before(done => { + app = utils.app('apps/body_parser_testapp'); + const that = this; + + app.ready(() => { + request(app.callback()) + .get('/test/body_parser/user') + .expect(200, (err, res) => { + should.not.exist(err); + that.csrf = res.body.csrf || ''; + // that.cookies = res.headers['set-cookie'].join(';'); + // res.headers['set-cookie'].forEach(function(cookie) { + // const item = cookie.split(';')[0].trim().split('='); + // if (item[0] === 'ctoken') { + // that.ctoken = item[1]; + // } + // }); + // should.exist(that.csrf); + done(); + }); + }); + }); + + after(() => { + app.close(); + }); + + it('should 200 when post form body below the limit', done => { + request(app.callback()) + .post('/test/body_parser/user') + // .set('Cookie', this.cookies) + .set('Content-Type', 'application/x-www-form-urlencoded') + .set('Accept', 'application/json') + .send(querystring.stringify({ foo: 'bar', _csrf: this.csrf })) + .expect({ foo: 'bar', _csrf: this.csrf }) + .expect(200, done); + }); + + it('should 200 when post json body below the limit', done => { + request(app.callback()) + .post('/test/body_parser/user') + // .set('Cookie', this.cookies) + .set('Content-Type', 'application/json') + .send({ foo: 'bar', _csrf: this.csrf }) + .expect({ foo: 'bar', _csrf: this.csrf }) + .expect(200, done); + }); +}); diff --git a/test/lib/core/app/middleware/meta.test.js b/test/lib/core/app/middleware/meta.test.js new file mode 100644 index 0000000000..4a40e56f5e --- /dev/null +++ b/test/lib/core/app/middleware/meta.test.js @@ -0,0 +1,65 @@ +'use strict'; + +const should = require('should'); +const mm = require('egg-mock'); +const request = require('supertest-as-promised'); +const utils = require('../../../../utils'); + +describe('test/lib/core/app/middleware/meta.test.js', () => { + let app; + before(() => { + mm(process.env, 'HOSTNAME', 'appname-1-1'); + app = utils.app('apps/middlewares'); + return app.ready(); + }); + + after(() => app.close()); + + afterEach(mm.restore); + + it('should get X-Powered-By header', () => { + return request(app.callback()) + .get('/') + .expect('X-Powered-By', 'Egg') + .expect(200); + }); + + it('should hide X-Powered-By header', () => { + mm(app, 'poweredBy', false); + return request(app.callback()) + .get('/') + .expect(res => { + should.not.exist(res.headers['X-Powered-By']); + }) + .expect(200); + }); + + it('should get X-Server-Id header', () => { + return request(app.callback()) + .get('/') + .expect('X-Server-Id', '1-1') + .expect(200); + }); + + it('should hide X-Server-Id header', done => { + mm(process.env, 'HOSTNAME', ''); + const app = utils.app('apps/middlewares'); + request(app.callback()) + .get('/') + .expect(res => { + should.not.exist(res.headers['X-Server-Id']); + }) + .expect(200, err => { + app.close(); + should.not.exist(err); + done(); + }); + }); + + it('should get X-Readtime header', () => { + return request(app.callback()) + .get('/') + .expect('X-Readtime', /\d+/) + .expect(200); + }); +}); diff --git a/test/lib/core/app/middleware/notfound.test.js b/test/lib/core/app/middleware/notfound.test.js new file mode 100644 index 0000000000..2851e42dc9 --- /dev/null +++ b/test/lib/core/app/middleware/notfound.test.js @@ -0,0 +1,88 @@ +'use strict'; + +const pedding = require('pedding'); +const request = require('supertest'); +const mm = require('egg-mock'); +const utils = require('../../../../utils'); + +describe('test/lib/core/app/middleware/notfound.test.js', () => { + let app; + before(() => { + app = utils.app('apps/notfound'); + return app.ready(); + }); + after(() => app.close()); + + afterEach(mm.restore); + + it('should 302 redirect to 404.html on production env', () => { + mm(app.config.notfound, 'enableRedirect', true); + return request(app.callback()) + .get('/test/404') + .set('Accept', 'test/html') + .expect('Location', 'https://eggjs.org/404') + .expect(302); + }); + + it('should show 404 on dev env', () => { + return request(app.callback()) + .get('/test/404') + .expect('

    404 Not Found

    Because you are in a non-prod environment, you will be looking at this page, otherwise it will jump to https://eggjs.org/404') + .expect(404); + }); + + it('should 404 json response', () => { + return request(app.callback()) + .get('/test/404.json?ctoken=404') + .set('Cookie', 'ctoken=404') + .expect({ + message: 'Not Found', + }) + .expect(404); + }); + + it('should 404 json response on rest api', () => { + return request(app.callback()) + .get('/api/404.json?ctoken=404') + .set('Cookie', 'ctoken=404') + .expect({ + message: 'Not Found', + }) + .expect(404); + }); + + it('should show 404 page content when antx notfound.pageUrl not set', () => { + mm(app.config.notfound, 'pageUrl', ''); + return request(app.callback()) + .get('/foo') + .expect('

    404 Not Found

    ') + .expect(404); + }); + + describe('app.404.url=/404', () => { + let app; + before(() => { + app = utils.app('apps/notfound-custom-404'); + return app.ready(); + }); + after(() => app.close()); + + afterEach(mm.restore); + + it('should 302 redirect to custom /404 on production env', done => { + done = pedding(2, done); + mm(app.config.notfound, 'enableRedirect', true); + + request(app.callback()) + .get('/test/404') + .set('Accept', 'test/html') + .expect('Location', '/404') + .expect(302, done); + + request(app.callback()) + .get('/404') + .expect('Hi, this is 404') + .expect(200, done); + }); + }); +}); diff --git a/test/lib/core/app/middleware/override_method.test.js b/test/lib/core/app/middleware/override_method.test.js new file mode 100644 index 0000000000..304520253d --- /dev/null +++ b/test/lib/core/app/middleware/override_method.test.js @@ -0,0 +1,37 @@ +'use strict'; + +const request = require('supertest-as-promised'); +const utils = require('../../../../utils'); + +describe('test/lib/core/app/middleware/override_method.test.js', () => { + let app; + before(() => { + app = utils.app('apps/override_method'); + return app.ready(); + }); + after(() => app.close()); + + it('should put', () => { + return request(app.callback()) + .post('/test') + .send({ _method: 'PUT' }) + .expect(200) + .expect('test-put'); + }); + + it('should patch', () => { + return request(app.callback()) + .post('/test') + .send({ _method: 'PATCH' }) + .expect(200) + .expect('test-patch'); + }); + + it('should delete', () => { + return request(app.callback()) + .post('/test') + .send({ _method: 'DELETE' }) + .expect(200) + .expect('test-delete'); + }); +}); diff --git a/test/lib/core/app/middleware/security.test.js b/test/lib/core/app/middleware/security.test.js new file mode 100644 index 0000000000..408a106b0d --- /dev/null +++ b/test/lib/core/app/middleware/security.test.js @@ -0,0 +1,105 @@ +'use strict'; + +const request = require('supertest-as-promised'); +const utils = require('../../../../utils'); + +describe.skip('test/lib/core/app/middleware/security.test.js', () => { + describe('security.ctoken = false', function() { + it('should not check ctoken', function(done) { + const app = utils.app('apps/ctoken-disable'); + request(app.listen()) + .get('/api/user.json?name=suqian.yf') + .expect(200) + .expect({ + url: '/api/user.json?name=suqian.yf', + name: 'suqian.yf', + }, done); + }); + }); + + describe('security.ctoken = true', function() { + it('should check ctoken', function(done) { + const app = utils.app('apps/csrf-disable'); + request(app.listen()) + .get('/api/user.json?name=suqian.yf') + .set('accept', 'application/json') + .expect(403) + .expect({ + message: 'missing cookie ctoken', + }, done); + }); + }); + + describe('security.csrf = false', function() { + it('should not check csrf', function(done) { + const app = utils.app('apps/csrf-disable'); + request(app.listen()) + .post('/api/user') + .send({ name: 'suqian.yf' }) + .expect(200) + .expect({ + url: '/api/user', + name: 'suqian.yf', + }, done); + }); + }); + + describe('security.csrf = true', function() { + it('should check csrf', function(done) { + const app = utils.app('apps/ctoken-disable'); + request(app.listen()) + .post('/api/user') + .send({ name: 'suqian.yf' }) + .expect(403) + .expect('secret is missing', done); + }); + }); + + describe('security.csrfIgnore and ctokenIgnore', function() { + let app; + before(function() { + app = utils.app('apps/ctoken-ignore'); + }); + + it('should not check csrf on /api/*', function(done) { + request(app.listen()) + .post('/api/user') + .send({ name: 'suqian.yf' }) + .expect(200) + .expect({ + url: '/api/user', + name: 'suqian.yf', + }, done); + }); + + it('should not check ctoken on /api/*', function(done) { + request(app.listen()) + .post('/api/user.json') + .send({ name: 'suqian.yf' }) + .expect(200) + .expect({ + url: '/api/user.json', + name: 'suqian.yf', + }, done); + }); + + it('should check ctoken on other', function(done) { + request(app.listen()) + .post('/apiuser.json') + .set('accept', 'application/json') + .send({ name: 'suqian.yf' }) + .expect({ + message: 'missing cookie ctoken', + }) + .expect(403, done); + }); + + it('should check csrf on other', function(done) { + request(app.listen()) + .post('/apiuser') + .send({ name: 'suqian.yf' }) + .expect('secret is missing') + .expect(403, done); + }); + }); +}); diff --git a/test/lib/core/app/middleware/site_file.test.js b/test/lib/core/app/middleware/site_file.test.js new file mode 100644 index 0000000000..d3cb1837bd --- /dev/null +++ b/test/lib/core/app/middleware/site_file.test.js @@ -0,0 +1,98 @@ +'use strict'; + +const should = require('should'); +const request = require('supertest-as-promised'); +const utils = require('../../../../utils'); + +describe('test/lib/core/app/middleware/site_file.test.js', () => { + let app; + before(() => { + app = utils.app('apps/middlewares'); + return app.ready(); + }); + + after(() => app.close()); + + it('should GET /favicon.ico 200', () => { + return request(app.callback()) + .get('/favicon.ico') + .expect('Content-Type', 'image/x-icon') + // .expect(res => console.log(res.headers)) + .expect(200); + }); + + it('should GET /favicon.ico?t=123 200', () => { + return request(app.callback()) + .get('/favicon.ico?t=123') + .expect('Content-Type', 'image/x-icon') + // .expect(res => console.log(res.headers)) + .expect(200); + }); + + it('should 200 when accessing /robots.txt', () => { + return request(app.callback()) + .get('/robots.txt') + .expect('User-agent: Baiduspider\nDisallow: /\n\nUser-agent: baiduspider\nDisallow: /') + .expect(200); + }); + + it('should 200 when accessing crossdomain.xml', () => { + return request(app.callback()) + .get('/crossdomain.xml') + .expect('xxx') + .expect(200); + }); + + it('should support HEAD', () => { + return request(app.callback()) + .head('/robots.txt') + .expect('content-length', 72) + .expect('') // body must be empty for HEAD + .expect(200); + }); + + it('should ignore POST', () => { + return request(app.callback()) + .post('/robots.txt') + .expect(404); + }); + + it('normal router should work', () => { + return request(app.callback()) + .get('/') + .expect('home') + .expect(200); + }); + + it('not defined router should 404', () => { + return request(app.callback()) + .get('/xxx') + .expect(404); + }); + + it('should 404 when accessing fake.txt using wrong config', () => { + return request(app.callback()) + .get('/fake.txt') + .expect(404); + }); + + describe('custom favicon', () => { + let app; + before(() => { + app = utils.app('apps/favicon'); + return app.ready(); + }); + + after(() => app.close()); + + it('should redirect https://eggjs.org/favicon.ico', () => { + return request(app.callback()) + .get('/favicon.ico') + .expect(302, (err, res) => { + should.not.exist(err); + should.not.exist(res.headers['set-cookie']); + res.headers.location.should.eql('https://eggjs.org/favicon.ico'); + }); + }); + }); +}); diff --git a/test/lib/core/app_worker_client.test.js b/test/lib/core/app_worker_client.test.js new file mode 100644 index 0000000000..de5e9e9a75 --- /dev/null +++ b/test/lib/core/app_worker_client.test.js @@ -0,0 +1,204 @@ +'use strict'; + +const should = require('should'); +const mm = require('egg-mock'); +const pedding = require('pedding'); +const utils = require('../../utils'); + +describe('test/lib/core/app_worker_client.test.js', () => { + + let app; + let client; + + before(done => { + app = utils.app('apps/demo'); + const impl = { + subscribe(reg, listener) { + this._subscribe(reg, listener); + return this; + }, + + unSubscribe(reg, listener) { + return this._unSubscribe(reg, listener); + }, + + * getData(id) { + return yield this._invoke('getData', [ id ]); + }, + + sendOneway(data) { + this._invokeOneway('sendOneway', [ data ]); + }, + }; + + client = app.createAppWorkerClient('mock', impl, { + responseTimeout: 3000, + force: true, + }); + + client._on('xxx', () => {}); + client._once('yyy', () => {}); + client._removeListener('xxx', () => {}); + client._removeAllListeners('xxx'); + client._removeAllListeners('yyy'); + + client.ready(done); + }); + + afterEach(mm.restore); + + it('should work', () => { + mm(client, '_opaque', Math.pow(2, 31) - 10); + + client.publicEvents.should.eql([ 'agent_restart', 'error' ]); + client._getNextOpaque().should.equal(0); + }); + + it('should subscribe data well', done => { + done = pedding(done, 2); + const info = { + dataId: 'mockId', + groupId: 'mockGroup', + }; + + client.subscribe(info, function(value) { + value.should.equal('mock data'); + done(); + }); + + client.messenger.emit('mock_subscribe_changed', { + key: JSON.stringify(info), + info, + value: 'mock data', + }); + + client.unSubscribe(info, () => {}); + client.unSubscribe(info); + + client._subscriptions.has(JSON.stringify(info)).should.equal(false); + + client.messenger.emit('mock_subscribe_changed', { + key: JSON.stringify(info), + info, + value: 'mock data2', + }); + + setTimeout(() => done(), 500); + }); + + it('should subscribe string well', done => { + const info = 'mock-info'; + + client.subscribe(info, function(value) { + value.should.equal('mock data'); + done(); + }); + + client.messenger.emit('mock_subscribe_changed', { + key: JSON.stringify(info), + info, + value: 'mock data', + }); + }); + + it('should invoke API well', function* () { + mm(client, '_opaque', 1); + + setTimeout(() => { + client.messenger.emit('mock_invoke_response', { + opaque: 1, + success: true, + data: 'mock data', + }); + }, 100); + + const result = yield client.getData('123'); + result.should.equal('mock data'); + }); + + it('should invoke API well with wrong opaque', function* () { + let warned = false; + mm(client, '_opaque', 10); + const logger = client.logger; + mm(logger, 'warn', (msg, name, data) => { + if (data === 'mock data') { + warned = true; + } + }); + + setTimeout(() => { + client.messenger.emit('mock_invoke_response', { + opaque: 1, + success: true, + data: 'mock data', + }); + }, 100); + + try { + yield client.getData('123'); + } catch (err) { + // do noting + } + should(warned).equal(true); + }); + + it('should invoke oneway ok', done => { + client._invoke('sendOneway', [ '123' ], { + oneway: true, + }).then(done); + }); + + it('should call sendOneway ok', done => { + mm(client, '_opaque', 1); + mm(client, '_sendToAgent', (cmd, data) => { + cmd.should.equal('mock_invoke_request'); + const pid = client.pid; + data.should.eql({ + opaque: 1, + method: 'sendOneway', + args: [ '123' ], + pid, + oneway: true, + }); + done(); + }); + client.sendOneway('123'); + }); + + it('should invoke API with error', function* () { + mm(client, '_opaque', 1); + + setTimeout(() => { + client.messenger.emit('mock_invoke_response', { + opaque: 1, + success: false, + errorMessage: 'mock error', + }); + }, 100); + + try { + yield client.getData('123'); + throw new Error('should not run here'); + } catch (err) { + err.message.should.equal('mock error'); + } + }); + + it('should throw timeout error', function* () { + mm(client, '_opaque', 1); + try { + yield client.getData('123'); + + throw new Error('should not run here'); + } catch (err) { + err.name.should.equal('AgentWorkerRequestTimeoutError'); + err.message.should.equal('Agent worker no response in 3000ms, AppWorkerClient:mock invoke getData with req#1'); + } + }); + + it('should emit agent_restart when agent worker restart', done => { + client.on('agent_restart', done); + client.messenger.emit('agent-start'); + }); + +}); diff --git a/test/lib/core/config/config.test.js b/test/lib/core/config/config.test.js new file mode 100644 index 0000000000..91eb27cd85 --- /dev/null +++ b/test/lib/core/config/config.test.js @@ -0,0 +1,19 @@ +'use strict'; + +const mm = require('egg-mock'); +const utils = require('../../../utils'); + +describe('test/lib/core/config/config.test.js', () => { + let app; + before(() => { + app = utils.app('apps/demo'); + return app.ready(); + }); + after(() => app.close()); + + afterEach(mm.restore); + + it('should return config.core.name that is deprecated', () => { + app.config.core.name.should.equal('Egg'); + }); +}); diff --git a/test/lib/core/cookies.test.js b/test/lib/core/cookies.test.js new file mode 100644 index 0000000000..e52e782713 --- /dev/null +++ b/test/lib/core/cookies.test.js @@ -0,0 +1,249 @@ +'use strict'; + +const should = require('should'); +const mm = require('egg-mock'); +const request = require('supertest-as-promised'); +const utils = require('../../utils'); + +describe('test/lib/core/cookies.test.js', () => { + afterEach(mm.restore); + + describe('secure = true', () => { + let app; + before(() => { + app = utils.app('apps/secure-app'); + return app.ready(); + }); + + after(() => app.close()); + + it('should throw TypeError when set secure on not secure request', () => { + const ctx = app.mockContext(); + (function() { + ctx.setCookie('foo', 'bar', { secure: true }); + }).should.throw('Cannot send secure cookie over unencrypted connection'); + }); + + it('should set cookie twice and not set domain when ctx.hostname=localhost', () => { + const ctx = app.mockContext(); + ctx.set('Set-Cookie', 'foo=bar'); + ctx.setCookie('foo1', 'bar1'); + ctx.response.get('set-cookie').should.eql([ + 'foo=bar', + 'foo1=bar1; path=/; httponly', + ]); + }); + + it('should throw TypeError when set encrypt on keys not exists', () => { + mm(app, 'keys', null); + const ctx = app.mockContext(); + (function() { + ctx.setCookie('foo', 'bar', { + encrypt: true, + }); + }).should.throw('.keys required for encrypt cookies'); + }); + + it('should throw TypeError when get encrypt on keys not exists', () => { + mm(app, 'keys', null); + const ctx = app.mockContext(); + ctx.header.cookie = 'foo=bar'; + (function() { + ctx.getCookie('foo', { + encrypt: true, + }); + }).should.throw('.keys required for encrypt cookies'); + }); + + it.skip('should not set secure when request protocol is http', done => { + request(app.callback()) + .get('/') + .set('Host', 'demo.eggjs.org') + .set('X-Forwarded-Proto', 'http') + .expect('hello mock secure app') + .expect(200, (err, res) => { + should.not.exist(err); + const cookie = res.headers['set-cookie'][0]; + should.exist(cookie); + cookie.should.match(/^ctoken=\w+secure\-app; path=\/; domain=\.eggjs\.org$/); + done(); + }); + }); + + it.skip('should set secure:true and httponly cookie', done => { + request(app.callback()) + .get('/') + .set('Host', 'demo.eggjs.org') + .set('X-Forwarded-Proto', 'https') + .expect('hello mock secure app') + .expect(200, (err, res) => { + should.not.exist(err); + const cookie = res.headers['set-cookie'][0]; + should.exist(cookie); + cookie.should.match(/^ctoken=\w+secure\-app; path=\/; domain=\.eggjs\.org; secure$/); + done(); + }); + }); + + it('should set cookie with path: /cookiepath/ok', done => { + request(app.callback()) + .get('/?cookiepath=/cookiepath/ok') + .set('Host', 'demo.eggjs.org') + .set('X-Forwarded-Proto', 'https') + .expect('hello mock secure app') + .expect(200, (err, res) => { + should.not.exist(err); + const cookie = res.headers['set-cookie'][0]; + should.exist(cookie); + cookie.should.match(/^cookiepath=\/cookiepath\/ok; path=\/cookiepath\/ok; secure; httponly$/); + done(); + }); + }); + + it('should delete cookie', done => { + request(app.callback()) + .get('/?cookiedel=true') + .set('Host', 'demo.eggjs.org') + .set('Cookie', 'cookiedel=true') + .set('X-Forwarded-Proto', 'https') + .expect('hello mock secure app') + .expect(200, (err, res) => { + should.not.exist(err); + const cookie = res.headers['set-cookie'][0]; + should.exist(cookie); + cookie.should.equal('cookiedel=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; secure; httponly'); + const expires = cookie.match(/expires=(.*?);/)[1]; + (Date.now() > new Date(expires)).should.be.true; + done(); + }); + }); + + it('should delete cookie with options', done => { + request(app.callback()) + .get('/?cookiedel=true&opts=true') + .set('Host', 'demo.eggjs.org') + .set('Cookie', 'cookiedel=true; path=/hello; domain=eggjs.org; expires=30') + .set('X-Forwarded-Proto', 'https') + .expect('hello mock secure app') + .expect(200, (err, res) => { + const cookie = res.headers['set-cookie'][0]; + should.exist(cookie); + cookie.should.equal('cookiedel=; path=/hello; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=eggjs.org; secure; httponly'); + const expires = cookie.match(/expires=(.*?);/)[1]; + (Date.now() > new Date(expires)).should.be.true; + done(); + }); + }); + + it('should set cookie with domain: okcookie.eggjs.org', done => { + request(app.callback()) + .get('/?cookiedomain=okcookie.eggjs.org&cookiepath=/') + .set('Host', 'demo.eggjs.org') + .set('X-Forwarded-Proto', 'https') + .expect('hello mock secure app') + .expect(200, (err, res) => { + should.not.exist(err); + const cookie = res.headers['set-cookie'][0]; + should.exist(cookie); + cookie.should.equal('cookiepath=/; path=/; domain=okcookie.eggjs.org; secure; httponly'); + done(); + }); + }); + + it('should not set domain and path', done => { + request(app.callback()) + .get('/?notSetPath=okok') + .set('Host', 'demo.eggjs.org') + .set('X-Forwarded-Proto', 'https') + .expect('hello mock secure app') + .expect(200, (err, res) => { + should.not.exist(err); + const cookie = res.headers['set-cookie'][0]; + should.exist(cookie); + cookie.should.equal('notSetPath=okok; secure; httponly'); + done(); + }); + }); + }); + + describe('secure = false', () => { + it('should set secure:false cookie', done => { + const app = utils.app('apps/demo'); + request(app.callback()) + .get('/hello') + .set('Host', 'demo.eggjs.org') + .expect('hello') + .expect(200, (err, res) => { + should.not.exist(err); + const cookie = res.headers['set-cookie'][0]; + should.exist(cookie); + cookie.should.match('hi=foo; path=/; httponly'); + done(); + }); + }); + }); + + describe('encrypt = true', () => { + let app; + + before(() => { + app = utils.app('apps/encrypt-cookies'); + return app.ready(); + }); + + after(() => app.close()); + + it('should get encrypt cookie', done => { + request(app.callback()) + .get('/') + .expect({ + set: 'bar 中文', + }) + .expect(200, (err, res) => { + should.not.exist(err); + const encryptCookie = res.headers['set-cookie'][0]; + should.exist(encryptCookie); + encryptCookie.should.equal('foo=B9om8kiaZ7Xg9dzTUoH-Pw==; path=/; httponly'); + + const plainCookie = res.headers['set-cookie'][1]; + should.exist(plainCookie); + plainCookie.should.equal('plain=text ok; path=/; httponly'); + + request(app.callback()) + .get('/') + .set('Cookie', res.headers['set-cookie'].join(';')) + .expect({ + set: 'bar 中文', + encrypt: 'bar 中文', + encryptWrong: 'B9om8kiaZ7Xg9dzTUoH-Pw==', + plain: 'text ok', + }) + .expect(200, done); + }); + }); + + it('should decode encrypt value fail', done => { + request(app.callback()) + .get('/') + .expect({ + set: 'bar 中文', + }) + .expect(200, (err, res) => { + should.not.exist(err); + const encryptCookie = res.headers['set-cookie'][0]; + should.exist(encryptCookie); + encryptCookie.should.equal('foo=B9om8kiaZ7Xg9dzTUoH-Pw==; path=/; httponly'); + + request(app.callback()) + .get('/') + .set('Cookie', 'foo=123123; plain=text ok') + .expect({ + set: 'bar 中文', + encryptWrong: '123123', + plain: 'text ok', + }) + .expect(200, done); + }); + }); + }); +}); diff --git a/test/lib/core/loader/config_loader.test.js b/test/lib/core/loader/config_loader.test.js new file mode 100644 index 0000000000..e84ce746be --- /dev/null +++ b/test/lib/core/loader/config_loader.test.js @@ -0,0 +1,26 @@ +'use strict'; + +const utils = require('../../../utils'); +const AppWorkerLoader = require('../../../../').AppWorkerLoader; + +function createLoader(baseDir) { + baseDir = utils.getFilepath(baseDir); + const loader = new AppWorkerLoader({ + baseDir, + }); + loader.loadConfig(); + return loader; +} + +describe('test/lib/core/loader/config_loader.test.js', () => { + it('should get middlewares', () => { + const appLoader = createLoader('apps/demo'); + appLoader.config.coreMiddleware.should.eql([ + 'meta', + 'siteFile', + 'notfound', + 'bodyParser', + 'overrideMethod', + ]); + }); +}); diff --git a/test/lib/core/loader/load_app.test.js b/test/lib/core/loader/load_app.test.js new file mode 100644 index 0000000000..84494bd860 --- /dev/null +++ b/test/lib/core/loader/load_app.test.js @@ -0,0 +1,28 @@ +'use strict'; + +const should = require('should'); +const utils = require('../../../utils'); + +describe('test/lib/core/loader/load_app.test.js', () => { + let app; + before(() => { + app = utils.app('apps/loader-plugin'); + return app.ready(); + }); + after(() => app.close()); + + it('should load app.js', () => { + app.b.should.equal('plugin b'); + app.c.should.equal('plugin c'); + app.app.should.equal('app'); + }); + + it('should load plugin app.js first', () => { + (app.dateB <= app.date).should.equal(true); + (app.dateC <= app.date).should.equal(true); + }); + + it('should not load disable plugin', () => { + should.not.exists(app.a); + }); +}); diff --git a/test/lib/core/loader/load_plugin.test.js b/test/lib/core/loader/load_plugin.test.js new file mode 100644 index 0000000000..6d9aad1b0e --- /dev/null +++ b/test/lib/core/loader/load_plugin.test.js @@ -0,0 +1,250 @@ +'use strict'; + +const should = require('should'); +const path = require('path'); +const mm = require('egg-mock'); +const AppWorkerLoader = require('../../../../').AppWorkerLoader; +const utils = require('../../../utils'); + +const EGG_BASE = path.join(__dirname, '../../../../'); + +describe('test/lib/core/loader/load_plugin.test.js', () => { + + afterEach(mm.restore); + + it('should loadConfig all plugins', () => { + const baseDir = utils.getFilepath('apps/loader-plugin'); + const appLoader = new AppWorkerLoader({ + baseDir, + }); + appLoader.loadConfig(); + appLoader.plugins.b.should.eql({ + enable: true, + name: 'b', + dep: [], + env: [], + path: path.join(baseDir, 'node_modules/b'), + }); + appLoader.plugins.c.should.eql({ + enable: true, + name: 'c', + dep: [], + env: [], + path: path.join(baseDir, 'node_modules/c'), + }); + appLoader.plugins.e.should.eql({ + enable: true, + name: 'e', + dep: [ 'f' ], + env: [], + path: path.join(baseDir, 'plugins/e'), + }); + appLoader.plugins.onerror.path.should.equal(path.join(EGG_BASE, 'node_modules/egg-onerror')); + appLoader.plugins.onerror.package.should.equal('egg-onerror'); + appLoader.plugins.onerror.version.should.match(/\d+\.\d+\.\d+/); + appLoader.orderPlugins.should.be.an.Array; + }); + + it('should same name plugin level follow: app > framework > egg', () => { + const baseDir = utils.getFilepath('apps/loader-plugin'); + const appLoader = new AppWorkerLoader({ + baseDir, + }); + appLoader.loadConfig(); + + appLoader.plugins.rds.should.eql({ + enable: true, + name: 'rds', + dep: [ 'session' ], + env: [], + package: 'rds', + path: path.join(baseDir, 'node_modules/rds'), + }); + }); + + it('should plguin support alias name', () => { + const baseDir = utils.getFilepath('apps/loader-plugin'); + const appLoader = new AppWorkerLoader({ + baseDir, + }); + appLoader.loadConfig(); + appLoader.plugins.d1.should.eql({ + enable: true, + name: 'd1', + package: 'd', + dep: [], + env: [], + path: path.join(baseDir, 'node_modules/d'), + }); + should.not.exists(appLoader.plugins.d); + }); + + it('should support package.json config', () => { + const baseDir = utils.getFilepath('apps/loader-plugin'); + const appLoader = new AppWorkerLoader({ + baseDir, + }); + appLoader.loadConfig(); + appLoader.plugins.g.should.eql({ + enable: true, + name: 'g', + dep: [ 'f' ], + env: [], + path: path.join(baseDir, 'plugins/g'), + }); + }); + + it('should show warning message when plugin name wrong', () => { + let message; + mm(console, 'warn', m => { + if (!m.startsWith('[egg:loader] eggPlugin is missing') && !message) { + message = m; + } + }); + const baseDir = utils.getFilepath('apps/loader-plugin'); + const appLoader = new AppWorkerLoader({ + baseDir, + }); + appLoader.loadConfig(); + + message.should.eql('[egg:loader] pluginName(e) is different from pluginConfigName(wrong-name)'); + }); + + it('should loadConfig plugins with custom plugins config', () => { + const baseDir = utils.getFilepath('apps/loader-plugin'); + const plugins = { + foo: { + enable: true, + path: path.join(baseDir, 'node_modules/d'), + }, + d1: { + env: [ 'unittest' ], + }, + }; + const appLoader = new AppWorkerLoader({ + baseDir, + plugins, + }); + appLoader.loadConfig(); + + appLoader.plugins.d1.should.eql({ + enable: true, + name: 'd1', + package: 'd', + dep: [], + env: [ 'unittest' ], + path: path.join(baseDir, 'node_modules/d'), + }); + appLoader.plugins.foo.should.eql({ + enable: true, + name: 'foo', + dep: [], + env: [], + path: path.join(baseDir, 'node_modules/d'), + }); + should.not.exists(appLoader.plugins.d); + }); + + it('should throw error when plugin not exists', () => { + (function() { + const baseDir = utils.getFilepath('apps/loader-plugin-noexist'); + const appLoader = new AppWorkerLoader({ + baseDir, + }); + appLoader.loadConfig(); + }).should.throw(/Can not find plugin noexist in /); + }); + + it('should throw error when app baseDir not exists', () => { + (function() { + const baseDir = utils.getFilepath('apps/notexist-app'); + const appLoader = new AppWorkerLoader({ + baseDir, + }); + appLoader.loadConfig(); + }).should.throw(/notexist\-app not exists/); + }); + + it('should keep plugin list sorted', () => { + mm(process.env, 'NODE_ENV', 'development'); + const baseDir = utils.getFilepath('apps/loader-plugin-dep'); + const appLoader = new AppWorkerLoader({ + baseDir, + }); + appLoader.loadConfig(); + appLoader.orderPlugins.map(plugin => { + return plugin.name; + }).should.eql([ + 'onerror', + // 'userservice', + 'userrole', + 'session', + // 'i18n', + 'validate', + // 'watcher', + // 'multipart', + // 'security', + // 'development', + // 'logrotater', + // 'schedule', + 'b', + 'c1', + 'f', + 'a', + 'd', + 'e', + ]); + }); + + it('should throw recursive deps error', () => { + (function() { + const baseDir = utils.getFilepath('apps/loader-plugin-dep-recursive'); + const appLoader = new AppWorkerLoader({ + baseDir, + }); + appLoader.loadConfig(); + }).should.throw('sequencify plugins has problem, missing: [], recursive: [a,b,c,a]'); + }); + + it('should throw error when plugin dep not exists', function() { + (function() { + const baseDir = utils.getFilepath('apps/loader-plugin-dep-missing'); + const appLoader = new AppWorkerLoader({ + baseDir, + }); + appLoader.loadConfig(); + }).should.throw('sequencify plugins has problem, missing: [a1], recursive: []\n\t>> Plugin [a1] is disabled or missed, but is required by [c]'); + }); + + it('should auto fill plugin infos', () => { + mm(process.env, 'NODE_ENV', 'test'); + const baseDir = utils.getFilepath('apps/loader-plugin'); + const appLoader1 = new AppWorkerLoader({ + baseDir, + }); + appLoader1.loadConfig(); + // unittest disable + const keys1 = appLoader1.orderPlugins.map(plugin => { + return plugin.name; + }).join(','); + keys1.should.containEql('b,c,d1,f,e'); + should.not.exist(appLoader1.plugins.a1); + + mm(process.env, 'NODE_ENV', 'development'); + const appLoader2 = new AppWorkerLoader({ + baseDir, + }); + appLoader2.loadConfig(); + const keys2 = appLoader2.orderPlugins.map(plugin => { + return plugin.name; + }).join(','); + keys2.should.containEql('d1,a1,b,c,f,e'); + appLoader2.plugins.a1.should.eql({ + enable: true, + name: 'a1', + dep: [ 'd1' ], + env: [ 'local', 'prod' ], + path: path.join(baseDir, 'node_modules/a1'), + }); + }); +}); diff --git a/test/lib/core/loader/load_router.test.js b/test/lib/core/loader/load_router.test.js new file mode 100644 index 0000000000..f5196a8a1e --- /dev/null +++ b/test/lib/core/loader/load_router.test.js @@ -0,0 +1,27 @@ +'use strict'; + +const request = require('supertest-as-promised'); +const pedding = require('pedding'); +const utils = require('../../../utils'); + +describe('test/lib/core/loader/load_router.test.js', () => { + let app; + before(() => { + app = utils.app('apps/app-router'); + return app.ready(); + }); + after(() => app.close()); + + it('should load app/router.js', done => { + done = pedding(2, done); + request(app.callback()) + .get('/') + .expect(200) + .expect('hello', done); + + request(app.callback()) + .get('/home') + .expect(200) + .expect('hello', done); + }); +}); diff --git a/test/lib/core/loader/load_service.test.js b/test/lib/core/loader/load_service.test.js new file mode 100644 index 0000000000..5ab125f673 --- /dev/null +++ b/test/lib/core/loader/load_service.test.js @@ -0,0 +1,108 @@ +'use strict'; + +const should = require('should'); +const request = require('supertest'); +const mm = require('egg-mock'); +const utils = require('../../../utils'); + +describe('test/lib/core/loader/load_service.test.js', () => { + afterEach(mm.restore); + + it('should load app and plugin services', done => { + const app = utils.app('apps/loader-plugin'); + app.ready(() => { + should.exists(app.serviceClasses.foo); + should.exists(app.serviceClasses.foo2); + should.not.exists(app.serviceClasses.bar1); + should.exists(app.serviceClasses.bar2); + should.exists(app.serviceClasses.foo4); + + const ctx = app.mockContext(); + should.exists(ctx.service.fooDir.foo5); + should.exists(ctx.service.foo); + should.exists(ctx.service.foo2); + should.exists(ctx.service.bar2); + should.exists(ctx.service.foo4); + + request(app.callback()) + .get('/') + .expect({ + foo2: 'foo2', + foo3: 'foo3', + }) + .expect(200, err => { + app.close(); + done(err); + }); + }); + }); + + it('should service support es6', function* () { + const app = utils.app('apps/services_loader_verify'); + yield app.ready(); + app.serviceClasses.should.have.property('foo'); + app.serviceClasses.foo.should.have.properties('bar', 'bar1', 'aa'); + app.close(); + }); + + it('should support extend app.Service class', done => { + const app = utils.app('apps/service-app'); + app.ready(() => { + request(app.callback()) + .get('/user') + .expect(res => { + should.exists(res.body.user); + res.body.user.userId.should.equal('123mock'); + }) + .expect(200, err => { + app.close(); + done(err); + }); + }); + }); + + describe('sub dir', () => { + it('should support top 1 and 2 dirs, ignore others', done => { + mm(process.env, 'NO_DEPRECATION', '*'); + const app = utils.app('apps/subdir-services'); + request(app.callback()) + .get('/') + .expect({ + user: { + uid: '123', + }, + cif: { + uid: '123cif', + cif: true, + }, + bar1: { + name: 'bar1name', + bar: 'bar1', + }, + bar2: { + name: 'bar2name', + bar: 'bar2', + }, + 'foo.subdir2.sub2': { + name: 'bar3name', + bar: 'bar3', + }, + subdir11bar: false, + ok: { + ok: true, + }, + cmd: { + cmd: 'hihi', + method: 'GET', + url: '/', + }, + serviceIsSame: true, + oldStyle: '/', + }) + .expect(200, err => { + app.close(); + done(err); + }); + }); + }); +}); diff --git a/test/lib/core/logger.test.js b/test/lib/core/logger.test.js new file mode 100644 index 0000000000..bb1ad4188f --- /dev/null +++ b/test/lib/core/logger.test.js @@ -0,0 +1,267 @@ +'use strict'; + +const moment = require('moment'); +const should = require('should'); +const path = require('path'); +const fs = require('fs'); +const mm = require('egg-mock'); +const request = require('supertest'); +const Logger = require('egg-logger'); +const utils = require('../../utils'); +const Agent = require('../../..').Agent; + +describe('test/lib/core/logger.test.js', () => { + afterEach(mm.restore); + + it('should got right default config on prod env', () => { + mm.env('prod'); + mm(process.env, 'EGG_LOG', ''); + mm(process.env, 'HOME', utils.getFilepath('apps/mock-production-app/config')); + const app = utils.app('apps/mock-production-app'); + // 生产环境默认 _level = info + app.logger.get('file').options.level.should.equal(Logger.INFO); + // stdout 默认 INFO + app.logger.get('console').options.level.should.equal(Logger.INFO); + app.coreLogger.get('file').options.level.should.equal(Logger.INFO); + app.coreLogger.get('console').options.level.should.equal(Logger.INFO); + app.close(); + }); + + it('should got right level on local env', () => { + mm.env('local'); + mm(process.env, 'EGG_LOG', ''); + const app = utils.app('apps/mock-dev-app'); + + app.logger.get('file').options.level.should.equal(Logger.DEBUG); + app.logger.get('console').options.level.should.equal(Logger.INFO); + app.coreLogger.get('file').options.level.should.equal(Logger.DEBUG); + app.coreLogger.get('console').options.level.should.equal(Logger.WARN); + app.close(); + }); + + it('should set EGG_LOG level on local env', () => { + mm.env('local'); + mm(process.env, 'EGG_LOG', 'ERROR'); + const app = utils.app('apps/mock-dev-app'); + app.logger.get('file').options.level.should.equal(Logger.DEBUG); + app.logger.get('console').options.level.should.equal(Logger.ERROR); + app.coreLogger.get('file').options.level.should.equal(Logger.DEBUG); + app.coreLogger.get('console').options.level.should.equal(Logger.ERROR); + app.close(); + }); + + it('should got right config on unittest env', () => { + mm.env('unittest'); + mm(process.env, 'EGG_LOG', ''); + const app = utils.app('apps/mock-dev-app'); + app.logger.get('file').options.level.should.equal(Logger.INFO); + app.logger.get('console').options.level.should.equal(Logger.WARN); + app.coreLogger.get('file').options.level.should.equal(Logger.INFO); + app.coreLogger.get('console').options.level.should.equal(Logger.WARN); + app.close(); + }); + + it('should set log.consoleLevel to env.EGG_LOG', () => { + mm(process.env, 'EGG_LOG', 'ERROR'); + const app = utils.app('apps/mock-dev-app'); + app.logger.get('file').options.level.should.equal(Logger.INFO); + app.logger.get('console').options.level.should.equal(Logger.ERROR); + app.close(); + }); + + it('log buffer disable cache on local and unittest env', done => { + mm(process.env, 'EGG_LOG', 'NONE'); + const app = utils.app('apps/nobuffer-logger'); + app.ready(() => { + const ctx = app.mockContext(); + const logfile = path.join(app.config.logger.dir, 'common-error.log'); + // app.config.logger.buffer.should.equal(false); + ctx.logger.error(new Error('mock nobuffer error')); + setTimeout(() => { + app.close(); + fs.readFileSync(logfile, 'utf8').should.containEql('nodejs.Error: mock nobuffer error\n'); + done(); + }, 1000); + }); + }); + + it('log buffer enable cache on non-local and non-unittest env', done => { + mm(process.env, 'EGG_LOG', 'none'); + mm.env('prod'); + mm(process.env, 'HOME', utils.getFilepath('apps/mock-production-app/config')); + const app = utils.app('apps/mock-production-app'); + app.ready(() => { + const ctx = app.mockContext(); + const logfile = path.join(app.config.logger.dir, 'common-error.log'); + // app.config.logger.buffer.should.equal(true); + ctx.logger.error(new Error('mock enable buffer error')); + setTimeout(() => { + app.close(); + fs.readFileSync(logfile, 'utf8').should.containEql(''); + done(); + }, 1000); + }); + }); + + it('output .json format log', done => { + mm(process.env, 'EGG_LOG', 'none'); + mm.env('local'); + const app = utils.app('apps/logger-output-json'); + app.ready(() => { + const ctx = app.mockContext(); + const logfile = path.join(app.config.logger.dir, 'logger-output-json-web.json.log'); + ctx.logger.info('json format'); + setTimeout(() => { + app.close(); + fs.existsSync(logfile).should.be.true; + fs.readFileSync(logfile, 'utf8').should.containEql('"message":"json format"'); + done(); + }, 1000); + }); + }); + + it('dont output to console after app ready', done => { + mm.env('default'); + const app = utils.cluster('apps/logger'); + app + .debug(false) + .coverage(false) + .expect('stdout', /agent info/) + .expect('stdout', /app info/) + .notExpect('stdout', /app info after ready/) + .expect('stderr', /nodejs.Error: agent error/) + .expect('stderr', /nodejs.Error: app error/) + .end(err => { + app.close(); + should.not.exists(err); + done(); + }); + }); + + it('should still output to console after app ready on local env', done => { + mm.env('local'); + const app = utils.cluster('apps/logger'); + app + // .debug() + .coverage(false) + .expect('stdout', /agent info/) + .expect('stdout', /app info/) + .expect('stdout', /app info after ready/) + .expect('stderr', /nodejs.Error: agent error/) + .expect('stderr', /nodejs.Error: app error/) + .end(err => { + app.close(); + should.not.exists(err); + done(); + }); + }); + + it('agent and app error should output to common-error.log', done => { + const baseDir = utils.getFilepath('apps/logger'); + mm.env('default'); + mm(process.env, 'EGG_LOG', 'none'); + mm(process.env, 'HOME', baseDir); + const app = utils.cluster('apps/logger'); + app + // .debug() + .coverage(false) + .end(err => { + app.close(); + should.not.exists(err); + const content = fs.readFileSync(path.join(baseDir, 'logs/logger/common-error.log'), 'utf8'); + content.should.containEql('nodejs.Error: agent error'); + content.should.containEql('nodejs.Error: app error'); + done(); + }); + }); + + it('all loggers error should redirect to errorLogger', done => { + const app = utils.app('apps/logger'); + app.ready(() => { + app.logger.error(new Error('logger error')); + app.coreLogger.error(new Error('coreLogger error')); + app.loggers.errorLogger.error(new Error('errorLogger error')); + app.loggers.customLogger.error(new Error('customLogger error')); + + setTimeout(() => { + app.close(); + const content = fs.readFileSync(path.join(app.baseDir, 'logs/logger/common-error.log'), 'utf8'); + content.should.containEql('nodejs.Error: logger error'); + content.should.containEql('nodejs.Error: coreLogger error'); + content.should.containEql('nodejs.Error: errorLogger error'); + content.should.containEql('nodejs.Error: customLogger error'); + done(); + }, 10); + }); + }); + + it('agent\'s logger is same as coreLogger', done => { + const agent = new Agent({ + baseDir: utils.getFilepath('apps/logger'), + }); + agent.logger.options.file.should.equal(agent.coreLogger.options.file); + agent.ready(done); + }); + + describe.skip('logger.reload()', () => { + let app; + before(() => { + mm(process.env, 'EGG_LOG', 'none'); + app = utils.cluster('apps/logger-reload'); + return app.ready(); + }); + + after(() => app.close()); + + it('should reload worker loggers', done => { + request(app.callback()) + .get('/') + .expect({ + method: 'GET', + path: '/', + }) + .expect(200, err => { + should.not.exist(err); + app.process.send({ + to: 'agent', + action: 'test-reload-logger', + }); + setTimeout(() => { + const logname = moment().subtract(1, 'days').format('.YYYY-MM-DD'); + const logfile1 = utils.getFilepath('apps/logger-reload/logs/logger-reload/logger-reload-web.log'); + const content1 = fs.readFileSync(logfile1, 'utf8'); + content1.should.equal(''); + + const logfile2 = utils.getFilepath(`apps/logger-reload/logs/logger-reload/logger-reload-web.log${logname}`); + const content2 = fs.readFileSync(logfile2, 'utf8'); + content2.should.containEql('GET /'); + + const logfile3 = utils.getFilepath(`apps/logger-reload/logs/logger-reload/egg-agent.log${logname}`); + fs.existsSync(logfile3).should.be.true; + done(); + }, 2000); + }); + }); + }); + + describe('logger.level = DEBUG', () => { + let app; + before(done => { + app = utils.app('apps/logger-level-debug'); + app.ready(done); + }); + after(() => app.close()); + + it('should save debug log to file', done => { + request(app.callback()) + .get('/') + .expect('ok') + .end(err => { + should.not.exist(err); + fs.readFileSync(path.join(app.config.baseDir, 'logs/foo/foo-web.log'), 'utf8') + .should.containEql(' DEBUG '); + done(); + }); + }); + }); +}); diff --git a/test/lib/core/messenger.test.js b/test/lib/core/messenger.test.js new file mode 100644 index 0000000000..17bf087468 --- /dev/null +++ b/test/lib/core/messenger.test.js @@ -0,0 +1,167 @@ +'use strict'; + +const mm = require('egg-mock'); +const utils = require('../../utils'); +const Messenger = require('../../../lib/core/messenger'); + +describe('test/lib/core/messenger.test.js', () => { + let messenger; + + before(() => { + messenger = new Messenger(); + }); + + afterEach(mm.restore); + + describe('send()', () => { + it('should send demo message', done => { + // mock not childprocess + mm(process, 'send', null); + messenger.send('messenger-test-demo', { foo: 'haha' }); + messenger.once('messenger-test-demo', data => { + data.should.eql({ foo: 'haha' }); + done(); + }); + }); + + it('should mock process.send exists', done => { + mm(process, 'connected', true); + mm(process, 'send', msg => { + msg.should.eql({ + action: 'message-test-send-demo', + data: { + foo: 'ok', + }, + }); + done(); + }); + + messenger.send('message-test-send-demo', { + foo: 'ok', + }); + }); + }); + + describe('on(action, data)', () => { + it('should listen an action event', done => { + messenger.on('messenger-test-on-event', data => { + data.should.eql({ + success: true, + }); + done(); + }); + + process.emit('message', {}); + + process.emit('message', null); + + process.emit('message', { + action: 'messenger-test-on-event', + data: { + success: true, + }, + }); + }); + }); + + describe('close()', () => { + it('should remove all listeners', () => { + const messenger = new Messenger(); + messenger.on('messenger-test-on-event', () => { + throw new Error('should never emitted'); + }); + + messenger.close(); + + process.emit('message', { + action: 'messenger-test-on-event', + data: { + success: true, + }, + }); + }); + }); + + describe('cluster messenger', () => { + let app; + before(done => { + app = utils.cluster('apps/messenger'); + app.coverage(false); + // 等 agent 接受消息 + app.ready(() => setTimeout(done, 1000)); + }); + after(() => app.close()); + + it('app should accept agent message', () => { + app.expect('stdout', /\[app\] agent-to-app agent msg/); + }); + it('app should accept agent assgin pid message', () => { + app.expect('stdout', /\[app\] agent-to-app agent msg \d+/); + }); + it('app should accept itself message', () => { + app.expect('stdout', /\[app\] app-to-agent app msg/); + }); + it('agent should accept app message', () => { + app.expect('stdout', /\[agent\] app-to-agent app msg/); + }); + it('agent should accept itself message', () => { + app.expect('stdout', /\[agent\] agent-to-app agent msg/); + }); + it('agent should accept itself assgin pid message', () => { + app.expect('stdout', /\[agent\] agent-to-app agent msg \d+/); + }); + }); + + describe('sendRandom', () => { + let app; + before(() => { + mm.env('default'); + mm.consoleLevel('NONE'); + app = utils.cluster('apps/messenger-random', { workers: 4 }); + app.coverage(false); + return app.ready(); + }); + after(() => app.close()); + + it('app should accept agent message', done => { + setTimeout(() => { + const m = app.stdout.match(/\d+=\d+/g); + const map = new Map(); + for (const item of m) { + const a = item.split('='); + map.set(a[0], a[1]); + } + map.size.should.equal(4); + done(); + }, 8000); + }); + }); + + describe('sendToApp and sentToAgent', () => { + let app; + before(() => { + mm.env('default'); + mm.consoleLevel('NONE'); + app = utils.cluster('apps/messenger-app-agent', { workers: 2 }); + app.coverage(false); + return app.ready(); + }); + after(() => app.close()); + + it('app should accept agent message', done => { + setTimeout(() => { + count(app.stdout, 'agent2app').should.containEql(2); + count(app.stdout, 'app2app').should.containEql(4); + count(app.stdout, 'agent2agent').should.containEql(1); + count(app.stdout, 'app2agent').should.containEql(2); + done(); + }, 500); + + function count(data, key) { + return data.split('\n').filter(line => { + return line.indexOf(key) >= 0; + }).length; + } + }); + }); +}); diff --git a/test/lib/core/router.test.js b/test/lib/core/router.test.js new file mode 100644 index 0000000000..fe50a594cc --- /dev/null +++ b/test/lib/core/router.test.js @@ -0,0 +1,157 @@ +'use strict'; + +const request = require('supertest-as-promised'); +const utils = require('../../utils'); + +describe('test/lib/core/router.test.js', () => { + let app; + before(() => { + app = utils.app('apps/router-app'); + return app.ready(); + }); + after(() => app.close()); + + describe('router.resources', () => { + describe('normal', () => { + it('should GET /posts', () => { + return request(app.callback()) + .get('/posts') + .expect(200) + .expect('index'); + }); + + it('should GET /posts/new', () => { + return request(app.callback()) + .get('/posts/new') + .expect(200) + .expect('new'); + }); + + it('should POST /posts', () => { + return request(app.callback()) + .post('/posts') + .expect(200) + .expect('create'); + }); + + it('should GET /posts/:id', () => { + return request(app.callback()) + .get('/posts/123') + .expect(200) + .expect('show - 123'); + }); + + it('should GET /posts/:id/edit', () => { + return request(app.callback()) + .get('/posts/123/edit') + .expect(200) + .expect('edit - 123'); + }); + + it('should PUT /posts/:id', () => { + return request(app.callback()) + .put('/posts/123') + .expect(200) + .expect('update - 123'); + }); + + it('should DELETE /posts/:id', () => { + return request(app.callback()) + .delete('/posts/123') + .expect(200) + .expect('destroy - 123'); + }); + }); + + describe('controller url', () => { + it('should GET /members', () => { + return request(app.callback()) + .get('/members') + .expect(200) + .expect('index'); + }); + + it('should GET /members/index', () => { + return request(app.callback()) + .get('/members/index') + .expect(200) + .expect('index'); + }); + + it('should GET /members/new', () => { + return request(app.callback()) + .get('/members/new') + .expect(200) + .expect('new'); + }); + + it('should GET /members/:id', () => { + return request(app.callback()) + .get('/members/1231') + .expect(200) + .expect('show - 1231'); + }); + + it('should POST /members', () => { + return request(app.callback()) + .post('/members') + .expect(404); + }); + + it('should PUT /members/:id', () => { + return request(app.callback()) + .put('/members/1231') + .expect(404); + }); + + it('should GET /POSTS', () => { + return request(app.callback()) + .get('/POSTS') + .expect(404); + }); + }); + }); + + describe('router.url', () => { + it('should work', () => { + app.router.url('posts').should.equal('/posts'); + app.router.url('members').should.equal('/members'); + app.router.url('post', { id: 1 }).should.equal('/posts/1'); + app.router.url('member', { id: 1 }).should.equal('/members/1'); + app.router.url('new_post').should.equal('/posts/new'); + app.router.url('new_member').should.equal('/members/new'); + app.router.url('edit_post', { id: 1 }).should.equal('/posts/1/edit'); + }); + + it('should work with unknow params', () => { + app.router.url('posts', { name: 'foo', page: 2 }).should.equal('/posts?name=foo&page=2'); + app.router.url('posts', { name: 'foo&?', page: 2 }).should.equal('/posts?name=foo%26%3F&page=2'); + app.router.url('edit_post', { id: 10, page: 2 }).should.equal('/posts/10/edit?page=2'); + app.router.url('edit_post', { i: 2, id: 10 }).should.equal('/posts/10/edit?i=2'); + app.router.url('edit_post', { id: 10, page: 2, tags: [ 'chair', 'develop' ] }) + .should.equal('/posts/10/edit?page=2&tags=chair&tags=develop'); + app.router.url('edit_post', { id: [ 10 ], page: [ 2 ], tags: [ 'chair', 'develop' ] }) + .should.equal('/posts/10/edit?page=2&tags=chair&tags=develop'); + app.router.url('edit_post', { id: [ 10, 11 ], page: [ 2 ], tags: [ 'chair', 'develop' ] }) + .should.equal('/posts/10/edit?page=2&tags=chair&tags=develop'); + }); + + it.skip('should have router var in view', () => { + return request(app.callback()) + .get('/locals/router') + .expect('posts: /posts'); + }); + }); + + describe('router.pathFor', () => { + it('should work', () => { + app.router.pathFor('posts').should.equal('/posts'); + }); + }); + + describe('router.method', () => { + it('router method include HEAD', () => { + app.router.methods.should.containEql('HEAD'); + }); + }); +}); diff --git a/test/lib/core/singleton.test.js b/test/lib/core/singleton.test.js new file mode 100644 index 0000000000..5fefc8ee8c --- /dev/null +++ b/test/lib/core/singleton.test.js @@ -0,0 +1,138 @@ +'use strict'; + +const Singleton = require('../../../lib/core/singleton'); + +class DataService { + constructor(config) { + this.config = config; + } + + * query() { + return {}; + } +} + +function create(config) { + return new DataService(config); +} + +describe('test/lib/core/singleton.test.js', () => { + it('should init with client', () => { + const app = { + config: { + dataService: { + client: { foo: 'bar' }, + }, + }, + }; + const name = 'dataService'; + + const singleton = new Singleton({ + name, + app, + create, + }); + singleton.init(); + (app.dataService instanceof DataService).should.be.ok; + app.dataService.config.foo.should.equal('bar'); + (typeof app.dataService.createInstance).should.equal('function'); + }); + + it('should init with clients', () => { + const app = { + config: { + dataService: { + clients: { + first: { foo: 'bar1' }, + second: { foo: 'bar2' }, + }, + }, + }, + }; + const name = 'dataService'; + + const singleton = new Singleton({ + name, + app, + create, + }); + singleton.init(); + (app.dataService instanceof Singleton).should.be.ok; + app.dataService.get('first').config.foo.should.equal('bar1'); + app.dataService.get('second').config.foo.should.equal('bar2'); + (typeof app.dataService.createInstance).should.equal('function'); + }); + + it('should client support default', () => { + const app = { + config: { + dataService: { + client: { foo: 'bar' }, + default: { foo1: 'bar1' }, + }, + }, + }; + const name = 'dataService'; + + const singleton = new Singleton({ + name, + app, + create, + }); + singleton.init(); + (app.dataService instanceof DataService).should.be.ok; + app.dataService.config.foo.should.equal('bar'); + app.dataService.config.foo1.should.equal('bar1'); + (typeof app.dataService.createInstance).should.equal('function'); + }); + + it('should clients support default', () => { + const app = { + config: { + dataService: { + clients: { + first: { foo: 'bar1' }, + second: { }, + }, + default: { foo: 'bar' }, + }, + }, + }; + const name = 'dataService'; + + const singleton = new Singleton({ + name, + app, + create, + }); + singleton.init(); + (app.dataService instanceof Singleton).should.be.ok; + app.dataService.get('first').config.foo.should.equal('bar1'); + app.dataService.get('second').config.foo.should.equal('bar'); + (typeof app.dataService.createInstance).should.equal('function'); + }); + + it('should createInstance without client/clients support default', () => { + const app = { + config: { + dataService: { + default: { foo: 'bar' }, + }, + }, + }; + const name = 'dataService'; + + const singleton = new Singleton({ + name, + app, + create, + }); + singleton.init(); + app.dataService.should.equal(singleton); + (app.dataService instanceof Singleton).should.be.ok; + app.dataService = app.dataService.createInstance({ foo1: 'bar1' }); + (app.dataService instanceof DataService).should.be.ok; + app.dataService.config.foo1.should.equal('bar1'); + app.dataService.config.foo.should.equal('bar'); + }); +}); diff --git a/test/lib/core/urllib.test.js b/test/lib/core/urllib.test.js new file mode 100644 index 0000000000..f029d41603 --- /dev/null +++ b/test/lib/core/urllib.test.js @@ -0,0 +1,83 @@ +'use strict'; + +const mm = require('egg-mock'); +const urllib = require('../../../lib/core/urllib'); + +describe('test/lib/core/urllib.test.js', () => { + let client; + const url = 'https://a.alipayobjects.com/aliBridge/1.0.0/aliBridge.min.js'; + + before(() => { + client = urllib({ + config: {}, + }); + client.on('request', info => { + info.args.headers = info.args.headers || {}; + info.args.headers['mock-traceid'] = 'mock-traceid'; + info.args.headers['mock-rpcid'] = 'mock-rpcid'; + }); + }); + + afterEach(mm.restore); + + it('should request ok with log', done => { + const args = { + dataType: 'text', + }; + client.once('response', info => { + info.req.options.headers['mock-traceid'].should.equal('mock-traceid'); + info.req.options.headers['mock-rpcid'].should.equal('mock-rpcid'); + done(); + }); + + client.request(url, args); + }); + + it('should request callback with log', done => { + client.once('response', info => { + info.req.options.headers['mock-traceid'].should.equal('mock-traceid'); + info.req.options.headers['mock-rpcid'].should.equal('mock-rpcid'); + done(); + }); + + client.request(url, () => {}); + }); + + it('should curl ok with log', done => { + const args = { + dataType: 'text', + }; + client.once('response', info => { + info.req.options.headers['mock-traceid'].should.equal('mock-traceid'); + info.req.options.headers['mock-rpcid'].should.equal('mock-rpcid'); + done(); + }); + + client.curl(url, args); + }); + + it('should requestThunk ok with log', function* () { + const args = { + dataType: 'text', + }; + client.once('response', info => { + info.req.options.headers['mock-traceid'].should.equal('mock-traceid'); + info.req.options.headers['mock-rpcid'].should.equal('mock-rpcid'); + }); + + yield client.requestThunk(url, args); + }); + + it('should request error with log', done => { + mm.http.requestError(/.*/i, null, 'mock res error'); + + client.once('response', info => { + info.req.options.headers['mock-traceid'].should.equal('mock-traceid'); + info.req.options.headers['mock-rpcid'].should.equal('mock-rpcid'); + info.error.message.should.containEql('mock res error'); + done(); + }); + + client.request(url); + }); +}); diff --git a/test/lib/core/util.test.js b/test/lib/core/util.test.js new file mode 100644 index 0000000000..6e0d7c6175 --- /dev/null +++ b/test/lib/core/util.test.js @@ -0,0 +1,23 @@ +'use strict'; + +const util = require('../../../lib/core/util'); + +describe('test/lib/core/util.test.js', () => { + describe('assign()', () => { + it('should assign with object', () => { + const a = { a: 0, c: 1 }; + const b = { a: 1, b: 1 }; + const c = util.assign(a, b); + a.should.equal(c); + c.should.eql({ a: 1, b: 1, c: 1 }); + }); + + it('should assign with array', () => { + const a = { a: 0, c: 0 }; + const b = [{ a: 1, b: 0 }, { b: 1, c: 1 }]; + const c = util.assign(a, b); + a.should.equal(c); + c.should.eql({ a: 1, b: 1, c: 1 }); + }); + }); +}); diff --git a/test/lib/plugins/cors.test.js b/test/lib/plugins/cors.test.js new file mode 100644 index 0000000000..a43703b025 --- /dev/null +++ b/test/lib/plugins/cors.test.js @@ -0,0 +1,36 @@ +'use strict'; + +const request = require('supertest-as-promised'); +const mm = require('egg-mock'); +const utils = require('../../utils'); + +describe('test/lib/plugins/cors.test.js', () => { + let app; + before(() => { + app = utils.app('apps/cors'); + return app.ready(); + }); + after(() => app.close()); + + afterEach(mm.restore); + + it('should set `Access-Control-Allow-Origin` to request origin header', () => { + return request(app.callback()) + .get('/') + .set('Origin', 'http://eggjs.org/foo') + .expect('Access-Control-Allow-Origin', 'http://eggjs.org/foo') + .expect('Access-Control-Allow-Credentials', 'true') + .expect({ foo: 'bar' }) + .expect(200); + }); + + it('should set `Access-Control-Allow-Origin` on POST request', () => { + app.mockCsrf && app.mockCsrf(); + return request(app.callback()) + .post('/') + .set('Origin', 'http://eggjs.org') + .expect('Access-Control-Allow-Origin', 'http://eggjs.org') + .expect('Access-Control-Allow-Credentials', 'true') + .expect(200); + }); +}); diff --git a/test/lib/plugins/depd.test.js b/test/lib/plugins/depd.test.js new file mode 100644 index 0000000000..4f309d9f00 --- /dev/null +++ b/test/lib/plugins/depd.test.js @@ -0,0 +1,23 @@ +'use strict'; + +const mm = require('egg-mock'); +const utils = require('../../utils'); + +describe('test/lib/plugins/depd.test.js', () => { + afterEach(mm.restore); + + let app; + before(() => { + mm(process.env, 'NO_DEPRECATION', '*'); + app = utils.app('apps/demo'); + return app.ready(); + }); + after(() => app.close()); + + it('should use this.locals instead of this.state', () => { + const ctx = app.mockContext(); + ctx.locals.test = 'aaa'; + ctx.locals.should.eql(ctx.state); + ctx.locals.test.should.eql(ctx.state.test); + }); +}); diff --git a/test/lib/plugins/development.test.js b/test/lib/plugins/development.test.js new file mode 100644 index 0000000000..fb631397a3 --- /dev/null +++ b/test/lib/plugins/development.test.js @@ -0,0 +1,100 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const request = require('supertest'); +const pedding = require('pedding'); +const mm = require('egg-mock'); +const utils = require('../../utils'); + +describe.skip('test/lib/plugins/development.test.js', () => { + afterEach(mm.restore); + + describe('development app', () => { + let app; + before(() => { + mm.env('local'); + mm(process.env, 'EGG_LOG', 'none'); + app = utils.app('apps/development'); + return app.ready(); + }); + after(() => app.close()); + + it('should log status', done => { + done = pedding(3, done); + request(app.callback()) + .get('/foo') + .expect(200, done); + + request(app.callback()) + .get('/not_exist') + .expect(404, done); + + setTimeout(() => { + const content = fs.readFileSync( + utils.getFilepath('apps/development/logs/development/development-web.log'), 'utf8'); + content.should.containEql('GET /foo] status 200'); + content.should.containEql('GET /not_exist] status 404'); + done(); + }, 1000); + }); + + it('should ignore assets', done => { + done = pedding(4, done); + mm(app.logger, 'info', msg => { + if (msg.match(/status /)) { + throw new Error('should not log status'); + } + }); + + request(app.callback()) + .get('/foo.js') + .expect(200) + .end(done); + + request(app.callback()) + .get('/public/hello') + .expect(404, done); + + request(app.callback()) + .get('/assets/hello') + .expect(404, done); + + request(app.callback()) + .get('/__koa_mock_scene_toolbox/hello') + .expect(404, done); + }); + }); + + describe('reload workers', () => { + let app; + const baseDir = utils.getFilepath('apps/reload-worker'); + const filepath = path.join(baseDir, 'app/controller/home.js'); + const body = fs.readFileSync(filepath); + + before(done => { + mm.env('local'); + mm(process.env, 'EGG_LOG', 'none'); + app = utils.cluster('apps/reload-worker'); + app.debug(); + app.ready(done); + }); + after(() => { + fs.writeFileSync(filepath, body); + app.close(); + }); + + it('should reload when file changed', done => { + fs.writeFileSync(filepath, 'module.exports = function*() { this.body = \'change\'; };'); + // 等待 app worker 重启 + setTimeout(() => { + request(app.callback()) + .get('/') + .expect('change', err => { + app.expect('stdout', /App Worker#2:\d+ started/); + done(err); + }); + }, 10000); + }); + }); +}); diff --git a/test/lib/plugins/empty.txt b/test/lib/plugins/empty.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/lib/plugins/i18n.test.js b/test/lib/plugins/i18n.test.js new file mode 100644 index 0000000000..93f00d087b --- /dev/null +++ b/test/lib/plugins/i18n.test.js @@ -0,0 +1,53 @@ +'use strict'; + +const request = require('supertest-as-promised'); +const utils = require('../../utils'); + +describe.skip('test/lib/plugins/i18n.test.js', () => { + let app; + before(() => { + app = utils.app('apps/i18n'); + return app.ready(); + }); + after(() => app.close()); + + describe('ctx.__(key, value)', () => { + it('should return locale de', () => { + return request(app.callback()) + .get('/message?locale=de') + .expect(200) + .expect('Set-Cookie', /locale=de; path=\/; expires=[^;]+ GMT/) + .expect({ + message: 'Hallo fengmk2, wie geht es dir heute? Wie war dein 18.', + empty: '', + notexists_key: 'key not exists', + empty_string: '', + novalue: 'key %s ok', + arguments3: '1 2 3', + arguments4: '1 2 3 4', + arguments5: '1 2 3 4 5', + arguments6: '1 2 3 4 5. 6', + values: 'foo bar foo bar {2} {100}', + }); + }); + }); + + describe('view render with __(key, value)', () => { + it('should render with default locale: en-US', () => { + return request(app.callback()) + .get('/') + .expect(200) + .expect('Set-Cookie', /locale=en-us; path=\/; expires=[^;]+ GMT/) + .expect('
  • Email:
  • \n
  • Hello fengmk2, how are you today?
  • \n
  • foo bar
  • \n'); + }); + + it('should render with query locale: zh_CN', () => { + return request(app.callback()) + .get('/?locale=zh_CN') + .set('Host', 'foo.example.com') + .expect(200) + .expect('Set-Cookie', /locale=zh-cn; path=\/; expires=[^;]+ GMT/) + .expect('
  • 邮箱:
  • \n
  • fengmk2,今天过得如何?
  • \n
  • foo bar
  • \n'); + }); + }); +}); diff --git a/test/lib/plugins/logrotater.test.js b/test/lib/plugins/logrotater.test.js new file mode 100644 index 0000000000..8a479ae555 --- /dev/null +++ b/test/lib/plugins/logrotater.test.js @@ -0,0 +1,34 @@ +'use strict'; + +const path = require('path'); +const mm = require('egg-mock'); +const glob = require('glob'); +const utils = require('../../utils'); + +describe.skip('test/lib/plugins/logrotater.test.js', () => { + let app; + before(() => { + mm.env('unittest'); + app = utils.cluster('apps/logrotater-app', { coverage: true }); + return app.ready(); + }); + + afterEach(mm.restore); + + after(() => app.close()); + + it('should rotate log file default', done => { + app.process.send({ + to: 'agent', + action: 'test-reload-logger', + }); + setTimeout(() => { + const files = glob.sync(path.join(__dirname, '../../fixtures/apps/logrotater-app/logs/logrotater-app/*.log.*')); + files.length.should.above(0); + files.forEach(file => { + file.should.match(/log.\d{4}-\d{2}-\d{2}$/); + }); + done(); + }, 1000); + }); +}); diff --git a/test/lib/plugins/multipart.test.js b/test/lib/plugins/multipart.test.js new file mode 100644 index 0000000000..56b3d30e9f --- /dev/null +++ b/test/lib/plugins/multipart.test.js @@ -0,0 +1,53 @@ +'use strict'; + +const request = require('supertest'); +const should = require('should'); +const formstream = require('formstream'); +const urllib = require('urllib'); +const utils = require('../../utils'); + +describe.skip('test/lib/plugins/multipart.test.js', () => { + let app; + let csrfToken; + let cookies; + let host; + let server; + before(done => { + app = utils.app('apps/multipart'); + server = app.listen(); + request(server) + .get('/') + .expect(200, (err, res) => { + csrfToken = res.headers['x-csrf']; + cookies = res.headers['set-cookie'].join(';'); + host = `http://127.0.0.1:${server.address().port}`; + done(err); + }); + }); + + after(() => { + server.close(); + }); + + it('should upload with csrf', done => { + const form = formstream(); + // form.file('file', filepath, filename); + form.file('file', __filename); + // other form fields + form.field('foo', 'fengmk2').field('love', 'chair'); + + const headers = form.headers(); + headers.Cookie = cookies; + urllib.request(`${host}/upload?_csrf=${csrfToken}`, { + method: 'POST', + headers, + stream: form, + }, (err, body, res) => { + should.not.exist(err); + res.statusCode.should.equal(200); + const data = JSON.parse(body); + data.filename.should.equal('multipart.test.js'); + done(); + }); + }); +}); diff --git a/test/lib/plugins/onerror.test.js b/test/lib/plugins/onerror.test.js new file mode 100644 index 0000000000..ffee4a988e --- /dev/null +++ b/test/lib/plugins/onerror.test.js @@ -0,0 +1,27 @@ +'use strict'; + +const request = require('supertest-as-promised'); +const mm = require('egg-mock'); +const utils = require('../../utils'); + +describe('test/lib/plugins/onerror.test.js', () => { + let app; + before(() => { + mm.env('local'); + mm(process.env, 'EGG_LOG', 'none'); + app = utils.app('apps/onerror'); + return app.ready(); + }); + + after(() => app.close()); + + afterEach(mm.restore); + + it('should redirect to error page', () => { + mm(app.config, 'env', 'test'); + return request(app.callback()) + .get('/?status=500') + .expect('Location', 'http://eggjs.org/500?real_status=500') + .expect(302); + }); +}); diff --git a/test/lib/plugins/rest.test.js b/test/lib/plugins/rest.test.js new file mode 100644 index 0000000000..b74a95fc3f --- /dev/null +++ b/test/lib/plugins/rest.test.js @@ -0,0 +1,48 @@ +'use strict'; + +const request = require('supertest'); +const mm = require('egg-mock'); +const utils = require('../../utils'); + +describe.skip('test/lib/plugins/rest.test.js', () => { + let app; + before(() => { + app = utils.app('apps/rest'); + return app.ready(); + }); + after(() => app.close()); + + afterEach(mm.restore); + + it('should GET /api/{objects} => app/apis/{objects}.js:index()', () => { + return request(app.callback()) + .get('/api/users') + .expect({ + data: [ + { + id: 1, + name: 'suqian.yf', + age: 18, + }, + { + id: 2, + name: 'name2', + age: 30, + }, + ], + }) + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(200); + }); + + it('should GET /api/categories', () => { + return request(app.callback()) + .get('/api/categories') + .expect({ + data: [ + { name: 'c1' }, + { name: 'c2' }, + ], + }); + }); +}); diff --git a/test/lib/plugins/schedule.test.js b/test/lib/plugins/schedule.test.js new file mode 100644 index 0000000000..d28bef3c87 --- /dev/null +++ b/test/lib/plugins/schedule.test.js @@ -0,0 +1,34 @@ +'use strict'; + +const path = require('path'); +const fs = require('fs'); +const utils = require('../../utils'); + +describe.skip('test/lib/plugins/schedule.test.js', () => { + it('should schedule work', function* () { + const app = utils.cluster('apps/schedule', { + workers: 4, + }); + yield app.ready(); + yield sleep(5000); + app.close(); + const log = getLogContent('schedule'); + // 由于 app.ready() 在 agent.ready 之后,ci 可能要耗太多时间导致多执行一次 + contains(log, 'cron').should.within(1, 2); + }); +}); + +function sleep(time) { + return new Promise(resolve => { + setTimeout(resolve, time); + }); +} + +function getLogContent(name) { + const logPath = path.join(__dirname, '../../fixtures/apps', name, 'logs', name, `${name}-web.log`); + return fs.readFileSync(logPath, 'utf8'); +} + +function contains(content, match) { + return content.split('\n').filter(line => line.indexOf(match) >= 0).length; +} diff --git a/test/lib/plugins/session.test.js b/test/lib/plugins/session.test.js new file mode 100644 index 0000000000..7fb28b7ac4 --- /dev/null +++ b/test/lib/plugins/session.test.js @@ -0,0 +1,75 @@ +'use strict'; + +const request = require('supertest'); +const should = require('should'); +const mm = require('egg-mock'); +const utils = require('../../utils'); + +describe('test/lib/plugins/session.test.js', () => { + let app; + before(() => { + app = utils.app('apps/koa-session'); + return app.ready(); + }); + after(() => app.close()); + afterEach(mm.restore); + + it('should work when userId change', done => { + app.mockContext({ + userId: 's1', + }); + request(app.callback()) + .get('/?uid=1') + .expect({ + userId: 's1', + sessionUid: '1', + uid: '1', + }) + .expect(200, (err, res) => { + if (err) return done(err); + should.exist(res.headers['set-cookie']); + const cookie = res.headers['set-cookie'].join(';'); + cookie.should.match(/EGG_SESS=[\w\-]+/); + + // userId 不变,还是读取到上次的 session 值 + app.mockContext({ + userId: 's1', + }); + request(app.callback()) + .get('/?uid=2&userId=s1') + .set('Cookie', cookie) + .expect({ + userId: 's1', + sessionUid: '1', + uid: '2', + }) + .expect(200, (err, res) => { + if (err) return done(err); + should.not.exist(res.headers['set-cookie']); + + // userId change, session still not change + app.mockContext({ + userId: 's2', + }); + request(app.callback()) + .get('/?uid=2') + .set('Cookie', cookie) + .expect({ + userId: 's2', + sessionUid: '1', + uid: '2', + }) + .expect(res => { + should.not.exist(res.headers['set-cookie']); + }) + .expect(200, err => { + if (err) return done(err); + request(app.callback()) + .get('/clear') + .set('Cookie', cookie) + .expect('set-cookie', /EGG_SESS=;/, done); + }); + }); + }); + }); +}); diff --git a/test/lib/plugins/static.test.js b/test/lib/plugins/static.test.js new file mode 100644 index 0000000000..b5acd6be26 --- /dev/null +++ b/test/lib/plugins/static.test.js @@ -0,0 +1,19 @@ +'use strict'; + +const request = require('supertest-as-promised'); +const utils = require('../../utils'); + +describe('test/lib/plugins/static.test.js', () => { + let app; + before(() => { + app = utils.app('apps/static-server'); + return app.ready(); + }); + + it('should get exists js file', () => { + return request(app.callback()) + .get('/public/foo.js') + .expect('alert(\'bar\');\n') + .expect(200); + }); +}); diff --git a/test/lib/plugins/userrole.test.js b/test/lib/plugins/userrole.test.js new file mode 100644 index 0000000000..db90d985ab --- /dev/null +++ b/test/lib/plugins/userrole.test.js @@ -0,0 +1,27 @@ +'use strict'; + +const request = require('supertest-as-promised'); +const utils = require('../../utils'); + +describe.skip('test/lib/plugins/userrole.test.js', () => { + let app; + before(() => { + app = utils.app('apps/userrole'); + return app.ready(); + }); + after(() => app.close()); + + it('should GET /user 200 when user login', () => { + return request(app.callback()) + .get('/user?name=user2') + .expect('hello user2') + .expect(200); + }); + + it('should GET /admin 200 when admin login', () => { + return request(app.callback()) + .get('/admin?name=fengmk2') + .expect('hello admin') + .expect(200); + }); +}); diff --git a/test/lib/plugins/userservice.test.js b/test/lib/plugins/userservice.test.js new file mode 100644 index 0000000000..80c69eb4da --- /dev/null +++ b/test/lib/plugins/userservice.test.js @@ -0,0 +1,26 @@ +'use strict'; + +const request = require('supertest-as-promised'); +const utils = require('../../utils'); + +describe.skip('test/lib/plugins/userservice.test.js', () => { + let app; + before(() => { + app = utils.app('apps/userservice'); + return app.ready(); + }); + after(() => app.close()); + + it('should get user and userId', () => { + return request(app.callback()) + .get('/?uid=456&name=fengmk2') + .expect({ + userId: '456', + user: { + uid: '456', + name: 'fengmk2', + }, + }) + .expect(200); + }); +}); diff --git a/test/lib/plugins/validate.test.js b/test/lib/plugins/validate.test.js new file mode 100644 index 0000000000..c359f9f26b --- /dev/null +++ b/test/lib/plugins/validate.test.js @@ -0,0 +1,27 @@ +'use strict'; + +const request = require('supertest-as-promised'); +const utils = require('../../utils'); + +describe('test/lib/plugins/validate.test.js', () => { + let app; + before(() => { + app = utils.app('apps/validate_form'); + return app.ready(); + }); + after(() => app.close()); + + it('should return invalid_param when body empty', () => { + return request(app.callback()) + .get('/users.json') + .expect({ + code: 'invalid_param', + message: 'Validation Failed', + errors: [ + { field: 'username', code: 'missing_field', message: 'required' }, + { field: 'password', code: 'missing_field', message: 'required' }, + ], + }) + .expect(422); + }); +}); diff --git a/test/lib/plugins/view/render.test.js b/test/lib/plugins/view/render.test.js new file mode 100644 index 0000000000..478d950ab9 --- /dev/null +++ b/test/lib/plugins/view/render.test.js @@ -0,0 +1,44 @@ +'use strict'; + +const request = require('supertest'); +const mm = require('egg-mock'); +const utils = require('../../../utils'); + +describe.skip('test/lib/plugins/view/render.test.js', () => { + let app; + before(function() { + app = utils.app('apps/view-render'); + app.locals = { + copyright: '2014 @ mk2
    ', + }; + }); + + afterEach(mm.restore); + + it('should render with options', function(done) { + request(app.callback()) + .get('/') + .expect(200) + .expect(`Hi, mk・2\ntest-app-helper: test-bar@${app.config.baseDir}\nraw:
    dar
    \n2014 @ mk2 <br>\n`, done); + }); + + it('should render have helper instance', function(done) { + request(app.callback()) + .get('/') + .expect(200, done); + }); + + it('should render with empty', function(done) { + request(app.callback()) + .get('/empty') + .expect(200) + .expect(`Hi, \ntest-app-helper: test-bar@${app.config.baseDir}\nraw:
    dar
    \n2014 @ mk2 <br>\n`, done); + }); + + it('should render template string', function(done) { + request(app.callback()) + .get('/string') + .expect(200) + .expect('templateString', done); + }); +}); diff --git a/test/lib/plugins/watcher.test.js b/test/lib/plugins/watcher.test.js new file mode 100644 index 0000000000..3ee2062f47 --- /dev/null +++ b/test/lib/plugins/watcher.test.js @@ -0,0 +1,167 @@ +'use strict'; + +require('should'); +const mm = require('egg-mock'); +const fs = require('fs'); +const request = require('supertest'); +const utils = require('../../utils'); +const file_path1 = utils.getFilepath('apps/watcher-development-app/tmp.txt'); +const file_path2 = utils.getFilepath('apps/watcher-development-app/tmp/tmp.txt'); +const file_path1_agent = utils.getFilepath('apps/watcher-development-app/tmp-agent.txt'); + +describe.skip('test/lib/plugins/watcher.test.js', () => { + describe('default', () => { + let app; + beforeEach(() => { + app = utils.cluster('apps/watcher-development-app'); + return app.ready(); + }); + + afterEach(() => { + app.close(); + mm.restore(); + }); + + it('should app watcher work', done => { + const server = app.callback(); + let count = 0; + request(server) + .get('/app-watch') + .expect(200) + .expect('app watch success') + .end(function(err) { + if (err) { + return done(err); + } + fs.writeFileSync(file_path1, 'aaa'); + setTimeout(function() { + request(server) + .get('/app-msg') + .expect(200) + .expect(function(res) { + const lastCount = count; + count = parseInt(res.text); + count.should.greaterThan(lastCount); + }) + .end(function(err) { + if (err) { + return done(err); + } + fs.writeFileSync(file_path2, 'aaa'); + setTimeout(function() { + request(server) + .get('/app-msg') + .expect(200) + .expect(function(res) { + const lastCount = count; + count = parseInt(res.text); + count.should.greaterThan(lastCount); + }) + .end(function(err) { + if (err) { + return done(err); + } + request(server) + .get('/app-unwatch') + .expect(200) + .expect('app unwatch success') + .end(function(err) { + if (err) return done(err); + setTimeout(() => { + fs.writeFileSync(file_path2, 'aaa'); + fs.writeFileSync(file_path1, 'aaa'); + setTimeout(function() { + request(server) + .get('/app-msg') + .expect(200) + .expect(function(res) { + const lastCount = count; + count = parseInt(res.text); + count.should.equal(lastCount); + }) // unchanged + .end(done); + }, 100); + }, 100); + }); + + }); + }, 100); + }); + }, 100); + }); + }); + + it('should agent watcher work', done => { + let count = 0; + request(app.callback()) + .get('/agent-watch') + .expect(200) + .expect('agent watch success') + .end(err => { + if (err) { + return done(err); + } + fs.writeFileSync(file_path1_agent, 'bbb'); + setTimeout(() => { + request(app.callback()) + .get('/agent-msg') + .expect(200) + .expect(res => { + const lastCount = count; + count = parseInt(res.text); + count.should.greaterThan(lastCount); + }) + .end(err => { + if (err) { + return done(err); + } + request(app.callback()) + .get('/agent-unwatch') + .expect(200) + .expect('agent unwatch success') + .end(err => { + if (err) { + return done(err); + } + + setTimeout(() => { + fs.writeFileSync(file_path1_agent, 'bbb'); + setTimeout(() => { + request(app.callback()) + .get('/agent-msg') + .expect(200) + .expect(res => { + const lastCount = count; + count = parseInt(res.text); + count.should.equal(lastCount); + }) + .end(done); + }, 100); + }, 100); + }); + }); + }, 100); + }); + }); + + }); + + describe('config.watcher.type is default', () => { + let app; + before(() => { + app = utils.cluster('apps/watcher-type-default'); + return app.ready(); + }); + + after(() => app.close()); + + it('should warn user', done => { + setTimeout(() => { + const content = fs.readFileSync( + utils.getFilepath('apps/watcher-type-default/logs/watcher-type-default/egg-agent.log')).toString(); + content.should.containEql('defaultEventSource watcher will NOT take effect'); + done(); + }, 1000); + }); + }); +}); diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000000..e3052016d3 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1 @@ +--require should diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 0000000000..a70ec6c989 --- /dev/null +++ b/test/utils.js @@ -0,0 +1,87 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const mm = require('egg-mock'); +const fixtures = path.join(__dirname, 'fixtures'); +const eggPath = path.join(__dirname, '..'); + +exports.app = (name, options) => { + options = formatOptions(name, options); + return mm.app(options); +}; + +/** + * start app with cluster mode + * + * @param {String} name - cluster name. + * @param {Object} [options] - optional + * @return {App} app - Application object. + */ +exports.cluster = (name, options) => { + options = formatOptions(name, options); + return mm.cluster(options); +}; + +exports.getFilepath = name => { + return path.join(fixtures, name); +}; + +exports.getJSON = name => { + return JSON.parse(fs.readFileSync(exports.getFilepath(name))); +}; + +// context helper, come from https://github.com/koajs/koa/blob/master/test/context.js +exports.createContext = (ctx, cb) => { + const app = exports.app('apps/demo'); + return new Promise(function(resolve, reject) { + app.ready(() => { + const mockCtx = app.mockContext(ctx); + if (cb) cb(mockCtx); + resolve(mockCtx); + }); + + app.on('error', err => { + reject(err); + }); + }); +}; + +exports.createRequest = function(ctx, cb) { + return new Promise(function(resolve, reject) { + exports.createContext(ctx).then(mockCtx => { + const req = mockCtx.request; + if (cb) cb(req); + resolve(req); + }, err => { + reject(err); + }); + }); +}; + +exports.createResponse = function(ctx, cb) { + return new Promise(function(resolve, reject) { + exports.createContext(ctx).then(mockCtx => { + const res = mockCtx.response; + if (cb) cb(res); + resolve(res); + }, err => { + reject(err); + }); + }); +}; + +function formatOptions(name, options) { + let baseDir; + if (typeof name === 'string') { + baseDir = name; + } else { + // name is options + options = name; + } + return Object.assign({}, { + baseDir, + customEgg: eggPath, + cache: false, + }, options); +}