diff --git a/packages/server/src/routes/api/files.test.ts b/packages/server/src/routes/api/files.test.ts new file mode 100644 index 0000000000..288d8674f9 --- /dev/null +++ b/packages/server/src/routes/api/files.test.ts @@ -0,0 +1,112 @@ +import routeHandler from '../../middleware/routeHandler'; +import { testAssetDir, beforeAllDb, afterAllDb, beforeEachDb, koaAppContext, createUserAndSession, models } from '../../utils/testing/testUtils'; +import * as fs from 'fs-extra'; + +function testFilePath(ext: string = 'jpg') { + const basename = ext === 'jpg' ? 'photo' : 'poster'; + return `${testAssetDir}/${basename}.${ext}`; +} + +describe('api_files', function() { + + beforeAll(async () => { + await beforeAllDb('api_files'); + }); + + afterAll(async () => { + await afterAllDb(); + }); + + beforeEach(async () => { + await beforeEachDb(); + }); + + test('should create a file', async function() { + const { user, session } = await createUserAndSession(1, true); + const filePath = testFilePath(); + + const context = await koaAppContext({ + sessionId: session.id, + request: { + method: 'PUT', + url: '/api/files/root:/photo.jpg:/content', + files: { file: { path: filePath } }, + }, + }); + + await routeHandler(context); + + const newFile = context.response.body; + + expect(!!newFile.id).toBe(true); + expect(newFile.name).toBe('photo.jpg'); + expect(newFile.mime_type).toBe('image/jpeg'); + expect(!!newFile.parent_id).toBe(true); + expect(!newFile.content).toBe(true); + expect(newFile.size > 0).toBe(true); + + const fileModel = models().file({ userId: user.id }); + const newFileReload = await fileModel.loadWithContent(newFile.id); + + expect(!!newFileReload).toBe(true); + + const fileContent = await fs.readFile(filePath); + const newFileHex = fileContent.toString('hex'); + const newFileReloadHex = (newFileReload.content as Buffer).toString('hex'); + expect(newFileReloadHex.length > 0).toBe(true); + expect(newFileReloadHex).toBe(newFileHex); + }); + + test('should create sub-directories', async function() { + const { session } = await createUserAndSession(1, true); + + const context1 = await koaAppContext({ + sessionId: session.id, + request: { + method: 'POST', + url: '/api/files/root/children', + body: { + is_directory: 1, + name: 'subdir', + }, + }, + }); + + await routeHandler(context1); + + const newDir = context1.response.body; + expect(!!newDir.id).toBe(true); + expect(newDir.is_directory).toBe(1); + + const context2 = await koaAppContext({ + sessionId: session.id, + request: { + method: 'POST', + url: '/api/files/root:/subdir:/children', + body: { + is_directory: 1, + name: 'subdir2', + }, + }, + }); + + await routeHandler(context2); + + const newDir2 = context2.response.body; + + const context3 = await koaAppContext({ + sessionId: session.id, + request: { + method: 'GET', + url: '/api/files/root:/subdir/subdir2:', + }, + }); + + await routeHandler(context3); + + const newDirReload2 = context3.response.body; + expect(newDirReload2.id).toBe(newDir2.id); + expect(newDirReload2.name).toBe(newDir2.name); + }); + +}); diff --git a/packages/server/src/routes/api/files.ts b/packages/server/src/routes/api/files.ts index c84b359c16..d5d4028a16 100644 --- a/packages/server/src/routes/api/files.ts +++ b/packages/server/src/routes/api/files.ts @@ -76,8 +76,6 @@ const route: Route = { throw new ErrorNotFound(`Invalid link: ${path.link}`); }, - needsBodyMiddleware: true, - }; export default route; diff --git a/packages/server/src/routes/api/ping.test.ts b/packages/server/src/routes/api/ping.test.ts index 1b620b3d54..24a0ae7d62 100644 --- a/packages/server/src/routes/api/ping.test.ts +++ b/packages/server/src/routes/api/ping.test.ts @@ -17,7 +17,9 @@ describe('api_ping', function() { test('should ping', async function() { const context = await koaAppContext({ - path: '/api/ping', + request: { + url: '/api/ping', + }, }); await routeHandler(context); diff --git a/packages/server/src/utils/requestUtils.ts b/packages/server/src/utils/requestUtils.ts index 87280fae6e..12928fc55a 100644 --- a/packages/server/src/utils/requestUtils.ts +++ b/packages/server/src/utils/requestUtils.ts @@ -12,6 +12,16 @@ interface FormParseResult { // Input should be Koa ctx.req, which corresponds to the native Node request export async function formParse(req: any): Promise { + // It's not clear how to get mocked requests to be parsed successfully by + // formidable so we use this small hack. If it's mocked, we are running test + // units and the request body is already an object and can be returned. + if (req.__isMocked) { + const output: any = {}; + if (req.files) output.files = req.files; + output.fields = req.body || {}; + return output; + } + return new Promise((resolve: Function, reject: Function) => { const form = formidable({ multiples: true }); form.parse(req, (error: any, fields: any, files: any) => { @@ -30,11 +40,6 @@ export async function bodyFields(req: any): Promise { throw new ErrorBadRequest(`Unsupported Content-Type: "${req.headers['content-type']}". Expected: "application/json"`); } - // It's not clear how to get mocked requests to be parsed successfully by - // formidable so we use this small hack. If it's mocked, we are running test - // units and the request body is already an object and can be returned. - if (req.__isMocked) return { ...req.body }; - const form = await formParse(req); return form.fields; } diff --git a/packages/server/src/utils/routeUtils.ts b/packages/server/src/utils/routeUtils.ts index c2ac8c3ba4..3ddb5c5a3b 100644 --- a/packages/server/src/utils/routeUtils.ts +++ b/packages/server/src/utils/routeUtils.ts @@ -24,7 +24,6 @@ export enum RouteResponseFormat { export interface Route { exec: Function; - needsBodyMiddleware?: boolean; responseFormat?: RouteResponseFormat; } diff --git a/packages/server/src/utils/testing/testUtils.ts b/packages/server/src/utils/testing/testUtils.ts index d9c3deed93..3a0c5157b0 100644 --- a/packages/server/src/utils/testing/testUtils.ts +++ b/packages/server/src/utils/testing/testUtils.ts @@ -44,7 +44,6 @@ export async function beforeEachDb() { } interface AppContextTestOptions { - // path?: string; owner?: User; sessionId?: string; request?: any; @@ -59,7 +58,6 @@ export async function koaAppContext(options: AppContextTestOptions = null): Prom if (!db_) throw new Error('Database must be initialized first'); options = { - // path: '/home', ...options, }; @@ -74,6 +72,10 @@ export async function koaAppContext(options: AppContextTestOptions = null): Prom if (!reqOptions.headers) reqOptions.headers = {}; if (!reqOptions.headers['content-type']) reqOptions.headers['content-type'] = 'application/json'; + if (options.sessionId) { + reqOptions.headers['x-api-auth'] = options.sessionId; + } + const req = httpMocks.createRequest(reqOptions); req.__isMocked = true; @@ -94,6 +96,7 @@ export async function koaAppContext(options: AppContextTestOptions = null): Prom appContext.cookies = new FakeCookies(); appContext.request = new FakeRequest(req); appContext.response = new FakeResponse(); + appContext.headers = { ...reqOptions.headers }; appContext.req = req; appContext.method = req.method;