mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-12-23 12:30:55 +00:00
8693edbf6a
Previous versions of HedgeDoc suffered from the problem that changing the media backend required manipulation of the media links in all created notes. We discussed in #3704 that it's favourable to have an endpoint that redirects to the image's original URL. When changing the media backend, the link stays the same but just the redirect changes. Signed-off-by: Erik Michelson <github@erik.michelson.eu>
220 lines
7.7 KiB
TypeScript
220 lines
7.7 KiB
TypeScript
/*
|
|
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
import { promises as fs } from 'fs';
|
|
import { join } from 'path';
|
|
import request from 'supertest';
|
|
|
|
import { ConsoleLoggerService } from '../../src/logger/console-logger.service';
|
|
import { TestSetup, TestSetupBuilder } from '../test-setup';
|
|
import { ensureDeleted } from '../utils';
|
|
|
|
describe('Media', () => {
|
|
let testSetup: TestSetup;
|
|
let uploadPath: string;
|
|
|
|
beforeAll(async () => {
|
|
testSetup = await TestSetupBuilder.create().withUsers().build();
|
|
|
|
uploadPath =
|
|
testSetup.configService.get('mediaConfig').backend.filesystem.uploadPath;
|
|
|
|
testSetup.app.useStaticAssets(uploadPath, {
|
|
prefix: '/uploads',
|
|
});
|
|
|
|
await testSetup.app.init();
|
|
|
|
const logger = await testSetup.app.resolve(ConsoleLoggerService);
|
|
logger.log('Switching logger', 'AppBootstrap');
|
|
testSetup.app.useLogger(logger);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
// Delete the upload folder
|
|
await ensureDeleted(uploadPath);
|
|
await testSetup.app.close();
|
|
await testSetup.cleanup();
|
|
});
|
|
|
|
describe('POST /media', () => {
|
|
it('works', async () => {
|
|
const agent = request.agent(testSetup.app.getHttpServer());
|
|
const uploadResponse = await agent
|
|
.post('/api/v2/media')
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[0].secret}`)
|
|
.attach('file', 'test/public-api/fixtures/test.png')
|
|
.set('HedgeDoc-Note', 'testAlias1')
|
|
.expect('Content-Type', /json/)
|
|
.expect(201);
|
|
const fileName = uploadResponse.body.id;
|
|
const path: string = '/api/v2/media/' + fileName;
|
|
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
|
const apiResponse = await agent
|
|
.get(path)
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[0].secret}`);
|
|
expect(apiResponse.statusCode).toEqual(302);
|
|
const downloadResponse = await agent.get(apiResponse.header.location);
|
|
expect(downloadResponse.body).toEqual(testImage);
|
|
// delete the file afterwards
|
|
await fs.unlink(join(uploadPath, fileName));
|
|
});
|
|
describe('fails:', () => {
|
|
beforeEach(async () => {
|
|
await ensureDeleted(uploadPath);
|
|
});
|
|
it('MIME type not supported', async () => {
|
|
await request(testSetup.app.getHttpServer())
|
|
.post('/api/v2/media')
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[0].secret}`)
|
|
.attach('file', 'test/public-api/fixtures/test.zip')
|
|
.set('HedgeDoc-Note', 'testAlias1')
|
|
.expect(400);
|
|
await expect(fs.access(uploadPath)).rejects.toBeDefined();
|
|
});
|
|
it('note does not exist', async () => {
|
|
await request(testSetup.app.getHttpServer())
|
|
.post('/api/v2/media')
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[0].secret}`)
|
|
.attach('file', 'test/public-api/fixtures/test.zip')
|
|
.set('HedgeDoc-Note', 'i_dont_exist')
|
|
.expect(404);
|
|
await expect(fs.access(uploadPath)).rejects.toBeDefined();
|
|
});
|
|
it('mediaBackend error', async () => {
|
|
await fs.mkdir(uploadPath, {
|
|
mode: '444',
|
|
});
|
|
await request(testSetup.app.getHttpServer())
|
|
.post('/api/v2/media')
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[0].secret}`)
|
|
.attach('file', 'test/public-api/fixtures/test.png')
|
|
.set('HedgeDoc-Note', 'testAlias1')
|
|
.expect('Content-Type', /json/)
|
|
.expect(500);
|
|
});
|
|
it('no file uploaded', async () => {
|
|
await request(testSetup.app.getHttpServer())
|
|
.post('/api/v2/media')
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[0].secret}`)
|
|
.set('HedgeDoc-Note', 'testAlias1')
|
|
.expect('Content-Type', /json/)
|
|
.expect(400);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await ensureDeleted(uploadPath);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('DELETE /media/{filename}', () => {
|
|
it('successfully deletes an uploaded file', async () => {
|
|
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
|
const upload = await testSetup.mediaService.saveFile(
|
|
testImage,
|
|
testSetup.users[0],
|
|
testSetup.ownedNotes[0],
|
|
);
|
|
const filename = upload.fileUrl.split('/').pop() || '';
|
|
await request(testSetup.app.getHttpServer())
|
|
.delete('/api/v2/media/' + filename)
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[0].secret}`)
|
|
.expect(204);
|
|
});
|
|
it('returns an error if the user does not own the file', async () => {
|
|
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
|
const upload = await testSetup.mediaService.saveFile(
|
|
testImage,
|
|
testSetup.users[0],
|
|
testSetup.ownedNotes[0],
|
|
);
|
|
const filename = upload.fileUrl.split('/').pop() || '';
|
|
await request(testSetup.app.getHttpServer())
|
|
.delete('/api/v2/media/' + filename)
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[1].secret}`)
|
|
.expect(403);
|
|
});
|
|
it('deleting user is owner of file', async () => {
|
|
// upload a file with the default test user
|
|
const testNote = await testSetup.notesService.createNote(
|
|
'test content',
|
|
null,
|
|
'test_delete_media_file',
|
|
);
|
|
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
|
const upload = await testSetup.mediaService.saveFile(
|
|
testImage,
|
|
testSetup.users[0],
|
|
testNote,
|
|
);
|
|
const filename = upload.fileUrl.split('/').pop() || '';
|
|
|
|
const agent2 = request.agent(testSetup.app.getHttpServer());
|
|
|
|
// try to delete upload with second user
|
|
await agent2
|
|
.delete('/api/v2/media/' + filename)
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[1].secret}`)
|
|
.expect(403);
|
|
|
|
await agent2
|
|
.get('/uploads/' + filename)
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[1].secret}`)
|
|
.expect(200);
|
|
|
|
// delete upload for real
|
|
await agent2
|
|
.delete('/api/v2/media/' + filename)
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[0].secret}`)
|
|
.expect(204);
|
|
|
|
// Test if file is really deleted
|
|
await agent2
|
|
.get('/uploads/' + filename)
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[1].secret}`)
|
|
.expect(404);
|
|
});
|
|
it('deleting user is owner of note', async () => {
|
|
// upload a file with the default test user
|
|
const testNote = await testSetup.notesService.createNote(
|
|
'test content',
|
|
testSetup.users[2],
|
|
'test_delete_media_note',
|
|
);
|
|
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
|
const upload = await testSetup.mediaService.saveFile(
|
|
testImage,
|
|
testSetup.users[0],
|
|
testNote,
|
|
);
|
|
const filename = upload.fileUrl.split('/').pop() || '';
|
|
|
|
const agent2 = request.agent(testSetup.app.getHttpServer());
|
|
// try to delete upload with second user
|
|
await agent2
|
|
.delete('/api/v2/media/' + filename)
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[1].secret}`)
|
|
.expect(403);
|
|
|
|
await agent2
|
|
.get('/uploads/' + filename)
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[1].secret}`)
|
|
.expect(200);
|
|
|
|
// delete upload for real
|
|
await agent2
|
|
.delete('/api/v2/media/' + filename)
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[2].secret}`)
|
|
.expect(204);
|
|
|
|
// Test if file is really deleted
|
|
await agent2
|
|
.get('/uploads/' + filename)
|
|
.set('Authorization', `Bearer ${testSetup.authTokens[1].secret}`)
|
|
.expect(404);
|
|
});
|
|
});
|
|
});
|