codex/backend-architecture-refactor #48
@ -8,6 +8,7 @@ import {
|
||||
IsIn,
|
||||
MaxLength,
|
||||
MinLength,
|
||||
IsDateString,
|
||||
} from 'class-validator';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
@ -31,7 +32,7 @@ export class AddPositionDto {
|
||||
|
||||
@ApiProperty({ example: 10 })
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Min(1)
|
||||
quantity!: number;
|
||||
|
||||
@ApiPropertyOptional({ example: 250.5 })
|
||||
@ -41,7 +42,7 @@ export class AddPositionDto {
|
||||
buyPrice?: number;
|
||||
|
||||
@ApiPropertyOptional({ example: '2026-06-01' })
|
||||
@IsString()
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
buyDate?: string;
|
||||
|
||||
|
||||
43
apps/backend/src/modules/portfolio/dto/position.dto.spec.ts
Normal file
43
apps/backend/src/modules/portfolio/dto/position.dto.spec.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import 'reflect-metadata';
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
import { validate } from 'class-validator';
|
||||
import { AddPositionDto } from './add-position.dto';
|
||||
import { UpdatePositionDto } from './update-position.dto';
|
||||
|
||||
describe('position DTO validation', () => {
|
||||
const validateDto = async <T extends object>(cls: new () => T, payload: Record<string, unknown>) =>
|
||||
validate(plainToInstance(cls, payload));
|
||||
|
||||
it('rejects zero quantity when adding a position', async () => {
|
||||
const errors = await validateDto(AddPositionDto, { secid: 'SBER', quantity: 0 });
|
||||
|
||||
expect(errors.some((error) => error.property === 'quantity')).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects zero quantity when updating a position', async () => {
|
||||
const errors = await validateDto(UpdatePositionDto, { quantity: 0 });
|
||||
|
||||
expect(errors.some((error) => error.property === 'quantity')).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects invalid buyDate values', async () => {
|
||||
const addErrors = await validateDto(AddPositionDto, {
|
||||
secid: 'SBER',
|
||||
quantity: 1,
|
||||
buyDate: 'not-a-date',
|
||||
});
|
||||
const updateErrors = await validateDto(UpdatePositionDto, { buyDate: 'not-a-date' });
|
||||
|
||||
expect(addErrors.some((error) => error.property === 'buyDate')).toBe(true);
|
||||
expect(updateErrors.some((error) => error.property === 'buyDate')).toBe(true);
|
||||
});
|
||||
|
||||
it('accepts valid position payloads', async () => {
|
||||
await expect(
|
||||
validateDto(AddPositionDto, { secid: 'SBER', quantity: 1, buyDate: '2026-06-01' }),
|
||||
).resolves.toHaveLength(0);
|
||||
await expect(
|
||||
validateDto(UpdatePositionDto, { quantity: 2, buyDate: '2026-06-15' }),
|
||||
).resolves.toHaveLength(0);
|
||||
});
|
||||
});
|
||||
@ -7,6 +7,7 @@ import {
|
||||
IsArray,
|
||||
IsIn,
|
||||
MaxLength,
|
||||
IsDateString,
|
||||
} from 'class-validator';
|
||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
@ -24,7 +25,7 @@ const TAGS = [
|
||||
export class UpdatePositionDto {
|
||||
@ApiPropertyOptional({ example: 15 })
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Min(1)
|
||||
@IsOptional()
|
||||
quantity?: number;
|
||||
|
||||
@ -35,7 +36,7 @@ export class UpdatePositionDto {
|
||||
buyPrice?: number;
|
||||
|
||||
@ApiPropertyOptional({ example: '2026-06-15' })
|
||||
@IsString()
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
buyDate?: string;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user