text: /*globals describe, beforeEach, afterEach, it*/
  var assert   = require('assert'),
      moment   = require('moment'),
      should   = require('should'),
      sinon    = require('sinon'),
      when     = require('when'),
      rewire   = require("rewire"),
  
  // Stuff we are testing
      api      = require('../../server/api'),
      frontend = rewire('../../server/controllers/frontend');
  
  describe('Frontend Controller', function () {
  
      var ghost,
          sandbox,
          apiSettingsStub,
          adminEditPagePath = '/ghost/editor/';
  
      beforeEach(function () {
          sandbox = sinon.sandbox.create();
  
          // Reset frontend controller for next test
          frontend = rewire('../../server/controllers/frontend');
      });
  
      afterEach(function () {
          sandbox.restore();
      });
  
  
      describe('homepage redirects', function () {
          var res;
  
          beforeEach(function () {
              res = {
                  redirect: sandbox.spy(),
                  render: sandbox.spy()
              };
  
              sandbox.stub(api.posts, 'browse', function () {
                  return when({posts: {}, pages: 3});
              });
  
              apiSettingsStub = sandbox.stub(api.settings, 'read');
              apiSettingsStub.withArgs('postsPerPage').returns(when({
                  'key': 'postsPerPage',
                  'value': 6
              }));
          });
  
          it('Redirects to home if page number is -1', function () {
              var req = {params: {page: -1}, route: {path: '/page/:page/'}};
  
              frontend.homepage(req, res, null);
  
              res.redirect.called.should.be.true;
              res.redirect.calledWith('/').should.be.true;
              res.render.called.should.be.false;
  
          });
  
          it('Redirects to home if page number is 0', function () {
              var req = {params: {page: 0}, route: {path: '/page/:page/'}};
  
              frontend.homepage(req, res, null);
  
              res.redirect.called.should.be.true;
              res.redirect.calledWith('/').should.be.true;
              res.render.called.should.be.false;
  
          });
  
          it('Redirects to home if page number is 1', function () {
              var req = {params: {page: 1}, route: {path: '/page/:page/'}};
  
              frontend.homepage(req, res, null);
  
              res.redirect.called.should.be.true;
              res.redirect.calledWith('/').should.be.true;
              res.render.called.should.be.false;
          });
  
          it('Redirects to home if page number is 0 with subdirectory', function () {
              frontend.__set__('config', function() {
                  return {
                      paths: {subdir: '/blog'}
                  };
              });
  
              var req = {params: {page: 0}, route: {path: '/page/:page/'}};
  
              frontend.homepage(req, res, null);
  
              res.redirect.called.should.be.true;
              res.redirect.calledWith('/blog/').should.be.true;
              res.render.called.should.be.false;
          });
  
          it('Redirects to home if page number is 1 with subdirectory', function () {
              frontend.__set__('config', function() {
                  return {
                      paths: {subdir: '/blog'}
                  };
              });
  
              var req = {params: {page: 1}, route: {path: '/page/:page/'}};
  
              frontend.homepage(req, res, null);
  
              res.redirect.called.should.be.true;
              res.redirect.calledWith('/blog/').should.be.true;
              res.render.called.should.be.false;
          });
  
          it('Redirects to last page if page number too big', function (done) {
              var req = {params: {page: 4}, route: {path: '/page/:page/'}};
  
              frontend.homepage(req, res, done).then(function () {
                  res.redirect.called.should.be.true;
                  res.redirect.calledWith('/page/3/').should.be.true;
                  res.render.called.should.be.false;
                  done();
              });
          });
  
          it('Redirects to last page if page number too big with subdirectory', function (done) {
              frontend.__set__('config', function() {
                  return {
                      paths: {subdir: '/blog'}
                  };
              });
  
              var req = {params: {page: 4}, route: {path: '/page/:page/'}};
  
              frontend.homepage(req, res, done).then(function () {
                  res.redirect.calledOnce.should.be.true;
                  res.redirect.calledWith('/blog/page/3/').should.be.true;
                  res.render.called.should.be.false;
                  done();
              });
  
          });
      });
  
      describe('tag redirects', function () {
          var res;
  
          beforeEach(function () {
              res = {
                  redirect: sandbox.spy(),
                  render: sandbox.spy()
              };
  
              sandbox.stub(api.posts, 'browse', function () {
                  return when({posts: {}, pages: 3});
              });
  
              apiSettingsStub = sandbox.stub(api.settings, 'read');
              apiSettingsStub.withArgs('postsPerPage').returns(when({
                  'key': 'postsPerPage',
                  'value': 6
              }));
          });
  
          it('Redirects to base tag page if page number is -1', function () {
              var req = {params: {page: -1, slug: 'pumpkin'}};
  
              frontend.tag(req, res, null);
  
              res.redirect.called.should.be.true;
              res.redirect.calledWith('/tag/pumpkin/').should.be.true;
              res.render.called.should.be.false;
  
          });
  
          it('Redirects to base tag page if page number is 0', function () {
              var req = {params: {page: 0, slug: 'pumpkin'}};
  
              frontend.tag(req, res, null);
  
              res.redirect.called.should.be.true;
              res.redirect.calledWith('/tag/pumpkin/').should.be.true;
              res.render.called.should.be.false;
  
          });
  
          it('Redirects to base tag page if page number is 1', function () {
              var req = {params: {page: 1, slug: 'pumpkin'}};
  
              frontend.tag(req, res, null);
  
              res.redirect.called.should.be.true;
              res.redirect.calledWith('/tag/pumpkin/').should.be.true;
              res.render.called.should.be.false;
          });
  
          it('Redirects to base tag page if page number is 0 with subdirectory', function () {
              frontend.__set__('config', function() {
                  return {
                      paths: {subdir: '/blog'}
                  };
              });
  
              var req = {params: {page: 0, slug: 'pumpkin'}};
  
              frontend.tag(req, res, null);
  
              res.redirect.called.should.be.true;
              res.redirect.calledWith('/blog/tag/pumpkin/').should.be.true;
              res.render.called.should.be.false;
          });
  
          it('Redirects to base tag page if page number is 1 with subdirectory', function () {
              frontend.__set__('config', function() {
                  return {
                      paths: {subdir: '/blog'}
                  };
              });
  
              var req = {params: {page: 1, slug: 'pumpkin'}};
  
              frontend.tag(req, res, null);
  
              res.redirect.called.should.be.true;
              res.redirect.calledWith('/blog/tag/pumpkin/').should.be.true;
              res.render.called.should.be.false;
          });
  
          it('Redirects to last page if page number too big', function (done) {
              var req = {params: {page: 4, slug: 'pumpkin'}};
  
              frontend.tag(req, res, done).then(function () {
                  res.redirect.called.should.be.true;
                  res.redirect.calledWith('/tag/pumpkin/page/3/').should.be.true;
                  res.render.called.should.be.false;
                  done();
              });
          });
  
          it('Redirects to last page if page number too big with subdirectory', function (done) {
              frontend.__set__('config', function() {
                  return {
                      paths: {subdir: '/blog'}
                  };
              });
  
              var req = {params: {page: 4, slug: 'pumpkin'}};
  
              frontend.tag(req, res, done).then(function () {
                  res.redirect.calledOnce.should.be.true;
                  res.redirect.calledWith('/blog/tag/pumpkin/page/3/').should.be.true;
                  res.render.called.should.be.false;
                  done();
              });
  
          });
      });
  
      describe('single', function () {
          var mockStaticPost = {
                  'status': 'published',
                  'id': 1,
                  'title': 'Test static page',
                  'slug': 'test-static-page',
                  'markdown': 'Test static page content',
                  'page': 1,
                  'published_at': new Date('2013/12/30').getTime()
              },
              mockPost = {
                  'status': 'published',
                  'id': 2,
                  'title': 'Test normal post',
                  'slug': 'test-normal-post',
                  'markdown': 'The test normal post content',
                  'page': 0,
                  'published_at': new Date('2014/1/2').getTime()
              },
              // Helper function to prevent unit tests
              // from failing via timeout when they
              // should just immediately fail
              failTest = function(done, msg) {
                  return function() {
                      done(new Error(msg));
                  };
              };
  
          beforeEach(function () {
              sandbox.stub(api.posts, 'read', function (args) {
                  if (args.slug) {
                      return when(args.slug === mockStaticPost.slug ? mockStaticPost : mockPost);
                  } else if (args.id) {
                      return when(args.id === mockStaticPost.id ? mockStaticPost : mockPost);
                  } else {
                      return when({});
                  }
              });
  
              apiSettingsStub = sandbox.stub(api.settings, 'read');
  
              apiSettingsStub.withArgs('activeTheme').returns(when({
                  'key': 'activeTheme',
                  'value': 'casper'
              }));
  
              frontend.__set__('config',  sandbox.stub().returns({
                  'paths': {
                      'subdir': '',
                      'availableThemes': {
                          'casper': {
                              'assets': null,
                              'default.hbs': '/content/themes/casper/default.hbs',
                              'index.hbs': '/content/themes/casper/index.hbs',
                              'page.hbs': '/content/themes/casper/page.hbs',
                              'post.hbs': '/content/themes/casper/post.hbs'
                          }
                      }
                  }
              }));
          });
  
          describe('static pages', function () {
  
              describe('permalink set to slug', function () {
                  beforeEach(function () {
                      apiSettingsStub.withArgs('permalinks').returns(when({
                          value: '/:slug/'
                      }));
                  });
  
                  it('will render static page via /:slug', function (done) {
                      var req = {
                              path: '/' + mockStaticPost.slug
                          },
                          res = {
                              render: function (view, context) {
                                  assert.equal(view, 'page');
                                  assert.equal(context.post, mockStaticPost);
                                  done();
                              }
                          };
  
                      frontend.single(req, res, failTest(done));
                  });
  
                  it('will NOT render static page via /YYY/MM/DD/:slug', function (done) {
                      var req = {
                              path: '/' + ['2012/12/30', mockStaticPost.slug].join('/')
                          },
                          res = {
                              render: sinon.spy()
                          };
  
                      frontend.single(req, res, function () {
                          res.render.called.should.be.false;
                          done();
                      });
                  });
  
                  it('will redirect static page to admin edit page via /:slug/edit', function (done) {
                      var req = {
                              path: '/' + [mockStaticPost.slug, 'edit'].join('/')
                          },
                          res = {
                              render: sinon.spy(),
                              redirect: function(arg) {
                                  res.render.called.should.be.false;
                                  arg.should.eql(adminEditPagePath + mockStaticPost.id + '/');
                                  done();
                              }
                          };
  
                      frontend.single(req, res, failTest(done));
                  });
  
                  it('will NOT redirect static page to admin edit page via /YYYY/MM/DD/:slug/edit', function (done) {
                      var req = {
                              path: '/' + ['2012/12/30', mockStaticPost.slug, 'edit'].join('/')
                          },
                          res = {
                              render: sinon.spy(),
                              redirect: sinon.spy()
                          };
  
                      frontend.single(req, res, function () {
                          res.render.called.should.be.false;
                          res.redirect.called.should.be.false;
                          done();
                      });
                  });
              });
  
              describe('permalink set to date', function () {
                  beforeEach(function () {
                      apiSettingsStub.withArgs('permalinks').returns(when({
                          value: '/:year/:month/:day/:slug/'
                      }));
                  });
  
                  it('will render static page via /:slug', function (done) {
                      var req = {
                              path: '/' + mockStaticPost.slug
                          },
                          res = {
                              render: function (view, context) {
                                  assert.equal(view, 'page');
                                  assert.equal(context.post, mockStaticPost);
                                  done();
                              }
                          };
  
                      frontend.single(req, res, failTest(done));
                  });
  
                  it('will NOT render static page via /YYYY/MM/DD/:slug', function (done) {
                      var req = {
                              path: '/' + ['2012/12/30', mockStaticPost.slug].join('/')
                          },
                          res = {
                              render: sinon.spy()
                          };
  
                      frontend.single(req, res, function () {
                          res.render.called.should.be.false;
                          done();
                      });
                  });
  
                  it('will redirect static page to admin edit page via /:slug/edit', function (done) {
                      var req = {
                              path: '/' + [mockStaticPost.slug, 'edit'].join('/')
                          },
                          res = {
                              render: sinon.spy(),
                              redirect: function (arg) {
                                  res.render.called.should.be.false;
                                  arg.should.eql(adminEditPagePath + mockStaticPost.id + '/');
                                  done();
                              }
                          };
  
                      frontend.single(req, res, failTest(done));
                  });
  
                  it('will NOT redirect static page to admin edit page via /YYYY/MM/DD/:slug/edit', function (done) {
                      var req = {
                              path: '/' + ['2012/12/30', mockStaticPost.slug, 'edit'].join('/')
                          },
                          res = {
                              render: sinon.spy(),
                              redirect: sinon.spy()
                          };
  
                      frontend.single(req, res, function () {
                          res.render.called.should.be.false;
                          res.redirect.called.should.be.false;
                          done();
                      });
                  });
              });
          });
  
          describe('post', function () {
              describe('permalink set to slug', function () {
                  beforeEach(function () {
                      apiSettingsStub.withArgs('permalinks').returns(when({
                          value: '/:slug'
                      }));
                  });
  
                  it('will render post via /:slug', function (done) {
                      var req = {
                              path: '/' + mockPost.slug
                          },
                          res = {
                              render: function (view, context) {
                                  assert.equal(view, 'post');
                                  assert(context.post, 'Context object has post attribute');
                                  assert.equal(context.post, mockPost);
                                  done();
                              }
                          };
  
                      frontend.single(req, res, failTest(done));
                  });
  
                  it('will NOT render post via /YYYY/MM/DD/:slug', function (done) {
                      var req = {
                              path: '/' + ['2012/12/30', mockPost.slug].join('/')
                          },
                          res = {
                              render: sinon.spy()
                          };
  
                      frontend.single(req, res, function () {
                          res.render.called.should.be.false;
                          done();
                      });
                  });
  
                  // Handle Edit append
                  it('will redirect post to admin edit page via /:slug/edit', function (done) {
                      var req = {
                              path: '/' + [mockPost.slug, 'edit'].join('/')
                          },
                          res = {
                              render: sinon.spy(),
                              redirect: function(arg) {
                                  res.render.called.should.be.false;
                                  arg.should.eql(adminEditPagePath + mockPost.id + '/');
                                  done();
                              }
                          };
  
                      frontend.single(req, res, failTest(done));
                  });
  
                  it('will NOT redirect post to admin edit page via /YYYY/MM/DD/:slug/edit', function (done) {
                      var req = {
                              path: '/' + ['2012/12/30', mockPost.slug, 'edit'].join('/')
                          },
                          res = {
                              render: sinon.spy(),
                              redirect: sinon.spy()
                          };
  
                      frontend.single(req, res, function () {
                          res.render.called.should.be.false;
                          res.redirect.called.should.be.false;
                          done();
                      });
                  });
              });
  
              describe('permalink set to date', function () {
                  beforeEach(function () {
                      apiSettingsStub.withArgs('permalinks').returns(when({
                          value: '/:year/:month/:day/:slug'
                      }));
                  });
  
                  it('will render post via /YYYY/MM/DD/:slug', function (done) {
                      var date = moment(mockPost.published_at).format('YYYY/MM/DD'),
                          req = {
                              path: '/' + [date, mockPost.slug].join('/')
                          },
                          res = {
                              render: function (view, context) {
                                  assert.equal(view, 'post');
                                  assert(context.post, 'Context object has post attribute');
                                  assert.equal(context.post, mockPost);
                                  done();
                              }
                          };
  
                      frontend.single(req, res, failTest(done));
                  });
  
                  it('will NOT render post via /YYYY/MM/DD/:slug with non-matching date in url', function (done) {
                      var date = moment(mockPost.published_at).subtract('days', 1).format('YYYY/MM/DD'),
                          req = {
                              path: '/' + [date, mockPost.slug].join('/')
                          },
                          res = {
                              render: sinon.spy()
                          };
  
                      frontend.single(req, res, function () {
                          res.render.called.should.be.false;
                          done();
                      });
                  });
  
                  it('will NOT render post via /:slug', function (done) {
                      var req = {
                              path: '/' + mockPost.slug
                          },
                          res = {
                              render: sinon.spy()
                          };
  
                      frontend.single(req, res, function () {
                          res.render.called.should.be.false;
                          done();
                      });
                  });
  
                  // Handle Edit append
                  it('will redirect post to admin edit page via /YYYY/MM/DD/:slug/edit', function (done) {
                      var dateFormat = moment(mockPost.published_at).format('YYYY/MM/DD'),
                          req = {
                              path: '/' + [dateFormat, mockPost.slug, 'edit'].join('/')
                          },
                          res = {
                              render: sinon.spy(),
                              redirect: function (arg) {
                                  res.render.called.should.be.false;
                                  arg.should.eql(adminEditPagePath + mockPost.id + '/');
                                  done();
                              }
                          };
  
                      frontend.single(req, res, failTest(done));
                  });
  
                  it('will NOT redirect post to admin edit page via /:slug/edit', function (done) {
                      var req = {
                              path: '/' + [mockPost.slug, 'edit'].join('/')
                          },
                          res = {
                              render: sinon.spy(),
                              redirect: sinon.spy()
                          };
  
                      frontend.single(req, res, function () {
                          res.render.called.should.be.false;
                          res.redirect.called.should.be.false;
                          done();
                      });
                  });
              });
  
              describe('permalink set to custom format', function () {
                  beforeEach(function () {
                      apiSettingsStub.withArgs('permalinks').returns(when({
                          value: '/:year/:slug'
                      }));
                  });
  
                  it('will render post via /:year/:slug', function (done) {
                      var date = moment(mockPost.published_at).format('YYYY'),
                          req = {
                              path: '/' + [date, mockPost.slug].join('/')
                          },
                          res = {
                              render: function (view, context) {
                                  assert.equal(view, 'post');
                                  assert(context.post, 'Context object has post attribute');
                                  assert.equal(context.post, mockPost);
                                  done();
                              }
                          };
  
                      frontend.single(req, res, failTest(done));
                  });
  
                  it('will NOT render post via /YYYY/MM/DD/:slug', function (done) {
                      var date = moment(mockPost.published_at).format('YYYY/MM/DD'),
                          req = {
                              path: '/' + [date, mockPost.slug].join('/')
                          },
                          res = {
                              render: sinon.spy()
                          };
  
                      frontend.single(req, res, function () {
                          res.render.called.should.be.false;
                          done();
                      });
                  });
  
                  it('will NOT render post via /:year/slug when year does not match post year', function (done) {
                      var date = moment(mockPost.published_at).subtract('years', 1).format('YYYY'),
                          req = {
                              path: '/' + [date, mockPost.slug].join('/')
                          },
                          res = {
                              render: sinon.spy()
                          };
  
                      frontend.single(req, res, function () {
                          res.render.called.should.be.false;
                          done();
                      });
                  });
  
                  it('will NOT render post via /:slug', function (done) {
                      var req = {
                              path: '/' + mockPost.slug
                          },
                          res = {
                              render: sinon.spy()
                          };
  
                      frontend.single(req, res, function () {
                          res.render.called.should.be.false;
                          done();
                      });
                  });
  
                  // Handle Edit append
                  it('will redirect post to admin edit page via /:year/:slug/edit', function (done) {
                      var date = moment(mockPost.published_at).format('YYYY'),
                          req = {
                              path: '/' + [date, mockPost.slug, 'edit'].join('/')
                          },
                          res = {
                              render: sinon.spy(),
                              redirect: function (arg) {
                                  res.render.called.should.be.false;
                                  arg.should.eql(adminEditPagePath + mockPost.id + '/');
                                  done();
                              }
                          };
  
                      frontend.single(req, res, failTest(done));
                  });
  
                  it('will NOT redirect post to admin edit page /:slug/edit', function (done) {
                      var req = {
                              path: '/' + [mockPost.slug, 'edit'].join('/')
                          },
                          res = {
                              render: sinon.spy(),
                              redirect: sinon.spy()
                          };
  
                      frontend.single(req, res, function () {
                          res.render.called.should.be.false;
                          res.redirect.called.should.be.false;
                          done();
                      });
                  });
              });
          });
      });
  
      describe('rss redirects', function () {
          var res,
              apiUsersStub,
              overwriteConfig = function(newConfig) {
                  var existingConfig = frontend.__get__('config');
                  var newConfigModule = function() {
                      return newConfig;
                  };
                  newConfigModule.urlFor = existingConfig.urlFor;
                  frontend.__set__('config', newConfigModule);
              };
  
          beforeEach(function () {
              res = {
                  locals: { version: '' },
                  redirect: sandbox.spy(),
                  render: sandbox.spy()
              };
  
              sandbox.stub(api.posts, 'browse', function () {
                  return when({posts: {}, pages: 3});
              });
  
              apiUsersStub = sandbox.stub(api.users, 'read').returns(when({}));
  
              apiSettingsStub = sandbox.stub(api.settings, 'read');
              apiSettingsStub.withArgs('title').returns(when({
                  'key': 'title',
                  'value': 'Test'
              }));
              apiSettingsStub.withArgs('description').returns(when({
                  'key': 'description',
                  'value': 'Some Text'
              }));
              apiSettingsStub.withArgs('permalinks').returns(when({
                  'key': 'permalinks',
                  'value': '/:slug/'
              }));
          });
  
          it('Redirects to rss if page number is 0', function () {
              var req = {params: {page: -1}, route: {path: '/rss/:page/'}};
  
              frontend.rss(req, res, null);
  
              res.redirect.called.should.be.true;
              res.redirect.calledWith('/rss/').should.be.true;
              res.render.called.should.be.false;
  
          });
  
          it('Redirects to rss if page number is 0', function () {
              var req = {params: {page: 0}, route: {path: '/rss/:page/'}};
  
              frontend.rss(req, res, null);
  
              res.redirect.called.should.be.true;
              res.redirect.calledWith('/rss/').should.be.true;
              res.render.called.should.be.false;
  
          });
  
          it('Redirects to home if page number is 1', function () {
              var req = {params: {page: 1}, route: {path: '/rss/:page/'}};
  
              frontend.rss(req, res, null);
  
              res.redirect.called.should.be.true;
              res.redirect.calledWith('/rss/').should.be.true;
              res.render.called.should.be.false;
          });
  
          it('Redirects to home if page number is 0 with subdirectory', function () {
              overwriteConfig({paths: {subdir: '/blog'}});
  
              var req = {params: {page: 0}, route: {path: '/rss/:page/'}};
  
              frontend.rss(req, res, null);
  
              res.redirect.called.should.be.true;
              res.redirect.calledWith('/blog/rss/').should.be.true;
              res.render.called.should.be.false;
          });
  
          it('Redirects to home if page number is 1 with subdirectory', function () {
              overwriteConfig({paths: {subdir: '/blog'}});
  
              var req = {params: {page: 1}, route: {path: '/rss/:page/'}};
  
              frontend.rss(req, res, null);
  
              res.redirect.called.should.be.true;
              res.redirect.calledWith('/blog/rss/').should.be.true;
              res.render.called.should.be.false;
          });
  
          it('Redirects to last page if page number too big', function (done) {
              var req = {params: {page: 4}, route: {path: '/rss/:page/'}};
  
              frontend.rss(req, res, done).then(function () {
                  res.redirect.called.should.be.true;
                  res.redirect.calledWith('/rss/3/').should.be.true;
                  res.render.called.should.be.false;
                  done();
              });
          });
  
          it('Redirects to last page if page number too big with subdirectory', function (done) {
              overwriteConfig({paths: {subdir: '/blog'}});
  
              var req = {params: {page: 4}, route: {path: '/rss/:page/'}};
  
              frontend.rss(req, res, done).then(function () {
                  res.redirect.calledOnce.should.be.true;
                  res.redirect.calledWith('/blog/rss/3/').should.be.true;
                  res.render.called.should.be.false;
                  done();
              });
  
          });
      });
  });