mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Merge pull request #1247 from sebgie/bookshelf-session
Replace cookieSession with session
This commit is contained in:
commit
53af625c49
23 changed files with 394 additions and 161 deletions
23
Gruntfile.js
23
Gruntfile.js
|
@ -197,14 +197,10 @@ var path = require('path'),
|
|||
timeout: '15000'
|
||||
},
|
||||
|
||||
all: {
|
||||
unit: {
|
||||
src: ['core/test/unit/**/*_spec.js']
|
||||
},
|
||||
|
||||
api: {
|
||||
src: ['core/test/integration/**/api*_spec.js']
|
||||
},
|
||||
|
||||
model: {
|
||||
src: ['core/test/integration/**/model*_spec.js']
|
||||
},
|
||||
|
@ -233,10 +229,11 @@ var path = require('path'),
|
|||
},
|
||||
|
||||
integration: {
|
||||
src: [
|
||||
'core/test/integration/**/model*_spec.js',
|
||||
'core/test/integration/**/api*_spec.js'
|
||||
]
|
||||
src: ['core/test/integration/**/model*_spec.js']
|
||||
},
|
||||
|
||||
api: {
|
||||
src: ['core/test/functional/api/*_test.js']
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -872,13 +869,15 @@ var path = require('path'),
|
|||
|
||||
// ## Running the test suites
|
||||
|
||||
grunt.registerTask('test-unit', 'Run unit tests', ['clean:test', 'setTestEnv', 'loadConfig', 'express:test', 'mochacli:all', 'express:test:stop']);
|
||||
grunt.registerTask('test-unit', 'Run unit tests', ['clean:test', 'setTestEnv', 'loadConfig', 'mochacli:unit']);
|
||||
|
||||
grunt.registerTask('test-integration', 'Run integration tests', ['clean:test', 'setTestEnv', 'loadConfig', 'express:test', 'mochacli:integration', 'express:test:stop']);
|
||||
grunt.registerTask('test-integration', 'Run integration tests', ['clean:test', 'setTestEnv', 'loadConfig', 'mochacli:integration']);
|
||||
|
||||
grunt.registerTask('test-functional', 'Run casperjs tests only', ['clean:test', 'setTestEnv', 'loadConfig', 'express:test', 'spawn-casperjs', 'express:test:stop']);
|
||||
|
||||
grunt.registerTask('validate', 'Run tests and lint code', ['jslint', 'test-unit', 'test-integration', 'test-functional']);
|
||||
grunt.registerTask('test-api', 'Run functional api tests only', ['clean:test', 'setTestEnv', 'loadConfig', 'express:test', 'mochacli:api', 'express:test:stop']);
|
||||
|
||||
grunt.registerTask('validate', 'Run tests and lint code', ['jslint', 'test-unit', 'test-integration', 'test-api', 'test-functional']);
|
||||
|
||||
|
||||
// ## Coverage report for Unit and Integration Tests
|
||||
|
|
106
core/bookshelf-session.js
Normal file
106
core/bookshelf-session.js
Normal file
|
@ -0,0 +1,106 @@
|
|||
var Store = require('express').session.Store,
|
||||
time12h = 12 * 60 * 60 * 1000,
|
||||
BSStore,
|
||||
dataProvider,
|
||||
db,
|
||||
client;
|
||||
|
||||
// Initialize store and clean old sessions
|
||||
BSStore = function BSStore(dataProvider, options) {
|
||||
var self = this;
|
||||
this.dataProvider = dataProvider;
|
||||
options = options || {};
|
||||
Store.call(this, options);
|
||||
|
||||
this.dataProvider.Session.findAll()
|
||||
.then(function (model) {
|
||||
var i,
|
||||
now = new Date().getTime();
|
||||
for (i = 0; i < model.length; i = i + 1) {
|
||||
if (now > model.at(i).get('expires')) {
|
||||
self.destroy(model.at(i).get('id'));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
BSStore.prototype = new Store();
|
||||
|
||||
// store a given session
|
||||
BSStore.prototype.set = function (sid, sessData, callback) {
|
||||
var maxAge = sessData.cookie.maxAge,
|
||||
now = new Date().getTime(),
|
||||
expires = maxAge ? now + maxAge : now + time12h,
|
||||
sessionModel = this.dataProvider.Session;
|
||||
|
||||
sessData = JSON.stringify(sessData);
|
||||
|
||||
//necessary since bookshelf updates models if id is set
|
||||
sessionModel.forge({id: sid}).fetch()
|
||||
.then(function (model) {
|
||||
if (model) {
|
||||
sessionModel.forge({id: sid, expires: expires, sess: sessData }).save();
|
||||
} else {
|
||||
sessionModel.forge({id: sid, expires: expires, sess: sessData })
|
||||
.save(null, {method: 'insert'});
|
||||
}
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
// fetch a session, if session is expired delete it
|
||||
BSStore.prototype.get = function (sid, callback) {
|
||||
var now = new Date().getTime(),
|
||||
self = this,
|
||||
sess,
|
||||
expires;
|
||||
|
||||
this.dataProvider.Session.forge({id: sid})
|
||||
.fetch()
|
||||
.then(function (model) {
|
||||
if (model) {
|
||||
sess = JSON.parse(model.get('sess'));
|
||||
expires = model.get('expires');
|
||||
if (now < expires) {
|
||||
callback(null, sess);
|
||||
} else {
|
||||
self.destroy(sid, callback);
|
||||
}
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// delete a given sessions
|
||||
BSStore.prototype.destroy = function (sid, callback) {
|
||||
this.dataProvider.Session.forge({id: sid})
|
||||
.destroy()
|
||||
.then(function () {
|
||||
// check if callback is null
|
||||
// session.regenerate doesn't provide callback
|
||||
// cleanup at startup does neither
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// get the count of all stored sessions
|
||||
BSStore.prototype.length = function (callback) {
|
||||
this.dataProvider.Session.findAll()
|
||||
.then(function (model) {
|
||||
callback(null, model.length);
|
||||
});
|
||||
};
|
||||
|
||||
// delete all sessions
|
||||
BSStore.prototype.clear = function (callback) {
|
||||
this.dataProvider.Session.destroyAll()
|
||||
.then(function () {
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
module.exports = BSStore;
|
|
@ -133,7 +133,7 @@ db = {
|
|||
};
|
||||
|
||||
return api.notifications.add(notification).then(function () {
|
||||
delete req.session.user;
|
||||
req.session.destroy();
|
||||
res.set({
|
||||
"X-Cache-Invalidate": "/*"
|
||||
});
|
||||
|
|
|
@ -84,9 +84,13 @@ adminControllers = {
|
|||
if (!denied) {
|
||||
loginSecurity.push({ip: req.connection.remoteAddress, time: process.hrtime()[0]});
|
||||
api.users.check({email: req.body.email, pw: req.body.password}).then(function (user) {
|
||||
req.session.user = user.id;
|
||||
res.json(200, {redirect: req.body.redirect ? '/ghost/'
|
||||
+ decodeURIComponent(req.body.redirect) : '/ghost/'});
|
||||
req.session.regenerate(function (err) {
|
||||
if (!err) {
|
||||
req.session.user = user.id;
|
||||
res.json(200, {redirect: req.body.redirect ? '/ghost/'
|
||||
+ decodeURIComponent(req.body.redirect) : '/ghost/'});
|
||||
}
|
||||
});
|
||||
}, function (error) {
|
||||
res.json(401, {error: error.message});
|
||||
});
|
||||
|
@ -125,10 +129,14 @@ adminControllers = {
|
|||
password: password
|
||||
}).then(function (user) {
|
||||
api.settings.edit('email', email).then(function () {
|
||||
if (req.session.user === undefined) {
|
||||
req.session.user = user.id;
|
||||
}
|
||||
res.json(200, {redirect: '/ghost/'});
|
||||
req.session.regenerate(function (err) {
|
||||
if (!err) {
|
||||
if (req.session.user === undefined) {
|
||||
req.session.user = user.id;
|
||||
}
|
||||
res.json(200, {redirect: '/ghost/'});
|
||||
}
|
||||
});
|
||||
});
|
||||
}).otherwise(function (error) {
|
||||
res.json(401, {error: error.message});
|
||||
|
@ -230,7 +238,7 @@ adminControllers = {
|
|||
});
|
||||
},
|
||||
'logout': function (req, res) {
|
||||
req.session = null;
|
||||
req.session.destroy();
|
||||
var notification = {
|
||||
type: 'success',
|
||||
message: 'You were successfully signed out',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"core": {
|
||||
"databaseVersion": {
|
||||
"defaultValue": "000"
|
||||
"defaultValue": "001"
|
||||
}
|
||||
},
|
||||
"blog": {
|
||||
|
|
|
@ -4,6 +4,7 @@ var when = require('when'),
|
|||
knex = require('../../models/base').knex,
|
||||
schema = require('../schema'),
|
||||
|
||||
excludedTables = ['sessions'],
|
||||
exporter;
|
||||
|
||||
exporter = function () {
|
||||
|
@ -13,7 +14,9 @@ exporter = function () {
|
|||
var version = results[0],
|
||||
tables = results[1],
|
||||
selectOps = _.map(tables, function (name) {
|
||||
return knex(name).select();
|
||||
if (excludedTables.indexOf(name) < 0) {
|
||||
return knex(name).select();
|
||||
}
|
||||
});
|
||||
|
||||
return when.all(selectOps).then(function (tableData) {
|
||||
|
|
|
@ -12,8 +12,7 @@ Importer000 = function () {
|
|||
|
||||
this.importFrom = {
|
||||
'000': this.basicImport,
|
||||
'001': this.tempImport,
|
||||
'002': this.tempImport
|
||||
'001': this.basicImport
|
||||
};
|
||||
};
|
||||
|
||||
|
|
8
core/server/data/import/001.js
Normal file
8
core/server/data/import/001.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
var Importer000 = require('./000');
|
||||
|
||||
module.exports = {
|
||||
Importer001: Importer000,
|
||||
importData: function (data) {
|
||||
return new Importer000.importData(data);
|
||||
}
|
||||
};
|
|
@ -81,6 +81,11 @@ var db = {
|
|||
role_id: {type: 'integer', nullable: false},
|
||||
permission_id: {type: 'integer', nullable: false}
|
||||
},
|
||||
sessions: {
|
||||
id: {type: 'string', nullable: false, primary: true},
|
||||
expires: {type: 'bigInteger', nullable: false},
|
||||
sess: {type: 'string', maxlength: 4096, nullable: false}
|
||||
},
|
||||
settings: {
|
||||
id: {type: 'increments', nullable: false, primary: true},
|
||||
uuid: {type: 'string', maxlength: 36, nullable: false},
|
||||
|
@ -113,5 +118,4 @@ var db = {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = db;
|
|
@ -9,6 +9,7 @@ var middleware = require('./middleware'),
|
|||
Ghost = require('../../ghost'),
|
||||
storage = require('../storage'),
|
||||
packageInfo = require('../../../package.json'),
|
||||
BSStore = require('../../bookshelf-session'),
|
||||
|
||||
ghost = new Ghost();
|
||||
|
||||
|
@ -166,8 +167,14 @@ module.exports = function (server) {
|
|||
server.use('/ghost/upload/', express.multipart());
|
||||
server.use('/ghost/upload/', express.multipart({uploadDir: corePath + '/content/images'}));
|
||||
server.use('/ghost/api/v0.1/db/', express.multipart());
|
||||
server.use(express.cookieParser(ghost.dbHash));
|
||||
server.use(express.cookieSession({ cookie : { maxAge: 12 * 60 * 60 * 1000 }}));
|
||||
|
||||
// Session handling
|
||||
server.use(express.cookieParser());
|
||||
server.use(express.session({
|
||||
store: new BSStore(ghost.dataProvider),
|
||||
secret: ghost.dbHash,
|
||||
cookie: { maxAge: 12 * 60 * 60 * 1000 }
|
||||
}));
|
||||
|
||||
//enable express csrf protection
|
||||
server.use(express.csrf());
|
||||
|
|
|
@ -8,6 +8,8 @@ module.exports = {
|
|||
Settings: require('./settings').Settings,
|
||||
Tag: require('./tag').Tag,
|
||||
Base: require('./base'),
|
||||
Session: require('./session').Session,
|
||||
|
||||
init: function () {
|
||||
return migrations.init();
|
||||
},
|
||||
|
|
32
core/server/models/session.js
Normal file
32
core/server/models/session.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
var ghostBookshelf = require('./base'),
|
||||
Session,
|
||||
Sessions;
|
||||
|
||||
Session = ghostBookshelf.Model.extend({
|
||||
|
||||
tableName: 'sessions',
|
||||
|
||||
permittedAttributes: ['id', 'expires', 'sess'],
|
||||
|
||||
saving: function () {
|
||||
// Remove any properties which don't belong on the session model
|
||||
this.attributes = this.pick(this.permittedAttributes);
|
||||
}
|
||||
}, {
|
||||
destroyAll: function (options) {
|
||||
options = options || {};
|
||||
return ghostBookshelf.Collection.forge([], {model: this}).fetch().
|
||||
then(function (collection) {
|
||||
collection.invokeThen('destroy', options);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Sessions = ghostBookshelf.Collection.extend({
|
||||
model: Session
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
Session: Session,
|
||||
Sessions: Sessions
|
||||
};
|
|
@ -14,17 +14,12 @@ describe('Post API', function () {
|
|||
before(function (done) {
|
||||
testUtils.clearData()
|
||||
.then(function () {
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
|
||||
beforeEach(function (done) {
|
||||
testUtils.initData()
|
||||
return testUtils.initData();
|
||||
})
|
||||
.then(function () {
|
||||
return testUtils.insertDefaultFixtures();
|
||||
})
|
||||
.then(function () {
|
||||
// do a get request to get the CSRF token first
|
||||
request.get(testUtils.API.getSigninURL(), function (error, response, body) {
|
||||
response.should.have.status(200);
|
||||
var pattern_meta = /<meta.*?name="csrf-param".*?content="(.*?)".*?>/i;
|
||||
|
@ -32,21 +27,19 @@ describe('Post API', function () {
|
|||
csrfToken = body.match(pattern_meta)[1];
|
||||
setTimeout((function () {
|
||||
request.post({uri: testUtils.API.getSigninURL(),
|
||||
headers: {'X-CSRF-Token': csrfToken}}, function (error, response, body) {
|
||||
headers: {'X-CSRF-Token': csrfToken}}, function (error, response, body) {
|
||||
response.should.have.status(200);
|
||||
done();
|
||||
request.get(testUtils.API.getAdminURL(), function (error, response, body) {
|
||||
response.should.have.status(200);
|
||||
csrfToken = body.match(pattern_meta)[1];
|
||||
done();
|
||||
});
|
||||
}).form({email: user.email, password: user.password});
|
||||
}), 2000);
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
testUtils.clearData().then(function () {
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('can retrieve all posts', function (done) {
|
||||
request.get(testUtils.API.getApiURL('posts/'), function (error, response, body) {
|
||||
response.should.have.status(200);
|
||||
|
@ -114,6 +107,60 @@ describe('Post API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
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);
|
||||
should.not.exist(response.headers['x-cache-invalidate']);
|
||||
response.should.be.json;
|
||||
var jsonResponse = JSON.parse(body);
|
||||
jsonResponse.should.exist;
|
||||
testUtils.API.checkResponseValue(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.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.headers['x-cache-invalidate'].should.eql('/, /page/*, /rss/, /rss/*, /' + putBody.slug + '/');
|
||||
response.should.be.json;
|
||||
putBody.should.exist;
|
||||
putBody.title.should.eql(changedValue);
|
||||
|
||||
testUtils.API.checkResponse(putBody, 'post');
|
||||
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);
|
||||
should.not.exist(response.headers['x-cache-invalidate']);
|
||||
response.should.be.json;
|
||||
testUtils.API.checkResponseValue(putBody, ['error']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can delete a post', function (done) {
|
||||
var deletePostId = 1;
|
||||
request.del({uri: testUtils.API.getApiURL('posts/' + deletePostId + '/'),
|
||||
|
@ -171,57 +218,4 @@ describe('Post API', function () {
|
|||
});
|
||||
|
||||
|
||||
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);
|
||||
should.not.exist(response.headers['x-cache-invalidate']);
|
||||
response.should.be.json;
|
||||
var jsonResponse = JSON.parse(body);
|
||||
jsonResponse.should.exist;
|
||||
testUtils.API.checkResponseValue(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.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.headers['x-cache-invalidate'].should.eql('/, /page/*, /rss/, /rss/*, /' + putBody.slug + '/');
|
||||
response.should.be.json;
|
||||
putBody.should.exist;
|
||||
putBody.title.should.eql(changedValue);
|
||||
|
||||
testUtils.API.checkResponse(putBody, 'post');
|
||||
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);
|
||||
should.not.exist(response.headers['x-cache-invalidate']);
|
||||
response.should.be.json;
|
||||
testUtils.API.checkResponseValue(putBody, ['error']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -14,17 +14,12 @@ describe('Settings API', function () {
|
|||
before(function (done) {
|
||||
testUtils.clearData()
|
||||
.then(function () {
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
|
||||
beforeEach(function (done) {
|
||||
testUtils.initData()
|
||||
return testUtils.initData();
|
||||
})
|
||||
.then(function () {
|
||||
return testUtils.insertDefaultFixtures();
|
||||
})
|
||||
.then(function () {
|
||||
// do a get request to get the CSRF token first
|
||||
request.get(testUtils.API.getSigninURL(), function (error, response, body) {
|
||||
response.should.have.status(200);
|
||||
var pattern_meta = /<meta.*?name="csrf-param".*?content="(.*?)".*?>/i;
|
||||
|
@ -32,21 +27,19 @@ describe('Settings API', function () {
|
|||
csrfToken = body.match(pattern_meta)[1];
|
||||
setTimeout((function () {
|
||||
request.post({uri: testUtils.API.getSigninURL(),
|
||||
headers: {'X-CSRF-Token': csrfToken}}, function (error, response, body) {
|
||||
headers: {'X-CSRF-Token': csrfToken}}, function (error, response, body) {
|
||||
response.should.have.status(200);
|
||||
done();
|
||||
request.get(testUtils.API.getAdminURL(), function (error, response, body) {
|
||||
response.should.have.status(200);
|
||||
csrfToken = body.match(pattern_meta)[1];
|
||||
done();
|
||||
});
|
||||
}).form({email: user.email, password: user.password});
|
||||
}), 2000);
|
||||
});
|
||||
}, 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) {
|
|
@ -14,17 +14,12 @@ describe('Tag API', function () {
|
|||
before(function (done) {
|
||||
testUtils.clearData()
|
||||
.then(function () {
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
|
||||
beforeEach(function (done) {
|
||||
testUtils.initData()
|
||||
return testUtils.initData();
|
||||
})
|
||||
.then(function () {
|
||||
return testUtils.insertDefaultFixtures();
|
||||
})
|
||||
.then(function () {
|
||||
// do a get request to get the CSRF token first
|
||||
request.get(testUtils.API.getSigninURL(), function (error, response, body) {
|
||||
response.should.have.status(200);
|
||||
var pattern_meta = /<meta.*?name="csrf-param".*?content="(.*?)".*?>/i;
|
||||
|
@ -34,19 +29,17 @@ describe('Tag API', function () {
|
|||
request.post({uri: testUtils.API.getSigninURL(),
|
||||
headers: {'X-CSRF-Token': csrfToken}}, function (error, response, body) {
|
||||
response.should.have.status(200);
|
||||
done();
|
||||
request.get(testUtils.API.getAdminURL(), function (error, response, body) {
|
||||
response.should.have.status(200);
|
||||
csrfToken = body.match(pattern_meta)[1];
|
||||
done();
|
||||
});
|
||||
}).form({email: user.email, password: user.password});
|
||||
}), 2000);
|
||||
});
|
||||
}, 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);
|
|
@ -14,17 +14,12 @@ describe('User API', function () {
|
|||
before(function (done) {
|
||||
testUtils.clearData()
|
||||
.then(function () {
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
|
||||
beforeEach(function (done) {
|
||||
testUtils.initData()
|
||||
return testUtils.initData();
|
||||
})
|
||||
.then(function () {
|
||||
return testUtils.insertDefaultFixtures();
|
||||
})
|
||||
.then(function () {
|
||||
// do a get request to get the CSRF token first
|
||||
request.get(testUtils.API.getSigninURL(), function (error, response, body) {
|
||||
response.should.have.status(200);
|
||||
var pattern_meta = /<meta.*?name="csrf-param".*?content="(.*?)".*?>/i;
|
||||
|
@ -34,19 +29,17 @@ describe('User API', function () {
|
|||
request.post({uri: testUtils.API.getSigninURL(),
|
||||
headers: {'X-CSRF-Token': csrfToken}}, function (error, response, body) {
|
||||
response.should.have.status(200);
|
||||
done();
|
||||
request.get(testUtils.API.getAdminURL(), function (error, response, body) {
|
||||
response.should.have.status(200);
|
||||
csrfToken = body.match(pattern_meta)[1];
|
||||
done();
|
||||
});
|
||||
}).form({email: user.email, password: user.password});
|
||||
}), 2000);
|
||||
});
|
||||
}, 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);
|
|
@ -24,7 +24,7 @@
|
|||
var host = casper.cli.options.host || 'localhost',
|
||||
noPort = casper.cli.options.noPort || false,
|
||||
port = casper.cli.options.port || '2368',
|
||||
email = casper.cli.options.email || 'ghost@tryghost.org',
|
||||
email = casper.cli.options.email || 'jbloggs@example.com',
|
||||
password = casper.cli.options.password || 'Sl1m3rson',
|
||||
url = "http://" + host + (noPort ? '/' : ":" + port + "/"),
|
||||
newUser = {
|
||||
|
|
|
@ -29,6 +29,12 @@ describe('Settings Model', function () {
|
|||
}, done);
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
testUtils.clearData().then(function () {
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
|
||||
describe('API', function () {
|
||||
|
||||
it('can browse', function (done) {
|
||||
|
@ -67,7 +73,6 @@ describe('Settings Model', function () {
|
|||
});
|
||||
|
||||
it('can edit single', function (done) {
|
||||
var firstSetting;
|
||||
|
||||
SettingsModel.browse().then(function (results) {
|
||||
|
||||
|
@ -149,7 +154,7 @@ describe('Settings Model', function () {
|
|||
});
|
||||
|
||||
it('can delete', function (done) {
|
||||
var firstSettingId;
|
||||
var settingId;
|
||||
|
||||
SettingsModel.browse().then(function (results) {
|
||||
|
||||
|
@ -157,9 +162,11 @@ describe('Settings Model', function () {
|
|||
|
||||
results.length.should.be.above(0);
|
||||
|
||||
firstSettingId = results.models[0].id;
|
||||
// dont't use results.models[0], since it will delete databaseversion
|
||||
// which is used for testUtils.reset()
|
||||
settingId = results.models[1].id;
|
||||
|
||||
return SettingsModel.destroy(firstSettingId);
|
||||
return SettingsModel.destroy(settingId);
|
||||
|
||||
}).then(function () {
|
||||
|
||||
|
@ -172,7 +179,7 @@ describe('Settings Model', function () {
|
|||
ids = _.pluck(newResults.models, "id");
|
||||
|
||||
hasDeletedId = _.any(ids, function (id) {
|
||||
return id === firstSettingId;
|
||||
return id === settingId;
|
||||
});
|
||||
|
||||
hasDeletedId.should.equal(false);
|
||||
|
|
|
@ -36,7 +36,7 @@ describe("Exporter", function () {
|
|||
it("exports data", function (done) {
|
||||
// Stub migrations to return 000 as the current database version
|
||||
var migrationStub = sinon.stub(migration, "getDatabaseVersion", function () {
|
||||
return when.resolve("000");
|
||||
return when.resolve("001");
|
||||
});
|
||||
|
||||
exporter().then(function (exportData) {
|
||||
|
@ -48,8 +48,8 @@ describe("Exporter", function () {
|
|||
should.exist(exportData.meta);
|
||||
should.exist(exportData.data);
|
||||
|
||||
exportData.meta.version.should.equal("000");
|
||||
_.findWhere(exportData.data.settings, {key: "databaseVersion"}).value.should.equal("000");
|
||||
exportData.meta.version.should.equal("001");
|
||||
_.findWhere(exportData.data.settings, {key: "databaseVersion"}).value.should.equal("001");
|
||||
|
||||
_.each(tables, function (name) {
|
||||
should.exist(exportData.data[name]);
|
||||
|
|
|
@ -34,6 +34,12 @@ describe("Ghost API", function () {
|
|||
sandbox.restore();
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
testUtils.clearData().then(function () {
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it("is a singleton", function () {
|
||||
var ghost2 = new Ghost();
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ var testUtils = require('../utils'),
|
|||
exporter = require('../../server/data/export'),
|
||||
importer = require('../../server/data/import'),
|
||||
Importer000 = require('../../server/data/import/000'),
|
||||
Importer001 = require('../../server/data/import/001'),
|
||||
fixtures = require('../../server/data/fixtures'),
|
||||
Settings = require('../../server/models/settings').Settings;
|
||||
|
||||
|
@ -42,11 +43,29 @@ describe("Import", function () {
|
|||
}).then(null, done);
|
||||
});
|
||||
|
||||
it("resolves 001", function (done) {
|
||||
var importStub = sinon.stub(Importer001, "importData", function () {
|
||||
return when.resolve();
|
||||
}),
|
||||
fakeData = { test: true };
|
||||
|
||||
importer("001", fakeData).then(function () {
|
||||
importStub.calledWith(fakeData).should.equal(true);
|
||||
|
||||
importStub.restore();
|
||||
|
||||
done();
|
||||
}).then(null, done);
|
||||
});
|
||||
|
||||
describe("000", function () {
|
||||
should.exist(Importer000);
|
||||
|
||||
it("imports data from 000", function (done) {
|
||||
it("imports data from 001", function (done) {
|
||||
var exportData;
|
||||
var migrationStub = sinon.stub(migration, "getDatabaseVersion", function () {
|
||||
return when.resolve("000");
|
||||
});
|
||||
|
||||
// migrate to current version
|
||||
migration.migrateUp().then(function () {
|
||||
|
@ -83,7 +102,58 @@ describe("Import", function () {
|
|||
|
||||
// test settings
|
||||
importedData[2].length.should.be.above(0, 'Wrong number of settings');
|
||||
_.findWhere(importedData[2], {key: "databaseVersion"}).value.should.equal("000", 'Wrong database version');
|
||||
_.findWhere(importedData[2], {key: "databaseVersion"}).value.should.equal("001", 'Wrong database version');
|
||||
|
||||
// test tags
|
||||
importedData[3].length.should.equal(exportData.data.tags.length, 'no new tags');
|
||||
|
||||
done();
|
||||
}).then(null, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("001", function () {
|
||||
should.exist(Importer001);
|
||||
|
||||
it("imports data from 001", function (done) {
|
||||
var exportData;
|
||||
|
||||
// Migrate to version 001
|
||||
migration.migrateUp().then(function () {
|
||||
// Load the fixtures
|
||||
return fixtures.populateFixtures();
|
||||
}).then(function () {
|
||||
// Initialise the default settings
|
||||
return Settings.populateDefaults();
|
||||
}).then(function () {
|
||||
// export the version 000 data ready to import
|
||||
// TODO: Should have static test data here?
|
||||
return exporter();
|
||||
}).then(function (exported) {
|
||||
exportData = exported;
|
||||
|
||||
return importer("001", exportData);
|
||||
}).then(function () {
|
||||
// Grab the data from tables
|
||||
return when.all([
|
||||
knex("users").select(),
|
||||
knex("posts").select(),
|
||||
knex("settings").select(),
|
||||
knex("tags").select()
|
||||
]);
|
||||
}).then(function (importedData) {
|
||||
|
||||
should.exist(importedData);
|
||||
importedData.length.should.equal(4, 'Did not get data successfully');
|
||||
|
||||
// we always have 0 users as there isn't one in fixtures
|
||||
importedData[0].length.should.equal(0, 'There should not be a user');
|
||||
// import no longer requires all data to be dropped, and adds posts
|
||||
importedData[1].length.should.equal(exportData.data.posts.length + 1, 'Wrong number of posts');
|
||||
|
||||
// test settings
|
||||
importedData[2].length.should.be.above(0, 'Wrong number of settings');
|
||||
_.findWhere(importedData[2], {key: "databaseVersion"}).value.should.equal("001", 'Wrong database version');
|
||||
|
||||
// test tags
|
||||
importedData[3].length.should.equal(exportData.data.tags.length, 'no new tags');
|
||||
|
@ -92,9 +162,8 @@ describe("Import", function () {
|
|||
}).then(null, done);
|
||||
});
|
||||
|
||||
it("doesn't imports invalid post data from 000", function (done) {
|
||||
it("doesn't import invalid post data from 001", function (done) {
|
||||
var exportData;
|
||||
|
||||
// migrate to current version
|
||||
migration.migrateUp().then(function () {
|
||||
// Load the fixtures
|
||||
|
@ -111,11 +180,12 @@ describe("Import", function () {
|
|||
//change title to 151 characters
|
||||
exportData.data.posts[0].title = new Array(152).join('a');
|
||||
exportData.data.posts[0].tags = 'Tag';
|
||||
return importer("000", exportData);
|
||||
return importer("001", exportData);
|
||||
}).then(function () {
|
||||
(1).should.eql(0, 'Data import should not resolve promise.');
|
||||
}, function (error) {
|
||||
error.should.eql('Error importing data: Post title maximum length is 150 characters.');
|
||||
|
||||
when.all([
|
||||
knex("users").select(),
|
||||
knex("posts").select(),
|
||||
|
@ -132,7 +202,7 @@ describe("Import", function () {
|
|||
|
||||
// test settings
|
||||
importedData[2].length.should.be.above(0, 'Wrong number of settings');
|
||||
_.findWhere(importedData[2], {key: "databaseVersion"}).value.should.equal("000", 'Wrong database version');
|
||||
_.findWhere(importedData[2], {key: "databaseVersion"}).value.should.equal("001", 'Wrong database version');
|
||||
|
||||
// test tags
|
||||
importedData[3].length.should.equal(exportData.data.tags.length, 'no new tags');
|
||||
|
@ -142,7 +212,7 @@ describe("Import", function () {
|
|||
|
||||
}).then(null, done);
|
||||
});
|
||||
it("doesn't imports invalid settings data from 000", function (done) {
|
||||
it("doesn't import invalid settings data from 001", function (done) {
|
||||
var exportData;
|
||||
|
||||
// migrate to current version
|
||||
|
@ -160,7 +230,7 @@ describe("Import", function () {
|
|||
exportData = exported;
|
||||
//change to blank settings key
|
||||
exportData.data.settings[3].key = null;
|
||||
return importer("000", exportData);
|
||||
return importer("001", exportData);
|
||||
}).then(function () {
|
||||
(1).should.eql(0, 'Data import should not resolve promise.');
|
||||
}, function (error) {
|
||||
|
@ -181,7 +251,7 @@ describe("Import", function () {
|
|||
|
||||
// test settings
|
||||
importedData[2].length.should.be.above(0, 'Wrong number of settings');
|
||||
_.findWhere(importedData[2], {key: "databaseVersion"}).value.should.equal("000", 'Wrong database version');
|
||||
_.findWhere(importedData[2], {key: "databaseVersion"}).value.should.equal("001", 'Wrong database version');
|
||||
|
||||
// test tags
|
||||
importedData[3].length.should.equal(exportData.data.tags.length, 'no new tags');
|
||||
|
@ -192,4 +262,5 @@ describe("Import", function () {
|
|||
}).then(null, done);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -16,16 +16,14 @@ var testUtils = require('../utils'),
|
|||
describe('Permissions', function () {
|
||||
|
||||
before(function (done) {
|
||||
testUtils.clearData()
|
||||
.then(function () {
|
||||
testUtils.clearData().then(function () {
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
|
||||
beforeEach(function (done) {
|
||||
testUtils.initData()
|
||||
.then(testUtils.insertDefaultUser)
|
||||
.then(function () {
|
||||
.then(testUtils.insertDefaultUser).then(function () {
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
|
@ -37,6 +35,12 @@ describe('Permissions', function () {
|
|||
}, done);
|
||||
});
|
||||
|
||||
after(function (done) {
|
||||
testUtils.clearData().then(function () {
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
|
||||
var testPerms = [
|
||||
{ act: "edit", obj: "post" },
|
||||
{ act: "edit", obj: "tag" },
|
||||
|
|
|
@ -27,6 +27,9 @@ function getApiURL (route) {
|
|||
function getSigninURL () {
|
||||
return url.resolve(schema + host + ':' + port, 'ghost/signin/');
|
||||
}
|
||||
function getAdminURL () {
|
||||
return url.resolve(schema + host + ':' + port, 'ghost/');
|
||||
}
|
||||
|
||||
// make sure the API only returns expected properties only
|
||||
function checkResponse (jsonResponse, objectType) {
|
||||
|
@ -42,6 +45,7 @@ function checkResponseValue (jsonResponse, properties) {
|
|||
module.exports = {
|
||||
getApiURL: getApiURL,
|
||||
getSigninURL: getSigninURL,
|
||||
getAdminURL: getAdminURL,
|
||||
checkResponse: checkResponse,
|
||||
checkResponseValue: checkResponseValue,
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue