@staticcms/proxy-server-lite
Version:
Proxy server to be used with Static CMS proxy backend
262 lines (223 loc) • 7.19 kB
text/typescript
import winston from 'winston';
import { join } from 'path';
import { getSchema, localFsMiddleware } from '.';
import { listRepoFiles } from '../utils/fs';
import type Joi from 'joi';
import type express from 'express';
jest.mock('../utils/fs');
function assetFailure(result: Joi.ValidationResult, expectedMessage: string) {
const { error } = result;
expect(error).not.toBeNull();
expect(error!.details).toHaveLength(1);
const message = error!.details.map(({ message }) => message)[0];
expect(message).toBe(expectedMessage);
}
const defaultParams = {
branch: 'master',
};
describe('localFsMiddleware', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('getSchema', () => {
it('should throw on path traversal', () => {
const schema = getSchema({ repoPath: join('Users', 'user', 'documents', 'code', 'repo') });
assetFailure(
schema.validate({
action: 'getEntry',
params: { ...defaultParams, path: '../' },
}),
'"params.path" must resolve to a path under the configured repository',
);
});
it('should not throw on valid path', () => {
const schema = getSchema({ repoPath: join('Users', 'user', 'documents', 'code', 'repo') });
const { error } = schema.validate({
action: 'getEntry',
params: { ...defaultParams, path: 'src/content/posts/title.md' },
});
expect(error).toBeUndefined();
});
it('should throw on folder traversal', () => {
const schema = getSchema({ repoPath: join('Users', 'user', 'documents', 'code', 'repo') });
assetFailure(
schema.validate({
action: 'entriesByFolder',
params: { ...defaultParams, folder: '../', extension: 'md' },
}),
'"params.folder" must resolve to a path under the configured repository',
);
});
it('should not throw on valid folder', () => {
const schema = getSchema({ repoPath: join('Users', 'user', 'documents', 'code', 'repo') });
const { error } = schema.validate({
action: 'entriesByFolder',
params: { ...defaultParams, folder: 'src/posts', extension: 'md' },
});
expect(error).toBeUndefined();
});
it('should throw on media folder traversal', () => {
const schema = getSchema({ repoPath: join('Users', 'user', 'documents', 'code', 'repo') });
assetFailure(
schema.validate({
action: 'getMedia',
params: { ...defaultParams, mediaFolder: '../' },
}),
'"params.mediaFolder" must resolve to a path under the configured repository',
);
});
it('should not throw on valid folder', () => {
const schema = getSchema({ repoPath: join('Users', 'user', 'documents', 'code', 'repo') });
const { error } = schema.validate({
action: 'getMedia',
params: { ...defaultParams, mediaFolder: 'static/images' },
});
expect(error).toBeUndefined();
});
});
describe('getMedia', () => {
it('should get media files', async () => {
(listRepoFiles as jest.Mock).mockResolvedValue([
{
file: 'mediaFolder/asset1.jpg',
isDirectory: false,
},
{
file: 'mediaFolder/asset2.jpg',
isDirectory: false,
},
{
file: 'mediaFolder/asset3.jpg',
isDirectory: false,
},
]);
const json = jest.fn();
const res: express.Response = { json } as unknown as express.Response;
const req = {
body: {
action: 'getMedia',
params: {
mediaFolder: 'mediaFolder',
branch: 'main',
},
},
} as express.Request;
const repoPath = '.';
await localFsMiddleware({ repoPath, logger: winston.createLogger() })(req, res);
expect(json).toHaveBeenCalledTimes(1);
expect(json).toHaveBeenCalledWith([
{
path: 'mediaFolder/asset1.jpg',
url: 'mediaFolder/asset1.jpg',
isDirectory: false,
},
{
path: 'mediaFolder/asset2.jpg',
url: 'mediaFolder/asset2.jpg',
isDirectory: false,
},
{
path: 'mediaFolder/asset3.jpg',
url: 'mediaFolder/asset3.jpg',
isDirectory: false,
},
]);
});
it('should translate media path to public path even when media path is absolute', async () => {
(listRepoFiles as jest.Mock).mockResolvedValue([
{
file: 'mediaFolder/asset1.jpg',
isDirectory: false,
},
{
file: 'mediaFolder/asset2.jpg',
isDirectory: false,
},
{
file: 'mediaFolder/asset3.jpg',
isDirectory: false,
},
]);
const json = jest.fn();
const res: express.Response = { json } as unknown as express.Response;
const req = {
body: {
action: 'getMedia',
params: {
mediaFolder: '/mediaFolder',
publicFolder: '/publicFolder',
branch: 'main',
},
},
} as express.Request;
const repoPath = '.';
await localFsMiddleware({ repoPath, logger: winston.createLogger() })(req, res);
expect(json).toHaveBeenCalledTimes(1);
expect(json).toHaveBeenCalledWith([
{
path: 'mediaFolder/asset1.jpg',
url: '/publicFolder/asset1.jpg',
isDirectory: false,
},
{
path: 'mediaFolder/asset2.jpg',
url: '/publicFolder/asset2.jpg',
isDirectory: false,
},
{
path: 'mediaFolder/asset3.jpg',
url: '/publicFolder/asset3.jpg',
isDirectory: false,
},
]);
});
it('should translate media path to public path', async () => {
(listRepoFiles as jest.Mock).mockResolvedValue([
{
file: 'mediaFolder/asset1.jpg',
isDirectory: false,
},
{
file: 'mediaFolder/asset2.jpg',
isDirectory: false,
},
{
file: 'mediaFolder/asset3.jpg',
isDirectory: false,
},
]);
const json = jest.fn();
const res: express.Response = { json } as unknown as express.Response;
const req = {
body: {
action: 'getMedia',
params: {
mediaFolder: 'mediaFolder',
publicFolder: '/publicFolder',
branch: 'main',
},
},
} as express.Request;
const repoPath = '.';
await localFsMiddleware({ repoPath, logger: winston.createLogger() })(req, res);
expect(json).toHaveBeenCalledTimes(1);
expect(json).toHaveBeenCalledWith([
{
path: 'mediaFolder/asset1.jpg',
url: '/publicFolder/asset1.jpg',
isDirectory: false,
},
{
path: 'mediaFolder/asset2.jpg',
url: '/publicFolder/asset2.jpg',
isDirectory: false,
},
{
path: 'mediaFolder/asset3.jpg',
url: '/publicFolder/asset3.jpg',
isDirectory: false,
},
]);
});
});
});