view data tests
This commit is contained in:
@@ -1,7 +1,182 @@
|
||||
import * as mod from '@core/media/domain/entities/Avatar';
|
||||
import { Avatar } from './Avatar';
|
||||
import { MediaUrl } from '../value-objects/MediaUrl';
|
||||
|
||||
describe('media/domain/entities/Avatar.ts', () => {
|
||||
it('imports', () => {
|
||||
expect(mod).toBeTruthy();
|
||||
describe('Avatar', () => {
|
||||
describe('create', () => {
|
||||
it('creates a new avatar with required properties', () => {
|
||||
const avatar = Avatar.create({
|
||||
id: 'avatar-1',
|
||||
driverId: 'driver-1',
|
||||
mediaUrl: 'https://example.com/avatar.png',
|
||||
});
|
||||
|
||||
expect(avatar.id).toBe('avatar-1');
|
||||
expect(avatar.driverId).toBe('driver-1');
|
||||
expect(avatar.mediaUrl).toBeInstanceOf(MediaUrl);
|
||||
expect(avatar.mediaUrl.value).toBe('https://example.com/avatar.png');
|
||||
expect(avatar.isActive).toBe(true);
|
||||
expect(avatar.selectedAt).toBeInstanceOf(Date);
|
||||
});
|
||||
|
||||
it('throws error when driverId is missing', () => {
|
||||
expect(() =>
|
||||
Avatar.create({
|
||||
id: 'avatar-1',
|
||||
driverId: '',
|
||||
mediaUrl: 'https://example.com/avatar.png',
|
||||
})
|
||||
).toThrow('Driver ID is required');
|
||||
});
|
||||
|
||||
it('throws error when mediaUrl is missing', () => {
|
||||
expect(() =>
|
||||
Avatar.create({
|
||||
id: 'avatar-1',
|
||||
driverId: 'driver-1',
|
||||
mediaUrl: '',
|
||||
})
|
||||
).toThrow('Media URL is required');
|
||||
});
|
||||
|
||||
it('throws error when mediaUrl is invalid', () => {
|
||||
expect(() =>
|
||||
Avatar.create({
|
||||
id: 'avatar-1',
|
||||
driverId: 'driver-1',
|
||||
mediaUrl: 'invalid-url',
|
||||
})
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('reconstitute', () => {
|
||||
it('reconstitutes an avatar from props', () => {
|
||||
const selectedAt = new Date('2024-01-01T00:00:00.000Z');
|
||||
const avatar = Avatar.reconstitute({
|
||||
id: 'avatar-1',
|
||||
driverId: 'driver-1',
|
||||
mediaUrl: 'https://example.com/avatar.png',
|
||||
selectedAt,
|
||||
isActive: true,
|
||||
});
|
||||
|
||||
expect(avatar.id).toBe('avatar-1');
|
||||
expect(avatar.driverId).toBe('driver-1');
|
||||
expect(avatar.mediaUrl.value).toBe('https://example.com/avatar.png');
|
||||
expect(avatar.selectedAt).toEqual(selectedAt);
|
||||
expect(avatar.isActive).toBe(true);
|
||||
});
|
||||
|
||||
it('reconstitutes an inactive avatar', () => {
|
||||
const avatar = Avatar.reconstitute({
|
||||
id: 'avatar-1',
|
||||
driverId: 'driver-1',
|
||||
mediaUrl: 'https://example.com/avatar.png',
|
||||
selectedAt: new Date(),
|
||||
isActive: false,
|
||||
});
|
||||
|
||||
expect(avatar.isActive).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deactivate', () => {
|
||||
it('deactivates an active avatar', () => {
|
||||
const avatar = Avatar.create({
|
||||
id: 'avatar-1',
|
||||
driverId: 'driver-1',
|
||||
mediaUrl: 'https://example.com/avatar.png',
|
||||
});
|
||||
|
||||
expect(avatar.isActive).toBe(true);
|
||||
|
||||
avatar.deactivate();
|
||||
|
||||
expect(avatar.isActive).toBe(false);
|
||||
});
|
||||
|
||||
it('can deactivate an already inactive avatar', () => {
|
||||
const avatar = Avatar.reconstitute({
|
||||
id: 'avatar-1',
|
||||
driverId: 'driver-1',
|
||||
mediaUrl: 'https://example.com/avatar.png',
|
||||
selectedAt: new Date(),
|
||||
isActive: false,
|
||||
});
|
||||
|
||||
avatar.deactivate();
|
||||
|
||||
expect(avatar.isActive).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toProps', () => {
|
||||
it('returns correct props for a new avatar', () => {
|
||||
const avatar = Avatar.create({
|
||||
id: 'avatar-1',
|
||||
driverId: 'driver-1',
|
||||
mediaUrl: 'https://example.com/avatar.png',
|
||||
});
|
||||
|
||||
const props = avatar.toProps();
|
||||
|
||||
expect(props.id).toBe('avatar-1');
|
||||
expect(props.driverId).toBe('driver-1');
|
||||
expect(props.mediaUrl).toBe('https://example.com/avatar.png');
|
||||
expect(props.selectedAt).toBeInstanceOf(Date);
|
||||
expect(props.isActive).toBe(true);
|
||||
});
|
||||
|
||||
it('returns correct props for an inactive avatar', () => {
|
||||
const selectedAt = new Date('2024-01-01T00:00:00.000Z');
|
||||
const avatar = Avatar.reconstitute({
|
||||
id: 'avatar-1',
|
||||
driverId: 'driver-1',
|
||||
mediaUrl: 'https://example.com/avatar.png',
|
||||
selectedAt,
|
||||
isActive: false,
|
||||
});
|
||||
|
||||
const props = avatar.toProps();
|
||||
|
||||
expect(props.id).toBe('avatar-1');
|
||||
expect(props.driverId).toBe('driver-1');
|
||||
expect(props.mediaUrl).toBe('https://example.com/avatar.png');
|
||||
expect(props.selectedAt).toEqual(selectedAt);
|
||||
expect(props.isActive).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('value object validation', () => {
|
||||
it('validates mediaUrl as MediaUrl value object', () => {
|
||||
const avatar = Avatar.create({
|
||||
id: 'avatar-1',
|
||||
driverId: 'driver-1',
|
||||
mediaUrl: 'https://example.com/avatar.png',
|
||||
});
|
||||
|
||||
expect(avatar.mediaUrl).toBeInstanceOf(MediaUrl);
|
||||
expect(avatar.mediaUrl.value).toBe('https://example.com/avatar.png');
|
||||
});
|
||||
|
||||
it('accepts data URI for mediaUrl', () => {
|
||||
const avatar = Avatar.create({
|
||||
id: 'avatar-1',
|
||||
driverId: 'driver-1',
|
||||
mediaUrl: 'data:image/png;base64,abc',
|
||||
});
|
||||
|
||||
expect(avatar.mediaUrl.value).toBe('data:image/png;base64,abc');
|
||||
});
|
||||
|
||||
it('accepts root-relative path for mediaUrl', () => {
|
||||
const avatar = Avatar.create({
|
||||
id: 'avatar-1',
|
||||
driverId: 'driver-1',
|
||||
mediaUrl: '/images/avatar.png',
|
||||
});
|
||||
|
||||
expect(avatar.mediaUrl.value).toBe('/images/avatar.png');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,476 @@
|
||||
import * as mod from '@core/media/domain/entities/AvatarGenerationRequest';
|
||||
import { AvatarGenerationRequest } from './AvatarGenerationRequest';
|
||||
import { MediaUrl } from '../value-objects/MediaUrl';
|
||||
|
||||
describe('media/domain/entities/AvatarGenerationRequest.ts', () => {
|
||||
it('imports', () => {
|
||||
expect(mod).toBeTruthy();
|
||||
describe('AvatarGenerationRequest', () => {
|
||||
describe('create', () => {
|
||||
it('creates a new request with required properties', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
style: 'realistic',
|
||||
});
|
||||
|
||||
expect(request.id).toBe('req-1');
|
||||
expect(request.userId).toBe('user-1');
|
||||
expect(request.facePhotoUrl).toBeInstanceOf(MediaUrl);
|
||||
expect(request.facePhotoUrl.value).toBe('data:image/png;base64,abc');
|
||||
expect(request.suitColor).toBe('red');
|
||||
expect(request.style).toBe('realistic');
|
||||
expect(request.status).toBe('pending');
|
||||
expect(request.generatedAvatarUrls).toEqual([]);
|
||||
expect(request.selectedAvatarIndex).toBeUndefined();
|
||||
expect(request.errorMessage).toBeUndefined();
|
||||
expect(request.createdAt).toBeInstanceOf(Date);
|
||||
expect(request.updatedAt).toBeInstanceOf(Date);
|
||||
});
|
||||
|
||||
it('creates request with default style when not provided', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'blue',
|
||||
});
|
||||
|
||||
expect(request.style).toBe('realistic');
|
||||
});
|
||||
|
||||
it('throws error when userId is missing', () => {
|
||||
expect(() =>
|
||||
AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: '',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
})
|
||||
).toThrow('User ID is required');
|
||||
});
|
||||
|
||||
it('throws error when facePhotoUrl is missing', () => {
|
||||
expect(() =>
|
||||
AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: '',
|
||||
suitColor: 'red',
|
||||
})
|
||||
).toThrow('Face photo URL is required');
|
||||
});
|
||||
|
||||
it('throws error when facePhotoUrl is invalid', () => {
|
||||
expect(() =>
|
||||
AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'invalid-url',
|
||||
suitColor: 'red',
|
||||
})
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('reconstitute', () => {
|
||||
it('reconstitutes a request from props', () => {
|
||||
const createdAt = new Date('2024-01-01T00:00:00.000Z');
|
||||
const updatedAt = new Date('2024-01-01T01:00:00.000Z');
|
||||
const request = AvatarGenerationRequest.reconstitute({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
style: 'realistic',
|
||||
status: 'pending',
|
||||
generatedAvatarUrls: [],
|
||||
createdAt,
|
||||
updatedAt,
|
||||
});
|
||||
|
||||
expect(request.id).toBe('req-1');
|
||||
expect(request.userId).toBe('user-1');
|
||||
expect(request.facePhotoUrl.value).toBe('data:image/png;base64,abc');
|
||||
expect(request.suitColor).toBe('red');
|
||||
expect(request.style).toBe('realistic');
|
||||
expect(request.status).toBe('pending');
|
||||
expect(request.generatedAvatarUrls).toEqual([]);
|
||||
expect(request.selectedAvatarIndex).toBeUndefined();
|
||||
expect(request.errorMessage).toBeUndefined();
|
||||
expect(request.createdAt).toEqual(createdAt);
|
||||
expect(request.updatedAt).toEqual(updatedAt);
|
||||
});
|
||||
|
||||
it('reconstitutes a request with selected avatar', () => {
|
||||
const request = AvatarGenerationRequest.reconstitute({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
style: 'realistic',
|
||||
status: 'completed',
|
||||
generatedAvatarUrls: ['https://example.com/a.png', 'https://example.com/b.png'],
|
||||
selectedAvatarIndex: 1,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
expect(request.selectedAvatarIndex).toBe(1);
|
||||
expect(request.selectedAvatarUrl).toBe('https://example.com/b.png');
|
||||
});
|
||||
|
||||
it('reconstitutes a failed request', () => {
|
||||
const request = AvatarGenerationRequest.reconstitute({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
style: 'realistic',
|
||||
status: 'failed',
|
||||
generatedAvatarUrls: [],
|
||||
errorMessage: 'Generation failed',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
expect(request.status).toBe('failed');
|
||||
expect(request.errorMessage).toBe('Generation failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('status transitions', () => {
|
||||
it('transitions from pending to validating', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
});
|
||||
|
||||
expect(request.status).toBe('pending');
|
||||
|
||||
request.markAsValidating();
|
||||
|
||||
expect(request.status).toBe('validating');
|
||||
});
|
||||
|
||||
it('transitions from validating to generating', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
});
|
||||
request.markAsValidating();
|
||||
|
||||
request.markAsGenerating();
|
||||
|
||||
expect(request.status).toBe('generating');
|
||||
});
|
||||
|
||||
it('throws error when marking as validating from non-pending status', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
});
|
||||
request.markAsValidating();
|
||||
|
||||
expect(() => request.markAsValidating()).toThrow('Can only start validation from pending status');
|
||||
});
|
||||
|
||||
it('throws error when marking as generating from non-validating status', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
});
|
||||
|
||||
expect(() => request.markAsGenerating()).toThrow('Can only start generation from validating status');
|
||||
});
|
||||
|
||||
it('completes request with avatars', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
});
|
||||
request.markAsValidating();
|
||||
request.markAsGenerating();
|
||||
|
||||
request.completeWithAvatars(['https://example.com/a.png', 'https://example.com/b.png']);
|
||||
|
||||
expect(request.status).toBe('completed');
|
||||
expect(request.generatedAvatarUrls).toEqual(['https://example.com/a.png', 'https://example.com/b.png']);
|
||||
});
|
||||
|
||||
it('throws error when completing with empty avatar list', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
});
|
||||
request.markAsValidating();
|
||||
request.markAsGenerating();
|
||||
|
||||
expect(() => request.completeWithAvatars([])).toThrow('At least one avatar URL is required');
|
||||
});
|
||||
|
||||
it('fails request with error message', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
});
|
||||
request.markAsValidating();
|
||||
|
||||
request.fail('Face validation failed');
|
||||
|
||||
expect(request.status).toBe('failed');
|
||||
expect(request.errorMessage).toBe('Face validation failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('avatar selection', () => {
|
||||
it('selects avatar when request is completed', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
});
|
||||
request.markAsValidating();
|
||||
request.markAsGenerating();
|
||||
request.completeWithAvatars(['https://example.com/a.png', 'https://example.com/b.png']);
|
||||
|
||||
request.selectAvatar(1);
|
||||
|
||||
expect(request.selectedAvatarIndex).toBe(1);
|
||||
expect(request.selectedAvatarUrl).toBe('https://example.com/b.png');
|
||||
});
|
||||
|
||||
it('throws error when selecting avatar from non-completed request', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
});
|
||||
request.markAsValidating();
|
||||
|
||||
expect(() => request.selectAvatar(0)).toThrow('Can only select avatar when generation is completed');
|
||||
});
|
||||
|
||||
it('throws error when selecting invalid index', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
});
|
||||
request.markAsValidating();
|
||||
request.markAsGenerating();
|
||||
request.completeWithAvatars(['https://example.com/a.png', 'https://example.com/b.png']);
|
||||
|
||||
expect(() => request.selectAvatar(-1)).toThrow('Invalid avatar index');
|
||||
expect(() => request.selectAvatar(2)).toThrow('Invalid avatar index');
|
||||
});
|
||||
|
||||
it('returns undefined for selectedAvatarUrl when no avatar selected', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
});
|
||||
request.markAsValidating();
|
||||
request.markAsGenerating();
|
||||
request.completeWithAvatars(['https://example.com/a.png', 'https://example.com/b.png']);
|
||||
|
||||
expect(request.selectedAvatarUrl).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildPrompt', () => {
|
||||
it('builds prompt for red suit, realistic style', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
style: 'realistic',
|
||||
});
|
||||
|
||||
const prompt = request.buildPrompt();
|
||||
|
||||
expect(prompt).toContain('vibrant racing red');
|
||||
expect(prompt).toContain('photorealistic, professional motorsport portrait');
|
||||
expect(prompt).toContain('racing driver');
|
||||
expect(prompt).toContain('racing suit');
|
||||
expect(prompt).toContain('helmet');
|
||||
});
|
||||
|
||||
it('builds prompt for blue suit, cartoon style', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'blue',
|
||||
style: 'cartoon',
|
||||
});
|
||||
|
||||
const prompt = request.buildPrompt();
|
||||
|
||||
expect(prompt).toContain('deep motorsport blue');
|
||||
expect(prompt).toContain('stylized cartoon racing character');
|
||||
});
|
||||
|
||||
it('builds prompt for pixel-art style', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'green',
|
||||
style: 'pixel-art',
|
||||
});
|
||||
|
||||
const prompt = request.buildPrompt();
|
||||
|
||||
expect(prompt).toContain('racing green');
|
||||
expect(prompt).toContain('8-bit pixel art retro racing avatar');
|
||||
});
|
||||
|
||||
it('builds prompt for all suit colors', () => {
|
||||
const colors = ['red', 'blue', 'green', 'yellow', 'orange', 'purple', 'black', 'white', 'pink', 'cyan'] as const;
|
||||
|
||||
colors.forEach((color) => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: color,
|
||||
});
|
||||
|
||||
const prompt = request.buildPrompt();
|
||||
|
||||
expect(prompt).toContain(color);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toProps', () => {
|
||||
it('returns correct props for a new request', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
style: 'realistic',
|
||||
});
|
||||
|
||||
const props = request.toProps();
|
||||
|
||||
expect(props.id).toBe('req-1');
|
||||
expect(props.userId).toBe('user-1');
|
||||
expect(props.facePhotoUrl).toBe('data:image/png;base64,abc');
|
||||
expect(props.suitColor).toBe('red');
|
||||
expect(props.style).toBe('realistic');
|
||||
expect(props.status).toBe('pending');
|
||||
expect(props.generatedAvatarUrls).toEqual([]);
|
||||
expect(props.selectedAvatarIndex).toBeUndefined();
|
||||
expect(props.errorMessage).toBeUndefined();
|
||||
expect(props.createdAt).toBeInstanceOf(Date);
|
||||
expect(props.updatedAt).toBeInstanceOf(Date);
|
||||
});
|
||||
|
||||
it('returns correct props for a completed request with selected avatar', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
style: 'realistic',
|
||||
});
|
||||
request.markAsValidating();
|
||||
request.markAsGenerating();
|
||||
request.completeWithAvatars(['https://example.com/a.png', 'https://example.com/b.png']);
|
||||
request.selectAvatar(1);
|
||||
|
||||
const props = request.toProps();
|
||||
|
||||
expect(props.id).toBe('req-1');
|
||||
expect(props.userId).toBe('user-1');
|
||||
expect(props.facePhotoUrl).toBe('data:image/png;base64,abc');
|
||||
expect(props.suitColor).toBe('red');
|
||||
expect(props.style).toBe('realistic');
|
||||
expect(props.status).toBe('completed');
|
||||
expect(props.generatedAvatarUrls).toEqual(['https://example.com/a.png', 'https://example.com/b.png']);
|
||||
expect(props.selectedAvatarIndex).toBe(1);
|
||||
expect(props.errorMessage).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns correct props for a failed request', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
style: 'realistic',
|
||||
});
|
||||
request.markAsValidating();
|
||||
request.fail('Face validation failed');
|
||||
|
||||
const props = request.toProps();
|
||||
|
||||
expect(props.id).toBe('req-1');
|
||||
expect(props.userId).toBe('user-1');
|
||||
expect(props.facePhotoUrl).toBe('data:image/png;base64,abc');
|
||||
expect(props.suitColor).toBe('red');
|
||||
expect(props.style).toBe('realistic');
|
||||
expect(props.status).toBe('failed');
|
||||
expect(props.generatedAvatarUrls).toEqual([]);
|
||||
expect(props.selectedAvatarIndex).toBeUndefined();
|
||||
expect(props.errorMessage).toBe('Face validation failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('value object validation', () => {
|
||||
it('validates facePhotoUrl as MediaUrl value object', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'data:image/png;base64,abc',
|
||||
suitColor: 'red',
|
||||
});
|
||||
|
||||
expect(request.facePhotoUrl).toBeInstanceOf(MediaUrl);
|
||||
expect(request.facePhotoUrl.value).toBe('data:image/png;base64,abc');
|
||||
});
|
||||
|
||||
it('accepts http URL for facePhotoUrl', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: 'https://example.com/face.png',
|
||||
suitColor: 'red',
|
||||
});
|
||||
|
||||
expect(request.facePhotoUrl.value).toBe('https://example.com/face.png');
|
||||
});
|
||||
|
||||
it('accepts root-relative path for facePhotoUrl', () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
facePhotoUrl: '/images/face.png',
|
||||
suitColor: 'red',
|
||||
});
|
||||
|
||||
expect(request.facePhotoUrl.value).toBe('/images/face.png');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,307 @@
|
||||
import * as mod from '@core/media/domain/entities/Media';
|
||||
import { Media } from './Media';
|
||||
import { MediaUrl } from '../value-objects/MediaUrl';
|
||||
|
||||
describe('media/domain/entities/Media.ts', () => {
|
||||
it('imports', () => {
|
||||
expect(mod).toBeTruthy();
|
||||
describe('Media', () => {
|
||||
describe('create', () => {
|
||||
it('creates a new media with required properties', () => {
|
||||
const media = Media.create({
|
||||
id: 'media-1',
|
||||
filename: 'avatar.png',
|
||||
originalName: 'avatar.png',
|
||||
mimeType: 'image/png',
|
||||
size: 123,
|
||||
url: 'https://example.com/avatar.png',
|
||||
type: 'image',
|
||||
uploadedBy: 'user-1',
|
||||
});
|
||||
|
||||
expect(media.id).toBe('media-1');
|
||||
expect(media.filename).toBe('avatar.png');
|
||||
expect(media.originalName).toBe('avatar.png');
|
||||
expect(media.mimeType).toBe('image/png');
|
||||
expect(media.size).toBe(123);
|
||||
expect(media.url).toBeInstanceOf(MediaUrl);
|
||||
expect(media.url.value).toBe('https://example.com/avatar.png');
|
||||
expect(media.type).toBe('image');
|
||||
expect(media.uploadedBy).toBe('user-1');
|
||||
expect(media.uploadedAt).toBeInstanceOf(Date);
|
||||
expect(media.metadata).toBeUndefined();
|
||||
});
|
||||
|
||||
it('creates media with metadata', () => {
|
||||
const media = Media.create({
|
||||
id: 'media-1',
|
||||
filename: 'avatar.png',
|
||||
originalName: 'avatar.png',
|
||||
mimeType: 'image/png',
|
||||
size: 123,
|
||||
url: 'https://example.com/avatar.png',
|
||||
type: 'image',
|
||||
uploadedBy: 'user-1',
|
||||
metadata: { width: 100, height: 100 },
|
||||
});
|
||||
|
||||
expect(media.metadata).toEqual({ width: 100, height: 100 });
|
||||
});
|
||||
|
||||
it('throws error when filename is missing', () => {
|
||||
expect(() =>
|
||||
Media.create({
|
||||
id: 'media-1',
|
||||
filename: '',
|
||||
originalName: 'avatar.png',
|
||||
mimeType: 'image/png',
|
||||
size: 123,
|
||||
url: 'https://example.com/avatar.png',
|
||||
type: 'image',
|
||||
uploadedBy: 'user-1',
|
||||
})
|
||||
).toThrow('Filename is required');
|
||||
});
|
||||
|
||||
it('throws error when url is missing', () => {
|
||||
expect(() =>
|
||||
Media.create({
|
||||
id: 'media-1',
|
||||
filename: 'avatar.png',
|
||||
originalName: 'avatar.png',
|
||||
mimeType: 'image/png',
|
||||
size: 123,
|
||||
url: '',
|
||||
type: 'image',
|
||||
uploadedBy: 'user-1',
|
||||
})
|
||||
).toThrow('URL is required');
|
||||
});
|
||||
|
||||
it('throws error when uploadedBy is missing', () => {
|
||||
expect(() =>
|
||||
Media.create({
|
||||
id: 'media-1',
|
||||
filename: 'avatar.png',
|
||||
originalName: 'avatar.png',
|
||||
mimeType: 'image/png',
|
||||
size: 123,
|
||||
url: 'https://example.com/avatar.png',
|
||||
type: 'image',
|
||||
uploadedBy: '',
|
||||
})
|
||||
).toThrow('Uploaded by is required');
|
||||
});
|
||||
|
||||
it('throws error when url is invalid', () => {
|
||||
expect(() =>
|
||||
Media.create({
|
||||
id: 'media-1',
|
||||
filename: 'avatar.png',
|
||||
originalName: 'avatar.png',
|
||||
mimeType: 'image/png',
|
||||
size: 123,
|
||||
url: 'invalid-url',
|
||||
type: 'image',
|
||||
uploadedBy: 'user-1',
|
||||
})
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('reconstitute', () => {
|
||||
it('reconstitutes a media from props', () => {
|
||||
const uploadedAt = new Date('2024-01-01T00:00:00.000Z');
|
||||
const media = Media.reconstitute({
|
||||
id: 'media-1',
|
||||
filename: 'avatar.png',
|
||||
originalName: 'avatar.png',
|
||||
mimeType: 'image/png',
|
||||
size: 123,
|
||||
url: 'https://example.com/avatar.png',
|
||||
type: 'image',
|
||||
uploadedBy: 'user-1',
|
||||
uploadedAt,
|
||||
});
|
||||
|
||||
expect(media.id).toBe('media-1');
|
||||
expect(media.filename).toBe('avatar.png');
|
||||
expect(media.originalName).toBe('avatar.png');
|
||||
expect(media.mimeType).toBe('image/png');
|
||||
expect(media.size).toBe(123);
|
||||
expect(media.url.value).toBe('https://example.com/avatar.png');
|
||||
expect(media.type).toBe('image');
|
||||
expect(media.uploadedBy).toBe('user-1');
|
||||
expect(media.uploadedAt).toEqual(uploadedAt);
|
||||
expect(media.metadata).toBeUndefined();
|
||||
});
|
||||
|
||||
it('reconstitutes a media with metadata', () => {
|
||||
const media = Media.reconstitute({
|
||||
id: 'media-1',
|
||||
filename: 'avatar.png',
|
||||
originalName: 'avatar.png',
|
||||
mimeType: 'image/png',
|
||||
size: 123,
|
||||
url: 'https://example.com/avatar.png',
|
||||
type: 'image',
|
||||
uploadedBy: 'user-1',
|
||||
uploadedAt: new Date(),
|
||||
metadata: { width: 100, height: 100 },
|
||||
});
|
||||
|
||||
expect(media.metadata).toEqual({ width: 100, height: 100 });
|
||||
});
|
||||
|
||||
it('reconstitutes a video media', () => {
|
||||
const media = Media.reconstitute({
|
||||
id: 'media-1',
|
||||
filename: 'video.mp4',
|
||||
originalName: 'video.mp4',
|
||||
mimeType: 'video/mp4',
|
||||
size: 1024,
|
||||
url: 'https://example.com/video.mp4',
|
||||
type: 'video',
|
||||
uploadedBy: 'user-1',
|
||||
uploadedAt: new Date(),
|
||||
});
|
||||
|
||||
expect(media.type).toBe('video');
|
||||
});
|
||||
|
||||
it('reconstitutes a document media', () => {
|
||||
const media = Media.reconstitute({
|
||||
id: 'media-1',
|
||||
filename: 'document.pdf',
|
||||
originalName: 'document.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
size: 2048,
|
||||
url: 'https://example.com/document.pdf',
|
||||
type: 'document',
|
||||
uploadedBy: 'user-1',
|
||||
uploadedAt: new Date(),
|
||||
});
|
||||
|
||||
expect(media.type).toBe('document');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toProps', () => {
|
||||
it('returns correct props for a new media', () => {
|
||||
const media = Media.create({
|
||||
id: 'media-1',
|
||||
filename: 'avatar.png',
|
||||
originalName: 'avatar.png',
|
||||
mimeType: 'image/png',
|
||||
size: 123,
|
||||
url: 'https://example.com/avatar.png',
|
||||
type: 'image',
|
||||
uploadedBy: 'user-1',
|
||||
});
|
||||
|
||||
const props = media.toProps();
|
||||
|
||||
expect(props.id).toBe('media-1');
|
||||
expect(props.filename).toBe('avatar.png');
|
||||
expect(props.originalName).toBe('avatar.png');
|
||||
expect(props.mimeType).toBe('image/png');
|
||||
expect(props.size).toBe(123);
|
||||
expect(props.url).toBe('https://example.com/avatar.png');
|
||||
expect(props.type).toBe('image');
|
||||
expect(props.uploadedBy).toBe('user-1');
|
||||
expect(props.uploadedAt).toBeInstanceOf(Date);
|
||||
expect(props.metadata).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns correct props for a media with metadata', () => {
|
||||
const media = Media.create({
|
||||
id: 'media-1',
|
||||
filename: 'avatar.png',
|
||||
originalName: 'avatar.png',
|
||||
mimeType: 'image/png',
|
||||
size: 123,
|
||||
url: 'https://example.com/avatar.png',
|
||||
type: 'image',
|
||||
uploadedBy: 'user-1',
|
||||
metadata: { width: 100, height: 100 },
|
||||
});
|
||||
|
||||
const props = media.toProps();
|
||||
|
||||
expect(props.metadata).toEqual({ width: 100, height: 100 });
|
||||
});
|
||||
|
||||
it('returns correct props for a reconstituted media', () => {
|
||||
const uploadedAt = new Date('2024-01-01T00:00:00.000Z');
|
||||
const media = Media.reconstitute({
|
||||
id: 'media-1',
|
||||
filename: 'avatar.png',
|
||||
originalName: 'avatar.png',
|
||||
mimeType: 'image/png',
|
||||
size: 123,
|
||||
url: 'https://example.com/avatar.png',
|
||||
type: 'image',
|
||||
uploadedBy: 'user-1',
|
||||
uploadedAt,
|
||||
metadata: { width: 100, height: 100 },
|
||||
});
|
||||
|
||||
const props = media.toProps();
|
||||
|
||||
expect(props.id).toBe('media-1');
|
||||
expect(props.filename).toBe('avatar.png');
|
||||
expect(props.originalName).toBe('avatar.png');
|
||||
expect(props.mimeType).toBe('image/png');
|
||||
expect(props.size).toBe(123);
|
||||
expect(props.url).toBe('https://example.com/avatar.png');
|
||||
expect(props.type).toBe('image');
|
||||
expect(props.uploadedBy).toBe('user-1');
|
||||
expect(props.uploadedAt).toEqual(uploadedAt);
|
||||
expect(props.metadata).toEqual({ width: 100, height: 100 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('value object validation', () => {
|
||||
it('validates url as MediaUrl value object', () => {
|
||||
const media = Media.create({
|
||||
id: 'media-1',
|
||||
filename: 'avatar.png',
|
||||
originalName: 'avatar.png',
|
||||
mimeType: 'image/png',
|
||||
size: 123,
|
||||
url: 'https://example.com/avatar.png',
|
||||
type: 'image',
|
||||
uploadedBy: 'user-1',
|
||||
});
|
||||
|
||||
expect(media.url).toBeInstanceOf(MediaUrl);
|
||||
expect(media.url.value).toBe('https://example.com/avatar.png');
|
||||
});
|
||||
|
||||
it('accepts data URI for url', () => {
|
||||
const media = Media.create({
|
||||
id: 'media-1',
|
||||
filename: 'avatar.png',
|
||||
originalName: 'avatar.png',
|
||||
mimeType: 'image/png',
|
||||
size: 123,
|
||||
url: 'data:image/png;base64,abc',
|
||||
type: 'image',
|
||||
uploadedBy: 'user-1',
|
||||
});
|
||||
|
||||
expect(media.url.value).toBe('data:image/png;base64,abc');
|
||||
});
|
||||
|
||||
it('accepts root-relative path for url', () => {
|
||||
const media = Media.create({
|
||||
id: 'media-1',
|
||||
filename: 'avatar.png',
|
||||
originalName: 'avatar.png',
|
||||
mimeType: 'image/png',
|
||||
size: 123,
|
||||
url: '/images/avatar.png',
|
||||
type: 'image',
|
||||
uploadedBy: 'user-1',
|
||||
});
|
||||
|
||||
expect(media.url.value).toBe('/images/avatar.png');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user