Add API tests

closes #1189
- added tests
- added request module
- added status codes to API calls
- fixed return values of API calls
- fixed that drafts caused an error when being deleted
- fixed X-Invalidate-Cache headers
- moved testUtils.js to utils/index.js
This commit is contained in:
Sebastian Gierlinger 2013-11-03 18:13:19 +01:00
parent dee054e2c3
commit bb17e1c0e9
28 changed files with 595 additions and 188 deletions

View File

@ -156,4 +156,4 @@ db = {
} }
}; };
module.exports.db = db; module.exports = db;

View File

@ -20,7 +20,7 @@ var Ghost = require('../../ghost'),
settingsObject, settingsObject,
settingsCollection, settingsCollection,
settingsFilter, settingsFilter,
filteredUserAttributes = ['password', 'created_by', 'updated_by']; filteredUserAttributes = ['password', 'created_by', 'updated_by', 'last_login'];
// ## Posts // ## Posts
posts = { posts = {
@ -28,6 +28,7 @@ posts = {
// **takes:** filter / pagination parameters // **takes:** filter / pagination parameters
browse: function browse(options) { browse: function browse(options) {
// **returns:** a promise for a page of posts in a json object // **returns:** a promise for a page of posts in a json object
//return dataProvider.Post.findPage(options); //return dataProvider.Post.findPage(options);
return dataProvider.Post.findPage(options).then(function (result) { return dataProvider.Post.findPage(options).then(function (result) {
@ -57,8 +58,8 @@ posts = {
omitted.user = _.omit(omitted.user, filteredUserAttributes); omitted.user = _.omit(omitted.user, filteredUserAttributes);
return omitted; return omitted;
} }
return when.reject({errorCode: 404, message: 'Post not found'});
return null;
}); });
}, },
@ -68,13 +69,28 @@ posts = {
edit: function edit(postData) { edit: function edit(postData) {
// **returns:** a promise for the resulting post in a json object // **returns:** a promise for the resulting post in a json object
if (!this.user) { if (!this.user) {
return when.reject("You do not have permission to edit this post."); return when.reject({errorCode: 403, message: 'You do not have permission to edit this post.'});
} }
var self = this;
return canThis(this.user).edit.post(postData.id).then(function () { return canThis(self.user).edit.post(postData.id).then(function () {
return dataProvider.Post.edit(postData); return dataProvider.Post.edit(postData).then(function (result) {
if (result) {
var omitted = result.toJSON();
omitted.author = _.omit(omitted.author, filteredUserAttributes);
omitted.user = _.omit(omitted.user, filteredUserAttributes);
return omitted;
}
return when.reject({errorCode: 404, message: 'Post not found'});
}).otherwise(function (error) {
return dataProvider.Post.findOne({id: postData.id, status: 'all'}).then(function (result) {
if (!result) {
return when.reject({errorCode: 404, message: 'Post not found'});
}
return when.reject({message: error.message});
});
});
}, function () { }, function () {
return when.reject("You do not have permission to edit this post."); return when.reject({errorCode: 403, message: 'You do not have permission to edit this post.'});
}); });
}, },
@ -84,13 +100,13 @@ posts = {
add: function add(postData) { add: function add(postData) {
// **returns:** a promise for the resulting post in a json object // **returns:** a promise for the resulting post in a json object
if (!this.user) { if (!this.user) {
return when.reject("You do not have permission to add posts."); return when.reject({errorCode: 403, message: 'You do not have permission to add posts.'});
} }
return canThis(this.user).create.post().then(function () { return canThis(this.user).create.post().then(function () {
return dataProvider.Post.add(postData); return dataProvider.Post.add(postData);
}, function () { }, function () {
return when.reject("You do not have permission to add posts."); return when.reject({errorCode: 403, message: 'You do not have permission to add posts.'});
}); });
}, },
@ -100,11 +116,11 @@ posts = {
destroy: function destroy(args) { destroy: function destroy(args) {
// **returns:** a promise for a json response with the id of the deleted post // **returns:** a promise for a json response with the id of the deleted post
if (!this.user) { if (!this.user) {
return when.reject("You do not have permission to remove posts."); return when.reject({errorCode: 403, message: 'You do not have permission to remove posts.'});
} }
return canThis(this.user).remove.post(args.id).then(function () { return canThis(this.user).remove.post(args.id).then(function () {
return when(posts.read({id : args.id})).then(function (result) { return when(posts.read({id : args.id, status: 'all'})).then(function (result) {
return dataProvider.Post.destroy(args.id).then(function () { return dataProvider.Post.destroy(args.id).then(function () {
var deletedObj = {}; var deletedObj = {};
deletedObj.id = result.id; deletedObj.id = result.id;
@ -113,7 +129,7 @@ posts = {
}); });
}); });
}, function () { }, function () {
return when.reject("You do not have permission to remove posts."); return when.reject({errorCode: 403, message: 'You do not have permission to remove posts.'});
}); });
} }
}; };
@ -157,7 +173,7 @@ users = {
return omitted; return omitted;
} }
return null; return when.reject({errorCode: 404, message: 'User not found'});
}); });
}, },
@ -167,7 +183,13 @@ users = {
edit: function edit(userData) { edit: function edit(userData) {
// **returns:** a promise for the resulting user in a json object // **returns:** a promise for the resulting user in a json object
userData.id = this.user; userData.id = this.user;
return dataProvider.User.edit(userData); return dataProvider.User.edit(userData).then(function (result) {
if (result) {
var omitted = _.omit(result.toJSON(), filteredUserAttributes);
return omitted;
}
return when.reject({errorCode: 404, message: 'User not found'});
});
}, },
// #### Add // #### Add
@ -289,6 +311,7 @@ settings = {
// **returns:** a promise for a settings json object // **returns:** a promise for a settings json object
if (ghost.settings()) { if (ghost.settings()) {
return when(ghost.settings()).then(function (settings) { return when(ghost.settings()).then(function (settings) {
//TODO: omit where type==core
return settingsObject(settingsFilter(settings, options.type)); return settingsObject(settingsFilter(settings, options.type));
}, errors.logAndThrowError); }, errors.logAndThrowError);
} }
@ -305,7 +328,7 @@ settings = {
if (ghost.settings()) { if (ghost.settings()) {
return when(ghost.settings()[options.key]).then(function (setting) { return when(ghost.settings()[options.key]).then(function (setting) {
if (!setting) { if (!setting) {
return when.reject("Unable to find setting: " + options.key); return when.reject({errorCode: 404, message: 'Unable to find setting: ' + options.key});
} }
var res = {}; var res = {};
res.key = options.key; res.key = options.key;
@ -333,11 +356,18 @@ settings = {
ghost.updateSettingsCache(settings); ghost.updateSettingsCache(settings);
return settingsObject(settingsFilter(ghost.settings(), type)); return settingsObject(settingsFilter(ghost.settings(), type));
}); });
}, errors.logAndThrowError); }).otherwise(function (error) {
return dataProvider.Settings.read(key.key).then(function (result) {
if (!result) {
return when.reject({errorCode: 404, message: 'Unable to find setting: ' + key});
}
return when.reject({message: error.message});
});
});
} }
return dataProvider.Settings.read(key).then(function (setting) { return dataProvider.Settings.read(key).then(function (setting) {
if (!setting) { if (!setting) {
return when.reject("Unable to find setting: " + key); return when.reject({errorCode: 404, message: 'Unable to find setting: ' + key});
} }
if (!_.isString(value)) { if (!_.isString(value)) {
value = JSON.stringify(value); value = JSON.stringify(value);
@ -356,20 +386,18 @@ settings = {
function invalidateCache(req, res, result) { function invalidateCache(req, res, result) {
var parsedUrl = req._parsedUrl.pathname.replace(/\/$/, '').split('/'), var parsedUrl = req._parsedUrl.pathname.replace(/\/$/, '').split('/'),
method = req.method, method = req.method,
endpoint = parsedUrl[3], endpoint = parsedUrl[4],
id = parsedUrl[4], id = parsedUrl[5],
cacheInvalidate; cacheInvalidate,
jsonResult = result.toJSON ? result.toJSON() : result;
if (method === 'POST' || method === 'PUT' || method === 'DELETE') { if (method === 'POST' || method === 'PUT' || method === 'DELETE') {
if (endpoint === 'settings' || endpoint === 'users') { if (endpoint === 'settings' || endpoint === 'users') {
cacheInvalidate = "/*"; cacheInvalidate = "/*";
} else if (endpoint === 'posts') { } else if (endpoint === 'posts') {
cacheInvalidate = "/, /page/*, /rss/, /rss/*"; cacheInvalidate = "/, /page/*, /rss/, /rss/*";
if (id) { if (id && jsonResult.slug) {
if (result.toJSON) { cacheInvalidate += ', /' + jsonResult.slug + '/';
cacheInvalidate += ', /' + result.toJSON().slug + '/';
} else {
cacheInvalidate += ', /' + result.slug + '/';
}
} }
} }
if (cacheInvalidate) { if (cacheInvalidate) {
@ -394,8 +422,9 @@ requestHandler = function (apiMethod) {
invalidateCache(req, res, result); invalidateCache(req, res, result);
res.json(result || {}); res.json(result || {});
}, function (error) { }, function (error) {
error = {error: _.isString(error) ? error : (_.isObject(error) ? error.message : 'Unknown API Error')}; var errorCode = error.errorCode || 500,
res.json(400, error); errorMsg = {error: _.isString(error) ? error : (_.isObject(error) ? error.message : 'Unknown API Error')};
res.json(errorCode, errorMsg);
}); });
}; };
}; };
@ -406,5 +435,5 @@ module.exports.users = users;
module.exports.tags = tags; module.exports.tags = tags;
module.exports.notifications = notifications; module.exports.notifications = notifications;
module.exports.settings = settings; module.exports.settings = settings;
module.exports.db = db.db; module.exports.db = db;
module.exports.requestHandler = requestHandler; module.exports.requestHandler = requestHandler;

View File

@ -341,9 +341,19 @@ Post = ghostBookshelf.Model.extend({
}, },
add: function (newPostData, options) { add: function (newPostData, options) {
return ghostBookshelf.Model.add.call(this, newPostData, options).tap(function (post) { var self = this;
return ghostBookshelf.Model.add.call(this, newPostData, options).then(function (post) {
// associated models can't be created until the post has an ID, so run this after // associated models can't be created until the post has an ID, so run this after
return post.updateTags(newPostData.tags); return when(post.updateTags(newPostData.tags)).then(function () {
return self.findOne({status: 'all', id: post.id});
});
});
},
edit: function (editedPost, options) {
var self = this;
return ghostBookshelf.Model.edit.call(this, editedPost, options).then(function (editedObj) {
return self.findOne({status: 'all', id: editedObj.id});
}); });
}, },
destroy: function (_identifier, options) { destroy: function (_identifier, options) {

View File

@ -28,8 +28,8 @@ User = ghostBookshelf.Model.extend({
permittedAttributes: [ permittedAttributes: [
'id', 'uuid', 'name', 'slug', 'password', 'email', 'image', 'cover', 'bio', 'website', 'location', 'id', 'uuid', 'name', 'slug', 'password', 'email', 'image', 'cover', 'bio', 'website', 'location',
'accessibility', 'status', 'language', 'meta_title', 'meta_description', 'created_at', 'created_by', 'accessibility', 'status', 'language', 'meta_title', 'meta_description', 'last_login', 'created_at',
'updated_at', 'updated_by' 'created_by', 'updated_at', 'updated_by'
], ],
validate: function () { validate: function () {

View File

@ -1,5 +1,5 @@
/*globals describe, it, before, beforeEach, afterEach */ /*globals describe, it, before, beforeEach, afterEach */
var testUtils = require('../unit/testUtils'), var testUtils = require('../unit/utils'),
should = require('should'), should = require('should'),
errors = require('../../server/errorHandling'), errors = require('../../server/errorHandling'),

View File

@ -1,5 +1,5 @@
/*globals describe, before, beforeEach, afterEach, it */ /*globals describe, before, beforeEach, afterEach, it */
var testUtils = require('../unit/testUtils'), var testUtils = require('../unit/utils'),
should = require('should'), should = require('should'),
_ = require('underscore'), _ = require('underscore'),
when = require('when'), when = require('when'),
@ -310,8 +310,7 @@ describe('Post Model', function () {
}).then(function (newPost) { }).then(function (newPost) {
should.exist(newPost); should.exist(newPost);
newPost.get('published_at').should.equal(previousPublishedAtDate.getTime());
newPost.get('published_at').should.equal(previousPublishedAtDate);
done(); done();

View File

@ -1,5 +1,5 @@
/*globals describe, it, before, beforeEach, afterEach */ /*globals describe, it, before, beforeEach, afterEach */
var testUtils = require('../unit/testUtils'), var testUtils = require('../unit/utils'),
should = require('should'), should = require('should'),
errors = require('../../server/errorHandling'), errors = require('../../server/errorHandling'),

View File

@ -1,5 +1,5 @@
/*globals describe, before, beforeEach, afterEach, it*/ /*globals describe, before, beforeEach, afterEach, it*/
var testUtils = require('../unit/testUtils'), var testUtils = require('../unit/utils'),
should = require('should'), should = require('should'),
_ = require("underscore"), _ = require("underscore"),

View File

@ -1,5 +1,5 @@
/*globals describe, before, beforeEach, afterEach, it */ /*globals describe, before, beforeEach, afterEach, it */
var testUtils = require('../unit/testUtils'), var testUtils = require('../unit/utils'),
_ = require("underscore"), _ = require("underscore"),
when = require('when'), when = require('when'),
sequence = require('when/sequence'), sequence = require('when/sequence'),

View File

@ -1,5 +1,5 @@
/*globals describe, before, beforeEach, afterEach, it*/ /*globals describe, before, beforeEach, afterEach, it*/
var testUtils = require('../unit/testUtils'), var testUtils = require('../unit/utils'),
should = require('should'), should = require('should'),
when = require('when'), when = require('when'),
_ = require('underscore'), _ = require('underscore'),

View File

@ -1,12 +1,21 @@
/*globals describe, before, beforeEach, afterEach, it */ /*globals describe, before, beforeEach, afterEach, it */
var testUtils = require('./testUtils'), var testUtils = require('./utils'),
should = require('should'), should = require('should'),
_ = require('underscore'); _ = require('underscore'),
request = require('request'),
expectedPostsProperties = ['posts', 'page', 'limit', 'pages', 'total'],
expectedPostProperties = ['id', 'uuid', 'title', 'slug', 'markdown', 'html', 'meta_title', 'meta_description',
'featured', 'image', 'status', 'language', 'author_id', 'created_at', 'created_by', 'updated_at', 'updated_by',
'published_at', 'published_by', 'page', 'author', 'user', 'tags'];
request = request.defaults({jar:true})
describe('Post API', function () { describe('Post API', function () {
var user = testUtils.DataGenerator.forModel.users[0], var user = testUtils.DataGenerator.forModel.users[0],
authCookie; csrfToken = '';
before(function (done) { before(function (done) {
testUtils.clearData() testUtils.clearData()
@ -16,17 +25,22 @@ describe('Post API', function () {
}); });
beforeEach(function (done) { beforeEach(function (done) {
this.timeout(5000);
testUtils.initData() testUtils.initData()
.then(function () { .then(function () {
return testUtils.insertDefaultFixtures(); testUtils.insertDefaultFixtures();
}) })
.then(function () { .then(function () {
return testUtils.API.login(user.email, user.password); // do a get request to get the CSRF token first
}) request.get(testUtils.API.getSigninURL(), function (error, response, body) {
.then(function (authResponse) { var pattern_meta = /<meta.*?name="csrf-param".*?content="(.*?)".*?>/i;
authCookie = authResponse; pattern_meta.should.exist;
csrfToken = body.match(pattern_meta)[1];
done(); request.post({uri:testUtils.API.getSigninURL(),
headers: {'X-CSRF-Token': csrfToken}}, function (error, response, body) {
done();
}).form({email: user.email, password: user.password});
});
}, done); }, done);
}); });
@ -36,13 +50,170 @@ describe('Post API', function () {
}, done); }, done);
}); });
// it('can retrieve a post', function (done) { it('can retrieve all posts', function (done) {
// testUtils.API.get(testUtils.API.ApiRouteBase + 'posts/?status=all', authCookie).then(function (result) { request.get(testUtils.API.getApiURL('posts/'), function (error, response, body) {
// should.exist(result); response.should.have.status(200);
// should.exist(result.response); response.should.be.json;
// result.response.posts.length.should.be.above(1); var jsonResponse = JSON.parse(body);
// done(); jsonResponse.posts.should.exist;
// }).otherwise(done); testUtils.API.checkResponse (jsonResponse, expectedPostsProperties);
// }); jsonResponse.posts.should.have.length(5);
testUtils.API.checkResponse (jsonResponse.posts[0], expectedPostProperties);
done();
});
});
it('can retrieve a post', function (done) {
request.get(testUtils.API.getApiURL('posts/1/'), function (error, response, body) {
response.should.have.status(200);
response.should.be.json;
var jsonResponse = JSON.parse(body);
jsonResponse.should.exist;
testUtils.API.checkResponse (jsonResponse, expectedPostProperties);
done();
});
});
it('can create a new draft, publish post, update post', function (done) {
var newTitle = 'My Post',
changedTitle = 'My Post changed',
publishedState = 'published',
newPost = {status:'draft', title:newTitle, markdown:'my post'};
request.post({uri: testUtils.API.getApiURL('posts/'),
headers: {'X-CSRF-Token': csrfToken},
json: newPost}, function (error, response, draftPost) {
response.should.have.status(200);
response.should.be.json;
draftPost.should.exist;
draftPost.title.should.eql(newTitle);
draftPost.status = publishedState;
testUtils.API.checkResponse (draftPost, expectedPostProperties);
request.put({uri: testUtils.API.getApiURL('posts/' + draftPost.id + '/'),
headers: {'X-CSRF-Token': csrfToken},
json: draftPost}, function (error, response, publishedPost) {
response.should.have.status(200);
response.should.be.json;
publishedPost.should.exist;
publishedPost.title.should.eql(newTitle);
publishedPost.status.should.eql(publishedState);
testUtils.API.checkResponse (publishedPost, expectedPostProperties);
request.put({uri: testUtils.API.getApiURL('posts/' + publishedPost.id + '/'),
headers: {'X-CSRF-Token': csrfToken},
json: publishedPost}, function (error, response, updatedPost) {
response.should.have.status(200);
response.should.be.json;
updatedPost.should.exist;
updatedPost.title.should.eql(newTitle);
testUtils.API.checkResponse (updatedPost, expectedPostProperties);
done();
});
});
});
});
it('can delete a post', function (done) {
var deletePostId = 1;
request.del({uri: testUtils.API.getApiURL('posts/' + deletePostId +'/'),
headers: {'X-CSRF-Token': csrfToken}}, function (error, response, body) {
response.should.have.status(200);
response.should.be.json;
var jsonResponse = JSON.parse(body);
jsonResponse.should.exist;
testUtils.API.checkResponse (jsonResponse, ['id', 'slug']);
jsonResponse.id.should.eql(deletePostId);
done();
});
});
it('can\'t delete a non existent post', function (done) {
request.del({uri: testUtils.API.getApiURL('posts/99/'),
headers: {'X-CSRF-Token': csrfToken}}, function (error, response, body) {
response.should.have.status(404);
response.should.be.json;
var jsonResponse = JSON.parse(body);
jsonResponse.should.exist;
testUtils.API.checkResponse (jsonResponse, ['error']);
done();
});
});
it('can delete a new draft', function (done) {
var newTitle = 'My Post',
publishedState = 'draft',
newPost = {status: publishedState, title: newTitle, markdown: 'my post'};
request.post({uri: testUtils.API.getApiURL('posts/'),
headers: {'X-CSRF-Token': csrfToken},
json: newPost}, function (error, response, draftPost) {
response.should.have.status(200);
response.should.be.json;
draftPost.should.exist;
draftPost.title.should.eql(newTitle);
draftPost.status = publishedState;
testUtils.API.checkResponse (draftPost, expectedPostProperties);
request.del({uri: testUtils.API.getApiURL('posts/' + draftPost.id + '/'),
headers: {'X-CSRF-Token': csrfToken}}, function (error, response, body) {
response.should.have.status(200);
response.should.be.json;
var jsonResponse = JSON.parse(body);
jsonResponse.should.exist;
testUtils.API.checkResponse (jsonResponse, ['id', 'slug']);
done();
});
});
});
it('can\'t retrieve non existent post', function (done) {
request.get(testUtils.API.getApiURL('posts/99/'), function (error, response, body) {
response.should.have.status(404);
response.should.be.json;
var jsonResponse = JSON.parse(body);
jsonResponse.should.exist;
testUtils.API.checkResponse (jsonResponse, ['error']);
done();
});
});
it('can edit a post', function (done) {
request.get(testUtils.API.getApiURL('posts/1/'), function (error, response, body) {
var jsonResponse = JSON.parse(body),
changedValue = 'My new Title';
jsonResponse.should.exist;
//jsonResponse.websiteshould.be.empty;
jsonResponse.title = changedValue;
request.put({uri: testUtils.API.getApiURL('posts/1/'),
headers: {'X-CSRF-Token': csrfToken},
json: jsonResponse}, function (error, response, putBody) {
response.should.have.status(200);
response.should.be.json;
putBody.should.exist;
putBody.title.should.eql(changedValue);
testUtils.API.checkResponse (putBody, expectedPostProperties);
done();
});
});
});
it('can\'t edit non existent post', function (done) {
request.get(testUtils.API.getApiURL('posts/1/'), function (error, response, body) {
var jsonResponse = JSON.parse(body),
changedValue = 'My new Title';
jsonResponse.title.exist;
jsonResponse.testvalue = changedValue;
jsonResponse.id = 99;
request.put({uri: testUtils.API.getApiURL('posts/99/'),
headers: {'X-CSRF-Token': csrfToken},
json: jsonResponse}, function (error, response, putBody) {
response.should.have.status(404);
response.should.be.json;
testUtils.API.checkResponse (putBody, ['error']);
done();
});
});
});
}); });

View File

@ -0,0 +1,123 @@
/*globals describe, before, beforeEach, afterEach, it */
var testUtils = require('./utils'),
should = require('should'),
_ = require('underscore'),
request = require('request'),
// TODO: remove databaseVersion
expectedProperties = ['databaseVersion', 'title', 'description', 'email', 'logo', 'cover', 'defaultLang',
'postsPerPage', 'forceI18n', 'activeTheme', 'activePlugins', 'installedPlugins', 'availableThemes'];
request = request.defaults({jar:true})
describe('Settings API', function () {
var user = testUtils.DataGenerator.forModel.users[0],
csrfToken = '';
before(function (done) {
testUtils.clearData()
.then(function () {
done();
}, done);
});
beforeEach(function (done) {
this.timeout(5000);
testUtils.initData()
.then(function () {
testUtils.insertDefaultFixtures();
})
.then(function () {
// do a get request to get the CSRF token first
request.get(testUtils.API.getSigninURL(), function (error, response, body) {
var pattern_meta = /<meta.*?name="csrf-param".*?content="(.*?)".*?>/i;
pattern_meta.should.exist;
csrfToken = body.match(pattern_meta)[1];
request.post({uri:testUtils.API.getSigninURL(),
headers: {'X-CSRF-Token': csrfToken}}, function (error, response, body) {
done();
}).form({email: user.email, password: user.password});
});
}, done);
});
afterEach(function (done) {
testUtils.clearData().then(function () {
done();
}, done);
});
// TODO: currently includes values of type=core
it('can retrieve all settings', function (done) {
request.get(testUtils.API.getApiURL('settings/'), function (error, response, body) {
response.should.have.status(200);
response.should.be.json;
var jsonResponse = JSON.parse(body);
jsonResponse.should.exist;
testUtils.API.checkResponse (jsonResponse, expectedProperties);
done();
});
});
it('can retrieve a setting', function (done) {
request.get(testUtils.API.getApiURL('settings/title/'), function (error, response, body) {
response.should.have.status(200);
response.should.be.json;
var jsonResponse = JSON.parse(body);
jsonResponse.should.exist;
testUtils.API.checkResponse (jsonResponse, ['key','value']);
jsonResponse.key.should.eql('title');
done();
});
});
it('can\'t retrieve non existent setting', function (done) {
request.get(testUtils.API.getApiURL('settings/testsetting/'), function (error, response, body) {
response.should.have.status(404);
response.should.be.json;
var jsonResponse = JSON.parse(body);
jsonResponse.should.exist;
testUtils.API.checkResponse (jsonResponse, ['error']);
done();
});
});
it('can edit settings', function (done) {
request.get(testUtils.API.getApiURL('settings'), function (error, response, body) {
var jsonResponse = JSON.parse(body),
changedValue = 'Ghost changed';
jsonResponse.should.exist;
jsonResponse.title = changedValue;
request.put({uri: testUtils.API.getApiURL('settings/'),
headers: {'X-CSRF-Token': csrfToken},
json: jsonResponse}, function (error, response, putBody) {
response.should.have.status(200);
response.should.be.json;
putBody.should.exist;
putBody.title.should.eql(changedValue);
testUtils.API.checkResponse (putBody, expectedProperties);
done();
});
});
});
it('can\'t edit non existent setting', function (done) {
request.get(testUtils.API.getApiURL('settings'), function (error, response, body) {
var jsonResponse = JSON.parse(body),
newValue = 'new value';
jsonResponse.should.exist;
jsonResponse.testvalue = newValue;
request.put({uri: testUtils.API.getApiURL('settings/'),
headers: {'X-CSRF-Token': csrfToken},
json: jsonResponse}, function (error, response, putBody) {
response.should.have.status(404);
response.should.be.json;
testUtils.API.checkResponse (putBody, ['error']);
done();
});
});
});
});

View File

@ -0,0 +1,60 @@
/*globals describe, before, beforeEach, afterEach, it */
var testUtils = require('./utils'),
should = require('should'),
_ = require('underscore'),
request = require('request'),
expectedProperties = ['id', 'uuid', 'name', 'slug', 'description', 'parent_id',
'meta_title', 'meta_description', 'created_at', 'created_by', 'updated_at', 'updated_by'];
request = request.defaults({jar:true})
describe('Tag API', function () {
var user = testUtils.DataGenerator.forModel.users[0],
csrfToken = '';
before(function (done) {
testUtils.clearData()
.then(function () {
done();
}, done);
});
beforeEach(function (done) {
this.timeout(5000);
testUtils.initData()
.then(function () {
testUtils.insertDefaultFixtures();
})
.then(function () {
// do a get request to get the CSRF token first
request.get(testUtils.API.getSigninURL(), function (error, response, body) {
var pattern_meta = /<meta.*?name="csrf-param".*?content="(.*?)".*?>/i;
pattern_meta.should.exist;
csrfToken = body.match(pattern_meta)[1];
request.post({uri:testUtils.API.getSigninURL(),
headers: {'X-CSRF-Token': csrfToken}}, function (error, response, body) {
done();
}).form({email: user.email, password: user.password});
});
}, done);
});
afterEach(function (done) {
testUtils.clearData().then(function () {
done();
}, done);
});
it('can retrieve all tags', function (done) {
request.get(testUtils.API.getApiURL('tags/'), function (error, response, body) {
response.should.have.status(200);
response.should.be.json;
var jsonResponse = JSON.parse(body);
jsonResponse.should.exist;
jsonResponse.should.have.length(5);
testUtils.API.checkResponse (jsonResponse[0], expectedProperties);
done();
});
});
});

View File

@ -0,0 +1,106 @@
/*globals describe, before, beforeEach, afterEach, it */
var testUtils = require('./utils'),
should = require('should'),
_ = require('underscore'),
request = require('request'),
expectedProperties = ['id', 'uuid', 'name', 'slug', 'email', 'image', 'cover', 'bio', 'website',
'location', 'accessibility', 'status', 'language', 'meta_title', 'meta_description',
'created_at', 'updated_at'];
request = request.defaults({jar:true})
describe('User API', function () {
var user = testUtils.DataGenerator.forModel.users[0],
csrfToken = '';
before(function (done) {
testUtils.clearData()
.then(function () {
done();
}, done);
});
beforeEach(function (done) {
this.timeout(5000);
testUtils.initData()
.then(function () {
testUtils.insertDefaultFixtures();
})
.then(function () {
// do a get request to get the CSRF token first
request.get(testUtils.API.getSigninURL(), function (error, response, body) {
var pattern_meta = /<meta.*?name="csrf-param".*?content="(.*?)".*?>/i;
pattern_meta.should.exist;
csrfToken = body.match(pattern_meta)[1];
request.post({uri:testUtils.API.getSigninURL(),
headers: {'X-CSRF-Token': csrfToken}}, function (error, response, body) {
done();
}).form({email: user.email, password: user.password});
});
}, done);
});
afterEach(function (done) {
testUtils.clearData().then(function () {
done();
}, done);
});
it('can retrieve all users', function (done) {
request.get(testUtils.API.getApiURL('users/'), function (error, response, body) {
response.should.have.status(200);
response.should.be.json;
var jsonResponse = JSON.parse(body);
jsonResponse[0].should.exist;
testUtils.API.checkResponse (jsonResponse[0], expectedProperties);
done();
});
});
it('can retrieve a user', function (done) {
request.get(testUtils.API.getApiURL('users/me/'), function (error, response, body) {
response.should.have.status(200);
response.should.be.json;
var jsonResponse = JSON.parse(body);
jsonResponse.should.exist;
testUtils.API.checkResponse (jsonResponse, expectedProperties);
done();
});
});
it('can\'t retrieve non existent user', function (done) {
request.get(testUtils.API.getApiURL('users/99/'), function (error, response, body) {
response.should.have.status(404);
response.should.be.json;
var jsonResponse = JSON.parse(body);
jsonResponse.should.exist;
testUtils.API.checkResponse (jsonResponse, ['error']);
done();
});
});
it('can edit a user', function (done) {
request.get(testUtils.API.getApiURL('users/me/'), function (error, response, body) {
var jsonResponse = JSON.parse(body),
changedValue = 'joe-bloggs.ghost.org';
jsonResponse.should.exist;
jsonResponse.website = changedValue;
request.put({uri: testUtils.API.getApiURL('users/me/'),
headers: {'X-CSRF-Token': csrfToken},
json: jsonResponse}, function (error, response, putBody) {
response.should.have.status(200);
response.should.be.json;
putBody.should.exist;
putBody.website.should.eql(changedValue);
testUtils.API.checkResponse (putBody, expectedProperties);
done();
});
});
});
});

View File

@ -6,7 +6,7 @@
*/ */
/*globals describe, it */ /*globals describe, it */
var testUtils = require('./testUtils'), var testUtils = require('./utils'),
should = require('should'), should = require('should'),
// Stuff we are testing // Stuff we are testing

View File

@ -5,7 +5,7 @@
*/ */
/*globals describe, it */ /*globals describe, it */
var testUtils = require('./testUtils'), var testUtils = require('./utils'),
should = require('should'), should = require('should'),
// Stuff we are testing // Stuff we are testing

View File

@ -1,5 +1,5 @@
/*globals describe, beforeEach, it*/ /*globals describe, beforeEach, it*/
var testUtils = require('./testUtils'), var testUtils = require('./utils'),
should = require('should'), should = require('should'),
when = require('when'), when = require('when'),
sinon = require('sinon'), sinon = require('sinon'),

View File

@ -1,5 +1,5 @@
/*globals describe, before, beforeEach, afterEach, it*/ /*globals describe, before, beforeEach, afterEach, it*/
var testUtils = require('./testUtils'), var testUtils = require('./utils'),
should = require('should'), should = require('should'),
sinon = require('sinon'), sinon = require('sinon'),
when = require('when'), when = require('when'),

View File

@ -1,5 +1,5 @@
/*globals describe, before, beforeEach, it*/ /*globals describe, before, beforeEach, it*/
var testUtils = require('./testUtils'), var testUtils = require('./utils'),
should = require('should'), should = require('should'),
sinon = require('sinon'), sinon = require('sinon'),
when = require('when'), when = require('when'),

View File

@ -1,5 +1,5 @@
/*globals describe, beforeEach, it*/ /*globals describe, beforeEach, it*/
var testUtils = require('./testUtils'), var testUtils = require('./utils'),
should = require('should'), should = require('should'),
sinon = require('sinon'), sinon = require('sinon'),
when = require('when'), when = require('when'),

View File

@ -1,5 +1,5 @@
/*globals describe, beforeEach, afterEach, it*/ /*globals describe, beforeEach, afterEach, it*/
var testUtils = require('./testUtils'), var testUtils = require('./utils'),
should = require('should'), should = require('should'),
sinon = require('sinon'), sinon = require('sinon'),
when = require('when'), when = require('when'),

View File

@ -1,5 +1,5 @@
/*globals describe, before, beforeEach, afterEach, it*/ /*globals describe, before, beforeEach, afterEach, it*/
var testUtils = require('./testUtils'), var testUtils = require('./utils'),
should = require('should'), should = require('should'),
sinon = require('sinon'), sinon = require('sinon'),
when = require('when'), when = require('when'),

View File

@ -1,5 +1,5 @@
/*globals describe, beforeEach, afterEach, before, it*/ /*globals describe, beforeEach, afterEach, before, it*/
var testUtils = require('./testUtils'), var testUtils = require('./utils'),
should = require('should'), should = require('should'),
sinon = require('sinon'), sinon = require('sinon'),
_ = require("underscore"), _ = require("underscore"),

View File

@ -1,5 +1,5 @@
/*globals describe, beforeEach, it*/ /*globals describe, beforeEach, it*/
var testUtils = require('./testUtils'), var testUtils = require('./utils'),
should = require('should'), should = require('should'),
sinon = require('sinon'), sinon = require('sinon'),
when = require('when'), when = require('when'),

View File

@ -4,7 +4,7 @@
*/ */
/*globals describe, it */ /*globals describe, it */
var testUtils = require('./testUtils'), var testUtils = require('./utils'),
should = require('should'), should = require('should'),
// Stuff we are testing // Stuff we are testing

View File

@ -1,118 +1,26 @@
var _ = require('underscore'), var _ = require('underscore'),
when = require('when'), url = require('url'),
http = require('http'), ApiRouteBase = '/ghost/api/v0.1/',
HttpMethods, host = 'localhost',
ApiRouteBase = '/ghost/api/v0.1/'; port = '2369';
schema = "http://"
HttpMethods = { function getApiURL (route) {
GET: 'GET', var baseURL = url.resolve(schema + host + ':' + port, ApiRouteBase);
POST: 'POST', return url.resolve(baseURL, route);
PUT: 'PUT', }
DELETE: 'DELETE' function getSigninURL () {
}; return url.resolve(schema + host + ':' + port, 'ghost/signin/');
function createRequest(httpMethod, overrides) {
return _.defaults(overrides, {
'host': 'localhost',
'port': '2369',
'method': httpMethod
});
} }
function post(route, data, authCookie) { // make sure the API only returns expected properties only
var jsonData = JSON.stringify(data), function checkResponse (jsonResponse, expectedProperties) {
options = createRequest(HttpMethods.POST, { Object.keys(jsonResponse).length.should.eql(expectedProperties.length);
path: route, for(var i=0; i<expectedProperties.length; i = i + 1) {
headers: { jsonResponse.should.have.property(expectedProperties[i]);
'Content-Type': 'application/json',
'Content-Length': jsonData.length
}
}),
req,
response = '',
deferred = when.defer();
if (authCookie) {
options.headers['Cookie'] = authCookie;
} }
req = http.request(options, function (res) {
res.setEncoding('utf-8');
if (res.statusCode === 401) {
return deferred.resolver.reject(new Error('401 Unauthorized.'));
}
res.on('data', function (chunk) {
response += chunk;
});
res.on('end', function () {
deferred.resolver.resolve({
headers: res.headers,
response: JSON.parse(response)
});
});
}).on('error', deferred.resolver.reject);
req.write(jsonData);
req.end();
return deferred.promise;
} }
function get(route, authCookie) { module.exports.getApiURL = getApiURL;
var options = createRequest(HttpMethods.GET, { module.exports.getSigninURL = getSigninURL;
path: route, module.exports.checkResponse = checkResponse;
headers: {}
}),
response = '',
deferred = when.defer();
if (authCookie) {
options.headers['Cookie'] = authCookie;
}
http.get(options, function (res) {
res.setEncoding('utf-8');
if (res.statusCode === 401) {
return deferred.resolver.reject(new Error('401 Unauthorized.'));
}
res.on('data', function (chunk) {
response += chunk;
});
res.on('end', function () {
deferred.resolve({
headers: res.headers,
response: JSON.parse(response)
});
});
}).on('error', deferred.resolver.reject);
return deferred.promise;
}
function login(email, password) {
var data = {
email: email,
password: password
};
return post('/ghost/signin/', data).then(function (response) {
return response.headers['set-cookie'];
});
}
module.exports = {
HttpMethods: HttpMethods,
ApiRouteBase: ApiRouteBase,
createRequest: createRequest,
post: post,
get: get,
login: login
};

View File

@ -1,9 +1,9 @@
var knex = require('../../server/models/base').knex, var knex = require('../../../server/models/base').knex,
when = require('when'), when = require('when'),
migration = require("../../server/data/migration/"), migration = require("../../../server/data/migration/"),
Settings = require('../../server/models/settings').Settings, Settings = require('../../../server/models/settings').Settings,
DataGenerator = require('./fixtures/data-generator'), DataGenerator = require('../fixtures/data-generator'),
API = require('./utils/api'); API = require('./api');
function initData() { function initData() {
return migration.init(); return migration.init();

View File

@ -73,6 +73,7 @@
"matchdep": "~0.3.0", "matchdep": "~0.3.0",
"mocha": "~1.13.0", "mocha": "~1.13.0",
"should": "~2.0.2", "should": "~2.0.2",
"sinon": "~1.7.3" "sinon": "~1.7.3",
"request": "~2.27.0"
} }
} }