mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-24 19:33:02 +03:00
Removed api integration tests (#9940)
refs #9866 - moved the tests either to unit tests or routing tests - or removed test case (a lot) - this commit is very big 🤪, it was not rly possible to create clean commits for this - it only changes the test env, no real code is touched Next steps: - optimise folder structure + make v2 testing possible - reduce some more tests from routing and model integeration tests
This commit is contained in:
parent
476ac185aa
commit
db1d2f62dd
@ -114,5 +114,9 @@ module.exports = {
|
||||
common.events.removeListener('settings.edited', _private.updateSettingFromModel);
|
||||
common.events.removeListener('settings.added', _private.updateSettingFromModel);
|
||||
common.events.removeListener('settings.deleted', _private.updateSettingFromModel);
|
||||
},
|
||||
|
||||
reset() {
|
||||
settingsCache = {};
|
||||
}
|
||||
};
|
||||
|
@ -15,239 +15,401 @@ var should = require('should'),
|
||||
describe('Authentication API', function () {
|
||||
var accesstoken = '', ghostServer;
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
return localUtils.doAuth(request);
|
||||
})
|
||||
.then(function (token) {
|
||||
accesstoken = token;
|
||||
});
|
||||
});
|
||||
describe('auth & authorize', function () {
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
return localUtils.doAuth(request);
|
||||
})
|
||||
.then(function (token) {
|
||||
accesstoken = token;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return testUtils.clearBruteData();
|
||||
});
|
||||
afterEach(function () {
|
||||
return testUtils.clearBruteData();
|
||||
});
|
||||
|
||||
it('can authenticate', function (done) {
|
||||
request.post(localUtils.API.getApiQuery('authentication/token'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
grant_type: 'password',
|
||||
username: user.email,
|
||||
password: user.password,
|
||||
client_id: 'ghost-admin',
|
||||
client_secret: 'not_available'
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
// TODO: make it possible to override oauth2orize's header so that this is consistent
|
||||
.expect('Cache-Control', 'no-store')
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body,
|
||||
newAccessToken;
|
||||
it('can authenticate', function (done) {
|
||||
request.post(localUtils.API.getApiQuery('authentication/token'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
grant_type: 'password',
|
||||
username: user.email,
|
||||
password: user.password,
|
||||
client_id: 'ghost-admin',
|
||||
client_secret: 'not_available'
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
// TODO: make it possible to override oauth2orize's header so that this is consistent
|
||||
.expect('Cache-Control', 'no-store')
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body,
|
||||
newAccessToken;
|
||||
|
||||
should.exist(jsonResponse.access_token);
|
||||
should.exist(jsonResponse.refresh_token);
|
||||
should.exist(jsonResponse.expires_in);
|
||||
should.exist(jsonResponse.token_type);
|
||||
should.exist(jsonResponse.access_token);
|
||||
should.exist(jsonResponse.refresh_token);
|
||||
should.exist(jsonResponse.expires_in);
|
||||
should.exist(jsonResponse.token_type);
|
||||
|
||||
models.Accesstoken.findOne({
|
||||
token: jsonResponse.access_token
|
||||
}).then(function (_newAccessToken) {
|
||||
newAccessToken = _newAccessToken;
|
||||
models.Accesstoken.findOne({
|
||||
token: jsonResponse.access_token
|
||||
}).then(function (_newAccessToken) {
|
||||
newAccessToken = _newAccessToken;
|
||||
|
||||
return models.Refreshtoken.findOne({
|
||||
token: jsonResponse.refresh_token
|
||||
});
|
||||
}).then(function (newRefreshToken) {
|
||||
newAccessToken.get('issued_by').should.eql(newRefreshToken.id);
|
||||
return models.Refreshtoken.findOne({
|
||||
token: jsonResponse.refresh_token
|
||||
});
|
||||
}).then(function (newRefreshToken) {
|
||||
newAccessToken.get('issued_by').should.eql(newRefreshToken.id);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t authenticate unknown user', function (done) {
|
||||
request.post(localUtils.API.getApiQuery('authentication/token'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Accept', 'application/json')
|
||||
.send({
|
||||
grant_type: 'password',
|
||||
username: 'invalid@email.com',
|
||||
password: user.password,
|
||||
client_id: 'ghost-admin',
|
||||
client_secret: 'not_available'
|
||||
}).expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.errors[0].errorType);
|
||||
jsonResponse.errors[0].errorType.should.eql('NotFoundError');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t authenticate unknown user', function (done) {
|
||||
request.post(localUtils.API.getApiQuery('authentication/token'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Accept', 'application/json')
|
||||
.send({
|
||||
grant_type: 'password',
|
||||
username: 'invalid@email.com',
|
||||
password: user.password,
|
||||
client_id: 'ghost-admin',
|
||||
client_secret: 'not_available'
|
||||
}).expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.errors[0].errorType);
|
||||
jsonResponse.errors[0].errorType.should.eql('NotFoundError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('can\'t authenticate invalid password user', function (done) {
|
||||
request.post(localUtils.API.getApiQuery('authentication/token'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Accept', 'application/json')
|
||||
.send({
|
||||
grant_type: 'password',
|
||||
username: user.email,
|
||||
password: 'invalid',
|
||||
client_id: 'ghost-admin',
|
||||
client_secret: 'not_available'
|
||||
}).expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(422)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.errors[0].errorType);
|
||||
jsonResponse.errors[0].errorType.should.eql('ValidationError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t authenticate invalid password user', function (done) {
|
||||
request.post(localUtils.API.getApiQuery('authentication/token'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Accept', 'application/json')
|
||||
.send({
|
||||
grant_type: 'password',
|
||||
username: user.email,
|
||||
password: 'invalid',
|
||||
client_id: 'ghost-admin',
|
||||
client_secret: 'not_available'
|
||||
}).expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(422)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.errors[0].errorType);
|
||||
jsonResponse.errors[0].errorType.should.eql('ValidationError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('can request new access token', function (done) {
|
||||
request.post(localUtils.API.getApiQuery('authentication/token'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
grant_type: 'password',
|
||||
username: user.email,
|
||||
password: user.password,
|
||||
client_id: 'ghost-admin',
|
||||
client_secret: 'not_available'
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
// TODO: make it possible to override oauth2orize's header so that this is consistent
|
||||
.expect('Cache-Control', 'no-store')
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
it('can request new access token', function (done) {
|
||||
request.post(localUtils.API.getApiQuery('authentication/token'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
grant_type: 'password',
|
||||
username: user.email,
|
||||
password: user.password,
|
||||
client_id: 'ghost-admin',
|
||||
client_secret: 'not_available'
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
// TODO: make it possible to override oauth2orize's header so that this is consistent
|
||||
.expect('Cache-Control', 'no-store')
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
var refreshToken = res.body.refresh_token;
|
||||
|
||||
var refreshToken = res.body.refresh_token;
|
||||
models.Accesstoken.findOne({
|
||||
token: accesstoken
|
||||
}).then(function (oldAccessToken) {
|
||||
moment(oldAccessToken.get('expires')).diff(moment(), 'minutes').should.be.above(6);
|
||||
|
||||
models.Accesstoken.findOne({
|
||||
token: accesstoken
|
||||
}).then(function (oldAccessToken) {
|
||||
moment(oldAccessToken.get('expires')).diff(moment(), 'minutes').should.be.above(6);
|
||||
request.post(localUtils.API.getApiQuery('authentication/token'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.send({
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: refreshToken,
|
||||
client_id: 'ghost-admin',
|
||||
client_secret: 'not_available'
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
// TODO: make it possible to override oauth2orize's header so that this is consistent
|
||||
.expect('Cache-Control', 'no-store')
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
request.post(localUtils.API.getApiQuery('authentication/token'))
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.access_token);
|
||||
should.exist(jsonResponse.expires_in);
|
||||
|
||||
models.Accesstoken.findOne({
|
||||
token: accesstoken
|
||||
}).then(function (oldAccessToken) {
|
||||
moment(oldAccessToken.get('expires')).diff(moment(), 'minutes').should.be.below(6);
|
||||
return models.Refreshtoken.findOne({
|
||||
token: refreshToken
|
||||
});
|
||||
}).then(function (refreshTokenModel) {
|
||||
// NOTE: the static 6 month ms number in our constants are based on 30 days
|
||||
// We have to compare against the static number. We can't compare against the month in
|
||||
// the next 6 month dynamically, because each month has a different number of days,
|
||||
// which results in a different ms number.
|
||||
moment(Date.now() + constants.SIX_MONTH_MS)
|
||||
.startOf('day')
|
||||
.diff(moment(refreshTokenModel.get('expires')).startOf('day'), 'month').should.eql(0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t request new access token with invalid refresh token', function (done) {
|
||||
request.post(localUtils.API.getApiQuery('authentication/token'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Accept', 'application/json')
|
||||
.send({
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: 'invalid',
|
||||
client_id: 'ghost-admin',
|
||||
client_secret: 'not_available'
|
||||
}).expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(403)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.errors[0].errorType);
|
||||
jsonResponse.errors[0].errorType.should.eql('NoPermissionError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('reset password', function (done) {
|
||||
models.User.getOwnerUser(testUtils.context.internal)
|
||||
.then(function (ownerUser) {
|
||||
var token = security.tokens.resetToken.generateHash({
|
||||
expires: Date.now() + (1000 * 60),
|
||||
email: user.email,
|
||||
dbHash: settingsCache.get('db_hash'),
|
||||
password: ownerUser.get('password')
|
||||
});
|
||||
|
||||
request.put(localUtils.API.getApiQuery('authentication/passwordreset'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.set('Accept', 'application/json')
|
||||
.send({
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: refreshToken,
|
||||
client_id: 'ghost-admin',
|
||||
client_secret: 'not_available'
|
||||
passwordreset: [{
|
||||
token: token,
|
||||
newPassword: 'thisissupersafe',
|
||||
ne2Password: 'thisissupersafe'
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
// TODO: make it possible to override oauth2orize's header so that this is consistent
|
||||
.expect('Cache-Control', 'no-store')
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.access_token);
|
||||
should.exist(jsonResponse.expires_in);
|
||||
|
||||
models.Accesstoken.findOne({
|
||||
token: accesstoken
|
||||
}).then(function (oldAccessToken) {
|
||||
moment(oldAccessToken.get('expires')).diff(moment(), 'minutes').should.be.below(6);
|
||||
return models.Refreshtoken.findOne({
|
||||
token: refreshToken
|
||||
});
|
||||
}).then(function (refreshTokenModel) {
|
||||
// NOTE: the static 6 month ms number in our constants are based on 30 days
|
||||
// We have to compare against the static number. We can't compare against the month in
|
||||
// the next 6 month dynamically, because each month has a different number of days,
|
||||
// which results in a different ms number.
|
||||
moment(Date.now() + constants.SIX_MONTH_MS)
|
||||
.startOf('day')
|
||||
.diff(moment(refreshTokenModel.get('expires')).startOf('day'), 'month').should.eql(0);
|
||||
|
||||
done();
|
||||
});
|
||||
done();
|
||||
});
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('reset password: invalid token', function () {
|
||||
return request
|
||||
.put(localUtils.API.getApiQuery('authentication/passwordreset'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Accept', 'application/json')
|
||||
.send({
|
||||
passwordreset: [{
|
||||
token: 'invalid',
|
||||
newPassword: 'thisissupersafe',
|
||||
ne2Password: 'thisissupersafe'
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(401);
|
||||
});
|
||||
|
||||
it('revoke token', function () {
|
||||
return request
|
||||
.post(localUtils.API.getApiQuery('authentication/revoke'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Accept', 'application/json')
|
||||
.send({
|
||||
token: accesstoken,
|
||||
token_type_hint: 'access_token'
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.then(() => {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery('posts/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.expect(401);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t request new access token with invalid refresh token', function (done) {
|
||||
request.post(localUtils.API.getApiQuery('authentication/token'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Accept', 'application/json')
|
||||
.send({
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: 'invalid',
|
||||
client_id: 'ghost-admin',
|
||||
client_secret: 'not_available'
|
||||
}).expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(403)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.errors[0].errorType);
|
||||
jsonResponse.errors[0].errorType.should.eql('NoPermissionError');
|
||||
done();
|
||||
});
|
||||
describe('Blog setup', function () {
|
||||
before(function () {
|
||||
return ghost({forceStart: true})
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
});
|
||||
});
|
||||
|
||||
it('is setup? no', function () {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery('authentication/setup'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
res.body.setup[0].status.should.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
it('complete setup', function () {
|
||||
return request
|
||||
.post(localUtils.API.getApiQuery('authentication/setup'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
setup: [{
|
||||
name: 'test user',
|
||||
email: 'test@example.com',
|
||||
password: 'thisissupersafe',
|
||||
blogTitle: 'a test blog'
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(201)
|
||||
.then((res) => {
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse.users);
|
||||
should.not.exist(jsonResponse.meta);
|
||||
|
||||
jsonResponse.users.should.have.length(1);
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user');
|
||||
|
||||
const newUser = jsonResponse.users[0];
|
||||
newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id);
|
||||
newUser.name.should.equal('test user');
|
||||
newUser.email.should.equal('test@example.com');
|
||||
});
|
||||
});
|
||||
|
||||
it('is setup? yes', function () {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery('authentication/setup'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
res.body.setup[0].status.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('complete setup again', function () {
|
||||
return request
|
||||
.post(localUtils.API.getApiQuery('authentication/setup'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
setup: [{
|
||||
name: 'test user',
|
||||
email: 'test-leo@example.com',
|
||||
password: 'thisissupersafe',
|
||||
blogTitle: 'a test blog'
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(403);
|
||||
});
|
||||
});
|
||||
|
||||
it('reset password', function (done) {
|
||||
models.User.getOwnerUser(testUtils.context.internal)
|
||||
.then(function (ownerUser) {
|
||||
var token = security.tokens.resetToken.generateHash({
|
||||
expires: Date.now() + (1000 * 60),
|
||||
email: user.email,
|
||||
dbHash: settingsCache.get('db_hash'),
|
||||
password: ownerUser.get('password')
|
||||
describe('Invitation', function () {
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
|
||||
// simulates blog setup (initialises the owner)
|
||||
return localUtils.doAuth(request, 'invites');
|
||||
});
|
||||
});
|
||||
|
||||
request.put(localUtils.API.getApiQuery('authentication/passwordreset'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Accept', 'application/json')
|
||||
.send({
|
||||
passwordreset: [{
|
||||
token: token,
|
||||
newPassword: 'thisissupersafe',
|
||||
ne2Password: 'thisissupersafe'
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
it('try to accept without invite', function () {
|
||||
return request
|
||||
.post(localUtils.API.getApiQuery('authentication/invitation'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
invitation: [{
|
||||
token: 'lul11111',
|
||||
password: 'lel123456',
|
||||
email: 'not-invited@example.org',
|
||||
name: 'not invited'
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(404);
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
})
|
||||
.catch(done);
|
||||
it('try to accept with invite', function () {
|
||||
return request
|
||||
.post(localUtils.API.getApiQuery('authentication/invitation'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
invitation: [{
|
||||
token: testUtils.DataGenerator.forKnex.invites[0].token,
|
||||
password: '12345678910',
|
||||
email: testUtils.DataGenerator.forKnex.invites[0].email,
|
||||
name: 'invited'
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -24,7 +24,7 @@ describe('Configuration API', function () {
|
||||
});
|
||||
|
||||
describe('success', function () {
|
||||
it('can retrieve public configuration', function (done) {
|
||||
it('can retrieve public configuration and all expected properties', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('configuration/'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
@ -35,6 +35,48 @@ describe('Configuration API', function () {
|
||||
}
|
||||
|
||||
should.exist(res.body.configuration);
|
||||
|
||||
res.body.configuration.should.be.an.Array().with.lengthOf(1);
|
||||
const props = res.body.configuration[0];
|
||||
|
||||
props.blogUrl.should.eql('http://127.0.0.1:2369/');
|
||||
|
||||
props.useGravatar.should.eql(false);
|
||||
props.publicAPI.should.eql(true);
|
||||
props.clientId.should.eql('ghost-admin');
|
||||
props.clientSecret.should.eql('not_available');
|
||||
|
||||
// value not available, because settings API was not called yet
|
||||
props.hasOwnProperty('blogTitle').should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can read about config and get all expected properties', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('configuration/about/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.exist(res.body.configuration);
|
||||
|
||||
res.body.configuration.should.be.an.Array().with.lengthOf(1);
|
||||
const props = res.body.configuration[0];
|
||||
|
||||
// Check the structure
|
||||
props.should.have.property('version').which.is.a.String();
|
||||
props.should.have.property('environment').which.is.a.String();
|
||||
props.should.have.property('database').which.is.a.String();
|
||||
props.should.have.property('mail').which.is.a.String();
|
||||
|
||||
// Check a few values
|
||||
props.environment.should.match(/^testing/);
|
||||
props.version.should.eql(require('../../../../../package.json').version);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -1,18 +1,20 @@
|
||||
var should = require('should'),
|
||||
supertest = require('supertest'),
|
||||
Promise = require('bluebird'),
|
||||
testUtils = require('../../../utils'),
|
||||
localUtils = require('./utils'),
|
||||
path = require('path'),
|
||||
sinon = require('sinon'),
|
||||
config = require('../../../../../core/server/config'),
|
||||
models = require('../../../../../core/server/models'),
|
||||
fs = require('fs-extra'),
|
||||
_ = require('lodash'),
|
||||
ghost = testUtils.startGhost,
|
||||
request,
|
||||
const should = require('should');
|
||||
const supertest = require('supertest');
|
||||
const Promise = require('bluebird');
|
||||
const testUtils = require('../../../utils');
|
||||
const path = require('path');
|
||||
const sinon = require('sinon');
|
||||
const config = require('../../../../../core/server/config');
|
||||
const models = require('../../../../../core/server/models');
|
||||
const fs = require('fs-extra');
|
||||
const _ = require('lodash');
|
||||
const common = require('../../../../server/lib/common');
|
||||
const localUtils = require('./utils');
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
let ghost = testUtils.startGhost;
|
||||
let request;
|
||||
let sandbox = sinon.sandbox.create();
|
||||
let eventsTriggered;
|
||||
|
||||
describe('DB API', function () {
|
||||
var accesstoken = '', ghostServer, clients, backupClient, schedulerClient, backupQuery, schedulerQuery, fsStub;
|
||||
@ -37,6 +39,18 @@ describe('DB API', function () {
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
eventsTriggered = {};
|
||||
|
||||
sandbox.stub(common.events, 'emit').callsFake(function (eventName, eventObj) {
|
||||
if (!eventsTriggered[eventName]) {
|
||||
eventsTriggered[eventName] = [];
|
||||
}
|
||||
|
||||
eventsTriggered[eventName].push(eventObj);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
@ -182,4 +196,46 @@ describe('DB API', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('delete all content (owner)', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('posts/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
let jsonResponse = res.body;
|
||||
let results = jsonResponse.posts;
|
||||
jsonResponse.posts.should.have.length(7);
|
||||
_.filter(results, {page: false, status: 'published'}).length.should.equal(7);
|
||||
|
||||
request.delete(localUtils.API.getApiQuery('db/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.set('Accept', 'application/json')
|
||||
.expect(204)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
request.get(localUtils.API.getApiQuery('posts/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.posts.should.have.length(0);
|
||||
eventsTriggered['post.unpublished'].length.should.eql(7);
|
||||
eventsTriggered['post.deleted'].length.should.eql(7);
|
||||
eventsTriggered['tag.deleted'].length.should.eql(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
166
core/test/functional/routes/api/invites_spec.js
Normal file
166
core/test/functional/routes/api/invites_spec.js
Normal file
@ -0,0 +1,166 @@
|
||||
const should = require('should');
|
||||
const supertest = require('supertest');
|
||||
const sinon = require('sinon');
|
||||
const testUtils = require('../../../utils');
|
||||
const localUtils = require('./utils');
|
||||
const config = require('../../../../../core/server/config');
|
||||
const mailService = require('../../../../../core/server/services/mail');
|
||||
const ghost = testUtils.startGhost;
|
||||
const sandbox = sinon.sandbox.create();
|
||||
let request;
|
||||
|
||||
describe('Invites API', function () {
|
||||
var accesstoken = '', ghostServer;
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
return localUtils.doAuth(request, 'invites');
|
||||
})
|
||||
.then(function (token) {
|
||||
accesstoken = token;
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox.stub(mailService.GhostMailer.prototype, 'send').resolves('Mail is disabled');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('browse', function () {
|
||||
it('default', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('invites/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.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']);
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.invites);
|
||||
jsonResponse.invites.should.have.length(2);
|
||||
|
||||
testUtils.API.checkResponse(jsonResponse, 'invites');
|
||||
testUtils.API.checkResponse(jsonResponse.invites[0], 'invite');
|
||||
|
||||
jsonResponse.invites[0].status.should.eql('sent');
|
||||
jsonResponse.invites[0].email.should.eql('test1@ghost.org');
|
||||
jsonResponse.invites[0].role_id.should.eql(testUtils.roles.ids.admin);
|
||||
|
||||
jsonResponse.invites[1].status.should.eql('sent');
|
||||
jsonResponse.invites[1].email.should.eql('test2@ghost.org');
|
||||
jsonResponse.invites[1].role_id.should.eql(testUtils.roles.ids.author);
|
||||
|
||||
mailService.GhostMailer.prototype.send.called.should.be.false();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('read', function () {
|
||||
it('default', function (done) {
|
||||
request.get(localUtils.API.getApiQuery(`invites/${testUtils.DataGenerator.forKnex.invites[0].id}/`))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.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']);
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.invites);
|
||||
jsonResponse.invites.should.have.length(1);
|
||||
|
||||
testUtils.API.checkResponse(jsonResponse.invites[0], 'invite');
|
||||
|
||||
mailService.GhostMailer.prototype.send.called.should.be.false();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('add', function () {
|
||||
it('default', function (done) {
|
||||
request.post(localUtils.API.getApiQuery('invites/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.send({
|
||||
invites: [{email: 'test@example.com', role_id: testUtils.existingData.roles[1].id}]
|
||||
})
|
||||
.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']);
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.invites);
|
||||
jsonResponse.invites.should.have.length(1);
|
||||
|
||||
testUtils.API.checkResponse(jsonResponse.invites[0], 'invite');
|
||||
jsonResponse.invites[0].role_id.should.eql(testUtils.existingData.roles[1].id);
|
||||
|
||||
mailService.GhostMailer.prototype.send.called.should.be.true();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('user exists', function (done) {
|
||||
request.post(localUtils.API.getApiQuery('invites/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.send({
|
||||
invites: [{email: 'ghost-author@example.com', role_id: testUtils.existingData.roles[1].id}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(422)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
mailService.GhostMailer.prototype.send.called.should.be.false();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('destroy', function () {
|
||||
it('default', function (done) {
|
||||
request.del(localUtils.API.getApiQuery(`invites/${testUtils.DataGenerator.forKnex.invites[0].id}/`))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(204)
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
mailService.GhostMailer.prototype.send.called.should.be.false();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -24,13 +24,14 @@ describe('Notifications API', function () {
|
||||
});
|
||||
|
||||
describe('Add', function () {
|
||||
var newNotification = {
|
||||
type: 'info',
|
||||
message: 'test notification',
|
||||
custom: true
|
||||
};
|
||||
it('creates a new notification and sets default fields', function (done) {
|
||||
const newNotification = {
|
||||
type: 'info',
|
||||
message: 'test notification',
|
||||
custom: true,
|
||||
id: 'customId'
|
||||
};
|
||||
|
||||
it('creates a new notification', function (done) {
|
||||
request.post(localUtils.API.getApiQuery('notifications/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.send({notifications: [newNotification]})
|
||||
@ -51,6 +52,10 @@ describe('Notifications API', function () {
|
||||
jsonResponse.notifications[0].type.should.equal(newNotification.type);
|
||||
jsonResponse.notifications[0].message.should.equal(newNotification.message);
|
||||
jsonResponse.notifications[0].status.should.equal('alert');
|
||||
jsonResponse.notifications[0].dismissible.should.be.true();
|
||||
should.exist(jsonResponse.notifications[0].location);
|
||||
jsonResponse.notifications[0].location.should.equal('bottom');
|
||||
jsonResponse.notifications[0].id.should.be.a.String();
|
||||
|
||||
done();
|
||||
});
|
||||
@ -99,6 +104,57 @@ describe('Notifications API', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should have correct order', function () {
|
||||
const firstNotification = {
|
||||
status: 'alert',
|
||||
type: 'info',
|
||||
custom: true,
|
||||
id: 'firstId',
|
||||
dismissible: true,
|
||||
message: '1'
|
||||
};
|
||||
|
||||
const secondNotification = {
|
||||
status: 'alert',
|
||||
type: 'info',
|
||||
custom: true,
|
||||
id: 'secondId',
|
||||
dismissible: true,
|
||||
message: '2'
|
||||
};
|
||||
|
||||
return request.post(localUtils.API.getApiQuery('notifications/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.send({notifications: [firstNotification]})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(201)
|
||||
.then(() => {
|
||||
return request.post(localUtils.API.getApiQuery('notifications/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.send({notifications: [secondNotification]})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(201);
|
||||
})
|
||||
.then(() => {
|
||||
return request.get(localUtils.API.getApiQuery('notifications/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.then(res => {
|
||||
const jsonResponse = res.body;
|
||||
|
||||
jsonResponse.notifications.should.be.an.Array().with.lengthOf(4);
|
||||
jsonResponse.notifications[0].id.should.equal(secondNotification.id);
|
||||
jsonResponse.notifications[1].id.should.equal(firstNotification.id);
|
||||
jsonResponse.notifications[2].id.should.equal('customId-2');
|
||||
jsonResponse.notifications[3].id.should.equal('customId');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Delete', function () {
|
||||
@ -147,5 +203,16 @@ describe('Notifications API', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 404 when removing notification with unknown id', function () {
|
||||
return request.del(localUtils.API.getApiQuery('notifications/unknown'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404)
|
||||
.then(res => {
|
||||
res.body.errors[0].message.should.equal('Notification does not exist.');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -52,6 +52,13 @@ describe('Post API', function () {
|
||||
_.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();
|
||||
});
|
||||
});
|
||||
@ -82,7 +89,7 @@ describe('Post API', function () {
|
||||
});
|
||||
|
||||
it('can retrieve multiple post formats', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('posts/?formats=plaintext,mobiledoc'))
|
||||
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)
|
||||
@ -96,12 +103,15 @@ describe('Post API', function () {
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.posts);
|
||||
testUtils.API.checkResponse(jsonResponse, 'posts');
|
||||
jsonResponse.posts.should.have.length(11);
|
||||
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();
|
||||
});
|
||||
});
|
||||
@ -218,7 +228,7 @@ describe('Post API', function () {
|
||||
});
|
||||
|
||||
it('can retrieve all published posts and pages', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('posts/?staticPages=all'))
|
||||
request.get(localUtils.API.getApiQuery('posts/?filter=page:[false,true]'))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
@ -240,7 +250,6 @@ describe('Post API', function () {
|
||||
});
|
||||
|
||||
// Test bits of the API we don't use in the app yet to ensure the API behaves properly
|
||||
|
||||
it('can retrieve all status posts and pages', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('posts/?staticPages=all&status=all'))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
@ -300,9 +309,39 @@ describe('Post API', function () {
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.posts);
|
||||
testUtils.API.checkResponse(jsonResponse, 'posts');
|
||||
jsonResponse.posts.should.have.length(2);
|
||||
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 non featured posts', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('posts/?filter=featured:false'))
|
||||
.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.be.an.Array().with.lengthOf(9);
|
||||
testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
|
||||
testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
|
||||
|
||||
const featured = _.map(jsonResponse.posts, 'featured');
|
||||
featured.should.matchEach(false);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -325,6 +364,11 @@ describe('Post API', function () {
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
@ -8,6 +8,7 @@ var should = require('should'),
|
||||
localUtils = require('./utils'),
|
||||
configUtils = require('../../../utils/configUtils'),
|
||||
config = require('../../../../../core/server/config'),
|
||||
models = require('../../../../../core/server/models'),
|
||||
ghost = testUtils.startGhost,
|
||||
request;
|
||||
|
||||
@ -21,7 +22,7 @@ describe('Public API', function () {
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
return localUtils.doAuth(request, 'users:no-owner', 'posts', 'tags:extra', 'client:trusted-domain');
|
||||
return localUtils.doAuth(request, 'users:no-owner', 'user:inactive', 'posts', 'tags:extra', 'client:trusted-domain');
|
||||
});
|
||||
});
|
||||
|
||||
@ -52,6 +53,26 @@ describe('Public API', function () {
|
||||
testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
|
||||
_.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
|
||||
_.isBoolean(jsonResponse.posts[0].page).should.eql(true);
|
||||
|
||||
// Public API does not return drafts
|
||||
_.map(jsonResponse.posts, (post) => {
|
||||
post.status.should.eql('published');
|
||||
});
|
||||
|
||||
// Default order check
|
||||
jsonResponse.posts[0].slug.should.eql('welcome');
|
||||
jsonResponse.posts[10].slug.should.eql('html-ipsum');
|
||||
|
||||
// check meta response for this test
|
||||
jsonResponse.meta.pagination.page.should.eql(1);
|
||||
jsonResponse.meta.pagination.limit.should.eql(15);
|
||||
jsonResponse.meta.pagination.pages.should.eql(1);
|
||||
jsonResponse.meta.pagination.total.should.eql(11);
|
||||
jsonResponse.meta.pagination.hasOwnProperty('next').should.be.true();
|
||||
jsonResponse.meta.pagination.hasOwnProperty('prev').should.be.true();
|
||||
should.not.exist(jsonResponse.meta.pagination.next);
|
||||
should.not.exist(jsonResponse.meta.pagination.prev);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -79,6 +100,166 @@ describe('Public API', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('browse posts with basic filters', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('posts/?client_id=ghost-admin&client_secret=not_available&filter=tag:kitchen-sink,featured:true&include=tags'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
const jsonResponse = res.body;
|
||||
const ids = _.map(jsonResponse.posts, 'id');
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
should.exist(jsonResponse.posts);
|
||||
testUtils.API.checkResponse(jsonResponse, 'posts');
|
||||
testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
|
||||
|
||||
// should content filtered data and order
|
||||
jsonResponse.posts.should.have.length(4);
|
||||
ids.should.eql([
|
||||
testUtils.DataGenerator.Content.posts[4].id,
|
||||
testUtils.DataGenerator.Content.posts[2].id,
|
||||
testUtils.DataGenerator.Content.posts[1].id,
|
||||
testUtils.DataGenerator.Content.posts[0].id,
|
||||
]);
|
||||
|
||||
// API does not return drafts
|
||||
jsonResponse.posts.forEach((post) => {
|
||||
post.page.should.be.false();
|
||||
post.status.should.eql('published');
|
||||
});
|
||||
|
||||
// Each post must either be featured or have the tag 'kitchen-sink'
|
||||
_.each(jsonResponse.posts, (post) => {
|
||||
if (post.featured) {
|
||||
post.featured.should.equal(true);
|
||||
} else {
|
||||
const tags = _.map(post.tags, 'slug');
|
||||
tags.should.containEql('kitchen-sink');
|
||||
}
|
||||
});
|
||||
|
||||
// The meta object should contain the right detail
|
||||
jsonResponse.meta.should.have.property('pagination');
|
||||
jsonResponse.meta.pagination.should.be.an.Object().with.properties(['page', 'limit', 'pages', 'total', 'next', 'prev']);
|
||||
jsonResponse.meta.pagination.page.should.eql(1);
|
||||
jsonResponse.meta.pagination.limit.should.eql(15);
|
||||
jsonResponse.meta.pagination.pages.should.eql(1);
|
||||
jsonResponse.meta.pagination.total.should.eql(4);
|
||||
should.equal(jsonResponse.meta.pagination.next, null);
|
||||
should.equal(jsonResponse.meta.pagination.prev, null);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('browse posts with author filter', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('posts/?client_id=ghost-admin&client_secret=not_available&filter=authors:[joe-bloggs,pat,ghost]&include=authors'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
const jsonResponse = res.body;
|
||||
const ids = _.map(jsonResponse.posts, 'id');
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
should.exist(jsonResponse.posts);
|
||||
testUtils.API.checkResponse(jsonResponse, 'posts');
|
||||
testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
|
||||
|
||||
// 2. The data part of the response should be correct
|
||||
// We should have 2 matching items
|
||||
jsonResponse.posts.should.be.an.Array().with.lengthOf(11);
|
||||
|
||||
// Each post must either have the author 'joe-bloggs' or 'ghost', 'pat' is non existing author
|
||||
const authors = _.map(jsonResponse.posts, function (post) {
|
||||
return post.primary_author.slug;
|
||||
});
|
||||
|
||||
authors.should.matchAny(/joe-bloggs|ghost'/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('browse posts with page filter', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('posts/?client_id=ghost-admin&client_secret=not_available&filter=page:true'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
const jsonResponse = res.body;
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
should.exist(jsonResponse.posts);
|
||||
testUtils.API.checkResponse(jsonResponse, 'posts');
|
||||
testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
|
||||
|
||||
jsonResponse.posts.should.be.an.Array().with.lengthOf(1);
|
||||
|
||||
const page = _.map(jsonResponse.posts, 'page');
|
||||
page.should.matchEach(true);
|
||||
|
||||
jsonResponse.posts[0].id.should.equal(testUtils.DataGenerator.Content.posts[5].id);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('browse posts with published and draft status, should not return drafts', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('posts/?client_id=ghost-admin&client_secret=not_available&filter=status:published,status:draft'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
const jsonResponse = res.body;
|
||||
|
||||
jsonResponse.posts.should.be.an.Array().with.lengthOf(11);
|
||||
jsonResponse.posts.forEach((post) => {
|
||||
post.page.should.be.false();
|
||||
post.status.should.eql('published');
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('[deprecated] browse posts with page non matching filter', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('posts/?client_id=ghost-admin&client_secret=not_available&filter=tag:no-posts'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
const jsonResponse = res.body;
|
||||
|
||||
jsonResponse.posts.should.be.an.Array().with.lengthOf(0);
|
||||
|
||||
jsonResponse.meta.should.have.property('pagination');
|
||||
jsonResponse.meta.pagination.should.be.an.Object().with.properties(['page', 'limit', 'pages', 'total', 'next', 'prev']);
|
||||
jsonResponse.meta.pagination.page.should.eql(1);
|
||||
jsonResponse.meta.pagination.limit.should.eql(15);
|
||||
jsonResponse.meta.pagination.pages.should.eql(1);
|
||||
jsonResponse.meta.pagination.total.should.eql(0);
|
||||
should.equal(jsonResponse.meta.pagination.next, null);
|
||||
should.equal(jsonResponse.meta.pagination.prev, null);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('browse posts: request absolute urls', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('posts/?client_id=ghost-admin&client_secret=not_available&absolute_urls=true'))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
@ -341,6 +522,34 @@ describe('Public API', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('browse tags - limit=all should fetch all tags and include count.posts', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('tags/?limit=all&client_id=ghost-admin&client_secret=not_available&include=count.posts'))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
const jsonResponse = res.body;
|
||||
|
||||
should.exist(jsonResponse.tags);
|
||||
jsonResponse.tags.should.be.an.Array().with.lengthOf(56);
|
||||
|
||||
// Each tag should have the correct count
|
||||
_.find(jsonResponse.tags, {name: 'Getting Started'}).count.posts.should.eql(7);
|
||||
_.find(jsonResponse.tags, {name: 'kitchen sink'}).count.posts.should.eql(2);
|
||||
_.find(jsonResponse.tags, {name: 'bacon'}).count.posts.should.eql(2);
|
||||
_.find(jsonResponse.tags, {name: 'chorizo'}).count.posts.should.eql(1);
|
||||
_.find(jsonResponse.tags, {name: 'pollo'}).count.posts.should.eql(0);
|
||||
_.find(jsonResponse.tags, {name: 'injection'}).count.posts.should.eql(0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('denies access with invalid client_secret', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('posts/?client_id=ghost-admin&client_secret=invalid_secret'))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
@ -462,11 +671,17 @@ describe('Public API', function () {
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.users);
|
||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
||||
jsonResponse.users.should.have.length(6);
|
||||
jsonResponse.users.should.have.length(7);
|
||||
|
||||
// We don't expose the email address.
|
||||
// We don't expose the email address, status and other attrs.
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', null, null, null, {public: true});
|
||||
done();
|
||||
|
||||
// Public api returns all users, but no status! Locked/Inactive users can still have written articles.
|
||||
models.User.findPage(Object.assign({status: 'all'}, testUtils.context.internal))
|
||||
.then((response) => {
|
||||
_.map(response.data, (model) => model.toJSON()).length.should.eql(7);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -485,7 +700,7 @@ describe('Public API', function () {
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.users);
|
||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
||||
jsonResponse.users.should.have.length(6);
|
||||
jsonResponse.users.should.have.length(7);
|
||||
|
||||
// We don't expose the email address.
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', null, null, null, {public: true});
|
||||
@ -581,6 +796,51 @@ describe('Public API', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('browse user with count.posts', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('users/?client_id=ghost-admin&client_secret=not_available&include=count.posts&order=count.posts ASC'))
|
||||
.set('Origin', testUtils.API.getURL())
|
||||
.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.users);
|
||||
jsonResponse.users.should.have.length(7);
|
||||
|
||||
// We don't expose the email address.
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['count'], null, null, {public: true});
|
||||
|
||||
// Each user should have the correct count
|
||||
_.find(jsonResponse.users, {slug:'joe-bloggs'}).count.posts.should.eql(4);
|
||||
_.find(jsonResponse.users, {slug:'contributor'}).count.posts.should.eql(0);
|
||||
_.find(jsonResponse.users, {slug:'slimer-mcectoplasm'}).count.posts.should.eql(0);
|
||||
_.find(jsonResponse.users, {slug:'jimothy-bogendath'}).count.posts.should.eql(0);
|
||||
_.find(jsonResponse.users, {slug: 'smith-wellingsworth'}).count.posts.should.eql(0);
|
||||
_.find(jsonResponse.users, {slug:'ghost'}).count.posts.should.eql(7);
|
||||
_.find(jsonResponse.users, {slug:'inactive'}).count.posts.should.eql(0);
|
||||
|
||||
const ids = jsonResponse.users
|
||||
.filter(user => (user.slug !== 'ghost'))
|
||||
.filter(user => (user.slug !== 'inactive'))
|
||||
.map(user=> user.id);
|
||||
|
||||
ids.should.eql([
|
||||
testUtils.DataGenerator.Content.users[1].id,
|
||||
testUtils.DataGenerator.Content.users[2].id,
|
||||
testUtils.DataGenerator.Content.users[3].id,
|
||||
testUtils.DataGenerator.Content.users[7].id,
|
||||
testUtils.DataGenerator.Content.users[0].id
|
||||
]);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('[unsupported] browse user by email', function (done) {
|
||||
request
|
||||
.get(localUtils.API.getApiQuery('users/email/ghost-author@ghost.org/?client_id=ghost-admin&client_secret=not_available'))
|
||||
@ -614,7 +874,6 @@ describe('Public API', function () {
|
||||
should.exist(jsonResponse.users);
|
||||
jsonResponse.users.should.have.length(1);
|
||||
|
||||
// We don't expose the email address.
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', null, null, null, {public: true});
|
||||
done();
|
||||
});
|
||||
@ -635,7 +894,7 @@ describe('Public API', function () {
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.users);
|
||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
||||
jsonResponse.users.should.have.length(6);
|
||||
jsonResponse.users.should.have.length(7);
|
||||
|
||||
// We don't expose the email address.
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['count'], null, null, {public: true});
|
||||
@ -658,7 +917,7 @@ describe('Public API', function () {
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.users);
|
||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
||||
jsonResponse.users.should.have.length(6);
|
||||
jsonResponse.users.should.have.length(7);
|
||||
|
||||
// We don't expose the email address.
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', null, null, null, {public: true});
|
||||
|
84
core/test/functional/routes/api/roles_spec.js
Normal file
84
core/test/functional/routes/api/roles_spec.js
Normal file
@ -0,0 +1,84 @@
|
||||
const should = require('should');
|
||||
const supertest = require('supertest');
|
||||
const testUtils = require('../../../utils');
|
||||
const localUtils = require('./utils');
|
||||
const config = require('../../../../../core/server/config');
|
||||
const ghost = testUtils.startGhost;
|
||||
let request;
|
||||
|
||||
describe('Roles API', function () {
|
||||
var accesstoken = '', ghostServer;
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
return localUtils.doAuth(request, 'posts');
|
||||
})
|
||||
.then(function (token) {
|
||||
accesstoken = token;
|
||||
});
|
||||
});
|
||||
|
||||
describe('browse', function () {
|
||||
it('default', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('roles/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.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']);
|
||||
const response = res.body;
|
||||
should.exist(response);
|
||||
should.exist(response.roles);
|
||||
testUtils.API.checkResponse(response, 'roles');
|
||||
response.roles.should.have.length(6);
|
||||
testUtils.API.checkResponse(response.roles[0], 'role');
|
||||
testUtils.API.checkResponse(response.roles[1], 'role');
|
||||
testUtils.API.checkResponse(response.roles[2], 'role');
|
||||
testUtils.API.checkResponse(response.roles[3], 'role');
|
||||
testUtils.API.checkResponse(response.roles[4], 'role');
|
||||
testUtils.API.checkResponse(response.roles[5], 'role');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('permissions=assign', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('roles/?permissions=assign'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.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']);
|
||||
const response = res.body;
|
||||
should.exist(response.roles);
|
||||
testUtils.API.checkResponse(response, 'roles');
|
||||
response.roles.should.have.length(4);
|
||||
testUtils.API.checkResponse(response.roles[0], 'role');
|
||||
testUtils.API.checkResponse(response.roles[1], 'role');
|
||||
testUtils.API.checkResponse(response.roles[2], 'role');
|
||||
testUtils.API.checkResponse(response.roles[3], 'role');
|
||||
response.roles[0].name.should.equal('Administrator');
|
||||
response.roles[1].name.should.equal('Editor');
|
||||
response.roles[2].name.should.equal('Author');
|
||||
response.roles[3].name.should.equal('Contributor');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
135
core/test/functional/routes/api/schedules_spec.js
Normal file
135
core/test/functional/routes/api/schedules_spec.js
Normal file
@ -0,0 +1,135 @@
|
||||
const should = require('should');
|
||||
const supertest = require('supertest');
|
||||
const Promise = require('bluebird');
|
||||
const sinon = require('sinon');
|
||||
const moment = require('moment-timezone');
|
||||
const testUtils = require('../../../utils');
|
||||
const localUtils = require('./utils');
|
||||
const SchedulingDefault = require('../../../../../core/server/adapters/scheduling/SchedulingDefault');
|
||||
const models = require('../../../../../core/server/models');
|
||||
const config = require('../../../../../core/server/config');
|
||||
const ghost = testUtils.startGhost;
|
||||
const sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Schedules API', function () {
|
||||
const posts = [];
|
||||
let request;
|
||||
let accesstoken;
|
||||
let ghostServer;
|
||||
|
||||
before(function () {
|
||||
models.init();
|
||||
|
||||
// @NOTE: mock the post scheduler, otherwise it will auto publish the post
|
||||
sandbox.stub(SchedulingDefault.prototype, '_pingUrl').resolves();
|
||||
});
|
||||
|
||||
after(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function (token) {
|
||||
accesstoken = token;
|
||||
|
||||
posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.existingData.users[0].id,
|
||||
author_id: testUtils.existingData.users[0].id,
|
||||
published_by: testUtils.existingData.users[0].id,
|
||||
published_at: moment().add(30, 'seconds').toDate(),
|
||||
status: 'scheduled',
|
||||
slug: 'first'
|
||||
}));
|
||||
|
||||
posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.existingData.users[0].id,
|
||||
author_id: testUtils.existingData.users[0].id,
|
||||
published_by: testUtils.existingData.users[0].id,
|
||||
published_at: moment().subtract(30, 'seconds').toDate(),
|
||||
status: 'scheduled',
|
||||
slug: 'second'
|
||||
}));
|
||||
|
||||
posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.existingData.users[0].id,
|
||||
author_id: testUtils.existingData.users[0].id,
|
||||
published_by: testUtils.existingData.users[0].id,
|
||||
published_at: moment().add(10, 'minute').toDate(),
|
||||
status: 'scheduled',
|
||||
slug: 'third'
|
||||
}));
|
||||
|
||||
posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.existingData.users[0].id,
|
||||
author_id: testUtils.existingData.users[0].id,
|
||||
published_by: testUtils.existingData.users[0].id,
|
||||
published_at: moment().subtract(10, 'minute').toDate(),
|
||||
status: 'scheduled',
|
||||
slug: 'fourth'
|
||||
}));
|
||||
|
||||
return Promise.mapSeries(posts, function (post) {
|
||||
return models.Post.add(post, {context: {internal: true}});
|
||||
}).then(function (result) {
|
||||
result.length.should.eql(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('publish', function () {
|
||||
it('default', function () {
|
||||
return request
|
||||
.put(localUtils.API.getApiQuery(`schedules/posts/${posts[0].id}/?client_id=ghost-scheduler&client_secret=${testUtils.existingData.clients[0].secret}`))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
should.exist(res.headers['x-cache-invalidate']);
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
jsonResponse.posts[0].id.should.eql(posts[0].id);
|
||||
jsonResponse.posts[0].status.should.eql('published');
|
||||
});
|
||||
});
|
||||
|
||||
it('no access', function () {
|
||||
return request
|
||||
.put(localUtils.API.getApiQuery(`schedules/posts/${posts[0].id}/?client_id=ghost-admin&client_secret=${testUtils.existingData.clients[0].secret}`))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(403);
|
||||
});
|
||||
|
||||
it('published_at is x seconds in past, but still in tolerance', function () {
|
||||
return request
|
||||
.put(localUtils.API.getApiQuery(`schedules/posts/${posts[1].id}/?client_id=ghost-scheduler&client_secret=${testUtils.existingData.clients[0].secret}`))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it('not found', function () {
|
||||
return request
|
||||
.put(localUtils.API.getApiQuery(`schedules/posts/${posts[2].id}/?client_id=ghost-scheduler&client_secret=${testUtils.existingData.clients[0].secret}`))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(404);
|
||||
});
|
||||
|
||||
it('force publish', function () {
|
||||
return request
|
||||
.put(localUtils.API.getApiQuery(`schedules/posts/${posts[3].id}/?client_id=ghost-scheduler&client_secret=${testUtils.existingData.clients[0].secret}`))
|
||||
.send({
|
||||
force: true
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200);
|
||||
});
|
||||
});
|
||||
});
|
@ -26,8 +26,11 @@ describe('Settings API', function () {
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: currently includes values of type=core
|
||||
it('can retrieve all settings', function (done) {
|
||||
after(function () {
|
||||
return ghostServer.stop();
|
||||
});
|
||||
|
||||
it('browse', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('settings/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.expect('Content-Type', /json/)
|
||||
@ -48,11 +51,18 @@ describe('Settings API', function () {
|
||||
JSON.parse(_.find(jsonResponse.settings, {key: 'amp'}).value).should.eql(true);
|
||||
should.not.exist(_.find(jsonResponse.settings, {key: 'permalinks'}));
|
||||
|
||||
testUtils.API.isISO8601(jsonResponse.settings[0].created_at).should.be.true();
|
||||
jsonResponse.settings[0].created_at.should.be.an.instanceof(String);
|
||||
|
||||
should.not.exist(_.find(jsonResponse.settings, function (setting) {
|
||||
return setting.type === 'core';
|
||||
}));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can retrieve a setting', function (done) {
|
||||
it('read', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('settings/title/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.expect('Content-Type', /json/)
|
||||
@ -76,7 +86,7 @@ describe('Settings API', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t retrieve permalinks', function (done) {
|
||||
it('can\'t read permalinks', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('settings/permalinks/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.expect('Content-Type', /json/)
|
||||
@ -91,7 +101,7 @@ describe('Settings API', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t retrieve non existent setting', function (done) {
|
||||
it('can\'t read non existent setting', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('settings/testsetting/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.set('Accept', 'application/json')
|
||||
@ -123,7 +133,7 @@ describe('Settings API', function () {
|
||||
}
|
||||
|
||||
var jsonResponse = res.body,
|
||||
changedValue = 'Ghost changed',
|
||||
changedValue = [],
|
||||
settingToChange = {
|
||||
settings: [
|
||||
{key: 'title', value: changedValue}
|
||||
@ -147,7 +157,7 @@ describe('Settings API', function () {
|
||||
var putBody = res.body;
|
||||
res.headers['x-cache-invalidate'].should.eql('/*');
|
||||
should.exist(putBody);
|
||||
putBody.settings[0].value.should.eql(changedValue);
|
||||
putBody.settings[0].value.should.eql(JSON.stringify(changedValue));
|
||||
testUtils.API.checkResponse(putBody, 'settings');
|
||||
done();
|
||||
});
|
||||
|
80
core/test/functional/routes/api/subscribers_spec.js
Normal file
80
core/test/functional/routes/api/subscribers_spec.js
Normal file
@ -0,0 +1,80 @@
|
||||
const should = require('should');
|
||||
const supertest = require('supertest');
|
||||
const sinon = require('sinon');
|
||||
const testUtils = require('../../../utils');
|
||||
const localUtils = require('./utils');
|
||||
const config = require('../../../../../core/server/config');
|
||||
const labs = require('../../../../../core/server/services/labs');
|
||||
const ghost = testUtils.startGhost;
|
||||
const sandbox = sinon.sandbox.create();
|
||||
let request;
|
||||
|
||||
describe('Subscribers API', function () {
|
||||
let accesstoken = '', ghostServer;
|
||||
|
||||
before(function () {
|
||||
sandbox.stub(labs, 'isSet').withArgs('subscribers').returns(true);
|
||||
});
|
||||
|
||||
after(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
return localUtils.doAuth(request, 'subscriber');
|
||||
})
|
||||
.then(function (token) {
|
||||
accesstoken = token;
|
||||
});
|
||||
});
|
||||
|
||||
it('browse', function () {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery('subscribers/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.subscribers);
|
||||
jsonResponse.subscribers.should.have.length(1);
|
||||
testUtils.API.checkResponse(jsonResponse.subscribers[0], 'subscriber');
|
||||
|
||||
testUtils.API.isISO8601(jsonResponse.subscribers[0].created_at).should.be.true();
|
||||
jsonResponse.subscribers[0].created_at.should.be.an.instanceof(String);
|
||||
|
||||
jsonResponse.meta.pagination.should.have.property('page', 1);
|
||||
jsonResponse.meta.pagination.should.have.property('limit', 15);
|
||||
jsonResponse.meta.pagination.should.have.property('pages', 1);
|
||||
jsonResponse.meta.pagination.should.have.property('total', 1);
|
||||
jsonResponse.meta.pagination.should.have.property('next', null);
|
||||
jsonResponse.meta.pagination.should.have.property('prev', null);
|
||||
});
|
||||
});
|
||||
|
||||
it('read', function () {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery(`subscribers/${testUtils.DataGenerator.Content.subscribers[0].id}/`))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.subscribers);
|
||||
jsonResponse.subscribers.should.have.length(1);
|
||||
testUtils.API.checkResponse(jsonResponse.subscribers[0], 'subscriber');
|
||||
});
|
||||
});
|
||||
});
|
@ -23,26 +23,137 @@ describe('Tag API', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('can retrieve all tags', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('tags/'))
|
||||
it('browse', function () {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery('tags/?include=count.posts'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
.then((res) => {
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body;
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.tags);
|
||||
jsonResponse.tags.should.have.length(6);
|
||||
testUtils.API.checkResponse(jsonResponse.tags[0], 'tag');
|
||||
testUtils.API.isISO8601(jsonResponse.tags[0].created_at).should.be.true();
|
||||
testUtils.API.checkResponse(jsonResponse.tags[0], 'tag', 'count');
|
||||
|
||||
done();
|
||||
testUtils.API.isISO8601(jsonResponse.tags[0].created_at).should.be.true();
|
||||
jsonResponse.tags[0].created_at.should.be.an.instanceof(String);
|
||||
|
||||
jsonResponse.meta.pagination.should.have.property('page', 1);
|
||||
jsonResponse.meta.pagination.should.have.property('limit', 15);
|
||||
jsonResponse.meta.pagination.should.have.property('pages', 1);
|
||||
jsonResponse.meta.pagination.should.have.property('total', 6);
|
||||
jsonResponse.meta.pagination.should.have.property('next', null);
|
||||
jsonResponse.meta.pagination.should.have.property('prev', null);
|
||||
|
||||
should.exist(jsonResponse.tags[0].count.posts);
|
||||
});
|
||||
});
|
||||
|
||||
it('read', function () {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery(`tags/${testUtils.existingData.tags[0].id}/?include=count.posts`))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.tags);
|
||||
jsonResponse.tags.should.have.length(1);
|
||||
testUtils.API.checkResponse(jsonResponse.tags[0], 'tag', 'count');
|
||||
should.exist(jsonResponse.tags[0].count.posts);
|
||||
});
|
||||
});
|
||||
|
||||
it('add', function () {
|
||||
const tag = testUtils.DataGenerator.forKnex.createTag();
|
||||
|
||||
return request
|
||||
.post(localUtils.API.getApiQuery('tags/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.send({
|
||||
tags: [tag]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(201)
|
||||
.then((res) => {
|
||||
should.exist(res.headers['x-cache-invalidate']);
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.tags);
|
||||
jsonResponse.tags.should.have.length(1);
|
||||
// @TODO: model layer has no defaults for these properties
|
||||
testUtils.API.checkResponse(jsonResponse.tags[0], 'tag', null, [
|
||||
'feature_image',
|
||||
'meta_description',
|
||||
'meta_title',
|
||||
'parent'
|
||||
]);
|
||||
testUtils.API.isISO8601(jsonResponse.tags[0].created_at).should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('add internal', function () {
|
||||
const tag = testUtils.DataGenerator.forKnex.createTag({
|
||||
name: '#test',
|
||||
slug: null
|
||||
});
|
||||
|
||||
return request
|
||||
.post(localUtils.API.getApiQuery('tags/'))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.send({
|
||||
tags: [tag]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(201)
|
||||
.then((res) => {
|
||||
should.exist(res.headers['x-cache-invalidate']);
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
jsonResponse.tags[0].visibility.should.eql('internal');
|
||||
jsonResponse.tags[0].name.should.eql('#test');
|
||||
jsonResponse.tags[0].slug.should.eql('hash-test');
|
||||
});
|
||||
});
|
||||
|
||||
it('edit', function () {
|
||||
return request
|
||||
.put(localUtils.API.getApiQuery(`tags/${testUtils.existingData.tags[0].id}`))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.send({
|
||||
tags: [Object.assign({}, testUtils.existingData.tags[0], {description: 'hey ho ab ins klo'})]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
should.exist(res.headers['x-cache-invalidate']);
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.tags);
|
||||
jsonResponse.tags.should.have.length(1);
|
||||
testUtils.API.checkResponse(jsonResponse.tags[0], 'tag');
|
||||
jsonResponse.tags[0].description.should.eql('hey ho ab ins klo');
|
||||
});
|
||||
});
|
||||
|
||||
it('destroy', function () {
|
||||
return request
|
||||
.del(localUtils.API.getApiQuery(`tags/${testUtils.existingData.tags[0].id}`))
|
||||
.set('Authorization', 'Bearer ' + accesstoken)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(204)
|
||||
.then((res) => {
|
||||
should.exist(res.headers['x-cache-invalidate']);
|
||||
res.body.should.eql({});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2,10 +2,12 @@ var should = require('should'),
|
||||
_ = require('lodash'),
|
||||
supertest = require('supertest'),
|
||||
moment = require('moment'),
|
||||
Promise = require('bluebird'),
|
||||
testUtils = require('../../../utils'),
|
||||
localUtils = require('./utils'),
|
||||
ObjectId = require('bson-objectid'),
|
||||
config = require('../../../../../core/server/config'),
|
||||
models = require('../../../../../core/server/models'),
|
||||
ghost = testUtils.startGhost,
|
||||
request;
|
||||
|
||||
@ -13,7 +15,7 @@ describe('User API', function () {
|
||||
var ownerAccessToken = '',
|
||||
editorAccessToken = '',
|
||||
authorAccessToken = '',
|
||||
editor, author, ghostServer, inactiveUser;
|
||||
editor, author, ghostServer, inactiveUser, admin;
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
@ -49,6 +51,15 @@ describe('User API', function () {
|
||||
.then(function (_user3) {
|
||||
inactiveUser = _user3;
|
||||
|
||||
// create admin user
|
||||
return testUtils.createUser({
|
||||
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+admin@ghost.org'}),
|
||||
role: testUtils.DataGenerator.Content.roles[0].name
|
||||
});
|
||||
})
|
||||
.then(function (_user4) {
|
||||
admin = _user4;
|
||||
|
||||
// by default we login with the owner
|
||||
return localUtils.doAuth(request);
|
||||
})
|
||||
@ -72,39 +83,8 @@ describe('User API', function () {
|
||||
describe('As Owner', function () {
|
||||
describe('Browse', function () {
|
||||
it('returns dates in ISO 8601 format', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('users/?order=id%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);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.users);
|
||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
||||
|
||||
// owner use + ghost-author user when Ghost starts
|
||||
// and two extra users, see createUser in before
|
||||
jsonResponse.users.should.have.length(5);
|
||||
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user');
|
||||
testUtils.API.isISO8601(jsonResponse.users[0].last_seen).should.be.true();
|
||||
testUtils.API.isISO8601(jsonResponse.users[0].created_at).should.be.true();
|
||||
testUtils.API.isISO8601(jsonResponse.users[0].updated_at).should.be.true();
|
||||
|
||||
testUtils.API.isISO8601(jsonResponse.users[2].last_seen).should.be.true();
|
||||
testUtils.API.isISO8601(jsonResponse.users[2].created_at).should.be.true();
|
||||
testUtils.API.isISO8601(jsonResponse.users[2].updated_at).should.be.true();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can retrieve all users', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('users/'))
|
||||
// @NOTE: ASC is default
|
||||
request.get(localUtils.API.getApiQuery('users/?order=id%20DESC'))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
@ -115,13 +95,29 @@ describe('User API', function () {
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.users);
|
||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
||||
|
||||
jsonResponse.users.should.have.length(5);
|
||||
// owner use + ghost-author user when Ghost starts
|
||||
// and two extra users, see createUser in before
|
||||
jsonResponse.users.should.have.length(6);
|
||||
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user');
|
||||
jsonResponse.users[4].status.should.eql(inactiveUser.status);
|
||||
testUtils.API.isISO8601(jsonResponse.users[5].last_seen).should.be.true();
|
||||
testUtils.API.isISO8601(jsonResponse.users[5].created_at).should.be.true();
|
||||
testUtils.API.isISO8601(jsonResponse.users[5].updated_at).should.be.true();
|
||||
|
||||
testUtils.API.isISO8601(jsonResponse.users[2].last_seen).should.be.true();
|
||||
testUtils.API.isISO8601(jsonResponse.users[2].created_at).should.be.true();
|
||||
testUtils.API.isISO8601(jsonResponse.users[2].updated_at).should.be.true();
|
||||
|
||||
jsonResponse.users[0].email.should.eql('test+admin@ghost.org');
|
||||
jsonResponse.users[1].email.should.eql('test+3@ghost.org');
|
||||
jsonResponse.users[1].status.should.eql(inactiveUser.status);
|
||||
jsonResponse.users[5].email.should.eql(testUtils.DataGenerator.Content.users[0].email);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -142,7 +138,7 @@ describe('User API', function () {
|
||||
should.exist(jsonResponse.users);
|
||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
||||
|
||||
jsonResponse.users.should.have.length(5);
|
||||
jsonResponse.users.should.have.length(6);
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', 'roles');
|
||||
done();
|
||||
});
|
||||
@ -421,7 +417,10 @@ describe('User API', function () {
|
||||
|
||||
dataToSend = {
|
||||
users: [
|
||||
{website: changedValue}
|
||||
{
|
||||
website: changedValue,
|
||||
password: 'mynewfancypasswordwhichisnotallowed'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@ -442,7 +441,21 @@ describe('User API', function () {
|
||||
putBody.users[0].website.should.eql(changedValue);
|
||||
putBody.users[0].email.should.eql(jsonResponse.users[0].email);
|
||||
testUtils.API.checkResponse(putBody.users[0], 'user');
|
||||
done();
|
||||
|
||||
should.not.exist(putBody.users[0].password);
|
||||
|
||||
models.User.findOne({id: putBody.users[0].id})
|
||||
.then((user) => {
|
||||
return models.User.isPasswordCorrect({
|
||||
plainPassword: 'mynewfancypasswordwhichisnotallowed',
|
||||
hashedPassword: user.get('password')
|
||||
});
|
||||
})
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
err.code.should.eql('PASSWORD_INCORRECT');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -528,16 +541,33 @@ describe('User API', function () {
|
||||
});
|
||||
|
||||
describe('Destroy', function () {
|
||||
it('[success] Destroy active user', function (done) {
|
||||
request.delete(localUtils.API.getApiQuery('users/' + editor.id))
|
||||
it('[success] Destroy active user', function () {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery(`posts/?filter=author_id:${testUtils.existingData.users[1].id}`))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
.expect(204)
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
res.body.posts.length.should.eql(7);
|
||||
|
||||
done();
|
||||
return request
|
||||
.delete(localUtils.API.getApiQuery(`users/${testUtils.existingData.users[1].id}`))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
.expect(204);
|
||||
})
|
||||
.then(() => {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery(`users/${testUtils.existingData.users[1].id}/`))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
.expect(404);
|
||||
})
|
||||
.then(() => {
|
||||
return request
|
||||
.get(localUtils.API.getApiQuery(`posts/?filter=author_id:${testUtils.existingData.users[1].id}`))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
.expect(200);
|
||||
})
|
||||
.then((res) => {
|
||||
res.body.posts.length.should.eql(0);
|
||||
});
|
||||
});
|
||||
|
||||
@ -556,6 +586,26 @@ describe('User API', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Transfer ownership', function () {
|
||||
it('Owner can transfer ownership to admin user', function () {
|
||||
return request
|
||||
.put(localUtils.API.getApiQuery('users/owner'))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
.send({
|
||||
owner: [{
|
||||
id: admin.id
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
res.body.users[0].roles[0].name.should.equal(testUtils.DataGenerator.Content.roles[0].name);
|
||||
res.body.users[1].roles[0].name.should.equal(testUtils.DataGenerator.Content.roles[3].name);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('As Editor', function () {
|
||||
before(function () {
|
||||
return ghost()
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,598 +0,0 @@
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
testUtils = require('../../utils'),
|
||||
_ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
AuthAPI = require('../../../server/api/v0.1/authentication'),
|
||||
mail = require('../../../server/api/v0.1/mail'),
|
||||
models = require('../../../server/models'),
|
||||
common = require('../../../server/lib/common'),
|
||||
security = require('../../../server/lib/security'),
|
||||
context = testUtils.context,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
User,
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Authentication API', function () {
|
||||
var testInvite = {
|
||||
invitation: [{
|
||||
token: 'abc',
|
||||
password: 'abcdefgh',
|
||||
email: 'test@testghost.org',
|
||||
name: 'Jo Bloggs'
|
||||
}]
|
||||
},
|
||||
testGenerateReset = {
|
||||
passwordreset: [{
|
||||
email: 'jbloggs@example.com'
|
||||
}]
|
||||
},
|
||||
testReset = {
|
||||
passwordreset: [{
|
||||
token: 'abc',
|
||||
newPassword: 'abcdefghij',
|
||||
ne2Password: 'abcdefghij'
|
||||
}]
|
||||
};
|
||||
|
||||
// Keep the DB clean
|
||||
before(testUtils.teardown);
|
||||
afterEach(testUtils.teardown);
|
||||
|
||||
// Stub mail
|
||||
beforeEach(function () {
|
||||
sandbox.stub(mail, 'send').callsFake(function () {
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
should.exist(AuthAPI);
|
||||
|
||||
describe('Setup', function () {
|
||||
describe('Cannot run', function () {
|
||||
before(function () {
|
||||
User = require('../../../server/models/user').User;
|
||||
});
|
||||
|
||||
beforeEach(testUtils.setup('owner:pre', 'settings', 'perms:setting', 'perms:mail', 'perms:init'));
|
||||
|
||||
describe('Invalid database state', function () {
|
||||
it('should not allow setup to be run if owner missing from database', function (done) {
|
||||
var setupData = {
|
||||
name: 'test user',
|
||||
email: 'test@example.com',
|
||||
password: 'thisissupersafe',
|
||||
blogTitle: 'a test blog'
|
||||
};
|
||||
|
||||
User.fetchAll().call('invokeThen', 'destroy').then(function () {
|
||||
AuthAPI.setup({setup: [setupData]}).then(function () {
|
||||
done(new Error('Setup ran when it should not have.'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
err.name.should.equal('NotFoundError');
|
||||
err.message.should.equal('Owner not found');
|
||||
err.statusCode.should.equal(404);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Not completed', function () {
|
||||
// TODO: stub settings
|
||||
beforeEach(testUtils.setup('owner:pre', 'settings', 'perms:setting', 'perms:mail', 'perms:init'));
|
||||
|
||||
it('should report that setup has not been completed', function (done) {
|
||||
AuthAPI.isSetup().then(function (result) {
|
||||
should.exist(result);
|
||||
result.setup[0].status.should.be.false();
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should allow setup to be completed', function (done) {
|
||||
var setupData = {
|
||||
name: 'test user',
|
||||
email: 'test@example.com',
|
||||
password: 'thisissupersafe',
|
||||
blogTitle: 'a test blog'
|
||||
};
|
||||
|
||||
AuthAPI.setup({setup: [setupData]}).then(function (result) {
|
||||
should.exist(result);
|
||||
should.exist(result.users);
|
||||
should.not.exist(result.meta);
|
||||
result.users.should.have.length(1);
|
||||
testUtils.API.checkResponse(result.users[0], 'user');
|
||||
|
||||
var newUser = result.users[0];
|
||||
|
||||
newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id);
|
||||
newUser.name.should.equal(setupData.name);
|
||||
newUser.email.should.equal(setupData.email);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should allow setup to be completed without a blog title', function (done) {
|
||||
var setupData = {
|
||||
name: 'test user',
|
||||
email: 'test@example.com',
|
||||
password: 'thisissupersafe'
|
||||
};
|
||||
|
||||
AuthAPI.setup({setup: [setupData]}).then(function (result) {
|
||||
should.exist(result);
|
||||
should.exist(result.users);
|
||||
should.not.exist(result.meta);
|
||||
result.users.should.have.length(1);
|
||||
testUtils.API.checkResponse(result.users[0], 'user');
|
||||
|
||||
var newUser = result.users[0];
|
||||
|
||||
newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id);
|
||||
newUser.name.should.equal(setupData.name);
|
||||
newUser.email.should.equal(setupData.email);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should return an error for an invitation check', function (done) {
|
||||
AuthAPI.isInvitation({email: 'a@example.com'}).then(function () {
|
||||
done(new Error('Did not receive an error response'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
|
||||
err.name.should.equal('NoPermissionError');
|
||||
err.statusCode.should.equal(403);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should not allow an invitation to be accepted', function (done) {
|
||||
AuthAPI.acceptInvitation(testInvite).then(function () {
|
||||
done(new Error('Invitation was allowed to be accepted'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
|
||||
err.name.should.equal('NoPermissionError');
|
||||
err.statusCode.should.equal(403);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should not generate a password reset token', function (done) {
|
||||
AuthAPI.generateResetToken(testGenerateReset).then(function () {
|
||||
done(new Error('Reset token was generated'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
|
||||
err.name.should.equal('NoPermissionError');
|
||||
err.statusCode.should.equal(403);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should not allow a password reset', function (done) {
|
||||
AuthAPI.resetPassword(testReset).then(function () {
|
||||
done(new Error('Password was reset'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
|
||||
err.name.should.equal('NoPermissionError');
|
||||
err.statusCode.should.equal(403);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Completed', function () {
|
||||
before(function () {
|
||||
accessToken = require('../../../server/models/accesstoken').Accesstoken;
|
||||
refreshToken = require('../../../server/models/refreshtoken').Refreshtoken;
|
||||
User = require('../../../server/models/user').User;
|
||||
});
|
||||
|
||||
beforeEach(testUtils.setup('invites', 'roles', 'owner', 'clients', 'settings', 'perms:setting', 'perms:mail', 'perms:init'));
|
||||
|
||||
it('should report that setup has been completed', function (done) {
|
||||
AuthAPI.isSetup().then(function (result) {
|
||||
should.exist(result);
|
||||
result.setup[0].status.should.be.true();
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should not allow setup to be run again', function (done) {
|
||||
var setupData = {
|
||||
name: 'test user',
|
||||
email: 'test@example.com',
|
||||
password: 'thisissupersafe',
|
||||
blogTitle: 'a test blog'
|
||||
};
|
||||
|
||||
AuthAPI.setup({setup: [setupData]}).then(function () {
|
||||
done(new Error('Setup was able to be run'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
|
||||
err.name.should.equal('NoPermissionError');
|
||||
err.statusCode.should.equal(403);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should allow an invitation to be accepted, but fail on token validation', function (done) {
|
||||
AuthAPI.acceptInvitation(testInvite).then(function () {
|
||||
done(new Error('invitation did not fail on token validation'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
|
||||
err.name.should.equal('NotFoundError');
|
||||
err.statusCode.should.equal(404);
|
||||
err.message.should.equal('Invite not found.');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should allow an invitation to be accepted', function () {
|
||||
var invite;
|
||||
|
||||
return models.Invite.add({
|
||||
email: '123@meins.de',
|
||||
role_id: testUtils.DataGenerator.Content.roles[0].id
|
||||
}, context.internal)
|
||||
.then(function (_invite) {
|
||||
invite = _invite;
|
||||
invite.toJSON().role_id.should.eql(testUtils.DataGenerator.Content.roles[0].id);
|
||||
|
||||
return models.Invite.edit({status: 'sent'}, _.merge({}, {id: invite.id}, context.internal));
|
||||
})
|
||||
.then(function () {
|
||||
return AuthAPI.acceptInvitation({
|
||||
invitation: [
|
||||
{
|
||||
token: invite.get('token'),
|
||||
email: invite.get('email'),
|
||||
name: invite.get('email'),
|
||||
password: 'tencharacterslong'
|
||||
}
|
||||
]
|
||||
});
|
||||
})
|
||||
.then(function (res) {
|
||||
should.exist(res.invitation[0].message);
|
||||
return models.Invite.findOne({id: invite.id}, context.internal);
|
||||
})
|
||||
.then(function (_invite) {
|
||||
should.not.exist(_invite);
|
||||
return models.User.findOne({
|
||||
email: invite.get('email')
|
||||
}, _.merge({withRelated: ['roles']}, context.internal));
|
||||
})
|
||||
.then(function (user) {
|
||||
user.toJSON().roles.length.should.eql(1);
|
||||
user.toJSON().roles[0].id.should.eql(testUtils.DataGenerator.Content.roles[0].id);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not allow an invitation to be accepted: expired', function () {
|
||||
var invite;
|
||||
|
||||
return models.Invite.add({email: '123@meins.de', role_id: testUtils.roles.ids.author}, context.internal)
|
||||
.then(function (_invite) {
|
||||
invite = _invite;
|
||||
|
||||
return models.Invite.edit({
|
||||
status: 'sent',
|
||||
expires: Date.now() - 10000
|
||||
}, _.merge({}, {id: invite.id}, context.internal));
|
||||
})
|
||||
.then(function () {
|
||||
return AuthAPI.acceptInvitation({
|
||||
invitation: [
|
||||
{
|
||||
token: invite.get('token'),
|
||||
email: invite.get('email'),
|
||||
name: invite.get('email'),
|
||||
password: 'tencharacterslong'
|
||||
}
|
||||
]
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
throw new Error('should not pass the test: expected expired invitation');
|
||||
})
|
||||
.catch(function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.NotFoundError).should.eql(true);
|
||||
err.message.should.eql('Invite is expired.');
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate a password reset token', function (done) {
|
||||
AuthAPI.generateResetToken(testGenerateReset).then(function (result) {
|
||||
should.exist(result);
|
||||
result.passwordreset.should.be.an.Array().with.lengthOf(1);
|
||||
result.passwordreset[0].should.have.property('message', 'Check your email for further instructions.');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should not generate a password reset token for an invalid email address', function (done) {
|
||||
var badResetRequest = {
|
||||
passwordreset: [{email: ''}]
|
||||
};
|
||||
|
||||
AuthAPI.generateResetToken(badResetRequest).then(function () {
|
||||
done(new Error('reset token was generated for invalid email address'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
|
||||
err.name.should.equal('BadRequestError');
|
||||
err.statusCode.should.equal(400);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should not allow a password reset', function (done) {
|
||||
AuthAPI.resetPassword(testReset).then(function () {
|
||||
done(new Error('password reset did not fail on token validation'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
|
||||
err.name.should.equal('UnauthorizedError');
|
||||
err.statusCode.should.equal(401);
|
||||
err.message.should.equal('Invalid token structure');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should allow an access token to be revoked', function (done) {
|
||||
var id = security.identifier.uid(191);
|
||||
|
||||
accessToken.add({
|
||||
token: id,
|
||||
expires: Date.now() + 8640000,
|
||||
user_id: testUtils.DataGenerator.Content.users[0].id,
|
||||
client_id: testUtils.DataGenerator.forKnex.clients[0].id
|
||||
}, testUtils.context.internal).then(function (token) {
|
||||
should.exist(token);
|
||||
token.get('token').should.equal(id);
|
||||
|
||||
return AuthAPI.revoke({
|
||||
token: token.get('token'),
|
||||
token_type_hint: 'access_token'
|
||||
});
|
||||
}).then(function (response) {
|
||||
should.exist(response);
|
||||
response.token.should.equal(id);
|
||||
|
||||
return accessToken.findOne({token: id});
|
||||
}).then(function (token) {
|
||||
should.not.exist(token);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should know an email address has an active invitation', function () {
|
||||
return AuthAPI.isInvitation({email: testUtils.DataGenerator.forKnex.invites[0].email})
|
||||
.then(function (response) {
|
||||
should.exist(response);
|
||||
response.invitation[0].valid.should.be.true();
|
||||
response.invitation[0].invitedBy.should.eql('Joe Bloggs');
|
||||
});
|
||||
});
|
||||
|
||||
it('should know an email address does not have an active invitation', function (done) {
|
||||
var user = {
|
||||
name: 'uninvited user',
|
||||
email: 'notinvited@example.com',
|
||||
password: 'thisissupersafe',
|
||||
status: 'active'
|
||||
},
|
||||
options = {
|
||||
context: {internal: true}
|
||||
};
|
||||
|
||||
User.add(user, options).then(function (user) {
|
||||
return AuthAPI.isInvitation({email: user.get('email')});
|
||||
}).then(function (response) {
|
||||
should.exist(response);
|
||||
response.invitation[0].valid.should.be.false();
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should know an unknown email address is not an active invitation', function (done) {
|
||||
AuthAPI.isInvitation({email: 'unknown@example.com'}).then(function (response) {
|
||||
should.exist(response);
|
||||
response.invitation[0].valid.should.be.false();
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should allow a refresh token to be revoked', function (done) {
|
||||
var id = security.identifier.uid(191);
|
||||
|
||||
refreshToken.add({
|
||||
token: id,
|
||||
expires: Date.now() + 8640000,
|
||||
user_id: testUtils.DataGenerator.Content.users[0].id,
|
||||
client_id: testUtils.DataGenerator.forKnex.clients[0].id
|
||||
}).then(function (token) {
|
||||
should.exist(token);
|
||||
token.get('token').should.equal(id);
|
||||
|
||||
return AuthAPI.revoke({
|
||||
token: token.get('token'),
|
||||
token_type_hint: 'refresh_token'
|
||||
});
|
||||
}).then(function (response) {
|
||||
should.exist(response);
|
||||
response.token.should.equal(id);
|
||||
|
||||
return refreshToken.findOne({token: id});
|
||||
}).then(function (token) {
|
||||
should.not.exist(token);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should return success when attempting to revoke an invalid token', function (done) {
|
||||
var id = security.identifier.uid(191);
|
||||
|
||||
accessToken.add({
|
||||
token: id,
|
||||
expires: Date.now() + 8640000,
|
||||
user_id: testUtils.DataGenerator.Content.users[0].id,
|
||||
client_id: testUtils.DataGenerator.forKnex.clients[0].id
|
||||
}).then(function (token) {
|
||||
should.exist(token);
|
||||
token.get('token').should.equal(id);
|
||||
|
||||
return AuthAPI.revoke({
|
||||
token: 'notavalidtoken',
|
||||
token_type_hint: 'access_token'
|
||||
});
|
||||
}).then(function (response) {
|
||||
should.exist(response);
|
||||
response.token.should.equal('notavalidtoken');
|
||||
response.error.should.equal('Invalid token provided');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Setup Update', function () {
|
||||
describe('Setup not complete', function () {
|
||||
beforeEach(testUtils.setup('owner:pre', 'settings', 'perms:setting', 'perms:init'));
|
||||
|
||||
it('should report that setup has not been completed', function (done) {
|
||||
AuthAPI.isSetup().then(function (result) {
|
||||
should.exist(result);
|
||||
result.setup[0].status.should.be.false();
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should not allow setup to be updated', function (done) {
|
||||
var setupData = {
|
||||
name: 'test user',
|
||||
email: 'test@example.com',
|
||||
password: 'thisissupersafe',
|
||||
blogTitle: 'a test blog'
|
||||
};
|
||||
|
||||
AuthAPI.updateSetup({setup: [setupData]}, {}).then(function () {
|
||||
done(new Error('Update was able to be run'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
|
||||
err.name.should.equal('NoPermissionError');
|
||||
err.statusCode.should.equal(403);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Not Owner', function () {
|
||||
beforeEach(testUtils.setup('users:roles', 'settings', 'perms:setting', 'perms:init', 'perms:user'));
|
||||
|
||||
it('should report that setup has been completed', function (done) {
|
||||
AuthAPI.isSetup().then(function (result) {
|
||||
should.exist(result);
|
||||
result.setup[0].status.should.be.true();
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should not allow setup to be updated', function (done) {
|
||||
var setupData = {
|
||||
name: 'test user',
|
||||
email: 'test@example.com',
|
||||
password: 'thisissupersafe',
|
||||
blogTitle: 'a test blog'
|
||||
};
|
||||
|
||||
AuthAPI.updateSetup({setup: [setupData]}, context.author).then(function () {
|
||||
done(new Error('Update was able to be run'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
|
||||
err.name.should.equal('NoPermissionError');
|
||||
err.statusCode.should.equal(403);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Owner', function () {
|
||||
beforeEach(testUtils.setup('users:roles', 'settings', 'perms:setting', 'perms:init'));
|
||||
|
||||
it('should report that setup has been completed', function (done) {
|
||||
AuthAPI.isSetup().then(function (result) {
|
||||
should.exist(result);
|
||||
result.setup[0].status.should.be.true();
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should allow setup to be updated', function (done) {
|
||||
var setupData = {
|
||||
name: 'test user',
|
||||
email: 'test@example.com',
|
||||
password: 'thisissupersafe',
|
||||
blogTitle: 'a test blog'
|
||||
};
|
||||
|
||||
AuthAPI.updateSetup({setup: [setupData]}, context.owner).then(function (result) {
|
||||
should.exist(result);
|
||||
should.exist(result.users);
|
||||
should.not.exist(result.meta);
|
||||
result.users.should.have.length(1);
|
||||
testUtils.API.checkResponse(result.users[0], 'user');
|
||||
|
||||
var newUser = result.users[0];
|
||||
|
||||
newUser.id.should.equal(testUtils.DataGenerator.Content.users[0].id);
|
||||
newUser.name.should.equal(setupData.name);
|
||||
newUser.email.should.equal(setupData.email);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,64 +0,0 @@
|
||||
var should = require('should'),
|
||||
testUtils = require('../../utils'),
|
||||
rewire = require('rewire'),
|
||||
configUtils = require('../../utils/configUtils'),
|
||||
|
||||
// Stuff we are testing
|
||||
ConfigurationAPI = rewire('../../../server/api/v0.1/configuration');
|
||||
|
||||
describe('Configuration API', function () {
|
||||
// Keep the DB clean
|
||||
before(testUtils.teardown);
|
||||
beforeEach(testUtils.setup('clients', 'settings'));
|
||||
afterEach(function () {
|
||||
configUtils.restore();
|
||||
return testUtils.teardown();
|
||||
});
|
||||
|
||||
should.exist(ConfigurationAPI);
|
||||
|
||||
it('can read basic config and get all expected properties', function (done) {
|
||||
ConfigurationAPI.read().then(function (response) {
|
||||
var props;
|
||||
|
||||
should.exist(response);
|
||||
should.exist(response.configuration);
|
||||
response.configuration.should.be.an.Array().with.lengthOf(1);
|
||||
props = response.configuration[0];
|
||||
|
||||
props.blogUrl.should.eql('http://127.0.0.1:2369/');
|
||||
|
||||
props.useGravatar.should.eql(false);
|
||||
props.publicAPI.should.eql(true);
|
||||
props.clientId.should.eql('ghost-admin');
|
||||
props.clientSecret.should.eql('not_available');
|
||||
|
||||
// value not available, because settings API was not called yet
|
||||
props.hasOwnProperty('blogTitle').should.eql(true);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can read about config and get all expected properties', function (done) {
|
||||
ConfigurationAPI.read({key: 'about'}).then(function (response) {
|
||||
var props;
|
||||
|
||||
should.exist(response);
|
||||
should.exist(response.configuration);
|
||||
response.configuration.should.be.an.Array().with.lengthOf(1);
|
||||
props = response.configuration[0];
|
||||
|
||||
// Check the structure
|
||||
props.should.have.property('version').which.is.a.String();
|
||||
props.should.have.property('environment').which.is.a.String();
|
||||
props.should.have.property('database').which.is.a.String();
|
||||
props.should.have.property('mail').which.is.a.String();
|
||||
|
||||
// Check a few values
|
||||
props.environment.should.match(/^testing/);
|
||||
props.version.should.eql(require('../../../../package.json').version);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
@ -1,178 +0,0 @@
|
||||
var should = require('should'),
|
||||
_ = require('lodash'),
|
||||
sinon = require('sinon'),
|
||||
testUtils = require('../../utils'),
|
||||
common = require('../../../server/lib/common'),
|
||||
dbAPI = require('../../../server/api/v0.1/db'),
|
||||
models = require('../../../server/models'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('DB API', function () {
|
||||
var eventsTriggered;
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
// Keep the DB clean
|
||||
before(testUtils.teardown);
|
||||
afterEach(testUtils.teardown);
|
||||
beforeEach(testUtils.setup('users:roles', 'settings', 'posts', 'subscriber', 'perms:db', 'perms:init'));
|
||||
|
||||
beforeEach(function () {
|
||||
eventsTriggered = {};
|
||||
|
||||
sandbox.stub(common.events, 'emit').callsFake(function (eventName, eventObj) {
|
||||
if (!eventsTriggered[eventName]) {
|
||||
eventsTriggered[eventName] = [];
|
||||
}
|
||||
|
||||
eventsTriggered[eventName].push(eventObj);
|
||||
});
|
||||
});
|
||||
|
||||
should.exist(dbAPI);
|
||||
|
||||
it('delete all content (owner)', function () {
|
||||
return models.Post.findAll(testUtils.context.internal)
|
||||
.then(function (results) {
|
||||
results = results.toJSON();
|
||||
|
||||
results.length.should.eql(8);
|
||||
|
||||
_.filter(results, {page: false, status: 'published'}).length.should.equal(4);
|
||||
_.filter(results, {page: false, status: 'draft'}).length.should.equal(1);
|
||||
_.filter(results, {page: false, status: 'scheduled'}).length.should.equal(1);
|
||||
_.filter(results, {page: true, status: 'published'}).length.should.equal(1);
|
||||
_.filter(results, {page: true, status: 'draft'}).length.should.equal(1);
|
||||
})
|
||||
.then(function () {
|
||||
return dbAPI.deleteAllContent(testUtils.context.owner);
|
||||
})
|
||||
.then(function (result) {
|
||||
should.exist(result.db);
|
||||
result.db.should.be.instanceof(Array);
|
||||
result.db.should.be.empty();
|
||||
|
||||
return models.Tag.findAll(testUtils.context.internal);
|
||||
})
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
results.length.should.equal(0);
|
||||
|
||||
return models.Post.findAll(testUtils.context.internal);
|
||||
})
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
results.length.should.equal(0);
|
||||
|
||||
return models.Subscriber.findAll(testUtils.context.internal);
|
||||
})
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
results.length.should.equal(1);
|
||||
})
|
||||
.then(function () {
|
||||
eventsTriggered['post.unpublished'].length.should.eql(4);
|
||||
eventsTriggered['post.deleted'].length.should.eql(6);
|
||||
|
||||
eventsTriggered['page.unpublished'].length.should.eql(1);
|
||||
eventsTriggered['page.deleted'].length.should.eql(2);
|
||||
|
||||
eventsTriggered['tag.deleted'].length.should.eql(5);
|
||||
});
|
||||
});
|
||||
|
||||
it('delete all content (admin)', function () {
|
||||
return dbAPI.deleteAllContent(testUtils.context.admin).then(function (result) {
|
||||
should.exist(result.db);
|
||||
result.db.should.be.instanceof(Array);
|
||||
result.db.should.be.empty();
|
||||
}).then(function () {
|
||||
return models.Tag.findAll(testUtils.context.admin).then(function (results) {
|
||||
should.exist(results);
|
||||
results.length.should.equal(0);
|
||||
});
|
||||
}).then(function () {
|
||||
return models.Post.findAll(testUtils.context.admin).then(function (results) {
|
||||
should.exist(results);
|
||||
results.length.should.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('delete all content is denied (editor, author, contributor & without authentication)', function () {
|
||||
return dbAPI.deleteAllContent(testUtils.context.editor).then(function () {
|
||||
throw new Error('Delete all content is not denied for editor.');
|
||||
}, function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
return dbAPI.deleteAllContent(testUtils.context.author);
|
||||
}).then(function () {
|
||||
throw new Error('Delete all content is not denied for author.');
|
||||
}, function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
return dbAPI.deleteAllContent(testUtils.context.contributor);
|
||||
}).then(function () {
|
||||
throw new Error('Delete all content is not denied for contributor.');
|
||||
}, function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
return dbAPI.deleteAllContent();
|
||||
}).then(function () {
|
||||
throw new Error('Delete all content is not denied without authentication.');
|
||||
}).catch(function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
});
|
||||
});
|
||||
|
||||
it('export content is denied (editor, author, contributor & without authentication)', function () {
|
||||
return dbAPI.exportContent(testUtils.context.editor).then(function () {
|
||||
throw new Error('Export content is not denied for editor.');
|
||||
}, function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
return dbAPI.exportContent(testUtils.context.author);
|
||||
}).then(function () {
|
||||
throw new Error('Export content is not denied for author.');
|
||||
}, function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
return dbAPI.exportContent(testUtils.context.contributor);
|
||||
}).then(function () {
|
||||
throw new Error('Export content is not denied for contributor.');
|
||||
}, function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
return dbAPI.exportContent();
|
||||
}).then(function () {
|
||||
throw new Error('Export content is not denied without authentication.');
|
||||
}).catch(function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
});
|
||||
});
|
||||
|
||||
it('import content is denied (editor, author, contributor & without authentication)', function () {
|
||||
var file = {
|
||||
originalname: 'myFile.json',
|
||||
path: '/my/path/myFile.json',
|
||||
mimetype: 'application/json'
|
||||
};
|
||||
|
||||
return dbAPI.importContent(_.extend(testUtils.context.editor, file)).then(function () {
|
||||
throw new Error('Import content is not denied for editor.');
|
||||
}, function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
return dbAPI.importContent(_.extend(testUtils.context.author, file));
|
||||
}).then(function () {
|
||||
throw new Error('Import content is not denied for author.');
|
||||
}, function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
return dbAPI.importContent(_.extend(testUtils.context.contributor, file));
|
||||
}).then(function () {
|
||||
throw new Error('Import content is not denied for contributor.');
|
||||
}, function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
return dbAPI.importContent(file);
|
||||
}).then(function () {
|
||||
throw new Error('Import content is not denied without authentication.');
|
||||
}).catch(function (error) {
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
});
|
||||
});
|
||||
});
|
@ -1,444 +0,0 @@
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
testUtils = require('../../utils'),
|
||||
_ = require('lodash'),
|
||||
ObjectId = require('bson-objectid'),
|
||||
Promise = require('bluebird'),
|
||||
InvitesAPI = require('../../../server/api/v0.1/invites'),
|
||||
mail = require('../../../server/api/v0.1/mail'),
|
||||
common = require('../../../server/lib/common'),
|
||||
context = testUtils.context,
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Invites API', function () {
|
||||
before(testUtils.teardown);
|
||||
before(testUtils.setup('invites', 'settings', 'users:roles', 'perms:invite', 'perms:init'));
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox.stub(mail, 'send').callsFake(function () {
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
after(testUtils.teardown);
|
||||
|
||||
describe('CRUD', function () {
|
||||
describe('Browse', function () {
|
||||
it('browse invites', function (done) {
|
||||
InvitesAPI.browse(testUtils.context.owner)
|
||||
.then(function (response) {
|
||||
response.invites.length.should.eql(2);
|
||||
|
||||
response.invites[0].status.should.eql('sent');
|
||||
response.invites[0].email.should.eql('test1@ghost.org');
|
||||
response.invites[0].role_id.should.eql(testUtils.roles.ids.admin);
|
||||
|
||||
response.invites[1].status.should.eql('sent');
|
||||
response.invites[1].email.should.eql('test2@ghost.org');
|
||||
response.invites[1].role_id.should.eql(testUtils.roles.ids.author);
|
||||
|
||||
should.not.exist(response.invites[0].token);
|
||||
should.exist(response.invites[0].expires);
|
||||
|
||||
should.not.exist(response.invites[1].token);
|
||||
should.exist(response.invites[1].expires);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Add', function () {
|
||||
it('add invite 1', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [{email: 'test@example.com', role_id: testUtils.roles.ids.editor}]
|
||||
}, testUtils.context.owner)
|
||||
.then(function (response) {
|
||||
response.invites.length.should.eql(1);
|
||||
response.invites[0].role_id.should.eql(testUtils.roles.ids.editor);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('add invite 2', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [{email: 'test2@example.com', role_id: testUtils.roles.ids.author}]
|
||||
}, testUtils.context.owner)
|
||||
.then(function (response) {
|
||||
response.invites.length.should.eql(1);
|
||||
response.invites[0].role_id.should.eql(testUtils.roles.ids.author);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('add invite 3', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [{email: 'test3@example.com', role_id: testUtils.roles.ids.contributor}]
|
||||
}, testUtils.context.owner)
|
||||
.then(function (response) {
|
||||
response.invites.length.should.eql(1);
|
||||
response.invites[0].role_id.should.eql(testUtils.roles.ids.contributor);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('add invite: empty invites object', function (done) {
|
||||
InvitesAPI.add({invites: []}, testUtils.context.owner)
|
||||
.then(function () {
|
||||
throw new Error('expected validation error');
|
||||
})
|
||||
.catch(function (err) {
|
||||
should.exist(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('add invite: no email provided', function (done) {
|
||||
InvitesAPI.add({invites: [{status: 'sent'}]}, testUtils.context.owner)
|
||||
.then(function () {
|
||||
throw new Error('expected validation error');
|
||||
})
|
||||
.catch(function (err) {
|
||||
(err instanceof common.errors.ValidationError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('add invite: invite existing user', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [{
|
||||
email: testUtils.DataGenerator.Content.users[0].email,
|
||||
role_id: testUtils.roles.ids.author
|
||||
}]
|
||||
}, testUtils.context.owner)
|
||||
.then(function () {
|
||||
throw new Error('expected validation error');
|
||||
})
|
||||
.catch(function (err) {
|
||||
(err instanceof common.errors.ValidationError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Read', function () {
|
||||
it('read invites: not found', function (done) {
|
||||
InvitesAPI.read(_.merge({}, testUtils.context.owner, {
|
||||
email: 'not-existend@hey.org'
|
||||
})).then(function () {
|
||||
throw new Error('expected not found error for invite');
|
||||
}).catch(function (err) {
|
||||
(err instanceof common.errors.NotFoundError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('read invite', function (done) {
|
||||
InvitesAPI.read(_.merge({}, {email: 'test1@ghost.org'}, testUtils.context.owner))
|
||||
.then(function (response) {
|
||||
response.invites.length.should.eql(1);
|
||||
response.invites[0].role_id.should.eql(testUtils.roles.ids.admin);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('read invite', function (done) {
|
||||
InvitesAPI.read(_.merge({}, testUtils.context.owner, {email: 'test2@ghost.org'}))
|
||||
.then(function (response) {
|
||||
response.invites.length.should.eql(1);
|
||||
response.invites[0].role_id.should.eql(testUtils.roles.ids.author);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Destroy', function () {
|
||||
it('destroy invite', function (done) {
|
||||
InvitesAPI.destroy(_.merge({}, testUtils.context.owner, {id: testUtils.DataGenerator.forKnex.invites[0].id}))
|
||||
.then(function () {
|
||||
return InvitesAPI.read(_.merge({}, testUtils.context.owner, {
|
||||
email: 'test1@ghost.org'
|
||||
})).catch(function (err) {
|
||||
(err instanceof common.errors.NotFoundError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('destroy invite: id does not exist', function (done) {
|
||||
InvitesAPI.destroy(_.merge({id: ObjectId.generate()}, testUtils.context.owner))
|
||||
.then(function () {
|
||||
throw new Error('expect error on destroy invite');
|
||||
})
|
||||
.catch(function (err) {
|
||||
(err instanceof common.errors.NotFoundError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Permissions', function () {
|
||||
function checkForErrorType(type, done) {
|
||||
return function checkForErrorType(error) {
|
||||
if (error.errorType) {
|
||||
error.errorType.should.eql(type);
|
||||
done();
|
||||
} else {
|
||||
done(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function checkAddResponse(response) {
|
||||
should.exist(response);
|
||||
should.exist(response.invites);
|
||||
should.not.exist(response.meta);
|
||||
|
||||
response.invites.should.have.length(1);
|
||||
testUtils.API.checkResponse(response.invites[0], 'invites');
|
||||
response.invites[0].created_at.should.be.an.instanceof(Date);
|
||||
}
|
||||
|
||||
describe('Owner', function () {
|
||||
it('CANNOT invite an Owner', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.owner
|
||||
}
|
||||
]
|
||||
}, context.owner).then(function () {
|
||||
done(new Error('Owner should not be able to add an owner'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('Can invite an Admin', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.admin
|
||||
}
|
||||
]
|
||||
}, testUtils.context.owner).then(function (response) {
|
||||
checkAddResponse(response);
|
||||
response.invites[0].role_id.should.equal(testUtils.roles.ids.admin);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can invite an Editor', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.editor
|
||||
}
|
||||
]
|
||||
}, testUtils.context.owner).then(function (response) {
|
||||
checkAddResponse(response);
|
||||
response.invites[0].role_id.should.equal(testUtils.roles.ids.editor);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can invite an Author', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.author
|
||||
}
|
||||
]
|
||||
}, testUtils.context.owner).then(function (response) {
|
||||
checkAddResponse(response);
|
||||
response.invites[0].role_id.should.equal(testUtils.roles.ids.author);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can invite a Contributor', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.contributor
|
||||
}
|
||||
]
|
||||
}, testUtils.context.owner).then(function (response) {
|
||||
checkAddResponse(response);
|
||||
response.invites[0].role_id.should.equal(testUtils.roles.ids.contributor);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can invite with role set as string', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.author.toString()
|
||||
}
|
||||
]
|
||||
}, testUtils.context.owner).then(function (response) {
|
||||
checkAddResponse(response);
|
||||
response.invites[0].role_id.should.equal(testUtils.roles.ids.author);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Admin', function () {
|
||||
it('CANNOT invite an Owner', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.owner
|
||||
}
|
||||
]
|
||||
}, testUtils.context.admin).then(function () {
|
||||
done(new Error('Admin should not be able to add an owner'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('Can invite an Admin', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.admin
|
||||
}
|
||||
]
|
||||
}, _.merge({}, {include: 'roles'}, testUtils.context.admin)).then(function (response) {
|
||||
checkAddResponse(response);
|
||||
response.invites[0].role_id.should.equal(testUtils.roles.ids.admin);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can invite an Editor', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.editor
|
||||
}
|
||||
]
|
||||
}, testUtils.context.admin).then(function (response) {
|
||||
checkAddResponse(response);
|
||||
response.invites[0].role_id.should.equal(testUtils.roles.ids.editor);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can invite an Author', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.author
|
||||
}
|
||||
]
|
||||
}, testUtils.context.admin).then(function (response) {
|
||||
checkAddResponse(response);
|
||||
response.invites[0].role_id.should.equal(testUtils.roles.ids.author);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can invite a Contributor', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.contributor
|
||||
}
|
||||
]
|
||||
}, testUtils.context.admin).then(function (response) {
|
||||
checkAddResponse(response);
|
||||
response.invites[0].role_id.should.equal(testUtils.roles.ids.contributor);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Editor', function () {
|
||||
it('CANNOT invite an Owner', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.owner
|
||||
}
|
||||
]
|
||||
}, context.editor).then(function () {
|
||||
done(new Error('Editor should not be able to invite an owner'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('CANNOT invite an Adminstrator', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.admin
|
||||
}
|
||||
]
|
||||
}, context.editor).then(function () {
|
||||
done(new Error('Editor should not be able to invite an administrator'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('CANNOT invite an Editor', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.editor
|
||||
}
|
||||
]
|
||||
}, context.editor).then(function () {
|
||||
done(new Error('Editor should not be able to invite an editor'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('Can invite an Author', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.author
|
||||
}
|
||||
]
|
||||
}, context.editor).then(function (response) {
|
||||
checkAddResponse(response);
|
||||
response.invites[0].role_id.should.equal(testUtils.roles.ids.author);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can invite a Contributor', function (done) {
|
||||
InvitesAPI.add({
|
||||
invites: [
|
||||
{
|
||||
email: 'test@example.com',
|
||||
role_id: testUtils.roles.ids.contributor
|
||||
}
|
||||
]
|
||||
}, context.editor).then(function (response) {
|
||||
checkAddResponse(response);
|
||||
response.invites[0].role_id.should.equal(testUtils.roles.ids.contributor);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,66 +0,0 @@
|
||||
var should = require('should'),
|
||||
testUtils = require('../../utils'),
|
||||
_ = require('lodash'),
|
||||
configUtils = require('../../utils/configUtils'),
|
||||
common = require('../../../server/lib/common'),
|
||||
mailData = {
|
||||
mail: [{
|
||||
message: {
|
||||
to: 'joe@example.com',
|
||||
subject: 'testemail',
|
||||
html: '<p>This</p>'
|
||||
},
|
||||
options: {}
|
||||
}]
|
||||
};
|
||||
|
||||
common.i18n.init();
|
||||
|
||||
describe('Mail API', function () {
|
||||
before(testUtils.teardown);
|
||||
afterEach(testUtils.teardown);
|
||||
beforeEach(testUtils.setup('perms:mail', 'perms:init'));
|
||||
|
||||
beforeEach(function () {
|
||||
_.each(require.cache, function (value, key) {
|
||||
if (key.match(/server\/api\/v0.1\/mail/)) {
|
||||
delete require.cache[key];
|
||||
}
|
||||
});
|
||||
|
||||
require('../../../server/api/v0.1/mail');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
configUtils.restore();
|
||||
});
|
||||
|
||||
it('returns a success', function (done) {
|
||||
configUtils.set({mail: {transport: 'stub'}});
|
||||
|
||||
var MailAPI = require('../../../server/api/v0.1/mail');
|
||||
|
||||
MailAPI.send(mailData, testUtils.context.internal).then(function (response) {
|
||||
should.exist(response.mail);
|
||||
should.exist(response.mail[0].message);
|
||||
should.exist(response.mail[0].status);
|
||||
|
||||
response.mail[0].message.subject.should.eql('testemail');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns a boo boo', function (done) {
|
||||
configUtils.set({mail: {transport: 'stub', options: {error: 'Stub made a boo boo :('}}});
|
||||
|
||||
var MailAPI = require('../../../server/api/v0.1/mail');
|
||||
|
||||
MailAPI.send(mailData, testUtils.context.internal).then(function () {
|
||||
done(new Error('Stub did not error'));
|
||||
}).catch(function (error) {
|
||||
error.stack.should.match(/Error: Stub made a boo boo/);
|
||||
error.errorType.should.eql('EmailError');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
@ -1,297 +0,0 @@
|
||||
var should = require('should'),
|
||||
_ = require('lodash'),
|
||||
uuid = require('uuid'),
|
||||
ObjectId = require('bson-objectid'),
|
||||
testUtils = require('../../utils'),
|
||||
models = require('../../../server/models'),
|
||||
NotificationsAPI = require('../../../server/api/v0.1/notifications');
|
||||
|
||||
describe('Notifications API', function () {
|
||||
// Keep the DB clean
|
||||
before(testUtils.teardown);
|
||||
after(testUtils.teardown);
|
||||
before(testUtils.setup('settings', 'users:roles', 'perms:setting', 'perms:notification', 'perms:init'));
|
||||
|
||||
beforeEach(function () {
|
||||
return models.Settings.edit({key: 'notifications', value: '[]'}, testUtils.context.internal);
|
||||
});
|
||||
|
||||
should.exist(NotificationsAPI);
|
||||
|
||||
it('can add, adds defaults (internal)', function (done) {
|
||||
var msg = {
|
||||
type: 'info',
|
||||
message: 'Hello, this is dog'
|
||||
};
|
||||
|
||||
NotificationsAPI.add({notifications: [msg]}, testUtils.context.internal).then(function (result) {
|
||||
var notification;
|
||||
|
||||
should.exist(result);
|
||||
should.exist(result.notifications);
|
||||
|
||||
notification = result.notifications[0];
|
||||
notification.dismissible.should.be.true();
|
||||
should.exist(notification.location);
|
||||
notification.location.should.equal('bottom');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can add, adds defaults (owner)', function (done) {
|
||||
var msg = {
|
||||
type: 'info',
|
||||
message: 'Hello, this is another dog'
|
||||
};
|
||||
|
||||
NotificationsAPI.add({notifications: [msg]}, testUtils.context.owner).then(function (result) {
|
||||
var notification;
|
||||
|
||||
should.exist(result);
|
||||
should.exist(result.notifications);
|
||||
|
||||
notification = result.notifications[0];
|
||||
notification.dismissible.should.be.true();
|
||||
should.exist(notification.location);
|
||||
notification.location.should.equal('bottom');
|
||||
notification.id.should.be.a.String();
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can add, adds id and status (internal)', function (done) {
|
||||
var msg = {
|
||||
type: 'info',
|
||||
message: 'Hello, this is dog number 3',
|
||||
// id can't be passed from outside
|
||||
id: ObjectId.generate()
|
||||
};
|
||||
|
||||
NotificationsAPI.add({notifications: [msg]}, testUtils.context.internal).then(function (result) {
|
||||
var notification;
|
||||
|
||||
should.exist(result);
|
||||
should.exist(result.notifications);
|
||||
|
||||
notification = result.notifications[0];
|
||||
notification.id.should.be.a.String();
|
||||
should.exist(notification.status);
|
||||
notification.status.should.equal('alert');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('duplicates', function (done) {
|
||||
var customNotification1 = {
|
||||
status: 'alert',
|
||||
type: 'info',
|
||||
location: 'test.to-be-deleted1',
|
||||
custom: true,
|
||||
id: uuid.v1(),
|
||||
dismissible: true,
|
||||
message: 'Hello, this is dog number 1'
|
||||
};
|
||||
|
||||
NotificationsAPI
|
||||
.add({notifications: [customNotification1]}, testUtils.context.internal)
|
||||
.then(function () {
|
||||
return NotificationsAPI.add({notifications: [customNotification1]}, testUtils.context.internal);
|
||||
})
|
||||
.then(function () {
|
||||
return NotificationsAPI.browse(testUtils.context.internal);
|
||||
})
|
||||
.then(function (response) {
|
||||
response.notifications.length.should.eql(1);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('can browse (internal)', function (done) {
|
||||
var msg = {
|
||||
type: 'error', // this can be 'error', 'success', 'warn' and 'info'
|
||||
message: 'This is an error', // A string. Should fit in one line.
|
||||
custom: true
|
||||
};
|
||||
NotificationsAPI.add({notifications: [msg]}, testUtils.context.internal).then(function () {
|
||||
NotificationsAPI.browse(testUtils.context.internal).then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.notifications);
|
||||
results.notifications.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(results.notifications[0], 'notification');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('can browse (owner)', function (done) {
|
||||
var msg = {
|
||||
type: 'error', // this can be 'error', 'success', 'warn' and 'info'
|
||||
message: 'This is an error', // A string. Should fit in one line.
|
||||
custom: true
|
||||
};
|
||||
NotificationsAPI.add({notifications: [msg]}, testUtils.context.owner).then(function () {
|
||||
NotificationsAPI.browse(testUtils.context.owner).then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.notifications);
|
||||
results.notifications.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(results.notifications[0], 'notification');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('receive correct order', function (done) {
|
||||
var customNotification1 = {
|
||||
status: 'alert',
|
||||
type: 'info',
|
||||
custom: true,
|
||||
id: uuid.v1(),
|
||||
dismissible: true,
|
||||
message: '1'
|
||||
}, customNotification2 = {
|
||||
status: 'alert',
|
||||
type: 'info',
|
||||
custom: true,
|
||||
id: uuid.v1(),
|
||||
dismissible: true,
|
||||
message: '2'
|
||||
};
|
||||
|
||||
NotificationsAPI
|
||||
.add({notifications: [customNotification1]}, testUtils.context.internal)
|
||||
.then(function () {
|
||||
return NotificationsAPI.add({notifications: [customNotification2]}, testUtils.context.internal);
|
||||
})
|
||||
.then(function () {
|
||||
return NotificationsAPI.browse(testUtils.context.internal);
|
||||
})
|
||||
.then(function (response) {
|
||||
response.notifications.length.should.eql(2);
|
||||
response.notifications[0].message.should.eql('2');
|
||||
response.notifications[1].message.should.eql('1');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('can destroy (internal)', function (done) {
|
||||
var msg = {
|
||||
type: 'error',
|
||||
message: 'Goodbye, cruel world!'
|
||||
};
|
||||
|
||||
NotificationsAPI.add({notifications: [msg]}, testUtils.context.internal).then(function (result) {
|
||||
var notification = result.notifications[0];
|
||||
|
||||
NotificationsAPI
|
||||
.destroy(_.extend({}, testUtils.context.internal, {id: notification.id}))
|
||||
.then(function (result) {
|
||||
should.not.exist(result);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('can destroy (owner)', function (done) {
|
||||
var msg = {
|
||||
type: 'error',
|
||||
message: 'Goodbye, cruel world!'
|
||||
};
|
||||
|
||||
NotificationsAPI.add({notifications: [msg]}, testUtils.context.internal).then(function (result) {
|
||||
var notification = result.notifications[0];
|
||||
|
||||
NotificationsAPI
|
||||
.destroy(_.extend({}, testUtils.context.owner, {id: notification.id}))
|
||||
.then(function (result) {
|
||||
should.not.exist(result);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('ensure notification get\'s removed', function (done) {
|
||||
var customNotification = {
|
||||
status: 'alert',
|
||||
type: 'info',
|
||||
location: 'test.to-be-deleted',
|
||||
custom: true,
|
||||
id: uuid.v1(),
|
||||
dismissible: true,
|
||||
message: 'Hello, this is dog number 4'
|
||||
};
|
||||
|
||||
NotificationsAPI.add({notifications: [customNotification]}, testUtils.context.internal).then(function (result) {
|
||||
var notification = result.notifications[0];
|
||||
|
||||
return NotificationsAPI.browse(testUtils.context.internal)
|
||||
.then(function (response) {
|
||||
response.notifications.length.should.eql(1);
|
||||
return NotificationsAPI.destroy(_.extend({}, testUtils.context.internal, {id: notification.id}));
|
||||
})
|
||||
.then(function () {
|
||||
return NotificationsAPI.browse(testUtils.context.internal);
|
||||
})
|
||||
.then(function (response) {
|
||||
response.notifications.length.should.eql(0);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('destroy unknown id', function (done) {
|
||||
NotificationsAPI
|
||||
.destroy(_.extend({}, testUtils.context.internal, {id: 1}))
|
||||
.then(function () {
|
||||
done(new Error('Expected notification error.'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
err.statusCode.should.eql(404);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('destroy all', function (done) {
|
||||
var customNotification1 = {
|
||||
status: 'alert',
|
||||
type: 'info',
|
||||
location: 'test.to-be-deleted1',
|
||||
custom: true,
|
||||
id: uuid.v1(),
|
||||
dismissible: true,
|
||||
message: 'Hello, this is dog number 1'
|
||||
}, customNotification2 = {
|
||||
status: 'alert',
|
||||
type: 'info',
|
||||
location: 'test.to-be-deleted2',
|
||||
custom: true,
|
||||
id: uuid.v1(),
|
||||
dismissible: true,
|
||||
message: 'Hello, this is dog number 2'
|
||||
};
|
||||
|
||||
NotificationsAPI
|
||||
.add({notifications: [customNotification1]}, testUtils.context.internal)
|
||||
.then(function () {
|
||||
return NotificationsAPI.add({notifications: [customNotification2]}, testUtils.context.internal);
|
||||
})
|
||||
.then(function () {
|
||||
return NotificationsAPI.destroyAll(testUtils.context.internal);
|
||||
})
|
||||
.then(function () {
|
||||
return NotificationsAPI.browse(testUtils.context.internal);
|
||||
})
|
||||
.then(function (response) {
|
||||
response.notifications.length.should.eql(0);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
@ -1,713 +0,0 @@
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
testUtils = require('../../utils'),
|
||||
_ = require('lodash'),
|
||||
moment = require('moment'),
|
||||
ObjectId = require('bson-objectid'),
|
||||
configUtils = require('../../utils/configUtils'),
|
||||
common = require('../../../server/lib/common'),
|
||||
PostAPI = require('../../../server/api/v0.1/posts'),
|
||||
urlService = require('../../../server/services/url'),
|
||||
settingsCache = require('../../../server/services/settings/cache'),
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Post API', function () {
|
||||
var localSettingsCache = {};
|
||||
|
||||
before(testUtils.teardown);
|
||||
after(testUtils.teardown);
|
||||
|
||||
before(testUtils.setup('users:roles', 'perms:post', 'perms:init', 'posts'));
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox.stub(settingsCache, 'get').callsFake(function (key) {
|
||||
return localSettingsCache[key];
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
localSettingsCache = {};
|
||||
});
|
||||
|
||||
should.exist(PostAPI);
|
||||
|
||||
describe('Browse', function () {
|
||||
beforeEach(function () {
|
||||
localSettingsCache.permalinks = '/:slug/';
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
configUtils.restore();
|
||||
});
|
||||
|
||||
it('can fetch all posts with internal context in correct order', function () {
|
||||
return PostAPI.browse({context: {internal: true}}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(8);
|
||||
|
||||
results.posts[0].status.should.eql('scheduled');
|
||||
|
||||
results.posts[1].status.should.eql('draft');
|
||||
results.posts[2].status.should.eql('draft');
|
||||
|
||||
results.posts[3].status.should.eql('published');
|
||||
results.posts[4].status.should.eql('published');
|
||||
results.posts[5].status.should.eql('published');
|
||||
results.posts[6].status.should.eql('published');
|
||||
results.posts[7].status.should.eql('published');
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch featured posts for user 1', function () {
|
||||
return PostAPI.browse(_.merge({filter: 'featured:true'}, testUtils.context.owner)).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(2);
|
||||
results.posts[0].featured.should.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch featured posts for user 2', function () {
|
||||
return PostAPI.browse(_.merge({filter: 'featured:true'}, testUtils.context.admin)).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(2);
|
||||
results.posts[0].featured.should.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('can exclude featured posts for user 1', function () {
|
||||
return PostAPI.browse(_.merge({
|
||||
status: 'all',
|
||||
filter: 'featured:false'
|
||||
}, testUtils.context.owner)).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(4);
|
||||
results.posts[0].featured.should.eql(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('can limit the number of posts', function () {
|
||||
return PostAPI.browse({context: {user: 1}, status: 'all', limit: 3}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(3);
|
||||
results.meta.pagination.limit.should.eql(3);
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch only static posts', function () {
|
||||
return PostAPI.browse({context: {user: 1}, staticPages: true}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(1);
|
||||
results.posts[0].page.should.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch only static posts with string \'true\'', function () {
|
||||
return PostAPI.browse({context: {user: 1}, staticPages: 'true'}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(1);
|
||||
results.posts[0].page.should.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch only static posts with string \'1\'', function () {
|
||||
return PostAPI.browse({context: {user: 1}, staticPages: '1'}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(1);
|
||||
results.posts[0].page.should.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('can exclude static posts', function () {
|
||||
return PostAPI.browse({context: {user: 1}, staticPages: false}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(4);
|
||||
results.posts[0].page.should.eql(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch static and normal posts', function () {
|
||||
return PostAPI.browse({context: {user: 1}, staticPages: 'all'}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(5);
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch static and normal posts (filter version)', function () {
|
||||
return PostAPI.browse({context: {user: 1}, filter: 'page:[false,true]'}).then(function (results) {
|
||||
// should be the same as the current staticPages: 'all'
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(5);
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch page 1', function () {
|
||||
return PostAPI.browse({context: {user: 1}, page: 1, limit: 2, status: 'all'}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(2);
|
||||
results.posts[0].slug.should.eql('scheduled-post');
|
||||
results.posts[1].slug.should.eql('unfinished');
|
||||
results.meta.pagination.page.should.eql(1);
|
||||
results.meta.pagination.next.should.eql(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch page 2', function () {
|
||||
return PostAPI.browse({context: {user: 1}, page: 2, limit: 2, status: 'all'}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(2);
|
||||
results.posts[0].slug.should.eql('not-so-short-bit-complex');
|
||||
results.posts[1].slug.should.eql('short-and-sweet');
|
||||
results.meta.pagination.page.should.eql(2);
|
||||
results.meta.pagination.next.should.eql(3);
|
||||
results.meta.pagination.prev.should.eql(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('without context.user cannot fetch all posts', function () {
|
||||
return PostAPI.browse({status: 'all'}).then(function (results) {
|
||||
should.not.exist(results);
|
||||
|
||||
throw new Error('should not provide results if invalid status provided');
|
||||
}).catch(function (err) {
|
||||
err.errorType.should.eql('NoPermissionError');
|
||||
});
|
||||
});
|
||||
|
||||
it('without context.user cannot fetch draft posts', function () {
|
||||
return PostAPI.browse({status: 'draft'}).then(function (results) {
|
||||
should.not.exist(results);
|
||||
|
||||
throw new Error('should not provide results if invalid status provided');
|
||||
}).catch(function (err) {
|
||||
err.errorType.should.eql('NoPermissionError');
|
||||
});
|
||||
});
|
||||
|
||||
it('without context.user cannot use uuid to fetch draft posts in browse', function () {
|
||||
return PostAPI.browse({status: 'draft', uuid: 'imastring'}).then(function (results) {
|
||||
should.not.exist(results);
|
||||
|
||||
throw new Error('should not provide results if invalid status provided');
|
||||
}).catch(function (err) {
|
||||
err.errorType.should.eql('NoPermissionError');
|
||||
});
|
||||
});
|
||||
|
||||
it('with context.user can fetch drafts', function () {
|
||||
return PostAPI.browse({context: {user: 1}, status: 'draft'}).then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'posts');
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(1);
|
||||
results.posts[0].status.should.eql('draft');
|
||||
testUtils.API.checkResponse(results.posts[0], 'post');
|
||||
});
|
||||
});
|
||||
|
||||
it('with context.user can fetch all posts', function () {
|
||||
return PostAPI.browse({context: {user: 1}, status: 'all'}).then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'posts');
|
||||
should.exist(results.posts);
|
||||
|
||||
// DataGenerator creates 6 posts by default + 2 static pages
|
||||
results.posts.length.should.eql(6);
|
||||
testUtils.API.checkResponse(results.posts[0], 'post');
|
||||
});
|
||||
});
|
||||
|
||||
it('can include tags', function () {
|
||||
return PostAPI.browse({context: {user: 1}, status: 'all', include: 'tags'}).then(function (results) {
|
||||
results.posts[0].tags.length.should.eql(0);
|
||||
results.posts[1].tags.length.should.eql(1);
|
||||
|
||||
results.posts[1].tags[0].name.should.eql('pollo');
|
||||
});
|
||||
});
|
||||
|
||||
it('[DEPRECATED] can include author (using status:all)', function () {
|
||||
return PostAPI.browse({context: {user: 1}, status: 'all', include: 'author'}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
should.exist(results.posts[0].author.name);
|
||||
results.posts[0].author.name.should.eql('Joe Bloggs');
|
||||
});
|
||||
});
|
||||
|
||||
it('[DEPRECATED] can include author', function () {
|
||||
return PostAPI.read({
|
||||
context: {user: testUtils.DataGenerator.Content.users[1].id},
|
||||
id: testUtils.DataGenerator.Content.posts[1].id,
|
||||
include: 'author'
|
||||
}).then(function (results) {
|
||||
should.exist(results.posts[0].author.name);
|
||||
results.posts[0].author.name.should.eql('Joe Bloggs');
|
||||
});
|
||||
});
|
||||
|
||||
it('can include authors', function () {
|
||||
return PostAPI.browse({context: {user: 1}, status: 'all', include: 'authors'}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
should.exist(results.posts[0].authors);
|
||||
should.exist(results.posts[0].authors[0]);
|
||||
results.posts[0].authors[0].name.should.eql('Joe Bloggs');
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch all posts for a tag', function () {
|
||||
return PostAPI.browse({
|
||||
context: {user: 1},
|
||||
status: 'all',
|
||||
filter: 'tags:kitchen-sink',
|
||||
include: 'tags'
|
||||
}).then(function (results) {
|
||||
results.posts.length.should.be.eql(2);
|
||||
|
||||
_.each(results.posts, function (post) {
|
||||
var slugs = _.map(post.tags, 'slug');
|
||||
slugs.should.containEql('kitchen-sink');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can include authors and be case insensitive', function () {
|
||||
return PostAPI.browse({context: {user: 1}, status: 'all', include: 'Authors'}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
should.exist(results.posts[0].authors);
|
||||
should.exist(results.posts[0].authors[0]);
|
||||
results.posts[0].authors[0].name.should.eql('Joe Bloggs');
|
||||
});
|
||||
});
|
||||
|
||||
it('can include authors and ignore space in include', function () {
|
||||
return PostAPI.browse({context: {user: 1}, status: 'all', include: ' authors'}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
should.exist(results.posts[0].authors);
|
||||
should.exist(results.posts[0].authors[0]);
|
||||
results.posts[0].authors[0].name.should.eql('Joe Bloggs');
|
||||
});
|
||||
});
|
||||
|
||||
it('[DEPRECATED] can fetch all posts for an author', function () {
|
||||
return PostAPI.browse({
|
||||
context: {user: 1},
|
||||
status: 'all',
|
||||
filter: 'author:joe-bloggs',
|
||||
include: 'author'
|
||||
}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(6);
|
||||
|
||||
_.each(results.posts, function (post) {
|
||||
post.author.slug.should.eql('joe-bloggs');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch all posts for an author', function () {
|
||||
return PostAPI.browse({
|
||||
context: {user: 1},
|
||||
status: 'all',
|
||||
filter: 'authors:joe-bloggs',
|
||||
include: 'authors'
|
||||
}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(6);
|
||||
|
||||
_.each(results.posts, function (post) {
|
||||
post.primary_author.slug.should.eql('joe-bloggs');
|
||||
});
|
||||
|
||||
_.find(results.posts, {id: testUtils.DataGenerator.forKnex.posts[0].id}).authors.length.should.eql(1);
|
||||
_.find(results.posts, {id: testUtils.DataGenerator.forKnex.posts[3].id}).authors.length.should.eql(2);
|
||||
});
|
||||
});
|
||||
|
||||
// @TODO: ensure filters are fully validated
|
||||
it.skip('cannot fetch all posts for a tag with invalid slug', function () {
|
||||
return PostAPI.browse({filter: 'tags:invalid!'}).then(function () {
|
||||
throw new Error('Should not return a result with invalid tag');
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
err.message.should.eql('Validation (isSlug) failed for tag');
|
||||
err.statusCode.should.eql(422);
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('cannot fetch all posts for an author with invalid slug', function () {
|
||||
return PostAPI.browse({filter: 'authors:invalid!'}).then(function () {
|
||||
throw new Error('Should not return a result with invalid author');
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
err.message.should.eql('Validation (isSlug) failed for author');
|
||||
err.statusCode.should.eql(422);
|
||||
});
|
||||
});
|
||||
|
||||
it('with context.user can fetch a single field', function () {
|
||||
return PostAPI.browse({context: {user: 1}, status: 'all', limit: 5, fields: 'title'}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
|
||||
should.exist(results.posts[0].title);
|
||||
should.not.exist(results.posts[0].slug);
|
||||
});
|
||||
});
|
||||
|
||||
it('with context.user can fetch multiple fields', function () {
|
||||
return PostAPI.browse({
|
||||
context: {user: 1},
|
||||
status: 'all',
|
||||
limit: 5,
|
||||
fields: 'slug,published_at'
|
||||
}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
|
||||
should.exist(results.posts[0].published_at);
|
||||
should.exist(results.posts[0].slug);
|
||||
should.not.exist(results.posts[0].title);
|
||||
});
|
||||
});
|
||||
|
||||
it('with context.user can fetch url and author fields', function () {
|
||||
sandbox.stub(urlService, 'getUrlByResourceId').withArgs(testUtils.DataGenerator.Content.posts[7].id).returns('/html-ipsum/');
|
||||
|
||||
return PostAPI.browse({context: {user: 1}, status: 'all', limit: 5}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
|
||||
should.exist(results.posts[0].url);
|
||||
should.notEqual(results.posts[0].url, 'undefined');
|
||||
should.exist(results.posts[0].author);
|
||||
});
|
||||
});
|
||||
|
||||
it('with context.user can fetch multiple fields and be case insensitive', function () {
|
||||
return PostAPI.browse({
|
||||
context: {user: 1},
|
||||
status: 'all',
|
||||
limit: 5,
|
||||
fields: 'Slug,Published_At'
|
||||
}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
|
||||
should.exist(results.posts[0].published_at);
|
||||
should.exist(results.posts[0].slug);
|
||||
should.not.exist(results.posts[0].title);
|
||||
});
|
||||
});
|
||||
|
||||
it('with context.user can fetch multiple fields ignoring spaces', function () {
|
||||
return PostAPI.browse({
|
||||
context: {user: 1},
|
||||
status: 'all',
|
||||
limit: 5,
|
||||
fields: ' slug , published_at '
|
||||
}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
|
||||
should.exist(results.posts[0].published_at);
|
||||
should.exist(results.posts[0].slug);
|
||||
should.not.exist(results.posts[0].title);
|
||||
});
|
||||
});
|
||||
|
||||
it('with context.user can fetch a field and not return invalid field', function () {
|
||||
return PostAPI.browse({context: {user: 1}, status: 'all', limit: 5, fields: 'foo,title'})
|
||||
.then(function (results) {
|
||||
var objectKeys;
|
||||
should.exist(results.posts);
|
||||
|
||||
should.exist(results.posts[0].title);
|
||||
should.not.exist(results.posts[0].foo);
|
||||
objectKeys = _.keys(results.posts[0]);
|
||||
objectKeys.length.should.eql(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('can order posts using asc', function () {
|
||||
var posts, expectedTitles;
|
||||
|
||||
posts = _(testUtils.DataGenerator.Content.posts).reject('page').value();
|
||||
expectedTitles = _(posts).map('title').sortBy().value();
|
||||
|
||||
return PostAPI.browse({
|
||||
context: {user: 1},
|
||||
status: 'all',
|
||||
order: 'title asc',
|
||||
fields: 'title'
|
||||
}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
|
||||
var titles = _.map(results.posts, 'title');
|
||||
titles.should.eql(expectedTitles);
|
||||
});
|
||||
});
|
||||
|
||||
it('can order posts using desc', function () {
|
||||
var posts, expectedTitles;
|
||||
|
||||
posts = _(testUtils.DataGenerator.Content.posts).reject('page').value();
|
||||
expectedTitles = _(posts).map('title').sortBy().reverse().value();
|
||||
|
||||
return PostAPI.browse({
|
||||
context: {user: 1},
|
||||
status: 'all',
|
||||
order: 'title DESC',
|
||||
fields: 'title'
|
||||
}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
|
||||
var titles = _.map(results.posts, 'title');
|
||||
titles.should.eql(expectedTitles);
|
||||
});
|
||||
});
|
||||
|
||||
it('can order posts and filter disallowed attributes', function () {
|
||||
var posts, expectedTitles;
|
||||
|
||||
posts = _(testUtils.DataGenerator.Content.posts).reject('page').value();
|
||||
expectedTitles = _(posts).map('title').sortBy().value();
|
||||
|
||||
return PostAPI.browse({
|
||||
context: {user: 1},
|
||||
status: 'all',
|
||||
order: 'bunny DESC, title ASC',
|
||||
fields: 'title'
|
||||
}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
|
||||
var titles = _.map(results.posts, 'title');
|
||||
titles.should.eql(expectedTitles);
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch all posts with correct order when unpublished draft is present', function () {
|
||||
return testUtils.fixtures.insertPosts([{
|
||||
id: ObjectId.generate(),
|
||||
title: 'Not published draft post',
|
||||
slug: 'not-published-draft-post',
|
||||
status: 'draft',
|
||||
updated_at: moment().add(3, 'minutes').toDate(),
|
||||
published_at: null
|
||||
},
|
||||
{
|
||||
id: ObjectId.generate(),
|
||||
title: 'Unpublished post',
|
||||
slug: 'unpublished-post',
|
||||
status: 'draft',
|
||||
updated_at: moment().add(2, 'minutes').toDate(),
|
||||
published_at: moment().add(1, 'minutes').toDate()
|
||||
}])
|
||||
.then(function () {
|
||||
return PostAPI.browse({context: {internal: true}});
|
||||
})
|
||||
.then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.eql(10);
|
||||
|
||||
results.posts[1].slug.should.eql('not-published-draft-post');
|
||||
results.posts[2].slug.should.eql('unpublished-post');
|
||||
|
||||
results.posts[0].status.should.eql('scheduled');
|
||||
|
||||
results.posts[1].status.should.eql('draft');
|
||||
results.posts[2].status.should.eql('draft');
|
||||
results.posts[3].status.should.eql('draft');
|
||||
results.posts[4].status.should.eql('draft');
|
||||
|
||||
results.posts[5].status.should.eql('published');
|
||||
results.posts[6].status.should.eql('published');
|
||||
results.posts[7].status.should.eql('published');
|
||||
results.posts[8].status.should.eql('published');
|
||||
results.posts[9].status.should.eql('published');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Read', function () {
|
||||
it('can fetch a post', function () {
|
||||
var firstPost;
|
||||
|
||||
return PostAPI.browse().then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.posts);
|
||||
results.posts.length.should.be.above(0);
|
||||
firstPost = _.find(results.posts, {title: testUtils.DataGenerator.Content.posts[0].title});
|
||||
return PostAPI.read({slug: firstPost.slug, include: 'tags'});
|
||||
}).then(function (found) {
|
||||
var post;
|
||||
|
||||
should.exist(found);
|
||||
testUtils.API.checkResponse(found.posts[0], 'post', 'tags');
|
||||
|
||||
post = found.posts[0];
|
||||
|
||||
post.created_at.should.be.an.instanceof(Date);
|
||||
|
||||
should.exist(post.tags);
|
||||
post.tags.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(post.tags[0], 'tag');
|
||||
});
|
||||
});
|
||||
|
||||
it('without context.user cannot fetch draft', function () {
|
||||
return PostAPI.read({slug: 'unfinished', status: 'draft'}).then(function () {
|
||||
throw new Error('Should not return a result with no permission');
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
err.errorType.should.eql('NoPermissionError');
|
||||
});
|
||||
});
|
||||
|
||||
it('with context.user can fetch a draft', function () {
|
||||
return PostAPI.read({context: {user: 1}, slug: 'unfinished', status: 'draft'}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts[0].status.should.eql('draft');
|
||||
});
|
||||
});
|
||||
|
||||
it('without context.user can fetch a draft if uuid is provided', function () {
|
||||
return PostAPI.read({uuid: 'd52c42ae-2755-455c-80ec-70b2ec55c903', status: 'draft'}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts[0].slug.should.eql('unfinished');
|
||||
});
|
||||
});
|
||||
|
||||
it('cannot fetch post with unknown id', function () {
|
||||
return PostAPI.read({context: {user: 1}, slug: 'not-a-post'}).then(function () {
|
||||
throw new Error('Should not return a result with unknown id');
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
err.message.should.eql('Post not found.');
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch post with by id', function () {
|
||||
return PostAPI.read({
|
||||
context: {user: testUtils.DataGenerator.Content.users[1].id},
|
||||
id: testUtils.DataGenerator.Content.posts[1].id,
|
||||
status: 'all'
|
||||
}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
results.posts[0].id.should.eql(testUtils.DataGenerator.Content.posts[1].id);
|
||||
results.posts[0].slug.should.eql('ghostly-kitchen-sink');
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch post returning a slug only permalink', function () {
|
||||
sandbox.stub(urlService, 'getUrlByResourceId').withArgs(testUtils.DataGenerator.Content.posts[0].id).returns('/html-ipsum/');
|
||||
|
||||
return PostAPI.read({
|
||||
id: testUtils.DataGenerator.Content.posts[0].id
|
||||
})
|
||||
.then(function (result) {
|
||||
should.exist(result);
|
||||
|
||||
result.posts[0].url.should.equal('/html-ipsum/');
|
||||
});
|
||||
});
|
||||
|
||||
it('can fetch post returning a dated permalink', function () {
|
||||
sandbox.stub(urlService, 'getUrlByResourceId').withArgs(testUtils.DataGenerator.Content.posts[0].id).returns('/2015/01/01/html-ipsum/');
|
||||
|
||||
return PostAPI.read({
|
||||
id: testUtils.DataGenerator.Content.posts[0].id
|
||||
})
|
||||
.then(function (result) {
|
||||
should.exist(result);
|
||||
|
||||
// published_at of post 1 is 2015-01-01 00:00:00
|
||||
// default blog TZ is UTC
|
||||
result.posts[0].url.should.equal('/2015/01/01/html-ipsum/');
|
||||
});
|
||||
});
|
||||
|
||||
it('can include tags', function () {
|
||||
return PostAPI.read({
|
||||
context: {user: testUtils.DataGenerator.Content.users[1].id},
|
||||
id: testUtils.DataGenerator.Content.posts[2].id,
|
||||
include: 'tags'
|
||||
}).then(function (results) {
|
||||
should.exist(results.posts[0].tags);
|
||||
results.posts[0].tags[0].slug.should.eql('chorizo');
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: this should be a 422?
|
||||
it('cannot fetch a post with an invalid slug', function () {
|
||||
return PostAPI.read({slug: 'invalid!'}).then(function () {
|
||||
throw new Error('Should not return a result with invalid slug');
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
err.message.should.eql('Post not found.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Destroy', function () {
|
||||
beforeEach(testUtils.teardown);
|
||||
beforeEach(testUtils.setup('users:roles', 'perms:post', 'perms:init', 'posts'));
|
||||
after(testUtils.teardown);
|
||||
|
||||
it('can delete a post', function () {
|
||||
var options = {
|
||||
context: {user: testUtils.DataGenerator.Content.users[1].id},
|
||||
id: testUtils.DataGenerator.Content.posts[0].id
|
||||
};
|
||||
|
||||
PostAPI.read(options).then(function (results) {
|
||||
should.exist(results.posts[0]);
|
||||
|
||||
return PostAPI.destroy(options);
|
||||
}).then(function (results) {
|
||||
should.not.exist(results);
|
||||
|
||||
return PostAPI.read(options);
|
||||
}).then(function () {
|
||||
throw new Error('Post still exists when it should have been deleted');
|
||||
}).catch(function (error) {
|
||||
error.errorType.should.eql('NotFoundError');
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error when attempting to delete a non-existent post', function () {
|
||||
var options = {context: {user: testUtils.DataGenerator.Content.users[1].id}, id: ObjectId.generate()};
|
||||
|
||||
return PostAPI.destroy(options).then(function () {
|
||||
throw new Error('No error was thrown');
|
||||
}).catch(function (error) {
|
||||
error.errorType.should.eql('NotFoundError');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edit', function () {
|
||||
beforeEach(testUtils.teardown);
|
||||
beforeEach(testUtils.setup('users:roles', 'perms:post', 'perms:init', 'posts'));
|
||||
after(testUtils.teardown);
|
||||
|
||||
it('can edit own post', function () {
|
||||
return PostAPI.edit({posts: [{status: 'test'}]}, {
|
||||
context: {user: testUtils.DataGenerator.Content.users[1].id},
|
||||
id: testUtils.DataGenerator.Content.posts[0].id
|
||||
}).then(function (results) {
|
||||
should.exist(results.posts);
|
||||
});
|
||||
});
|
||||
|
||||
it('cannot edit others post', function () {
|
||||
return PostAPI.edit(
|
||||
{posts: [{status: 'test'}]},
|
||||
{
|
||||
context: {user: testUtils.DataGenerator.Content.users[3].id},
|
||||
id: testUtils.DataGenerator.Content.posts[0].id
|
||||
}
|
||||
).then(function () {
|
||||
throw new Error('expected permission error');
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,144 +0,0 @@
|
||||
var should = require('should'),
|
||||
testUtils = require('../../utils'),
|
||||
_ = require('lodash'),
|
||||
RoleAPI = require('../../../server/api/v0.1/roles'),
|
||||
context = testUtils.context;
|
||||
|
||||
describe('Roles API', function () {
|
||||
// Keep the DB clean
|
||||
before(testUtils.teardown);
|
||||
after(testUtils.teardown);
|
||||
|
||||
before(testUtils.setup('users:roles', 'perms:role', 'perms:init'));
|
||||
|
||||
describe('Browse', function () {
|
||||
function checkBrowseResponse(response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'roles');
|
||||
should.exist(response.roles);
|
||||
response.roles.should.have.length(6);
|
||||
testUtils.API.checkResponse(response.roles[0], 'role');
|
||||
testUtils.API.checkResponse(response.roles[1], 'role');
|
||||
testUtils.API.checkResponse(response.roles[2], 'role');
|
||||
testUtils.API.checkResponse(response.roles[3], 'role');
|
||||
testUtils.API.checkResponse(response.roles[4], 'role');
|
||||
testUtils.API.checkResponse(response.roles[5], 'role');
|
||||
}
|
||||
|
||||
it('Owner can browse', function (done) {
|
||||
RoleAPI.browse(context.owner).then(function (response) {
|
||||
checkBrowseResponse(response);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Admin can browse', function (done) {
|
||||
RoleAPI.browse(context.admin).then(function (response) {
|
||||
checkBrowseResponse(response);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Editor can browse', function (done) {
|
||||
RoleAPI.browse(context.editor).then(function (response) {
|
||||
checkBrowseResponse(response);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Author can browse', function (done) {
|
||||
RoleAPI.browse(context.author).then(function (response) {
|
||||
checkBrowseResponse(response);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Contributor can browse', function (done) {
|
||||
RoleAPI.browse(context.contributor).then(function (response) {
|
||||
checkBrowseResponse(response);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('No-auth CANNOT browse', function (done) {
|
||||
RoleAPI.browse().then(function () {
|
||||
done(new Error('Browse roles is not denied without authentication.'));
|
||||
}, function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Browse permissions=assign', function () {
|
||||
function checkBrowseResponse(response) {
|
||||
should.exist(response);
|
||||
should.exist(response.roles);
|
||||
testUtils.API.checkResponse(response, 'roles');
|
||||
response.roles.should.have.length(4);
|
||||
testUtils.API.checkResponse(response.roles[0], 'role');
|
||||
testUtils.API.checkResponse(response.roles[1], 'role');
|
||||
testUtils.API.checkResponse(response.roles[2], 'role');
|
||||
testUtils.API.checkResponse(response.roles[3], 'role');
|
||||
response.roles[0].name.should.equal('Administrator');
|
||||
response.roles[1].name.should.equal('Editor');
|
||||
response.roles[2].name.should.equal('Author');
|
||||
response.roles[3].name.should.equal('Contributor');
|
||||
}
|
||||
|
||||
it('Owner can assign all', function (done) {
|
||||
RoleAPI.browse(_.extend({}, context.owner, {permissions: 'assign'})).then(function (response) {
|
||||
checkBrowseResponse(response);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Admin can assign all', function (done) {
|
||||
RoleAPI.browse(_.extend({}, context.admin, {permissions: 'assign'})).then(function (response) {
|
||||
checkBrowseResponse(response);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Editor can assign Author & Contributor', function (done) {
|
||||
RoleAPI.browse(_.extend({}, context.editor, {permissions: 'assign'})).then(function (response) {
|
||||
should.exist(response);
|
||||
should.exist(response.roles);
|
||||
testUtils.API.checkResponse(response, 'roles');
|
||||
response.roles.should.have.length(2);
|
||||
testUtils.API.checkResponse(response.roles[0], 'role');
|
||||
testUtils.API.checkResponse(response.roles[1], 'role');
|
||||
response.roles[0].name.should.equal('Author');
|
||||
response.roles[1].name.should.equal('Contributor');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Author CANNOT assign any', function (done) {
|
||||
RoleAPI.browse(_.extend({}, context.author, {permissions: 'assign'})).then(function (response) {
|
||||
should.exist(response);
|
||||
should.exist(response.roles);
|
||||
testUtils.API.checkResponse(response, 'roles');
|
||||
response.roles.should.have.length(0);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Contributor CANNOT assign any', function (done) {
|
||||
RoleAPI.browse(_.extend({}, context.contributor, {permissions: 'assign'})).then(function (response) {
|
||||
should.exist(response);
|
||||
should.exist(response.roles);
|
||||
testUtils.API.checkResponse(response, 'roles');
|
||||
response.roles.should.have.length(0);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('No-auth CANNOT browse', function (done) {
|
||||
RoleAPI.browse({permissions: 'assign'}).then(function () {
|
||||
done(new Error('Browse roles is not denied without authentication.'));
|
||||
}, function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,487 +0,0 @@
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
moment = require('moment'),
|
||||
Promise = require('bluebird'),
|
||||
ObjectId = require('bson-objectid'),
|
||||
testUtils = require('../../utils'),
|
||||
config = require('../../../server/config'),
|
||||
sequence = require('../../../server/lib/promise/sequence'),
|
||||
common = require('../../../server/lib/common'),
|
||||
api = require('../../../server/api'),
|
||||
models = require('../../../server/models'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Schedules API', function () {
|
||||
var scope = {posts: []};
|
||||
|
||||
after(testUtils.teardown);
|
||||
|
||||
describe('fn: getScheduledPosts', function () {
|
||||
before(function (done) {
|
||||
sequence([
|
||||
testUtils.teardown,
|
||||
testUtils.setup('clients', 'users:roles', 'perms:post', 'perms:init')
|
||||
]).then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
describe('success', function () {
|
||||
before(function (done) {
|
||||
scope.posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.users.ids.editor,
|
||||
author_id: testUtils.users.ids.editor,
|
||||
published_by: testUtils.users.ids.editor,
|
||||
created_at: moment().add(2, 'days').set('hours', 8).toDate(),
|
||||
published_at: moment().add(5, 'days').toDate(),
|
||||
status: 'scheduled',
|
||||
slug: '2'
|
||||
}));
|
||||
|
||||
scope.posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.users.ids.owner,
|
||||
author_id: testUtils.users.ids.owner,
|
||||
published_by: testUtils.users.ids.owner,
|
||||
created_at: moment().add(2, 'days').set('hours', 12).toDate(),
|
||||
published_at: moment().add(5, 'days').toDate(),
|
||||
status: 'scheduled',
|
||||
page: 1,
|
||||
slug: '5'
|
||||
}));
|
||||
|
||||
scope.posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.users.ids.author,
|
||||
author_id: testUtils.users.ids.author,
|
||||
published_by: testUtils.users.ids.author,
|
||||
created_at: moment().add(5, 'days').set('hours', 6).toDate(),
|
||||
published_at: moment().add(10, 'days').toDate(),
|
||||
status: 'scheduled',
|
||||
slug: '1'
|
||||
}));
|
||||
|
||||
scope.posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.users.ids.owner,
|
||||
author_id: testUtils.users.ids.owner,
|
||||
published_by: testUtils.users.ids.owner,
|
||||
created_at: moment().add(6, 'days').set('hours', 10).set('minutes', 0).toDate(),
|
||||
published_at: moment().add(7, 'days').toDate(),
|
||||
status: 'scheduled',
|
||||
slug: '3'
|
||||
}));
|
||||
|
||||
scope.posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.users.ids.owner,
|
||||
author_id: testUtils.users.ids.owner,
|
||||
published_by: testUtils.users.ids.owner,
|
||||
created_at: moment().add(6, 'days').set('hours', 11).toDate(),
|
||||
published_at: moment().add(8, 'days').toDate(),
|
||||
status: 'scheduled',
|
||||
slug: '4'
|
||||
}));
|
||||
|
||||
scope.posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.users.ids.owner,
|
||||
author_id: testUtils.users.ids.owner,
|
||||
published_by: testUtils.users.ids.owner,
|
||||
status: 'draft',
|
||||
slug: '6'
|
||||
}));
|
||||
|
||||
Promise.all(scope.posts.map(function (post) {
|
||||
return models.Post.add(post, {context: {internal: true}, importing: true});
|
||||
})).then(function () {
|
||||
return done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
after(function () {
|
||||
scope.posts = [];
|
||||
});
|
||||
|
||||
it('all', function (done) {
|
||||
api.schedules.getScheduledPosts()
|
||||
.then(function (result) {
|
||||
result.posts.length.should.eql(5);
|
||||
testUtils.API.checkResponse(result, 'posts', null, ['meta']);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('for specific datetime', function (done) {
|
||||
api.schedules.getScheduledPosts({
|
||||
from: moment().add(2, 'days').startOf('day').toDate(),
|
||||
to: moment().add(2, 'days').endOf('day').toDate()
|
||||
}).then(function (result) {
|
||||
result.posts.length.should.eql(2);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('for specific datetime', function (done) {
|
||||
api.schedules.getScheduledPosts({
|
||||
from: moment().add(2, 'days').startOf('day').toDate(),
|
||||
to: moment().add(2, 'days').set('hours', 8).toDate()
|
||||
}).then(function (result) {
|
||||
result.posts.length.should.eql(1);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('for specific date', function (done) {
|
||||
api.schedules.getScheduledPosts({
|
||||
from: moment().add(5, 'days').startOf('day').toDate(),
|
||||
to: moment().add(6, 'days').endOf('day').toDate()
|
||||
}).then(function (result) {
|
||||
result.posts.length.should.eql(3);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('for specific date', function (done) {
|
||||
api.schedules.getScheduledPosts({
|
||||
from: moment().add(6, 'days').set('hours', 10).set('minutes', 30).toDate(),
|
||||
to: moment().add(6, 'days').endOf('day').toDate()
|
||||
}).then(function (result) {
|
||||
result.posts.length.should.eql(1);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('for specific date', function (done) {
|
||||
api.schedules.getScheduledPosts({
|
||||
from: moment().add(1, 'days').toDate()
|
||||
}).then(function (result) {
|
||||
result.posts.length.should.eql(5);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error', function () {
|
||||
it('from is invalid', function (done) {
|
||||
api.schedules.getScheduledPosts({
|
||||
from: 'bee'
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.ValidationError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fn: publishPost', function () {
|
||||
before(function (done) {
|
||||
sequence([
|
||||
testUtils.teardown,
|
||||
testUtils.setup('clients', 'users:roles', 'perms:post', 'perms:init')
|
||||
]).then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('success', function () {
|
||||
beforeEach(function (done) {
|
||||
scope.posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.users.ids.author,
|
||||
author_id: testUtils.users.ids.author,
|
||||
published_by: testUtils.users.ids.author,
|
||||
published_at: moment().toDate(),
|
||||
status: 'scheduled',
|
||||
title: 'title',
|
||||
slug: 'first'
|
||||
}));
|
||||
|
||||
scope.posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.users.ids.author,
|
||||
author_id: testUtils.users.ids.author,
|
||||
published_by: testUtils.users.ids.author,
|
||||
published_at: moment().add(30, 'seconds').toDate(),
|
||||
status: 'scheduled',
|
||||
slug: 'second'
|
||||
}));
|
||||
|
||||
scope.posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.users.ids.author,
|
||||
author_id: testUtils.users.ids.author,
|
||||
published_by: testUtils.users.ids.author,
|
||||
published_at: moment().subtract(30, 'seconds').toDate(),
|
||||
status: 'scheduled',
|
||||
slug: 'third'
|
||||
}));
|
||||
|
||||
scope.posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.users.ids.author,
|
||||
author_id: testUtils.users.ids.author,
|
||||
published_by: testUtils.users.ids.author,
|
||||
published_at: moment().subtract(10, 'minute').toDate(),
|
||||
status: 'scheduled',
|
||||
slug: 'fourth'
|
||||
}));
|
||||
|
||||
Promise.mapSeries(scope.posts, function (post) {
|
||||
return models.Post.add(post, {context: {internal: true}});
|
||||
}).then(function (result) {
|
||||
result.length.should.eql(4);
|
||||
return done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
scope.posts = [];
|
||||
});
|
||||
|
||||
it('client with specific perms has access to publish post', function (done) {
|
||||
api.schedules.publishPost({id: scope.posts[0].id, context: {client: 'ghost-scheduler'}})
|
||||
.then(function (result) {
|
||||
result.posts[0].id.should.eql(scope.posts[0].id);
|
||||
result.posts[0].status.should.eql('published');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('can publish with tolerance (30 seconds in the future)', function (done) {
|
||||
api.schedules.publishPost({id: scope.posts[1].id, context: {client: 'ghost-scheduler'}})
|
||||
.then(function (result) {
|
||||
result.posts[0].id.should.eql(scope.posts[1].id);
|
||||
result.posts[0].status.should.eql('published');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('can publish with tolerance (30seconds in the past)', function (done) {
|
||||
api.schedules.publishPost({id: scope.posts[2].id, context: {client: 'ghost-scheduler'}})
|
||||
.then(function (result) {
|
||||
result.posts[0].id.should.eql(scope.posts[2].id);
|
||||
result.posts[0].status.should.eql('published');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('can publish a post in the past with force flag', function (done) {
|
||||
api.schedules.publishPost({force: true}, {id: scope.posts[3].id, context: {client: 'ghost-scheduler'}})
|
||||
.then(function (result) {
|
||||
result.posts[0].id.should.eql(scope.posts[3].id);
|
||||
result.posts[0].status.should.eql('published');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('collision protection', function (done) {
|
||||
var originalPostApi = api.posts.edit,
|
||||
postId = scope.posts[0].id, // first post is status=scheduled!
|
||||
requestCanComeIn = false,
|
||||
interval;
|
||||
|
||||
// this request get's blocked
|
||||
interval = setInterval(function () {
|
||||
if (requestCanComeIn) {
|
||||
clearInterval(interval);
|
||||
|
||||
// happens in a transaction, request has to wait until the scheduler api finished
|
||||
return models.Post.edit({title: 'Berlin'}, {id: postId, context: {internal: true}})
|
||||
.then(function (post) {
|
||||
post.id.should.eql(postId);
|
||||
post.get('status').should.eql('published');
|
||||
post.get('title').should.eql('Berlin');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
// target post to publish was read already, simulate a client request
|
||||
sandbox.stub(api.posts, 'edit').callsFake(function () {
|
||||
var self = this,
|
||||
args = arguments;
|
||||
|
||||
requestCanComeIn = true;
|
||||
return Promise.delay(2000)
|
||||
.then(function () {
|
||||
return originalPostApi.apply(self, args);
|
||||
});
|
||||
});
|
||||
|
||||
api.schedules.publishPost({id: postId, context: {client: 'ghost-scheduler'}})
|
||||
.then(function (result) {
|
||||
result.posts[0].id.should.eql(postId);
|
||||
result.posts[0].status.should.eql('published');
|
||||
result.posts[0].title.should.eql('title');
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error', function () {
|
||||
beforeEach(function (done) {
|
||||
scope.posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.users.ids.author,
|
||||
author_id: testUtils.users.ids.author,
|
||||
published_by: testUtils.users.ids.author,
|
||||
published_at: moment().add(2, 'days').toDate(),
|
||||
status: 'scheduled',
|
||||
slug: 'first'
|
||||
}));
|
||||
|
||||
scope.posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.users.ids.author,
|
||||
author_id: testUtils.users.ids.author,
|
||||
published_by: testUtils.users.ids.author,
|
||||
published_at: moment().add(2, 'days').toDate(),
|
||||
status: 'draft',
|
||||
slug: 'second'
|
||||
}));
|
||||
|
||||
scope.posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.users.ids.author,
|
||||
author_id: testUtils.users.ids.author,
|
||||
published_by: testUtils.users.ids.author,
|
||||
published_at: moment().add(4, 'minutes').toDate(),
|
||||
status: 'scheduled',
|
||||
slug: 'third'
|
||||
}));
|
||||
|
||||
scope.posts.push(testUtils.DataGenerator.forKnex.createPost({
|
||||
created_by: testUtils.users.ids.author,
|
||||
author_id: testUtils.users.ids.author,
|
||||
published_by: testUtils.users.ids.author,
|
||||
published_at: moment().subtract(4, 'minutes').toDate(),
|
||||
status: 'scheduled',
|
||||
slug: 'fourth'
|
||||
}));
|
||||
|
||||
Promise.all(scope.posts.map(function (post) {
|
||||
return models.Post.add(post, {context: {internal: true}});
|
||||
})).then(function (result) {
|
||||
result.length.should.eql(4);
|
||||
return done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
scope.posts = [];
|
||||
});
|
||||
|
||||
it('ghost admin has no access', function (done) {
|
||||
api.schedules.publishPost({id: scope.posts[0].id, context: {client: 'ghost-admin'}})
|
||||
.then(function () {
|
||||
done(new Error('expected NoPermissionError'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('owner has no access (this is how it is right now!)', function (done) {
|
||||
api.schedules.publishPost({id: scope.posts[1].id, context: {user: testUtils.users.ids.author}})
|
||||
.then(function () {
|
||||
done(new Error('expected NoPermissionError'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('other user has no access', function (done) {
|
||||
testUtils.fixtures.insertOne('User', 'users', 'createUser', 4)
|
||||
.then(function (result) {
|
||||
api.schedules.publishPost({id: scope.posts[0].id, context: {user: result[0]}})
|
||||
.then(function () {
|
||||
done(new Error('expected NoPermissionError'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('invalid params: id is integer', function (done) {
|
||||
api.schedules.publishPost({id: 100, context: {client: 'ghost-scheduler'}})
|
||||
.then(function () {
|
||||
done(new Error('expected ValidationError'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.ValidationError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('post does not exist', function (done) {
|
||||
api.schedules.publishPost({id: ObjectId.generate(), context: {client: 'ghost-scheduler'}})
|
||||
.then(function () {
|
||||
done(new Error('expected ValidationError'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.NotFoundError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('publish at a wrong time', function (done) {
|
||||
api.schedules.publishPost({id: scope.posts[0].id, context: {client: 'ghost-scheduler'}})
|
||||
.then(function () {
|
||||
done(new Error('expected ValidationError'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.NotFoundError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('publish at a wrong time', function (done) {
|
||||
api.schedules.publishPost({id: scope.posts[2].id, context: {client: 'ghost-scheduler'}})
|
||||
.then(function () {
|
||||
done(new Error('expected ValidationError'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.NotFoundError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('publish at a wrong time', function (done) {
|
||||
api.schedules.publishPost({id: scope.posts[3].id, context: {client: 'ghost-scheduler'}})
|
||||
.then(function () {
|
||||
done(new Error('expected ValidationError'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.NotFoundError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('publish, but status is draft', function (done) {
|
||||
api.schedules.publishPost({id: scope.posts[1].id, context: {client: 'ghost-scheduler'}})
|
||||
.then(function () {
|
||||
done(new Error('expected ValidationError'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.NotFoundError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,197 +0,0 @@
|
||||
var should = require('should'),
|
||||
testUtils = require('../../utils'),
|
||||
_ = require('lodash'),
|
||||
|
||||
// Stuff we are testing
|
||||
SettingsAPI = require('../../../server/api/v0.1/settings'),
|
||||
settingsCache = require('../../../server/services/settings/cache'),
|
||||
defaultContext = {user: 1},
|
||||
internalContext = {internal: true},
|
||||
callApiWithContext,
|
||||
getErrorDetails;
|
||||
|
||||
describe('Settings API', function () {
|
||||
// Keep the DB clean
|
||||
before(testUtils.teardown);
|
||||
after(testUtils.teardown);
|
||||
before(testUtils.setup('settings', 'users:roles', 'perms:setting', 'perms:init'));
|
||||
|
||||
should.exist(SettingsAPI);
|
||||
|
||||
callApiWithContext = function (context, method) {
|
||||
var args = _.toArray(arguments),
|
||||
options = args[args.length - 1];
|
||||
|
||||
if (_.isObject(options)) {
|
||||
options.context = _.clone(context);
|
||||
}
|
||||
|
||||
return SettingsAPI[method].apply({}, args.slice(2));
|
||||
};
|
||||
getErrorDetails = function (err) {
|
||||
if (err instanceof Error) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
throw new Error(err.message);
|
||||
};
|
||||
|
||||
it('uses Date objects for dateTime fields', function () {
|
||||
return callApiWithContext(defaultContext, 'browse', {}).then(function (results) {
|
||||
should.exist(results);
|
||||
results.settings[0].created_at.should.be.an.instanceof(Date);
|
||||
}).catch(getErrorDetails);
|
||||
});
|
||||
|
||||
it('can browse', function () {
|
||||
return callApiWithContext(defaultContext, 'browse', {}).then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'settings');
|
||||
results.settings.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(results.settings[0], 'setting');
|
||||
|
||||
// Check for a core setting
|
||||
should.not.exist(_.find(results.settings, function (setting) {
|
||||
return setting.type === 'core';
|
||||
}));
|
||||
}).catch(getErrorDetails);
|
||||
});
|
||||
|
||||
it('can browse by type', function () {
|
||||
return callApiWithContext(defaultContext, 'browse', {type: 'blog'}).then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'settings');
|
||||
results.settings.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(results.settings[0], 'setting');
|
||||
|
||||
// Check for a core setting
|
||||
should.not.exist(_.find(results.settings, function (setting) {
|
||||
return setting.type === 'core';
|
||||
}));
|
||||
}).catch(getErrorDetails);
|
||||
});
|
||||
|
||||
it('returns core settings for internal requests when browsing', function () {
|
||||
return callApiWithContext(internalContext, 'browse', {}).then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'settings');
|
||||
results.settings.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(results.settings[0], 'setting');
|
||||
|
||||
// Check for a core setting
|
||||
should.exist(_.find(results.settings, function (setting) {
|
||||
return setting.type === 'core';
|
||||
}));
|
||||
}).catch(getErrorDetails);
|
||||
});
|
||||
|
||||
it('can read blog settings by string', function () {
|
||||
return SettingsAPI.read('title').then(function (response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'settings');
|
||||
response.settings.length.should.equal(1);
|
||||
testUtils.API.checkResponse(response.settings[0], 'setting');
|
||||
}).catch(getErrorDetails);
|
||||
});
|
||||
|
||||
it('cannot read core settings if not an internal request', function () {
|
||||
return callApiWithContext(defaultContext, 'read', {key: 'db_hash'}).then(function () {
|
||||
throw new Error('Allowed to read db_hash with external request');
|
||||
}).catch(function (error) {
|
||||
should.exist(error);
|
||||
error.errorType.should.eql('NoPermissionError');
|
||||
});
|
||||
});
|
||||
|
||||
it('can read core settings if an internal request', function () {
|
||||
return callApiWithContext(internalContext, 'read', {key: 'db_hash'}).then(function (response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'settings');
|
||||
response.settings.length.should.equal(1);
|
||||
testUtils.API.checkResponse(response.settings[0], 'setting');
|
||||
}).catch(getErrorDetails);
|
||||
});
|
||||
|
||||
it('can read by object key', function () {
|
||||
return callApiWithContext(defaultContext, 'read', {key: 'title'}).then(function (response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'settings');
|
||||
response.settings.length.should.equal(1);
|
||||
testUtils.API.checkResponse(response.settings[0], 'setting');
|
||||
}).catch(getErrorDetails);
|
||||
});
|
||||
|
||||
it('can edit', function () {
|
||||
// see default-settings.json
|
||||
settingsCache.get('title').should.eql('Ghost');
|
||||
|
||||
return callApiWithContext(defaultContext, 'edit', {settings: [{key: 'title', value: 'UpdatedGhost'}]}, {})
|
||||
.then(function (response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'settings');
|
||||
response.settings.length.should.equal(1);
|
||||
testUtils.API.checkResponse(response.settings[0], 'setting');
|
||||
|
||||
settingsCache.get('title').should.eql('UpdatedGhost');
|
||||
});
|
||||
});
|
||||
|
||||
it('cannot edit a core setting if not an internal request', function () {
|
||||
return callApiWithContext(defaultContext, 'edit', {settings: [{key: 'db_hash', value: 'hash'}]}, {})
|
||||
.then(function () {
|
||||
throw new Error('Allowed to edit a core setting as external request');
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
|
||||
err.errorType.should.eql('NoPermissionError');
|
||||
});
|
||||
});
|
||||
|
||||
it('can edit a core setting with an internal request', function () {
|
||||
return callApiWithContext(internalContext, 'edit', {settings: [{key: 'db_hash', value: 'hash'}]}, {})
|
||||
.then(function (response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'settings');
|
||||
response.settings.length.should.equal(1);
|
||||
testUtils.API.checkResponse(response.settings[0], 'setting');
|
||||
});
|
||||
});
|
||||
|
||||
it('cannot edit the active theme setting via API even with internal context', function () {
|
||||
return callApiWithContext(internalContext, 'edit', 'active_theme', {
|
||||
settings: [{key: 'active_theme', value: 'rasper'}]
|
||||
}).then(function () {
|
||||
throw new Error('Allowed to change active theme settting');
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
|
||||
err.errorType.should.eql('BadRequestError');
|
||||
err.message.should.eql('Attempted to change active_theme via settings API');
|
||||
});
|
||||
});
|
||||
|
||||
it('ensures values are stringified before saving to database', function () {
|
||||
return callApiWithContext(defaultContext, 'edit', 'title', []).then(function (response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'settings');
|
||||
response.settings.length.should.equal(1);
|
||||
testUtils.API.checkResponse(response.settings[0], 'setting');
|
||||
response.settings[0].value.should.equal('[]');
|
||||
});
|
||||
});
|
||||
|
||||
it('set active_timezone: unknown timezone', function () {
|
||||
return callApiWithContext(defaultContext, 'edit', {settings: [{key: 'active_timezone', value: 'MFG'}]}, {})
|
||||
.then(function () {
|
||||
throw new Error('We expect that the active_timezone cannot be stored');
|
||||
}).catch(function (errors) {
|
||||
should.exist(errors);
|
||||
errors.length.should.eql(1);
|
||||
errors[0].errorType.should.eql('ValidationError');
|
||||
});
|
||||
});
|
||||
|
||||
it('set active_timezone: known timezone', function () {
|
||||
return callApiWithContext(defaultContext, 'edit', {settings: [{key: 'active_timezone', value: 'Etc/UTC'}]}, {});
|
||||
});
|
||||
});
|
@ -1,82 +0,0 @@
|
||||
var should = require('should'),
|
||||
testUtils = require('../../utils'),
|
||||
|
||||
SlugAPI = require('../../../server/api/v0.1/slugs');
|
||||
|
||||
describe('Slug API', function () {
|
||||
// Keep the DB clean
|
||||
before(testUtils.teardown);
|
||||
after(testUtils.teardown);
|
||||
|
||||
before(testUtils.setup('settings', 'users:roles', 'perms:slug', 'perms:init'));
|
||||
|
||||
should.exist(SlugAPI);
|
||||
|
||||
it('can generate post slug', function (done) {
|
||||
SlugAPI.generate({context: {user: 1}, type: 'post', name: 'A fancy Title'})
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'slugs');
|
||||
results.slugs.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(results.slugs[0], 'slug');
|
||||
results.slugs[0].slug.should.equal('a-fancy-title');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can generate tag slug', function (done) {
|
||||
SlugAPI.generate({context: {user: 1}, type: 'tag', name: 'A fancy Title'})
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'slugs');
|
||||
results.slugs.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(results.slugs[0], 'slug');
|
||||
results.slugs[0].slug.should.equal('a-fancy-title');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can generate user slug', function (done) {
|
||||
SlugAPI.generate({context: {user: 1}, type: 'user', name: 'user name'})
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'slugs');
|
||||
results.slugs.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(results.slugs[0], 'slug');
|
||||
results.slugs[0].slug.should.equal('user-name');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can generate app slug', function (done) {
|
||||
SlugAPI.generate({context: {user: 1}, type: 'tag', name: 'app name'})
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'slugs');
|
||||
results.slugs.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(results.slugs[0], 'slug');
|
||||
results.slugs[0].slug.should.equal('app-name');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('rejects unknown types with BadRequestError', function (done) {
|
||||
SlugAPI.generate({context: {user: 1}, type: 'unknown-type', name: 'A fancy Title'})
|
||||
.then(function () {
|
||||
done(new Error('Generate a slug for an unknown type is not rejected.'));
|
||||
}).catch(function (error) {
|
||||
error.errorType.should.equal('BadRequestError');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('rejects invalid types with ValidationError', function (done) {
|
||||
SlugAPI.generate({context: {user: 1}, type: 'unknown type', name: 'A fancy Title'})
|
||||
.then(function () {
|
||||
done(new Error('Generate a slug for an unknown type is not rejected.'));
|
||||
}).catch(function (errors) {
|
||||
errors.should.have.property('errorType', 'ValidationError');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
@ -1,382 +0,0 @@
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
testUtils = require('../../utils'),
|
||||
Promise = require('bluebird'),
|
||||
ObjectId = require('bson-objectid'),
|
||||
fs = require('fs-extra'),
|
||||
_ = require('lodash'),
|
||||
context = testUtils.context,
|
||||
common = require('../../../server/lib/common'),
|
||||
fsLib = require('../../../server/lib/fs'),
|
||||
SubscribersAPI = require('../../../server/api/v0.1/subscribers'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Subscribers API', function () {
|
||||
// Keep the DB clean
|
||||
before(testUtils.teardown);
|
||||
after(testUtils.teardown);
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
before(testUtils.setup('settings', 'users:roles', 'perms:subscriber', 'perms:init', 'posts'));
|
||||
|
||||
should.exist(SubscribersAPI);
|
||||
|
||||
describe('Add', function () {
|
||||
var newSubscriber;
|
||||
|
||||
beforeEach(function () {
|
||||
return testUtils.fixtures.insertOne('Subscriber', 'subscribers', 'createSubscriber');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return testUtils.truncate('subscribers');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
newSubscriber = _.clone(testUtils.DataGenerator.forKnex.createSubscriber(testUtils.DataGenerator.Content.subscribers[1]));
|
||||
Promise.resolve(newSubscriber);
|
||||
});
|
||||
|
||||
it('can add a subscriber (admin)', function (done) {
|
||||
SubscribersAPI.add({subscribers: [newSubscriber]}, testUtils.context.admin)
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.subscribers);
|
||||
results.subscribers.length.should.be.above(0);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can add a subscriber (editor)', function (done) {
|
||||
SubscribersAPI.add({subscribers: [newSubscriber]}, testUtils.context.editor)
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.subscribers);
|
||||
results.subscribers.length.should.be.above(0);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can add a subscriber (author)', function (done) {
|
||||
SubscribersAPI.add({subscribers: [newSubscriber]}, testUtils.context.author)
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.subscribers);
|
||||
results.subscribers.length.should.be.above(0);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can add a subscriber (external)', function (done) {
|
||||
SubscribersAPI.add({subscribers: [newSubscriber]}, testUtils.context.external)
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.subscribers);
|
||||
results.subscribers.length.should.be.above(0);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('duplicate subscriber', function (done) {
|
||||
SubscribersAPI.add({subscribers: [newSubscriber]}, testUtils.context.external)
|
||||
.then(function () {
|
||||
SubscribersAPI.add({subscribers: [newSubscriber]}, testUtils.context.external)
|
||||
.then(function () {
|
||||
return done();
|
||||
})
|
||||
.catch(done);
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('CANNOT add subscriber without context', function (done) {
|
||||
SubscribersAPI.add({subscribers: [newSubscriber]})
|
||||
.then(function () {
|
||||
done(new Error('Add subscriber without context should have no access.'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edit', function () {
|
||||
beforeEach(function () {
|
||||
return testUtils.fixtures.insertOne('Subscriber', 'subscribers', 'createSubscriber');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return testUtils.truncate('subscribers');
|
||||
});
|
||||
|
||||
var newSubscriberEmail = 'subscriber@updated.com',
|
||||
firstSubscriber = testUtils.DataGenerator.Content.subscribers[0].id;
|
||||
|
||||
it('can edit a subscriber (admin)', function (done) {
|
||||
SubscribersAPI.edit({subscribers: [{email: newSubscriberEmail}]}, _.extend({}, context.admin, {id: firstSubscriber}))
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.subscribers);
|
||||
results.subscribers.length.should.be.above(0);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can edit subscriber (external)', function (done) {
|
||||
SubscribersAPI.edit({subscribers: [{email: newSubscriberEmail}]}, _.extend({}, context.external, {id: firstSubscriber}))
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.subscribers);
|
||||
results.subscribers.length.should.be.above(0);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('CANNOT edit a subscriber (editor)', function (done) {
|
||||
SubscribersAPI.edit({subscribers: [{email: newSubscriberEmail}]}, _.extend({}, context.editor, {id: firstSubscriber}))
|
||||
.then(function () {
|
||||
done(new Error('Edit subscriber as author should have no access.'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('CANNOT edit subscriber (author)', function (done) {
|
||||
SubscribersAPI.edit({subscribers: [{email: newSubscriberEmail}]}, _.extend({}, context.author, {id: firstSubscriber}))
|
||||
.then(function () {
|
||||
done(new Error('Edit subscriber as author should have no access.'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('CANNOT edit subscriber that doesn\'t exit', function (done) {
|
||||
SubscribersAPI.edit({subscribers: [{email: newSubscriberEmail}]}, _.extend({}, context.internal, {id: ObjectId.generate()}))
|
||||
.then(function () {
|
||||
done(new Error('Edit non-existent subscriber is possible.'));
|
||||
}, function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.NotFoundError).should.eql(true);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Destroy', function () {
|
||||
beforeEach(function () {
|
||||
return testUtils.fixtures.insertOne('Subscriber', 'subscribers', 'createSubscriber');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return testUtils.truncate('subscribers');
|
||||
});
|
||||
|
||||
var firstSubscriber = testUtils.DataGenerator.Content.subscribers[0];
|
||||
|
||||
it('can destroy subscriber as admin', function (done) {
|
||||
SubscribersAPI.destroy(_.extend({}, testUtils.context.admin, {id: firstSubscriber.id}))
|
||||
.then(function (results) {
|
||||
should.not.exist(results);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can destroy subscriber by email', function (done) {
|
||||
SubscribersAPI.destroy(_.extend({}, testUtils.context.admin, {email: firstSubscriber.email}))
|
||||
.then(function (results) {
|
||||
should.not.exist(results);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns NotFoundError for unknown email', function (done) {
|
||||
SubscribersAPI.destroy(_.extend({}, testUtils.context.admin, {email: 'unknown@example.com'}))
|
||||
.then(function (results) {
|
||||
done(new Error('Destroy subscriber should not be possible with unknown email.'));
|
||||
}, function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.NotFoundError).should.eql(true);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('CANNOT destroy subscriber', function (done) {
|
||||
SubscribersAPI.destroy(_.extend({}, testUtils.context.editor, {id: firstSubscriber.id}))
|
||||
.then(function () {
|
||||
done(new Error('Destroy subscriber should not be possible as editor.'));
|
||||
}, function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Browse', function () {
|
||||
beforeEach(function () {
|
||||
return testUtils.fixtures.insertOne('Subscriber', 'subscribers', 'createSubscriber');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return testUtils.truncate('subscribers');
|
||||
});
|
||||
|
||||
it('can browse (internal)', function (done) {
|
||||
SubscribersAPI.browse(testUtils.context.internal).then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.subscribers);
|
||||
results.subscribers.should.have.lengthOf(1);
|
||||
testUtils.API.checkResponse(results.subscribers[0], 'subscriber');
|
||||
results.subscribers[0].created_at.should.be.an.instanceof(Date);
|
||||
|
||||
results.meta.pagination.should.have.property('page', 1);
|
||||
results.meta.pagination.should.have.property('limit', 15);
|
||||
results.meta.pagination.should.have.property('pages', 1);
|
||||
results.meta.pagination.should.have.property('total', 1);
|
||||
results.meta.pagination.should.have.property('next', null);
|
||||
results.meta.pagination.should.have.property('prev', null);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('CANNOT browse subscriber (external)', function (done) {
|
||||
SubscribersAPI.browse(testUtils.context.external)
|
||||
.then(function () {
|
||||
done(new Error('Browse subscriber should be denied with external context.'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Read', function () {
|
||||
beforeEach(function () {
|
||||
return testUtils.fixtures.insertOne('Subscriber', 'subscribers', 'createSubscriber');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return testUtils.truncate('subscribers');
|
||||
});
|
||||
|
||||
it('with id', function (done) {
|
||||
SubscribersAPI.browse({context: {user: 1}}).then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.subscribers);
|
||||
results.subscribers.length.should.be.above(0);
|
||||
|
||||
var firstSubscriber = _.find(results.subscribers, {id: testUtils.DataGenerator.Content.subscribers[0].id});
|
||||
return SubscribersAPI.read({context: {user: 1}, id: firstSubscriber.id});
|
||||
}).then(function (found) {
|
||||
should.exist(found);
|
||||
testUtils.API.checkResponse(found.subscribers[0], 'subscriber');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('with email', function (done) {
|
||||
SubscribersAPI.browse({context: {user: 1}}).then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.subscribers);
|
||||
results.subscribers.length.should.be.above(0);
|
||||
|
||||
var firstSubscriber = _.find(results.subscribers, {id: testUtils.DataGenerator.Content.subscribers[0].id});
|
||||
return SubscribersAPI.read({context: {user: 1}, email: firstSubscriber.email});
|
||||
}).then(function (found) {
|
||||
should.exist(found);
|
||||
testUtils.API.checkResponse(found.subscribers[0], 'subscriber');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('CANNOT fetch a subscriber which doesn\'t exist', function (done) {
|
||||
SubscribersAPI.read({context: {user: 1}, id: 999}).then(function () {
|
||||
done(new Error('Should not return a result'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
(err instanceof common.errors.NotFoundError).should.eql(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Read CSV', function () {
|
||||
var scope = {};
|
||||
|
||||
beforeEach(function () {
|
||||
return testUtils.fixtures.insertOne('Subscriber', 'subscribers', 'createSubscriber');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return testUtils.truncate('subscribers');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox.stub(fs, 'unlink').resolves();
|
||||
sandbox.stub(fsLib, 'readCSV').value(function () {
|
||||
if (scope.csvError) {
|
||||
return Promise.reject(new Error('csv'));
|
||||
}
|
||||
|
||||
return Promise.resolve(scope.values);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
scope.csvError = false;
|
||||
});
|
||||
|
||||
it('check that fn works in general', function (done) {
|
||||
scope.values = [{email: 'lol@hallo.de'}, {email: 'test'}, {email: 'lol@hallo.de'}];
|
||||
|
||||
SubscribersAPI.importCSV(_.merge(testUtils.context.internal, {path: '/somewhere'}))
|
||||
.then(function (result) {
|
||||
result.meta.stats.imported.should.eql(1);
|
||||
result.meta.stats.duplicates.should.eql(1);
|
||||
result.meta.stats.invalid.should.eql(1);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('check that fn works in general', function (done) {
|
||||
scope.values = [{email: 'lol@hallo.de'}, {email: '1@kate.de'}];
|
||||
|
||||
SubscribersAPI.importCSV(_.merge(testUtils.context.internal, {path: '/somewhere'}))
|
||||
.then(function (result) {
|
||||
result.meta.stats.imported.should.eql(2);
|
||||
result.meta.stats.duplicates.should.eql(0);
|
||||
result.meta.stats.invalid.should.eql(0);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('read csv throws a not found error', function (done) {
|
||||
scope.csvError = true;
|
||||
|
||||
SubscribersAPI.importCSV(_.merge(testUtils.context.internal, {path: '/somewhere'}))
|
||||
.then(function () {
|
||||
done(new Error('we expected an error here!'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
err.message.should.eql('csv');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,433 +0,0 @@
|
||||
var should = require('should'),
|
||||
testUtils = require('../../utils'),
|
||||
_ = require('lodash'),
|
||||
// Stuff we are testing
|
||||
context = testUtils.context,
|
||||
|
||||
TagAPI = require('../../../server/api/v0.1/tags');
|
||||
|
||||
// there are some random generated tags in test database
|
||||
// which can't be sorted easily using _.sortBy()
|
||||
// so we filter them out and leave only pre-built fixtures
|
||||
// usage: tags.filter(onlyFixtures)
|
||||
function onlyFixtures(slug) {
|
||||
return testUtils.DataGenerator.Content.tags.indexOf(slug) >= 0;
|
||||
}
|
||||
|
||||
describe('Tags API', function () {
|
||||
// Keep the DB clean
|
||||
before(testUtils.teardown);
|
||||
after(testUtils.teardown);
|
||||
|
||||
before(testUtils.setup('settings', 'users:roles', 'perms:tag', 'perms:init'));
|
||||
|
||||
should.exist(TagAPI);
|
||||
|
||||
describe('Add', function () {
|
||||
var newTag;
|
||||
|
||||
beforeEach(function () {
|
||||
return testUtils.fixtures.insertTags();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return testUtils.truncate('tags');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
newTag = _.clone(_.omit(testUtils.DataGenerator.forKnex.createTag(testUtils.DataGenerator.Content.tags[0]), 'id'));
|
||||
});
|
||||
|
||||
it('can add a tag (admin)', function (done) {
|
||||
TagAPI.add({tags: [newTag]}, testUtils.context.admin)
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.length.should.be.above(0);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can add a tag (editor)', function (done) {
|
||||
TagAPI.add({tags: [newTag]}, testUtils.context.editor)
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.length.should.be.above(0);
|
||||
results.tags[0].visibility.should.eql('public');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can add a tag (author)', function (done) {
|
||||
TagAPI.add({tags: [newTag]}, testUtils.context.author)
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.length.should.be.above(0);
|
||||
results.tags[0].visibility.should.eql('public');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('add internal tag', function (done) {
|
||||
TagAPI
|
||||
.add({tags: [{name: '#test'}]}, testUtils.context.editor)
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.length.should.be.above(0);
|
||||
results.tags[0].visibility.should.eql('internal');
|
||||
results.tags[0].name.should.eql('#test');
|
||||
results.tags[0].slug.should.eql('hash-test');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('CANNOT add tag (contributor)', function (done) {
|
||||
TagAPI.add({tags: [newTag]}, testUtils.context.contributor)
|
||||
.then(function () {
|
||||
done(new Error('Add tag is not denied for contributor.'));
|
||||
}, function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('No-auth CANNOT add tag', function (done) {
|
||||
TagAPI.add({tags: [newTag]}).then(function () {
|
||||
done(new Error('Add tag is not denied without authentication.'));
|
||||
}, function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('rejects invalid names with ValidationError', function (done) {
|
||||
var invalidTag = _.clone(newTag);
|
||||
|
||||
invalidTag.name = ', starts with a comma';
|
||||
|
||||
TagAPI.add({tags: [invalidTag]}, testUtils.context.admin)
|
||||
.then(function () {
|
||||
done(new Error('Adding a tag with an invalid name is not rejected.'));
|
||||
}).catch(function (errors) {
|
||||
errors[0].errorType.should.eql('ValidationError');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edit', function () {
|
||||
beforeEach(function () {
|
||||
return testUtils.fixtures.insertTags();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return testUtils.truncate('tags');
|
||||
});
|
||||
|
||||
var newTagName = 'tagNameUpdated',
|
||||
firstTag = testUtils.DataGenerator.Content.tags[0].id;
|
||||
|
||||
it('can edit a tag (admin)', function (done) {
|
||||
TagAPI.edit({tags: [{name: newTagName}]}, _.extend({}, context.admin, {id: firstTag}))
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.length.should.be.above(0);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can edit a tag (editor)', function (done) {
|
||||
TagAPI.edit({tags: [{name: newTagName}]}, _.extend({}, context.editor, {id: firstTag}))
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.length.should.be.above(0);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('CANNOT edit a tag (author)', function (done) {
|
||||
TagAPI.edit({tags: [{name: newTagName}]}, _.extend({}, context.author, {id: firstTag}))
|
||||
.then(function () {
|
||||
done(new Error('Add tag is not denied for author.'));
|
||||
}, function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('No-auth CANNOT edit tag', function (done) {
|
||||
TagAPI.edit({tags: [{name: newTagName}]}, _.extend({}, {id: firstTag}))
|
||||
.then(function () {
|
||||
done(new Error('Add tag is not denied without authentication.'));
|
||||
}, function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('rejects invalid names with ValidationError', function (done) {
|
||||
var invalidTagName = ', starts with a comma';
|
||||
|
||||
TagAPI.edit({tags: [{name: invalidTagName}]}, _.extend({}, context.editor, {id: firstTag}))
|
||||
.then(function () {
|
||||
done(new Error('Adding a tag with an invalid name is not rejected.'));
|
||||
}).catch(function (errors) {
|
||||
errors[0].errorType.should.eql('ValidationError');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Destroy', function () {
|
||||
beforeEach(function () {
|
||||
return testUtils.fixtures.insertTags();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return testUtils.truncate('tags');
|
||||
});
|
||||
|
||||
var firstTag = testUtils.DataGenerator.Content.tags[0].id;
|
||||
|
||||
it('can destroy Tag', function (done) {
|
||||
TagAPI.destroy(_.extend({}, testUtils.context.admin, {id: firstTag}))
|
||||
.then(function (results) {
|
||||
should.not.exist(results);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Browse', function () {
|
||||
before(function () {
|
||||
return testUtils.fixtures.insertPostsAndTags();
|
||||
});
|
||||
|
||||
after(testUtils.teardown);
|
||||
|
||||
before(function () {
|
||||
return testUtils.fixtures.insertExtraTags();
|
||||
});
|
||||
|
||||
it('can browse (internal)', function (done) {
|
||||
TagAPI.browse(testUtils.context.internal).then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.should.have.lengthOf(15);
|
||||
testUtils.API.checkResponse(results.tags[0], 'tag');
|
||||
results.tags[0].created_at.should.be.an.instanceof(Date);
|
||||
|
||||
results.meta.pagination.should.have.property('page', 1);
|
||||
results.meta.pagination.should.have.property('limit', 15);
|
||||
results.meta.pagination.should.have.property('pages', 4);
|
||||
results.meta.pagination.should.have.property('total', 55);
|
||||
results.meta.pagination.should.have.property('next', 2);
|
||||
results.meta.pagination.should.have.property('prev', null);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can browse page 2 (internal)', function (done) {
|
||||
TagAPI.browse(_.extend({}, testUtils.context.internal, {page: 2})).then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.should.have.lengthOf(15);
|
||||
testUtils.API.checkResponse(results.tags[0], 'tag');
|
||||
results.tags[0].created_at.should.be.an.instanceof(Date);
|
||||
|
||||
results.meta.pagination.should.have.property('page', 2);
|
||||
results.meta.pagination.should.have.property('limit', 15);
|
||||
results.meta.pagination.should.have.property('pages', 4);
|
||||
results.meta.pagination.should.have.property('total', 55);
|
||||
results.meta.pagination.should.have.property('next', 3);
|
||||
results.meta.pagination.should.have.property('prev', 1);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can browse (owner)', function (done) {
|
||||
TagAPI.browse({context: {user: 1}}).then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(results.tags[0], 'tag');
|
||||
results.tags[0].created_at.should.be.an.instanceof(Date);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can browse (admin)', function (done) {
|
||||
TagAPI.browse(testUtils.context.admin).then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(results.tags[0], 'tag');
|
||||
results.tags[0].created_at.should.be.an.instanceof(Date);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can browse (editor)', function (done) {
|
||||
TagAPI.browse(testUtils.context.editor).then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(results.tags[0], 'tag');
|
||||
results.tags[0].created_at.should.be.an.instanceof(Date);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can browse (author)', function (done) {
|
||||
TagAPI.browse(testUtils.context.author).then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(results.tags[0], 'tag');
|
||||
results.tags[0].created_at.should.be.an.instanceof(Date);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can browse with include count.posts', function (done) {
|
||||
TagAPI.browse({context: {user: 1}, include: 'count.posts'}).then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.should.have.lengthOf(15);
|
||||
testUtils.API.checkResponse(results.tags[0], 'tag', 'count');
|
||||
should.exist(results.tags[0].count.posts);
|
||||
|
||||
results.tags[0].count.posts.should.eql(2);
|
||||
results.tags[1].count.posts.should.eql(2);
|
||||
results.meta.pagination.should.have.property('page', 1);
|
||||
results.meta.pagination.should.have.property('limit', 15);
|
||||
results.meta.pagination.should.have.property('pages', 4);
|
||||
results.meta.pagination.should.have.property('total', 55);
|
||||
results.meta.pagination.should.have.property('next', 2);
|
||||
results.meta.pagination.should.have.property('prev', null);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can browse page 4 with include count.posts', function (done) {
|
||||
TagAPI.browse({context: {user: 1}, include: 'count.posts', page: 4}).then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.should.have.lengthOf(10);
|
||||
testUtils.API.checkResponse(results.tags[0], 'tag', 'count');
|
||||
should.exist(results.tags[0].count.posts);
|
||||
|
||||
results.meta.pagination.should.have.property('page', 4);
|
||||
results.meta.pagination.should.have.property('limit', 15);
|
||||
results.meta.pagination.should.have.property('pages', 4);
|
||||
results.meta.pagination.should.have.property('total', 55);
|
||||
results.meta.pagination.should.have.property('next', null);
|
||||
results.meta.pagination.should.have.property('prev', 3);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can browse and order by slug using asc', function (done) {
|
||||
var expectedTags;
|
||||
|
||||
TagAPI.browse({context: {user: 1}})
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
|
||||
expectedTags = _(results.tags).map('slug').filter(onlyFixtures).sortBy().value();
|
||||
|
||||
return TagAPI.browse({context: {user: 1}, order: 'slug asc'});
|
||||
})
|
||||
.then(function (results) {
|
||||
var tags;
|
||||
|
||||
should.exist(results);
|
||||
|
||||
tags = _(results.tags).map('slug').filter(onlyFixtures).value();
|
||||
tags.should.eql(expectedTags);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('can browse and order by slug using desc', function (done) {
|
||||
var expectedTags;
|
||||
|
||||
TagAPI.browse({context: {user: 1}})
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
|
||||
expectedTags = _(results.tags).map('slug').filter(onlyFixtures).sortBy().reverse().value();
|
||||
|
||||
return TagAPI.browse({context: {user: 1}, order: 'slug desc'});
|
||||
})
|
||||
.then(function (results) {
|
||||
var tags;
|
||||
|
||||
should.exist(results);
|
||||
|
||||
tags = _(results.tags).map('slug').filter(onlyFixtures).value();
|
||||
tags.should.eql(expectedTags);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Read', function () {
|
||||
before(testUtils.setup('users:roles', 'posts'));
|
||||
|
||||
it('returns count.posts with include count.posts', function (done) {
|
||||
TagAPI.read({context: {user: 1}, include: 'count.posts', slug: 'kitchen-sink'}).then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.length.should.be.above(0);
|
||||
|
||||
testUtils.API.checkResponse(results.tags[0], 'tag', 'count');
|
||||
should.exist(results.tags[0].count.posts);
|
||||
results.tags[0].count.posts.should.equal(2);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('with slug', function (done) {
|
||||
TagAPI.browse({context: {user: 1}}).then(function (results) {
|
||||
should.exist(results);
|
||||
should.exist(results.tags);
|
||||
results.tags.length.should.be.above(0);
|
||||
|
||||
var firstTag = _.find(results.tags, {id: testUtils.DataGenerator.Content.tags[0].id});
|
||||
|
||||
return TagAPI.read({context: {user: 1}, slug: firstTag.slug});
|
||||
}).then(function (found) {
|
||||
should.exist(found);
|
||||
testUtils.API.checkResponse(found.tags[0], 'tag');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
// TODO: this should be a 422?
|
||||
it('cannot fetch a tag with an invalid slug', function (done) {
|
||||
TagAPI.read({slug: 'invalid!'}).then(function () {
|
||||
done(new Error('Should not return a result with invalid slug'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
err.message.should.eql('Tag not found.');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
File diff suppressed because it is too large
Load Diff
@ -1,147 +0,0 @@
|
||||
var _ = require('lodash'),
|
||||
should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
testUtils = require('../../utils'),
|
||||
Promise = require('bluebird'),
|
||||
WebhookAPI = require('../../../server/api/v0.1/webhooks'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Webhooks API', function () {
|
||||
beforeEach(testUtils.teardown);
|
||||
beforeEach(testUtils.setup('webhooks', 'users:roles', 'perms:webhook', 'perms:init'));
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
after(testUtils.teardown);
|
||||
|
||||
function checkForErrorType(type, done) {
|
||||
return function checkForErrorType(error) {
|
||||
if (Array.isArray(error)) {
|
||||
error = error[0];
|
||||
}
|
||||
|
||||
if (error.errorType) {
|
||||
error.errorType.should.eql(type);
|
||||
done();
|
||||
} else {
|
||||
done(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
describe('Validations', function () {
|
||||
it('Prevents mixed case event names', function (done) {
|
||||
WebhookAPI.add({webhooks: [{
|
||||
event: 'Mixed.Case',
|
||||
target_url: 'https://example.com/hooks/test'
|
||||
}]}, testUtils.context.owner)
|
||||
.then(function () {
|
||||
done(new Error('Should not allow mixed case event names'));
|
||||
}).catch(checkForErrorType('ValidationError', done));
|
||||
});
|
||||
|
||||
it('Prevents duplicate event/target pairs', function (done) {
|
||||
var duplicate = testUtils.DataGenerator.Content.webhooks[0];
|
||||
|
||||
WebhookAPI.add({webhooks: [{
|
||||
event: duplicate.event,
|
||||
target_url: duplicate.target_url
|
||||
}]}, testUtils.context.owner)
|
||||
.then(function () {
|
||||
done(new Error('Should not allow duplicate event/target'));
|
||||
}).catch(checkForErrorType('ValidationError', done));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Permissions', function () {
|
||||
var firstWebhook = testUtils.DataGenerator.Content.webhooks[0].id;
|
||||
var newWebhook;
|
||||
|
||||
function checkAddResponse(response) {
|
||||
should.exist(response);
|
||||
should.exist(response.webhooks);
|
||||
should.not.exist(response.meta);
|
||||
|
||||
response.webhooks.should.have.length(1);
|
||||
testUtils.API.checkResponse(response.webhooks[0], 'webhook');
|
||||
response.webhooks[0].created_at.should.be.an.instanceof(Date);
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
newWebhook = {
|
||||
event: 'test.added',
|
||||
target_url: 'https://example.com/webhooks/test-added'
|
||||
};
|
||||
});
|
||||
|
||||
describe('Owner', function () {
|
||||
it('Can add', function (done) {
|
||||
WebhookAPI.add({webhooks: [newWebhook]}, testUtils.context.owner)
|
||||
.then(function (response) {
|
||||
checkAddResponse(response);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can delete', function (done) {
|
||||
WebhookAPI.destroy(_.extend({}, testUtils.context.owner, {id: firstWebhook}))
|
||||
.then(function (results) {
|
||||
should.not.exist(results);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Admin', function () {
|
||||
it('Can add', function (done) {
|
||||
WebhookAPI.add({webhooks: [newWebhook]}, testUtils.context.admin)
|
||||
.then(function (response) {
|
||||
checkAddResponse(response);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('Can delete', function (done) {
|
||||
WebhookAPI.destroy(_.extend({}, testUtils.context.admin, {id: firstWebhook}))
|
||||
.then(function (results) {
|
||||
should.not.exist(results);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Editor', function () {
|
||||
it('CANNOT add', function (done) {
|
||||
WebhookAPI.add({webhooks: [newWebhook]}, testUtils.context.editor)
|
||||
.then(function () {
|
||||
done(new Error('Editor should not be able to add a webhook'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('CANNOT delete', function (done) {
|
||||
WebhookAPI.destroy(_.extend({}, testUtils.context.editor, {id: firstWebhook}))
|
||||
.then(function () {
|
||||
done(new Error('Editor should not be able to delete a webhook'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Author', function () {
|
||||
it('CANNOT add', function (done) {
|
||||
WebhookAPI.add({webhooks: [newWebhook]}, testUtils.context.author)
|
||||
.then(function () {
|
||||
done(new Error('Author should not be able to add a webhook'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
|
||||
it('CANNOT delete', function (done) {
|
||||
WebhookAPI.destroy(_.extend({}, testUtils.context.author, {id: firstWebhook}))
|
||||
.then(function () {
|
||||
done(new Error('Author should not be able to delete a webhook'));
|
||||
}).catch(checkForErrorType('NoPermissionError', done));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,112 +0,0 @@
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
testUtils = require('../../utils'),
|
||||
Promise = require('bluebird'),
|
||||
RedirectsAPI = require('../../../server/api/v0.1/redirects'),
|
||||
mail = require('../../../server/api/v0.1/mail'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
should.equal(true, true);
|
||||
|
||||
describe('Redirects API', function () {
|
||||
beforeEach(testUtils.teardown);
|
||||
beforeEach(testUtils.setup('settings', 'users:roles', 'perms:redirect', 'perms:init'));
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox.stub(mail, 'send').callsFake(function () {
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
after(testUtils.teardown);
|
||||
|
||||
describe('Permissions', function () {
|
||||
describe('Owner', function () {
|
||||
it('Can upload', function (done) {
|
||||
RedirectsAPI.upload(testUtils.context.owner)
|
||||
.then(function () {
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('Can download', function (done) {
|
||||
RedirectsAPI.download(testUtils.context.owner)
|
||||
.then(function () {
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Admin', function () {
|
||||
it('Can upload', function (done) {
|
||||
RedirectsAPI.upload(testUtils.context.admin)
|
||||
.then(function () {
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('Can download', function (done) {
|
||||
RedirectsAPI.download(testUtils.context.admin)
|
||||
.then(function () {
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Editor', function () {
|
||||
it('Can\'t upload', function (done) {
|
||||
RedirectsAPI.upload(testUtils.context.editor)
|
||||
.then(function () {
|
||||
done(new Error('Editor is not allowed to upload redirects.'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
err.statusCode.should.eql(403);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Can\'t download', function (done) {
|
||||
RedirectsAPI.upload(testUtils.context.editor)
|
||||
.then(function () {
|
||||
done(new Error('Editor is not allowed to download redirects.'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
err.statusCode.should.eql(403);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Author', function () {
|
||||
it('Can\'t upload', function (done) {
|
||||
RedirectsAPI.upload(testUtils.context.author)
|
||||
.then(function () {
|
||||
done(new Error('Author is not allowed to upload redirects.'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
err.statusCode.should.eql(403);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Can\'t download', function (done) {
|
||||
RedirectsAPI.upload(testUtils.context.author)
|
||||
.then(function () {
|
||||
done(new Error('Author is not allowed to download redirects.'));
|
||||
})
|
||||
.catch(function (err) {
|
||||
err.statusCode.should.eql(403);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -105,6 +105,26 @@ describe('Validation', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('webhooks.add', function () {
|
||||
it('event name is not lowercase', function () {
|
||||
const webhook = models.Webhook.forge(testUtils.DataGenerator.forKnex.createWebhook({event: 'Test'}));
|
||||
|
||||
// NOTE: Fields with `defaultTo` are getting ignored. This is handled on the DB level.
|
||||
return validation.validateSchema('webhooks', webhook, {method: 'insert'})
|
||||
.then(function () {
|
||||
throw new Error('Expected ValidationError.');
|
||||
})
|
||||
.catch(function (err) {
|
||||
if (!_.isArray(err)) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
err.length.should.eql(1);
|
||||
err[0].errorType.should.eql('ValidationError');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('models.edit', function () {
|
||||
it('uuid is invalid', function () {
|
||||
const postModel = models.Post.forge({id: ObjectId.generate(), uuid: '1234'});
|
||||
|
@ -1,10 +1,12 @@
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
_ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
security = require('../../../../server/lib/security'),
|
||||
models = require('../../../../server/models'),
|
||||
ghostBookshelf,
|
||||
urlService = require('../../../../server/services/url'),
|
||||
filters = require('../../../../server/filters'),
|
||||
testUtils = require('../../../utils'),
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Models: base', function () {
|
||||
@ -16,7 +18,92 @@ describe('Models: base', function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('fn: sanitizeData', function () {
|
||||
describe('generateSlug', function () {
|
||||
let Model;
|
||||
let options = {};
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox.stub(security.string, 'safe');
|
||||
sandbox.stub(filters, 'doFilter').resolves();
|
||||
sandbox.stub(urlService.utils, 'getProtectedSlugs').returns(['upsi', 'schwupsi']);
|
||||
|
||||
Model = sandbox.stub();
|
||||
Model.prototype = {
|
||||
tableName: 'tableName'
|
||||
};
|
||||
Model.findOne = sandbox.stub();
|
||||
});
|
||||
|
||||
it('default', function () {
|
||||
Model.findOne.resolves(false);
|
||||
security.string.safe.withArgs('My-Slug').returns('my-slug');
|
||||
|
||||
return models.Base.Model.generateSlug(Model, 'My-Slug', options)
|
||||
.then((slug) => {
|
||||
slug.should.eql('my-slug');
|
||||
});
|
||||
});
|
||||
|
||||
it('slug exists', function () {
|
||||
let i = 0;
|
||||
Model.findOne.callsFake(() => {
|
||||
i = i + 1;
|
||||
if (i === 1) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
return Promise.resolve(false);
|
||||
});
|
||||
|
||||
security.string.safe.withArgs('My-Slug').returns('my-slug');
|
||||
|
||||
return models.Base.Model.generateSlug(Model, 'My-Slug', options)
|
||||
.then((slug) => {
|
||||
slug.should.eql('my-slug-2');
|
||||
});
|
||||
});
|
||||
|
||||
it('too long', function () {
|
||||
Model.findOne.resolves(false);
|
||||
const slug = new Array(500).join('a');
|
||||
|
||||
security.string.safe.withArgs(slug).returns(slug);
|
||||
|
||||
return models.Base.Model.generateSlug(Model, slug, options)
|
||||
.then((slug) => {
|
||||
slug.should.eql(new Array(186).join('a'));
|
||||
});
|
||||
});
|
||||
|
||||
it('protected slug', function () {
|
||||
Model.findOne.resolves(false);
|
||||
const slug = 'upsi';
|
||||
|
||||
security.string.safe.withArgs(slug).returns(slug);
|
||||
|
||||
return models.Base.Model.generateSlug(Model, slug, options)
|
||||
.then((slug) => {
|
||||
slug.should.eql('upsi-tableName');
|
||||
});
|
||||
});
|
||||
|
||||
it('internal tag', function () {
|
||||
Model.findOne.resolves(false);
|
||||
const slug = '#lul';
|
||||
|
||||
Model.prototype = {
|
||||
tableName: 'tag'
|
||||
};
|
||||
|
||||
security.string.safe.withArgs(slug).returns(slug);
|
||||
|
||||
return models.Base.Model.generateSlug(Model, slug, options)
|
||||
.then((slug) => {
|
||||
slug.should.eql('hash-#lul');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('sanitizeData', function () {
|
||||
it('date is invalid', function () {
|
||||
const data = testUtils.DataGenerator.forKnex.createPost({updated_at: '0000-00-00 00:00:00'});
|
||||
|
||||
@ -74,7 +161,7 @@ describe('Models: base', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('fn: setEmptyValuesToNull', function () {
|
||||
describe('setEmptyValuesToNull', function () {
|
||||
it('resets given empty value to null', function () {
|
||||
const base = models.Base.Model.forge({a: '', b: ''});
|
||||
|
||||
@ -96,7 +183,7 @@ describe('Models: base', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('static destroy()', function () {
|
||||
describe('destroy', function () {
|
||||
it('forges model using destroyBy, fetches it, and calls destroy, passing filtered options', function () {
|
||||
const unfilteredOptions = {
|
||||
destroyBy: {
|
||||
@ -154,7 +241,7 @@ describe('Models: base', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('static findOne(data, unfilteredOptions)', function () {
|
||||
describe('findOne', function () {
|
||||
it('forges model using filtered data, fetches it passing filtered options and resolves with the fetched model', function () {
|
||||
const data = {
|
||||
id: 670
|
||||
@ -192,7 +279,7 @@ describe('Models: base', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('static edit(data, unfilteredOptions)', function () {
|
||||
describe('edit', function () {
|
||||
it('resolves with the savedModel after forges model w/ id, fetches w/ filtered options, saves w/ filtered data and options and method=update', function () {
|
||||
const data = {
|
||||
life: 'suffering'
|
||||
@ -272,7 +359,7 @@ describe('Models: base', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('static add(data, unfilteredOptions)', function () {
|
||||
describe('add', function () {
|
||||
it('forges model w/ filtered data, saves w/ null and options and method=insert', function () {
|
||||
const data = {
|
||||
rum: 'ham'
|
||||
|
@ -1,5 +1,6 @@
|
||||
const should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
Promise = require('bluebird'),
|
||||
common = require('../../../server/lib/common'),
|
||||
models = require('../../../server/models'),
|
||||
settingsCache = require('../../../server/services/settings/cache'),
|
||||
@ -9,10 +10,13 @@ const should = require('should'),
|
||||
describe('Unit: models/invite', function () {
|
||||
before(function () {
|
||||
models.init();
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox.stub(settingsCache, 'get').withArgs('db_hash').returns('12345678');
|
||||
});
|
||||
|
||||
after(function () {
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
@ -68,4 +72,258 @@ describe('Unit: models/invite', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('permissible', function () {
|
||||
describe('action: add', function () {
|
||||
let inviteModel;
|
||||
let context;
|
||||
let unsafeAttrs;
|
||||
let roleModel;
|
||||
let loadedPermissions;
|
||||
|
||||
before(function () {
|
||||
inviteModel = {};
|
||||
context = {};
|
||||
unsafeAttrs = {role_id: 'role_id'};
|
||||
roleModel = sandbox.stub();
|
||||
roleModel.get = sandbox.stub();
|
||||
loadedPermissions = {
|
||||
user: {
|
||||
roles: []
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
it('role does not exist', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(null);
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
(err instanceof common.errors.NotFoundError).should.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('invite owner', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Owner');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('as owner', function () {
|
||||
beforeEach(function () {
|
||||
loadedPermissions.user.roles = [{name: 'Owner'}];
|
||||
});
|
||||
|
||||
it('invite administrator', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Administrator');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions);
|
||||
});
|
||||
|
||||
it('invite editor', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Editor');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions);
|
||||
});
|
||||
|
||||
it('invite author', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Author');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions);
|
||||
});
|
||||
|
||||
it('invite contributor', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Contributor');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions);
|
||||
});
|
||||
});
|
||||
|
||||
describe('as administrator', function () {
|
||||
beforeEach(function () {
|
||||
loadedPermissions.user.roles = [{name: 'Administrator'}];
|
||||
});
|
||||
|
||||
it('invite administrator', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Administrator');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions);
|
||||
});
|
||||
|
||||
it('invite editor', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Editor');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions);
|
||||
});
|
||||
|
||||
it('invite author', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Author');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions);
|
||||
});
|
||||
|
||||
it('invite contributor', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Contributor');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions);
|
||||
});
|
||||
});
|
||||
|
||||
describe('as editor', function () {
|
||||
beforeEach(function () {
|
||||
loadedPermissions.user.roles = [{name: 'Editor'}];
|
||||
});
|
||||
|
||||
it('invite administrator', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Administrator');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('invite editor', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Editor');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('invite author', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Author');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions);
|
||||
});
|
||||
|
||||
it('invite contributor', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Contributor');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions);
|
||||
});
|
||||
});
|
||||
|
||||
describe('as author', function () {
|
||||
beforeEach(function () {
|
||||
loadedPermissions.user.roles = [{name: 'Author'}];
|
||||
});
|
||||
|
||||
it('invite administrator', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Administrator');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('invite editor', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Editor');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('invite author', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Author');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('invite contributor', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Contributor');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('as contributor', function () {
|
||||
beforeEach(function () {
|
||||
loadedPermissions.user.roles = [{name: 'Contributor'}];
|
||||
});
|
||||
|
||||
it('invite administrator', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Administrator');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('invite editor', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Editor');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('invite author', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Author');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('invite contributor', function () {
|
||||
sandbox.stub(models.Role, 'findOne').withArgs({id: 'role_id'}).resolves(roleModel);
|
||||
roleModel.get.withArgs('name').returns('Contributor');
|
||||
|
||||
return models.Invite.permissible(inviteModel, 'add', context, unsafeAttrs, loadedPermissions)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
(err instanceof common.errors.NoPermissionError).should.eql(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -4,16 +4,258 @@ const should = require('should'),
|
||||
url = require('url'),
|
||||
sinon = require('sinon'),
|
||||
_ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
testUtils = require('../../utils'),
|
||||
knex = require('../../../server/data/db').knex,
|
||||
db = require('../../../server/data/db'),
|
||||
urlService = require('../../../server/services/url'),
|
||||
schema = require('../../../server/data/schema'),
|
||||
models = require('../../../server/models'),
|
||||
common = require('../../../server/lib/common'),
|
||||
security = require('../../../server/lib/security'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
sandbox = sinon.sandbox.create(),
|
||||
userIdFor = testUtils.users.ids,
|
||||
context = testUtils.context;
|
||||
|
||||
describe('Unit: models/post', function () {
|
||||
const mockDb = require('mock-knex');
|
||||
let tracker;
|
||||
|
||||
before(function () {
|
||||
models.init();
|
||||
mockDb.mock(knex);
|
||||
tracker = mockDb.getTracker();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
after(function () {
|
||||
mockDb.unmock(knex);
|
||||
});
|
||||
|
||||
describe('filter', function () {
|
||||
it('generates correct query for - filter: tags: [photo, video] + id: -{id},limit of: 3, with related: tags', function () {
|
||||
const queries = [];
|
||||
tracker.install();
|
||||
|
||||
tracker.on('query', (query) => {
|
||||
queries.push(query);
|
||||
query.response([]);
|
||||
});
|
||||
|
||||
return models.Post.findPage({
|
||||
filter: 'tags: [photo, video] + id: -' + testUtils.filterData.data.posts[3].id,
|
||||
limit: 3,
|
||||
withRelated: ['tags']
|
||||
}).then(() => {
|
||||
queries.length.should.eql(2);
|
||||
queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` left outer join `posts_tags` on `posts_tags`.`post_id` = `posts`.`id` left outer join `tags` on `posts_tags`.`tag_id` = `tags`.`id` where (`posts`.`page` = ? and `posts`.`status` = ?) and (`tags`.`slug` in (?, ?) and `posts`.`id` != ?) order by count(tags.id) DESC');
|
||||
queries[0].bindings.should.eql([
|
||||
false,
|
||||
'published',
|
||||
'photo',
|
||||
'video',
|
||||
testUtils.filterData.data.posts[3].id
|
||||
]);
|
||||
|
||||
queries[1].sql.should.eql('select `posts`.* from `posts` left outer join `posts_tags` on `posts_tags`.`post_id` = `posts`.`id` left outer join `tags` on `posts_tags`.`tag_id` = `tags`.`id` where (`posts`.`page` = ? and `posts`.`status` = ?) and (`tags`.`slug` in (?, ?) and `posts`.`id` != ?) group by `posts`.`id` order by count(tags.id) DESC, CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC limit ?');
|
||||
queries[1].bindings.should.eql([
|
||||
false,
|
||||
'published',
|
||||
'photo',
|
||||
'video',
|
||||
testUtils.filterData.data.posts[3].id,
|
||||
3
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('generates correct query for - filter: authors:[leslie,pat]+(tag:hash-audio,feature_image:-null), with related: authors,tags', function () {
|
||||
const queries = [];
|
||||
tracker.install();
|
||||
|
||||
tracker.on('query', (query) => {
|
||||
queries.push(query);
|
||||
query.response([]);
|
||||
});
|
||||
|
||||
return models.Post.findPage({
|
||||
filter: 'authors:[leslie,pat]+(tag:hash-audio,feature_image:-null)',
|
||||
withRelated: ['authors', 'tags']
|
||||
}).then(() => {
|
||||
queries.length.should.eql(2);
|
||||
queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` left outer join `posts_tags` on `posts_tags`.`post_id` = `posts`.`id` left outer join `tags` on `posts_tags`.`tag_id` = `tags`.`id` left outer join `posts_authors` on `posts_authors`.`post_id` = `posts`.`id` left outer join `users` as `authors` on `posts_authors`.`author_id` = `authors`.`id` where (`posts`.`page` = ? and `posts`.`status` = ?) and (`authors`.`slug` in (?, ?) and (`tags`.`slug` = ? or `posts`.`feature_image` is not null)) order by count(authors.id) DESC');
|
||||
queries[0].bindings.should.eql([
|
||||
false,
|
||||
'published',
|
||||
'leslie',
|
||||
'pat',
|
||||
'hash-audio'
|
||||
]);
|
||||
|
||||
queries[1].sql.should.eql('select `posts`.* from `posts` left outer join `posts_tags` on `posts_tags`.`post_id` = `posts`.`id` left outer join `tags` on `posts_tags`.`tag_id` = `tags`.`id` left outer join `posts_authors` on `posts_authors`.`post_id` = `posts`.`id` left outer join `users` as `authors` on `posts_authors`.`author_id` = `authors`.`id` where (`posts`.`page` = ? and `posts`.`status` = ?) and (`authors`.`slug` in (?, ?) and (`tags`.`slug` = ? or `posts`.`feature_image` is not null)) group by `posts`.`id`, `posts`.`id` order by count(authors.id) DESC, CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC limit ?');
|
||||
queries[1].bindings.should.eql([
|
||||
false,
|
||||
'published',
|
||||
'leslie',
|
||||
'pat',
|
||||
'hash-audio',
|
||||
15
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('generates correct query for - filter: published_at:>\'2015-07-20\', limit of: 5, with related: tags', function () {
|
||||
const queries = [];
|
||||
tracker.install();
|
||||
|
||||
tracker.on('query', (query) => {
|
||||
queries.push(query);
|
||||
query.response([]);
|
||||
});
|
||||
|
||||
return models.Post.findPage({
|
||||
filter: 'published_at:>\'2015-07-20\'',
|
||||
limit: 5,
|
||||
withRelated: ['tags']
|
||||
}).then(() => {
|
||||
queries.length.should.eql(2);
|
||||
queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` where (`posts`.`page` = ? and `posts`.`status` = ?) and (`posts`.`published_at` > ?)');
|
||||
queries[0].bindings.should.eql([
|
||||
false,
|
||||
'published',
|
||||
'2015-07-20'
|
||||
]);
|
||||
|
||||
queries[1].sql.should.eql('select `posts`.* from `posts` where (`posts`.`page` = ? and `posts`.`status` = ?) and (`posts`.`published_at` > ?) order by CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC limit ?');
|
||||
queries[1].bindings.should.eql([
|
||||
false,
|
||||
'published',
|
||||
'2015-07-20',
|
||||
5
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('primary_tag/primary_author', function () {
|
||||
it('generates correct query for - filter: primary_tag:photo, with related: tags', function () {
|
||||
const queries = [];
|
||||
tracker.install();
|
||||
|
||||
tracker.on('query', (query) => {
|
||||
queries.push(query);
|
||||
query.response([]);
|
||||
});
|
||||
|
||||
return models.Post.findPage({
|
||||
filter: 'primary_tag:photo',
|
||||
withRelated: ['tags']
|
||||
}).then(() => {
|
||||
queries.length.should.eql(2);
|
||||
queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` left outer join `posts_tags` on `posts_tags`.`post_id` = `posts`.`id` left outer join `tags` on `posts_tags`.`tag_id` = `tags`.`id` where (`posts`.`page` = ? and `posts`.`status` = ?) and ((`tags`.`slug` = ? and `posts_tags`.`sort_order` = ? and `tags`.`visibility` = ?))');
|
||||
queries[0].bindings.should.eql([
|
||||
false,
|
||||
'published',
|
||||
'photo',
|
||||
0,
|
||||
'public'
|
||||
]);
|
||||
|
||||
queries[1].sql.should.eql('select `posts`.* from `posts` left outer join `posts_tags` on `posts_tags`.`post_id` = `posts`.`id` left outer join `tags` on `posts_tags`.`tag_id` = `tags`.`id` where (`posts`.`page` = ? and `posts`.`status` = ?) and ((`tags`.`slug` = ? and `posts_tags`.`sort_order` = ? and `tags`.`visibility` = ?)) group by `posts`.`id` order by CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC limit ?');
|
||||
queries[1].bindings.should.eql([
|
||||
false,
|
||||
'published',
|
||||
'photo',
|
||||
0,
|
||||
'public',
|
||||
15
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('generates correct query for - filter: primary_author:leslie, with related: authors', function () {
|
||||
const queries = [];
|
||||
tracker.install();
|
||||
|
||||
tracker.on('query', (query) => {
|
||||
queries.push(query);
|
||||
query.response([]);
|
||||
});
|
||||
|
||||
return models.Post.findPage({
|
||||
filter: 'primary_author:leslie',
|
||||
withRelated: ['authors']
|
||||
}).then(() => {
|
||||
queries.length.should.eql(2);
|
||||
queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` left outer join `posts_authors` on `posts_authors`.`post_id` = `posts`.`id` left outer join `users` as `authors` on `posts_authors`.`author_id` = `authors`.`id` where (`posts`.`page` = ? and `posts`.`status` = ?) and ((`authors`.`slug` = ? and `posts_authors`.`sort_order` = ? and `authors`.`visibility` = ?))');
|
||||
queries[0].bindings.should.eql([
|
||||
false,
|
||||
'published',
|
||||
'leslie',
|
||||
0,
|
||||
'public'
|
||||
]);
|
||||
|
||||
queries[1].sql.should.eql('select `posts`.* from `posts` left outer join `posts_authors` on `posts_authors`.`post_id` = `posts`.`id` left outer join `users` as `authors` on `posts_authors`.`author_id` = `authors`.`id` where (`posts`.`page` = ? and `posts`.`status` = ?) and ((`authors`.`slug` = ? and `posts_authors`.`sort_order` = ? and `authors`.`visibility` = ?)) group by `posts`.`id` order by CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC limit ?');
|
||||
queries[1].bindings.should.eql([
|
||||
false,
|
||||
'published',
|
||||
'leslie',
|
||||
0,
|
||||
'public',
|
||||
15
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('bad behavior', function () {
|
||||
it('generates correct query for - filter: status:[published,draft], limit of: all', function () {
|
||||
const queries = [];
|
||||
tracker.install();
|
||||
|
||||
tracker.on('query', (query) => {
|
||||
queries.push(query);
|
||||
query.response([]);
|
||||
});
|
||||
|
||||
return models.Post.findPage({
|
||||
filter: 'status:[published,draft]',
|
||||
limit: 'all',
|
||||
status: 'published',
|
||||
where: {
|
||||
statements: [{
|
||||
prop: 'status',
|
||||
op: '=',
|
||||
value: 'published'
|
||||
}]
|
||||
}
|
||||
}).then(() => {
|
||||
queries.length.should.eql(2);
|
||||
queries[0].sql.should.eql('select count(distinct posts.id) as aggregate from `posts` where (`posts`.`page` = ?) and (`posts`.`status` in (?, ?) and `posts`.`status` = ?)');
|
||||
queries[0].bindings.should.eql([
|
||||
false,
|
||||
'published',
|
||||
'draft',
|
||||
'published'
|
||||
]);
|
||||
|
||||
queries[1].sql.should.eql('select `posts`.* from `posts` where (`posts`.`page` = ?) and (`posts`.`status` in (?, ?) and `posts`.`status` = ?) order by CASE WHEN posts.status = \'scheduled\' THEN 1 WHEN posts.status = \'draft\' THEN 2 ELSE 3 END ASC,CASE WHEN posts.status != \'draft\' THEN posts.published_at END DESC,posts.updated_at DESC,posts.id DESC');
|
||||
queries[1].bindings.should.eql([
|
||||
false,
|
||||
'published',
|
||||
'draft',
|
||||
'published'
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Unit: models/post: uses database (@TODO: fix me)', function () {
|
||||
before(function () {
|
||||
models.init();
|
||||
});
|
||||
@ -34,6 +276,95 @@ describe('Unit: models/post', function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('processOptions', function () {
|
||||
it('generates correct where statement when filter contains unpermitted values', function () {
|
||||
const options = {
|
||||
filter: 'status:[published,draft]',
|
||||
limit: 'all',
|
||||
status: 'published'
|
||||
};
|
||||
|
||||
models.Post.processOptions(options);
|
||||
|
||||
options.where.statements.should.be.an.Array().with.lengthOf(1);
|
||||
options.where.statements[0].should.deepEqual({
|
||||
prop: 'status',
|
||||
op: '=',
|
||||
value: 'published'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('enforcedFilters', function () {
|
||||
const enforcedFilters = function enforcedFilters(model, options) {
|
||||
return new models.Post(model).enforcedFilters(options);
|
||||
};
|
||||
|
||||
it('returns published status filter for public context', function () {
|
||||
const options = {
|
||||
context: {
|
||||
public: true
|
||||
}
|
||||
};
|
||||
|
||||
const filter = enforcedFilters({}, options);
|
||||
|
||||
filter.should.equal('status:published');
|
||||
});
|
||||
|
||||
it('returns no status filter for non public context', function () {
|
||||
const options = {
|
||||
context: {
|
||||
internal: true
|
||||
}
|
||||
};
|
||||
|
||||
const filter = enforcedFilters({}, options);
|
||||
|
||||
should(filter).equal(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('defaultFilters', function () {
|
||||
const defaultFilters = function defaultFilters(model, options) {
|
||||
return new models.Post(model).defaultFilters(options);
|
||||
};
|
||||
|
||||
it('returns no default filter for internal context', function () {
|
||||
const options = {
|
||||
context: {
|
||||
internal: true
|
||||
}
|
||||
};
|
||||
|
||||
const filter = defaultFilters({}, options);
|
||||
|
||||
should(filter).equal(null);
|
||||
});
|
||||
|
||||
it('returns page:false filter for public context', function () {
|
||||
const options = {
|
||||
context: {
|
||||
public: true
|
||||
}
|
||||
};
|
||||
|
||||
const filter = defaultFilters({}, options);
|
||||
|
||||
filter.should.equal('page:false');
|
||||
});
|
||||
|
||||
it('returns page:false+status:published filter for non public context', function () {
|
||||
const options = {
|
||||
context: 'user'
|
||||
};
|
||||
|
||||
const filter = defaultFilters({}, options);
|
||||
|
||||
filter.should.equal('page:false+status:published');
|
||||
});
|
||||
});
|
||||
|
||||
describe('add', function () {
|
||||
describe('ensure full set of data for model events', function () {
|
||||
it('default', function () {
|
||||
@ -269,6 +600,47 @@ describe('Unit: models/post', function () {
|
||||
});
|
||||
|
||||
describe('edit', function () {
|
||||
it('ensure `forUpdate` works', function (done) {
|
||||
const originalFn = models.Post.prototype.onSaving;
|
||||
let requestCanComeIn = false;
|
||||
let postId = testUtils.DataGenerator.forKnex.posts[4].id;
|
||||
|
||||
testUtils.DataGenerator.forKnex.posts[4].featured.should.eql(true);
|
||||
|
||||
// @NOTE: simulate that the onSaving hook takes longer
|
||||
sandbox.stub(models.Post.prototype, 'onSaving').callsFake(function () {
|
||||
var self = this,
|
||||
args = arguments;
|
||||
|
||||
models.Post.prototype.onSaving.restore();
|
||||
requestCanComeIn = true;
|
||||
return Promise.delay(2000)
|
||||
.then(function () {
|
||||
return originalFn.apply(self, args);
|
||||
});
|
||||
});
|
||||
|
||||
const interval = setInterval(function () {
|
||||
if (requestCanComeIn) {
|
||||
clearInterval(interval);
|
||||
|
||||
// @NOTE: second call, should wait till the delay finished
|
||||
models.Post.edit({title: 'Berlin'}, {id: postId, context: {internal: true}})
|
||||
.then(function (post) {
|
||||
post.id.should.eql(postId);
|
||||
post.get('title').should.eql('Berlin');
|
||||
post.get('status').should.eql('published');
|
||||
post.get('featured').should.be.false();
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
}
|
||||
}, 10);
|
||||
|
||||
// @NOTE: first call to db locks the row (!)
|
||||
models.Post.edit({title: 'First', featured: false, status: 'published'}, _.merge({id: postId, migrating: true}, testUtils.context.editor));
|
||||
});
|
||||
|
||||
it('update post with options.migrating', function () {
|
||||
const events = {
|
||||
post: [],
|
||||
|
@ -1,23 +1,71 @@
|
||||
var should = require('should'),
|
||||
url = require('url'),
|
||||
sinon = require('sinon'),
|
||||
models = require('../../../server/models'),
|
||||
testUtils = require('../../utils'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
const should = require('should');
|
||||
const url = require('url');
|
||||
const sinon = require('sinon');
|
||||
const models = require('../../../server/models');
|
||||
const testUtils = require('../../utils');
|
||||
const {knex} = require('../../../server/data/db');
|
||||
|
||||
describe('Unit: models/tags', function () {
|
||||
const sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Unit: models/tag', function () {
|
||||
before(function () {
|
||||
models.init();
|
||||
});
|
||||
|
||||
after(function () {
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
before(testUtils.teardown);
|
||||
before(testUtils.setup('tags'));
|
||||
describe('SQL', function () {
|
||||
const mockDb = require('mock-knex');
|
||||
let tracker;
|
||||
|
||||
before(function () {
|
||||
mockDb.mock(knex);
|
||||
tracker = mockDb.getTracker();
|
||||
});
|
||||
|
||||
after(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
after(function () {
|
||||
mockDb.unmock(knex);
|
||||
});
|
||||
|
||||
it('generates correct query for - filter: count.posts:>=1, order: count.posts DESC, limit of: all, withRelated: count.posts', function () {
|
||||
const queries = [];
|
||||
tracker.install();
|
||||
|
||||
tracker.on('query', (query) => {
|
||||
queries.push(query);
|
||||
query.response([]);
|
||||
});
|
||||
|
||||
return models.Tag.findPage({
|
||||
filter: 'count.posts:>=1',
|
||||
order: 'count.posts DESC',
|
||||
limit: 'all',
|
||||
withRelated: ['count.posts']
|
||||
}).then(() => {
|
||||
queries.length.should.eql(2);
|
||||
queries[0].sql.should.eql('select count(distinct tags.id) as aggregate from `tags` where `count`.`posts` >= ?');
|
||||
queries[0].bindings.should.eql([
|
||||
1
|
||||
]);
|
||||
|
||||
queries[1].sql.should.eql('select `tags`.*, (select count(`posts`.`id`) from `posts` left outer join `posts_tags` on `posts`.`id` = `posts_tags`.`post_id` where posts_tags.tag_id = tags.id) as `count__posts` from `tags` where `count`.`posts` >= ? order by `count__posts` DESC');
|
||||
queries[1].bindings.should.eql([
|
||||
1
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edit', function () {
|
||||
before(testUtils.teardown);
|
||||
before(testUtils.setup('tags'));
|
||||
|
||||
it('resets given empty value to null', function () {
|
||||
return models.Tag.findOne({slug: 'kitchen-sink'})
|
||||
.then(function (tag) {
|
||||
|
@ -1,9 +1,11 @@
|
||||
const should = require('should'),
|
||||
url = require('url'),
|
||||
sinon = require('sinon'),
|
||||
Promise = require('bluebird'),
|
||||
_ = require('lodash'),
|
||||
schema = require('../../../server/data/schema'),
|
||||
models = require('../../../server/models'),
|
||||
permissions = require('../../../server/services/permissions'),
|
||||
validation = require('../../../server/data/validation'),
|
||||
common = require('../../../server/lib/common'),
|
||||
security = require('../../../server/lib/security'),
|
||||
@ -149,15 +151,16 @@ describe('Unit: models/user', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('fn: permissible', function () {
|
||||
function getUserModel(id, role) {
|
||||
describe('permissible', function () {
|
||||
function getUserModel(id, role, roleId) {
|
||||
var hasRole = sandbox.stub();
|
||||
|
||||
hasRole.withArgs(role).returns(true);
|
||||
|
||||
return {
|
||||
id: id,
|
||||
hasRole: hasRole,
|
||||
related: sandbox.stub().returns([{name: role}]),
|
||||
related: sandbox.stub().returns([{name: role, id: roleId}]),
|
||||
get: sandbox.stub().returns(id)
|
||||
};
|
||||
}
|
||||
@ -184,6 +187,134 @@ describe('Unit: models/user', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('cannot edit my status to inactive', function () {
|
||||
var mockUser = getUserModel(3, 'Editor'),
|
||||
context = {user: 3};
|
||||
|
||||
return models.User.permissible(mockUser, 'edit', context, {status: 'inactive'}, testUtils.permissions.editor, false, true)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
err.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
});
|
||||
});
|
||||
|
||||
it('without related roles', function () {
|
||||
sandbox.stub(models.User, 'findOne').withArgs({
|
||||
id: 3,
|
||||
status: 'all'
|
||||
}, {withRelated: ['roles']}).resolves(getUserModel(3, 'Contributor'));
|
||||
|
||||
const mockUser = {id: 3, related: sandbox.stub().returns()};
|
||||
const context = {user: 3};
|
||||
|
||||
return models.User.permissible(mockUser, 'edit', context, {}, testUtils.permissions.contributor, false, true)
|
||||
.then(() => {
|
||||
models.User.findOne.calledOnce.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('change role', function () {
|
||||
function getUserToEdit(id, role) {
|
||||
var hasRole = sandbox.stub();
|
||||
|
||||
hasRole.withArgs(role).returns(true);
|
||||
|
||||
return {
|
||||
id: id,
|
||||
hasRole: hasRole,
|
||||
related: sandbox.stub().returns([role]),
|
||||
get: sandbox.stub().returns(id)
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox.stub(models.User, 'getOwnerUser');
|
||||
sandbox.stub(permissions, 'canThis');
|
||||
|
||||
models.User.getOwnerUser.resolves({
|
||||
id: testUtils.context.owner.context.user,
|
||||
related: () => {
|
||||
return {
|
||||
at: () => {
|
||||
return testUtils.permissions.owner.user.roles[0].id;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('cannot change own role', function () {
|
||||
const mockUser = getUserToEdit(testUtils.context.admin.context.user, testUtils.permissions.editor.user.roles[0]);
|
||||
const context = testUtils.context.admin.context;
|
||||
const unsafeAttrs = testUtils.permissions.editor.user;
|
||||
|
||||
return models.User.permissible(mockUser, 'edit', context, unsafeAttrs, testUtils.permissions.admin, false, true)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
err.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
});
|
||||
});
|
||||
|
||||
it('is owner and does not change the role', function () {
|
||||
const mockUser = getUserToEdit(testUtils.context.owner.context.user, testUtils.permissions.owner.user.roles[0]);
|
||||
const context = testUtils.context.owner.context;
|
||||
const unsafeAttrs = testUtils.permissions.owner.user;
|
||||
|
||||
return models.User.permissible(mockUser, 'edit', context, unsafeAttrs, testUtils.permissions.owner, false, true)
|
||||
.then(() => {
|
||||
models.User.getOwnerUser.calledOnce.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('cannot change owner\'s role', function () {
|
||||
const mockUser = getUserToEdit(testUtils.context.owner.context.user, testUtils.permissions.owner.user.roles[0]);
|
||||
const context = testUtils.context.admin.context;
|
||||
const unsafeAttrs = testUtils.permissions.editor.user;
|
||||
|
||||
return models.User.permissible(mockUser, 'edit', context, unsafeAttrs, testUtils.permissions.admin, false, true)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
err.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
});
|
||||
});
|
||||
|
||||
it('admin can change author role', function () {
|
||||
const mockUser = getUserToEdit(testUtils.context.author.context.user, testUtils.permissions.author.user.roles[0]);
|
||||
const context = testUtils.context.admin.context;
|
||||
const unsafeAttrs = testUtils.permissions.editor.user;
|
||||
|
||||
permissions.canThis.returns({
|
||||
assign: {
|
||||
role: sandbox.stub().resolves()
|
||||
}
|
||||
});
|
||||
|
||||
return models.User.permissible(mockUser, 'edit', context, unsafeAttrs, testUtils.permissions.admin, true, true)
|
||||
.then(() => {
|
||||
models.User.getOwnerUser.calledOnce.should.be.true();
|
||||
permissions.canThis.calledOnce.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('author can\'t change admin role', function () {
|
||||
const mockUser = getUserToEdit(testUtils.context.admin.context.user, testUtils.permissions.admin.user.roles[0]);
|
||||
const context = testUtils.context.author.context;
|
||||
const unsafeAttrs = testUtils.permissions.editor.user;
|
||||
|
||||
permissions.canThis.returns({
|
||||
assign: {
|
||||
role: sandbox.stub().resolves()
|
||||
}
|
||||
});
|
||||
|
||||
return models.User.permissible(mockUser, 'edit', context, unsafeAttrs, testUtils.permissions.author, false, true)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
err.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('as editor', function () {
|
||||
it('can\'t edit another editor', function (done) {
|
||||
var mockUser = getUserModel(3, 'Editor'),
|
||||
@ -199,6 +330,20 @@ describe('Unit: models/user', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t edit owner', function (done) {
|
||||
var mockUser = getUserModel(3, 'Owner'),
|
||||
context = {user: 2};
|
||||
|
||||
models.User.permissible(mockUser, 'edit', context, {}, testUtils.permissions.editor, true, true).then(() => {
|
||||
done(new Error('Permissible function should have errored'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockUser.hasRole.called).be.true();
|
||||
should(mockUser.get.calledOnce).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t edit an admin', function (done) {
|
||||
var mockUser = getUserModel(3, 'Administrator'),
|
||||
context = {user: 2};
|
||||
@ -374,4 +519,87 @@ describe('Unit: models/user', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('transferOwnership', function () {
|
||||
let ownerRole;
|
||||
|
||||
beforeEach(function () {
|
||||
ownerRole = sandbox.stub();
|
||||
|
||||
sandbox.stub(models.Role, 'findOne');
|
||||
|
||||
models.Role.findOne
|
||||
.withArgs({name: 'Owner'})
|
||||
.resolves(testUtils.permissions.owner.user.roles[0]);
|
||||
|
||||
models.Role.findOne
|
||||
.withArgs({name: 'Administrator'})
|
||||
.resolves(testUtils.permissions.admin.user.roles[0]);
|
||||
|
||||
sandbox.stub(models.User, 'findOne');
|
||||
});
|
||||
|
||||
it('Cannot transfer ownership if not owner', function () {
|
||||
const loggedInUser = testUtils.context.admin;
|
||||
const userToChange = loggedInUser;
|
||||
const contextUser = sandbox.stub();
|
||||
|
||||
contextUser.toJSON = sandbox.stub().returns(testUtils.permissions.admin.user);
|
||||
|
||||
models.User
|
||||
.findOne
|
||||
.withArgs({id: loggedInUser.context.user}, {withRelated: ['roles']})
|
||||
.resolves(contextUser);
|
||||
|
||||
return models.User.transferOwnership({id: loggedInUser.context.user}, loggedInUser)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
err.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
});
|
||||
});
|
||||
|
||||
it('Owner tries to transfer ownership to author', function () {
|
||||
const loggedInUser = testUtils.context.owner;
|
||||
const userToChange = testUtils.context.editor;
|
||||
const contextUser = sandbox.stub();
|
||||
|
||||
contextUser.toJSON = sandbox.stub().returns(testUtils.permissions.owner.user);
|
||||
|
||||
models.User
|
||||
.findOne
|
||||
.withArgs({id: loggedInUser.context.user}, {withRelated: ['roles']})
|
||||
.resolves(contextUser);
|
||||
|
||||
models.User
|
||||
.findOne
|
||||
.withArgs({id: userToChange.context.user}, {withRelated: ['roles']})
|
||||
.resolves(contextUser);
|
||||
|
||||
return models.User.transferOwnership({id: userToChange.context.user}, loggedInUser)
|
||||
.then(Promise.reject)
|
||||
.catch((err) => {
|
||||
err.should.be.an.instanceof(common.errors.ValidationError);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isSetup', function () {
|
||||
it('active', function () {
|
||||
sandbox.stub(models.User, 'getOwnerUser').resolves({get: sandbox.stub().returns('active')});
|
||||
|
||||
return models.User.isSetup()
|
||||
.then((result) => {
|
||||
result.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('inactive', function () {
|
||||
sandbox.stub(models.User, 'getOwnerUser').resolves({get: sandbox.stub().returns('inactive')});
|
||||
|
||||
return models.User.isSetup()
|
||||
.then((result) => {
|
||||
result.should.be.false();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
28
core/test/unit/models/webhook_spec.js
Normal file
28
core/test/unit/models/webhook_spec.js
Normal file
@ -0,0 +1,28 @@
|
||||
const should = require('should');
|
||||
const url = require('url');
|
||||
const sinon = require('sinon');
|
||||
const models = require('../../../server/models');
|
||||
const testUtils = require('../../utils');
|
||||
const {knex} = require('../../../server/data/db');
|
||||
|
||||
const sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('Unit: models/webhooks', function () {
|
||||
before(function () {
|
||||
models.init();
|
||||
});
|
||||
after(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
before(testUtils.teardown);
|
||||
before(testUtils.setup('webhooks'));
|
||||
|
||||
it('can correctly use getByEventAndTarget', function () {
|
||||
return models.Webhook.getByEventAndTarget('subscriber.added', 'https://example.com/webhooks/subscriber-added')
|
||||
.then(function (webhook) {
|
||||
webhook.get('event').should.eql('subscriber.added');
|
||||
webhook.get('target_url').should.eql('https://example.com/webhooks/subscriber-added');
|
||||
});
|
||||
});
|
||||
});
|
@ -55,7 +55,8 @@ var _ = require('lodash'),
|
||||
notification: ['type', 'message', 'status', 'id', 'dismissible', 'location', 'custom'],
|
||||
theme: ['name', 'package', 'active'],
|
||||
themes: ['themes'],
|
||||
invites: _(schema.invites).keys().without('token').value(),
|
||||
invites: ['invites', 'meta'],
|
||||
invite: _(schema.invites).keys().without('token').value(),
|
||||
webhook: _.keys(schema.webhooks)
|
||||
};
|
||||
|
||||
|
@ -303,6 +303,16 @@ fixtures = {
|
||||
});
|
||||
},
|
||||
|
||||
createInactiveUser() {
|
||||
const user = DataGenerator.forKnex.createUser({
|
||||
email: 'inactive@test.org',
|
||||
slug: 'inactive',
|
||||
status: 'inactive'
|
||||
});
|
||||
|
||||
return models.User.add(user, module.exports.context.internal);
|
||||
},
|
||||
|
||||
createExtraUsers: function createExtraUsers() {
|
||||
// grab 3 more users
|
||||
var extraUsers = _.cloneDeep(DataGenerator.Content.users.slice(2, 6));
|
||||
@ -596,6 +606,9 @@ toDoList = {
|
||||
'users:no-owner': function createUsersWithoutOwner() {
|
||||
return fixtures.createUsersWithoutOwner();
|
||||
},
|
||||
'user:inactive': function createInactiveUser() {
|
||||
return fixtures.createInactiveUser();
|
||||
},
|
||||
'users:extra': function createExtraUsers() {
|
||||
return fixtures.createExtraUsers();
|
||||
},
|
||||
@ -906,7 +919,33 @@ startGhost = function startGhost(options) {
|
||||
web.shared.middlewares.customRedirects.reload();
|
||||
|
||||
common.events.emit('server.start');
|
||||
return ghostServer;
|
||||
|
||||
/**
|
||||
* @TODO: this is dirty, but makes routing testing a lot easier for now, because the routing test
|
||||
* has no easy way to access existing resource id's, which are added from the Ghost fixtures.
|
||||
* I can do `testUtils.existingData.roles[0].id`.
|
||||
*/
|
||||
module.exports.existingData = {};
|
||||
return models.Role.findAll({columns: ['id']})
|
||||
.then((roles) => {
|
||||
module.exports.existingData.roles = roles.toJSON();
|
||||
|
||||
return models.Client.findAll({columns: ['id', 'secret']});
|
||||
})
|
||||
.then((clients) => {
|
||||
module.exports.existingData.clients = clients.toJSON();
|
||||
|
||||
return models.User.findAll({columns: ['id']});
|
||||
})
|
||||
.then((users) => {
|
||||
module.exports.existingData.users = users.toJSON();
|
||||
|
||||
return models.Tag.findAll({columns: ['id']});
|
||||
})
|
||||
.then((tags) => {
|
||||
module.exports.existingData.tags = tags.toJSON();
|
||||
})
|
||||
.return(ghostServer);
|
||||
});
|
||||
}
|
||||
|
||||
@ -917,6 +956,8 @@ startGhost = function startGhost(options) {
|
||||
}
|
||||
})
|
||||
.then(function initialiseDatabase() {
|
||||
settingsCache.shutdown();
|
||||
settingsCache.reset();
|
||||
return knexMigrator.init();
|
||||
})
|
||||
.then(function initializeGhost() {
|
||||
@ -953,7 +994,32 @@ startGhost = function startGhost(options) {
|
||||
});
|
||||
})
|
||||
.then(function returnGhost() {
|
||||
return ghostServer;
|
||||
/**
|
||||
* @TODO: this is dirty, but makes routing testing a lot easier for now, because the routing test
|
||||
* has no easy way to access existing resource id's, which are added from the Ghost fixtures.
|
||||
* I can do `testUtils.existingData.roles[0].id`.
|
||||
*/
|
||||
module.exports.existingData = {};
|
||||
return models.Role.findAll({columns: ['id']})
|
||||
.then((roles) => {
|
||||
module.exports.existingData.roles = roles.toJSON();
|
||||
|
||||
return models.Client.findAll({columns: ['id', 'secret']});
|
||||
})
|
||||
.then((clients) => {
|
||||
module.exports.existingData.clients = clients.toJSON();
|
||||
|
||||
return models.User.findAll({columns: ['id']});
|
||||
})
|
||||
.then((users) => {
|
||||
module.exports.existingData.users = users.toJSON();
|
||||
|
||||
return models.Tag.findAll({columns: ['id']});
|
||||
})
|
||||
.then((tags) => {
|
||||
module.exports.existingData.tags = tags.toJSON();
|
||||
})
|
||||
.return(ghostServer);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -128,6 +128,7 @@
|
||||
"matchdep": "2.0.0",
|
||||
"minimist": "1.2.0",
|
||||
"mocha": "4.1.0",
|
||||
"mock-knex": "0.4.2",
|
||||
"nock": "9.4.0",
|
||||
"proxyquire": "2.1.0",
|
||||
"rewire": "3.0.2",
|
||||
|
12
yarn.lock
12
yarn.lock
@ -3652,6 +3652,10 @@ lodash@4.17.10, lodash@^4.13.1, lodash@^4.16.4, lodash@^4.17.10, lodash@^4.17.4,
|
||||
version "4.17.10"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
|
||||
|
||||
lodash@^4.14.2:
|
||||
version "4.17.11"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
||||
|
||||
lodash@~0.9.2:
|
||||
version "0.9.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-0.9.2.tgz#8f3499c5245d346d682e5b0d3b40767e09f1a92c"
|
||||
@ -3996,6 +4000,14 @@ mocha@^3.1.2:
|
||||
mkdirp "0.5.1"
|
||||
supports-color "3.1.2"
|
||||
|
||||
mock-knex@0.4.2:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/mock-knex/-/mock-knex-0.4.2.tgz#6a7c1941908dd6b9761c24a071c392b56c20bb5f"
|
||||
dependencies:
|
||||
bluebird "^3.4.1"
|
||||
lodash "^4.14.2"
|
||||
semver "^5.3.0"
|
||||
|
||||
module-not-found-error@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/module-not-found-error/-/module-not-found-error-1.0.1.tgz#cf8b4ff4f29640674d6cdd02b0e3bc523c2bbdc0"
|
||||
|
Loading…
Reference in New Issue
Block a user