fix: harden portfolio DTO validation for quantity and date fields

This commit is contained in:
Sergey Krylov 2026-06-25 21:51:08 +03:00
parent db28f46481
commit af0c93fd34
3 changed files with 49 additions and 4 deletions

View File

@ -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;

View 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);
});
});

View File

@ -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;