tests: add maxAge option tests for res.sendFile
[express.git] / test / Router.js
blob057ce443df4b20a8e9ffecb6861c04a29a6bf73b
2 var after = require('after');
3 var express = require('../')
4   , Router = express.Router
5   , methods = require('methods')
6   , assert = require('assert');
8 describe('Router', function(){
9   it('should return a function with router methods', function() {
10     var router = Router();
11     assert(typeof router == 'function');
13     var router = new Router();
14     assert(typeof router == 'function');
16     assert(typeof router.get == 'function');
17     assert(typeof router.handle == 'function');
18     assert(typeof router.use == 'function');
19   });
21   it('should support .use of other routers', function(done){
22     var router = new Router();
23     var another = new Router();
25     another.get('/bar', function(req, res){
26       res.end();
27     });
28     router.use('/foo', another);
30     router.handle({ url: '/foo/bar', method: 'GET' }, { end: done });
31   });
33   it('should support dynamic routes', function(done){
34     var router = new Router();
35     var another = new Router();
37     another.get('/:bar', function(req, res){
38       req.params.bar.should.equal('route');
39       res.end();
40     });
41     router.use('/:foo', another);
43     router.handle({ url: '/test/route', method: 'GET' }, { end: done });
44   });
46   it('should handle blank URL', function(done){
47     var router = new Router();
49     router.use(function (req, res) {
50       false.should.be.true()
51     });
53     router.handle({ url: '', method: 'GET' }, {}, done);
54   });
56   it('should handle missing URL', function (done) {
57     var router = new Router()
59     router.use(function (req, res) {
60       throw new Error('should not be called')
61     })
63     router.handle({ method: 'GET' }, {}, done)
64   })
66   it('should not stack overflow with many registered routes', function(done){
67     var handler = function(req, res){ res.end(new Error('wrong handler')) };
68     var router = new Router();
70     for (var i = 0; i < 6000; i++) {
71       router.get('/thing' + i, handler)
72     }
74     router.get('/', function (req, res) {
75       res.end();
76     });
78     router.handle({ url: '/', method: 'GET' }, { end: done });
79   });
81   describe('.handle', function(){
82     it('should dispatch', function(done){
83       var router = new Router();
85       router.route('/foo').get(function(req, res){
86         res.send('foo');
87       });
89       var res = {
90         send: function(val) {
91           val.should.equal('foo');
92           done();
93         }
94       }
95       router.handle({ url: '/foo', method: 'GET' }, res);
96     })
97   })
99   describe('.multiple callbacks', function(){
100     it('should throw if a callback is null', function(){
101       assert.throws(function () {
102         var router = new Router();
103         router.route('/foo').all(null);
104       })
105     })
107     it('should throw if a callback is undefined', function(){
108       assert.throws(function () {
109         var router = new Router();
110         router.route('/foo').all(undefined);
111       })
112     })
114     it('should throw if a callback is not a function', function(){
115       assert.throws(function () {
116         var router = new Router();
117         router.route('/foo').all('not a function');
118       })
119     })
121     it('should not throw if all callbacks are functions', function(){
122       var router = new Router();
123       router.route('/foo').all(function(){}).all(function(){});
124     })
125   })
127   describe('error', function(){
128     it('should skip non error middleware', function(done){
129       var router = new Router();
131       router.get('/foo', function(req, res, next){
132         next(new Error('foo'));
133       });
135       router.get('/bar', function(req, res, next){
136         next(new Error('bar'));
137       });
139       router.use(function(req, res, next){
140         assert(false);
141       });
143       router.use(function(err, req, res, next){
144         assert.equal(err.message, 'foo');
145         done();
146       });
148       router.handle({ url: '/foo', method: 'GET' }, {}, done);
149     });
151     it('should handle throwing inside routes with params', function(done) {
152       var router = new Router();
154       router.get('/foo/:id', function(req, res, next){
155         throw new Error('foo');
156       });
158       router.use(function(req, res, next){
159         assert(false);
160       });
162       router.use(function(err, req, res, next){
163         assert.equal(err.message, 'foo');
164         done();
165       });
167       router.handle({ url: '/foo/2', method: 'GET' }, {}, function() {});
168     });
170     it('should handle throwing in handler after async param', function(done) {
171       var router = new Router();
173       router.param('user', function(req, res, next, val){
174         process.nextTick(function(){
175           req.user = val;
176           next();
177         });
178       });
180       router.use('/:user', function(req, res, next){
181         throw new Error('oh no!');
182       });
184       router.use(function(err, req, res, next){
185         assert.equal(err.message, 'oh no!');
186         done();
187       });
189       router.handle({ url: '/bob', method: 'GET' }, {}, function() {});
190     });
192     it('should handle throwing inside error handlers', function(done) {
193       var router = new Router();
195       router.use(function(req, res, next){
196         throw new Error('boom!');
197       });
199       router.use(function(err, req, res, next){
200         throw new Error('oops');
201       });
203       router.use(function(err, req, res, next){
204         assert.equal(err.message, 'oops');
205         done();
206       });
208       router.handle({ url: '/', method: 'GET' }, {}, done);
209     });
210   })
212   describe('FQDN', function () {
213     it('should not obscure FQDNs', function (done) {
214       var request = { hit: 0, url: 'http://example.com/foo', method: 'GET' };
215       var router = new Router();
217       router.use(function (req, res, next) {
218         assert.equal(req.hit++, 0);
219         assert.equal(req.url, 'http://example.com/foo');
220         next();
221       });
223       router.handle(request, {}, function (err) {
224         if (err) return done(err);
225         assert.equal(request.hit, 1);
226         done();
227       });
228     });
230     it('should ignore FQDN in search', function (done) {
231       var request = { hit: 0, url: '/proxy?url=http://example.com/blog/post/1', method: 'GET' };
232       var router = new Router();
234       router.use('/proxy', function (req, res, next) {
235         assert.equal(req.hit++, 0);
236         assert.equal(req.url, '/?url=http://example.com/blog/post/1');
237         next();
238       });
240       router.handle(request, {}, function (err) {
241         if (err) return done(err);
242         assert.equal(request.hit, 1);
243         done();
244       });
245     });
247     it('should ignore FQDN in path', function (done) {
248       var request = { hit: 0, url: '/proxy/http://example.com/blog/post/1', method: 'GET' };
249       var router = new Router();
251       router.use('/proxy', function (req, res, next) {
252         assert.equal(req.hit++, 0);
253         assert.equal(req.url, '/http://example.com/blog/post/1');
254         next();
255       });
257       router.handle(request, {}, function (err) {
258         if (err) return done(err);
259         assert.equal(request.hit, 1);
260         done();
261       });
262     });
264     it('should adjust FQDN req.url', function (done) {
265       var request = { hit: 0, url: 'http://example.com/blog/post/1', method: 'GET' };
266       var router = new Router();
268       router.use('/blog', function (req, res, next) {
269         assert.equal(req.hit++, 0);
270         assert.equal(req.url, 'http://example.com/post/1');
271         next();
272       });
274       router.handle(request, {}, function (err) {
275         if (err) return done(err);
276         assert.equal(request.hit, 1);
277         done();
278       });
279     });
281     it('should adjust FQDN req.url with multiple handlers', function (done) {
282       var request = { hit: 0, url: 'http://example.com/blog/post/1', method: 'GET' };
283       var router = new Router();
285       router.use(function (req, res, next) {
286         assert.equal(req.hit++, 0);
287         assert.equal(req.url, 'http://example.com/blog/post/1');
288         next();
289       });
291       router.use('/blog', function (req, res, next) {
292         assert.equal(req.hit++, 1);
293         assert.equal(req.url, 'http://example.com/post/1');
294         next();
295       });
297       router.handle(request, {}, function (err) {
298         if (err) return done(err);
299         assert.equal(request.hit, 2);
300         done();
301       });
302     });
304     it('should adjust FQDN req.url with multiple routed handlers', function (done) {
305       var request = { hit: 0, url: 'http://example.com/blog/post/1', method: 'GET' };
306       var router = new Router();
308       router.use('/blog', function (req, res, next) {
309         assert.equal(req.hit++, 0);
310         assert.equal(req.url, 'http://example.com/post/1');
311         next();
312       });
314       router.use('/blog', function (req, res, next) {
315         assert.equal(req.hit++, 1);
316         assert.equal(req.url, 'http://example.com/post/1');
317         next();
318       });
320       router.use(function (req, res, next) {
321         assert.equal(req.hit++, 2);
322         assert.equal(req.url, 'http://example.com/blog/post/1');
323         next();
324       });
326       router.handle(request, {}, function (err) {
327         if (err) return done(err);
328         assert.equal(request.hit, 3);
329         done();
330       });
331     });
332   })
334   describe('.all', function() {
335     it('should support using .all to capture all http verbs', function(done){
336       var router = new Router();
338       var count = 0;
339       router.all('/foo', function(){ count++; });
341       var url = '/foo?bar=baz';
343       methods.forEach(function testMethod(method) {
344         router.handle({ url: url, method: method }, {}, function() {});
345       });
347       assert.equal(count, methods.length);
348       done();
349     })
351     it('should be called for any URL when "*"', function (done) {
352       var cb = after(4, done)
353       var router = new Router()
355       function no () {
356         throw new Error('should not be called')
357       }
359       router.all('*', function (req, res) {
360         res.end()
361       })
363       router.handle({ url: '/', method: 'GET' }, { end: cb }, no)
364       router.handle({ url: '/foo', method: 'GET' }, { end: cb }, no)
365       router.handle({ url: 'foo', method: 'GET' }, { end: cb }, no)
366       router.handle({ url: '*', method: 'GET' }, { end: cb }, no)
367     })
368   })
370   describe('.use', function() {
371     it('should require middleware', function () {
372       var router = new Router()
373       assert.throws(function () { router.use('/') }, /requires a middleware function/)
374     })
376     it('should reject string as middleware', function () {
377       var router = new Router()
378       assert.throws(function () { router.use('/', 'foo') }, /requires a middleware function but got a string/)
379     })
381     it('should reject number as middleware', function () {
382       var router = new Router()
383       assert.throws(function () { router.use('/', 42) }, /requires a middleware function but got a number/)
384     })
386     it('should reject null as middleware', function () {
387       var router = new Router()
388       assert.throws(function () { router.use('/', null) }, /requires a middleware function but got a Null/)
389     })
391     it('should reject Date as middleware', function () {
392       var router = new Router()
393       assert.throws(function () { router.use('/', new Date()) }, /requires a middleware function but got a Date/)
394     })
396     it('should be called for any URL', function (done) {
397       var cb = after(4, done)
398       var router = new Router()
400       function no () {
401         throw new Error('should not be called')
402       }
404       router.use(function (req, res) {
405         res.end()
406       })
408       router.handle({ url: '/', method: 'GET' }, { end: cb }, no)
409       router.handle({ url: '/foo', method: 'GET' }, { end: cb }, no)
410       router.handle({ url: 'foo', method: 'GET' }, { end: cb }, no)
411       router.handle({ url: '*', method: 'GET' }, { end: cb }, no)
412     })
414     it('should accept array of middleware', function(done){
415       var count = 0;
416       var router = new Router();
418       function fn1(req, res, next){
419         assert.equal(++count, 1);
420         next();
421       }
423       function fn2(req, res, next){
424         assert.equal(++count, 2);
425         next();
426       }
428       router.use([fn1, fn2], function(req, res){
429         assert.equal(++count, 3);
430         done();
431       });
433       router.handle({ url: '/foo', method: 'GET' }, {}, function(){});
434     })
435   })
437   describe('.param', function() {
438     it('should call param function when routing VERBS', function(done) {
439       var router = new Router();
441       router.param('id', function(req, res, next, id) {
442         assert.equal(id, '123');
443         next();
444       });
446       router.get('/foo/:id/bar', function(req, res, next) {
447         assert.equal(req.params.id, '123');
448         next();
449       });
451       router.handle({ url: '/foo/123/bar', method: 'get' }, {}, done);
452     });
454     it('should call param function when routing middleware', function(done) {
455       var router = new Router();
457       router.param('id', function(req, res, next, id) {
458         assert.equal(id, '123');
459         next();
460       });
462       router.use('/foo/:id/bar', function(req, res, next) {
463         assert.equal(req.params.id, '123');
464         assert.equal(req.url, '/baz');
465         next();
466       });
468       router.handle({ url: '/foo/123/bar/baz', method: 'get' }, {}, done);
469     });
471     it('should only call once per request', function(done) {
472       var count = 0;
473       var req = { url: '/foo/bob/bar', method: 'get' };
474       var router = new Router();
475       var sub = new Router();
477       sub.get('/bar', function(req, res, next) {
478         next();
479       });
481       router.param('user', function(req, res, next, user) {
482         count++;
483         req.user = user;
484         next();
485       });
487       router.use('/foo/:user/', new Router());
488       router.use('/foo/:user/', sub);
490       router.handle(req, {}, function(err) {
491         if (err) return done(err);
492         assert.equal(count, 1);
493         assert.equal(req.user, 'bob');
494         done();
495       });
496     });
498     it('should call when values differ', function(done) {
499       var count = 0;
500       var req = { url: '/foo/bob/bar', method: 'get' };
501       var router = new Router();
502       var sub = new Router();
504       sub.get('/bar', function(req, res, next) {
505         next();
506       });
508       router.param('user', function(req, res, next, user) {
509         count++;
510         req.user = user;
511         next();
512       });
514       router.use('/foo/:user/', new Router());
515       router.use('/:user/bob/', sub);
517       router.handle(req, {}, function(err) {
518         if (err) return done(err);
519         assert.equal(count, 2);
520         assert.equal(req.user, 'foo');
521         done();
522       });
523     });
524   });
526   describe('parallel requests', function() {
527     it('should not mix requests', function(done) {
528       var req1 = { url: '/foo/50/bar', method: 'get' };
529       var req2 = { url: '/foo/10/bar', method: 'get' };
530       var router = new Router();
531       var sub = new Router();
533       done = after(2, done);
535       sub.get('/bar', function(req, res, next) {
536         next();
537       });
539       router.param('ms', function(req, res, next, ms) {
540         ms = parseInt(ms, 10);
541         req.ms = ms;
542         setTimeout(next, ms);
543       });
545       router.use('/foo/:ms/', new Router());
546       router.use('/foo/:ms/', sub);
548       router.handle(req1, {}, function(err) {
549         assert.ifError(err);
550         assert.equal(req1.ms, 50);
551         assert.equal(req1.originalUrl, '/foo/50/bar');
552         done();
553       });
555       router.handle(req2, {}, function(err) {
556         assert.ifError(err);
557         assert.equal(req2.ms, 10);
558         assert.equal(req2.originalUrl, '/foo/10/bar');
559         done();
560       });
561     });
562   });