mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
refactor!: migrate class-validator to zod (#26597)
This commit is contained in:
@@ -1,92 +1,45 @@
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
import { validate } from 'class-validator';
|
||||
import { DateTime } from 'luxon';
|
||||
import { IsDateStringFormat, IsNotSiblingOf, MaxDateString, Optional } from 'src/validation';
|
||||
import { describe } from 'vitest';
|
||||
import { IsNotSiblingOf } from 'src/validation';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import z from 'zod';
|
||||
|
||||
describe('Validation', () => {
|
||||
describe('MaxDateString', () => {
|
||||
const maxDate = DateTime.fromISO('2000-01-01', { zone: 'utc' });
|
||||
|
||||
class MyDto {
|
||||
@MaxDateString(maxDate)
|
||||
date!: string;
|
||||
}
|
||||
|
||||
it('passes when date is before maxDate', async () => {
|
||||
const dto = plainToInstance(MyDto, { date: '1999-12-31' });
|
||||
await expect(validate(dto)).resolves.toHaveLength(0);
|
||||
});
|
||||
|
||||
it('passes when date is equal to maxDate', async () => {
|
||||
const dto = plainToInstance(MyDto, { date: '2000-01-01' });
|
||||
await expect(validate(dto)).resolves.toHaveLength(0);
|
||||
});
|
||||
|
||||
it('fails when date is after maxDate', async () => {
|
||||
const dto = plainToInstance(MyDto, { date: '2010-01-01' });
|
||||
await expect(validate(dto)).resolves.toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('IsDateStringFormat', () => {
|
||||
class MyDto {
|
||||
@IsDateStringFormat('yyyy-MM-dd')
|
||||
date!: string;
|
||||
}
|
||||
|
||||
it('passes when date is valid', async () => {
|
||||
const dto = plainToInstance(MyDto, { date: '1999-12-31' });
|
||||
await expect(validate(dto)).resolves.toHaveLength(0);
|
||||
});
|
||||
|
||||
it('fails when date has invalid format', async () => {
|
||||
const dto = plainToInstance(MyDto, { date: '2000-01-01T00:00:00Z' });
|
||||
await expect(validate(dto)).resolves.toHaveLength(1);
|
||||
});
|
||||
|
||||
it('fails when empty string', async () => {
|
||||
const dto = plainToInstance(MyDto, { date: '' });
|
||||
await expect(validate(dto)).resolves.toHaveLength(1);
|
||||
});
|
||||
|
||||
it('fails when undefined', async () => {
|
||||
const dto = plainToInstance(MyDto, {});
|
||||
await expect(validate(dto)).resolves.toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('IsNotSiblingOf', () => {
|
||||
class MyDto {
|
||||
@IsNotSiblingOf(['attribute2'])
|
||||
@Optional()
|
||||
attribute1?: string;
|
||||
|
||||
@IsNotSiblingOf(['attribute1', 'attribute3'])
|
||||
@Optional()
|
||||
attribute2?: string;
|
||||
|
||||
@IsNotSiblingOf(['attribute2'])
|
||||
@Optional()
|
||||
attribute3?: string;
|
||||
|
||||
@Optional()
|
||||
unrelatedAttribute?: string;
|
||||
}
|
||||
|
||||
it('passes when only one attribute is present', async () => {
|
||||
const dto = plainToInstance(MyDto, { attribute1: 'value1', unrelatedAttribute: 'value2' });
|
||||
await expect(validate(dto)).resolves.toHaveLength(0);
|
||||
const MySchemaBase = z.object({
|
||||
attribute1: z.string().optional(),
|
||||
attribute2: z.string().optional(),
|
||||
attribute3: z.string().optional(),
|
||||
unrelatedAttribute: z.string().optional(),
|
||||
});
|
||||
|
||||
it('fails when colliding attributes are present', async () => {
|
||||
const dto = plainToInstance(MyDto, { attribute1: 'value1', attribute2: 'value2' });
|
||||
await expect(validate(dto)).resolves.toHaveLength(2);
|
||||
const MySchema = MySchemaBase.pipe(IsNotSiblingOf(MySchemaBase, 'attribute1', ['attribute2']))
|
||||
.pipe(IsNotSiblingOf(MySchemaBase, 'attribute2', ['attribute1', 'attribute3']))
|
||||
.pipe(IsNotSiblingOf(MySchemaBase, 'attribute3', ['attribute2']));
|
||||
|
||||
it('passes when only one attribute is present', () => {
|
||||
const result = MySchema.safeParse({
|
||||
attribute1: 'value1',
|
||||
unrelatedAttribute: 'value2',
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('passes when no colliding attributes are present', async () => {
|
||||
const dto = plainToInstance(MyDto, { attribute1: 'value1', attribute3: 'value2' });
|
||||
await expect(validate(dto)).resolves.toHaveLength(0);
|
||||
it('fails when colliding attributes are present', () => {
|
||||
const result = MySchema.safeParse({
|
||||
attribute1: 'value1',
|
||||
attribute2: 'value2',
|
||||
});
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues[0].message).toBe('attribute1 cannot exist alongside attribute2');
|
||||
}
|
||||
});
|
||||
|
||||
it('passes when no colliding attributes are present', () => {
|
||||
const result = MySchema.safeParse({
|
||||
attribute1: 'value1',
|
||||
attribute3: 'value2',
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user