From 61f890766066c51a60a5382eb9167358e584c2f8 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Wed, 8 Feb 2017 17:02:25 +0800 Subject: [PATCH] test: improve coverage to 100% (#333) * test: remove all skip tests --- README.md | 2 - README.zh-CN.md | 2 - app/extend/context.js | 3 +- app/extend/request.js | 3 +- app/extend/response.js | 2 +- app/middleware/site_file.js | 1 + lib/agent.js | 19 +- lib/application.js | 10 +- lib/cluster/index.js | 4 +- lib/core/messenger.js | 1 + lib/core/singleton.js | 9 +- lib/egg.js | 8 +- test/app/extend/context.test.js | 186 +++++++++--------- test/app/extend/helper.test.js | 7 - test/app/extend/request.test.js | 10 +- test/app/extend/response.test.js | 59 ++++-- test/fixtures/apps/agent-throw/agent.js | 4 + test/fixtures/apps/agent-throw/app/router.js | 5 + test/fixtures/apps/app-throw/app/router.js | 24 +++ test/fixtures/apps/app-throw/package.json | 3 + .../apps/keys-exists/config/config.default.js | 1 + test/fixtures/apps/keys-exists/package.json | 3 + test/fixtures/apps/keys-missing/package.json | 3 + .../apps/response/config/config.default.js | 1 + .../fixtures/apps/view/app/controller/home.js | 5 + test/fixtures/apps/view/app/router.js | 1 + test/lib/agent.test.js | 20 +- test/lib/application.test.js | 90 ++++++++- test/lib/cluster/agent_worker.test.js | 86 -------- test/lib/cluster/master.test.js | 1 + test/lib/core/dnscache_httpclient.test.js | 13 +- test/lib/egg.test.js | 45 +++++ 32 files changed, 367 insertions(+), 264 deletions(-) create mode 100644 test/fixtures/apps/app-throw/app/router.js create mode 100644 test/fixtures/apps/app-throw/package.json create mode 100644 test/fixtures/apps/keys-exists/config/config.default.js create mode 100644 test/fixtures/apps/keys-exists/package.json create mode 100644 test/fixtures/apps/keys-missing/package.json create mode 100644 test/fixtures/apps/response/config/config.default.js delete mode 100644 test/lib/cluster/agent_worker.test.js diff --git a/README.md b/README.md index 00cb7f14ca..38a39ef5c1 100644 --- a/README.md +++ b/README.md @@ -65,8 +65,6 @@ Please let us know what we can help, check [issues](https://github.com/eggjs/egg If you are a contributor, follow [CONTRIBUTING](CONTRIBUTING.md). -If you are a member of egg, follow [MEMBER_GUIDE](MEMBER_GUIDE.md). - ## License [MIT](LICENSE) diff --git a/README.zh-CN.md b/README.zh-CN.md index 3bfc54fb09..0fd97e07c0 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -63,8 +63,6 @@ Please let us know what we can help, check [issues](https://github.com/eggjs/egg If you are a contributor, follow [CONTRIBUTING](CONTRIBUTING.md). -If you are a member of egg, follow [MEMBER_GUIDE](MEMBER_GUIDE.md). - ## 开源协议 [MIT](LICENSE) diff --git a/app/extend/context.js b/app/extend/context.js index e9992b9e89..6edc2154e1 100644 --- a/app/extend/context.js +++ b/app/extend/context.js @@ -223,7 +223,7 @@ const proto = module.exports = { * @see Context#locals */ get state() { - return this.locals || {}; + return this.locals; }, set state(val) { @@ -245,6 +245,7 @@ const proto = module.exports = { runInBackground(scope) { const ctx = this; const start = Date.now(); + /* istanbul ignore next */ const taskName = scope.name || '-'; co(function* () { yield scope(ctx); diff --git a/app/extend/request.js b/app/extend/request.js index 43df814c9a..9d6cb0012b 100644 --- a/app/extend/request.js +++ b/app/extend/request.js @@ -18,11 +18,12 @@ module.exports = { * proxy is enabled. * @member {String} Request#host * @example + * ip + port * ```js * this.request.host * => '127.0.0.1:7001' * ``` - * 如果是域名访问,会得到域名 + * or domain * ```js * this.request.host * => 'demo.eggjs.org' diff --git a/app/extend/response.js b/app/extend/response.js index dec2476636..60e06e743f 100644 --- a/app/extend/response.js +++ b/app/extend/response.js @@ -30,7 +30,7 @@ module.exports = { set type(type) { // copy from koa // change header name to lower case - type = getType(type) || false; + type = getType(type); if (type) { this.set('content-type', type); } else { diff --git a/app/middleware/site_file.js b/app/middleware/site_file.js index a2daf024fe..2fde8e5746 100644 --- a/app/middleware/site_file.js +++ b/app/middleware/site_file.js @@ -6,6 +6,7 @@ const MAX_AGE = 'public, max-age=2592000'; // 30 days module.exports = options => { return function* siteFile(next) { if (this.method !== 'HEAD' && this.method !== 'GET') return yield next; + /* istanbul ignore if */ if (this.path[0] !== '/') return yield next; const content = options[this.path]; diff --git a/lib/agent.js b/lib/agent.js index 5b13b05628..15efc8be02 100644 --- a/lib/agent.js +++ b/lib/agent.js @@ -4,7 +4,6 @@ const path = require('path'); const EggApplication = require('./egg'); const AgentWorkerLoader = require('./loader').AgentWorkerLoader; -const AGENT_CLIENTS = Symbol('Agent#agentClients'); const EGG_LOADER = Symbol.for('egg#loader'); const EGG_PATH = Symbol.for('egg#eggPath'); @@ -13,13 +12,11 @@ const EGG_PATH = Symbol.for('egg#eggPath'); * @extends EggApplication */ class Agent extends EggApplication { - /** * @constructor * @param {Object} options - see {@link EggApplication} */ - constructor(options) { - options = options || {}; + constructor(options = {}) { options.type = 'agent'; super(options); @@ -27,28 +24,18 @@ class Agent extends EggApplication { this.loader.load(); - // 不让 agent 退出 + // keep agent alive even it don't have any io tasks setInterval(() => {}, 24 * 60 * 60 * 1000); this._uncaughtExceptionHandler = this._uncaughtExceptionHandler.bind(this); process.on('uncaughtException', this._uncaughtExceptionHandler); } - /** - * 当前进程实例化的 AgentWorkerClient 集合,只在 mm.app 场景下才有用 - * @private - */ - get agentWorkerClients() { - if (!this[AGENT_CLIENTS]) { - this[AGENT_CLIENTS] = new Map(); - } - return this[AGENT_CLIENTS]; - } - _uncaughtExceptionHandler(err) { if (!(err instanceof Error)) { err = new Error(String(err)); } + /* istanbul ignore else */ if (err.name === 'Error') { err.name = 'unhandledExceptionError'; } diff --git a/lib/application.js b/lib/application.js index 85ea0390f6..2b7101a886 100644 --- a/lib/application.js +++ b/lib/application.js @@ -1,9 +1,3 @@ -/** - * 对 koa application 的所有扩展,都放在此文件统一维护。 - * - * - koa application: https://github.com/koajs/koa/blob/master/lib/application.js - */ - 'use strict'; const path = require('path'); @@ -30,8 +24,7 @@ class Application extends EggApplication { * @constructor * @param {Object} options - see {@link EggApplication} */ - constructor(options) { - options = options || {}; + constructor(options = {}) { options.type = 'application'; super(options); this.loader.load(); @@ -47,6 +40,7 @@ class Application extends EggApplication { } onServer(server) { + /* istanbul ignore next */ graceful({ server: [ server ], error: (err, throwErrorCount) => { diff --git a/lib/cluster/index.js b/lib/cluster/index.js index 7cd4959c4b..f2d2fd234e 100644 --- a/lib/cluster/index.js +++ b/lib/cluster/index.js @@ -3,8 +3,8 @@ const path = require('path'); const startCluster = require('egg-cluster').startCluster; -module.exports = (options, callback) => { - options = options || {}; +module.exports = (options = {}, callback) => { + /* istanbul ignore next */ options.customEgg = options.customEgg || path.join(__dirname, '../..'); startCluster(options, callback); }; diff --git a/lib/core/messenger.js b/lib/core/messenger.js index 25fb0bc6bb..b7a8cdca36 100644 --- a/lib/core/messenger.js +++ b/lib/core/messenger.js @@ -60,6 +60,7 @@ class Messenger extends EventEmitter { * @return {Messenger} this */ sendRandom(action, data) { + /* istanbul ignore if */ if (!this.opids.length) return this; const pid = random(this.opids); this.sendTo(String(pid), action, data); diff --git a/lib/core/singleton.js b/lib/core/singleton.js index fb1453513c..1df3d89093 100644 --- a/lib/core/singleton.js +++ b/lib/core/singleton.js @@ -3,8 +3,7 @@ const assert = require('assert'); class Singleton { - constructor(options) { - options = options || {}; + constructor(options = {}) { assert(options.name, '[egg:singleton] Singleton#constructor options.name is required'); assert(options.app, '[egg:singleton] Singleton#constructor options.app is required'); assert(options.create, '[egg:singleton] Singleton#constructor options.create is required'); @@ -13,14 +12,14 @@ class Singleton { this.app = options.app; this.name = options.name; this.create = options.create; + /* istanbul ignore next */ this.options = options.app.config[this.name] || {}; } init() { const options = this.options; - if (options.client && options.clients) { - throw new Error(`eggg:singleton ${this.name} can not set options.client and options.clients both`); - } + assert(!(options.client && options.clients), + `eggg:singleton ${this.name} can not set options.client and options.clients both`); // alias app[name] as client, but still support createInstance method if (options.client) { diff --git a/lib/egg.js b/lib/egg.js index 52019a52eb..a64eecfcfc 100644 --- a/lib/egg.js +++ b/lib/egg.js @@ -89,6 +89,7 @@ class EggApplication extends EggCore { function delegate(res, app, keys) { for (const key of keys) { + /* istanbul ignore else */ if (app[key]) { res[key] = app[key]; } @@ -97,6 +98,7 @@ class EggApplication extends EggCore { function abbr(res, app, keys) { for (const key of keys) { + /* istanbul ignore else */ if (app[key]) { res[key] = ``; } @@ -217,6 +219,7 @@ class EggApplication extends EggCore { if (!(err instanceof Error)) { err = new Error(String(err)); } + /* istanbul ignore else */ if (err.name === 'Error') { err.name = 'unhandledRejectionError'; } @@ -231,13 +234,14 @@ class EggApplication extends EggCore { const rundir = this.config.rundir; const configdir = path.join(rundir, `${this.type}_config.json`); try { + /* istanbul ignore if */ if (!fs.existsSync(rundir)) fs.mkdirSync(rundir); fs.writeFileSync(configdir, JSON.stringify({ config: this.config, plugins: this.plugins, }, null, 2)); } catch (err) { - this.logger.warn(`dumpConfig error: ${err.message}`); + this.coreLogger.warn(`dumpConfig error: ${err.message}`); } } @@ -247,7 +251,7 @@ class EggApplication extends EggCore { _setupTimeoutTimer() { const startTimeoutTimer = setTimeout(() => { - this.logger.error(`${this.type} still doesn't ready after ${this.config.workerStartTimeout} ms.`); + this.coreLogger.error(`${this.type} still doesn't ready after ${this.config.workerStartTimeout} ms.`); this.emit('startTimeout'); }, this.config.workerStartTimeout); this.ready(() => clearTimeout(startTimeoutTimer)); diff --git a/test/app/extend/context.test.js b/test/app/extend/context.test.js index 8d311be2c3..1a6ef8a49d 100644 --- a/test/app/extend/context.test.js +++ b/test/app/extend/context.test.js @@ -9,11 +9,9 @@ const assert = require('assert'); const utils = require('../../utils'); describe('test/app/extend/context.test.js', () => { - afterEach(mm.restore); describe('ctx.logger', () => { - let app; afterEach(() => app.close()); @@ -123,9 +121,10 @@ describe('test/app/extend/context.test.js', () => { it('should log with padding message', function* () { yield request(app.callback()) - .get('/logger') - .expect(200); + .get('/logger') + .expect(200); + yield sleep(100); const logPath = utils.getFilepath('apps/get-logger/logs/get-logger/a.log'); fs.readFileSync(logPath, 'utf8').should.match(/\[-\/127.0.0.1\/-\/\d+ms GET \/logger] aaa/); }); @@ -173,6 +172,15 @@ describe('test/app/extend/context.test.js', () => { .expect(200) .expect('tpl={{a}}, a=111, b=b, c=testHelper'); }); + + it('should ctx.view === ctx.view', () => { + return request(app.callback()) + .get('/sameView') + .expect(200) + .expect({ + same: true, + }); + }); }); describe('ctx.locals', () => { @@ -235,81 +243,6 @@ describe('test/app/extend/context.test.js', () => { }); }); - describe('ctx.curl()', () => { - let app; - before(() => { - app = utils.app('apps/demo'); - return app.ready(); - }); - after(() => app.close()); - afterEach(mm.restore); - - it('should curl ok', function* () { - const localServer = yield utils.startLocalServer(); - const context = app.mockContext(); - const res = yield context.curl(`${localServer}/foo/bar`); - res.status.should.equal(200); - }); - }); - - describe('ctx.httpclient', () => { - let app; - before(() => { - app = utils.app('apps/demo'); - return app.ready(); - }); - after(() => app.close()); - afterEach(mm.restore); - - it('should only one httpclient on one ctx', function* () { - const ctx = app.mockContext(); - assert(ctx.httpclient === ctx.httpclient); - assert(typeof ctx.httpclient.request === 'function'); - assert(typeof ctx.httpclient.curl === 'function'); - }); - }); - - describe('ctx.realStatus', () => { - let app; - before(() => { - app = utils.app('apps/demo'); - return app.ready(); - }); - after(() => app.close()); - afterEach(mm.restore); - - it('should get from status ok', () => { - const context = app.mockContext(); - context.status = 200; - assert(context.realStatus === 200); - }); - - it('should get from realStatus ok', () => { - const context = app.mockContext(); - context.status = 302; - context.realStatus = 500; - assert(context.realStatus === 500); - }); - }); - - describe('ctx.state', () => { - let app; - before(() => { - app = utils.app('apps/demo'); - return app.ready(); - }); - after(() => app.close()); - afterEach(mm.restore); - - it('should delegate ctx.locals', () => { - const context = app.mockContext(); - 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); - }); - }); - describe('ctx.runInBackground(scope)', () => { let app; before(() => { @@ -346,31 +279,94 @@ describe('test/app/extend/context.test.js', () => { }); }); - describe('ctx.ip', () => { + describe('tests on apps/demo', () => { let app; before(() => { app = utils.app('apps/demo'); return app.ready(); }); after(() => app.close()); - afterEach(mm.restore); - it('should get current request ip', () => { - return request(app.callback()) - .get('/ip') - .expect(200) - .expect({ - ip: '127.0.0.1', - }); + describe('ctx.curl()', () => { + it('should curl ok', function* () { + const localServer = yield utils.startLocalServer(); + const context = app.mockContext(); + const res = yield context.curl(`${localServer}/foo/bar`); + res.status.should.equal(200); + }); }); - it('should set current request ip', () => { - return request(app.callback()) - .get('/ip?set_ip=10.2.2.2') - .expect(200) - .expect({ - ip: '10.2.2.2', - }); + describe('ctx.httpclient', () => { + it('should only one httpclient on one ctx', function* () { + const ctx = app.mockContext(); + assert(ctx.httpclient === ctx.httpclient); + assert(typeof ctx.httpclient.request === 'function'); + assert(typeof ctx.httpclient.curl === 'function'); + }); + }); + + describe('ctx.realStatus', () => { + it('should get from status ok', () => { + const context = app.mockContext(); + context.status = 200; + assert(context.realStatus === 200); + }); + + it('should get from realStatus ok', () => { + const context = app.mockContext(); + context.status = 302; + context.realStatus = 500; + assert(context.realStatus === 500); + }); + }); + + describe('ctx.state', () => { + it('should delegate ctx.locals', () => { + const context = app.mockContext(); + 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); + }); + }); + + describe('ctx.ip', () => { + it('should get current request ip', () => { + return request(app.callback()) + .get('/ip') + .expect(200) + .expect({ + ip: '127.0.0.1', + }); + }); + + it('should set current request ip', () => { + return request(app.callback()) + .get('/ip?set_ip=10.2.2.2') + .expect(200) + .expect({ + ip: '10.2.2.2', + }); + }); + }); + + describe('get helper()', () => { + it('should be the same helper instance', () => { + const ctx = app.mockContext(); + assert(ctx.helper === ctx.helper); + }); + }); + + describe('getLogger()', () => { + it('should return null when logger name not exists', () => { + const ctx = app.mockContext(); + assert(ctx.getLogger('not-exist-logger') === null); + }); + + it('should return same logger instance', () => { + const ctx = app.mockContext(); + assert(ctx.getLogger('coreLogger') === ctx.getLogger('coreLogger')); + }); }); }); }); diff --git a/test/app/extend/helper.test.js b/test/app/extend/helper.test.js index c5052a94e5..2d3894fcc9 100644 --- a/test/app/extend/helper.test.js +++ b/test/app/extend/helper.test.js @@ -60,12 +60,5 @@ describe('test/app/extend/helper.test.js', () => { .expect('true') .expect(200); }); - - it.skip('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/app/extend/request.test.js b/test/app/extend/request.test.js index 81028fd585..b45ad2d546 100644 --- a/test/app/extend/request.test.js +++ b/test/app/extend/request.test.js @@ -1,13 +1,12 @@ 'use strict'; +const assert = require('assert'); const mm = require('egg-mock'); const urllib = require('urllib'); const request = require('supertest'); const utils = require('../../utils'); - describe('test/app/extend/request.test.js', () => { - describe('normal', () => { let app; let ctx; @@ -96,7 +95,6 @@ describe('test/app/extend/request.test.js', () => { }); describe('req.protocol', () => { - it('should return http when it not config and no protocol header', () => { mm(app.config, 'protocol', null); return request(app.callback()) @@ -149,6 +147,12 @@ describe('test/app/extend/request.test.js', () => { .get('/protocol') .expect('https'); }); + + it('should return value from socket.encrypted', () => { + const ctx = app.mockContext(); + ctx.request.socket.encrypted = true; + assert(ctx.request.protocol === 'https'); + }); }); describe('this.query[key] => String', () => { diff --git a/test/app/extend/response.test.js b/test/app/extend/response.test.js index ca2cce0edf..56fe59d0e5 100644 --- a/test/app/extend/response.test.js +++ b/test/app/extend/response.test.js @@ -25,9 +25,29 @@ describe('test/app/extend/response.test.js', () => { assert(res.res.rawHeaders.indexOf('content-length') >= 0); }); }); + + it('should get body length', () => { + const ctx = app.mockContext(); + ctx.body = null; + ctx.response.remove('content-length'); + assert(ctx.response.length === undefined); + ctx.body = '1'; + ctx.response.remove('content-length'); + assert(ctx.response.length === 1); + ctx.body = new Buffer(2); + ctx.response.remove('content-length'); + assert(ctx.response.length === 2); + ctx.body = {}; + ctx.response.remove('content-length'); + assert(ctx.response.length === 2); + // mock stream + ctx.body = { pipe() {} }; + ctx.response.remove('content-length'); + assert(ctx.response.length === undefined); + }); }); - describe('response.realStatus', () => { + describe('test on apps/demo', () => { let app; before(() => { app = utils.app('apps/demo'); @@ -35,19 +55,34 @@ describe('test/app/extend/response.test.js', () => { }); after(() => app.close()); - it('should get from status ok', () => { - const ctx = app.mockContext(); - ctx.response.status = 200; - assert(ctx.response.realStatus === 200); - assert(ctx.realStatus === 200); + describe('response.realStatus', () => { + it('should get from status ok', () => { + const ctx = app.mockContext(); + ctx.response.status = 200; + assert(ctx.response.realStatus === 200); + assert(ctx.realStatus === 200); + }); + + it('should get from realStatus ok', () => { + const ctx = app.mockContext(); + ctx.response.status = 302; + ctx.response.realStatus = 404; + assert(ctx.response.realStatus === 404); + assert(ctx.realStatus === 404); + }); }); - it('should get from realStatus ok', () => { - const ctx = app.mockContext(); - ctx.response.status = 302; - ctx.response.realStatus = 404; - assert(ctx.response.realStatus === 404); - assert(ctx.realStatus === 404); + describe('response.type = type', () => { + it('should remove content-type when type is invalid', () => { + const ctx = app.mockContext(); + ctx.response.type = 'html'; + assert(ctx.response.header['content-type'] === 'text/html; charset=utf-8'); + assert(ctx.response.type === 'text/html'); + + ctx.response.type = 'html-ooooooxxx'; + assert(ctx.response.header['content-type'] === undefined); + assert(ctx.response.type === ''); + }); }); }); }); diff --git a/test/fixtures/apps/agent-throw/agent.js b/test/fixtures/apps/agent-throw/agent.js index e1857e7638..ba1a9e613d 100644 --- a/test/fixtures/apps/agent-throw/agent.js +++ b/test/fixtures/apps/agent-throw/agent.js @@ -4,4 +4,8 @@ module.exports = agent => { agent.messenger.on('agent-throw', () => { throw new Error('agent error'); }); + + agent.messenger.on('agent-throw-string', () => { + throw 'agent error string'; + }); }; diff --git a/test/fixtures/apps/agent-throw/app/router.js b/test/fixtures/apps/agent-throw/app/router.js index 7377d3f8f6..e15a903b78 100644 --- a/test/fixtures/apps/agent-throw/app/router.js +++ b/test/fixtures/apps/agent-throw/app/router.js @@ -5,4 +5,9 @@ module.exports = app => { app.messenger.broadcast('agent-throw'); this.body = 'done'; }); + + app.get('/agent-throw-string', function*() { + app.messenger.broadcast('agent-throw-string'); + this.body = 'done'; + }); }; diff --git a/test/fixtures/apps/app-throw/app/router.js b/test/fixtures/apps/app-throw/app/router.js new file mode 100644 index 0000000000..75481a63f1 --- /dev/null +++ b/test/fixtures/apps/app-throw/app/router.js @@ -0,0 +1,24 @@ +'use strict'; + +module.exports = app => { + app.get('/throw', function* () { + this.body = 'foo'; + setTimeout(() => { + a.b = c; + }, 1); + }); + + app.get('/throw-unhandledRejection', function* () { + this.body = 'foo'; + new Promise((resolve, reject) => { + reject(new Error('foo reject error')); + }); + }); + + app.get('/throw-unhandledRejection-string', function* () { + this.body = 'foo'; + new Promise((resolve, reject) => { + reject('foo reject string error'); + }); + }); +}; diff --git a/test/fixtures/apps/app-throw/package.json b/test/fixtures/apps/app-throw/package.json new file mode 100644 index 0000000000..864c8b9241 --- /dev/null +++ b/test/fixtures/apps/app-throw/package.json @@ -0,0 +1,3 @@ +{ + "name": "app-throw" +} diff --git a/test/fixtures/apps/keys-exists/config/config.default.js b/test/fixtures/apps/keys-exists/config/config.default.js new file mode 100644 index 0000000000..ea93c9c13a --- /dev/null +++ b/test/fixtures/apps/keys-exists/config/config.default.js @@ -0,0 +1 @@ +exports.keys = 'my keys'; diff --git a/test/fixtures/apps/keys-exists/package.json b/test/fixtures/apps/keys-exists/package.json new file mode 100644 index 0000000000..2debc74bcb --- /dev/null +++ b/test/fixtures/apps/keys-exists/package.json @@ -0,0 +1,3 @@ +{ + "name": "keys-missing-local" +} diff --git a/test/fixtures/apps/keys-missing/package.json b/test/fixtures/apps/keys-missing/package.json new file mode 100644 index 0000000000..1a27b7aba3 --- /dev/null +++ b/test/fixtures/apps/keys-missing/package.json @@ -0,0 +1,3 @@ +{ + "name": "keys-missing" +} diff --git a/test/fixtures/apps/response/config/config.default.js b/test/fixtures/apps/response/config/config.default.js new file mode 100644 index 0000000000..a490a2efc3 --- /dev/null +++ b/test/fixtures/apps/response/config/config.default.js @@ -0,0 +1 @@ +exports.keys = 'f' diff --git a/test/fixtures/apps/view/app/controller/home.js b/test/fixtures/apps/view/app/controller/home.js index d9c79475ea..98c095e372 100644 --- a/test/fixtures/apps/view/app/controller/home.js +++ b/test/fixtures/apps/view/app/controller/home.js @@ -14,3 +14,8 @@ exports.string = function *() { }); }; +exports.sameView = function* () { + this.body = { + same: this.view === this.view, + }; +}; diff --git a/test/fixtures/apps/view/app/router.js b/test/fixtures/apps/view/app/router.js index e548171535..7be4d7e2e6 100644 --- a/test/fixtures/apps/view/app/router.js +++ b/test/fixtures/apps/view/app/router.js @@ -3,4 +3,5 @@ module.exports = app => { app.get('/', app.controller.home.index); app.get('/string', app.controller.home.string); + app.get('/sameView', app.controller.home.sameView); }; diff --git a/test/lib/agent.test.js b/test/lib/agent.test.js index 20898841c6..85d83235f7 100644 --- a/test/lib/agent.test.js +++ b/test/lib/agent.test.js @@ -1,6 +1,6 @@ 'use strict'; -const should = require('should'); +const assert = require('assert'); const fs = require('fs'); const path = require('path'); const request = require('supertest'); @@ -9,7 +9,6 @@ const mm = require('egg-mock'); const utils = require('../utils'); describe('test/lib/agent.test.js', () => { - afterEach(mm.restore); describe('agent throw', () => { @@ -25,15 +24,28 @@ describe('test/lib/agent.test.js', () => { request(app.callback()) .get('/agent-throw') .expect(200, err => { - should.not.exists(err); + assert(!err); setTimeout(() => { const body = fs.readFileSync(path.join(baseDir, 'logs/agent-throw/common-error.log'), 'utf8'); - body.should.containEql('nodejs.unhandledExceptionError: agent error'); + assert(body.includes('nodejs.unhandledExceptionError: agent error')); app.notExpect(/nodejs.AgentWorkerDiedError/); done(); }, 1000); }); }); + + it('should catch uncaughtException string error', done => { + request(app.callback()) + .get('/agent-throw-string') + .expect(200, err => { + assert(!err); + setTimeout(() => { + const body = fs.readFileSync(path.join(baseDir, 'logs/agent-throw/common-error.log'), 'utf8'); + assert(body.includes('nodejs.unhandledExceptionError: agent error string')); + done(); + }, 1000); + }); + }); }); if (process.platform !== 'win32') { diff --git a/test/lib/application.test.js b/test/lib/application.test.js index 098900eb36..82579a1b6f 100644 --- a/test/lib/application.test.js +++ b/test/lib/application.test.js @@ -1,36 +1,43 @@ 'use strict'; +const assert = require('assert'); +const mm = require('egg-mock'); +const request = require('supertest'); +const sleep = require('ko-sleep'); +const fs = require('fs'); +const path = require('path'); const Application = require('../../lib/application'); const utils = require('../utils'); -const assert = require('assert'); describe('test/lib/application.test.js', () => { let app; + afterEach(mm.restore); + describe('create application', () => { it('should throw options.baseDir required', () => { - (function() { + assert.throws(() => { new Application({ baseDir: 1, }); - }).should.throw('options.baseDir required, and must be a string'); + }, /options.baseDir required, and must be a string/); }); it('should throw options.baseDir not exist', () => { - (function() { + assert.throws(() => { new Application({ baseDir: 'not-exist', }); - }).should.throw('Directory not-exist not exists'); + }, /Directory not-exist not exists/); }); it('should throw options.baseDir is not a directory', () => { - (function() { + assert.throws(() => { new Application({ baseDir: __filename, }); - }).should.throw(`Directory ${__filename} is not a directory`); + }, /is not a directory/); }); }); @@ -41,8 +48,8 @@ describe('test/lib/application.test.js', () => { app = utils.app('apps/demo'); yield app.ready(); const deprecate = app.deprecate; - deprecate._namespace.should.equal('egg'); - deprecate.should.equal(app.deprecate); + assert(deprecate._namespace === 'egg'); + assert(deprecate === app.deprecate); }); }); @@ -54,7 +61,7 @@ describe('test/lib/application.test.js', () => { yield app.ready(); const localServer = yield utils.startLocalServer(); const res = yield app.curl(`${localServer}/foo/app`); - res.status.should.equal(200); + assert(res.status === 200); }); }); @@ -85,4 +92,67 @@ describe('test/lib/application.test.js', () => { app.once('startTimeout', done); }); }); + + describe('app.keys', () => { + it('should throw when config.keys missing on non-local and non-unittest env', function* () { + mm.env('test'); + app = utils.app('apps/keys-missing'); + yield app.ready(); + + try { + app.keys; + throw new Error('should not run this'); + } catch (err) { + assert(err.message === 'Please set config.keys first'); + } + + // make sure app close + yield app.close(); + }); + + it('should auto set keys on unittest', function* () { + mm.env('unittest'); + app = utils.app('apps/keys-missing'); + yield app.ready(); + + assert(app.keys); + assert(app.keys); + assert(app.config.keys === 'foo, keys, you need to set your app keys'); + + yield app.close(); + }); + + it('should use exists keys', function* () { + mm.env('unittest'); + app = utils.app('apps/keys-exists'); + yield app.ready(); + + assert(app.keys); + assert(app.keys); + assert(app.config.keys === 'my keys'); + + yield app.close(); + }); + }); + + describe('handle uncaughtException', () => { + let app; + before(() => { + app = utils.cluster('apps/app-throw'); + return app.ready(); + }); + after(() => app.close()); + + it('should handle uncaughtException and log it', function* () { + yield request(app.callback()) + .get('/throw') + .expect('foo') + .expect(200); + + yield sleep(1100); + const logfile = path.join(utils.getFilepath('apps/app-throw'), 'logs/app-throw/common-error.log'); + const body = fs.readFileSync(logfile, 'utf8'); + assert(body.includes('ReferenceError: a is not defined (uncaughtException throw')); + }); + }); }); diff --git a/test/lib/cluster/agent_worker.test.js b/test/lib/cluster/agent_worker.test.js deleted file mode 100644 index 15f53afb3d..0000000000 --- a/test/lib/cluster/agent_worker.test.js +++ /dev/null @@ -1,86 +0,0 @@ -'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('logrotator', () => { - 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/master.test.js b/test/lib/cluster/master.test.js index 64a9a40173..b9cc08b202 100644 --- a/test/lib/cluster/master.test.js +++ b/test/lib/cluster/master.test.js @@ -242,6 +242,7 @@ describe('test/lib/cluster/master.test.js', () => { it('should start without customEgg', done => { app = coffee.fork(utils.getFilepath('apps/master-worker-started/dispatch.js')) + // .debug() .coverage(false); setTimeout(() => { diff --git a/test/lib/core/dnscache_httpclient.test.js b/test/lib/core/dnscache_httpclient.test.js index f026b8d8a7..7939b29509 100644 --- a/test/lib/core/dnscache_httpclient.test.js +++ b/test/lib/core/dnscache_httpclient.test.js @@ -119,13 +119,12 @@ describe('test/lib/core/dnscache_httpclient.test.js', () => { const result = yield app.curl(obj, { dataType: 'json' }); assert(result.status === 200); assert(result.data.host === 'localhost'); - }); - it.skip('should ctx.curl work on remote url', function* () { - const url = process.env.CI ? 'https://registry.npmjs.org' : 'https://r.cnpmjs.org'; - yield request(app.callback()) - .get('/?url=' + encodeURIComponent(url + '/pedding/latest')) - .expect(200) - .expect(/{"name":"pedding"/); + const obj2 = urlparse(url + '/get_headers'); + // mock obj2.host + obj2.host = null; + const result2 = yield app.curl(obj2, { dataType: 'json' }); + assert(result2.status === 200); + assert(result2.data.host === 'localhost'); }); }); diff --git a/test/lib/egg.test.js b/test/lib/egg.test.js index eadec4dd81..1bef291937 100644 --- a/test/lib/egg.test.js +++ b/test/lib/egg.test.js @@ -1,10 +1,15 @@ 'use strict'; +const mm = require('egg-mock'); const assert = require('assert'); const path = require('path'); +const fs = require('fs'); +const request = require('supertest'); +const sleep = require('ko-sleep'); const utils = require('../utils'); describe('test/lib/egg.test.js', () => { + afterEach(mm.restore); describe('dumpConfig()', () => { const baseDir = utils.getFilepath('apps/demo'); @@ -23,6 +28,21 @@ describe('test/lib/egg.test.js', () => { assert(/\d+\.\d+\.\d+/.test(json.plugins.onerror.version)); assert(json.config.name === 'demo'); }); + + it('should console.log call inspect()', () => { + console.log(app); + }); + + it('should mock fs.writeFileSync error', done => { + mm(fs, 'writeFileSync', () => { + throw new Error('mock error'); + }); + mm(app.coreLogger, 'warn', msg => { + assert(msg === 'dumpConfig error: mock error'); + done(); + }); + app.dumpConfig(); + }); }); @@ -61,4 +81,29 @@ describe('test/lib/egg.test.js', () => { }); }); + describe('handle unhandledRejection', () => { + let app; + before(() => { + app = utils.cluster('apps/app-throw'); + return app.ready(); + }); + after(() => app.close()); + + it('should handle unhandledRejection and log it', function* () { + yield request(app.callback()) + .get('/throw-unhandledRejection') + .expect('foo') + .expect(200); + yield request(app.callback()) + .get('/throw-unhandledRejection-string') + .expect('foo') + .expect(200); + + yield sleep(1100); + const logfile = path.join(utils.getFilepath('apps/app-throw'), 'logs/app-throw/common-error.log'); + const body = fs.readFileSync(logfile, 'utf8'); + assert(body.includes('nodejs.unhandledRejectionError: foo reject error')); + assert(body.includes('nodejs.unhandledRejectionError: foo reject string error')); + }); + }); });