text: var should = require('should'),
      supertest = require('supertest'),
      moment = require('moment'),
      _ = require('lodash'),
      ObjectId = require('bson-objectid'),
      testUtils = require('../../../utils'),
      localUtils = require('./utils'),
      config = require('../../../../../core/server/config'),
      ghost = testUtils.startGhost,
      markdownToMobiledoc = testUtils.DataGenerator.markdownToMobiledoc,
      request;
  
  describe('Post API', function () {
      let authorAccessToken, contributorAccessToken, author, contributor;
      var ghostServer;
  
      describe('As Owner', function () {
          var ownerAccessToken;
  
          before(function () {
              return ghost()
                  .then(function (_ghostServer) {
                      ghostServer = _ghostServer;
                      request = supertest.agent(config.get('url'));
                  })
                  .then(function () {
                      return localUtils.doAuth(request, 'users:extra', 'posts');
                  })
                  .then(function (token) {
                      ownerAccessToken = token;
  
                      // create author
                      return testUtils.createUser({
                          user: testUtils.DataGenerator.forKnex.createUser({email: 'test+2@ghost.org'}),
                          role: testUtils.DataGenerator.Content.roles[2].name
                      });
                  })
                  .then((_author) => {
                      request.user = author = _author;
                      return localUtils.doAuth(request);
                  })
                  .then(function (token) {
                      authorAccessToken = token;
  
                      // create contributor
                      return testUtils.createUser({
                          user: testUtils.DataGenerator.forKnex.createUser({email: 'test+3@ghost.org'}),
                          role: testUtils.DataGenerator.Content.roles[4].name
                      });
                  })
                  .then(function (_contributor) {
                      request.user = contributor = _contributor;
                      return localUtils.doAuth(request);
                  })
                  .then(function (token) {
                      contributorAccessToken = token;
                  });
          });
  
          describe('Browse', function () {
              it('retrieves all published posts only by default', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.should.have.length(11);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
                          _.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
                          _.isBoolean(jsonResponse.posts[0].page).should.eql(true);
  
                          // Ensure default order
                          jsonResponse.posts[0].slug.should.eql('welcome');
                          jsonResponse.posts[10].slug.should.eql('html-ipsum');
  
                          // Relative by default
                          jsonResponse.posts[0].url.should.eql('/welcome/');
  
                          done();
                      });
              });
  
              it('can retrieve a single post format', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/?formats=mobiledoc'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.should.have.length(11);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post', ['mobiledoc'], ['html']);
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
                          _.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
                          _.isBoolean(jsonResponse.posts[0].page).should.eql(true);
  
                          done();
                      });
              });
  
              it('can retrieve multiple post formats', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/?formats=plaintext,mobiledoc&limit=3&order=title%20ASC'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.should.have.length(3);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post', ['mobiledoc', 'plaintext'], ['html']);
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
                          _.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
                          _.isBoolean(jsonResponse.posts[0].page).should.eql(true);
  
                          // ensure order works
                          jsonResponse.posts[0].slug.should.eql('apps-integrations');
  
                          done();
                      });
              });
  
              it('can handle unknown post formats', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/?formats=plaintext,mobiledo'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.should.have.length(11);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post', ['plaintext'], ['html']);
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
                          _.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
                          _.isBoolean(jsonResponse.posts[0].page).should.eql(true);
  
                          done();
                      });
              });
  
              it('can handle empty formats (default html is expected)', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/?formats='))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.should.have.length(11);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
                          _.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
                          _.isBoolean(jsonResponse.posts[0].page).should.eql(true);
  
                          done();
                      });
              });
  
              it('fields and formats', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/?formats=mobiledoc,html&fields=id,title'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.should.have.length(11);
  
                          testUtils.API.checkResponse(
                              jsonResponse.posts[0],
                              'post',
                              null,
                              null,
                              ['mobiledoc', 'id', 'title', 'html']
                          );
  
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
  
                          done();
                      });
              });
  
              it('fields and formats and include', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/?formats=mobiledoc,html&fields=id,title&include=authors'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.should.have.length(11);
                          testUtils.API.checkResponse(
                              jsonResponse.posts[0],
                              'post',
                              null,
                              null,
                              ['mobiledoc', 'id', 'title', 'html', 'authors']
                          );
  
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
  
                          done();
                      });
              });
  
              it('can retrieve all published posts and pages', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/?filter=page:[false,true]&status=all'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.should.have.length(15);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
                          done();
                      });
              });
  
              it('can retrieve just published pages', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/?filter=page:true'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.should.have.length(1);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
                          done();
                      });
              });
  
              it('can retrieve posts filtered by related tag', function (done) {
                  request.get(localUtils.API.getApiQuery(`posts/?filter=tags.slug:[kitchen-sink]`))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.should.have.length(2);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
                          jsonResponse.posts[0].slug.should.equal('ghostly-kitchen-sink');
                          done();
                      });
              });
  
              it('can retrieve posts filtered by related author', function (done) {
                  request.get(localUtils.API.getApiQuery(`posts/?filter=authors.slug:joe-bloggs`))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.should.have.length(4);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
                          jsonResponse.posts[0].slug.should.equal('not-so-short-bit-complex');
                          done();
                      });
              });
  
              it('can retrieve posts filtered by primary_tag', function (done) {
                  request.get(localUtils.API.getApiQuery(`posts/?filter=primary_tag:kitchen-sink`))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.should.have.length(2);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
                          jsonResponse.posts[0].slug.should.equal('ghostly-kitchen-sink');
                          done();
                      });
              });
  
              it('can retrieve posts filtered by primary_author', function (done) {
                  request.get(localUtils.API.getApiQuery(`posts/?filter=primary_author:joe-bloggs`))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.should.have.length(4);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
                          jsonResponse.posts[0].slug.should.equal('not-so-short-bit-complex');
                          done();
                      });
              });
  
              it('can retrieve just featured posts', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/?filter=featured:true'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.length.should.eql(2);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
  
                          const featured = _.map(jsonResponse.posts, 'featured');
                          featured.should.matchEach(true);
  
                          done();
                      });
              });
  
              it('can retrieve just draft posts', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/?status=draft'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.should.have.length(1);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
  
                          _.each(jsonResponse.posts, (post) => {
                              post.status.should.eql('draft');
                          });
  
                          done();
                      });
              });
  
              it('can retrieve just scheduled posts', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/?status=scheduled'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse, 'posts');
                          jsonResponse.posts.should.have.length(1);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
                          testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
                          done();
                      });
              });
          });
  
          // ## Read
          describe('Read', function () {
              it('can retrieve a post by id', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse);
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
                          jsonResponse.posts[0].id.should.equal(testUtils.DataGenerator.Content.posts[0].id);
                          jsonResponse.posts[0].page.should.not.be.ok();
                          _.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
                          _.isBoolean(jsonResponse.posts[0].page).should.eql(true);
                          jsonResponse.posts[0].author.should.be.a.String();
                          testUtils.API.isISO8601(jsonResponse.posts[0].created_at).should.be.true();
                          jsonResponse.posts[0].created_by.should.be.a.String();
                          // Tags aren't included by default
                          should.not.exist(jsonResponse.posts[0].tags);
                          done();
                      });
              });
  
              it('can retrieve multiple post formats', function (done) {
                  request
                      .get(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/?formats=plaintext,mobiledoc'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts);
                          jsonResponse.posts.should.have.length(1);
                          jsonResponse.posts[0].id.should.equal(testUtils.DataGenerator.Content.posts[0].id);
  
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post', ['mobiledoc', 'plaintext'], ['html']);
  
                          done();
                      });
              });
  
              it('can retrieve a post by slug', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/slug/welcome/'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse);
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
                          jsonResponse.posts[0].slug.should.equal('welcome');
                          jsonResponse.posts[0].page.should.not.be.ok();
                          _.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
                          _.isBoolean(jsonResponse.posts[0].page).should.eql(true);
                          jsonResponse.posts[0].author.should.be.a.String();
                          jsonResponse.posts[0].created_by.should.be.a.String();
                          // Tags aren't included by default
                          should.not.exist(jsonResponse.posts[0].tags);
                          done();
                      });
              });
  
              it('[DEPRECATED] can retrieve a post with author, created_by, and tags', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/?include=author,tags,created_by'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse);
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post', 'tags');
                          jsonResponse.posts[0].page.should.not.be.ok();
  
                          jsonResponse.posts[0].author.should.be.an.Object();
                          testUtils.API.checkResponse(jsonResponse.posts[0].author, 'user');
                          jsonResponse.posts[0].tags[0].should.be.an.Object();
                          testUtils.API.checkResponse(jsonResponse.posts[0].tags[0], 'tag');
                          done();
                      });
              });
  
              it('can retrieve a post with authors, created_by, and tags', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/?include=authors,tags,created_by'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse);
                          should.exist(jsonResponse.posts);
  
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post', ['tags', 'authors']);
  
                          jsonResponse.posts[0].author.should.be.a.String();
                          jsonResponse.posts[0].page.should.not.be.ok();
  
                          jsonResponse.posts[0].authors[0].should.be.an.Object();
                          testUtils.API.checkResponse(jsonResponse.posts[0].authors[0], 'user');
  
                          jsonResponse.posts[0].tags[0].should.be.an.Object();
                          testUtils.API.checkResponse(jsonResponse.posts[0].tags[0], 'tag');
                          done();
                      });
              });
  
              it('can retrieve a static page', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[5].id + '/'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse);
                          should.exist(jsonResponse.posts);
                          testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
                          jsonResponse.posts[0].page.should.be.ok();
                          _.isBoolean(jsonResponse.posts[0].page).should.eql(true);
                          done();
                      });
              });
  
              it('can\'t retrieve non existent post', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/99/'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .set('Accept', 'application/json')
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(404)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse);
                          should.exist(jsonResponse.errors);
                          testUtils.API.checkResponseValue(jsonResponse.errors[0], ['message', 'errorType']);
                          done();
                      });
              });
          });
  
          // ## Add
          describe('Add', function () {
              it('create and ensure dates are correct', function (done) {
                  var newPost = {
                      posts: [{
                          status: 'published',
                          published_at: '2016-05-30T07:00:00.000Z',
                          mobiledoc: markdownToMobiledoc()
                      }]
                  };
  
                  request.post(localUtils.API.getApiQuery('posts'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .send(newPost)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(201)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          res.body.posts[0].published_at.should.eql('2016-05-30T07:00:00.000Z');
                          res.body.posts[0].published_at = '2016-05-30T09:00:00.000Z';
  
                          request.put(localUtils.API.getApiQuery('posts/' + res.body.posts[0].id + '/'))
                              .set('Authorization', 'Bearer ' + ownerAccessToken)
                              .send(res.body)
                              .expect('Content-Type', /json/)
                              .expect('Cache-Control', testUtils.cacheRules.private)
                              .expect(200)
                              .end(function (err, res) {
                                  if (err) {
                                      return done(err);
                                  }
  
                                  res.body.posts[0].published_at.should.eql('2016-05-30T09:00:00.000Z');
  
                                  request.get(localUtils.API.getApiQuery('posts/' + res.body.posts[0].id + '/'))
                                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                                      .expect('Content-Type', /json/)
                                      .expect('Cache-Control', testUtils.cacheRules.private)
                                      .expect(200)
                                      .end(function (err, res) {
                                          if (err) {
                                              return done(err);
                                          }
  
                                          res.body.posts[0].published_at.should.eql('2016-05-30T09:00:00.000Z');
                                          done();
                                      });
                              });
                      });
              });
  
              it('can create a new draft, publish post, update post', function (done) {
                  var newTitle = 'My Post',
                      newTagName = 'My Tag',
                      publishedState = 'published',
                      newTag = {id: null, name: newTagName},
                      newPost = {
                          posts: [{
                              status: 'draft',
                              title: newTitle,
                              mobiledoc: markdownToMobiledoc('my post'),
                              tags: [newTag]
                          }]
                      };
  
                  request.post(localUtils.API.getApiQuery('posts/?include=tags'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .send(newPost)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(201)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var draftPost = res.body;
                          res.headers.location.should.equal('/ghost/api/v0.1/posts/' + draftPost.posts[0].id + '/?status=draft');
                          should.exist(draftPost.posts);
                          draftPost.posts.length.should.be.above(0);
                          draftPost.posts[0].title.should.eql(newTitle);
                          draftPost.posts[0].status = publishedState;
                          testUtils.API.checkResponse(draftPost.posts[0], 'post', 'tags');
  
                          should.exist(draftPost.posts[0].tags);
                          draftPost.posts[0].tags.length.should.be.above(0);
                          draftPost.posts[0].tags[0].name.should.eql(newTagName);
                          testUtils.API.checkResponse(draftPost.posts[0].tags[0], 'tag');
  
                          request.put(localUtils.API.getApiQuery('posts/' + draftPost.posts[0].id + '/?include=tags'))
                              .set('Authorization', 'Bearer ' + ownerAccessToken)
                              .send(draftPost)
                              .expect('Content-Type', /json/)
                              .expect('Cache-Control', testUtils.cacheRules.private)
                              .expect(200)
                              .end(function (err, res) {
                                  if (err) {
                                      return done(err);
                                  }
  
                                  var publishedPost = res.body;
                                  _.has(res.headers, 'x-cache-invalidate').should.equal(true);
                                  res.headers['x-cache-invalidate'].should.eql('/*');
  
                                  should.exist(publishedPost);
                                  should.exist(publishedPost.posts);
                                  publishedPost.posts.length.should.be.above(0);
                                  publishedPost.posts[0].title.should.eql(newTitle);
                                  publishedPost.posts[0].status.should.eql(publishedState);
                                  testUtils.API.checkResponse(publishedPost.posts[0], 'post', 'tags');
  
                                  should.exist(publishedPost.posts[0].tags);
                                  publishedPost.posts[0].tags.length.should.be.above(0);
                                  publishedPost.posts[0].tags[0].name.should.eql(newTagName);
                                  testUtils.API.checkResponse(publishedPost.posts[0].tags[0], 'tag');
  
                                  request.put(localUtils.API.getApiQuery('posts/' + publishedPost.posts[0].id + '/?include=tags'))
                                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                                      .send(publishedPost)
                                      .expect('Content-Type', /json/)
                                      .expect('Cache-Control', testUtils.cacheRules.private)
                                      .expect(200)
                                      .end(function (err, res) {
                                          if (err) {
                                              return done(err);
                                          }
  
                                          var updatedPost = res.body;
                                          // Require cache invalidation when post was updated and published
                                          res.headers['x-cache-invalidate'].should.eql('/*');
  
                                          should.exist(updatedPost);
                                          should.exist(updatedPost.posts);
                                          updatedPost.posts.length.should.be.above(0);
                                          updatedPost.posts[0].title.should.eql(newTitle);
                                          testUtils.API.isISO8601(updatedPost.posts[0].created_at).should.be.true();
                                          testUtils.API.isISO8601(updatedPost.posts[0].updated_at).should.be.true();
                                          testUtils.API.checkResponse(updatedPost.posts[0], 'post', 'tags');
  
                                          should.exist(updatedPost.posts[0].tags);
                                          updatedPost.posts[0].tags.length.should.be.above(0);
                                          updatedPost.posts[0].tags[0].name.should.eql(newTagName);
                                          testUtils.API.checkResponse(updatedPost.posts[0].tags[0], 'tag');
  
                                          done();
                                      });
                              });
                      });
              });
  
              it('check which fields can be added', function (done) {
                  var newPost = {
                      status: 'draft',
                      title: 'title',
                      mobiledoc: markdownToMobiledoc('my post'),
                      created_at: moment().subtract(2, 'days').toDate(),
                      updated_at: moment().subtract(2, 'days').toDate(),
                      created_by: ObjectId.generate(),
                      updated_by: ObjectId.generate()
                  };
  
                  request
                      .post(localUtils.API.getApiQuery('posts/'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .send({posts: [newPost]})
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(201)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var response = res.body;
                          res.headers.location.should.equal('/ghost/api/v0.1/posts/' + response.posts[0].id + '/?status=draft');
                          should.exist(response.posts);
                          response.posts.length.should.be.above(0);
  
                          response.posts[0].title.should.eql(newPost.title);
                          response.posts[0].status.should.eql(newPost.status);
  
                          response.posts[0].created_at.should.not.eql(newPost.created_at.toISOString());
                          response.posts[0].updated_at.should.not.eql(newPost.updated_at.toISOString());
                          response.posts[0].updated_by.should.not.eql(newPost.updated_by);
                          response.posts[0].created_by.should.not.eql(newPost.created_by);
  
                          testUtils.API.checkResponse(response.posts[0], 'post');
                          done();
                      });
              });
          });
  
          // ## edit
          describe('Edit', function () {
              it('can edit a post', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/?include=tags'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var jsonResponse = res.body,
                              changedTitle = 'My new Title',
                              changedAuthor = testUtils.DataGenerator.Content.extraUsers[0].id;
  
                          should.exist(jsonResponse.posts[0]);
                          jsonResponse.posts[0].author.should.not.eql(changedAuthor);
                          jsonResponse.posts[0].title = changedTitle;
                          jsonResponse.posts[0].author = changedAuthor;
                          jsonResponse.posts[0].custom_template = 'custom-about';
  
                          request.put(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
                              .set('Authorization', 'Bearer ' + ownerAccessToken)
                              .send(jsonResponse)
                              .expect('Content-Type', /json/)
                              .expect('Cache-Control', testUtils.cacheRules.private)
                              .expect(200)
                              .end(function (err, res) {
                                  if (err) {
                                      return done(err);
                                  }
  
                                  var putBody = res.body;
                                  res.headers['x-cache-invalidate'].should.eql('/*');
                                  should.exist(putBody);
                                  putBody.posts[0].title.should.eql(changedTitle);
                                  putBody.posts[0].author.should.eql(changedAuthor);
                                  putBody.posts[0].status.should.eql('published');
                                  putBody.posts[0].custom_template.should.eql('custom-about');
  
                                  testUtils.API.checkResponse(putBody.posts[0], 'post');
                                  done();
                              });
                      });
              });
  
              it('can edit a new draft and update post', function (done) {
                  var newTitle = 'My Post',
                      newTagName = 'My Tag',
                      newTag = {id: null, name: newTagName},
                      newPost = {
                          posts: [{
                              status: 'draft',
                              title: newTitle,
                              mobiledoc: markdownToMobiledoc('my post'),
                              tags: [newTag]
                          }]
                      };
  
                  request.post(localUtils.API.getApiQuery('posts/?include=tags'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .send(newPost)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(201)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var draftPost = res.body;
                          res.headers.location.should.equal('/ghost/api/v0.1/posts/' + draftPost.posts[0].id + '/?status=draft');
                          should.exist(draftPost.posts);
                          draftPost.posts.length.should.be.above(0);
                          draftPost.posts[0].title.should.eql(newTitle);
                          testUtils.API.checkResponse(draftPost.posts[0], 'post', 'tags');
  
                          draftPost.posts[0].title = 'Vote for Casper in red';
  
                          request.put(localUtils.API.getApiQuery('posts/' + draftPost.posts[0].id + '/?include=tags'))
                              .set('Authorization', 'Bearer ' + ownerAccessToken)
                              .send(draftPost)
                              .expect('Content-Type', /json/)
                              .expect('Cache-Control', testUtils.cacheRules.private)
                              .expect(200)
                              .end(function (err, res) {
                                  if (err) {
                                      return done(err);
                                  }
  
                                  // Updating a draft should send x-cache-invalidate headers for the preview only
                                  res.headers['x-cache-invalidate'].should.eql('/p/' + draftPost.posts[0].uuid + '/');
                                  done();
                              });
                      });
              });
  
              it('can edit a new published post and unpublish', function (done) {
                  var newTitle = 'My Post',
                      newTagName = 'My Tag',
                      draftState = 'draft',
                      newTag = {id: null, name: newTagName},
                      newPost = {
                          posts: [{
                              status: 'published',
                              title: newTitle,
                              mobiledoc: markdownToMobiledoc('my post'),
                              tags: [newTag]
                          }]
                      };
  
                  request.post(localUtils.API.getApiQuery('posts/?include=tags'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .send(newPost)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(201)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var draftPost = res.body;
                          res.headers.location.should.equal('/ghost/api/v0.1/posts/' + draftPost.posts[0].id + '/?status=published');
                          should.exist(draftPost.posts);
                          draftPost.posts.length.should.be.above(0);
                          draftPost.posts[0].title.should.eql(newTitle);
                          testUtils.API.checkResponse(draftPost.posts[0], 'post', 'tags');
  
                          draftPost.posts[0].title = 'Vote for Casper in red';
                          draftPost.posts[0].status = draftState;
  
                          request.put(localUtils.API.getApiQuery('posts/' + draftPost.posts[0].id + '/?include=tags'))
                              .set('Authorization', 'Bearer ' + ownerAccessToken)
                              .send(draftPost)
                              .expect('Content-Type', /json/)
                              .expect('Cache-Control', testUtils.cacheRules.private)
                              .expect(200)
                              .end(function (err, res) {
                                  if (err) {
                                      return done(err);
                                  }
  
                                  // Unpublishing a post should send x-cache-invalidate headers
                                  res.headers['x-cache-invalidate'].should.eql('/*');
                                  done();
                              });
                      });
              });
  
              it('can change a post to a static page', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/?include=tags'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var jsonResponse = res.body;
  
                          should.exist(jsonResponse);
                          jsonResponse.posts[0].page.should.not.be.ok();
                          jsonResponse.posts[0].page = true;
  
                          request.put(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
                              .set('Authorization', 'Bearer ' + ownerAccessToken)
                              .send(jsonResponse)
                              .expect('Content-Type', /json/)
                              .expect('Cache-Control', testUtils.cacheRules.private)
                              .expect(200)
                              .end(function (err, res) {
                                  if (err) {
                                      return done(err);
                                  }
  
                                  var putBody = res.body;
                                  res.headers['x-cache-invalidate'].should.eql('/*');
                                  should.exist(putBody);
                                  putBody.posts[0].page.should.be.ok();
  
                                  testUtils.API.checkResponse(putBody.posts[0], 'post');
                                  done();
                              });
                      });
              });
  
              it('can change a static page to a post', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[5].id + '/'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var jsonResponse = res.body;
  
                          should.exist(jsonResponse);
                          jsonResponse.posts[0].page.should.be.ok();
                          jsonResponse.posts[0].page = false;
  
                          request.put(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[5].id + '/'))
                              .set('Authorization', 'Bearer ' + ownerAccessToken)
                              .send(jsonResponse)
                              .expect('Content-Type', /json/)
                              .expect('Cache-Control', testUtils.cacheRules.private)
                              .expect(200)
                              .end(function (err, res) {
                                  if (err) {
                                      return done(err);
                                  }
  
                                  var putBody = res.body;
  
                                  res.headers['x-cache-invalidate'].should.eql('/*');
                                  should.exist(putBody);
                                  putBody.posts[0].page.should.not.be.ok();
                                  testUtils.API.checkResponse(putBody.posts[0], 'post');
                                  done();
                              });
                      });
              });
  
              it('can\'t edit post with invalid page field', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[5].id + '/'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var jsonResponse = res.body,
                              changedValue = 'invalid';
                          should.exist(jsonResponse);
                          jsonResponse.posts[0].page.should.eql(false);
                          jsonResponse.posts[0].page = changedValue;
  
                          request.put(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[5].id + '/'))
                              .set('Authorization', 'Bearer ' + ownerAccessToken)
                              .send(jsonResponse)
                              .expect('Content-Type', /json/)
                              .expect('Cache-Control', testUtils.cacheRules.private)
                              .expect(422)
                              .end(function (err, res) {
                                  if (err) {
                                      return done(err);
                                  }
  
                                  should.not.exist(res.headers['x-cache-invalidate']);
                                  jsonResponse = res.body;
                                  should.exist(jsonResponse.errors);
                                  testUtils.API.checkResponseValue(jsonResponse.errors[0], ['message', 'errorType', 'context']);
                                  done();
                              });
                      });
              });
  
              it('can\'t edit a post with invalid accesstoken', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var jsonResponse = res.body;
                          request.put(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
                              .set('Authorization', 'Bearer ' + 'invalidtoken')
                              .send(jsonResponse)
                              .expect('Content-Type', /json/)
                              .expect('Cache-Control', testUtils.cacheRules.private)
                              .expect(401)
                              .end(function (err) {
                                  if (err) {
                                      return done(err);
                                  }
  
                                  done();
                              });
                      });
              });
  
              it('throws an error if there is an id mismatch', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var jsonResponse = res.body;
                          should.exist(jsonResponse);
  
                          request.put(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[1].id + '/'))
                              .set('Authorization', 'Bearer ' + ownerAccessToken)
                              .send(jsonResponse)
                              .expect('Content-Type', /json/)
                              .expect('Cache-Control', testUtils.cacheRules.private)
                              .expect(400)
                              .end(function (err, res) {
                                  if (err) {
                                      return done(err);
                                  }
  
                                  done();
                              });
                      });
              });
  
              it('published_at = null', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/?include=tags'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var jsonResponse = res.body,
                              changedValue = 'A title.';
  
                          should.exist(jsonResponse);
                          jsonResponse.posts[0].title = changedValue;
                          jsonResponse.posts[0].published_at = null;
  
                          request.put(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
                              .set('Authorization', 'Bearer ' + ownerAccessToken)
                              .send(jsonResponse)
                              .expect('Content-Type', /json/)
                              .expect('Cache-Control', testUtils.cacheRules.private)
                              .expect(200)
                              .end(function (err, res) {
                                  if (err) {
                                      return done(err);
                                  }
  
                                  var putBody = res.body;
                                  res.headers['x-cache-invalidate'].should.eql('/*');
                                  should.exist(putBody);
                                  should.exist(putBody.posts);
                                  putBody.posts[0].title.should.eql(changedValue);
                                  if (_.isEmpty(putBody.posts[0].published_at)) {
                                      should.fail('null', 'valid date', 'publish_at should not be empty');
                                      done();
                                  }
                                  testUtils.API.checkResponse(putBody.posts[0], 'post');
                                  done();
                              });
                      });
              });
  
              it('check which fields can be modified', function (done) {
                  var existingPostData, modifiedPostData;
  
                  request
                      .get(localUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var jsonResponse = res.body;
                          should.exist(jsonResponse.posts[0]);
                          existingPostData = _.cloneDeep(jsonResponse.posts[0]);
                          modifiedPostData = _.cloneDeep(jsonResponse);
  
                          modifiedPostData.posts[0].created_by = ObjectId.generate();
                          modifiedPostData.posts[0].updated_by = ObjectId.generate();
                          modifiedPostData.posts[0].created_at = moment().add(2, 'days').format();
                          modifiedPostData.posts[0].updated_at = moment().add(2, 'days').format();
  
                          request
                              .put(localUtils.API.getApiQuery('posts/' + modifiedPostData.posts[0].id + '/'))
                              .set('Authorization', 'Bearer ' + ownerAccessToken)
                              .send(modifiedPostData)
                              .expect('Content-Type', /json/)
                              .expect('Cache-Control', testUtils.cacheRules.private)
                              .expect(200)
                              .end(function (err, res) {
                                  if (err) {
                                      return done(err);
                                  }
  
                                  var jsonResponse = res.body;
                                  should.exist(jsonResponse.posts[0]);
  
                                  // We expect that the changed properties aren't changed, they are still the same than before.
                                  jsonResponse.posts[0].created_by.should.eql(existingPostData.created_by);
                                  jsonResponse.posts[0].updated_by.should.eql(existingPostData.updated_by);
                                  jsonResponse.posts[0].created_at.should.eql(existingPostData.created_at);
                                  // `updated_at` is automatically set, but it's not the date we send to override.
                                  jsonResponse.posts[0].updated_at.should.not.eql(modifiedPostData.updated_at);
                                  done();
                              });
                      });
              });
          });
  
          // ## delete
          describe('Delete', function () {
              it('can delete a post', function (done) {
                  var deletePostId = testUtils.DataGenerator.Content.posts[0].id;
  
                  request.del(localUtils.API.getApiQuery('posts/' + deletePostId + '/'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(204)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          res.body.should.be.empty();
                          res.headers['x-cache-invalidate'].should.eql('/*');
  
                          done();
                      });
              });
  
              it('can\'t delete a non existent post', function (done) {
                  request.del(localUtils.API.getApiQuery('posts/' + ObjectId.generate() + '/'))
                      .set('Authorization', 'Bearer ' + ownerAccessToken)
                      .set('Accept', 'application/json')
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(404)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          should.not.exist(res.headers['x-cache-invalidate']);
                          var jsonResponse = res.body;
                          should.exist(jsonResponse);
                          should.exist(jsonResponse.errors);
                          testUtils.API.checkResponseValue(jsonResponse.errors[0], ['message', 'errorType']);
                          done();
                      });
              });
          });
      });
  
      describe('As Author', function () {
          describe('Add', function () {
              it('can add own post', function (done) {
                  var post = {
                      title: 'Freshly added',
                      slug: 'freshly-added',
                      author_id: author.id
                  };
  
                  request.post(localUtils.API.getApiQuery('posts/'))
                      .set('Authorization', 'Bearer ' + authorAccessToken)
                      .send({posts: [post]})
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(201)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var postBody = res.body;
  
                          res.headers['x-cache-invalidate'].should.eql('/p/' + postBody.posts[0].uuid + '/');
                          should.exist(postBody);
  
                          testUtils.API.checkResponse(postBody.posts[0], 'post');
                          done();
                      });
              });
          });
  
          describe('Edit', function () {
              var authorPostId;
  
              before(function () {
                  return testUtils.createPost({
                      post: {
                          title: 'Author\'s test post',
                          slug: 'author-post',
                          author_id: author.id
                      }
                  }).then(function (post) {
                      authorPostId = post.id;
                  });
              });
  
              it('can edit own post', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/' + authorPostId + '/?include=tags'))
                      .set('Authorization', 'Bearer ' + authorAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var jsonResponse = res.body,
                              changedTitle = 'My new Title',
                              changedSlug = 'my-new-slug';
  
                          should.exist(jsonResponse.posts[0]);
                          jsonResponse.posts[0].title = changedTitle;
                          jsonResponse.posts[0].slug = changedSlug;
  
                          request.put(localUtils.API.getApiQuery('posts/' + authorPostId + '/'))
                              .set('Authorization', 'Bearer ' + authorAccessToken)
                              .send(jsonResponse)
                              .expect('Content-Type', /json/)
                              .expect('Cache-Control', testUtils.cacheRules.private)
                              .expect(200)
                              .end(function (err, res) {
                                  if (err) {
                                      return done(err);
                                  }
  
                                  var putBody = res.body;
                                  res.headers['x-cache-invalidate'].should.eql('/*');
                                  should.exist(putBody);
                                  putBody.posts[0].title.should.eql(changedTitle);
                                  putBody.posts[0].slug.should.eql(changedSlug);
  
                                  testUtils.API.checkResponse(putBody.posts[0], 'post');
                                  done();
                              });
                      });
              });
          });
  
          describe('Delete', function () {
              it('can delete own published post', function (done) {
                  testUtils.createPost({
                      post: {
                          title: 'Author\'s test post for deletion',
                          slug: 'author-delete-post-published',
                          author_id: author.id,
                          status: 'published'
                      }
                  }).then(function (post) {
                      request.del(localUtils.API.getApiQuery('posts/' + post.id + '/'))
                          .set('Authorization', 'Bearer ' + authorAccessToken)
                          .expect('Cache-Control', testUtils.cacheRules.private)
                          .expect(204)
                          .end(function (err, res) {
                              if (err) {
                                  return done(err);
                              }
  
                              res.body.should.be.empty();
  
                              done();
                          });
                  });
              });
          });
      });
  
      describe('As Contributor', function () {
          describe('Add', function () {
              it('can add own post', function (done) {
                  var post = {
                      title: 'Freshly added',
                      slug: 'freshly-added',
                      author_id: contributor.id
                  };
  
                  request.post(localUtils.API.getApiQuery('posts/'))
                      .set('Authorization', 'Bearer ' + contributorAccessToken)
                      .send({posts: [post]})
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(201)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var postBody = res.body;
  
                          res.headers['x-cache-invalidate'].should.eql('/p/' + postBody.posts[0].uuid + '/');
                          should.exist(postBody);
  
                          testUtils.API.checkResponse(postBody.posts[0], 'post');
                          done();
                      });
              });
          });
  
          describe('Edit', function () {
              var contributorPostId;
  
              before(function () {
                  return testUtils.createPost({
                      post: {
                          title: 'Contributor\'s test post',
                          slug: 'contributor-post',
                          author_id: contributor.id,
                          status: 'draft'
                      }
                  })
                      .then(function (post) {
                          contributorPostId = post.id;
                      });
              });
  
              it('can edit own post', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/' + contributorPostId + '/?include=tags&status=draft'))
                      .set('Authorization', 'Bearer ' + contributorAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var jsonResponse = res.body,
                              changedTitle = 'My new Title',
                              changedSlug = 'my-new-slug-contributor';
  
                          should.exist(jsonResponse.posts[0]);
                          jsonResponse.posts[0].title = changedTitle;
                          jsonResponse.posts[0].slug = changedSlug;
  
                          request.put(localUtils.API.getApiQuery('posts/' + contributorPostId + '/'))
                              .set('Authorization', 'Bearer ' + contributorAccessToken)
                              .send(jsonResponse)
                              .expect('Content-Type', /json/)
                              .expect('Cache-Control', testUtils.cacheRules.private)
                              .expect(200)
                              .end(function (err, res) {
                                  if (err) {
                                      return done(err);
                                  }
  
                                  var putBody = res.body;
                                  should.exist(putBody);
                                  res.headers['x-cache-invalidate'].should.eql('/p/' + putBody.posts[0].uuid + '/');
                                  putBody.posts[0].title.should.eql(changedTitle);
                                  putBody.posts[0].slug.should.eql(changedSlug);
  
                                  testUtils.API.checkResponse(putBody.posts[0], 'post');
                                  done();
                              });
                      });
              });
  
              it('CANNOT publish a post', function (done) {
                  request.get(localUtils.API.getApiQuery('posts/' + contributorPostId + '/?include=tags&status=draft'))
                      .set('Authorization', 'Bearer ' + contributorAccessToken)
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .end(function (err, res) {
                          if (err) {
                              return done(err);
                          }
  
                          var jsonResponse = res.body,
                              changedStatus = 'published';
  
                          should.exist(jsonResponse.posts[0]);
                          jsonResponse.posts[0].status = changedStatus;
  
                          request.put(localUtils.API.getApiQuery('posts/' + contributorPostId + '/'))
                              .set('Authorization', 'Bearer ' + contributorAccessToken)
                              .send(jsonResponse)
                              .expect('Content-Type', /json/)
                              .expect('Cache-Control', testUtils.cacheRules.private)
                              .expect(403)
                              .end(function (err, res) {
                                  if (err) {
                                      return done(err);
                                  }
  
                                  should.exist(res.body.errors);
                                  res.body.errors[0].errorType.should.eql('NoPermissionError');
                                  done();
                              });
                      });
              });
          });
  
          describe('Delete', function () {
              it('can delete own draft post', function (done) {
                  testUtils.createPost({
                      post: {
                          title: 'Contrbutor\'s test post for deletion',
                          slug: 'contributor-delete-post',
                          author_id: contributor.id,
                          status: 'draft'
                      }
                  }).then(function (post) {
                      request.del(localUtils.API.getApiQuery('posts/' + post.id + '/'))
                          .set('Authorization', 'Bearer ' + contributorAccessToken)
                          .expect('Cache-Control', testUtils.cacheRules.private)
                          .expect(204)
                          .end(function (err, res) {
                              if (err) {
                                  return done(err);
                              }
  
                              res.body.should.be.empty();
  
                              done();
                          });
                  });
              });
  
              it('can\'t delete own published post', function (done) {
                  testUtils.createPost({
                      post: {
                          title: 'Contributor\'s test post for deletion',
                          slug: 'contributor-delete-post-published',
                          author_id: contributor.id,
                          status: 'published'
                      }
                  }).then(function (post) {
                      request.del(localUtils.API.getApiQuery('posts/' + post.id + '/'))
                          .set('Authorization', 'Bearer ' + contributorAccessToken)
                          .expect('Cache-Control', testUtils.cacheRules.private)
                          .expect(403)
                          .end(function (err, res) {
                              if (err) {
                                  return done(err);
                              }
  
                              should.exist(res.body.errors);
                              res.body.errors[0].errorType.should.eql('NoPermissionError');
                              done();
                          });
                  });
              });
          });
      });
  });
  