view models
This commit is contained in:
121
apps/website/lib/services/media/AvatarService.test.ts
Normal file
121
apps/website/lib/services/media/AvatarService.test.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { AvatarService } from './AvatarService';
|
||||
import { MediaApiClient } from '../../api/media/MediaApiClient';
|
||||
import { RequestAvatarGenerationViewModel } from '../../view-models/RequestAvatarGenerationViewModel';
|
||||
import { AvatarViewModel } from '../../view-models/AvatarViewModel';
|
||||
import { UpdateAvatarViewModel } from '../../view-models/UpdateAvatarViewModel';
|
||||
|
||||
describe('AvatarService', () => {
|
||||
let mockApiClient: Mocked<MediaApiClient>;
|
||||
let service: AvatarService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockApiClient = {
|
||||
requestAvatarGeneration: vi.fn(),
|
||||
getAvatar: vi.fn(),
|
||||
updateAvatar: vi.fn(),
|
||||
} as Mocked<MediaApiClient>;
|
||||
|
||||
service = new AvatarService(mockApiClient);
|
||||
});
|
||||
|
||||
describe('requestAvatarGeneration', () => {
|
||||
it('should call apiClient.requestAvatarGeneration with correct input and return view model', async () => {
|
||||
const input = {
|
||||
userId: 'user-123',
|
||||
facePhotoData: 'base64data',
|
||||
suitColor: 'red' as const,
|
||||
};
|
||||
|
||||
const expectedOutput = { success: true, avatarUrl: 'https://example.com/avatar.jpg' };
|
||||
mockApiClient.requestAvatarGeneration.mockResolvedValue(expectedOutput);
|
||||
|
||||
const result = await service.requestAvatarGeneration(input);
|
||||
|
||||
expect(mockApiClient.requestAvatarGeneration).toHaveBeenCalledWith(input);
|
||||
expect(result).toBeInstanceOf(RequestAvatarGenerationViewModel);
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.avatarUrl).toBe('https://example.com/avatar.jpg');
|
||||
});
|
||||
|
||||
it('should handle error response', async () => {
|
||||
const input = {
|
||||
userId: 'user-123',
|
||||
facePhotoData: 'base64data',
|
||||
suitColor: 'blue' as const,
|
||||
};
|
||||
|
||||
const expectedOutput = { success: false, error: 'Generation failed' };
|
||||
mockApiClient.requestAvatarGeneration.mockResolvedValue(expectedOutput);
|
||||
|
||||
const result = await service.requestAvatarGeneration(input);
|
||||
|
||||
expect(result).toBeInstanceOf(RequestAvatarGenerationViewModel);
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toBe('Generation failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAvatar', () => {
|
||||
it('should call apiClient.getAvatar with driverId and return view model', async () => {
|
||||
const driverId = 'driver-123';
|
||||
|
||||
const expectedOutput = { driverId: 'driver-123', avatarUrl: 'https://example.com/avatar.jpg' };
|
||||
mockApiClient.getAvatar.mockResolvedValue(expectedOutput);
|
||||
|
||||
const result = await service.getAvatar(driverId);
|
||||
|
||||
expect(mockApiClient.getAvatar).toHaveBeenCalledWith(driverId);
|
||||
expect(result).toBeInstanceOf(AvatarViewModel);
|
||||
expect(result.driverId).toBe('driver-123');
|
||||
expect(result.avatarUrl).toBe('https://example.com/avatar.jpg');
|
||||
expect(result.hasAvatar).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle driver without avatar', async () => {
|
||||
const driverId = 'driver-456';
|
||||
|
||||
const expectedOutput = { driverId: 'driver-456' };
|
||||
mockApiClient.getAvatar.mockResolvedValue(expectedOutput);
|
||||
|
||||
const result = await service.getAvatar(driverId);
|
||||
|
||||
expect(result).toBeInstanceOf(AvatarViewModel);
|
||||
expect(result.driverId).toBe('driver-456');
|
||||
expect(result.avatarUrl).toBeUndefined();
|
||||
expect(result.hasAvatar).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateAvatar', () => {
|
||||
it('should call apiClient.updateAvatar with correct input and return view model', async () => {
|
||||
const input = { driverId: 'driver-123', avatarUrl: 'https://example.com/new-avatar.jpg' };
|
||||
|
||||
const expectedOutput = { success: true };
|
||||
mockApiClient.updateAvatar.mockResolvedValue(expectedOutput);
|
||||
|
||||
const result = await service.updateAvatar(input);
|
||||
|
||||
expect(mockApiClient.updateAvatar).toHaveBeenCalledWith(input);
|
||||
expect(result).toBeInstanceOf(UpdateAvatarViewModel);
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.isSuccessful).toBe(true);
|
||||
expect(result.hasError).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle error response', async () => {
|
||||
const input = { driverId: 'driver-123', avatarUrl: 'https://example.com/new-avatar.jpg' };
|
||||
|
||||
const expectedOutput = { success: false, error: 'Update failed' };
|
||||
mockApiClient.updateAvatar.mockResolvedValue(expectedOutput);
|
||||
|
||||
const result = await service.updateAvatar(input);
|
||||
|
||||
expect(result).toBeInstanceOf(UpdateAvatarViewModel);
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toBe('Update failed');
|
||||
expect(result.isSuccessful).toBe(false);
|
||||
expect(result.hasError).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,13 +1,11 @@
|
||||
import { RequestAvatarGenerationInputDTO } from '@/lib/types/generated/RequestAvatarGenerationInputDTO';
|
||||
import { AvatarViewModel } from '@/lib/view-models/AvatarViewModel';
|
||||
import { RequestAvatarGenerationViewModel } from '@/lib/view-models/RequestAvatarGenerationViewModel';
|
||||
import { UpdateAvatarViewModel } from '@/lib/view-models/UpdateAvatarViewModel';
|
||||
import type { MediaApiClient } from '../../api/media/MediaApiClient';
|
||||
import type { RequestAvatarGenerationInputDTO } from '../../types/generated';
|
||||
|
||||
// TODO: Move these types to apps/website/lib/types/generated when available
|
||||
type UpdateAvatarInputDto = { driverId: string; avatarUrl: string };
|
||||
import {
|
||||
RequestAvatarGenerationViewModel,
|
||||
AvatarViewModel,
|
||||
UpdateAvatarViewModel
|
||||
} from '../../view-models';
|
||||
|
||||
/**
|
||||
* Avatar Service
|
||||
|
||||
159
apps/website/lib/services/media/MediaService.test.ts
Normal file
159
apps/website/lib/services/media/MediaService.test.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { MediaService } from './MediaService';
|
||||
import { MediaApiClient } from '../../api/media/MediaApiClient';
|
||||
import { MediaViewModel } from '../../view-models/MediaViewModel';
|
||||
import { UploadMediaViewModel } from '../../view-models/UploadMediaViewModel';
|
||||
import { DeleteMediaViewModel } from '../../view-models/DeleteMediaViewModel';
|
||||
|
||||
describe('MediaService', () => {
|
||||
let mockApiClient: Mocked<MediaApiClient>;
|
||||
let service: MediaService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockApiClient = {
|
||||
uploadMedia: vi.fn(),
|
||||
getMedia: vi.fn(),
|
||||
deleteMedia: vi.fn(),
|
||||
} as Mocked<MediaApiClient>;
|
||||
|
||||
service = new MediaService(mockApiClient);
|
||||
});
|
||||
|
||||
describe('uploadMedia', () => {
|
||||
it('should call apiClient.uploadMedia with correct input and return view model', async () => {
|
||||
const input = {
|
||||
file: new File(['test'], 'test.jpg', { type: 'image/jpeg' }),
|
||||
type: 'image',
|
||||
category: 'avatar' as const,
|
||||
};
|
||||
|
||||
const expectedOutput = { success: true, mediaId: 'media-123', url: 'https://example.com/media.jpg' };
|
||||
mockApiClient.uploadMedia.mockResolvedValue(expectedOutput);
|
||||
|
||||
const result = await service.uploadMedia(input);
|
||||
|
||||
expect(mockApiClient.uploadMedia).toHaveBeenCalledWith(input);
|
||||
expect(result).toBeInstanceOf(UploadMediaViewModel);
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.mediaId).toBe('media-123');
|
||||
expect(result.url).toBe('https://example.com/media.jpg');
|
||||
expect(result.isSuccessful).toBe(true);
|
||||
expect(result.hasError).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle error response', async () => {
|
||||
const input = {
|
||||
file: new File(['test'], 'test.jpg', { type: 'image/jpeg' }),
|
||||
type: 'image',
|
||||
};
|
||||
|
||||
const expectedOutput = { success: false, error: 'Upload failed' };
|
||||
mockApiClient.uploadMedia.mockResolvedValue(expectedOutput);
|
||||
|
||||
const result = await service.uploadMedia(input);
|
||||
|
||||
expect(result).toBeInstanceOf(UploadMediaViewModel);
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toBe('Upload failed');
|
||||
expect(result.isSuccessful).toBe(false);
|
||||
expect(result.hasError).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle upload without category', async () => {
|
||||
const input = {
|
||||
file: new File(['test'], 'test.jpg', { type: 'image/jpeg' }),
|
||||
type: 'image',
|
||||
};
|
||||
|
||||
const expectedOutput = { success: true, mediaId: 'media-456', url: 'https://example.com/media2.jpg' };
|
||||
mockApiClient.uploadMedia.mockResolvedValue(expectedOutput);
|
||||
|
||||
const result = await service.uploadMedia(input);
|
||||
|
||||
expect(mockApiClient.uploadMedia).toHaveBeenCalledWith(input);
|
||||
expect(result).toBeInstanceOf(UploadMediaViewModel);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMedia', () => {
|
||||
it('should call apiClient.getMedia with mediaId and return view model', async () => {
|
||||
const mediaId = 'media-123';
|
||||
|
||||
const expectedOutput = {
|
||||
id: 'media-123',
|
||||
url: 'https://example.com/image.jpg',
|
||||
type: 'image' as const,
|
||||
category: 'avatar' as const,
|
||||
uploadedAt: new Date('2023-01-15'),
|
||||
size: 2048000,
|
||||
};
|
||||
mockApiClient.getMedia.mockResolvedValue(expectedOutput);
|
||||
|
||||
const result = await service.getMedia(mediaId);
|
||||
|
||||
expect(mockApiClient.getMedia).toHaveBeenCalledWith(mediaId);
|
||||
expect(result).toBeInstanceOf(MediaViewModel);
|
||||
expect(result.id).toBe('media-123');
|
||||
expect(result.url).toBe('https://example.com/image.jpg');
|
||||
expect(result.type).toBe('image');
|
||||
expect(result.category).toBe('avatar');
|
||||
expect(result.uploadedAt).toEqual(new Date('2023-01-15'));
|
||||
expect(result.size).toBe(2048000);
|
||||
expect(result.formattedSize).toBe('2000.00 KB');
|
||||
});
|
||||
|
||||
it('should handle media without category and size', async () => {
|
||||
const mediaId = 'media-456';
|
||||
|
||||
const expectedOutput = {
|
||||
id: 'media-456',
|
||||
url: 'https://example.com/video.mp4',
|
||||
type: 'video' as const,
|
||||
uploadedAt: new Date('2023-02-20'),
|
||||
};
|
||||
mockApiClient.getMedia.mockResolvedValue(expectedOutput);
|
||||
|
||||
const result = await service.getMedia(mediaId);
|
||||
|
||||
expect(result).toBeInstanceOf(MediaViewModel);
|
||||
expect(result.id).toBe('media-456');
|
||||
expect(result.type).toBe('video');
|
||||
expect(result.category).toBeUndefined();
|
||||
expect(result.size).toBeUndefined();
|
||||
expect(result.formattedSize).toBe('Unknown');
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteMedia', () => {
|
||||
it('should call apiClient.deleteMedia with mediaId and return view model', async () => {
|
||||
const mediaId = 'media-123';
|
||||
|
||||
const expectedOutput = { success: true };
|
||||
mockApiClient.deleteMedia.mockResolvedValue(expectedOutput);
|
||||
|
||||
const result = await service.deleteMedia(mediaId);
|
||||
|
||||
expect(mockApiClient.deleteMedia).toHaveBeenCalledWith(mediaId);
|
||||
expect(result).toBeInstanceOf(DeleteMediaViewModel);
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.isSuccessful).toBe(true);
|
||||
expect(result.hasError).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle error response', async () => {
|
||||
const mediaId = 'media-456';
|
||||
|
||||
const expectedOutput = { success: false, error: 'Deletion failed' };
|
||||
mockApiClient.deleteMedia.mockResolvedValue(expectedOutput);
|
||||
|
||||
const result = await service.deleteMedia(mediaId);
|
||||
|
||||
expect(result).toBeInstanceOf(DeleteMediaViewModel);
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toBe('Deletion failed');
|
||||
expect(result.isSuccessful).toBe(false);
|
||||
expect(result.hasError).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,8 +1,10 @@
|
||||
import { DeleteMediaViewModel } from '@/lib/view-models/DeleteMediaViewModel';
|
||||
import { MediaViewModel } from '@/lib/view-models/MediaViewModel';
|
||||
import { UploadMediaViewModel } from '@/lib/view-models/UploadMediaViewModel';
|
||||
import type { MediaApiClient } from '../../api/media/MediaApiClient';
|
||||
import { MediaViewModel, UploadMediaViewModel, DeleteMediaViewModel } from '../../view-models';
|
||||
|
||||
// TODO: Move these types to apps/website/lib/types/generated when available
|
||||
type UploadMediaInputDto = { url: string; mediaType: string; entityType: string; entityId: string };
|
||||
type UploadMediaInputDto = { file: File; type: string; category?: string };
|
||||
|
||||
/**
|
||||
* Media Service
|
||||
|
||||
Reference in New Issue
Block a user