Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions src/modules/file/file.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { type NestExpressApplication } from '@nestjs/platform-express';
import {
BadRequestException,
ConflictException,
HttpStatus,
type ArgumentsHost,
} from '@nestjs/common';
import { createMock } from '@golevelup/ts-jest';
Expand All @@ -26,6 +27,7 @@ import { UniqueConstraintFilter } from '../../common/filters/unique-constraint.f
import { AesService } from '../../externals/crypto/aes';
import request from 'supertest';
import { ClientEnum } from '../../common/enums/platform.enum';
import { SequelizeThumbnailRepository } from '../thumbnail/thumbnail.repository';

describe('File module', () => {
let app: NestExpressApplication;
Expand All @@ -52,6 +54,18 @@ describe('File module', () => {
await testUser.cleanup();
});

const createUserFile = (owner: TestUserContext) =>
fileRepository.create(
newFile({
attributes: {
folderId: owner.rootFolder?.id,
folderUuid: owner.rootFolder?.uuid,
userId: owner.user.id,
status: FileStatus.EXISTS,
},
}),
);

afterAll(async () => {
await app.close();
});
Expand Down Expand Up @@ -746,6 +760,144 @@ describe('File module', () => {
});
});

describe('POST /files', () => {
const buildCreateFileBody = (folderUuid: string) => ({
bucket: v4().substring(0, 24),
fileId: v4().substring(0, 24),
encryptVersion: '03-aes',
folderUuid,
size: 100,
plainName: `file-${v4()}`,
type: 'txt',
});

it('When the user uploads a file to their own folder, then the file is created', async () => {
const body = buildCreateFileBody(testUser.rootFolder?.uuid);

const response = await request(app.getHttpServer())
.post('/files')
.set('Authorization', `bearer ${testUser.token}`)
.send(body);
const storedFile = await fileModel.findOne({
where: { uuid: response.body.uuid ?? null },
});

expect(response.status).toBe(HttpStatus.CREATED);
expect(response.body).toMatchObject({
uuid: expect.any(String),
plainName: body.plainName,
status: FileStatus.EXISTS,
folderUuid: body.folderUuid,
userId: testUser.user.id,
});
expect(storedFile).toMatchObject({
uuid: response.body.uuid,
plainName: body.plainName,
status: FileStatus.EXISTS,
folderUuid: body.folderUuid,
userId: testUser.user.id,
});
});

it('When the user uploads a file to a folder of another user, then it is not allowed', async () => {
const anotherTestUser = await createTestUser(app);
const body = buildCreateFileBody(anotherTestUser.rootFolder?.uuid);

const response = await request(app.getHttpServer())
.post('/files')
.set('Authorization', `bearer ${testUser.token}`)
.send(body);
const filesInFolder = await fileModel.count({
where: { folderUuid: anotherTestUser.rootFolder?.uuid },
});
await anotherTestUser.cleanup();

expect(response.status).toBe(HttpStatus.FORBIDDEN);
expect(filesInFolder).toBe(0);
});
});

describe('GET /files/sync', () => {
it('When listing files, then only the files of the current user are returned', async () => {
const anotherTestUser = await createTestUser(app);
const requesterFile = await createUserFile(testUser);
const anotherUserFile = await createUserFile(anotherTestUser);

const response = await request(app.getHttpServer())
.get('/files/sync')
.query({
status: FileStatus.EXISTS,
updatedAt: new Date(0).toISOString(),
})
.set('Authorization', `bearer ${testUser.token}`);
await anotherTestUser.cleanup();

expect(response.status).toBe(HttpStatus.OK);
expect(response.body.files).toHaveLength(1);
expect(response.body.files[0]).toMatchObject({
uuid: requesterFile.uuid,
plainName: requesterFile.plainName,
status: FileStatus.EXISTS,
});
expect(response.body.files[0].uuid).not.toBe(anotherUserFile.uuid);
});
});

describe('POST /files/thumbnail', () => {
let thumbnailRepository: SequelizeThumbnailRepository;

beforeAll(() => {
thumbnailRepository = app.get(SequelizeThumbnailRepository);
});

const buildThumbnailBody = (fileUuid: string) => ({
fileUuid,
type: 'png',
size: 100,
maxWidth: 300,
maxHeight: 300,
bucketId: v4().substring(0, 24),
bucketFile: v4().substring(0, 24),
encryptVersion: '03-aes',
});

it('When the user adds a thumbnail to their own file, then the thumbnail is created', async () => {
const file = await createUserFile(testUser);
const body = buildThumbnailBody(file.uuid);

const response = await request(app.getHttpServer())
.post('/files/thumbnail')
.set('Authorization', `bearer ${testUser.token}`)
.send(body);
const thumbnails = await thumbnailRepository.findAll({
fileUuid: file.uuid,
});
await thumbnailRepository.deleteBy({ fileUuid: file.uuid });

expect(response.status).toBe(HttpStatus.CREATED);
expect(response.body).toMatchObject({ fileUuid: file.uuid });
expect(thumbnails).toHaveLength(1);
});

it('When the user adds a thumbnail to a file of another user, then it is not allowed', async () => {
const anotherTestUser = await createTestUser(app);
const otherUserFile = await createUserFile(anotherTestUser);
const body = buildThumbnailBody(otherUserFile.uuid);

const response = await request(app.getHttpServer())
.post('/files/thumbnail')
.set('Authorization', `bearer ${testUser.token}`)
.send(body);
const thumbnails = await thumbnailRepository.findAll({
fileUuid: otherUserFile.uuid,
});
await anotherTestUser.cleanup();

expect(response.status).toBe(HttpStatus.FORBIDDEN);
expect(thumbnails).toHaveLength(0);
});
});

describe('File unique constraint', () => {
it('When a file with a duplicated name is inserted, then the violated constraint is mapped to a conflict', async () => {
const fileAttributes = newFile({
Expand Down
108 changes: 106 additions & 2 deletions src/modules/folder/folder.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import request from 'supertest';

import { SequelizeFileRepository } from '../file/file.repository';
import { newFile, newFolder } from '../../../test/fixtures';
import { type File } from '../file/file.domain';
import { type File, FileStatus } from '../file/file.domain';
import { SequelizeFolderRepository } from './folder.repository';
import { type Folder } from './folder.domain';
import { type Folder, FolderStatus } from './folder.domain';
import { CreateFolderDto } from './dto/create-folder.dto';
import {
createTestUser,
Expand Down Expand Up @@ -121,6 +121,8 @@ describe('Folder module', () => {
size: file.size,
type: file.type,
name: file.plainName,
plainName: file.plainName,
status: FileStatus.EXISTS,
});
});

Expand Down Expand Up @@ -176,6 +178,108 @@ describe('Folder module', () => {

expect(response.body).toHaveProperty('folders');
expect(response.body.folders.length).toBe(1);
expect(response.body.folders[0]).toMatchObject({
uuid: folder.uuid,
plainName: folder.plainName,
status: FolderStatus.EXISTS,
});
});

it('When the user lists the subfolders of a folder of another user, then nothing is returned', async () => {
const anotherTestUser = await createTestUser(app);
const folderUuid = testUser.rootFolder?.uuid;

const response = await request(app.getHttpServer())
.get(`/folders/content/${folderUuid}/folders`)
.query({ limit: DEFAULT_LIMIT, offset: DEFAULT_OFFSET })
.set('Authorization', `bearer ${anotherTestUser.token}`);
await anotherTestUser.cleanup();

expect(response.status).toBe(HttpStatus.OK);
expect(response.body.folders.length).toBe(0);
});
});

describe('POST /folders/content/:uuid/files/existence', () => {
let file: File;

beforeEach(async () => {
file = await fileRepository.create(
newFile({
attributes: {
folderId: testUser.rootFolder?.id,
folderUuid: testUser.rootFolder?.uuid,
userId: testUser.user.id,
},
}),
);
});

afterEach(async () => {
await fileRepository.destroyFile({ id: file.id });
});

it('When the user checks a file that exists in their folder, then the file is returned', async () => {
const folderUuid = testUser.rootFolder?.uuid;
const body = { files: [{ plainName: file.plainName, type: file.type }] };

const response = await makeRequest(
'post',
`/folders/content/${folderUuid}/files/existence`,
)
.send(body)
.expect(HttpStatus.CREATED);

expect(response.body.existentFiles).toHaveLength(1);
expect(response.body.existentFiles[0]).toMatchObject({
uuid: file.uuid,
plainName: file.plainName,
status: FileStatus.EXISTS,
});
});

it('When the user checks files in a folder of another user, then an error is thrown', async () => {
const anotherTestUser = await createTestUser(app);
const folderUuid = testUser.rootFolder?.uuid;
const body = { files: [{ plainName: file.plainName, type: file.type }] };

const response = await request(app.getHttpServer())
.post(`/folders/content/${folderUuid}/files/existence`)
.set('Authorization', `bearer ${anotherTestUser.token}`)
.send(body);
await anotherTestUser.cleanup();

expect(response.status).toBe(HttpStatus.FORBIDDEN);
});
});

describe('GET /folders/:id/metadata', () => {
it('When the user requests the metadata of their own folder, then it is returned', async () => {
const folderId = testUser.rootFolder?.id;

const response = await makeRequest(
'get',
`/folders/${folderId}/metadata`,
).expect(HttpStatus.OK);

expect(response.body).toMatchObject({
id: folderId,
uuid: testUser.rootFolder?.uuid,
plainName: testUser.rootFolder?.plainName,
status: FolderStatus.EXISTS,
});
});

it('When the user requests the metadata of a folder of another user, then it is not found', async () => {
const anotherTestUser = await createTestUser(app);
const folderId = testUser.rootFolder?.id;

const response = await request(app.getHttpServer())
.get(`/folders/${folderId}/metadata`)
.set('Authorization', `bearer ${anotherTestUser.token}`);
await anotherTestUser.cleanup();

expect(response.status).toBe(HttpStatus.NOT_FOUND);
});
});

Expand Down
Loading