hedgedoc/backend/test/public-api/me.e2e-spec.ts
Erik Michelson 8693edbf6a refactor(media): add media redirection endpoint
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>
2024-04-18 22:11:49 +02:00

231 lines
8.5 KiB
TypeScript

/*
* SPDX-FileCopyrightText: 2022 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 { HistoryEntryUpdateDto } from '../../src/history/history-entry-update.dto';
import { HistoryEntryDto } from '../../src/history/history-entry.dto';
import { NoteMetadataDto } from '../../src/notes/note-metadata.dto';
import { User } from '../../src/users/user.entity';
import { TestSetup, TestSetupBuilder } from '../test-setup';
describe('Me', () => {
let testSetup: TestSetup;
let uploadPath: string;
let user: User;
beforeAll(async () => {
testSetup = await TestSetupBuilder.create().withMockAuth().build();
uploadPath =
testSetup.configService.get('mediaConfig').backend.filesystem.uploadPath;
user = await testSetup.userService.createUser('hardcoded', 'Testy');
await testSetup.app.init();
});
afterAll(async () => {
await testSetup.app.close();
await testSetup.cleanup();
});
it(`GET /me`, async () => {
const userInfo = testSetup.userService.toFullUserDto(user);
const response = await request(testSetup.app.getHttpServer())
.get('/api/v2/me')
.expect('Content-Type', /json/)
.expect(200);
expect(response.body).toEqual(userInfo);
});
it(`GET /me/history`, async () => {
const noteName = 'testGetNoteHistory1';
const note = await testSetup.notesService.createNote('', null, noteName);
const createdHistoryEntry =
await testSetup.historyService.updateHistoryEntryTimestamp(note, user);
const response = await request(testSetup.app.getHttpServer())
.get('/api/v2/me/history')
.expect('Content-Type', /json/)
.expect(200);
const history: HistoryEntryDto[] = response.body;
expect(history.length).toEqual(1);
const historyDto =
await testSetup.historyService.toHistoryEntryDto(createdHistoryEntry);
for (const historyEntry of history) {
expect(historyEntry.identifier).toEqual(historyDto.identifier);
expect(historyEntry.title).toEqual(historyDto.title);
expect(historyEntry.tags).toEqual(historyDto.tags);
expect(historyEntry.pinStatus).toEqual(historyDto.pinStatus);
expect(historyEntry.lastVisitedAt).toEqual(
historyDto.lastVisitedAt.toISOString(),
);
}
});
describe(`GET /me/history/{note}`, () => {
it('works with an existing note', async () => {
const noteName = 'testGetNoteHistory2';
const note = await testSetup.notesService.createNote('', null, noteName);
const createdHistoryEntry =
await testSetup.historyService.updateHistoryEntryTimestamp(note, user);
const response = await request(testSetup.app.getHttpServer())
.get(`/api/v2/me/history/${noteName}`)
.expect('Content-Type', /json/)
.expect(200);
const historyEntry: HistoryEntryDto = response.body;
const historyEntryDto =
await testSetup.historyService.toHistoryEntryDto(createdHistoryEntry);
expect(historyEntry.identifier).toEqual(historyEntryDto.identifier);
expect(historyEntry.title).toEqual(historyEntryDto.title);
expect(historyEntry.tags).toEqual(historyEntryDto.tags);
expect(historyEntry.pinStatus).toEqual(historyEntryDto.pinStatus);
expect(historyEntry.lastVisitedAt).toEqual(
historyEntryDto.lastVisitedAt.toISOString(),
);
});
it('fails with a non-existing note', async () => {
await request(testSetup.app.getHttpServer())
.get('/api/v2/me/history/i_dont_exist')
.expect('Content-Type', /json/)
.expect(404);
});
});
describe(`PUT /me/history/{note}`, () => {
it('works', async () => {
const noteName = 'testGetNoteHistory3';
const note = await testSetup.notesService.createNote('', null, noteName);
await testSetup.historyService.updateHistoryEntryTimestamp(note, user);
const historyEntryUpdateDto = new HistoryEntryUpdateDto();
historyEntryUpdateDto.pinStatus = true;
const response = await request(testSetup.app.getHttpServer())
.put('/api/v2/me/history/' + noteName)
.send(historyEntryUpdateDto)
.expect(200);
const history = await testSetup.historyService.getEntriesByUser(user);
const historyEntry: HistoryEntryDto = response.body;
expect(historyEntry.pinStatus).toEqual(true);
let theEntry: HistoryEntryDto;
for (const entry of history) {
if (
(await (await entry.note).aliases).find(
(element) => element.name === noteName,
)
) {
theEntry = await testSetup.historyService.toHistoryEntryDto(entry);
}
}
expect(theEntry.pinStatus).toEqual(true);
});
it('fails with a non-existing note', async () => {
await request(testSetup.app.getHttpServer())
.put('/api/v2/me/history/i_dont_exist')
.expect('Content-Type', /json/)
.expect(404);
});
});
describe(`DELETE /me/history/{note}`, () => {
it('works', async () => {
const noteName = 'testGetNoteHistory4';
const note = await testSetup.notesService.createNote('', null, noteName);
await testSetup.historyService.updateHistoryEntryTimestamp(note, user);
const response = await request(testSetup.app.getHttpServer())
.delete(`/api/v2/me/history/${noteName}`)
.expect(204);
expect(response.body).toEqual({});
const history = await testSetup.historyService.getEntriesByUser(user);
for (const entry of history) {
if (
(await (await entry.note).aliases).find(
(element) => element.name === noteName,
)
) {
throw new Error('Deleted history entry still in history');
}
}
});
describe('fails', () => {
it('with a non-existing note', async () => {
await request(testSetup.app.getHttpServer())
.delete('/api/v2/me/history/i_dont_exist')
.expect(404);
});
it('with a non-existing history entry', async () => {
const noteName = 'testGetNoteHistory5';
await testSetup.notesService.createNote('', null, noteName);
await request(testSetup.app.getHttpServer())
.delete(`/api/v2/me/history/${noteName}`)
.expect(404);
});
});
});
it(`GET /me/notes/`, async () => {
const noteName = 'testNote';
await testSetup.notesService.createNote('', user, noteName);
const response = await request(testSetup.app.getHttpServer())
.get('/api/v2/me/notes/')
.expect('Content-Type', /json/)
.expect(200);
const noteMetaDtos = response.body as NoteMetadataDto[];
expect(noteMetaDtos).toHaveLength(1);
expect(noteMetaDtos[0].primaryAddress).toEqual(noteName);
expect(noteMetaDtos[0].updateUsername).toEqual(user.username);
});
it('GET /me/media', async () => {
const note1 = await testSetup.notesService.createNote(
'This is a test note.',
await testSetup.userService.getUserByUsername('hardcoded'),
'test8',
);
const note2 = await testSetup.notesService.createNote(
'This is a test note.',
await testSetup.userService.getUserByUsername('hardcoded'),
'test9',
);
const httpServer = testSetup.app.getHttpServer();
const response1 = await request(httpServer)
.get('/api/v2/me/media/')
.expect('Content-Type', /json/)
.expect(200);
expect(response1.body).toHaveLength(0);
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
const imageIds = [];
imageIds.push(
(await testSetup.mediaService.saveFile(testImage, user, note1)).id,
);
imageIds.push(
(await testSetup.mediaService.saveFile(testImage, user, note1)).id,
);
imageIds.push(
(await testSetup.mediaService.saveFile(testImage, user, note2)).id,
);
imageIds.push(
(await testSetup.mediaService.saveFile(testImage, user, note2)).id,
);
const response = await request(httpServer)
.get('/api/v2/me/media/')
.expect('Content-Type', /json/)
.expect(200);
expect(response.body).toHaveLength(4);
expect(imageIds).toContain(response.body[0].id);
expect(imageIds).toContain(response.body[1].id);
expect(imageIds).toContain(response.body[2].id);
expect(imageIds).toContain(response.body[3].id);
for (const imageId of imageIds) {
// delete the file afterwards
await fs.unlink(join(uploadPath, imageId));
}
await fs.rm(uploadPath, { recursive: true });
});
});