Ghost/ghost/api-framework/test/headers.test.js
Fabien O'Carroll a177600b30 Supported setting headers on a per-request basis
refs https://linear.app/tryghost/issue/ENG-674

This paves the way for us to have dynamic cache invalidation headers
without clobbering the shared headers config.
2024-02-28 21:31:04 +07:00

242 lines
8.3 KiB
JavaScript

const shared = require('../');
const Frame = require('../lib/Frame');
describe('Headers', function () {
it('empty headers config', function () {
return shared.headers.get({}, {}, new Frame()).then((result) => {
result.should.eql({});
});
});
describe('config.disposition', function () {
it('json', function () {
return shared.headers.get({}, {disposition: {type: 'json', value: 'value'}}, new Frame())
.then((result) => {
result.should.eql({
'Content-Disposition': 'Attachment; filename="value"',
'Content-Type': 'application/json',
'Content-Length': 2
});
});
});
it('csv', function () {
return shared.headers.get({}, {disposition: {type: 'csv', value: 'my.csv'}}, new Frame())
.then((result) => {
result.should.eql({
'Content-Disposition': 'Attachment; filename="my.csv"',
'Content-Type': 'text/csv'
});
});
});
it('csv with function', async function () {
const result = await shared.headers.get({}, {
disposition: {
type: 'csv',
value() {
// pretend we're doing some dynamic filename logic in this function
const filename = `awesome-data-2022-08-01.csv`;
return filename;
}
}
}, new Frame());
result.should.eql({
'Content-Disposition': 'Attachment; filename="awesome-data-2022-08-01.csv"',
'Content-Type': 'text/csv'
});
});
it('file', async function () {
const result = await shared.headers.get({}, {disposition: {type: 'file', value: 'my.txt'}}, new Frame());
result.should.eql({
'Content-Disposition': 'Attachment; filename="my.txt"'
});
});
it('file with function', async function () {
const result = await shared.headers.get({}, {
disposition: {
type: 'file',
value() {
// pretend we're doing some dynamic filename logic in this function
const filename = `awesome-data-2022-08-01.txt`;
return filename;
}
}
}, new Frame());
result.should.eql({
'Content-Disposition': 'Attachment; filename="awesome-data-2022-08-01.txt"'
});
});
it('yaml', function () {
return shared.headers.get('yaml file', {disposition: {type: 'yaml', value: 'my.yaml'}}, new Frame())
.then((result) => {
result.should.eql({
'Content-Disposition': 'Attachment; filename="my.yaml"',
'Content-Type': 'application/yaml',
'Content-Length': 11
});
});
});
});
describe('config.cacheInvalidate', function () {
it('default', function () {
return shared.headers.get({}, {cacheInvalidate: true}, new Frame())
.then((result) => {
result.should.eql({
'X-Cache-Invalidate': '/*'
});
});
});
it('custom value', function () {
return shared.headers.get({}, {cacheInvalidate: {value: 'value'}}, new Frame())
.then((result) => {
result.should.eql({
'X-Cache-Invalidate': 'value'
});
});
});
});
describe('location header', function () {
it('adds header when all needed data is present and method is add', function () {
const apiResult = {
posts: [{
id: 'id_value'
}]
};
const apiConfigHeaders = {};
const frame = new Frame();
frame.docName = 'posts',
frame.method = 'add',
frame.original = {
url: {
host: 'example.com',
pathname: `/api/content/posts/`
}
};
return shared.headers.get(apiResult, apiConfigHeaders, frame)
.then((result) => {
result.should.eql({
// NOTE: the backslash in the end is important to avoid unecessary 301s using the header
Location: 'https://example.com/api/content/posts/id_value/'
});
});
});
it('adds header when a location resolver is provided', function () {
const apiResult = {
posts: [{
id: 'id_value'
}]
};
const resolvedLocationUrl = 'resolved location';
const apiConfigHeaders = {
location: {
resolve() {
return resolvedLocationUrl;
}
}
};
const frame = new Frame();
frame.docName = 'posts';
frame.method = 'copy';
frame.original = {
url: {
host: 'example.com',
pathname: `/api/content/posts/existing_post_id_value/copy`
}
};
return shared.headers.get(apiResult, apiConfigHeaders, frame)
.then((result) => {
result.should.eql({
Location: resolvedLocationUrl
});
});
});
it('respects HTTP redirects', async function () {
const apiResult = {
posts: [{
id: 'id_value'
}]
};
const apiConfigHeaders = {};
const frame = new Frame();
frame.docName = 'posts';
frame.method = 'add';
frame.original = {
url: {
host: 'example.com',
pathname: `/api/content/posts/`,
secure: false
}
};
const result = await shared.headers.get(apiResult, apiConfigHeaders, frame);
result.should.eql({
// NOTE: the backslash in the end is important to avoid unecessary 301s using the header
Location: 'http://example.com/api/content/posts/id_value/'
});
});
it('adds and resolves header to correct url when pathname does not contain backslash in the end', function () {
const apiResult = {
posts: [{
id: 'id_value'
}]
};
const apiConfigHeaders = {};
const frame = new Frame();
frame.docName = 'posts';
frame.method = 'add';
frame.original = {
url: {
host: 'example.com',
pathname: `/api/content/posts`
}
};
return shared.headers.get(apiResult, apiConfigHeaders, frame)
.then((result) => {
result.should.eql({
// NOTE: the backslash in the end is important to avoid unecessary 301s using the header
Location: 'https://example.com/api/content/posts/id_value/'
});
});
});
it('does not add header when missing result values', function () {
const apiResult = {};
const apiConfigHeaders = {};
const frame = new Frame();
frame.docName = 'posts';
frame.method = 'add';
frame.original = {
url: {
host: 'example.com',
pathname: `/api/content/posts/`
}
};
return shared.headers.get(apiResult, apiConfigHeaders, frame)
.then((result) => {
result.should.eql({});
});
});
});
});