diff --git a/core/test/acceptance/old/admin/slugs_spec.js b/core/test/acceptance/old/admin/slugs_spec.js index 724b2835a7..843e5afe43 100644 --- a/core/test/acceptance/old/admin/slugs_spec.js +++ b/core/test/acceptance/old/admin/slugs_spec.js @@ -1,8 +1,8 @@ var should = require('should'), supertest = require('supertest'), - testUtils = require('../../../../utils'), - localUtils = require('../../../../acceptance/old/admin/utils'), - config = require('../../../../../server/config'), + testUtils = require('../../../utils'), + localUtils = require('../../../acceptance/old/admin/utils'), + config = require('../../../../server/config'), ghost = testUtils.startGhost, request; diff --git a/core/test/acceptance/old/content/authors_spec.js b/core/test/acceptance/old/content/authors_spec.js index ec5953306d..3c0cd086e4 100644 --- a/core/test/acceptance/old/content/authors_spec.js +++ b/core/test/acceptance/old/content/authors_spec.js @@ -10,7 +10,7 @@ const localUtils = require('./utils'); const ghost = testUtils.startGhost; let request; -describe('Authors Content API V2', function () { +describe('Authors Content API', function () { let ghostServer; before(function () { @@ -30,7 +30,7 @@ describe('Authors Content API V2', function () { const validKey = localUtils.getValidKey(); - it('browse authors', function (done) { + it('Can request authors', function (done) { request.get(localUtils.API.getApiQuery(`authors/?key=${validKey}`)) .set('Origin', testUtils.API.getURL()) .expect('Content-Type', /json/) @@ -67,69 +67,7 @@ describe('Authors Content API V2', function () { }); }); - it('browse authors: does not give back roles if trying to fetch roles', function (done) { - request.get(localUtils.API.getApiQuery(`authors/?key=${validKey}&include=roles`)) - .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); - } - - should.not.exist(res.body.authors[0].roles); - done(); - }); - }); - - it('browse user by slug: count.posts', function (done) { - request.get(localUtils.API.getApiQuery(`authors/slug/ghost/?key=${validKey}&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); - } - - should.not.exist(res.headers['x-cache-invalidate']); - var jsonResponse = res.body; - - should.exist(jsonResponse.authors); - jsonResponse.authors.should.have.length(1); - - // We don't expose the email address. - localUtils.API.checkResponse(jsonResponse.authors[0], 'author', ['count', 'url'], null, null); - done(); - }); - }); - - it('browse user by id: count.posts', function (done) { - request.get(localUtils.API.getApiQuery(`authors/1/?key=${validKey}&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); - } - - should.not.exist(res.headers['x-cache-invalidate']); - var jsonResponse = res.body; - - should.exist(jsonResponse.authors); - jsonResponse.authors.should.have.length(1); - - // We don't expose the email address. - localUtils.API.checkResponse(jsonResponse.authors[0], 'author', ['count', 'url'], null, null); - done(); - }); - }); - - it('browse user with count.posts', function (done) { + it('Can request authors including post count', function (done) { request.get(localUtils.API.getApiQuery(`authors/?key=${validKey}&include=count.posts&order=count.posts ASC`)) .set('Origin', testUtils.API.getURL()) .expect('Content-Type', /json/) @@ -166,8 +104,8 @@ describe('Authors Content API V2', function () { }); }); - it('browse authors: post count', function (done) { - request.get(localUtils.API.getApiQuery(`authors/?key=${validKey}&include=count.posts`)) + it('Can request single author', function (done) { + request.get(localUtils.API.getApiQuery(`authors/slug/ghost/?key=${validKey}`)) .set('Origin', testUtils.API.getURL()) .expect('Content-Type', /json/) .expect('Cache-Control', testUtils.cacheRules.private) @@ -179,9 +117,32 @@ describe('Authors Content API V2', function () { should.not.exist(res.headers['x-cache-invalidate']); var jsonResponse = res.body; + should.exist(jsonResponse.authors); - localUtils.API.checkResponse(jsonResponse, 'authors'); - jsonResponse.authors.should.have.length(3); + jsonResponse.authors.should.have.length(1); + + // We don't expose the email address. + localUtils.API.checkResponse(jsonResponse.authors[0], 'author', ['url'], null, null); + done(); + }); + }); + + it('Can request author by id including post count', function (done) { + request.get(localUtils.API.getApiQuery(`authors/1/?key=${validKey}&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); + } + + should.not.exist(res.headers['x-cache-invalidate']); + var jsonResponse = res.body; + + should.exist(jsonResponse.authors); + jsonResponse.authors.should.have.length(1); // We don't expose the email address. localUtils.API.checkResponse(jsonResponse.authors[0], 'author', ['count', 'url'], null, null); diff --git a/core/test/acceptance/old/content/key_authentication_spec.js b/core/test/acceptance/old/content/key_authentication_spec.js index 636ee55bb9..33dc662dae 100644 --- a/core/test/acceptance/old/content/key_authentication_spec.js +++ b/core/test/acceptance/old/content/key_authentication_spec.js @@ -7,12 +7,12 @@ const config = require('../../../../server/config'); const ghost = testUtils.startGhost; -describe('Content API V2 key authentication', function () { +describe('Content API key authentication', function () { let request; before(function () { return ghost() - .then(function (_ghostServer) { + .then(function () { request = supertest.agent(config.get('url')); }) .then(function () { @@ -20,14 +20,14 @@ describe('Content API V2 key authentication', function () { }); }); - it('forbid access without key', function () { + it('Can not access without key', function () { return request.get(localUtils.API.getApiQuery('posts/')) .expect('Content-Type', /json/) .expect('Cache-Control', testUtils.cacheRules.private) .expect(403); }); - it('browse with valid key', function () { + it('Can access with with valid key', function () { const key = localUtils.getValidKey(); return request.get(localUtils.API.getApiQuery(`posts/?key=${key}`)) diff --git a/core/test/acceptance/old/content/pages_spec.js b/core/test/acceptance/old/content/pages_spec.js index 62e64da1ca..fd1cafd44b 100644 --- a/core/test/acceptance/old/content/pages_spec.js +++ b/core/test/acceptance/old/content/pages_spec.js @@ -9,7 +9,7 @@ const config = require('../../../../server/config'); const ghost = testUtils.startGhost; let request; -describe('Pages', function () { +describe('Pages Content API', function () { before(function () { return ghost() .then(function () { @@ -24,7 +24,7 @@ describe('Pages', function () { configUtils.restore(); }); - it('browse pages', function () { + it('Can request pages', function () { const key = localUtils.getValidKey(); return request.get(localUtils.API.getApiQuery(`pages/?key=${key}`)) .set('Origin', testUtils.API.getURL()) @@ -49,27 +49,7 @@ describe('Pages', function () { }); }); - it('browse pages with page:false', function () { - const key = localUtils.getValidKey(); - return request.get(localUtils.API.getApiQuery(`pages/?key=${key}&filter=page:false`)) - .set('Origin', testUtils.API.getURL()) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(200) - .then((res) => { - res.headers.vary.should.eql('Accept-Encoding'); - should.exist(res.headers['access-control-allow-origin']); - should.not.exist(res.headers['x-cache-invalidate']); - - const jsonResponse = res.body; - should.exist(jsonResponse.pages); - should.exist(jsonResponse.meta); - - jsonResponse.pages.should.have.length(0); - }); - }); - - it('read page', function () { + it('Can request page', function () { const key = localUtils.getValidKey(); return request.get(localUtils.API.getApiQuery(`pages/${testUtils.DataGenerator.Content.posts[5].id}/?key=${key}`)) .set('Origin', testUtils.API.getURL()) @@ -92,15 +72,4 @@ describe('Pages', function () { should.exist(urlParts.host); }); }); - - it('can\'t read post', function () { - const key = localUtils.getValidKey(); - - return request - .get(localUtils.API.getApiQuery(`pages/${testUtils.DataGenerator.Content.posts[0].id}/?key=${key}`)) - .set('Origin', testUtils.API.getURL()) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404); - }); }); diff --git a/core/test/acceptance/old/content/posts_spec.js b/core/test/acceptance/old/content/posts_spec.js index b04f1c3805..533294f0f4 100644 --- a/core/test/acceptance/old/content/posts_spec.js +++ b/core/test/acceptance/old/content/posts_spec.js @@ -12,7 +12,7 @@ const config = require('../../../../server/config'); const ghost = testUtils.startGhost; let request; -describe('Posts', function () { +describe('Posts Content API', function () { before(function () { return ghost() .then(function () { @@ -29,7 +29,7 @@ describe('Posts', function () { const validKey = localUtils.getValidKey(); - it('browse posts', function (done) { + it('Can request posts', function (done) { request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}`)) .set('Origin', testUtils.API.getURL()) .expect('Content-Type', /json/) @@ -91,7 +91,7 @@ describe('Posts', function () { }); }); - it('browse posts with basic filters', function (done) { + it('Can filter posts by tag', function (done) { request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&filter=tag:kitchen-sink,featured:true&include=tags`)) .expect('Content-Type', /json/) .expect('Cache-Control', testUtils.cacheRules.private) @@ -140,50 +140,7 @@ describe('Posts', function () { }); }); - it('browse posts with basic page filter should not return pages', function (done) { - request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&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); - localUtils.API.checkResponse(jsonResponse, 'posts'); - localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination'); - jsonResponse.posts.should.have.length(0); - - done(); - }); - }); - - it('browse posts with basic page filter should not return pages', function (done) { - request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&filter=page:true,featured: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); - localUtils.API.checkResponse(jsonResponse, 'posts'); - localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination'); - jsonResponse.posts.should.have.length(2); - jsonResponse.posts.filter(p => (p.page === true)).should.have.length(0); - - done(); - }); - }); - - it('browse posts with author filter', function (done) { + it('Can filter posts by authors', function (done) { request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&filter=authors:[joe-bloggs,pat,ghost,slimer-mcectoplasm]&include=authors`)) .expect('Content-Type', /json/) .expect('Cache-Control', testUtils.cacheRules.private) @@ -220,50 +177,7 @@ describe('Posts', function () { }); }); - it('browse posts with published and draft status, should not return drafts', function (done) { - request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&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); - - done(); - }); - }); - - it('[deprecated] browse posts with page non matching filter', function (done) { - request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&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 only url fields', function (done) { + it('Can request fields of posts', function (done) { request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&fields=url`)) .set('Origin', testUtils.API.getURL()) .expect('Content-Type', /json/) @@ -283,28 +197,7 @@ describe('Posts', function () { }); }); - it('browse posts: request only url fields with include', function (done) { - request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&fields=url&include=tags`)) - .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.posts); - - localUtils.API.checkResponse(jsonResponse.posts[0], 'post', false, false, ['url','tags']); - jsonResponse.posts[0].url.should.eql('http://127.0.0.1:2369/welcome/'); - jsonResponse.posts[0].tags[0].url.should.eql('http://127.0.0.1:2369/tag/getting-started/'); - done(); - }); - }); - - it('browse posts: request to include tags and authors should always contain absolute urls', function (done) { + it('Can include relations', function (done) { request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&include=tags,authors`)) .set('Origin', testUtils.API.getURL()) .expect('Content-Type', /json/) @@ -344,7 +237,7 @@ describe('Posts', function () { }); }); - it('browse posts from different origin', function (done) { + it('Can request posts from different origin', function (done) { request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}`)) .set('Origin', 'https://example.com') .expect('Content-Type', /json/) @@ -370,73 +263,7 @@ describe('Posts', function () { }); }); - it('ensure origin header on redirect is not getting lost', function (done) { - // NOTE: force a redirect to the admin url - configUtils.set('admin:url', 'http://localhost:9999'); - - request.get(localUtils.API.getApiQuery(`posts?key=${validKey}`)) - .set('Origin', 'https://example.com') - // 301 Redirects _should_ be cached - .expect('Cache-Control', testUtils.cacheRules.year) - .expect(301) - .end(function (err, res) { - if (err) { - return done(err); - } - - res.headers.vary.should.eql('Accept, Accept-Encoding'); - res.headers.location.should.eql(`http://localhost:9999/ghost/api/v2/content/posts/?key=${validKey}`); - should.exist(res.headers['access-control-allow-origin']); - should.not.exist(res.headers['x-cache-invalidate']); - done(); - }); - }); - - it('browse posts, ignores staticPages', function (done) { - request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&staticPages=true`)) - .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); - } - - should.not.exist(res.headers['x-cache-invalidate']); - var jsonResponse = res.body; - should.exist(jsonResponse.posts); - localUtils.API.checkResponse(jsonResponse, 'posts'); - jsonResponse.posts.should.have.length(11); - localUtils.API.checkResponse(jsonResponse.posts[0], 'post'); - localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination'); - _.isBoolean(jsonResponse.posts[0].featured).should.eql(true); - done(); - }); - }); - - it('denies access with invalid key', function (done) { - request.get(localUtils.API.getApiQuery('posts/?key=invalid-key')) - .set('Origin', testUtils.API.getURL()) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(401) - .end(function (err, res) { - if (err) { - return done(err); - } - - should.not.exist(res.headers['x-cache-invalidate']); - var jsonResponse = res.body; - should.exist(jsonResponse); - should.exist(jsonResponse.errors); - testUtils.API.checkResponseValue(jsonResponse.errors[0], ['message', 'errorType']); - done(); - }); - }); - - it('fetch the most recent post, then the prev, then the next should match the first', function (done) { + it('Can filter by published date', function (done) { function createFilter(publishedAt, op) { // This line deliberately uses double quotes because GQL cannot handle either double quotes // or escaped singles, see TryGhost/GQL#34 @@ -488,7 +315,7 @@ describe('Posts', function () { .catch(done); }); - it('read posts', function () { + it('Can request a single post', function () { return request .get(localUtils.API.getApiQuery(`posts/${testUtils.DataGenerator.Content.posts[0].id}/?key=${validKey}`)) .set('Origin', testUtils.API.getURL()) @@ -504,13 +331,4 @@ describe('Posts', function () { localUtils.API.checkResponse(jsonResponse.posts[0], 'post'); }); }); - - it('can\'t read page', function () { - return request - .get(localUtils.API.getApiQuery(`posts/${testUtils.DataGenerator.Content.posts[5].id}/?key=${validKey}`)) - .set('Origin', testUtils.API.getURL()) - .expect('Content-Type', /json/) - .expect('Cache-Control', testUtils.cacheRules.private) - .expect(404); - }); }); diff --git a/core/test/acceptance/old/content/settings_spec.js b/core/test/acceptance/old/content/settings_spec.js index dae61f9a47..4fe341375b 100644 --- a/core/test/acceptance/old/content/settings_spec.js +++ b/core/test/acceptance/old/content/settings_spec.js @@ -12,7 +12,7 @@ const defaultSettings = require('../../../../server/data/schema').defaultSetting const ghost = testUtils.startGhost; let request; -describe('Settings', function () { +describe('Settings Content API', function () { before(function () { return ghost() .then(function () { @@ -22,7 +22,7 @@ describe('Settings', function () { }); }); - it('browse settings', function () { + it('Can request settings', function () { const key = localUtils.getValidKey(); return request.get(localUtils.API.getApiQuery(`settings/?key=${key}`)) .set('Origin', testUtils.API.getURL()) diff --git a/core/test/acceptance/old/content/tags_spec.js b/core/test/acceptance/old/content/tags_spec.js index 9e5e13802d..9b75f6e587 100644 --- a/core/test/acceptance/old/content/tags_spec.js +++ b/core/test/acceptance/old/content/tags_spec.js @@ -8,7 +8,7 @@ const testUtils = require('../../../utils'); const localUtils = require('./utils'); const ghost = testUtils.startGhost; -describe('Tags Content API V2', function () { +describe('Tags Content API', function () { let request; before(function () { @@ -27,7 +27,7 @@ describe('Tags Content API V2', function () { const validKey = localUtils.getValidKey(); - it('browse tags without limit defaults to 15', function (done) { + it('Can request tags', function (done) { request.get(localUtils.API.getApiQuery(`tags/?key=${validKey}`)) .set('Origin', testUtils.API.getURL()) .expect('Content-Type', /json/) @@ -65,7 +65,7 @@ describe('Tags Content API V2', function () { }); }); - it('browse tags - limit=all should fetch all tags', function (done) { + it('Can request tags with limit=all', function (done) { request.get(localUtils.API.getApiQuery(`tags/?limit=all&key=${validKey}`)) .set('Origin', testUtils.API.getURL()) .expect('Content-Type', /json/) @@ -86,7 +86,7 @@ describe('Tags Content API V2', function () { }); }); - it('browse tags without limit=4 fetches 4 tags', function (done) { + it('Can limit tags to receive', function (done) { request.get(localUtils.API.getApiQuery(`tags/?limit=3&key=${validKey}`)) .set('Origin', testUtils.API.getURL()) .expect('Content-Type', /json/) @@ -108,8 +108,8 @@ describe('Tags Content API V2', function () { }); }); - it('browse tags - limit=all should fetch all tags and include count.posts', function (done) { - request.get(localUtils.API.getApiQuery(`tags/?limit=all&key=${validKey}&include=count.posts`)) + it('Can include post count', function (done) { + request.get(localUtils.API.getApiQuery(`tags/?key=${validKey}&include=count.posts`)) .set('Origin', testUtils.API.getURL()) .expect('Content-Type', /json/) .expect('Cache-Control', testUtils.cacheRules.private) diff --git a/core/test/regression/api/v2/admin/db_spec.js b/core/test/regression/api/v2/admin/db_spec.js index ff2ce217b7..741b25bb56 100644 --- a/core/test/regression/api/v2/admin/db_spec.js +++ b/core/test/regression/api/v2/admin/db_spec.js @@ -89,7 +89,7 @@ describe('DB API', () => { .expect('Content-Type', /json/) .expect(200) .then((res) => { - (typeof res.body).should.be.Object(); + res.body.should.be.Object(); res.body.db[0].filename.should.match(/test\.json/); fsStub.calledOnce.should.eql(true); }); diff --git a/core/test/regression/api/v2/content/pages_spec.js b/core/test/regression/api/v2/content/pages_spec.js new file mode 100644 index 0000000000..0c1fb6916a --- /dev/null +++ b/core/test/regression/api/v2/content/pages_spec.js @@ -0,0 +1,57 @@ +const url = require('url'); +const should = require('should'); +const supertest = require('supertest'); +const testUtils = require('../../../../utils'); +const localUtils = require('./utils'); +const configUtils = require('../../../../utils/configUtils'); +const config = require('../../../../../server/config'); + +const ghost = testUtils.startGhost; +let request; + +describe('Pages', function () { + before(function () { + return ghost() + .then(function () { + request = supertest.agent(config.get('url')); + }) + .then(function () { + return testUtils.initFixtures('users:no-owner', 'user:inactive', 'posts', 'tags:extra', 'api_keys'); + }); + }); + + afterEach(function () { + configUtils.restore(); + }); + + it('browse pages with page:false', function () { + const key = localUtils.getValidKey(); + return request.get(localUtils.API.getApiQuery(`pages/?key=${key}&filter=page:false`)) + .set('Origin', testUtils.API.getURL()) + .expect('Content-Type', /json/) + .expect('Cache-Control', testUtils.cacheRules.private) + .expect(200) + .then((res) => { + res.headers.vary.should.eql('Accept-Encoding'); + should.exist(res.headers['access-control-allow-origin']); + should.not.exist(res.headers['x-cache-invalidate']); + + const jsonResponse = res.body; + should.exist(jsonResponse.pages); + should.exist(jsonResponse.meta); + + jsonResponse.pages.should.have.length(0); + }); + }); + + it('can\'t read post', function () { + const key = localUtils.getValidKey(); + + return request + .get(localUtils.API.getApiQuery(`pages/${testUtils.DataGenerator.Content.posts[0].id}/?key=${key}`)) + .set('Origin', testUtils.API.getURL()) + .expect('Content-Type', /json/) + .expect('Cache-Control', testUtils.cacheRules.private) + .expect(404); + }); +}); diff --git a/core/test/regression/api/v2/content/posts_spec.js b/core/test/regression/api/v2/content/posts_spec.js new file mode 100644 index 0000000000..20c6adbb4e --- /dev/null +++ b/core/test/regression/api/v2/content/posts_spec.js @@ -0,0 +1,145 @@ +const should = require('should'); +const supertest = require('supertest'); +const _ = require('lodash'); +const url = require('url'); +const cheerio = require('cheerio'); +const moment = require('moment'); +const testUtils = require('../../../../utils'); +const localUtils = require('./utils'); +const configUtils = require('../../../../utils/configUtils'); +const config = require('../../../../../server/config'); + +const ghost = testUtils.startGhost; +let request; + +describe('Posts', function () { + before(function () { + return ghost() + .then(function () { + request = supertest.agent(config.get('url')); + }) + .then(function () { + return testUtils.initFixtures('users:no-owner', 'user:inactive', 'posts', 'tags:extra', 'api_keys'); + }); + }); + + afterEach(function () { + configUtils.restore(); + }); + + const validKey = localUtils.getValidKey(); + + it('browse posts with basic page filter should not return pages', function (done) { + request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&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); + localUtils.API.checkResponse(jsonResponse, 'posts'); + localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination'); + jsonResponse.posts.should.have.length(0); + + done(); + }); + }); + + it('browse posts with basic page filter should not return pages', function (done) { + request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&filter=page:true,featured: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); + localUtils.API.checkResponse(jsonResponse, 'posts'); + localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination'); + jsonResponse.posts.should.have.length(2); + jsonResponse.posts.filter(p => (p.page === true)).should.have.length(0); + + done(); + }); + }); + + it('browse posts with published and draft status, should not return drafts', function (done) { + request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&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); + + done(); + }); + }); + + it('ensure origin header on redirect is not getting lost', function (done) { + // NOTE: force a redirect to the admin url + configUtils.set('admin:url', 'http://localhost:9999'); + + request.get(localUtils.API.getApiQuery(`posts?key=${validKey}`)) + .set('Origin', 'https://example.com') + // 301 Redirects _should_ be cached + .expect('Cache-Control', testUtils.cacheRules.year) + .expect(301) + .end(function (err, res) { + if (err) { + return done(err); + } + + res.headers.vary.should.eql('Accept, Accept-Encoding'); + res.headers.location.should.eql(`http://localhost:9999/ghost/api/v2/content/posts/?key=${validKey}`); + should.exist(res.headers['access-control-allow-origin']); + should.not.exist(res.headers['x-cache-invalidate']); + done(); + }); + }); + + it('browse posts, ignores staticPages', function (done) { + request.get(localUtils.API.getApiQuery(`posts/?key=${validKey}&staticPages=true`)) + .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); + } + + should.not.exist(res.headers['x-cache-invalidate']); + var jsonResponse = res.body; + should.exist(jsonResponse.posts); + localUtils.API.checkResponse(jsonResponse, 'posts'); + jsonResponse.posts.should.have.length(11); + localUtils.API.checkResponse(jsonResponse.posts[0], 'post'); + localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination'); + _.isBoolean(jsonResponse.posts[0].featured).should.eql(true); + done(); + }); + }); + + it('can\'t read page', function () { + return request + .get(localUtils.API.getApiQuery(`posts/${testUtils.DataGenerator.Content.posts[5].id}/?key=${validKey}`)) + .set('Origin', testUtils.API.getURL()) + .expect('Content-Type', /json/) + .expect('Cache-Control', testUtils.cacheRules.private) + .expect(404); + }); +}); diff --git a/core/test/regression/api/v2/content/utils.js b/core/test/regression/api/v2/content/utils.js new file mode 100644 index 0000000000..6da3f963f8 --- /dev/null +++ b/core/test/regression/api/v2/content/utils.js @@ -0,0 +1,87 @@ +const url = require('url'); +const _ = require('lodash'); +const testUtils = require('../../../../utils'); +const schema = require('../../../../../server/data/schema').tables; +const API_URL = '/ghost/api/v2/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, primary_tag, primary_author + .concat('url', 'primary_tag', 'primary_author') + // v2 API doesn't return unused fields + .without('locale', 'visibility') + // These fields aren't useful as they always have known values + .without('status') + // @TODO: https://github.com/TryGhost/Ghost/issues/10335 + // .without('page') + // v2 returns a calculated excerpt field + .concat('excerpt') + , + author: _(schema.users) + .keys() + .without( + 'password', + 'email', + 'ghost_auth_access_token', + 'ghost_auth_id', + '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; + } +};