test apps api
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Controller, Post, Get, Delete, Put, Body, HttpStatus, Res, Param, UseInterceptors, UploadedFile } from '@nestjs/common';
|
||||
import { Controller, Post, Get, Delete, Put, Body, HttpStatus, Res, Param, UseInterceptors, UploadedFile, Inject } from '@nestjs/common';
|
||||
import { ApiTags, ApiResponse, ApiOperation, ApiParam, ApiConsumes } from '@nestjs/swagger';
|
||||
import type { Response } from 'express';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
@@ -21,7 +21,7 @@ type UpdateAvatarInput = UpdateAvatarInputDTO;
|
||||
@ApiTags('media')
|
||||
@Controller('media')
|
||||
export class MediaController {
|
||||
constructor(private readonly mediaService: MediaService) {}
|
||||
constructor(@Inject(MediaService) private readonly mediaService: MediaService) {}
|
||||
|
||||
@Post('avatar/generate')
|
||||
@ApiOperation({ summary: 'Request avatar generation' })
|
||||
|
||||
@@ -5,7 +5,7 @@ import { MediaProviders } from './MediaProviders';
|
||||
|
||||
@Module({
|
||||
controllers: [MediaController],
|
||||
providers: MediaProviders,
|
||||
providers: [MediaService, ...MediaProviders],
|
||||
exports: [MediaService],
|
||||
})
|
||||
export class MediaModule {}
|
||||
|
||||
@@ -34,30 +34,29 @@ import { DeleteMediaPresenter } from './presenters/DeleteMediaPresenter';
|
||||
import { GetAvatarPresenter } from './presenters/GetAvatarPresenter';
|
||||
import { UpdateAvatarPresenter } from './presenters/UpdateAvatarPresenter';
|
||||
|
||||
// Define injection tokens
|
||||
export const AVATAR_GENERATION_REPOSITORY_TOKEN = 'IAvatarGenerationRepository';
|
||||
export const MEDIA_REPOSITORY_TOKEN = 'IMediaRepository';
|
||||
export const AVATAR_REPOSITORY_TOKEN = 'IAvatarRepository';
|
||||
export const FACE_VALIDATION_PORT_TOKEN = 'FaceValidationPort';
|
||||
export const AVATAR_GENERATION_PORT_TOKEN = 'AvatarGenerationPort';
|
||||
export const MEDIA_STORAGE_PORT_TOKEN = 'MediaStoragePort';
|
||||
export const LOGGER_TOKEN = 'Logger';
|
||||
import {
|
||||
AVATAR_GENERATION_REPOSITORY_TOKEN,
|
||||
MEDIA_REPOSITORY_TOKEN,
|
||||
AVATAR_REPOSITORY_TOKEN,
|
||||
FACE_VALIDATION_PORT_TOKEN,
|
||||
AVATAR_GENERATION_PORT_TOKEN,
|
||||
MEDIA_STORAGE_PORT_TOKEN,
|
||||
LOGGER_TOKEN,
|
||||
REQUEST_AVATAR_GENERATION_USE_CASE_TOKEN,
|
||||
UPLOAD_MEDIA_USE_CASE_TOKEN,
|
||||
GET_MEDIA_USE_CASE_TOKEN,
|
||||
DELETE_MEDIA_USE_CASE_TOKEN,
|
||||
GET_AVATAR_USE_CASE_TOKEN,
|
||||
UPDATE_AVATAR_USE_CASE_TOKEN,
|
||||
REQUEST_AVATAR_GENERATION_OUTPUT_PORT_TOKEN,
|
||||
UPLOAD_MEDIA_OUTPUT_PORT_TOKEN,
|
||||
GET_MEDIA_OUTPUT_PORT_TOKEN,
|
||||
DELETE_MEDIA_OUTPUT_PORT_TOKEN,
|
||||
GET_AVATAR_OUTPUT_PORT_TOKEN,
|
||||
UPDATE_AVATAR_OUTPUT_PORT_TOKEN,
|
||||
} from './MediaTokens';
|
||||
|
||||
// Use case tokens
|
||||
export const REQUEST_AVATAR_GENERATION_USE_CASE_TOKEN = 'RequestAvatarGenerationUseCase';
|
||||
export const UPLOAD_MEDIA_USE_CASE_TOKEN = 'UploadMediaUseCase';
|
||||
export const GET_MEDIA_USE_CASE_TOKEN = 'GetMediaUseCase';
|
||||
export const DELETE_MEDIA_USE_CASE_TOKEN = 'DeleteMediaUseCase';
|
||||
export const GET_AVATAR_USE_CASE_TOKEN = 'GetAvatarUseCase';
|
||||
export const UPDATE_AVATAR_USE_CASE_TOKEN = 'UpdateAvatarUseCase';
|
||||
|
||||
// Output port tokens
|
||||
export const REQUEST_AVATAR_GENERATION_OUTPUT_PORT_TOKEN = 'RequestAvatarGenerationOutputPort';
|
||||
export const UPLOAD_MEDIA_OUTPUT_PORT_TOKEN = 'UploadMediaOutputPort';
|
||||
export const GET_MEDIA_OUTPUT_PORT_TOKEN = 'GetMediaOutputPort';
|
||||
export const DELETE_MEDIA_OUTPUT_PORT_TOKEN = 'DeleteMediaOutputPort';
|
||||
export const GET_AVATAR_OUTPUT_PORT_TOKEN = 'GetAvatarOutputPort';
|
||||
export const UPDATE_AVATAR_OUTPUT_PORT_TOKEN = 'UpdateAvatarOutputPort';
|
||||
export * from './MediaTokens';
|
||||
|
||||
import type { AvatarGenerationRequest } from '@core/media/domain/entities/AvatarGenerationRequest';
|
||||
import type { Media } from '@core/media/domain/entities/Media';
|
||||
@@ -133,7 +132,6 @@ class MockLogger implements Logger {
|
||||
}
|
||||
|
||||
export const MediaProviders: Provider[] = [
|
||||
MediaService, // Provide the service itself
|
||||
RequestAvatarGenerationPresenter,
|
||||
UploadMediaPresenter,
|
||||
GetMediaPresenter,
|
||||
|
||||
510
apps/api/src/domain/media/MediaService.test.ts
Normal file
510
apps/api/src/domain/media/MediaService.test.ts
Normal file
@@ -0,0 +1,510 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import { MediaService } from './MediaService';
|
||||
|
||||
describe('MediaService', () => {
|
||||
const logger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() };
|
||||
|
||||
it('requestAvatarGeneration returns presenter response on success', async () => {
|
||||
const requestAvatarGenerationPresenter = {
|
||||
responseModel: { success: true, requestId: 'r1', avatarUrls: ['u1'], errorMessage: '' },
|
||||
};
|
||||
|
||||
const requestAvatarGenerationUseCase = {
|
||||
execute: vi.fn(async () => Result.ok(undefined)),
|
||||
};
|
||||
|
||||
const service = new MediaService(
|
||||
requestAvatarGenerationUseCase as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
requestAvatarGenerationPresenter as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(
|
||||
service.requestAvatarGeneration({ userId: 'u1', facePhotoData: {} as any, suitColor: 'red' as any }),
|
||||
).resolves.toEqual({ success: true, requestId: 'r1', avatarUrls: ['u1'], errorMessage: '' });
|
||||
|
||||
expect(requestAvatarGenerationUseCase.execute).toHaveBeenCalledWith({
|
||||
userId: 'u1',
|
||||
facePhotoData: {} as any,
|
||||
suitColor: 'red',
|
||||
});
|
||||
});
|
||||
|
||||
it('requestAvatarGeneration returns failure DTO on error', async () => {
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'fail' } })) } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: { success: true } } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(
|
||||
service.requestAvatarGeneration({ userId: 'u1', facePhotoData: {} as any, suitColor: 'red' as any }),
|
||||
).resolves.toEqual({
|
||||
success: false,
|
||||
requestId: '',
|
||||
avatarUrls: [],
|
||||
errorMessage: 'fail',
|
||||
});
|
||||
});
|
||||
|
||||
it('uploadMedia returns presenter response on success', async () => {
|
||||
const uploadMediaPresenter = { responseModel: { success: true, mediaId: 'm1' } };
|
||||
const uploadMediaUseCase = { execute: vi.fn(async () => Result.ok(undefined)) };
|
||||
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
uploadMediaUseCase as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
uploadMediaPresenter as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(
|
||||
service.uploadMedia({ file: {} as any, userId: 'u1', metadata: { a: 1 } } as any),
|
||||
).resolves.toEqual({
|
||||
success: true,
|
||||
mediaId: 'm1',
|
||||
});
|
||||
|
||||
expect(uploadMediaUseCase.execute).toHaveBeenCalledWith({
|
||||
file: {} as any,
|
||||
uploadedBy: 'u1',
|
||||
metadata: { a: 1 },
|
||||
});
|
||||
});
|
||||
|
||||
it('uploadMedia uses empty uploadedBy when userId missing', async () => {
|
||||
const uploadMediaUseCase = { execute: vi.fn(async () => Result.ok(undefined)) };
|
||||
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
uploadMediaUseCase as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: { success: true, mediaId: 'm1' } } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(service.uploadMedia({ file: {} as any } as any)).resolves.toEqual({ success: true, mediaId: 'm1' });
|
||||
expect(uploadMediaUseCase.execute).toHaveBeenCalledWith({ file: {} as any, uploadedBy: '', metadata: {} });
|
||||
});
|
||||
|
||||
it('uploadMedia returns failure DTO on error', async () => {
|
||||
const uploadMediaUseCase = {
|
||||
execute: vi.fn(async () => Result.err({ code: 'UPLOAD_FAILED', details: { message: 'nope' } })),
|
||||
};
|
||||
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
uploadMediaUseCase as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: { success: true } } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(service.uploadMedia({ file: {} as any, userId: 'u1' } as any)).resolves.toEqual({
|
||||
success: false,
|
||||
error: 'nope',
|
||||
});
|
||||
});
|
||||
|
||||
it('getMedia returns presenter response on success', async () => {
|
||||
const getMediaUseCase = { execute: vi.fn(async () => Result.ok(undefined)) };
|
||||
const getMediaPresenter = { responseModel: { mediaId: 'm1' } };
|
||||
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
getMediaUseCase as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
getMediaPresenter as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(service.getMedia('m1')).resolves.toEqual({ mediaId: 'm1' });
|
||||
expect(getMediaUseCase.execute).toHaveBeenCalledWith({ mediaId: 'm1' });
|
||||
});
|
||||
|
||||
it('getMedia returns null on MEDIA_NOT_FOUND', async () => {
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn(async () => Result.err({ code: 'MEDIA_NOT_FOUND', details: { message: 'n/a' } })) } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(service.getMedia('m1')).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it('getMedia throws on non-not-found error', async () => {
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'boom' } })) } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(service.getMedia('m1')).rejects.toThrow('boom');
|
||||
});
|
||||
|
||||
it('deleteMedia returns presenter response on success', async () => {
|
||||
const deleteMediaUseCase = { execute: vi.fn(async () => Result.ok(undefined)) };
|
||||
const deleteMediaPresenter = { responseModel: { success: true } };
|
||||
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
deleteMediaUseCase as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
deleteMediaPresenter as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(service.deleteMedia('m1')).resolves.toEqual({ success: true });
|
||||
expect(deleteMediaUseCase.execute).toHaveBeenCalledWith({ mediaId: 'm1' });
|
||||
});
|
||||
|
||||
it('deleteMedia returns failure DTO on error', async () => {
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'nope' } })) } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: { success: true } } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(service.deleteMedia('m1')).resolves.toEqual({ success: false, error: 'nope' });
|
||||
});
|
||||
|
||||
it('getAvatar returns presenter response on success', async () => {
|
||||
const getAvatarUseCase = { execute: vi.fn(async () => Result.ok(undefined)) };
|
||||
const getAvatarPresenter = { responseModel: { avatarUrl: 'u1' } };
|
||||
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
getAvatarUseCase as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
getAvatarPresenter as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(service.getAvatar('d1')).resolves.toEqual({ avatarUrl: 'u1' });
|
||||
expect(getAvatarUseCase.execute).toHaveBeenCalledWith({ driverId: 'd1' });
|
||||
});
|
||||
|
||||
it('getAvatar returns null on AVATAR_NOT_FOUND', async () => {
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn(async () => Result.err({ code: 'AVATAR_NOT_FOUND', details: { message: 'n/a' } })) } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(service.getAvatar('d1')).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it('getAvatar throws on non-not-found error', async () => {
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'boom' } })) } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(service.getAvatar('d1')).rejects.toThrow('boom');
|
||||
});
|
||||
|
||||
it('updateAvatar returns presenter response on success', async () => {
|
||||
const updateAvatarUseCase = { execute: vi.fn(async () => Result.ok(undefined)) };
|
||||
const updateAvatarPresenter = { responseModel: { success: true } };
|
||||
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
updateAvatarUseCase as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
updateAvatarPresenter as any,
|
||||
);
|
||||
|
||||
await expect(service.updateAvatar('d1', { avatarUrl: 'u1' } as any)).resolves.toEqual({ success: true });
|
||||
expect(updateAvatarUseCase.execute).toHaveBeenCalledWith({ driverId: 'd1', mediaUrl: 'u1' });
|
||||
});
|
||||
|
||||
it('updateAvatar returns failure DTO on error', async () => {
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'nope' } })) } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: { success: true } } as any,
|
||||
);
|
||||
|
||||
await expect(service.updateAvatar('d1', { avatarUrl: 'u' } as any)).resolves.toEqual({
|
||||
success: false,
|
||||
error: 'nope',
|
||||
});
|
||||
});
|
||||
|
||||
it('requestAvatarGeneration uses fallback errorMessage when no details.message', async () => {
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR' } as any)) } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: { success: true } } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(
|
||||
service.requestAvatarGeneration({ userId: 'u1', facePhotoData: {} as any, suitColor: 'red' as any }),
|
||||
).resolves.toEqual({
|
||||
success: false,
|
||||
requestId: '',
|
||||
avatarUrls: [],
|
||||
errorMessage: 'Failed to request avatar generation',
|
||||
});
|
||||
});
|
||||
|
||||
it('uploadMedia uses fallback error when no details.message', async () => {
|
||||
const uploadMediaUseCase = {
|
||||
execute: vi.fn(async () => Result.err({ code: 'UPLOAD_FAILED' } as any)),
|
||||
};
|
||||
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
uploadMediaUseCase as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: { success: true } } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(service.uploadMedia({ file: {} as any, userId: 'u1' } as any)).resolves.toEqual({
|
||||
success: false,
|
||||
error: 'Upload failed',
|
||||
});
|
||||
});
|
||||
|
||||
it('getMedia throws fallback message when no details.message and not not-found', async () => {
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR' } as any)) } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(service.getMedia('m1')).rejects.toThrow('Failed to get media');
|
||||
});
|
||||
|
||||
it('deleteMedia uses fallback message when no details.message', async () => {
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR' } as any)) } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: { success: true } } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(service.deleteMedia('m1')).resolves.toEqual({ success: false, error: 'Failed to delete media' });
|
||||
});
|
||||
|
||||
it('getAvatar throws fallback message when no details.message and not not-found', async () => {
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR' } as any)) } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
);
|
||||
|
||||
await expect(service.getAvatar('d1')).rejects.toThrow('Failed to get avatar');
|
||||
});
|
||||
|
||||
it('updateAvatar uses fallback message when no details.message', async () => {
|
||||
const service = new MediaService(
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn() } as any,
|
||||
{ execute: vi.fn(async () => Result.err({ code: 'REPOSITORY_ERROR' } as any)) } as any,
|
||||
logger as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: {} } as any,
|
||||
{ responseModel: { success: true } } as any,
|
||||
);
|
||||
|
||||
await expect(service.updateAvatar('d1', { avatarUrl: 'u' } as any)).resolves.toEqual({
|
||||
success: false,
|
||||
error: 'Failed to update avatar',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -31,7 +31,6 @@ import { DeleteMediaPresenter } from './presenters/DeleteMediaPresenter';
|
||||
import { GetAvatarPresenter } from './presenters/GetAvatarPresenter';
|
||||
import { UpdateAvatarPresenter } from './presenters/UpdateAvatarPresenter';
|
||||
|
||||
// Tokens
|
||||
import {
|
||||
REQUEST_AVATAR_GENERATION_USE_CASE_TOKEN,
|
||||
UPLOAD_MEDIA_USE_CASE_TOKEN,
|
||||
@@ -40,7 +39,7 @@ import {
|
||||
GET_AVATAR_USE_CASE_TOKEN,
|
||||
UPDATE_AVATAR_USE_CASE_TOKEN,
|
||||
LOGGER_TOKEN,
|
||||
} from './MediaProviders';
|
||||
} from './MediaTokens';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
|
||||
@Injectable()
|
||||
|
||||
21
apps/api/src/domain/media/MediaTokens.ts
Normal file
21
apps/api/src/domain/media/MediaTokens.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export const AVATAR_GENERATION_REPOSITORY_TOKEN = 'IAvatarGenerationRepository';
|
||||
export const MEDIA_REPOSITORY_TOKEN = 'IMediaRepository';
|
||||
export const AVATAR_REPOSITORY_TOKEN = 'IAvatarRepository';
|
||||
export const FACE_VALIDATION_PORT_TOKEN = 'FaceValidationPort';
|
||||
export const AVATAR_GENERATION_PORT_TOKEN = 'AvatarGenerationPort';
|
||||
export const MEDIA_STORAGE_PORT_TOKEN = 'MediaStoragePort';
|
||||
export const LOGGER_TOKEN = 'Logger';
|
||||
|
||||
export const REQUEST_AVATAR_GENERATION_USE_CASE_TOKEN = 'RequestAvatarGenerationUseCase';
|
||||
export const UPLOAD_MEDIA_USE_CASE_TOKEN = 'UploadMediaUseCase';
|
||||
export const GET_MEDIA_USE_CASE_TOKEN = 'GetMediaUseCase';
|
||||
export const DELETE_MEDIA_USE_CASE_TOKEN = 'DeleteMediaUseCase';
|
||||
export const GET_AVATAR_USE_CASE_TOKEN = 'GetAvatarUseCase';
|
||||
export const UPDATE_AVATAR_USE_CASE_TOKEN = 'UpdateAvatarUseCase';
|
||||
|
||||
export const REQUEST_AVATAR_GENERATION_OUTPUT_PORT_TOKEN = 'RequestAvatarGenerationOutputPort';
|
||||
export const UPLOAD_MEDIA_OUTPUT_PORT_TOKEN = 'UploadMediaOutputPort';
|
||||
export const GET_MEDIA_OUTPUT_PORT_TOKEN = 'GetMediaOutputPort';
|
||||
export const DELETE_MEDIA_OUTPUT_PORT_TOKEN = 'DeleteMediaOutputPort';
|
||||
export const GET_AVATAR_OUTPUT_PORT_TOKEN = 'GetAvatarOutputPort';
|
||||
export const UPDATE_AVATAR_OUTPUT_PORT_TOKEN = 'UpdateAvatarOutputPort';
|
||||
Reference in New Issue
Block a user