Ghost/ghost/core/test/e2e-api/shared/version.test.js
Daniel Lockyer 3d989eba23 Converted Ghost repo into a monorepo
refs https://github.com/TryGhost/Toolbox/issues/354

- this commit turns the Ghost repo into a monorepo so we can bring our
  internal packages back in, which makes life easier when working on
  Ghost
2022-07-20 16:41:05 +02:00

346 lines
13 KiB
JavaScript

const {agentProvider, fixtureManager, matchers, mockManager} = require('../../utils/e2e-framework');
const {anyErrorId, stringMatching, anyObjectId, anyLocationFor, anyISODateTime, anyEtag, anyString, anyContentLength, anyContentVersion} = matchers;
const settingsMatcher = {
settings: {
version: anyString
}
};
describe('API Versioning', function () {
describe('Admin API', function () {
let agentAdminAPI;
before(async function () {
agentAdminAPI = await agentProvider.getAdminAPIAgent();
await fixtureManager.init();
await agentAdminAPI.loginAsOwner();
});
beforeEach(function () {
mockManager.mockMail();
});
afterEach(function () {
mockManager.restore();
});
it('responds with no content version header when accept version header is NOT PRESENT', async function () {
await agentAdminAPI
.get('site/')
.expectStatus(200)
.matchBodySnapshot({
site: {
version: stringMatching(/\d+\.\d+/)
}
})
.matchHeaderSnapshot({
'content-length': anyContentLength,
etag: anyEtag
});
});
it('responds with current content version header when requested version is BEHIND current version and CAN respond', async function () {
await agentAdminAPI
.get('site/')
.header('Accept-Version', 'v3.0')
.expectStatus(200)
.matchBodySnapshot({
site: {
version: stringMatching(/\d+\.\d+/)
}
})
.matchHeaderSnapshot({
etag: anyEtag,
'content-version': anyContentVersion,
'content-length': anyContentLength
});
});
it('responds with current content version header when requested version is AHEAD and CAN respond', async function () {
await agentAdminAPI
.get('site/')
.header('Accept-Version', 'v999.5')
.expectStatus(200)
.matchBodySnapshot({
site: {
version: stringMatching(/\d+\.\d+/)
}
})
.matchHeaderSnapshot({
etag: anyEtag,
'content-version': anyContentVersion,
'content-length': anyContentLength
});
});
it('allows invalid accept-version header', async function () {
await agentAdminAPI
.get('site/')
.header('Accept-Version', 'canary')
.expectStatus(200)
.matchBodySnapshot({
site: {
version: stringMatching(/\d+\.\d+/)
}
})
.matchHeaderSnapshot({
etag: anyEtag,
'content-version': anyContentVersion,
'content-length': anyContentLength
});
});
it('responds with error requested version is AHEAD and CANNOT respond', async function () {
// CASE 2: If accept-version is behind, send a 406 & tell them the client needs updating.
await agentAdminAPI
.get('removed_endpoint')
.header('Accept-Version', 'v999.1')
.expectStatus(406)
.matchHeaderSnapshot({
etag: anyEtag,
'content-version': anyContentVersion,
'content-length': anyContentLength
})
.matchBodySnapshot({
errors: [{
context: stringMatching(/Provided client accept-version v999\.1 is ahead of current Ghost version v\d+\.\d+/),
id: anyErrorId
}]
});
});
it('responds with error when requested version is BEHIND and CANNOT respond', async function () {
// CASE 2: If accept-version is behind, send a 406 & tell them the client needs updating.
await agentAdminAPI
.get('removed_endpoint')
.header('Accept-Version', 'v3.1')
.header('User-Agent', 'Zapier 1.3')
.expectStatus(406)
.matchHeaderSnapshot({
etag: anyEtag,
'content-version': anyContentVersion,
'content-length': anyContentLength
})
.matchBodySnapshot({
errors: [{
context: stringMatching(/Provided client accept-version v3.1 is behind current Ghost version v\d+\.\d+/),
id: anyErrorId
}]
});
mockManager.assert.sentEmailCount(1);
mockManager.assert.sentEmail({
subject: 'Attention required: Your Zapier integration has failed',
to: 'jbloggs@example.com'
});
});
it('responds with error and sends email ONCE when requested version is BEHIND and CANNOT respond multiple times', async function () {
await agentAdminAPI
.get('removed_endpoint')
.header('Accept-Version', 'v3.5')
.header('User-Agent', 'Zapier 1.4')
.expectStatus(406)
.matchHeaderSnapshot({
etag: anyEtag,
'content-version': anyContentVersion,
'content-length': anyContentLength
})
.matchBodySnapshot({
errors: [{
context: stringMatching(/Provided client accept-version v3.5 is behind current Ghost version v\d+\.\d+/),
id: anyErrorId
}]
});
mockManager.assert.sentEmailCount(1);
mockManager.assert.sentEmail({
subject: 'Attention required: Your Zapier integration has failed',
to: 'jbloggs@example.com'
});
await agentAdminAPI
.get('removed_endpoint')
.header('Accept-Version', 'v3.5')
.header('User-Agent', 'Zapier 1.4')
.expectStatus(406)
.matchHeaderSnapshot({
etag: anyEtag,
'content-version': anyContentVersion,
'content-length': anyContentLength
})
.matchBodySnapshot({
errors: [{
context: stringMatching(/Provided client accept-version v3.5 is behind current Ghost version v\d+\.\d+/),
id: anyErrorId
}]
});
mockManager.assert.sentEmailCount(1);
});
it('responds with 404 error when the resource cannot be found', async function () {
await agentAdminAPI
.get('/members/member_does_not_exist@example.com')
.header('Accept-Version', 'v4.1')
.expectStatus(404)
.matchHeaderSnapshot({
etag: anyEtag,
'content-version': anyContentVersion,
'content-length': anyContentLength
})
.matchBodySnapshot({
errors: [{
id: anyErrorId
}]
});
mockManager.assert.sentEmailCount(0);
});
it('Does an internal rewrite for canary URLs with accept version set', async function () {
await agentAdminAPI
.get('/site/', {baseUrl: '/ghost/api/canary/admin/'})
.expectStatus(200)
.matchHeaderSnapshot({
etag: anyEtag,
'content-version': anyContentVersion,
'content-length': anyContentLength
})
.matchBodySnapshot({site: {
version: stringMatching(/\d+\.\d+/)
}});
});
it('Does an internal rewrite for v3 URL + POST with accept version set', async function () {
await agentAdminAPI
.post('/tags/', {baseUrl: '/ghost/api/v3/admin/'})
.body({
tags: [{name: 'version tag'}]
})
.expectStatus(201)
.matchHeaderSnapshot({
etag: anyEtag,
location: anyLocationFor('tags'),
'content-version': anyContentVersion,
'content-length': anyContentLength
})
.matchBodySnapshot({
tags: [{
id: anyObjectId,
created_at: anyISODateTime,
updated_at: anyISODateTime
}]
});
});
it('responds with 406 for an unknown version with accept-version set ahead', async function () {
await agentAdminAPI
.get('/site/', {baseUrl: '/ghost/api/v99/admin/'})
.header('Accept-Version', 'v99.0')
.expectStatus(406)
.matchHeaderSnapshot({
etag: anyEtag,
'content-version': anyContentVersion,
'content-length': anyContentLength
})
.matchBodySnapshot({errors: [{
context: stringMatching(/Provided client accept-version v99\.0 is ahead of current Ghost version v\d+\.\d+/),
id: anyErrorId
}]});
});
it('responds with 406 for an unknown version with accept-version set behind', async function () {
await agentAdminAPI
.get('/site/', {baseUrl: '/ghost/api/v1/admin/'})
.header('Accept-Version', 'v1.0')
.expectStatus(406)
.matchHeaderSnapshot({
etag: anyEtag,
'content-version': anyContentVersion,
'content-length': anyContentLength
})
.matchBodySnapshot({errors: [{
context: stringMatching(/Provided client accept-version v1\.0 is behind current Ghost version v\d+\.\d+/),
id: anyErrorId
}]});
});
});
describe('Content API', function () {
let agentContentAPI;
beforeEach(function () {
mockManager.mockMail();
});
afterEach(function () {
mockManager.restore();
});
before(async function () {
agentContentAPI = await agentProvider.getContentAPIAgent();
await fixtureManager.init('api_keys');
agentContentAPI.authenticate();
});
it('responds with no content version header when accept version header is NOT PRESENT', async function () {
await agentContentAPI.get('settings/')
.expectStatus(200)
.matchHeaderSnapshot({
etag: anyEtag,
'content-length': anyContentLength
})
.matchBodySnapshot(settingsMatcher);
});
it('responds with current content version header when requested version is BEHIND current version and CAN respond', async function () {
await agentContentAPI.get('settings/')
.header('Accept-Version', 'v3.0')
.expectStatus(200)
.matchHeaderSnapshot({
etag: anyEtag,
'content-version': anyContentVersion,
'content-length': anyContentLength
})
.matchBodySnapshot(settingsMatcher);
});
it('Does an internal rewrite with accept version set when version is included in the URL', async function () {
await agentContentAPI
.get('/tags/?limit=1', {baseUrl: '/ghost/api/canary/content/'})
.expectStatus(200)
.matchHeaderSnapshot({
etag: anyEtag,
'content-version': anyContentVersion,
'content-length': anyContentLength
})
.matchBodySnapshot({
tags: [{
id: anyObjectId
}]
});
});
it('responds with 404 error when the resource cannot be found', async function () {
await agentContentAPI
.get('/posts/5ddc9141c35e7700383b2937/')
.header('Accept-Version', 'v5.0')
.expectStatus(404)
.matchHeaderSnapshot({
etag: anyEtag,
'content-version': anyContentVersion,
'content-length': anyContentLength
})
.matchBodySnapshot({
errors: [{
id: anyErrorId
}]
});
mockManager.assert.sentEmailCount(0);
});
});
});