diff --git a/packages/web3-validator/test/fixtures/abi_to_json_schema.ts b/packages/web3-validator/test/fixtures/abi_to_json_schema.ts index 1340159ee91..63a3b9611ce 100644 --- a/packages/web3-validator/test/fixtures/abi_to_json_schema.ts +++ b/packages/web3-validator/test/fixtures/abi_to_json_schema.ts @@ -17,7 +17,7 @@ along with web3.js. If not, see . import { FullValidationSchema, JsonSchema, ShortValidationSchema } from '../../src/types'; -export const abiToJsonSchemaCases: { +export type AbiToJsonSchemaCase = { title: string; abi: { fullSchema: FullValidationSchema; @@ -29,9 +29,10 @@ export const abiToJsonSchemaCases: { shortSchema: JsonSchema; data: Record | Array; }; -}[] = [ +}; +export const abiToJsonSchemaCases: AbiToJsonSchemaCase[] = [ { - title: 'single param', + title: 'single param uint', abi: { fullSchema: [{ name: 'a', type: 'uint' }], shortSchema: ['uint'], @@ -53,6 +54,75 @@ export const abiToJsonSchemaCases: { data: [12], }, }, + { + title: 'single param address', + abi: { + fullSchema: [{ name: 'a', type: 'address' }], + shortSchema: ['address'], + data: ['0xCB00CDE33a7a0Fba30C63745534F1f7Ae607076b'], + }, + json: { + fullSchema: { + type: 'array', + items: [{ $id: 'a', eth: 'address' }], + minItems: 1, + maxItems: 1, + }, + shortSchema: { + type: 'array', + items: [{ $id: '/0/0', eth: 'address' }], + minItems: 1, + maxItems: 1, + }, + data: ['0xCB00CDE33a7a0Fba30C63745534F1f7Ae607076b'], + }, + }, + { + title: 'single param bool', + abi: { + fullSchema: [{ name: 'a', type: 'bool' }], + shortSchema: ['bool'], + data: [true], + }, + json: { + fullSchema: { + type: 'array', + items: [{ $id: 'a', eth: 'bool' }], + minItems: 1, + maxItems: 1, + }, + shortSchema: { + type: 'array', + items: [{ $id: '/0/0', eth: 'bool' }], + minItems: 1, + maxItems: 1, + }, + data: [true], + }, + }, + { + title: 'single param bytes', + abi: { + fullSchema: [{ name: 'a', type: 'bytes' }], + shortSchema: ['bytes'], + data: ['0xCB00CDE33a7a0Fba30C63745534F1f7Ae607076b'], + }, + json: { + fullSchema: { + type: 'array', + items: [{ $id: 'a', eth: 'bytes' }], + minItems: 1, + maxItems: 1, + }, + shortSchema: { + type: 'array', + items: [{ $id: '/0/0', eth: 'bytes' }], + minItems: 1, + maxItems: 1, + }, + data: ['0xCB00CDE33a7a0Fba30C63745534F1f7Ae607076b'], + }, + }, { title: 'multiple params', diff --git a/packages/web3-validator/test/fixtures/errors.ts b/packages/web3-validator/test/fixtures/errors.ts new file mode 100644 index 00000000000..dca0a2bc7e4 --- /dev/null +++ b/packages/web3-validator/test/fixtures/errors.ts @@ -0,0 +1,69 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ + +export const fullErrors: any[] = [ + { + instancePath: '', + schemaPath: '#/minItems', + keyword: 'minItems', + params: { limit: 1 }, + message: 'must NOT have fewer than 1 items', + }, + { + instancePath: '', + schemaPath: '#/minItems', + keyword: 'minItems', + params: { limit: 2 }, + message: 'must NOT have fewer than 2 items', + }, + { + instancePath: '', + schemaPath: '#/maxItems', + keyword: 'maxItems', + params: { limit: 1 }, + message: 'must NOT have more than 1 items', + }, +]; + +export const fullErrorsWithInstance: any[] = [ + { + message: 'must pass "uint" validation', + keyword: 'eth', + params: { value: -1 }, + instancePath: '/0', + schemaPath: '#/items/0/eth', + }, +]; + +export const errorsWithInstanceNoParams: any[] = [ + { + message: 'must pass "uint" validation', + keyword: 'eth', + instancePath: '/0', + schemaPath: '#/items/0/eth', + }, +]; + +export const errorsWithInstanceNoParamsNoMessage: any[] = [ + { + keyword: 'eth', + instancePath: '/0', + schemaPath: '#/items/0/eth', + }, +]; + +export const unspecifiedErrors: any[] = [{}]; diff --git a/packages/web3-validator/test/fixtures/validation.ts b/packages/web3-validator/test/fixtures/validation.ts index 64ac4e968ee..2e7dd416ca0 100644 --- a/packages/web3-validator/test/fixtures/validation.ts +++ b/packages/web3-validator/test/fixtures/validation.ts @@ -16,6 +16,7 @@ along with web3.js. If not, see . */ import { Filter } from 'web3-types'; +import { ValidInputTypes } from '../../src/types'; export const validUintData: any[] = [ '0x48', @@ -109,16 +110,28 @@ export const invalidIntDataWithAbiType: [any, string][] = [ ['-0x0dec0518fa672a70027b04c286582e543ab17319fbdd384fa7bc8f3d5a542c0b', 'uint8'], ]; +export const validHexStrictDataWithNumber: [string, number | bigint][] = [ + ['0x48', 72], + ['0x123c', 4668], + [ + '0xdec0518fa672a70027b04c286582e543ab17319fbdd384fa7bc8f3d5a542c0b', + BigInt('6297078121011128569053558207054331251192909352593326480842737114300118477835'), + ], + [ + '0xd115bffabbdd893a6f7cea402e7338643ced44a6', + BigInt('1193664110518272216229793131906554422260021413030'), + ], + [ + '0x2C941171bD2A7aEda7c2767c438DfF36EAaFdaFc', + BigInt('254497623817844434235817792799421766503337286396'), + ], + ['0x1', 1], + ['0xcd', 205], + ['-0xcd', -205], +]; export const validHexStrictData: any[] = [ - '0x48', - '0x123c', - '0x0dec0518fa672a70027b04c286582e543ab17319fbdd384fa7bc8f3d5a542c0b', - '0xd115bffabbdd893a6f7cea402e7338643ced44a6', - '0x2C941171bD2A7aEda7c2767c438DfF36EAaFdaFc', - '0x1', - '0xcd', - '-0xcd', - '-0x0dec0518fa672a70027b04c286582e543ab17319fbdd384fa7bc8f3d5a542c0b', + ...validHexStrictDataWithNumber.map(tuple => tuple[0]), + '-0xdec0518fa672a70027b04c286582e543ab17319fbdd384fa7bc8f3d5a542c0b', ]; export const invalidHexData: any[] = [ @@ -158,6 +171,31 @@ export const validHexData: any[] = [ BigInt(-255), ]; +export const validStringNumbersWithHex: [string, string][] = [ + ['72', '0x48'], + ['4668', '0x123c'], + [ + '6297078121011128569053558207054331251192909352593326480842737114300118477835', + '0xdec0518fa672a70027b04c286582e543ab17319fbdd384fa7bc8f3d5a542c0b', + ], + [ + '1193664110518272216229793131906554422260021413030', + '0xd115bffabbdd893a6f7cea402e7338643ced44a6', + ], + [ + '254497623817844434235817792799421766503337286396', + '0x2C941171bD2A7aEda7c2767c438DfF36EAaFdaFc', + ], + ['1', '0x1'], + ['205', '0xcd'], + ['-205', '-0xcd'], +]; + +export const invalidStringNumbers: ValidInputTypes[] = [ + new ArrayBuffer(23255), + Buffer.from('abce', 'hex'), + Buffer.from('hello', 'utf8'), +]; export const validCheckAddressCheckSumData: any[] = [ '0xc1912fEE45d61C87Cc5EA59DaE31190FFFFf232d', '0x52908400098527886E0F7030069857D2E4169EE7', @@ -261,6 +299,12 @@ export const invalidInBloomData: any[] = [ '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', '0xhello', ], + [0, '0x6254B927ecC25DDd233aAECD5296D746B1C006B4'], + [ + // mix a and A + '0xaA000000200000000010000080000000000002000000000000000000000000000000000000020200000000000000000000800001000000000000000000200000000000000000000000000008000000800000000000000000000000000000000000000000020000000000000000000800000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000080000000000000000000000100000000000000000000000002000000000001000080000000000000000000000000000000000020200010000000000000000000000000000000000000100000000000000000000000', + '0x98afe7a8d28bbc88dcf41f8e06d97c74958a47dc', + ], ]; export const validUserEthereumAddressInBloomData: any[] = [ @@ -279,6 +323,11 @@ export const invalidUserEthereumAddressInBloomData: any[] = [ '0x00000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000002000000000000000000000000000000100000000000000082000000000080000000000000000000000000000000000000000000000002000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000', '0xH1', ], + [ + // mix a and A + '0xaA000000200000000010000080000000000002000000000000000000000000000000000000020200000000000000000000800001000000000000000000200000000000000000000000000008000000800000000000000000000000000000000000000000020000000000000000000800000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000080000000000000000000000100000000000000000000000002000000000001000080000000000000000000000000000000000020200010000000000000000000000000000000000000100000000000000000000000', + '0x98afe7a8d28bbc88dcf41f8e06d97c74958a47dc', + ], ]; export const validTopicData: any[] = [ @@ -545,3 +594,50 @@ export const invalidEthTypeData: string[] = [ 'my-addresss', 'boolean', ]; + +export const validCodePoints: [number, number][] = [ + [48, 0], + [51, 3], + [55, 7], + [57, 9], + [65, 10], + [70, 15], + [97, 10], + [100, 13], + [102, 15], +]; + +export const invalidCodePoints: number[] = [-100, -5, 0, 30, 58, 75, 90, 103, 200]; + +export const padLeftData: { padDigits: number; data: [ValidInputTypes, string][] } = { + padDigits: 64, + data: [ + [ + 'dfd5293d8e347dfe59e90efd55b2956a1343963d', + '000000000000000000000000dfd5293d8e347dfe59e90efd55b2956a1343963d', + ], + [ + '-0xdfd5293d8e347dfe59e90efd55b2956a1343963d', + '-0x000000000000000000000000dfd5293d8e347dfe59e90efd55b2956a1343963d', + ], + [2, '0x0000000000000000000000000000000000000000000000000000000000000002'], + ], +}; + +export const validNotBaseTypeData: { dataType: string; data: any }[] = [ + { dataType: 'hex', data: '0x000000000000000000000000dfd5293d8e347dfe59e90efd55b2956a1343963d' }, + { dataType: 'number', data: 42 }, + { dataType: 'blockNumber', data: 42 }, + { dataType: 'blockNumberOrTag', data: 'latest' }, + { + dataType: 'filter', + data: { + fromBlock: 'latest', + toBlock: 'latest', + }, + }, + { + dataType: 'bloom', + data: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + }, +]; diff --git a/packages/web3-validator/test/unit/default_validator.test.ts b/packages/web3-validator/test/unit/default_validator.test.ts new file mode 100644 index 00000000000..352cae826d2 --- /dev/null +++ b/packages/web3-validator/test/unit/default_validator.test.ts @@ -0,0 +1,26 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ + +import { validator } from '../../src/default_validator'; +import { Web3Validator } from '../../src/web3_validator'; + +describe('default web3-validator', () => { + it('should be instance', () => { + expect(validator).toBeDefined(); + expect(validator).toBeInstanceOf(Web3Validator); + }); +}); diff --git a/packages/web3-validator/test/unit/error.test.ts b/packages/web3-validator/test/unit/error.test.ts new file mode 100644 index 00000000000..d5e500a643c --- /dev/null +++ b/packages/web3-validator/test/unit/error.test.ts @@ -0,0 +1,80 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ + +import { Web3ValidatorError } from '../../src/errors'; +import { + fullErrors, + fullErrorsWithInstance, + errorsWithInstanceNoParams, + errorsWithInstanceNoParamsNoMessage, + unspecifiedErrors, +} from '../fixtures/errors'; +import { Web3ValidationErrorObject } from '../../src/types'; + +describe('Web3ValidationError', () => { + it.each(fullErrors)('errors with message', (error: Web3ValidationErrorObject) => { + const validationError = new Web3ValidatorError([error]); + + expect(validationError).toBeInstanceOf(Web3ValidatorError); + expect(validationError.message).toBe(`Web3 validator found 1 error[s]:\n${error.message}`); + }); + + it.each(fullErrorsWithInstance)( + 'errors with message, instance and params', + (error: Web3ValidationErrorObject) => { + const validationError = new Web3ValidatorError([error]); + + expect(validationError).toBeInstanceOf(Web3ValidatorError); + expect(validationError.message).toBe( + `Web3 validator found 1 error[s]:\nvalue "${ + (error.params as { value: unknown }).value + }" at "${error.instancePath}" ${error.message}`, + ); + }, + ); + + it.each(errorsWithInstanceNoParams)( + 'errors with only message and instance', + (error: Web3ValidationErrorObject) => { + const validationError = new Web3ValidatorError([error]); + + expect(validationError).toBeInstanceOf(Web3ValidatorError); + expect(validationError.message).toBe( + `Web3 validator found 1 error[s]:\nvalue at "${error.instancePath}" ${error.message}`, + ); + }, + ); + + it.each(errorsWithInstanceNoParamsNoMessage)( + 'errors with only instance', + (error: Web3ValidationErrorObject) => { + const validationError = new Web3ValidatorError([error]); + + expect(validationError).toBeInstanceOf(Web3ValidatorError); + expect(validationError.message).toBe( + `Web3 validator found 1 error[s]:\nvalue at "${error.instancePath}" caused unspecified error`, + ); + }, + ); + + it.each(unspecifiedErrors)('unspecified errors', (error: Web3ValidationErrorObject) => { + const validationError = new Web3ValidatorError([error]); + + expect(validationError).toBeInstanceOf(Web3ValidatorError); + expect(validationError.message).toBe(`Web3 validator found 1 error[s]:\nunspecified error`); + }); +}); diff --git a/packages/web3-validator/test/unit/index.test.ts b/packages/web3-validator/test/unit/index.test.ts new file mode 100644 index 00000000000..efc2586580f --- /dev/null +++ b/packages/web3-validator/test/unit/index.test.ts @@ -0,0 +1,30 @@ +/* +This file is part of web3.js. + +web3.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +web3.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with web3.js. If not, see . +*/ +import * as Validator from '../../src'; + +describe('main package', () => { + it('should import', () => { + expect(Validator.validator).toBeDefined(); + expect(Validator.Web3Validator).toBeDefined(); + expect(Validator.utils).toBeDefined(); + expect(Validator.Web3ValidatorError).toBeDefined(); + expect(Validator.VALID_ETH_BASE_TYPES).toBeDefined(); + + expect(Validator.validator).toBeInstanceOf(Validator.Web3Validator); + expect(Validator.VALID_ETH_BASE_TYPES).toBeInstanceOf(Array); + }); +}); diff --git a/packages/web3-validator/test/unit/utils.test.ts b/packages/web3-validator/test/unit/utils.test.ts index 24a9a07791c..d15092f3fd0 100644 --- a/packages/web3-validator/test/unit/utils.test.ts +++ b/packages/web3-validator/test/unit/utils.test.ts @@ -15,8 +15,26 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -import { ethAbiToJsonSchema, transformJsonDataToAbiFormat } from '../../src/utils'; +import { ValidInputTypes } from '../../src/types'; +import { + ethAbiToJsonSchema, + transformJsonDataToAbiFormat, + codePointToInt, + hexToNumber, + numberToHex, + padLeft, +} from '../../src/utils'; import { abiToJsonSchemaCases } from '../fixtures/abi_to_json_schema'; +import { + validCodePoints, + invalidCodePoints, + validHexStrictDataWithNumber, + invalidHexData, + validHexStrictData, + validStringNumbersWithHex, + invalidStringNumbers, + padLeftData, +} from '../fixtures/validation'; describe('utils', () => { describe('ethAbiToJsonSchema', () => { @@ -40,4 +58,58 @@ describe('utils', () => { }); }); }); + + describe('codePointToInt', () => { + it.each(validCodePoints)('valid code points', (input, res) => { + expect(codePointToInt(input)).toEqual(res); + }); + + it.each(invalidCodePoints)('valid code points', (input: number) => { + expect(() => { + codePointToInt(input); + }).toThrow(new Error(`Invalid code point: ${input}`)); + }); + }); + + describe('hexToNumber', () => { + it.each(validHexStrictDataWithNumber)('valid hex string data', (input, res) => { + expect(hexToNumber(input)).toEqual(res); + }); + + it.each(invalidHexData)('invalidHexData', (input: string) => { + expect(() => { + hexToNumber(input); + }).toThrow(new Error('Invalid hex string')); + }); + }); + describe('numberToHex', () => { + it.each(validHexStrictDataWithNumber.map(tuple => [tuple[1], tuple[0]]))( + 'valid numbers and bigints', + (input, res) => { + expect(numberToHex(input)).toEqual((res as string).toLowerCase()); + }, + ); + + it.each(validHexStrictData)('valid hex strings', input => { + expect(numberToHex(input)).toEqual((input as string).toLowerCase()); + }); + + it.each(validStringNumbersWithHex)('valid string numbers', (input, res) => { + expect(numberToHex(input)).toEqual(res.toLowerCase()); + }); + + it.each(invalidStringNumbers)('invalid string number', (input: ValidInputTypes) => { + expect(() => { + numberToHex(input); + }).toThrow(new Error('Invalid number value')); + }); + }); + + describe('padLeft', () => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + it.each(padLeftData.data)('valid numbers and bigints', (input, res) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + expect(padLeft(input, padLeftData.padDigits)).toBe(res); + }); + }); }); diff --git a/packages/web3-validator/test/unit/validation/bloom.test.ts b/packages/web3-validator/test/unit/validation/bloom.test.ts index 765eddca99d..ef20809cf84 100644 --- a/packages/web3-validator/test/unit/validation/bloom.test.ts +++ b/packages/web3-validator/test/unit/validation/bloom.test.ts @@ -73,6 +73,12 @@ describe('validation', () => { expect(isContractAddressInBloom(bloom, address)).toBeTruthy(); }); }); + + describe('invalid cases', () => { + it.each(invalidUserEthereumAddressInBloomData)('%s', (bloom, address) => { + expect(isContractAddressInBloom(bloom, address)).toBeFalsy(); + }); + }); }); }); }); diff --git a/packages/web3-validator/test/unit/web3_validator.test.ts b/packages/web3-validator/test/unit/web3_validator.test.ts index 408726f4920..f5b2087e548 100644 --- a/packages/web3-validator/test/unit/web3_validator.test.ts +++ b/packages/web3-validator/test/unit/web3_validator.test.ts @@ -14,10 +14,12 @@ GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ - +import { abiToJsonSchemaCases } from '../fixtures/abi_to_json_schema'; import { Web3Validator } from '../../src/web3_validator'; +import { Web3ValidatorError } from '../../src/errors'; import * as keywords from '../../src/keywords'; import * as formats from '../../src/formats'; +import { validNotBaseTypeData } from '../fixtures/validation'; describe('web3-validator', () => { describe('Web3Validator', () => { @@ -82,6 +84,73 @@ describe('web3-validator', () => { }, ]); }); + + it('should return undefined for empty schema and empty data', () => { + expect(validator.validate([], [])).toBeUndefined(); + }); + + it('should return error is schema is empty but data no', () => { + const data = [1]; + const testFunction = () => { + validator.validate([], data); + }; + expect(testFunction).toThrow( + 'value at "/0" empty schema against data can not be validated', + ); + + expect(testFunction).toThrow(Web3ValidatorError); + }); + + it.each(validNotBaseTypeData)( + 'should pass for valid non base type data %s', + ({ dataType, data }: { dataType: string; data: any }) => { + expect(validator.validate([dataType], [data])).toBeUndefined(); + }, + ); + }); + describe('validateJsonSchema', () => { + it.each(abiToJsonSchemaCases.slice(0, 5))('should pass for valid data', abi => { + const jsonSchema = abi.json; + expect( + validator.validateJSONSchema(jsonSchema.fullSchema, jsonSchema.data), + ).toBeUndefined(); + }); + + it('should throw', () => { + expect(() => { + validator.validateJSONSchema( + { + type: 'array', + items: [{ $id: 'a', eth: 'uint' }], + minItems: 1, + maxItems: 1, + }, + [], + ); + }).toThrow(Web3ValidatorError); + }); + it('should return errors on silent', () => { + expect( + validator.validateJSONSchema( + { + type: 'array', + items: [{ $id: 'a', eth: 'uint' }], + minItems: 1, + maxItems: 1, + }, + [], + { silent: true }, + ), + ).toMatchObject([ + { + instancePath: '', + schemaPath: '#/minItems', + keyword: 'minItems', + params: { limit: 1 }, + message: 'must NOT have fewer than 1 items', + }, + ]); + }); }); }); });