Entity validator tests

This commit is contained in:
Alexandre Bodin 2023-09-05 19:47:01 +02:00
parent 06923d2a49
commit c4c570fe3b
25 changed files with 699 additions and 820 deletions

View File

@ -1,54 +1,52 @@
'use strict';
const strapiUtils = require('@strapi/utils');
const {
errors: { YupValidationError },
} = require('@strapi/utils');
const validators = require('../validators');
import strapiUtils, { errors } from '@strapi/utils';
import validators from '../validators';
import type { Schema } from '../../../types';
describe('BigInteger validator', () => {
const fakeModel: Schema.ContentType = {
modelType: 'contentType',
kind: 'collectionType',
modelName: 'test-model',
globalId: 'test-model',
uid: 'api::test.test-uid',
info: {
displayName: 'Test model',
singularName: 'test-model',
pluralName: 'test-models',
},
options: {},
attributes: {
attrBigIntegerUnique: { type: 'biginteger', unique: true },
},
};
describe('unique', () => {
const fakeFindOne = jest.fn();
global.strapi = {
db: {
query: jest.fn(() => ({
findOne: fakeFindOne,
})),
},
};
query: jest.fn(() => ({
findOne: fakeFindOne,
})),
} as any;
afterEach(() => {
jest.clearAllMocks();
fakeFindOne.mockReset();
});
const fakeModel = {
kind: 'contentType',
modelName: 'test-model',
uid: 'test-uid',
options: {},
attributes: {
attrBigIntegerUnique: { type: 'biginteger', unique: true },
},
};
test('it does not validate the unique constraint if the attribute is not set as unique', async () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.biginteger(
{
attr: { type: 'biginteger' },
model: fakeModel,
updatedAttribute: {
name: 'attrBigIntegerUnique',
value: 1,
},
entity: null,
validators.biginteger({
attr: { type: 'biginteger' },
model: fakeModel,
updatedAttribute: {
name: 'attrBigIntegerUnique',
value: 1,
},
{ isDraft: false }
)
entity: null,
})
);
await validator(1);
@ -61,18 +59,15 @@ describe('BigInteger validator', () => {
const validator = strapiUtils.validateYupSchema(
validators
.biginteger(
{
attr: { type: 'biginteger', unique: true },
model: fakeModel,
updatedAttribute: {
name: 'attrBigIntegerUnique',
value: null,
},
entity: null,
.biginteger({
attr: { type: 'biginteger', unique: true },
model: fakeModel,
updatedAttribute: {
name: 'attrBigIntegerUnique',
value: null,
},
{ isDraft: false }
)
entity: null,
})
.nullable()
);
@ -85,18 +80,15 @@ describe('BigInteger validator', () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.biginteger(
{
attr: { type: 'biginteger', unique: true },
model: fakeModel,
updatedAttribute: {
name: 'attrBigIntegerUnique',
value: 1,
},
entity: null,
validators.biginteger({
attr: { type: 'biginteger', unique: true },
model: fakeModel,
updatedAttribute: {
name: 'attrBigIntegerUnique',
value: 1,
},
{ isDraft: false }
)
entity: null,
})
);
expect(await validator(1)).toBe(1);
@ -107,24 +99,21 @@ describe('BigInteger validator', () => {
fakeFindOne.mockResolvedValueOnce({ attrBigIntegerUnique: 2 });
const validator = strapiUtils.validateYupSchema(
validators.biginteger(
{
attr: { type: 'biginteger', unique: true },
model: fakeModel,
updatedAttribute: {
name: 'attrBigIntegerUnique',
value: 2,
},
entity: null,
validators.biginteger({
attr: { type: 'biginteger', unique: true },
model: fakeModel,
updatedAttribute: {
name: 'attrBigIntegerUnique',
value: 2,
},
{ isDraft: false }
)
entity: null,
})
);
try {
await validator(2);
} catch (err) {
expect(err).toBeInstanceOf(YupValidationError);
expect(err).toBeInstanceOf(errors.YupValidationError);
}
});
@ -132,18 +121,15 @@ describe('BigInteger validator', () => {
fakeFindOne.mockResolvedValueOnce({ attrBigIntegerUnique: 3 });
const validator = strapiUtils.validateYupSchema(
validators.biginteger(
{
attr: { type: 'biginteger', unique: true },
model: fakeModel,
updatedAttribute: {
name: 'attrBigIntegerUnique',
value: 3,
},
entity: { id: 1, attrBigIntegerUnique: 3 },
validators.biginteger({
attr: { type: 'biginteger', unique: true },
model: fakeModel,
updatedAttribute: {
name: 'attrBigIntegerUnique',
value: 3,
},
{ isDraft: false }
)
entity: { id: 1, attrBigIntegerUnique: 3 },
})
);
expect(await validator(3)).toBe(3);
@ -153,18 +139,15 @@ describe('BigInteger validator', () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.biginteger(
{
attr: { type: 'biginteger', unique: true },
model: fakeModel,
updatedAttribute: {
name: 'attrBigIntegerUnique',
value: 4,
},
entity: null,
validators.biginteger({
attr: { type: 'biginteger', unique: true },
model: fakeModel,
updatedAttribute: {
name: 'attrBigIntegerUnique',
value: 4,
},
{ isDraft: false }
)
entity: null,
})
);
await validator(4);
@ -179,18 +162,15 @@ describe('BigInteger validator', () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.biginteger(
{
attr: { type: 'biginteger', unique: true },
model: fakeModel,
updatedAttribute: {
name: 'attrBigIntegerUnique',
value: 5,
},
entity: { id: 1, attrBigIntegerUnique: 42 },
validators.biginteger({
attr: { type: 'biginteger', unique: true },
model: fakeModel,
updatedAttribute: {
name: 'attrBigIntegerUnique',
value: 5,
},
{ isDraft: false }
)
entity: { id: 1, attrBigIntegerUnique: 42 },
})
);
await validator(5);
@ -201,19 +181,4 @@ describe('BigInteger validator', () => {
});
});
});
describe('min', () => {
test('it does not validate the min constraint if the attribute min is not a number', async () => {
const validator = strapiUtils.validateYupSchema(
validators.biginteger(
{
attr: { type: 'biginteger', minLength: '123' },
},
{ isDraft: false }
)
);
expect(await validator(1)).toBe(1);
});
});
});

View File

@ -1,51 +1,49 @@
'use strict';
const strapiUtils = require('@strapi/utils');
const {
errors: { YupValidationError },
} = require('@strapi/utils');
const validators = require('../validators');
import strapiUtils, { errors } from '@strapi/utils';
import validators from '../validators';
import type { Schema } from '../../../types';
describe('Date validator', () => {
const fakeModel: Schema.ContentType = {
modelType: 'contentType',
kind: 'collectionType',
modelName: 'test-model',
globalId: 'test-model',
uid: 'api::test.test-uid',
info: {
displayName: 'Test model',
singularName: 'test-model',
pluralName: 'test-models',
},
options: {},
attributes: {
attrDateUnique: { type: 'date', unique: true },
},
};
describe('unique', () => {
const fakeFindOne = jest.fn();
global.strapi = {
db: {
query: jest.fn(() => ({
findOne: fakeFindOne,
})),
},
};
query: jest.fn(() => ({
findOne: fakeFindOne,
})),
} as any;
afterEach(() => {
jest.clearAllMocks();
fakeFindOne.mockReset();
});
const fakeModel = {
kind: 'contentType',
modelName: 'test-model',
uid: 'test-uid',
options: {},
attributes: {
attrDateUnique: { type: 'date', unique: true },
},
};
test('it does not validates the unique constraint if the attribute is not set as unique', async () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.date(
{
attr: { type: 'date' },
model: fakeModel,
updatedAttribute: { name: 'attrDateUnique', value: '2021-11-29' },
entity: null,
},
{ isDraft: false }
)
validators.date({
attr: { type: 'date' },
model: fakeModel,
updatedAttribute: { name: 'attrDateUnique', value: '2021-11-29' },
entity: null,
})
);
await validator('2021-11-29');
@ -58,15 +56,12 @@ describe('Date validator', () => {
const validator = strapiUtils.validateYupSchema(
validators
.date(
{
attr: { type: 'date', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateUnique', value: null },
entity: null,
},
{ isDraft: false }
)
.date({
attr: { type: 'date', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateUnique', value: null },
entity: null,
})
.nullable()
);
@ -78,15 +73,12 @@ describe('Date validator', () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.date(
{
attr: { type: 'date', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateUnique', value: '2021-11-29' },
entity: null,
},
{ isDraft: false }
)
validators.date({
attr: { type: 'date', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateUnique', value: '2021-11-29' },
entity: null,
})
);
expect(await validator('2021-11-29')).toBe('2021-11-29');
@ -97,21 +89,18 @@ describe('Date validator', () => {
fakeFindOne.mockResolvedValueOnce({ attrDateUnique: '2021-11-29' });
const validator = strapiUtils.validateYupSchema(
validators.date(
{
attr: { type: 'date', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateUnique', value: '2021-11-29' },
entity: null,
},
{ isDraft: false }
)
validators.date({
attr: { type: 'date', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateUnique', value: '2021-11-29' },
entity: null,
})
);
try {
await validator('2021-11-29');
} catch (err) {
expect(err).toBeInstanceOf(YupValidationError);
expect(err).toBeInstanceOf(errors.YupValidationError);
}
});
@ -119,15 +108,12 @@ describe('Date validator', () => {
fakeFindOne.mockResolvedValueOnce({ attrDateUnique: '2021-11-29' });
const validator = strapiUtils.validateYupSchema(
validators.date(
{
attr: { type: 'date', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateUnique', value: '2021-11-29' },
entity: { id: 1, attrDateUnique: '2021-11-29' },
},
{ isDraft: false }
)
validators.date({
attr: { type: 'date', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateUnique', value: '2021-11-29' },
entity: { id: 1, attrDateUnique: '2021-11-29' },
})
);
expect(await validator('2021-11-29')).toBe('2021-11-29');
@ -137,15 +123,12 @@ describe('Date validator', () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.date(
{
attr: { type: 'date', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateUnique', value: '2021-11-29' },
entity: null,
},
{ isDraft: false }
)
validators.date({
attr: { type: 'date', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateUnique', value: '2021-11-29' },
entity: null,
})
);
await validator('2021-11-29');
@ -160,15 +143,12 @@ describe('Date validator', () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.date(
{
attr: { type: 'date', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateUnique', value: '2021-11-29' },
entity: { id: 1, attrDateUnique: '2021-12-15' },
},
{ isDraft: false }
)
validators.date({
attr: { type: 'date', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateUnique', value: '2021-11-29' },
entity: { id: 1, attrDateUnique: '2021-12-15' },
})
);
await validator('2021-11-29');

View File

@ -1,51 +1,49 @@
'use strict';
const strapiUtils = require('@strapi/utils');
const {
errors: { YupValidationError },
} = require('@strapi/utils');
const validators = require('../validators');
import strapiUtils, { errors } from '@strapi/utils';
import validators from '../validators';
import type { Schema } from '../../../types';
describe('Datetime validator', () => {
const fakeModel: Schema.ContentType = {
modelType: 'contentType',
kind: 'collectionType',
modelName: 'test-model',
globalId: 'test-model',
uid: 'api::test.test-uid',
info: {
displayName: 'Test model',
singularName: 'test-model',
pluralName: 'test-models',
},
options: {},
attributes: {
attrDateTimeUnique: { type: 'datetime', unique: true },
},
};
describe('unique', () => {
const fakeFindOne = jest.fn();
global.strapi = {
db: {
query: jest.fn(() => ({
findOne: fakeFindOne,
})),
},
};
query: jest.fn(() => ({
findOne: fakeFindOne,
})),
} as any;
afterEach(() => {
jest.clearAllMocks();
fakeFindOne.mockReset();
});
const fakeModel = {
kind: 'contentType',
modelName: 'test-model',
uid: 'test-uid',
options: {},
attributes: {
attrDateTimeUnique: { type: 'datetime', unique: true },
},
};
test('it does not validates the unique constraint if the attribute is not set as unique', async () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.datetime(
{
attr: { type: 'datetime' },
model: fakeModel,
updatedAttribute: { name: 'attrDateTimeUnique', value: '2021-11-29T00:00:00.000Z' },
entity: { id: 1, attrDateTimeUnique: '2021-11-29T00:00:00.000Z' },
},
{ isDraft: false }
)
validators.datetime({
attr: { type: 'datetime' },
model: fakeModel,
updatedAttribute: { name: 'attrDateTimeUnique', value: '2021-11-29T00:00:00.000Z' },
entity: { id: 1, attrDateTimeUnique: '2021-11-29T00:00:00.000Z' },
})
);
await validator('2021-11-29T00:00:00.000Z');
@ -58,15 +56,12 @@ describe('Datetime validator', () => {
const validator = strapiUtils.validateYupSchema(
validators
.datetime(
{
attr: { type: 'datetime', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateTimeUnique', value: null },
entity: null,
},
{ isDraft: false }
)
.datetime({
attr: { type: 'datetime', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateTimeUnique', value: null },
entity: null,
})
.nullable()
);
@ -78,15 +73,12 @@ describe('Datetime validator', () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.datetime(
{
attr: { type: 'datetime', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateTimeUnique', value: '2021-11-29T00:00:00.000Z' },
entity: null,
},
{ isDraft: false }
)
validators.datetime({
attr: { type: 'datetime', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateTimeUnique', value: '2021-11-29T00:00:00.000Z' },
entity: null,
})
);
expect(await validator('2021-11-29T00:00:00.000Z')).toBe('2021-11-29T00:00:00.000Z');
@ -97,21 +89,18 @@ describe('Datetime validator', () => {
fakeFindOne.mockResolvedValueOnce({ attrDateTimeUnique: '2021-11-29T00:00:00.000Z' });
const validator = strapiUtils.validateYupSchema(
validators.datetime(
{
attr: { type: 'datetime', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateTimeUnique', value: '2021-11-29T00:00:00.000Z' },
entity: null,
},
{ isDraft: false }
)
validators.datetime({
attr: { type: 'datetime', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateTimeUnique', value: '2021-11-29T00:00:00.000Z' },
entity: null,
})
);
try {
await validator('2021-11-29T00:00:00.000Z');
} catch (err) {
expect(err).toBeInstanceOf(YupValidationError);
expect(err).toBeInstanceOf(errors.YupValidationError);
}
});
@ -119,15 +108,12 @@ describe('Datetime validator', () => {
fakeFindOne.mockResolvedValueOnce({ attrDateTimeUnique: '2021-11-29T00:00:00.000Z' });
const validator = strapiUtils.validateYupSchema(
validators.datetime(
{
attr: { type: 'datetime', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateTimeUnique', value: '2021-11-29T00:00:00.000Z' },
entity: { id: 1, attrDateTimeUnique: '2021-11-29T00:00:00.000Z' },
},
{ isDraft: false }
)
validators.datetime({
attr: { type: 'datetime', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateTimeUnique', value: '2021-11-29T00:00:00.000Z' },
entity: { id: 1, attrDateTimeUnique: '2021-11-29T00:00:00.000Z' },
})
);
expect(await validator('2021-11-29T00:00:00.000Z')).toBe('2021-11-29T00:00:00.000Z');
@ -137,15 +123,12 @@ describe('Datetime validator', () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.datetime(
{
attr: { type: 'datetime', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateTimeUnique', value: '2021-11-29T00:00:00.000Z' },
entity: null,
},
{ isDraft: false }
)
validators.datetime({
attr: { type: 'datetime', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateTimeUnique', value: '2021-11-29T00:00:00.000Z' },
entity: null,
})
);
await validator('2021-11-29T00:00:00.000Z');
@ -160,15 +143,12 @@ describe('Datetime validator', () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.datetime(
{
attr: { type: 'datetime', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateTimeUnique', value: '2021-11-29T00:00:00.000Z' },
entity: { id: 1, attrDateTimeUnique: '2021-12-25T00:00:00.000Z' },
},
{ isDraft: false }
)
validators.datetime({
attr: { type: 'datetime', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrDateTimeUnique', value: '2021-11-29T00:00:00.000Z' },
entity: { id: 1, attrDateTimeUnique: '2021-12-25T00:00:00.000Z' },
})
);
await validator('2021-11-29T00:00:00.000Z');

View File

@ -1,56 +0,0 @@
'use strict';
const strapiUtils = require('@strapi/utils');
const {
errors: { YupValidationError },
} = require('@strapi/utils');
const validators = require('../validators');
describe('Email validator', () => {
describe('email', () => {
test('it fails the validation if the string is not a valid email', async () => {
expect.assertions(1);
const validator = strapiUtils.validateYupSchema(
validators.email(
{
attr: { type: 'email' },
},
{ isDraft: false }
)
);
try {
await validator('invalid-email');
} catch (err) {
expect(err).toBeInstanceOf(YupValidationError);
}
});
test('it validates the email if it is valid', async () => {
const validator = strapiUtils.validateYupSchema(
validators.email(
{
attr: { type: 'email' },
},
{ isDraft: false }
)
);
expect(await validator('valid@email.com')).toBe('valid@email.com');
});
test('it validates non-empty email required field', async () => {
const validator = strapiUtils.validateYupSchema(
validators.email({ attr: { type: 'email' } }, { isDraft: false })
);
try {
await validator('');
} catch (err) {
expect(err).toBeInstanceOf(YupValidationError);
expect(err.message).toBe('this cannot be empty');
}
});
});
});

View File

@ -0,0 +1,87 @@
import strapiUtils, { errors } from '@strapi/utils';
import validators from '../validators';
import type { Schema } from '../../../types';
describe('Email validator', () => {
const fakeModel: Schema.ContentType = {
modelType: 'contentType',
kind: 'collectionType',
modelName: 'test-model',
globalId: 'test-model',
uid: 'api::test.test-uid',
info: {
displayName: 'Test model',
singularName: 'test-model',
pluralName: 'test-models',
},
options: {},
attributes: {
attrEmail: { type: 'email' },
},
};
describe('email', () => {
test('it fails the validation if the string is not a valid email', async () => {
expect.assertions(1);
const validator = strapiUtils.validateYupSchema(
validators.email(
{
attr: { type: 'email' },
model: fakeModel,
updatedAttribute: { name: 'attrEmail', value: 1 },
entity: null,
},
{ isDraft: false }
)
);
try {
await validator('invalid-email');
} catch (err) {
expect(err).toBeInstanceOf(errors.YupValidationError);
}
});
test('it validates the email if it is valid', async () => {
const validator = strapiUtils.validateYupSchema(
validators.email(
{
attr: { type: 'email' },
model: fakeModel,
updatedAttribute: { name: 'attrEmail', value: 1 },
entity: null,
},
{ isDraft: false }
)
);
expect(await validator('valid@email.com')).toBe('valid@email.com');
});
test('it validates non-empty email required field', async () => {
const validator = strapiUtils.validateYupSchema(
validators.email(
{
attr: { type: 'email' },
model: fakeModel,
updatedAttribute: { name: 'attrEmail', value: 1 },
entity: null,
},
{ isDraft: false }
)
);
expect.hasAssertions();
try {
await validator('');
} catch (err) {
if (err instanceof Error) {
expect(err).toBeInstanceOf(errors.YupValidationError);
expect(err.message).toBe('this cannot be empty');
}
}
});
});
});

View File

@ -1,43 +0,0 @@
'use strict';
const strapiUtils = require('@strapi/utils');
const {
errors: { YupValidationError },
} = require('@strapi/utils');
const validators = require('../validators');
describe('Enumeration validator', () => {
describe('oneOf', () => {
test('it fails the validation if the value is not part of the allowed values', async () => {
expect.assertions(1);
const validator = strapiUtils.validateYupSchema(
validators.enumeration(
{
attr: { type: 'enum', enum: ['strapi', 'headless'] },
},
{ isDraft: false }
)
);
try {
await validator('invalid-vlue');
} catch (err) {
expect(err).toBeInstanceOf(YupValidationError);
}
});
test('it validates the value if it is part of the allowed values', async () => {
const validator = strapiUtils.validateYupSchema(
validators.enumeration(
{
attr: { type: 'enumeration', enum: ['strapi', 'headless'] },
},
{ isDraft: false }
)
);
expect(await validator('strapi')).toBe('strapi');
});
});
});

View File

@ -0,0 +1,56 @@
import strapiUtils, { errors } from '@strapi/utils';
import validators from '../validators';
import type { Schema } from '../../../types';
describe('Enumeration validator', () => {
const fakeModel: Schema.ContentType = {
modelType: 'contentType',
kind: 'collectionType',
modelName: 'test-model',
globalId: 'test-model',
uid: 'api::test.test-uid',
info: {
displayName: 'Test model',
singularName: 'test-model',
pluralName: 'test-models',
},
options: {},
attributes: {
attrEnumUnique: { type: 'float', unique: true },
},
};
describe('oneOf', () => {
test('it fails the validation if the value is not part of the allowed values', async () => {
expect.assertions(1);
const validator = strapiUtils.validateYupSchema(
validators.enumeration({
attr: { type: 'enumeration', enum: ['strapi', 'headless'] },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 1 },
entity: null,
})
);
try {
await validator('invalid-vlue');
} catch (err) {
expect(err).toBeInstanceOf(errors.YupValidationError);
}
});
test('it validates the value if it is part of the allowed values', async () => {
const validator = strapiUtils.validateYupSchema(
validators.enumeration({
attr: { type: 'enumeration', enum: ['strapi', 'headless'] },
model: fakeModel,
updatedAttribute: { name: 'attrEnumUnique', value: 1 },
entity: null,
})
);
expect(await validator('strapi')).toBe('strapi');
});
});
});

View File

@ -1,51 +1,49 @@
'use strict';
const strapiUtils = require('@strapi/utils');
const {
errors: { YupValidationError },
} = require('@strapi/utils');
const validators = require('../validators');
import strapiUtils, { errors } from '@strapi/utils';
import validators from '../validators';
import type { Schema } from '../../../types';
describe('Float validator', () => {
const fakeModel: Schema.ContentType = {
modelType: 'contentType',
kind: 'collectionType',
modelName: 'test-model',
globalId: 'test-model',
uid: 'api::test.test-uid',
info: {
displayName: 'Test model',
singularName: 'test-model',
pluralName: 'test-models',
},
options: {},
attributes: {
attrFloatUnique: { type: 'float', unique: true },
},
};
describe('unique', () => {
const fakeFindOne = jest.fn();
global.strapi = {
db: {
query: jest.fn(() => ({
findOne: fakeFindOne,
})),
},
};
query: jest.fn(() => ({
findOne: fakeFindOne,
})),
} as any;
afterEach(() => {
jest.clearAllMocks();
fakeFindOne.mockReset();
});
const fakeModel = {
kind: 'contentType',
uid: 'test-uid',
modelName: 'test-model',
options: {},
attributes: {
attrFloatUnique: { type: 'float', unique: true },
},
};
test('it does not validates the unique constraint if the attribute is not set as unique', async () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.float(
{
attr: { type: 'float' },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 1 },
entity: null,
},
{ isDraft: false }
)
validators.float({
attr: { type: 'float' },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 1 },
entity: null,
})
);
await validator(1);
@ -58,15 +56,12 @@ describe('Float validator', () => {
const validator = strapiUtils.validateYupSchema(
validators
.float(
{
attr: { type: 'float', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: null },
entity: null,
},
{ isDraft: false }
)
.float({
attr: { type: 'float', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: null },
entity: null,
})
.nullable()
);
@ -79,15 +74,12 @@ describe('Float validator', () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.float(
{
attr: { type: 'float', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 2 },
entity: null,
},
{ isDraft: false }
)
validators.float({
attr: { type: 'float', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 2 },
entity: null,
})
);
expect(await validator(1)).toBe(1);
@ -98,21 +90,18 @@ describe('Float validator', () => {
fakeFindOne.mockResolvedValueOnce({ attrFloatUnique: 2 });
const validator = strapiUtils.validateYupSchema(
validators.float(
{
attr: { type: 'float', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 2 },
entity: null,
},
{ isDraft: false }
)
validators.float({
attr: { type: 'float', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 2 },
entity: null,
})
);
try {
await validator(2);
} catch (err) {
expect(err).toBeInstanceOf(YupValidationError);
expect(err).toBeInstanceOf(errors.YupValidationError);
}
});
@ -120,15 +109,12 @@ describe('Float validator', () => {
fakeFindOne.mockResolvedValueOnce({ attrFloatUnique: 3 });
const validator = strapiUtils.validateYupSchema(
validators.float(
{
attr: { type: 'float', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 3 },
entity: { id: 1, attrFloatUnique: 3 },
},
{ isDraft: false }
)
validators.float({
attr: { type: 'float', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 3 },
entity: { id: 1, attrFloatUnique: 3 },
})
);
expect(await validator(3)).toBe(3);
@ -138,15 +124,12 @@ describe('Float validator', () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.float(
{
attr: { type: 'float', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 4 },
entity: null,
},
{ isDraft: false }
)
validators.float({
attr: { type: 'float', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 4 },
entity: null,
})
);
await validator(4);
@ -161,15 +144,12 @@ describe('Float validator', () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.float(
{
attr: { type: 'float', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 5 },
entity: { id: 1, attrFloatUnique: 42 },
},
{ isDraft: false }
)
validators.float({
attr: { type: 'float', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 5 },
entity: { id: 1, attrFloatUnique: 42 },
})
);
await validator(5);
@ -182,46 +162,33 @@ describe('Float validator', () => {
});
describe('min', () => {
test('it does not validates the min constraint if the attribute min is not a number', async () => {
const validator = strapiUtils.validateYupSchema(
validators.float(
{
attr: { type: 'float', minLength: '123' },
},
{ isDraft: false }
)
);
expect(await validator(1)).toBe(1);
});
test('it fails the validation if the float is lower than the define min', async () => {
expect.assertions(1);
const validator = strapiUtils.validateYupSchema(
validators.float(
{
attr: { type: 'float', min: 3 },
},
{ isDraft: false }
)
validators.float({
attr: { type: 'float', min: 3 },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 5 },
entity: { id: 1, attrFloatUnique: 42 },
})
);
try {
await validator(1);
} catch (err) {
expect(err).toBeInstanceOf(YupValidationError);
expect(err).toBeInstanceOf(errors.YupValidationError);
}
});
test('it validates the min constraint if the float is higher than the define min', async () => {
const validator = strapiUtils.validateYupSchema(
validators.float(
{
attr: { type: 'float', min: 3 },
},
{ isDraft: false }
)
validators.float({
attr: { type: 'float', min: 3 },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 5 },
entity: { id: 1, attrFloatUnique: 42 },
})
);
expect(await validator(4)).toBe(4);
@ -229,46 +196,33 @@ describe('Float validator', () => {
});
describe('max', () => {
test('it does not validates the max constraint if the attribute max is not an float', async () => {
const validator = strapiUtils.validateYupSchema(
validators.float(
{
attr: { type: 'float', maxLength: '123' },
},
{ isDraft: false }
)
);
expect(await validator(1)).toBe(1);
});
test('it fails the validation if the number is float than the define max', async () => {
expect.assertions(1);
const validator = strapiUtils.validateYupSchema(
validators.float(
{
attr: { type: 'float', max: 3 },
},
{ isDraft: false }
)
validators.float({
attr: { type: 'float', max: 3 },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 5 },
entity: { id: 1, attrFloatUnique: 42 },
})
);
try {
await validator(4);
} catch (err) {
expect(err).toBeInstanceOf(YupValidationError);
expect(err).toBeInstanceOf(errors.YupValidationError);
}
});
test('it validates the max constraint if the float is lower than the define max', async () => {
const validator = strapiUtils.validateYupSchema(
validators.float(
{
attr: { type: 'float', max: 3 },
},
{ isDraft: false }
)
validators.float({
attr: { type: 'float', max: 3 },
model: fakeModel,
updatedAttribute: { name: 'attrFloatUnique', value: 5 },
entity: { id: 1, attrFloatUnique: 42 },
})
);
expect(await validator(2)).toBe(2);

View File

@ -1,18 +1,31 @@
'use strict';
const entityValidator = require('..');
import entityValidator from '..';
import type { Schema } from '../../../types';
describe('Entity validator', () => {
const modelBase = { uid: 'api::test.test' };
const modelBase: Schema.ContentType = {
modelType: 'contentType',
uid: 'api::test.test',
kind: 'collectionType',
modelName: 'test',
globalId: 'test',
info: {
displayName: 'Test',
singularName: 'test',
pluralName: 'tests',
},
options: {},
attributes: {},
};
describe('Published input', () => {
describe('General Errors', () => {
let model;
let model: Schema.ContentType;
global.strapi = {
errors: {
badRequest: jest.fn(),
},
getModel: () => model,
};
} as any;
it('Throws a badRequest error on invalid input', async () => {
model = {
@ -144,6 +157,7 @@ describe('Entity validator', () => {
...modelBase,
attributes: {
uuid: {
// @ts-ignore
type: 'uuid',
},
},
@ -167,9 +181,9 @@ describe('Entity validator', () => {
badRequest: jest.fn(),
},
getModel: () => model,
};
} as any;
const model = {
const model: Schema.ContentType = {
...modelBase,
attributes: {
title: {
@ -203,7 +217,7 @@ describe('Entity validator', () => {
});
test('Throws on max length not respected', async () => {
const model = {
const model: Schema.ContentType = {
...modelBase,
attributes: {
title: {
@ -237,7 +251,7 @@ describe('Entity validator', () => {
});
test('Allows empty strings even when required', async () => {
const model = {
const model: Schema.ContentType = {
...modelBase,
attributes: {
title: {
@ -256,7 +270,7 @@ describe('Entity validator', () => {
});
test('Assign default values', async () => {
const model = {
const model: Schema.ContentType = {
...modelBase,
attributes: {
title: {
@ -274,7 +288,7 @@ describe('Entity validator', () => {
default: '2020-04-01T04:00:00.000Z',
},
testJSON: {
type: 'date',
type: 'json',
required: true,
default: {
foo: 1,
@ -296,7 +310,7 @@ describe('Entity validator', () => {
});
test("Don't assign default value if empty string", async () => {
const model = {
const model: Schema.ContentType = {
...modelBase,
attributes: {
title: {
@ -332,9 +346,9 @@ describe('Entity validator', () => {
badRequest: jest.fn(),
},
getModel: () => model,
};
} as any;
const model = {
const model: Schema.ContentType = {
...modelBase,
attributes: {
title: {
@ -367,7 +381,7 @@ describe('Entity validator', () => {
});
it('Returns data on valid input', async () => {
const model = {
const model: Schema.ContentType = {
...modelBase,
attributes: {
title: {
@ -385,7 +399,7 @@ describe('Entity validator', () => {
});
it('Returns casted data when possible', async () => {
const model = {
const model: Schema.ContentType = {
...modelBase,
attributes: {
title: {
@ -409,7 +423,7 @@ describe('Entity validator', () => {
});
test('Does not throws on required not respected', async () => {
const model = {
const model: Schema.ContentType = {
...modelBase,
attributes: {
title: {
@ -433,10 +447,11 @@ describe('Entity validator', () => {
});
it('Supports custom field types', async () => {
const model = {
const model: Schema.ContentType = {
...modelBase,
attributes: {
uuid: {
// @ts-ignore
type: 'uuid',
},
},
@ -455,7 +470,7 @@ describe('Entity validator', () => {
describe('String validator', () => {
test('Does not throws on min length not respected', async () => {
const model = {
const model: Schema.ContentType = {
...modelBase,
attributes: {
title: {
@ -470,7 +485,7 @@ describe('Entity validator', () => {
badRequest: jest.fn(),
},
getModel: () => model,
};
} as any;
const input = { title: 'tooSmall' };
@ -481,7 +496,7 @@ describe('Entity validator', () => {
});
test('Throws on max length not respected', async () => {
const model = {
const model: Schema.ContentType = {
...modelBase,
attributes: {
title: {
@ -515,7 +530,7 @@ describe('Entity validator', () => {
});
test('Allows empty strings even when required', async () => {
const model = {
const model: Schema.ContentType = {
...modelBase,
attributes: {
title: {
@ -533,7 +548,7 @@ describe('Entity validator', () => {
});
test('Assign default values', async () => {
const model = {
const model: Schema.ContentType = {
...modelBase,
attributes: {
title: {
@ -551,7 +566,7 @@ describe('Entity validator', () => {
default: '2020-04-01T04:00:00.000Z',
},
testJSON: {
type: 'date',
type: 'json',
required: true,
default: {
foo: 1,
@ -575,7 +590,7 @@ describe('Entity validator', () => {
});
test("Don't assign default value if empty string", async () => {
const model = {
const model: Schema.ContentType = {
...modelBase,
attributes: {
title: {

View File

@ -1,51 +1,49 @@
'use strict';
const strapiUtils = require('@strapi/utils');
const {
errors: { YupValidationError },
} = require('@strapi/utils');
const validators = require('../validators');
import strapiUtils, { errors } from '@strapi/utils';
import validators from '../validators';
import type { Schema } from '../../../types';
describe('Integer validator', () => {
const fakeModel: Schema.ContentType = {
modelType: 'contentType',
kind: 'collectionType',
modelName: 'test-model',
globalId: 'test-model',
uid: 'api::test.test-uid',
info: {
displayName: 'Test model',
singularName: 'test-model',
pluralName: 'test-models',
},
options: {},
attributes: {
attrIntegerUnique: { type: 'integer', unique: true },
},
};
describe('unique', () => {
const fakeFindOne = jest.fn();
global.strapi = {
db: {
query: jest.fn(() => ({
findOne: fakeFindOne,
})),
},
};
query: jest.fn(() => ({
findOne: fakeFindOne,
})),
} as any;
afterEach(() => {
jest.clearAllMocks();
fakeFindOne.mockReset();
});
const fakeModel = {
kind: 'contentType',
uid: 'test-uid',
modelName: 'test-model',
options: {},
attributes: {
attrIntegerUnique: { type: 'integer', unique: true },
},
};
test('it does not validates the unique constraint if the attribute is not set as unique', async () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.integer(
{
attr: { type: 'integer' },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 1 },
entity: null,
},
{ isDraft: false }
)
validators.integer({
attr: { type: 'integer' },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 1 },
entity: null,
})
);
await validator(1);
@ -58,15 +56,12 @@ describe('Integer validator', () => {
const validator = strapiUtils.validateYupSchema(
validators
.integer(
{
attr: { type: 'integer', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: null },
entity: null,
},
{ isDraft: false }
)
.integer({
attr: { type: 'integer', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: null },
entity: null,
})
.nullable()
);
@ -79,15 +74,12 @@ describe('Integer validator', () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.integer(
{
attr: { type: 'integer', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 2 },
entity: null,
},
{ isDraft: false }
)
validators.integer({
attr: { type: 'integer', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 2 },
entity: null,
})
);
expect(await validator(1)).toBe(1);
@ -98,21 +90,18 @@ describe('Integer validator', () => {
fakeFindOne.mockResolvedValueOnce({ attrIntegerUnique: 2 });
const validator = strapiUtils.validateYupSchema(
validators.integer(
{
attr: { type: 'integer', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 2 },
entity: null,
},
{ isDraft: false }
)
validators.integer({
attr: { type: 'integer', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 2 },
entity: null,
})
);
try {
await validator(2);
} catch (err) {
expect(err).toBeInstanceOf(YupValidationError);
expect(err).toBeInstanceOf(errors.YupValidationError);
}
});
@ -120,15 +109,12 @@ describe('Integer validator', () => {
fakeFindOne.mockResolvedValueOnce({ attrIntegerUnique: 3 });
const validator = strapiUtils.validateYupSchema(
validators.integer(
{
attr: { type: 'integer', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 3 },
entity: { id: 1, attrIntegerUnique: 3 },
},
{ isDraft: false }
)
validators.integer({
attr: { type: 'integer', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 3 },
entity: { id: 1, attrIntegerUnique: 3 },
})
);
expect(await validator(3)).toBe(3);
@ -138,15 +124,12 @@ describe('Integer validator', () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.integer(
{
attr: { type: 'integer', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 4 },
entity: null,
},
{ isDraft: false }
)
validators.integer({
attr: { type: 'integer', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 4 },
entity: null,
})
);
await validator(4);
@ -161,15 +144,12 @@ describe('Integer validator', () => {
fakeFindOne.mockResolvedValueOnce(null);
const validator = strapiUtils.validateYupSchema(
validators.integer(
{
attr: { type: 'integer', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 5 },
entity: { id: 1, attrIntegerUnique: 42 },
},
{ isDraft: false }
)
validators.integer({
attr: { type: 'integer', unique: true },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 5 },
entity: { id: 1, attrIntegerUnique: 42 },
})
);
await validator(5);
@ -182,46 +162,33 @@ describe('Integer validator', () => {
});
describe('min', () => {
test('it does not validates the min constraint if the attribute min is not a number', async () => {
const validator = strapiUtils.validateYupSchema(
validators.integer(
{
attr: { type: 'integer', minLength: '123' },
},
{ isDraft: false }
)
);
expect(await validator(1)).toBe(1);
});
test('it fails the validation if the integer is lower than the define min', async () => {
expect.assertions(1);
const validator = strapiUtils.validateYupSchema(
validators.integer(
{
attr: { type: 'integer', min: 3 },
},
{ isDraft: false }
)
validators.integer({
attr: { type: 'integer', min: 3 },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 5 },
entity: { id: 1, attrIntegerUnique: 42 },
})
);
try {
await validator(1);
} catch (err) {
expect(err).toBeInstanceOf(YupValidationError);
expect(err).toBeInstanceOf(errors.YupValidationError);
}
});
test('it validates the min constraint if the integer is higher than the define min', async () => {
const validator = strapiUtils.validateYupSchema(
validators.integer(
{
attr: { type: 'integer', min: 3 },
},
{ isDraft: false }
)
validators.integer({
attr: { type: 'integer', min: 3 },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 5 },
entity: { id: 1, attrIntegerUnique: 42 },
})
);
expect(await validator(4)).toBe(4);
@ -229,46 +196,33 @@ describe('Integer validator', () => {
});
describe('max', () => {
test('it does not validates the max constraint if the attribute max is not an integer', async () => {
const validator = strapiUtils.validateYupSchema(
validators.integer(
{
attr: { type: 'integer', maxLength: '123' },
},
{ isDraft: false }
)
);
expect(await validator(1)).toBe(1);
});
test('it fails the validation if the number is integer than the define max', async () => {
expect.assertions(1);
const validator = strapiUtils.validateYupSchema(
validators.integer(
{
attr: { type: 'integer', max: 3 },
},
{ isDraft: false }
)
validators.integer({
attr: { type: 'integer', max: 3 },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 5 },
entity: { id: 1, attrIntegerUnique: 42 },
})
);
try {
await validator(4);
} catch (err) {
expect(err).toBeInstanceOf(YupValidationError);
expect(err).toBeInstanceOf(errors.YupValidationError);
}
});
test('it validates the max constraint if the integer is lower than the define max', async () => {
const validator = strapiUtils.validateYupSchema(
validators.integer(
{
attr: { type: 'integer', max: 3 },
},
{ isDraft: false }
)
validators.integer({
attr: { type: 'integer', max: 3 },
model: fakeModel,
updatedAttribute: { name: 'attrIntegerUnique', value: 5 },
entity: { id: 1, attrIntegerUnique: 42 },
})
);
expect(await validator(2)).toBe(2);

View File

@ -1,38 +1,36 @@
'use strict';
import { errors } from '@strapi/utils';
const { ValidationError } = require('@strapi/utils').errors;
const entityValidator = require('../..');
const { models, existentIDs, nonExistentIds } = require('./utils/relations.testdata');
import entityValidator from '../..';
import { models, existentIDs, nonExistentIds } from './utils/relations.testdata';
import type * as Types from '../../../entity-service/types';
import type { Common } from '../../../../types';
/**
* Test that relations can be successfully validated and non existent relations
* can be detected at the Attribute level.
*/
describe('Entity validator | Relations | Attribute', () => {
const strapi = {
global.strapi = {
components: {
'basic.dev-compo': {},
},
db: {
query() {
return {
count: ({
where: {
id: { $in },
},
}) => existentIDs.filter((value) => $in.includes(value)).length,
};
},
query() {
return {
count: ({
where: {
id: { $in },
},
}: any) => existentIDs.filter((value) => $in.includes(value)).length,
};
},
errors: {
badRequest: jest.fn(),
},
getModel: (uid) => models.get(uid),
};
getModel: (uid: string) => models.get(uid),
} as any;
describe('Success', () => {
const testData = [
const testData: Array<[string, Types.Params.Data.Input<Common.UID.ContentType>]> = [
[
'Connect',
{
@ -71,8 +69,8 @@ describe('Entity validator | Relations | Attribute', () => {
},
],
];
test.each(testData)('%s', async (__, input = {}) => {
global.strapi = strapi;
const res = entityValidator.validateEntityCreation(models.get('api::dev.dev'), input, {
isDraft: true,
});
@ -81,10 +79,10 @@ describe('Entity validator | Relations | Attribute', () => {
});
describe('Error', () => {
const expectError = new ValidationError(
const expectError = new errors.ValidationError(
`2 relation(s) of type api::category.category associated with this entity do not exist`
);
const testData = [
const testData: Array<[string, Types.Params.Data.Input<Common.UID.ContentType>]> = [
[
'Connect',
{
@ -113,7 +111,6 @@ describe('Entity validator | Relations | Attribute', () => {
];
test.each(testData)('%s', async (__, input = {}) => {
global.strapi = strapi;
const res = entityValidator.validateEntityCreation(models.get('api::dev.dev'), input, {
isDraft: true,
});

View File

@ -1,35 +1,33 @@
'use strict';
import { errors } from '@strapi/utils';
const { ValidationError } = require('@strapi/utils').errors;
const entityValidator = require('../..');
const { models, nonExistentIds, existentIDs } = require('./utils/relations.testdata');
import entityValidator from '../..';
import { models, existentIDs, nonExistentIds } from './utils/relations.testdata';
/**
* Test that relations can be successfully validated and non existent relations
* can be detected at the Component level.
*/
describe('Entity validator | Relations | Component Level', () => {
const strapi = {
global.strapi = {
components: {
'basic.dev-compo': {},
},
db: {
query() {
return {
count: ({
where: {
id: { $in },
},
}) => existentIDs.filter((value) => $in.includes(value)).length,
};
},
query() {
return {
count: ({
where: {
id: { $in },
},
}: any) => existentIDs.filter((value) => $in.includes(value)).length,
};
},
errors: {
badRequest: jest.fn(),
},
getModel: (uid) => models.get(uid),
};
getModel: (uid: string) => models.get(uid),
} as any;
describe('Single Component', () => {
describe('Success', () => {
@ -82,7 +80,6 @@ describe('Entity validator | Relations | Component Level', () => {
];
test.each(testData)('%s', async (__, input = {}) => {
global.strapi = strapi;
const res = entityValidator.validateEntityCreation(models.get('api::dev.dev'), input, {
isDraft: true,
});
@ -91,7 +88,7 @@ describe('Entity validator | Relations | Component Level', () => {
});
describe('Error', () => {
const expectedError = new ValidationError(
const expectedError = new errors.ValidationError(
`1 relation(s) of type api::category.category associated with this entity do not exist`
);
const testData = [
@ -218,7 +215,7 @@ describe('Entity validator | Relations | Component Level', () => {
});
describe('Error', () => {
const expectedError = new ValidationError(
const expectedError = new errors.ValidationError(
`4 relation(s) of type api::category.category associated with this entity do not exist`
);
const testData = [
@ -264,7 +261,6 @@ describe('Entity validator | Relations | Component Level', () => {
];
test.each(testData)('%s', async (__, input = {}) => {
global.strapi = strapi;
const res = entityValidator.validateEntityCreation(models.get('api::dev.dev'), input, {
isDraft: true,
});

View File

@ -1,35 +1,29 @@
'use strict';
import { errors } from '@strapi/utils';
const { ValidationError } = require('@strapi/utils').errors;
const entityValidator = require('../..');
const { models, nonExistentIds, existentIDs } = require('./utils/relations.testdata');
import entityValidator from '../..';
import { models, existentIDs, nonExistentIds } from './utils/relations.testdata';
/**
* Test that relations can be successfully validated and non existent relations
* can be detected at the Dynamic Zone level.
*/
describe('Entity validator | Relations | Dynamic Zone', () => {
const strapi = {
global.strapi = {
components: {
'basic.dev-compo': {},
},
db: {
query() {
return {
count: ({
where: {
id: { $in },
},
}) => existentIDs.filter((value) => $in.includes(value)).length,
};
},
query() {
return {
count: ({
where: {
id: { $in },
},
}: any) => existentIDs.filter((value) => $in.includes(value)).length,
};
},
errors: {
badRequest: jest.fn(),
},
getModel: (uid) => models.get(uid),
};
errors: { badRequest: jest.fn() },
getModel: (uid: string) => models.get(uid),
} as any;
describe('Success', () => {
const testData = [
@ -89,7 +83,6 @@ describe('Entity validator | Relations | Dynamic Zone', () => {
];
test.each(testData)('%s', async (__, input = {}) => {
global.strapi = strapi;
const res = entityValidator.validateEntityCreation(models.get('api::dev.dev'), input, {
isDraft: true,
});
@ -98,7 +91,7 @@ describe('Entity validator | Relations | Dynamic Zone', () => {
});
describe('Error', () => {
const expectedError = new ValidationError(
const expectedError = new errors.ValidationError(
`2 relation(s) of type api::category.category associated with this entity do not exist`
);
const testData = [
@ -149,7 +142,6 @@ describe('Entity validator | Relations | Dynamic Zone', () => {
];
test.each(testData)('%s', async (__, input = {}) => {
global.strapi = strapi;
const res = entityValidator.validateEntityCreation(models.get('api::dev.dev'), input, {
isDraft: true,
});

View File

@ -1,38 +1,33 @@
'use strict';
import { errors } from '@strapi/utils';
const { ValidationError } = require('@strapi/utils').errors;
const entityValidator = require('../..');
const { models, existentIDs, nonExistentIds } = require('./utils/relations.testdata');
import entityValidator from '../..';
import { models, existentIDs, nonExistentIds } from './utils/relations.testdata';
/**
* Test that relations can be successfully validated and non existent relations
* can be detected at the Media level.
*/
describe('Entity validator | Relations | Media', () => {
const strapi = {
global.strapi = {
components: {
'basic.dev-compo': {},
},
db: {
query() {
return {
count: ({
where: {
id: { $in },
},
}) => existentIDs.filter((value) => $in.includes(value)).length,
};
},
query() {
return {
count: ({
where: {
id: { $in },
},
}: any) => existentIDs.filter((value) => $in.includes(value)).length,
};
},
errors: {
badRequest: jest.fn(),
},
getModel: (uid) => models.get(uid),
};
getModel: (uid: string) => models.get(uid),
} as any;
it('Success', async () => {
global.strapi = strapi;
const input = {
media: [
{
@ -49,8 +44,7 @@ describe('Entity validator | Relations | Media', () => {
});
it('Error', async () => {
global.strapi = strapi;
const expectedError = new ValidationError(
const expectedError = new errors.ValidationError(
`1 relation(s) of type plugin::upload.file associated with this entity do not exist`
);
const input = {

View File

@ -1,6 +1,5 @@
'use strict';
export const models = new Map();
const models = new Map();
models.set('api::dev.dev', {
kind: 'collectionType',
collectionName: 'devs',
@ -146,8 +145,5 @@ models.set('plugin::upload.file', {
globalId: 'UploadFile',
});
module.exports = {
models,
existentIDs: [1, 2, 3, 4, 5, 6],
nonExistentIds: [10, 11, 12, 13, 14, 15, 16],
};
export const existentIDs = [1, 2, 3, 4, 5, 6];
export const nonExistentIds = [10, 11, 12, 13, 14, 15, 16];

View File

@ -26,7 +26,7 @@ describe('String validator', () => {
global.strapi = {
query: jest.fn(() => ({
findOne: fakeFindOne,
})) as any,
})),
} as any;
afterEach(() => {

View File

@ -9,7 +9,7 @@ describe('Time validator', () => {
global.strapi = {
query: jest.fn(() => ({
findOne: fakeFindOne,
})) as any,
})),
} as any;
afterEach(() => {

View File

@ -10,7 +10,7 @@ describe('Time validator', () => {
global.strapi = {
query: jest.fn(() => ({
findOne: fakeFindOne,
})) as any,
})),
} as any;
afterEach(() => {

View File

@ -9,7 +9,7 @@ describe('UID validator', () => {
global.strapi = {
query: jest.fn(() => ({
findOne: fakeFindOne,
})) as any,
})),
} as any;
afterEach(() => {

View File

@ -4,11 +4,11 @@
*/
import { uniqBy, castArray, isNil, isArray, mergeWith } from 'lodash';
import { has, assoc, prop, isObject, isEmpty } from 'lodash/fp';
import { has, prop, isObject, isEmpty } from 'lodash/fp';
import strapiUtils from '@strapi/utils';
import * as validators from './validators';
import validators from './validators';
import { Common, Schema, Attribute, UID, Shared } from '../../types';
import * as Types from '../entity-service/types';
import type * as Types from '../entity-service/types';
type CreateOrUpdate = 'creation' | 'update';
@ -27,7 +27,7 @@ type RelationSource = string | number | ID;
interface ValidatorMeta<TAttribute = Attribute.Any> {
attr: TAttribute;
updatedAttribute: { name: string; value: unknown };
updatedAttribute: { name: string; value: any };
}
interface ValidatorContext {
@ -79,6 +79,7 @@ const addRequiredValidation = (createOrUpdate: CreateOrUpdate) => {
{ attr: { required } }: ValidatorMeta<Partial<Attribute.Any & Attribute.RequiredOption>>
): T => {
let nextValidator = validator;
if (required) {
if (createOrUpdate === 'creation') {
nextValidator = nextValidator.notNil();
@ -152,9 +153,10 @@ const createComponentValidator =
}
// FIXME: v4 was broken
let validator = yup.lazy((item) =>
createModelValidator(createOrUpdate)({ model, data: item }, { isDraft })
) as any;
let validator = createModelValidator(createOrUpdate)(
{ model, data: updatedAttribute.value },
{ isDraft }
);
validator = addRequiredValidation(createOrUpdate)(validator, {
attr: { required: !isDraft && attr.required },
@ -239,7 +241,7 @@ const createScalarAttributeValidator =
const createAttributeValidator =
(createOrUpdate: CreateOrUpdate) =>
(metas: AttributeValidatorMetas, options: ValidatorContext) => {
let validator: strapiUtils.yup.BaseSchema = yup.mixed();
let validator = yup.mixed();
if (isMediaAttribute(metas.attr)) {
validator = yup.mixed();
@ -277,17 +279,18 @@ const createModelValidator =
const writableAttributes = model ? getWritableAttributes(model) : [];
const schema = writableAttributes.reduce((validators, attributeName) => {
const validator = createAttributeValidator(createOrUpdate)(
{
attr: model.attributes[attributeName],
updatedAttribute: { name: attributeName, value: prop(attributeName, data) },
model,
entity,
},
options
);
const metas = {
attr: model.attributes[attributeName],
updatedAttribute: { name: attributeName, value: prop(attributeName, data) },
model,
entity,
};
return assoc(attributeName, validator)(validators);
const validator = createAttributeValidator(createOrUpdate)(metas, options);
validators[attributeName] = validator;
return validators;
}, {} as Record<string, strapiUtils.yup.BaseSchema>);
return yup.object().shape(schema);
@ -384,9 +387,13 @@ const buildRelationsStore = ({
if (Array.isArray(value)) {
source = value;
} else if (isObject(value)) {
source = castArray(
('connect' in value && value.connect) ?? ('set' in value && value.set) ?? []
) as RelationSource[];
if ('connect' in value && !isNil(value.connect)) {
source = value.connect as RelationSource[];
} else if ('set' in value && !isNil(value.set)) {
source = value.set as RelationSource[];
} else {
source = [];
}
} else {
source = castArray(value as RelationSource);
}

View File

@ -8,7 +8,8 @@ export type BigInteger = Attribute.OfType<'biginteger'> &
Attribute.PrivateOption &
Attribute.RequiredOption &
Attribute.WritableOption &
Attribute.VisibleOption;
Attribute.VisibleOption &
Attribute.UniqueOption;
export type BigIntegerValue = string;

View File

@ -8,7 +8,8 @@ export type Decimal = Attribute.OfType<'decimal'> &
Attribute.PrivateOption &
Attribute.RequiredOption &
Attribute.WritableOption &
Attribute.VisibleOption;
Attribute.VisibleOption &
Attribute.UniqueOption;
export type DecimalValue = number;

View File

@ -8,7 +8,8 @@ export type Float = Attribute.OfType<'float'> &
Attribute.PrivateOption &
Attribute.RequiredOption &
Attribute.WritableOption &
Attribute.VisibleOption;
Attribute.VisibleOption &
Attribute.UniqueOption;
export type FloatValue = number;

View File

@ -8,7 +8,8 @@ export type Integer = Attribute.OfType<'integer'> &
Attribute.PrivateOption &
Attribute.RequiredOption &
Attribute.WritableOption &
Attribute.VisibleOption;
Attribute.VisibleOption &
Attribute.UniqueOption;
export type IntegerValue = number;

View File

@ -6,7 +6,8 @@ export type JSON = Attribute.OfType<'json'> &
Attribute.RequiredOption &
Attribute.PrivateOption &
Attribute.WritableOption &
Attribute.VisibleOption;
Attribute.VisibleOption &
Attribute.DefaultOption<JsonValue>;
export type JsonValue<T extends object = object> = T;