Files
ulitka/src/modules/file/domain/entities/stored-file.entity.spec.ts
T
vadimandClaude Opus 5.5 f32c6e348d Add file module for uploads and storage
- File model + migration: file metadata, content lives in object storage
- IFileStorage port with local disk and S3-compatible (RustFS/MinIO) adapters
- POST /files, GET /files/:id/download, DELETE /files/:id
- MIME sniffed from content; only safe images served inline, nosniff header
- FileOwnerRegistry: owner modules register to grant visibility and keep
  their files from GC; FileGcService removes orphaned files hourly
- TransformResponseInterceptor passes StreamableFile through

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 09:17:55 +03:00

44 lines
1.5 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { StoredFile } from './stored-file.entity.js';
import { FileForbiddenException } from '../exceptions/file-forbidden.exception.js';
const build = (mimeType = 'image/png', uploaderId: string | null = 'user-1') =>
StoredFile.reconstitute({
id: 'file-1',
storageKey: 'files/key',
fileName: 'photo.png',
mimeType,
size: 10,
uploaderId,
createdAt: new Date('2026-01-01T00:00:00.000Z'),
});
describe('StoredFile', () => {
it('creates a new file with sanitized name and no id', () => {
const file = StoredFile.create({
storageKey: 'files/key',
fileName: '../secret/report.pdf',
mimeType: 'application/pdf',
size: 100,
uploaderId: 'user-1',
});
expect(file.id).toBeNull();
expect(file.fileName).toBe('report.pdf');
expect(file.uploaderId).toBe('user-1');
});
it('allows inline view only for safe image types', () => {
expect(build('image/png').isInlineViewable).toBe(true);
expect(build('image/svg+xml').isInlineViewable).toBe(false);
expect(build('text/html').isInlineViewable).toBe(false);
expect(build('application/octet-stream').isInlineViewable).toBe(false);
});
it('treats only the uploader as the owner', () => {
expect(() => build().assertOwnedBy('user-1')).not.toThrow();
expect(() => build().assertOwnedBy('user-2')).toThrow(FileForbiddenException);
expect(() => build('image/png', null).assertOwnedBy('user-1')).toThrow(FileForbiddenException);
});
});