mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-03 16:38:22 +03:00
Added acceptance tests on the v3 api version.
refs https://github.com/TryGhost/Team/issues/221
This commit is contained in:
parent
aae58ba4e4
commit
01126cf1bb
133
test/api-acceptance/admin/actions_spec_v3.js
Normal file
133
test/api-acceptance/admin/actions_spec_v3.js
Normal file
@ -0,0 +1,133 @@
|
||||
const should = require('should');
|
||||
const Promise = require('bluebird');
|
||||
const supertest = require('supertest');
|
||||
const testUtils = require('../../utils');
|
||||
const localUtils = require('./utils');
|
||||
const config = require('../../../core/shared/config');
|
||||
|
||||
describe('Actions API', function () {
|
||||
let request;
|
||||
|
||||
before(async function () {
|
||||
await testUtils.startGhost();
|
||||
request = supertest.agent(config.get('url'));
|
||||
await localUtils.doAuth(request, 'integrations', 'api_keys');
|
||||
});
|
||||
|
||||
// @NOTE: This test runs a little slower, because we store Dates without milliseconds.
|
||||
it('Can request actions for resource', async function () {
|
||||
let postUpdatedAt;
|
||||
|
||||
const res = await request
|
||||
.post(localUtils.API.getApiQuery('posts/'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
posts: [{
|
||||
title: 'test post'
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(201);
|
||||
|
||||
const postId = res.body.posts[0].id;
|
||||
postUpdatedAt = res.body.posts[0].updated_at;
|
||||
|
||||
const res2 = await request
|
||||
.get(localUtils.API.getApiQuery(`actions/?filter=resource_id:${postId}&include=actor`))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200);
|
||||
|
||||
localUtils.API.checkResponse(res2.body, 'actions');
|
||||
localUtils.API.checkResponse(res2.body.actions[0], 'action');
|
||||
|
||||
res2.body.actions.length.should.eql(1);
|
||||
|
||||
res2.body.actions[0].resource_type.should.eql('post');
|
||||
res2.body.actions[0].actor_type.should.eql('user');
|
||||
res2.body.actions[0].event.should.eql('added');
|
||||
Object.keys(res2.body.actions[0].actor).length.should.eql(4);
|
||||
res2.body.actions[0].actor.id.should.eql(testUtils.DataGenerator.Content.users[0].id);
|
||||
res2.body.actions[0].actor.image.should.eql(testUtils.DataGenerator.Content.users[0].profile_image);
|
||||
res2.body.actions[0].actor.name.should.eql(testUtils.DataGenerator.Content.users[0].name);
|
||||
res2.body.actions[0].actor.slug.should.eql(testUtils.DataGenerator.Content.users[0].slug);
|
||||
|
||||
await Promise.delay(1000);
|
||||
|
||||
const res3 = await request
|
||||
.put(localUtils.API.getApiQuery(`posts/${postId}/`))
|
||||
.set('Origin', config.get('url'))
|
||||
.send({
|
||||
posts: [{
|
||||
slug: 'new-slug',
|
||||
updated_at: postUpdatedAt
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200);
|
||||
|
||||
postUpdatedAt = res3.body.posts[0].updated_at;
|
||||
|
||||
const res4 = await request
|
||||
.get(localUtils.API.getApiQuery(`actions/?filter=resource_id:${postId}&include=actor`))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200);
|
||||
|
||||
localUtils.API.checkResponse(res4.body, 'actions');
|
||||
localUtils.API.checkResponse(res4.body.actions[0], 'action');
|
||||
|
||||
res4.body.actions.length.should.eql(2);
|
||||
|
||||
res4.body.actions[0].resource_type.should.eql('post');
|
||||
res4.body.actions[0].actor_type.should.eql('user');
|
||||
res4.body.actions[0].event.should.eql('edited');
|
||||
Object.keys(res4.body.actions[0].actor).length.should.eql(4);
|
||||
res4.body.actions[0].actor.id.should.eql(testUtils.DataGenerator.Content.users[0].id);
|
||||
res4.body.actions[0].actor.image.should.eql(testUtils.DataGenerator.Content.users[0].profile_image);
|
||||
res4.body.actions[0].actor.name.should.eql(testUtils.DataGenerator.Content.users[0].name);
|
||||
res4.body.actions[0].actor.slug.should.eql(testUtils.DataGenerator.Content.users[0].slug);
|
||||
|
||||
await Promise.delay(1000);
|
||||
|
||||
const integrationRequest = supertest.agent(config.get('url'));
|
||||
await integrationRequest
|
||||
.put(localUtils.API.getApiQuery(`posts/${postId}/`))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Authorization', `Ghost ${localUtils.getValidAdminToken('/v3/admin/')}`)
|
||||
.send({
|
||||
posts: [{
|
||||
featured: true,
|
||||
updated_at: postUpdatedAt
|
||||
}]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200);
|
||||
|
||||
const res5 = await request
|
||||
.get(localUtils.API.getApiQuery(`actions/?filter=resource_id:${postId}&include=actor`))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200);
|
||||
|
||||
localUtils.API.checkResponse(res5.body, 'actions');
|
||||
localUtils.API.checkResponse(res5.body.actions[0], 'action');
|
||||
|
||||
res5.body.actions.length.should.eql(3);
|
||||
|
||||
res5.body.actions[0].resource_type.should.eql('post');
|
||||
res5.body.actions[0].actor_type.should.eql('integration');
|
||||
res5.body.actions[0].event.should.eql('edited');
|
||||
Object.keys(res5.body.actions[0].actor).length.should.eql(4);
|
||||
res5.body.actions[0].actor.id.should.eql(testUtils.DataGenerator.Content.integrations[0].id);
|
||||
should.equal(res5.body.actions[0].actor.image, null);
|
||||
res5.body.actions[0].actor.name.should.eql(testUtils.DataGenerator.Content.integrations[0].name);
|
||||
res5.body.actions[0].actor.slug.should.eql(testUtils.DataGenerator.Content.integrations[0].slug);
|
||||
});
|
||||
});
|
71
test/api-acceptance/admin/key_authentication_spec_v3.js
Normal file
71
test/api-acceptance/admin/key_authentication_spec_v3.js
Normal file
@ -0,0 +1,71 @@
|
||||
const should = require('should');
|
||||
const supertest = require('supertest');
|
||||
const testUtils = require('../../utils');
|
||||
const config = require('../../../core/shared/config');
|
||||
const localUtils = require('./utils');
|
||||
|
||||
describe('Admin API key authentication', function () {
|
||||
let request;
|
||||
|
||||
before(async function () {
|
||||
await testUtils.startGhost();
|
||||
request = supertest.agent(config.get('url'));
|
||||
await testUtils.initFixtures('api_keys');
|
||||
});
|
||||
|
||||
it('Can not access endpoint without a token header', async function () {
|
||||
await request.get(localUtils.API.getApiQuery('posts/'))
|
||||
.set('Authorization', `Ghost`)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(401);
|
||||
});
|
||||
|
||||
it('Can not access endpoint with a wrong endpoint token', async function () {
|
||||
await request.get(localUtils.API.getApiQuery('posts/'))
|
||||
.set('Authorization', `Ghost ${localUtils.getValidAdminToken('https://wrong.com')}`)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(401);
|
||||
});
|
||||
|
||||
it('Can access browse endpoint with correct token', async function () {
|
||||
await request.get(localUtils.API.getApiQuery('posts/'))
|
||||
.set('Authorization', `Ghost ${localUtils.getValidAdminToken('/v3/admin/')}`)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it('Can create post', async function () {
|
||||
const post = {
|
||||
title: 'Post created with api_key'
|
||||
};
|
||||
|
||||
const res = await request
|
||||
.post(localUtils.API.getApiQuery('posts/?include=authors'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Authorization', `Ghost ${localUtils.getValidAdminToken('/v3/admin/')}`)
|
||||
.send({
|
||||
posts: [post]
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(201);
|
||||
|
||||
// falls back to owner user
|
||||
res.body.posts[0].authors.length.should.eql(1);
|
||||
});
|
||||
|
||||
it('Can read users', async function () {
|
||||
const res = await request
|
||||
.get(localUtils.API.getApiQuery('users/'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Authorization', `Ghost ${localUtils.getValidAdminToken('/v3/admin/')}`)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200);
|
||||
|
||||
localUtils.API.checkResponse(res.body.users[0], 'user');
|
||||
});
|
||||
});
|
47
test/api-acceptance/admin/labels_spec_v3.js
Normal file
47
test/api-acceptance/admin/labels_spec_v3.js
Normal file
@ -0,0 +1,47 @@
|
||||
const path = require('path');
|
||||
const should = require('should');
|
||||
const supertest = require('supertest');
|
||||
const sinon = require('sinon');
|
||||
const testUtils = require('../../utils');
|
||||
const localUtils = require('../../regression/api/v3/admin/utils');
|
||||
const config = require('../../../core/shared/config');
|
||||
|
||||
describe('Labels API', function () {
|
||||
let request;
|
||||
|
||||
after(function () {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
before(async function () {
|
||||
await testUtils.startGhost();
|
||||
request = supertest.agent(config.get('url'));
|
||||
await localUtils.doAuth(request);
|
||||
});
|
||||
|
||||
it('Can add', async function () {
|
||||
const label = {
|
||||
name: 'test'
|
||||
};
|
||||
|
||||
const res = await request
|
||||
.post(localUtils.API.getApiQuery(`labels/`))
|
||||
.send({labels: [label]})
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(201);
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.labels);
|
||||
|
||||
jsonResponse.labels.should.have.length(1);
|
||||
jsonResponse.labels[0].name.should.equal(label.name);
|
||||
jsonResponse.labels[0].slug.should.equal(label.name);
|
||||
|
||||
should.exist(res.headers.location);
|
||||
res.headers.location.should.equal(`http://127.0.0.1:2369${localUtils.API.getApiQuery('labels/')}${res.body.labels[0].id}/`);
|
||||
});
|
||||
});
|
172
test/api-acceptance/admin/utils_v3.js
Normal file
172
test/api-acceptance/admin/utils_v3.js
Normal file
@ -0,0 +1,172 @@
|
||||
const url = require('url');
|
||||
const _ = require('lodash');
|
||||
const testUtils = require('../../utils');
|
||||
const schema = require('../../../core/server/data/schema').tables;
|
||||
const API_URL = '/ghost/api/v3/admin/';
|
||||
|
||||
const expectedProperties = {
|
||||
// API top level
|
||||
posts: ['posts', 'meta'],
|
||||
pages: ['pages', 'meta'],
|
||||
tags: ['tags', 'meta'],
|
||||
users: ['users', 'meta'],
|
||||
settings: ['settings', 'meta'],
|
||||
subscribers: ['subscribers', 'meta'],
|
||||
roles: ['roles'],
|
||||
pagination: ['page', 'limit', 'pages', 'total', 'next', 'prev'],
|
||||
slugs: ['slugs'],
|
||||
slug: ['slug'],
|
||||
invites: ['invites', 'meta'],
|
||||
themes: ['themes'],
|
||||
actions: ['actions', 'meta'],
|
||||
members: ['members', 'meta'],
|
||||
snippets: ['snippets', 'meta'],
|
||||
|
||||
action: ['id', 'resource_type', 'actor_type', 'event', 'created_at', 'actor'],
|
||||
|
||||
config: ['version', 'environment', 'database', 'mail', 'labs', 'clientExtensions', 'enableDeveloperExperiments', 'useGravatar', 'stripeDirect', 'emailAnalytics'],
|
||||
|
||||
post: _(schema.posts)
|
||||
.keys()
|
||||
// by default we only return mobildoc
|
||||
.without('html', 'plaintext')
|
||||
.without('locale')
|
||||
.without('page')
|
||||
// v2 API doesn't return new type field
|
||||
.without('type')
|
||||
// deprecated
|
||||
.without('author_id', 'author')
|
||||
// always returns computed properties
|
||||
.concat('url', 'primary_tag', 'primary_author', 'excerpt')
|
||||
// returned by default
|
||||
.concat('tags', 'authors', 'email')
|
||||
// returns meta fields from `posts_meta` schema
|
||||
.concat(
|
||||
..._(schema.posts_meta).keys().without('post_id', 'id')
|
||||
)
|
||||
.concat('send_email_when_published')
|
||||
,
|
||||
|
||||
page: _(schema.posts)
|
||||
.keys()
|
||||
// by default we only return mobildoc
|
||||
.without('html', 'plaintext')
|
||||
.without('locale')
|
||||
.without('page')
|
||||
// v2 API doesn't return new type field
|
||||
.without('type')
|
||||
// deprecated
|
||||
.without('author_id', 'author')
|
||||
// pages are not sent as emails
|
||||
.without('email_recipient_filter')
|
||||
// always returns computed properties
|
||||
.concat('url', 'primary_tag', 'primary_author', 'excerpt')
|
||||
// returned by default
|
||||
.concat('tags', 'authors')
|
||||
// returns meta fields from `posts_meta` schema
|
||||
.concat(
|
||||
..._(schema.posts_meta).keys()
|
||||
.without('post_id', 'id')
|
||||
// pages are not sent as emails
|
||||
.without('email_subject')
|
||||
)
|
||||
,
|
||||
|
||||
user: _(schema.users)
|
||||
.keys()
|
||||
.without('visibility')
|
||||
.without('password')
|
||||
.without('locale')
|
||||
.concat('url')
|
||||
,
|
||||
tag: _(schema.tags)
|
||||
.keys()
|
||||
// unused field
|
||||
.without('parent_id')
|
||||
,
|
||||
setting: _(schema.settings)
|
||||
.keys()
|
||||
,
|
||||
subscriber: _(schema.subscribers)
|
||||
.keys()
|
||||
,
|
||||
member: _(schema.members)
|
||||
.keys()
|
||||
.concat('avatar_image')
|
||||
.concat('comped')
|
||||
.concat('labels')
|
||||
,
|
||||
member_signin_url: ['member_id', 'url'],
|
||||
role: _(schema.roles)
|
||||
.keys()
|
||||
,
|
||||
permission: _(schema.permissions)
|
||||
.keys()
|
||||
,
|
||||
notification: ['type', 'message', 'status', 'id', 'dismissible', 'location', 'custom'],
|
||||
theme: ['name', 'package', 'active'],
|
||||
invite: _(schema.invites)
|
||||
.keys()
|
||||
.without('token')
|
||||
,
|
||||
webhook: _(schema.webhooks)
|
||||
.keys()
|
||||
,
|
||||
email: _(schema.emails)
|
||||
.keys(),
|
||||
email_preview: ['html', 'subject', 'plaintext'],
|
||||
email_recipient: _(schema.email_recipients)
|
||||
.keys()
|
||||
.filter(key => key.indexOf('@@') === -1),
|
||||
snippet: _(schema.snippets).keys()
|
||||
};
|
||||
|
||||
_.each(expectedProperties, (value, key) => {
|
||||
if (!value.__wrapped__) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated: x_by
|
||||
*/
|
||||
expectedProperties[key] = value
|
||||
.without(
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'published_by'
|
||||
)
|
||||
.value();
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
API: {
|
||||
getApiQuery(route) {
|
||||
return url.resolve(API_URL, route);
|
||||
},
|
||||
|
||||
checkResponse(...args) {
|
||||
this.expectedProperties = expectedProperties;
|
||||
return testUtils.API.checkResponse.call(this, ...args);
|
||||
}
|
||||
},
|
||||
|
||||
doAuth(...args) {
|
||||
return testUtils.API.doAuth(`${API_URL}session/`, ...args);
|
||||
},
|
||||
|
||||
getValidAdminToken(audience) {
|
||||
const jwt = require('jsonwebtoken');
|
||||
const JWT_OPTIONS = {
|
||||
keyid: testUtils.DataGenerator.Content.api_keys[0].id,
|
||||
algorithm: 'HS256',
|
||||
expiresIn: '5m',
|
||||
audience: audience
|
||||
};
|
||||
|
||||
return jwt.sign(
|
||||
{},
|
||||
Buffer.from(testUtils.DataGenerator.Content.api_keys[0].secret, 'hex'),
|
||||
JWT_OPTIONS
|
||||
);
|
||||
}
|
||||
};
|
95
test/api-acceptance/content/utils_v3.js
Normal file
95
test/api-acceptance/content/utils_v3.js
Normal file
@ -0,0 +1,95 @@
|
||||
const url = require('url');
|
||||
const _ = require('lodash');
|
||||
const testUtils = require('../../utils');
|
||||
const schema = require('../../../core/server/data/schema').tables;
|
||||
const API_URL = '/ghost/api/v3/content/';
|
||||
|
||||
const expectedProperties = {
|
||||
// API top level
|
||||
posts: ['posts', 'meta'],
|
||||
tags: ['tags', 'meta'],
|
||||
authors: ['authors', 'meta'],
|
||||
pagination: ['page', 'limit', 'pages', 'total', 'next', 'prev'],
|
||||
|
||||
post: _(schema.posts)
|
||||
.keys()
|
||||
// by default we only return html
|
||||
.without('mobiledoc', 'plaintext')
|
||||
// v2 doesn't return author_id OR author
|
||||
.without('author_id', 'author')
|
||||
// and always returns computed properties: url
|
||||
.concat('url')
|
||||
// v2 API doesn't return unused fields
|
||||
.without('locale')
|
||||
// These fields aren't useful as they always have known values
|
||||
.without('status')
|
||||
// v2 API doesn't return new type field
|
||||
.without('type')
|
||||
// @TODO: https://github.com/TryGhost/Ghost/issues/10335
|
||||
// .without('page')
|
||||
// v2 returns a calculated excerpt field
|
||||
.concat('excerpt')
|
||||
// Access is a calculated property in >= v3
|
||||
.concat('access')
|
||||
// returns meta fields from `posts_meta` schema
|
||||
.concat(
|
||||
..._(schema.posts_meta).keys().without('post_id', 'id')
|
||||
)
|
||||
.concat('reading_time')
|
||||
.concat('send_email_when_published')
|
||||
,
|
||||
author: _(schema.users)
|
||||
.keys()
|
||||
.without(
|
||||
'password',
|
||||
'email',
|
||||
'created_at',
|
||||
'created_by',
|
||||
'updated_at',
|
||||
'updated_by',
|
||||
'last_seen',
|
||||
'status'
|
||||
)
|
||||
// v2 API doesn't return unused fields
|
||||
.without('accessibility', 'locale', 'tour', 'visibility')
|
||||
,
|
||||
tag: _(schema.tags)
|
||||
.keys()
|
||||
// v2 Tag API doesn't return parent_id or parent
|
||||
.without('parent_id', 'parent')
|
||||
// v2 Tag API doesn't return date fields
|
||||
.without('created_at', 'updated_at')
|
||||
};
|
||||
|
||||
_.each(expectedProperties, (value, key) => {
|
||||
if (!value.__wrapped__) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated: x_by
|
||||
*/
|
||||
expectedProperties[key] = value
|
||||
.without(
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'published_by'
|
||||
)
|
||||
.value();
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
API: {
|
||||
getApiQuery(route) {
|
||||
return url.resolve(API_URL, route);
|
||||
},
|
||||
|
||||
checkResponse(...args) {
|
||||
this.expectedProperties = expectedProperties;
|
||||
return testUtils.API.checkResponse.call(this, ...args);
|
||||
}
|
||||
},
|
||||
getValidKey() {
|
||||
return testUtils.DataGenerator.Content.api_keys[1].secret;
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user