test apps api

This commit is contained in:
2025-12-23 23:14:51 +01:00
parent 16cd572c63
commit efcdbd17f2
71 changed files with 3924 additions and 913 deletions

View File

@@ -1,4 +1,4 @@
import { Body, Controller, ForbiddenException, HttpCode, HttpStatus, InternalServerErrorException, NotFoundException, Param, Post } from '@nestjs/common';
import { Body, Controller, ForbiddenException, HttpCode, HttpStatus, Inject, InternalServerErrorException, NotFoundException, Param, Post } from '@nestjs/common';
import { ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
import { ProtestsService } from './ProtestsService';
import { ReviewProtestCommandDTO } from '../race/dtos/ReviewProtestCommandDTO';
@@ -7,7 +7,7 @@ import type { ReviewProtestResponseDTO } from './presenters/ReviewProtestPresent
@ApiTags('protests')
@Controller('protests')
export class ProtestsController {
constructor(private readonly protestsService: ProtestsService) {}
constructor(@Inject(ProtestsService) private readonly protestsService: ProtestsService) {}
@Post(':protestId/review')
@HttpCode(HttpStatus.OK)

View File

@@ -1,5 +1,4 @@
import { Provider } from '@nestjs/common';
import { ProtestsService } from './ProtestsService';
// Import core interfaces
import type { Logger } from '@core/shared/application/Logger';
@@ -24,7 +23,6 @@ export const LOGGER_TOKEN = 'Logger';
export const REVIEW_PROTEST_PRESENTER_TOKEN = 'ReviewProtestPresenter';
export const ProtestsProviders: Provider[] = [
ProtestsService, // Provide the service itself
{
provide: PROTEST_REPOSITORY_TOKEN,
useFactory: (logger: Logger) => new InMemoryProtestRepository(logger),

View File

@@ -4,34 +4,23 @@ import type { Logger } from '@core/shared/application/Logger';
import type {
ReviewProtestUseCase,
ReviewProtestApplicationError,
ReviewProtestResult,
} from '@core/racing/application/use-cases/ReviewProtestUseCase';
import { ProtestsService } from './ProtestsService';
import type { ReviewProtestPresenter } from './presenters/ReviewProtestPresenter';
import type { ReviewProtestResponseDTO } from './presenters/ReviewProtestPresenter';
import { ReviewProtestPresenter, type ReviewProtestResponseDTO } from './presenters/ReviewProtestPresenter';
describe('ProtestsService', () => {
let service: ProtestsService;
let executeMock: MockedFunction<ReviewProtestUseCase['execute']>;
let logger: Logger;
let presenter: ReviewProtestPresenter;
beforeEach(() => {
executeMock = vi.fn();
const reviewProtestUseCase = { execute: executeMock } as unknown as ReviewProtestUseCase;
const reviewProtestPresenter = {
reset: vi.fn(),
setCommand: vi.fn(),
present: vi.fn(),
presentError: vi.fn(),
getResponseModel: vi.fn(),
get responseModel() {
return {
success: true,
protestId: 'test',
stewardId: 'test',
decision: 'uphold' as const,
};
},
} as unknown as ReviewProtestPresenter;
presenter = new ReviewProtestPresenter();
logger = {
debug: vi.fn(),
info: vi.fn(),
@@ -39,7 +28,7 @@ describe('ProtestsService', () => {
error: vi.fn(),
} as unknown as Logger;
service = new ProtestsService(reviewProtestUseCase, reviewProtestPresenter, logger);
service = new ProtestsService(reviewProtestUseCase, presenter, logger);
});
const baseCommand = {
@@ -50,10 +39,14 @@ describe('ProtestsService', () => {
};
it('returns DTO with success model on success', async () => {
executeMock.mockResolvedValue(Result.ok(undefined));
executeMock.mockImplementation(async (command) => {
presenter.present({ protestId: command.protestId } as ReviewProtestResult);
return Result.ok(undefined);
});
const dto = await service.reviewProtest(baseCommand);
expect(presenter.getResponseModel()).not.toBeNull();
expect(executeMock).toHaveBeenCalledWith(baseCommand);
expect(dto).toEqual<ReviewProtestResponseDTO>({
success: true,
@@ -69,7 +62,10 @@ describe('ProtestsService', () => {
details: { message: 'Protest not found' },
};
executeMock.mockResolvedValue(Result.err<void, ReviewProtestApplicationError>(error));
executeMock.mockImplementation(async () => {
presenter.presentError(error);
return Result.err<void, ReviewProtestApplicationError>(error);
});
const dto = await service.reviewProtest(baseCommand);
@@ -86,7 +82,10 @@ describe('ProtestsService', () => {
details: { message: 'Race not found for protest' },
};
executeMock.mockResolvedValue(Result.err<void, ReviewProtestApplicationError>(error));
executeMock.mockImplementation(async () => {
presenter.presentError(error);
return Result.err<void, ReviewProtestApplicationError>(error);
});
const dto = await service.reviewProtest(baseCommand);
@@ -103,7 +102,10 @@ describe('ProtestsService', () => {
details: { message: 'Steward is not authorized to review this protest' },
};
executeMock.mockResolvedValue(Result.err<void, ReviewProtestApplicationError>(error));
executeMock.mockImplementation(async () => {
presenter.presentError(error);
return Result.err<void, ReviewProtestApplicationError>(error);
});
const dto = await service.reviewProtest(baseCommand);
@@ -121,7 +123,10 @@ describe('ProtestsService', () => {
details: { message: 'Failed to review protest' },
};
executeMock.mockResolvedValue(Result.err<void, ReviewProtestApplicationError>(error));
executeMock.mockImplementation(async () => {
presenter.presentError(error);
return Result.err<void, ReviewProtestApplicationError>(error);
});
const dto = await service.reviewProtest(baseCommand);