diff --git a/elements/lisk-validator/src/formats.ts b/elements/lisk-validator/src/formats.ts index b90baaebf96..810eda7212b 100644 --- a/elements/lisk-validator/src/formats.ts +++ b/elements/lisk-validator/src/formats.ts @@ -13,30 +13,21 @@ * */ import { address as cryptoAddress } from '@liskhq/lisk-cryptography'; -import { - isHexString, - isBytes, - isNumberString, - isSInt64, - isUInt64, - isUInt32, - isSInt32, - isIP, - isIPV4, - isEncryptedPassphrase, - isSemVer, -} from './validation'; +import { isHexString, isBytes, isIP, isIPV4, isEncryptedPassphrase, isSemVer } from './validation'; +import { MAX_UINT32, MAX_UINT64 } from './constants'; export const hex = isHexString; export const bytes = isBytes; -export const int64 = (data: string): boolean => isNumberString(data) && isSInt64(BigInt(data)); - -export const uint64 = (data: string): boolean => isNumberString(data) && isUInt64(BigInt(data)); - -export const uint32 = (data: string): boolean => isNumberString(data) && isUInt32(Number(data)); +export const uint64 = { + type: 'number', + validate: (value: number) => Number.isInteger(value) && value >= 0 && value <= MAX_UINT64, +}; -export const int32 = (data: string): boolean => isNumberString(data) && isSInt32(Number(data)); +export const uint32 = { + type: 'number', + validate: (value: number) => Number.isInteger(value) && value >= 0 && value <= MAX_UINT32, +}; const camelCaseRegex = /^[a-z][0-9]*([A-Z][a-z]*[a-zA-Z0-9]*|[a-z][a-z]*[a-zA-Z0-9]*)?$/; diff --git a/elements/lisk-validator/src/lisk_validator.ts b/elements/lisk-validator/src/lisk_validator.ts index 11bfc5d4342..4ef84240ac2 100644 --- a/elements/lisk-validator/src/lisk_validator.ts +++ b/elements/lisk-validator/src/lisk_validator.ts @@ -13,7 +13,7 @@ * */ -import Ajv, { SchemaObject, ValidateFunction } from 'ajv'; +import Ajv, { Format, SchemaObject, ValidateFunction } from 'ajv'; import addDefaultFormats from 'ajv-formats'; import * as formats from './formats'; import { convertErrorsToLegacyFormat, LiskValidationError } from './errors'; @@ -32,7 +32,6 @@ export class LiskValidator { strict: true, strictSchema: true, allErrors: true, - useDefaults: false, // FIXME: Combination with lisk-codec schema, making true would throw error because // Trace: Error: schema with key or id "/block/header" addUsedSchema: false, @@ -43,12 +42,8 @@ export class LiskValidator { addDefaultFormats(this._validator); - for (const formatName of Object.keys(formats)) { - this._validator.addFormat( - formatName, - // eslint-disable-next-line import/namespace - formats[formatName as keyof typeof formats], - ); + for (const [formatName, format] of Object.entries(formats)) { + this._validator.addFormat(formatName, format as Format); } this._validator.addKeyword({ diff --git a/elements/lisk-validator/test/lisk_validator_formats.spec.ts b/elements/lisk-validator/test/lisk_validator_formats.spec.ts index 9b8676c720d..a47925bbeea 100644 --- a/elements/lisk-validator/test/lisk_validator_formats.spec.ts +++ b/elements/lisk-validator/test/lisk_validator_formats.spec.ts @@ -13,35 +13,116 @@ * */ import { validator } from '../src'; +import { MAX_SINT32, MAX_SINT64, MAX_UINT32, MAX_UINT64 } from '../src/constants'; describe('validator formats', () => { - const baseSchemaId = '/test/schema'; - let baseSchema: Record; + const baseSchema = { + $id: '/test/schema', + type: 'object', + }; - beforeAll(() => { - baseSchema = { - $id: baseSchemaId, - type: 'object', + describe('uint32', () => { + const uint32IntegerSchema = { + ...baseSchema, + properties: { + height: { + type: 'integer', + format: 'uint32', + }, + }, }; + + it('should validate a correct uint32', () => { + expect(() => validator.validate(uint32IntegerSchema, { height: 8 })).not.toThrow(); + }); + + it('should throw for a negative number', () => { + expect(() => validator.validate(uint32IntegerSchema, { height: -1 })).toThrow(); + }); + + it('should throw when the number is too big', () => { + expect(() => validator.validate(uint32IntegerSchema, { height: MAX_UINT32 + 1 })).toThrow(); + }); }); - describe('hex', () => { - let schema: Record; - beforeEach(() => { - schema = { - allOf: [ - baseSchema, - { - properties: { - target: { - type: 'string', - format: 'hex', - }, - }, - }, - ], - }; + describe('int32', () => { + const int32IntegerSchema = { + ...baseSchema, + properties: { + height: { + type: 'integer', + format: 'int32', + }, + }, + }; + + it('should validate a correct int32', () => { + expect(() => validator.validate(int32IntegerSchema, { height: 8 })).not.toThrow(); + }); + + it('should throw when the number is too big', () => { + expect(() => validator.validate(int32IntegerSchema, { height: MAX_SINT32 + 1 })).toThrow(); + }); + }); + + describe('uint64', () => { + const uint32IntegerSchema = { + ...baseSchema, + properties: { + height: { + type: 'integer', + format: 'uint64', + }, + }, + }; + + it('should validate a correct uint64', () => { + expect(() => validator.validate(uint32IntegerSchema, { height: 8 })).not.toThrow(); + }); + + it('should throw for a negative number', () => { + expect(() => validator.validate(uint32IntegerSchema, { height: -1 })).toThrow(); + }); + + it('should throw when the number is too big', () => { + expect(() => + validator.validate(uint32IntegerSchema, { height: MAX_UINT64 + BigInt(1) }), + ).toThrow(); }); + }); + + describe('int64', () => { + const int32IntegerSchema = { + ...baseSchema, + properties: { + height: { + type: 'integer', + format: 'int64', + }, + }, + }; + + it('should validate a correct int64', () => { + expect(() => validator.validate(int32IntegerSchema, { height: 8 })).not.toThrow(); + }); + + it('should throw when the number is too big', () => { + expect(() => + validator.validate(int32IntegerSchema, { height: MAX_SINT64 + BigInt(1) }), + ).toThrow(); + }); + }); + + describe('hex', () => { + const schema = { + ...baseSchema, + properties: { + target: { + type: 'string', + format: 'hex', + }, + }, + }; it('should validate to true when valid hex string is provided', () => { expect(() => @@ -53,31 +134,22 @@ describe('validator formats', () => { const expectedError = 'Lisk validator found 1 error[s]:\nProperty \'.target\' must match format "hex"'; - expect(() => - validator.validate(schema, { - target: 'notValid?!hex-!!@', - }), - ).toThrow(expectedError); + expect(() => validator.validate(schema, { target: 'notValid?!hex-!!@' })).toThrow( + expectedError, + ); }); }); describe('lisk32', () => { - let schema: Record; - beforeEach(() => { - schema = { - allOf: [ - baseSchema, - { - properties: { - target: { - type: 'string', - format: 'lisk32', - }, - }, - }, - ], - }; - }); + const schema = { + ...baseSchema, + properties: { + target: { + type: 'string', + format: 'lisk32', + }, + }, + }; it('should validate to true when valid hex string is provided', () => { expect(() => @@ -110,6 +182,7 @@ describe('validator formats', () => { describe('path', () => { const pathSchema = { + ...baseSchema, properties: { rootPath: { type: 'string', @@ -135,6 +208,7 @@ describe('validator formats', () => { describe('encryptedPassphrase', () => { const encryptedPassphraseSchema = { + ...baseSchema, properties: { encryptedPassphrase: { type: 'string', @@ -173,6 +247,7 @@ describe('validator formats', () => { describe('camelCaseRegex', () => { const camelCaseRegexSchema = { + ...baseSchema, properties: { camelCaseRegex: { type: 'string', @@ -203,6 +278,7 @@ describe('validator formats', () => { describe('version', () => { const versionSchema = { + ...baseSchema, properties: { version: { type: 'string', diff --git a/framework/test/unit/modules/random/endpoint.spec.ts b/framework/test/unit/modules/random/endpoint.spec.ts index 7f3514af77f..5a61c63f91e 100644 --- a/framework/test/unit/modules/random/endpoint.spec.ts +++ b/framework/test/unit/modules/random/endpoint.spec.ts @@ -698,7 +698,7 @@ describe('RandomModuleEndpoint', () => { // Act & Assert await expect(randomEndpoint.setHashOnionUsage(context)).rejects.toThrow( - "Lisk validator found 2 error[s]:\nProperty '.usedHashOnions.0.count' should be of type 'integer'\nProperty '.usedHashOnions.0.count' must match format \"uint32\"", + "Lisk validator found 1 error[s]:\nProperty '.usedHashOnions.0.count' should be of type 'integer'", ); }); @@ -713,7 +713,7 @@ describe('RandomModuleEndpoint', () => { // Act & Assert await expect(randomEndpoint.setHashOnionUsage(context)).rejects.toThrow( - "Lisk validator found 2 error[s]:\nProperty '.usedHashOnions.0.height' should be of type 'integer'\nProperty '.usedHashOnions.0.height' must match format \"uint32\"", + "Lisk validator found 1 error[s]:\nProperty '.usedHashOnions.0.height' should be of type 'integer'", ); }); });