From ebba894fb33a1832392085ecf448b424b7de7c4b Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Wed, 15 May 2024 13:41:06 +0200 Subject: [PATCH 01/17] increase unit tests for SocketProvider --- .gitignore | 2 + packages/web3-utils/src/socket_provider.ts | 2 +- .../test/unit/socket_provider.test.ts | 390 +++++++++++++++++- 3 files changed, 386 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index c22337c0c9a..72f88324900 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,5 @@ packages/web3/.in3/ benchmark-data.txt .eslintcache + +.history \ No newline at end of file diff --git a/packages/web3-utils/src/socket_provider.ts b/packages/web3-utils/src/socket_provider.ts index d847dcbccc9..046fc69d0ad 100644 --- a/packages/web3-utils/src/socket_provider.ts +++ b/packages/web3-utils/src/socket_provider.ts @@ -360,7 +360,7 @@ export abstract class SocketProvider< const checkQueue = async () => new Promise(resolve => { const interval = setInterval(() => { - if (forceDisconnect && retryAttempt === 5) { + if (forceDisconnect && retryAttempt >= 5) { this.clearQueues(); } if (this.getPendingRequestQueueSize() === 0 && this.getSentRequestsQueueSize() === 0) { diff --git a/packages/web3-utils/test/unit/socket_provider.test.ts b/packages/web3-utils/test/unit/socket_provider.test.ts index a09f1467ce0..9878879e3ba 100644 --- a/packages/web3-utils/test/unit/socket_provider.test.ts +++ b/packages/web3-utils/test/unit/socket_provider.test.ts @@ -15,7 +15,14 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ -import { Web3APIPayload, EthExecutionAPI, JsonRpcResponse, Web3ProviderStatus } from 'web3-types'; +import { + Web3APIPayload, + EthExecutionAPI, + JsonRpcResponse, + Web3ProviderStatus, + JsonRpcIdentifier, +} from 'web3-types'; +import { MaxAttemptsReachedOnReconnectingError } from 'web3-errors'; import { EventEmitter } from '../../src/event_emitter'; // eslint-disable-next-line import/no-relative-packages import { sleep } from '../../../../fixtures/utils'; @@ -40,8 +47,27 @@ class TestProvider extends SocketProvider { // eslint-disable-next-line protected _sendToSocket(_payload: Web3APIPayload): void {} // eslint-disable-next-line - protected _parseResponses(_event: any): JsonRpcResponse[] { - return [] as JsonRpcResponse[]; + protected _parseResponses(_event: { data: string } | undefined): JsonRpcResponse[] { + if (!_event || !_event.data) { + return []; + } + const returnValues: JsonRpcResponse[] = []; + + // DE-CHUNKER + const dechunkedData = _event.data + .replace(/\}[\n\r]?\{/g, '}|--|{') // }{ + .replace(/\}\][\n\r]?\[\{/g, '}]|--|[{') // }][{ + .replace(/\}[\n\r]?\[\{/g, '}|--|[{') // }[{ + .replace(/\}\][\n\r]?\{/g, '}]|--|{') // }]{ + .split('|--|'); + + dechunkedData.forEach((chunkData: string) => { + const result = JSON.parse(chunkData) as unknown as JsonRpcResponse; + + if (result) returnValues.push(result); + }); + + return returnValues; } public message(_event: any): void { this._onMessage(_event); @@ -74,6 +100,26 @@ describe('SocketProvider', () => { expect(provider).toBeInstanceOf(SocketProvider); expect(provider.SocketConnection).toEqual(dummySocketConnection); }); + it('should call _clearQueues when chunkResponseParser emits an error', async () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + const clearQueuesSpy = jest.spyOn(provider as any, '_clearQueues'); + + try { + // @ts-expect-error access readonly method + provider['chunkResponseParser']['autoReconnect'] = false; + // @ts-expect-error access readonly method + provider['chunkResponseParser']['chunkTimeout'] = 0; + + provider['chunkResponseParser'].parseResponse('invalid-json'); + } catch (error) { + // nothing + } + + // wait 1 second for the timeout to trigger + await sleep(100); + + expect(clearQueuesSpy).toHaveBeenCalled(); + }); }); describe('testing _reconnect() method', () => { it('should not be called when { autoReconnect: false }', () => { @@ -94,6 +140,111 @@ describe('SocketProvider', () => { // @ts-expect-error run protected method expect(provider._reconnect).not.toHaveBeenCalled(); }); + it('should call _reconnect when isReconnecting is true and an error happens', () => { + const provider = new TestProvider(socketPath, socketOption); + provider['_reconnect'] = jest.fn(); + provider['isReconnecting'] = true; + + provider['_onError']({}); + + expect(provider['_reconnect']).toHaveBeenCalled(); + }); + + it('should call _reconnect when isReconnecting is false and an error happens', () => { + const provider = new TestProvider(socketPath, socketOption); + provider['_reconnect'] = jest.fn(); + provider['isReconnecting'] = false; + + provider['_onError']({}); + expect(provider['_reconnect']).not.toHaveBeenCalled(); + }); + + it('should return if the provider is already isReconnecting', async () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + // just to run the test faster moke `connect` + jest.spyOn(provider, 'connect'); + + // @ts-expect-error access protected method + expect(provider._reconnectAttempts).toBe(0); + provider['_reconnect'](); + // @ts-expect-error access protected method + expect(provider._reconnectAttempts).toBe(1); + + // after the first call provider.isReconnecting will set to true and so the `_reconnectAttempts` will not be incremented + provider['_reconnect'](); + + // @ts-expect-error access protected method + expect(provider._reconnectAttempts).toBe(1); + }); + + it('should reconnect the socket when the number of reconnect attempts is less than the maximum attempts', async () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + // @ts-expect-error access protected method + const openSocketConnectionSpy = jest.spyOn(provider, '_openSocketConnection'); + // @ts-expect-error access protected method + const removeSocketListenersSpy = jest.spyOn(provider, '_removeSocketListeners'); + const connectSpy = jest.spyOn(provider, 'connect'); + + // Set the reconnect attempts to less than the maximum attempts + provider['_reconnectAttempts'] = 2; + + provider['_reconnect'](); + + // wait for the timeout to trigger + await sleep(100); + + expect(openSocketConnectionSpy).toHaveBeenCalled(); + expect(removeSocketListenersSpy).toHaveBeenCalled(); + expect(connectSpy).toHaveBeenCalled(); + }); + + it('should clear the queues and emit an error event when the number of reconnect attempts reaches the maximum attempts', async () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + const clearQueuesSpy = jest.spyOn(provider as any, '_clearQueues'); + // @ts-expect-error access protected method + const removeSocketListenersSpy = jest.spyOn(provider, '_removeSocketListeners'); + const errorEventSpy = jest.spyOn(provider['_eventEmitter'], 'emit'); + + // Set the reconnect attempts to the maximum attempts + provider['_reconnectAttempts'] = 5; + + provider['_reconnect'](); + + // wait for the timeout to trigger + await sleep(100); + + expect(clearQueuesSpy).toHaveBeenCalled(); + expect(removeSocketListenersSpy).toHaveBeenCalled(); + expect(errorEventSpy).toHaveBeenCalledWith( + 'error', + expect.any(MaxAttemptsReachedOnReconnectingError), + ); + }); + + it('should keep pending requests but clear the sent requests queue when reconnecting', async () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + + provider.setStatus('connected'); + // Add a sent request + provider.request({ id: 2, method: 'some_rpc_method' }).catch(() => { + // it will throw with "Connection not open" because no actual connection is used in the test. So ignore the error + }); + // @ts-expect-error run protected method + expect(provider._sentRequestsQueue.size).toBe(1); + + // @ts-expect-error access protected method + const rejectSpy = jest.spyOn(provider['_pendingRequestsQueue'], 'delete'); + const deleteSpy = jest.spyOn(provider['_sentRequestsQueue'], 'delete'); + + const pendingRequestsQueueSize = provider['_pendingRequestsQueue'].size; + const sentRequestsQueueSize = provider['_sentRequestsQueue'].size; + + provider['_reconnect'](); + + expect(provider['_pendingRequestsQueue'].size).toEqual(pendingRequestsQueueSize); + + expect(deleteSpy).toHaveBeenCalledTimes(sentRequestsQueueSize); + }); }); describe('testing connect() method', () => { @@ -213,9 +364,7 @@ describe('SocketProvider', () => { const funcBSpy = jest // spy on provider.super._onDisconnect .spyOn( - Object.getPrototypeOf( - Object.getPrototypeOf(Object.getPrototypeOf(provider)), - ), + Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(provider))), '_onDisconnect', ) .mockReturnValue(new EventEmitter()); @@ -298,6 +447,30 @@ describe('SocketProvider', () => { // @ts-expect-error run protected method expect(provider._sentRequestsQueue.get(payload.id).payload).toBe(payload); }); + it('should clear _sentRequestsQueue in case `_sendToSocket` had an error', async () => { + // Create a mock SocketProvider instance + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + + const deleteSpy = jest.spyOn(provider['_sentRequestsQueue'], 'delete'); + + provider.setStatus('connected'); + // Assert that the _sendToSocket method was called with the correct payload + // @ts-expect-error access protected method + provider._sendToSocket = () => { + throw new Error('any error'); + }; + // Call the request method + provider + .request({ id: 1, method: 'some_rpc_method' }) + .then(() => { + // nothing + }) + .catch(() => { + // nothing + }); + + expect(deleteSpy).toHaveBeenCalled(); + }); }); describe('testing _clearQueues() method', () => { @@ -339,4 +512,207 @@ describe('SocketProvider', () => { }); }); }); -}); + + describe('safeDisconnect', () => { + it('should disconnect the socket when there are no pending or sent requests', async () => { + const provider = new TestProvider(socketPath, socketOption); + const disconnectSpy = jest.spyOn(provider, 'disconnect'); + await provider.safeDisconnect(); + expect(disconnectSpy).toHaveBeenCalled(); + }); + + it('should disconnect the socket after waiting for pending and sent requests to be empty', async () => { + const provider = new TestProvider(socketPath, socketOption); + const disconnectSpy = jest.spyOn(provider, 'disconnect'); + + // Add a pending request + provider.request({ id: 1, method: 'some_rpc_method' }).catch(() => { + // it will throw with "Connection not open" because no actual connection is used in the test. So ignore the error + }); + // Add a sent request + provider.request({ id: 2, method: 'some_rpc_method' }).catch(() => { + // it will throw with "Connection not open" because no actual connection is used in the test. So ignore the error + }); + expect(provider.getPendingRequestQueueSize()).toBe(2); + + provider.clearQueues(); + // Call safeDisconnect and wait for the queues to be empty + await provider.safeDisconnect(undefined, undefined, false, 100); + + expect(disconnectSpy).toHaveBeenCalled(); + expect(provider.getPendingRequestQueueSize()).toBe(0); + expect(provider.getSentRequestsQueueSize()).toBe(0); + }); + + it('should force disconnect the socket after waiting for 5 attempts', async () => { + const provider = new TestProvider(socketPath, socketOption); + const disconnectSpy = jest.spyOn(provider, 'disconnect'); + const clearQueuesSpy = jest.spyOn(provider as any, 'clearQueues'); + + // Add a pending request + provider.request({ id: 1, method: 'some_rpc_method' }).catch(() => { + // it will throw with "Connection not open" because no actual connection is used in the test. So ignore the error + }); + expect(provider.getPendingRequestQueueSize()).toBe(1); + + // Add a sent request + provider.request({ id: 2, method: 'some_rpc_method' }).catch(() => { + // it will throw with "Connection not open" because no actual connection is used in the test. So ignore the error + }); + // expect(provider.getSentRequestsQueueSize()).toBe(1); + + // Call safeDisconnect with forceDisconnect set to true and a small interval + await provider.safeDisconnect(undefined, undefined, true, 100); + + expect(disconnectSpy).toHaveBeenCalled(); + expect(clearQueuesSpy).toHaveBeenCalled(); + }); + }); + describe('removeAllListeners', () => { + it('should remove all listeners for the specified event type', () => { + const provider = new TestProvider(socketPath, socketOption); + const listener1 = jest.fn(); + const listener2 = jest.fn(); + const listener3 = jest.fn(); + provider.on('event', listener1); + provider.on('event', listener2); + provider.on('otherEvent', listener3); + + provider.removeAllListeners('event'); + + provider['_eventEmitter'].emit('event'); + provider['_eventEmitter'].emit('otherEvent'); + + expect(listener1).not.toHaveBeenCalled(); + expect(listener2).not.toHaveBeenCalled(); + expect(listener3).toHaveBeenCalled(); + }); + }); + + describe('_sendPendingRequests', () => { + it('should send pending requests to the socket', () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + + const payload1 = { id: 1, method: 'method1', params: [] }; + const payload2 = { id: 2, method: 'method2', params: [] }; + // Add a pending request + provider.request(payload1).catch(() => { + // it will throw with "Connection not open" because no actual connection is used in the test. So ignore the error + }); + // Add a sent request + provider.request(payload2).catch(() => { + // it will throw with "Connection not open" because no actual connection is used in the test. So ignore the error + }); + expect(provider.getPendingRequestQueueSize()).toBe(2); + + provider['_sendToSocket'] = jest.fn(); + + provider['_sendPendingRequests'](); + + expect(provider['_sendToSocket']).toHaveBeenCalledTimes(2); + expect(provider['_sendToSocket']).toHaveBeenCalledWith(payload1); + expect(provider['_sendToSocket']).toHaveBeenCalledWith(payload2); + expect(provider['_pendingRequestsQueue'].size).toBe(0); + expect(provider['_sentRequestsQueue'].size).toBe(2); + }); + + it('should not send any requests if the pending requests queue is empty', () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + provider['_sendToSocket'] = jest.fn(); + + provider['_sendPendingRequests'](); + + expect(provider['_sendToSocket']).not.toHaveBeenCalled(); + expect(provider['_pendingRequestsQueue'].size).toBe(0); + expect(provider['_sentRequestsQueue'].size).toBe(0); + }); + }); + + describe('_onConnect', () => { + it('should set the connection status to "connected"', () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + + // Act + provider['_onConnect'](); + + expect(provider['_connectionStatus']).toBe('connected'); + }); + }); + + describe('_onMessage', () => { + it('should resolve the deferred promise for valid responses with errors', () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + + const payload1 = { + id: 1, + method: 'method1', + params: [], + jsonrpc: '2.0' as JsonRpcIdentifier, + error: { code: -32601, message: 'Method not found' }, + }; + const event = { + data: JSON.stringify(payload1), + }; + // Add a pending request + provider.request(payload1).catch(() => { + // it will throw with "Connection not open" because no actual connection is used in the test. So ignore the error + }); + expect(provider.getPendingRequestQueueSize()).toBe(1); + + // @ts-expect-error access protected method + provider['_sentRequestsQueue'] = provider['_pendingRequestsQueue']; + + const deferredPromiseResolveSpy = jest.spyOn( + provider['_sentRequestsQueue'].get(1)!.deferredPromise, + 'resolve', + ); + provider['_onMessage']({ + ...event, + }); + + expect(deferredPromiseResolveSpy).toHaveBeenCalledWith(payload1); + }); + + it('should not emit "message" event for invalid responses', () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + const event = { + data: JSON.stringify([ + { id: 1, jsonrpc: '2.0', error: { code: -32601, message: 'Method not found' } }, + { id: 2, jsonrpc: '2.0', error: { code: -32601, message: 'Method not found' } }, + ]), + }; + + const eventEmitterSpy = jest.spyOn(provider['_eventEmitter'], 'emit'); + + provider['_onMessage'](event); + + expect(eventEmitterSpy).not.toHaveBeenCalledWith('message', { + id: 1, + jsonrpc: '2.0', + error: { code: -32601, message: 'Method not found' }, + }); + expect(eventEmitterSpy).not.toHaveBeenCalledWith('message', { + id: 2, + jsonrpc: '2.0', + error: { code: -32601, message: 'Method not found' }, + }); + }); + + it('should emit "message" event for notifications', () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + const event = { + data: JSON.stringify({ jsonrpc: '2.0', method: 'notification_1_subscription', params: {} }), + }; + + const eventEmitterSpy = jest.spyOn(provider['_eventEmitter'], 'emit'); + + provider['_onMessage'](event); + + expect(eventEmitterSpy).toHaveBeenCalledWith('message', { + jsonrpc: '2.0', + method: 'notification_1_subscription', + params: {}, + }); + }); + }); +}); \ No newline at end of file From 5dc439067f49fc5a17e0b2a0bfadf58bb1edf695 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Thu, 16 May 2024 15:15:54 +0200 Subject: [PATCH 02/17] add a test for SocketProvider --- .../web3-utils/test/unit/socket_provider.test.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/web3-utils/test/unit/socket_provider.test.ts b/packages/web3-utils/test/unit/socket_provider.test.ts index 9878879e3ba..9dd9ed69170 100644 --- a/packages/web3-utils/test/unit/socket_provider.test.ts +++ b/packages/web3-utils/test/unit/socket_provider.test.ts @@ -447,6 +447,22 @@ describe('SocketProvider', () => { // @ts-expect-error run protected method expect(provider._sentRequestsQueue.get(payload.id).payload).toBe(payload); }); + + it('should add request to the `_sentRequestsQueue` when the status is `connected` for batch requests', () => { + const provider = new TestProvider(socketPath, socketOption); + const payload = [ + { id: 1, method: 'some_rpc_method', jsonrpc: '2.0' as JsonRpcIdentifier }, + { id: 2, method: 'some_rpc_method', jsonrpc: '2.0' as JsonRpcIdentifier }, + ]; + provider.setStatus('connected'); + const reqPromise = provider.request(payload as any); + expect(reqPromise).toBeInstanceOf(Promise); + + // the id of the first request in the batch is the one used to identify the batch request + // @ts-expect-error run protected method + expect(provider._sentRequestsQueue.get(payload[0].id).payload).toBe(payload); + }); + it('should clear _sentRequestsQueue in case `_sendToSocket` had an error', async () => { // Create a mock SocketProvider instance const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); From 9f9d6df55a8b8bdd63c5dc66ab70811ac542aca7 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Thu, 16 May 2024 15:34:59 +0200 Subject: [PATCH 03/17] add a test for PromiseHelper --- packages/web3-utils/src/promise_helpers.ts | 2 +- .../test/unit/promise_helpers.test.ts | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/web3-utils/src/promise_helpers.ts b/packages/web3-utils/src/promise_helpers.ts index c8e211fbbb7..1b9c37ffc3a 100644 --- a/packages/web3-utils/src/promise_helpers.ts +++ b/packages/web3-utils/src/promise_helpers.ts @@ -146,7 +146,7 @@ export function rejectIfTimeout(timeout: number, error: Error): [Timer, Promise< /** * Sets an interval that repeatedly executes the given cond function with the specified interval between each call. * If the condition is met, the interval is cleared and a Promise that rejects with the returned value is returned. - * @param cond - The function/confition to call. + * @param cond - The function/condition to call. * @param interval - The interval in milliseconds. * @returns - an array with the interval ID and the Promise. */ diff --git a/packages/web3-utils/test/unit/promise_helpers.test.ts b/packages/web3-utils/test/unit/promise_helpers.test.ts index b18f4f1a5b0..f5df1dc1f25 100644 --- a/packages/web3-utils/test/unit/promise_helpers.test.ts +++ b/packages/web3-utils/test/unit/promise_helpers.test.ts @@ -60,6 +60,14 @@ describe('promise helpers', () => { new Error('time out'), ); }); + it('throws if result is an instance of Error', async () => { + const dummyError = new Error('dummy error'); + const asyncHelper = async () => { + return dummyError; + }; + + await expect(waitWithTimeout(asyncHelper(), 1000)).rejects.toThrow(dummyError); + }); }); describe('rejectIfTimeout', () => { it('%s', async () => { @@ -147,20 +155,22 @@ describe('promise helpers', () => { }); it('should return interval id if not resolved in specific time', async () => { - let counter = 0; const asyncHelper = async () => { if (counter <= 3000000) { counter += 1; return undefined; } - return "result"; + return 'result'; }; const testError = new Error('Test P2 Error'); - const [neverResolvePromise, intervalId] = pollTillDefinedAndReturnIntervalId(asyncHelper, 100); - const promiCheck = Promise.race([neverResolvePromise, rejectIfTimeout(500,testError)[1]]); + const [neverResolvePromise, intervalId] = pollTillDefinedAndReturnIntervalId( + asyncHelper, + 100, + ); + const promiCheck = Promise.race([neverResolvePromise, rejectIfTimeout(500, testError)[1]]); await expect(promiCheck).rejects.toThrow(testError); expect(intervalId).toBeDefined(); From 82a16b365e2f8558f53e300bf44ae4e6c5036ef1 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Mon, 20 May 2024 02:29:32 +0200 Subject: [PATCH 04/17] add unit tests for utils --- .../web3-utils/test/fixtures/converters.ts | 1 + .../web3-utils/test/unit/converters.test.ts | 46 +++++++++++-- packages/web3-utils/test/unit/index.test.ts | 64 +++++++++++++++++++ packages/web3-utils/test/unit/objects.test.ts | 9 +++ .../test/unit/web3_deferred_promise.test.ts | 15 ++++- 5 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 packages/web3-utils/test/unit/index.test.ts diff --git a/packages/web3-utils/test/fixtures/converters.ts b/packages/web3-utils/test/fixtures/converters.ts index 91f3a76f738..3a443ad4079 100644 --- a/packages/web3-utils/test/fixtures/converters.ts +++ b/packages/web3-utils/test/fixtures/converters.ts @@ -359,6 +359,7 @@ export const bytesToUint8ArrayValidData: [Bytes, Uint8Array][] = [ ['0x1234', new Uint8Array([18, 52])], ['0x1234', new Uint8Array([18, 52])], [new Uint8Array(hexToBytes('0c12')), new Uint8Array(hexToBytes('0c12'))], + [[72, 12] as any, new Uint8Array([72, 12])], ]; export const toBigIntValidData: [any, bigint][] = [ diff --git a/packages/web3-utils/test/unit/converters.test.ts b/packages/web3-utils/test/unit/converters.test.ts index 940590f06e7..8710eaee2e9 100644 --- a/packages/web3-utils/test/unit/converters.test.ts +++ b/packages/web3-utils/test/unit/converters.test.ts @@ -15,6 +15,9 @@ You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see . */ +import { InvalidBytesError } from 'web3-errors'; +import { validator, utils } from 'web3-validator'; + import { asciiToHex, bytesToHex, @@ -76,7 +79,6 @@ import { toBoolValidData, toBoolInvalidData, } from '../fixtures/converters'; - describe('converters', () => { describe('bytesToHex', () => { describe('valid cases', () => { @@ -375,10 +377,9 @@ describe('converters', () => { it.each(toWeiValidDataWarnings)('%s', (input, output) => { toWei(input[0], input[1]); // expect(() => toWei(input[0], input[1])).toThrow(output); - expect(console.warn).toHaveBeenCalledWith(output) + expect(console.warn).toHaveBeenCalledWith(output); }); - - }) + }); }); describe('toChecksumAddress', () => { describe('valid cases', () => { @@ -391,6 +392,31 @@ describe('converters', () => { expect(() => toChecksumAddress(input)).toThrow(output); }); }); + it('should return an empty string if hash is nullish', () => { + const address = '0xc1912fee45d61c87cc5ea59dae31190fffff232d'; + + // mock utils.uint8ArrayToHexString to return an empty string + jest.mock('web3-validator'); + jest.spyOn(utils, 'uint8ArrayToHexString').mockReturnValue(undefined as unknown as string); + + const result = toChecksumAddress(address); + expect(result).toEqual(''); + + jest.mock('web3-validator').restoreAllMocks(); + }); + + it('should return an empty string if hash is equal to "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"', () => { + const address = '0xc1912fee45d61c87cc5ea59dae31190fffff232d'; + + // mock utils.uint8ArrayToHexString to return '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' + jest.mock('web3-validator'); + const hash = '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'; + jest.spyOn(utils, 'uint8ArrayToHexString').mockReturnValue(hash); + const result = toChecksumAddress(address); + expect(result).toEqual(''); + + jest.mock('web3-validator').restoreAllMocks(); + }); }); describe('bytesToUint8Array', () => { describe('bytesToUint8Array', () => { @@ -404,6 +430,18 @@ describe('converters', () => { it.each(bytesToUint8ArrayInvalidData)('%s', (input, output) => { expect(() => bytesToUint8Array(input)).toThrow(output); }); + + it('should throw InvalidBytesError for invalid input even if it passed the validator', () => { + const invalidData = 8; + // the package 'web3-validator' contains `validator`. + // Mock mock the `validator.validate(...)` to not throw an error, but return `false` instead. + jest.mock('web3-validator'); + + jest.spyOn(validator, 'validate').mockReturnValue(undefined); + + expect(() => bytesToUint8Array(invalidData as any)).toThrow(InvalidBytesError); + jest.mock('web3-validator').restoreAllMocks(); + }); }); }); }); diff --git a/packages/web3-utils/test/unit/index.test.ts b/packages/web3-utils/test/unit/index.test.ts new file mode 100644 index 00000000000..8682408801a --- /dev/null +++ b/packages/web3-utils/test/unit/index.test.ts @@ -0,0 +1,64 @@ +/* +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 web3Utils from '../../src'; + +import * as converters from '../../src/converters.js'; +import * as eventEmitter from '../../src/event_emitter.js'; +import * as validation from '../../src/validation.js'; +import * as formatter from '../../src/formatter.js'; +import * as hash from '../../src/hash.js'; +import * as random from '../../src/random.js'; +import * as stringManipulation from '../../src/string_manipulation.js'; +import * as objects from '../../src/objects.js'; +import * as promiseHelpers from '../../src/promise_helpers.js'; +import * as jsonRpc from '../../src/json_rpc.js'; +import * as web3DeferredPromise from '../../src/web3_deferred_promise.js'; +import * as ChunkResponseParser from '../../src/chunk_response_parser.js'; +import * as uuid from '../../src/uuid.js'; +import * as web3Eip1193Provider from '../../src/web3_eip1193_provider.js'; +import * as socketProvider from '../../src/socket_provider.js'; +import * as uint8array from '../../src/uint8array.js'; + +describe('web3-utils exports', () => { + it('should export all modules', () => { + const modules = [ + converters, + eventEmitter, + validation, + formatter, + hash, + random, + stringManipulation, + objects, + promiseHelpers, + jsonRpc, + web3DeferredPromise, + ChunkResponseParser, + uuid, + web3Eip1193Provider, + socketProvider, + uint8array, + ]; + + modules.forEach(module => { + Object.keys(module).forEach((property: string | any[]) => { + expect(web3Utils).toHaveProperty(property); + }); + }); + }); +}); diff --git a/packages/web3-utils/test/unit/objects.test.ts b/packages/web3-utils/test/unit/objects.test.ts index 1201b3b7345..cca9d46cba9 100644 --- a/packages/web3-utils/test/unit/objects.test.ts +++ b/packages/web3-utils/test/unit/objects.test.ts @@ -79,5 +79,14 @@ describe('objects', () => { expect(result.a).toStrictEqual(new Uint8Array([1, 2])); }); + + it('should return the destination object if it is not iterable', () => { + const destination = 123; // Replace with your desired destination object + const sources: Record[] = []; // Replace with your desired sources array + + const result = mergeDeep(destination as unknown as Record, ...sources); + + expect(result).toBe(destination); + }); }); }); diff --git a/packages/web3-utils/test/unit/web3_deferred_promise.test.ts b/packages/web3-utils/test/unit/web3_deferred_promise.test.ts index b8180705969..044f558fb10 100644 --- a/packages/web3-utils/test/unit/web3_deferred_promise.test.ts +++ b/packages/web3-utils/test/unit/web3_deferred_promise.test.ts @@ -53,4 +53,17 @@ describe('Web3DeferredPromise', () => { expect(promise.state).toBe('rejected'); }); }); -}); + + describe('Web3DeferredPromise finally', () => { + it('should execute the callback when the promise is settled', async () => { + const promise = new Web3DeferredPromise(); + let callbackExecuted = false; + promise.resolve(1); + await promise.finally(() => { + callbackExecuted = true; + }); + + expect(callbackExecuted).toBe(true); + }); + }); +}); \ No newline at end of file From ad73c898aa671ae3df66e3a3454a1c5335297d18 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Mon, 20 May 2024 02:29:47 +0200 Subject: [PATCH 05/17] remove un visited code branch --- packages/web3-utils/src/converters.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/web3-utils/src/converters.ts b/packages/web3-utils/src/converters.ts index 55143a4277c..8e34e17bb49 100644 --- a/packages/web3-utils/src/converters.ts +++ b/packages/web3-utils/src/converters.ts @@ -383,9 +383,6 @@ export const toHex = ( if (isHex(value) && !isInt(value) && !isUInt(value)) { return returnType ? 'bytes' : `0x${value}`; } - if (isHex(value) && !isInt(value) && isUInt(value)) { - return returnType ? 'uint' : numberToHex(value); - } if (!Number.isFinite(value)) { return returnType ? 'string' : utf8ToHex(value); From 0d09e92024785eb7368db520a1b80665a5512434 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Mon, 20 May 2024 02:35:11 +0200 Subject: [PATCH 06/17] fix linting issue --- packages/web3-utils/test/unit/converters.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/web3-utils/test/unit/converters.test.ts b/packages/web3-utils/test/unit/converters.test.ts index 8710eaee2e9..7c566aaa2a1 100644 --- a/packages/web3-utils/test/unit/converters.test.ts +++ b/packages/web3-utils/test/unit/converters.test.ts @@ -79,6 +79,7 @@ import { toBoolValidData, toBoolInvalidData, } from '../fixtures/converters'; + describe('converters', () => { describe('bytesToHex', () => { describe('valid cases', () => { @@ -400,7 +401,7 @@ describe('converters', () => { jest.spyOn(utils, 'uint8ArrayToHexString').mockReturnValue(undefined as unknown as string); const result = toChecksumAddress(address); - expect(result).toEqual(''); + expect(result).toBe(''); jest.mock('web3-validator').restoreAllMocks(); }); @@ -413,7 +414,7 @@ describe('converters', () => { const hash = '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'; jest.spyOn(utils, 'uint8ArrayToHexString').mockReturnValue(hash); const result = toChecksumAddress(address); - expect(result).toEqual(''); + expect(result).toBe(''); jest.mock('web3-validator').restoreAllMocks(); }); From b0e0bb40793b93ee14e2f30736d7034b65e0e14a Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Mon, 20 May 2024 03:49:37 +0200 Subject: [PATCH 07/17] point to a pice of code to investigate --- packages/web3-utils/src/converters.ts | 12 ++++++++++++ packages/web3-utils/test/unit/converters.test.ts | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/packages/web3-utils/src/converters.ts b/packages/web3-utils/src/converters.ts index 8e34e17bb49..bce8e635044 100644 --- a/packages/web3-utils/src/converters.ts +++ b/packages/web3-utils/src/converters.ts @@ -383,6 +383,18 @@ export const toHex = ( if (isHex(value) && !isInt(value) && !isUInt(value)) { return returnType ? 'bytes' : `0x${value}`; } + if (isHex(value) && !isInt(value) && isUInt(value)) { + // This condition seems problematic because meeting + // both conditions `!isInt(value) && isUInt(value)` should be impossible. + // But a value pass for those conditions: "101611154195520776335741463917853444671577865378275924493376429267637792638729" + // Note that according to the docs: it is supposed to be treated as a string (https://docs.web3js.org/guides/web3_upgrade_guide/x/web3_utils_migration_guide#conversion-to-hex) + // In short, the strange is that isInt(value) is false but isUInt(value) is true for the value above. + // So, isUInt(value) should be investigated. + + // However, if `toHex('101611154195520776335741463917853444671577865378275924493376429267637792638729', true)` is called, it will return `true`. + // But, if `toHex('101611154195520776335741463917853444671577865378275924493376429267637792638729')` is called, it will throw inside `numberToHex`. + return returnType ? 'uint' : numberToHex(value); + } if (!Number.isFinite(value)) { return returnType ? 'string' : utf8ToHex(value); diff --git a/packages/web3-utils/test/unit/converters.test.ts b/packages/web3-utils/test/unit/converters.test.ts index 7c566aaa2a1..2ad49619652 100644 --- a/packages/web3-utils/test/unit/converters.test.ts +++ b/packages/web3-utils/test/unit/converters.test.ts @@ -328,6 +328,16 @@ describe('converters', () => { it.each(toHexValidData)('%s', (input, output) => { expect(toHex(input, true)).toEqual(output[1]); }); + + it('an interesting case that needs investigation', () => { + // This case is to be investigated further + expect( + toHex( + '101611154195520776335741463917853444671577865378275924493376429267637792638729', + true, + ), + ).toEqual('uint'); + }); }); describe('invalid cases', () => { From 5b9bb46fc06f38d25b792967cd8a86d0deab3fc0 Mon Sep 17 00:00:00 2001 From: Muhammad-Altabba <24407834+Muhammad-Altabba@users.noreply.github.com> Date: Mon, 20 May 2024 03:52:53 +0200 Subject: [PATCH 08/17] fix linting issue --- packages/web3-utils/test/unit/converters.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web3-utils/test/unit/converters.test.ts b/packages/web3-utils/test/unit/converters.test.ts index 2ad49619652..ae4a461ac08 100644 --- a/packages/web3-utils/test/unit/converters.test.ts +++ b/packages/web3-utils/test/unit/converters.test.ts @@ -336,7 +336,7 @@ describe('converters', () => { '101611154195520776335741463917853444671577865378275924493376429267637792638729', true, ), - ).toEqual('uint'); + ).toBe('uint'); }); }); From 1392b05e9a328e47c90870ed4e7b18c2706aec2b Mon Sep 17 00:00:00 2001 From: Alex Luu Date: Thu, 23 May 2024 14:55:33 -0400 Subject: [PATCH 09/17] add coverage to hash --- packages/web3-utils/src/hash.ts | 3 --- packages/web3-utils/test/fixtures/converters.ts | 3 ++- packages/web3-utils/test/fixtures/hash.ts | 6 ++++++ packages/web3-utils/test/unit/hash.test.ts | 8 ++++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/web3-utils/src/hash.ts b/packages/web3-utils/src/hash.ts index 6e498138c29..f7acc4f4fb9 100644 --- a/packages/web3-utils/src/hash.ts +++ b/packages/web3-utils/src/hash.ts @@ -169,9 +169,6 @@ export { keccak256Wrapper as keccak256 }; * @returns - the type and value of the input */ const getType = (arg: Sha3Input): [string, EncodingTypes] => { - if (Array.isArray(arg)) { - throw new Error('Autodetection of array types is not supported.'); - } let type; let value; diff --git a/packages/web3-utils/test/fixtures/converters.ts b/packages/web3-utils/test/fixtures/converters.ts index 2c20b823380..faa9f8bfe04 100644 --- a/packages/web3-utils/test/fixtures/converters.ts +++ b/packages/web3-utils/test/fixtures/converters.ts @@ -328,7 +328,7 @@ export const fromWeiValidData: [[Numbers, EtherUnits | number], Numbers][] = [ [['1123456789123456789', 'ether'], '1.123456789123456789'], [['1123', 'kwei'], '1.123'], [['1234100' ,'kwei'], '1234.1'], - [['3308685546611893', 'ether'], '0.003308685546611893'] + [['3308685546611893', 'ether'], '0.003308685546611893'], ]; export const toWeiValidData: [[Numbers, EtherUnits | number], Numbers][] = [ @@ -374,6 +374,7 @@ export const toWeiInvalidData: [[any, any], string][] = [ [[{}, 'kwei'], 'value "{}" at "/0" must pass "number" validation'], [['data', 'kwei'], 'value "data" at "/0" must pass "number" validation'], [['1234', 'uwei'], 'Invalid value given "uwei". Error: invalid unit.'], + [['123', -1], 'Invalid value given "-1". Error: not a valid unit. Must be a positive integer.'], ]; export const toCheckSumValidData: [string, string][] = [ ['0x0089d53f703f7e0843953d48133f74ce247184c2', '0x0089d53F703f7E0843953D48133f74cE247184c2'], diff --git a/packages/web3-utils/test/fixtures/hash.ts b/packages/web3-utils/test/fixtures/hash.ts index 6bfe9f63f33..4f337c191ae 100644 --- a/packages/web3-utils/test/fixtures/hash.ts +++ b/packages/web3-utils/test/fixtures/hash.ts @@ -256,6 +256,7 @@ export const encodePackData: [TypedObject[] | TypedObjectAbbreviated[], any][] = '0x12480000000000000000000000000000000000000000000000000000000000003c69a194aaf415ba5d6afca734660d0a3d45acdc05d54cd1ca89a8988e7625b4', ], [[{ type: 'bytes4[]', value: ['0x11223344', '0x22334455'] }], '0x1122334422334455'], + [[{ type: '', value: '31323334' }], '0x'], ]; export const encodePackedInvalidData: [any, string][] = [ @@ -349,3 +350,8 @@ export const soliditySha3BigIntValidData: [Sha3Input[], string][] = [ return keccak256(abi.encodePacked(int(90071992547409))) ;} */ ]; + +export const getStorageSlotNumForLongStringValidData: [string|number, string | undefined][] + = [ + [0, "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"] + ]; \ No newline at end of file diff --git a/packages/web3-utils/test/unit/hash.test.ts b/packages/web3-utils/test/unit/hash.test.ts index d381e3c4240..13b4c6aa1df 100644 --- a/packages/web3-utils/test/unit/hash.test.ts +++ b/packages/web3-utils/test/unit/hash.test.ts @@ -23,6 +23,7 @@ import { soliditySha3Raw, encodePacked, keccak256 as web3keccak256, + getStorageSlotNumForLongString, } from '../../src/hash'; import { sha3Data, @@ -37,6 +38,7 @@ import { encodePackedInvalidData, keccak256ValidData, soliditySha3BigIntValidData, + getStorageSlotNumForLongStringValidData, } from '../fixtures/hash'; describe('hash', () => { @@ -149,4 +151,10 @@ describe('hash', () => { }); }); }); + + describe('getStorageSlotNumForLongString', () => { + it.each(getStorageSlotNumForLongStringValidData)('%s', (input, output) => { + expect(getStorageSlotNumForLongString(input)).toEqual(output); + }); + }) }); From f9559f334502ab4a3a81a3c24e02993680627ef2 Mon Sep 17 00:00:00 2001 From: Alex Luu Date: Thu, 23 May 2024 17:17:09 -0400 Subject: [PATCH 10/17] cover json-rpc tests --- packages/web3-utils/src/json_rpc.ts | 2 +- packages/web3-utils/test/fixtures/json_rpc.ts | 17 ++++++++ .../web3-utils/test/unit/json_rpc.test.ts | 42 +++++++++++++++++-- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/packages/web3-utils/src/json_rpc.ts b/packages/web3-utils/src/json_rpc.ts index 573d39197e3..495a425611f 100644 --- a/packages/web3-utils/src/json_rpc.ts +++ b/packages/web3-utils/src/json_rpc.ts @@ -99,7 +99,7 @@ let requestIdSeed: number | undefined; /** * Optionally use to make the jsonrpc `id` start from a specific number. * Without calling this function, the `id` will be filled with a Uuid. - * But after this being called with a number, the `id` will be a number staring from the provided `start` variable. + * But after this being called with a number, the `id` will be a number starting from the provided `start` variable. * However, if `undefined` was passed to this function, the `id` will be a Uuid again. * @param start - a number to start incrementing from. * Or `undefined` to use a new Uuid (this is the default behavior) diff --git a/packages/web3-utils/test/fixtures/json_rpc.ts b/packages/web3-utils/test/fixtures/json_rpc.ts index 516711abceb..2be0f19b435 100644 --- a/packages/web3-utils/test/fixtures/json_rpc.ts +++ b/packages/web3-utils/test/fixtures/json_rpc.ts @@ -18,6 +18,7 @@ import { JsonRpcNotification, SubscriptionParams } from 'web3-types'; const responseWithResult = { jsonrpc: '2.0', id: 1, result: '' }; const responseWithError = { jsonrpc: '2.0', id: 1, error: { code: 1, message: 'string' } }; +const responseWithRpcError = { jsonrpc: '2.0', id: 1, error: { code: -32000, message: 'string' } }; const responseWithSubscription = { id: 1, jsonrpc: '2.0', result: '' }; const responseWithNotfication = { jsonrpc: '2.0', @@ -27,11 +28,13 @@ const responseWithNotfication = { export const isResponseWithResultValidTest: [any, boolean][] = [ [responseWithResult, true], [responseWithError, false], + [{...responseWithResult, id: '1'}, true], ]; export const isResponseWithErrorValidTest: [any, boolean][] = [ [responseWithResult, false], [responseWithError, true], + [{...responseWithError, id: '1'}, true], ]; export const isResponseWithNotificationValidTest: [JsonRpcNotification, boolean][] = [ @@ -63,4 +66,18 @@ export const toPayloadValidTest: [any, any][] = [ params: undefined, }, ], + [ + { method: 'add', jsonrpc: '1.0', id: 1}, + { + method: 'add', + id: 1, + jsonrpc: '1.0', + params: undefined + }, + ] ]; + +export const isResponseRpcErrorValidData: [any, boolean][] = [ + [responseWithRpcError, true], + [responseWithError, false], +] \ No newline at end of file diff --git a/packages/web3-utils/test/unit/json_rpc.test.ts b/packages/web3-utils/test/unit/json_rpc.test.ts index 89261009916..2cb958aad59 100644 --- a/packages/web3-utils/test/unit/json_rpc.test.ts +++ b/packages/web3-utils/test/unit/json_rpc.test.ts @@ -16,17 +16,21 @@ along with web3.js. If not, see . */ import { + isResponseRpcError, isResponseWithResult, isResponseWithError, isResponseWithNotification, isSubscriptionResult, isValidResponse, isBatchResponse, + setRequestIdStart, + toBatchPayload, toPayload, } from '../../src/json_rpc'; import { isResponseWithResultValidTest, isResponseWithErrorValidTest, + isResponseRpcErrorValidData, isResponseWithNotificationValidTest, isSubscriptionResultValidTest, toPayloadValidTest, @@ -51,6 +55,14 @@ describe('json rpc tests', () => { }); }); }); + describe('isResponseRpcError', () => { + describe('valid cases', () => { + it.each(isResponseRpcErrorValidData)('%s', (input, output) => { + const result = isResponseRpcError(input); + expect(result).toBe(output); + }); + }); + }); describe('isResponseWithNotification', () => { describe('valid cases', () => { it.each(isResponseWithNotificationValidTest)('should have notify', (input, output) => { @@ -85,10 +97,34 @@ describe('json rpc tests', () => { }); describe('toPayloadValid', () => { describe('valid cases', () => { - it.each(toPayloadValidTest)('isValidresponse valid test', (input, output) => { - const result = toPayload(input); + beforeEach (() => { + setRequestIdStart(undefined) + }); + it.each(toPayloadValidTest)('toPayload valid test', async (input, output) => { + const result = await new Promise((resolve) => { + resolve(toPayload(input)) + }) expect(result).toStrictEqual(output); }); + it('should give payload that has requestid set', async () => { + setRequestIdStart(1) + const result = await new Promise((resolve) => { + resolve(toPayload({ method: 'delete' })) + }) + expect(result).toStrictEqual({method: 'delete', id:2, params: undefined, jsonrpc: '2.0'}); + }); }); }); -}); + describe('toBatchPayload', () => { + it('should batch payload', async () => { + setRequestIdStart(0) + const result = await new Promise((resolve) => { + resolve(toBatchPayload([{ method: 'delete' }, {method: 'add'}])) + }) + expect(result).toStrictEqual([ + {method: 'delete', id:1, params: undefined, jsonrpc: '2.0'}, + {method: 'add', id:2, params: undefined, jsonrpc: '2.0'} + ]); + }); + }) +}); \ No newline at end of file From d61bd1dc8b55a2f9e96cd50b436bd631a3c4e60f Mon Sep 17 00:00:00 2001 From: Alex Luu Date: Thu, 23 May 2024 17:45:11 -0400 Subject: [PATCH 11/17] cover socket provider file --- packages/web3-utils/test/fixtures/json_rpc.ts | 15 +++++++++++++++ packages/web3-utils/test/unit/json_rpc.test.ts | 11 ++++++++++- .../web3-utils/test/unit/socket_provider.test.ts | 8 +++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/web3-utils/test/fixtures/json_rpc.ts b/packages/web3-utils/test/fixtures/json_rpc.ts index 2be0f19b435..0b87a35ec59 100644 --- a/packages/web3-utils/test/fixtures/json_rpc.ts +++ b/packages/web3-utils/test/fixtures/json_rpc.ts @@ -80,4 +80,19 @@ export const toPayloadValidTest: [any, any][] = [ export const isResponseRpcErrorValidData: [any, boolean][] = [ [responseWithRpcError, true], [responseWithError, false], +] + +export const isBatchRequestValidData: [any, boolean][] = [ + [[{ + method: 'add', + id: 1, + jsonrpc: '1.0', + params: undefined + }], true], + [{ + method: 'add', + id: 1, + jsonrpc: '1.0', + params: undefined + }, false] ] \ No newline at end of file diff --git a/packages/web3-utils/test/unit/json_rpc.test.ts b/packages/web3-utils/test/unit/json_rpc.test.ts index 2cb958aad59..27dee1a0354 100644 --- a/packages/web3-utils/test/unit/json_rpc.test.ts +++ b/packages/web3-utils/test/unit/json_rpc.test.ts @@ -25,6 +25,7 @@ import { isBatchResponse, setRequestIdStart, toBatchPayload, + isBatchRequest, toPayload, } from '../../src/json_rpc'; import { @@ -36,6 +37,7 @@ import { toPayloadValidTest, isValidResponseValidTest, isBatchResponseValidTest, + isBatchRequestValidData, } from '../fixtures/json_rpc'; describe('json rpc tests', () => { @@ -89,12 +91,19 @@ describe('json rpc tests', () => { }); describe('isBatchResponseValid', () => { describe('valid cases', () => { - it.each(isBatchResponseValidTest)('isValidresponse valid test', (input, output) => { + it.each(isBatchResponseValidTest)('isBatchResponseValid valid test', (input, output) => { const result = isBatchResponse(input); expect(result).toBe(output); }); }); }); + describe('isBatchRequest', () => { + describe('valid cases', () => { + it.each(isBatchRequestValidData)('isBatchRqeuest valid data', (input, output) => { + expect(isBatchRequest(input)).toBe(output); + }) + }) + }) describe('toPayloadValid', () => { describe('valid cases', () => { beforeEach (() => { diff --git a/packages/web3-utils/test/unit/socket_provider.test.ts b/packages/web3-utils/test/unit/socket_provider.test.ts index 9dd9ed69170..b116111e3aa 100644 --- a/packages/web3-utils/test/unit/socket_provider.test.ts +++ b/packages/web3-utils/test/unit/socket_provider.test.ts @@ -22,7 +22,7 @@ import { Web3ProviderStatus, JsonRpcIdentifier, } from 'web3-types'; -import { MaxAttemptsReachedOnReconnectingError } from 'web3-errors'; +import { MaxAttemptsReachedOnReconnectingError, InvalidClientError } from 'web3-errors'; import { EventEmitter } from '../../src/event_emitter'; // eslint-disable-next-line import/no-relative-packages import { sleep } from '../../../../fixtures/utils'; @@ -120,6 +120,12 @@ describe('SocketProvider', () => { expect(clearQueuesSpy).toHaveBeenCalled(); }); + it('should error when failing to _validateProviderPath', () => { + expect(() => { + // eslint-disable-next-line no-new + new TestProvider("", socketOption, { delay: 0 }); + }).toThrow(InvalidClientError) + }) }); describe('testing _reconnect() method', () => { it('should not be called when { autoReconnect: false }', () => { From e8ccfffcc0889943aafc8529fb5385357283642c Mon Sep 17 00:00:00 2001 From: Alex Luu Date: Thu, 23 May 2024 17:53:49 -0400 Subject: [PATCH 12/17] add coverage string manipulation --- packages/web3-utils/test/fixtures/string_manipulation.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/web3-utils/test/fixtures/string_manipulation.ts b/packages/web3-utils/test/fixtures/string_manipulation.ts index be5b298c803..86be918051d 100644 --- a/packages/web3-utils/test/fixtures/string_manipulation.ts +++ b/packages/web3-utils/test/fixtures/string_manipulation.ts @@ -58,7 +58,7 @@ export const padRightData: [[Numbers, number, string], HexString][] = [ [['15.5', 8, '0'], '15.50000'], ]; -export const toTwosComplementData: [[Numbers, number], HexString][] = [ +export const toTwosComplementData: [[Numbers, number | undefined], HexString][] = [ [[13, 32], '0x0000000000000000000000000000000d'], [[256, 30], '0x000000000000000000000000000100'], [[0, 32], '0x00000000000000000000000000000000'], @@ -69,9 +69,10 @@ export const toTwosComplementData: [[Numbers, number], HexString][] = [ [['13', 32], '0x0000000000000000000000000000000d'], [['-13', 32], '0xfffffffffffffffffffffffffffffff3'], [[-16, 2], '0xf0'], + [['0x1', undefined], '0x0000000000000000000000000000000000000000000000000000000000000001'] ]; -export const fromTwosComplementData: [[Numbers, number], number | bigint][] = [ +export const fromTwosComplementData: [[Numbers, number | undefined], number | bigint][] = [ [['0x0000000000000000000000000000000d', 32], 13], [['0x000000000000000000000000000100', 30], 256], [['0x00000000000000000020000000000000', 32], BigInt('9007199254740992')], @@ -81,6 +82,7 @@ export const fromTwosComplementData: [[Numbers, number], number | bigint][] = [ [[1000, 64], 1000], [[-1000, 64], -1000], [[BigInt(9), 1], -7], + [['0x0000000000000000000000000000000000000000000000000000000000000001', undefined], 1] ]; export const toTwosComplementInvalidData: [[Numbers, number], string][] = [ From 4ad228a992fa7237d977494aec2ae31144018382 Mon Sep 17 00:00:00 2001 From: Alex Luu Date: Wed, 29 May 2024 17:05:33 -0400 Subject: [PATCH 13/17] add coverage to formatters.ts --- packages/web3-utils/src/formatter.ts | 14 +- .../web3-utils/src/web3_eip1193_provider.ts | 3 +- .../web3-utils/test/fixtures/formatter.ts | 3 +- .../test/unit/chunk_response_parser.test.ts | 14 ++ .../web3-utils/test/unit/converters.test.ts | 1 - .../web3-utils/test/unit/formatter.test.ts | 159 +++++++++++++++++- packages/web3-utils/test/unit/hash.test.ts | 14 ++ .../test/unit/socket_provider.test.ts | 123 ++++++++++++++ 8 files changed, 317 insertions(+), 14 deletions(-) diff --git a/packages/web3-utils/src/formatter.ts b/packages/web3-utils/src/formatter.ts index 42062add5c6..102d0dead4f 100644 --- a/packages/web3-utils/src/formatter.ts +++ b/packages/web3-utils/src/formatter.ts @@ -57,10 +57,8 @@ const findSchemaByDataPath = ( for (const dataPart of dataPath) { if (result.oneOf && previousDataPath) { - const path = oneOfPath.find(function (element: [string, number]) { - return (this as unknown as string) === element[0]; - }, previousDataPath ?? ''); - + const currentDataPath = previousDataPath; + const path = oneOfPath.find(([key]) => key === currentDataPath); if (path && path[0] === previousDataPath) { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access result = result.oneOf[path[1]]; @@ -74,11 +72,7 @@ const findSchemaByDataPath = ( result = (result.properties as Record)[dataPart]; } else if (result.items && (result.items as JsonSchema).properties) { const node = (result.items as JsonSchema).properties as Record; - - if (!node) { - return undefined; - } - + result = node[dataPart]; } else if (result.items && isObject(result.items)) { result = result.items; @@ -307,7 +301,7 @@ export const convert = ( // If value is an object, recurse into it if (isObject(value)) { - convert(value, schema, dataPath, format); + convert(value, schema, dataPath, format, oneOfPath); dataPath.pop(); continue; } diff --git a/packages/web3-utils/src/web3_eip1193_provider.ts b/packages/web3-utils/src/web3_eip1193_provider.ts index f0e2d4d30bf..036ab03adc3 100644 --- a/packages/web3-utils/src/web3_eip1193_provider.ts +++ b/packages/web3-utils/src/web3_eip1193_provider.ts @@ -68,7 +68,7 @@ export abstract class Eip1193Provider< this._chainId = chainId; this._eventEmitter.emit('chainChanged', this._chainId); } - }) + }) .catch(err => { // todo: add error handler console.error(err); @@ -98,6 +98,7 @@ export abstract class Eip1193Provider< } as ProviderConnectInfo), ) .catch(err => { + console.log("enters") // todo: add error handler // eslint-disable-next-line no-console console.error(err); diff --git a/packages/web3-utils/test/fixtures/formatter.ts b/packages/web3-utils/test/fixtures/formatter.ts index 7437373d708..cc4c08c28ca 100644 --- a/packages/web3-utils/test/fixtures/formatter.ts +++ b/packages/web3-utils/test/fixtures/formatter.ts @@ -55,4 +55,5 @@ export const convertScalarValueValid: [[any, any, any], any][] = [ hexToBytes('0x00000000000000000000000000000000000000000000000000000000000000ff'), ), ], -]; + [[new Uint8Array(hexToBytes('FF')), 'bytes32', { bytes: "invalidFormat" }], [255]] // return original value when erroring +]; \ No newline at end of file diff --git a/packages/web3-utils/test/unit/chunk_response_parser.test.ts b/packages/web3-utils/test/unit/chunk_response_parser.test.ts index 22100a3e6a8..707197e2889 100644 --- a/packages/web3-utils/test/unit/chunk_response_parser.test.ts +++ b/packages/web3-utils/test/unit/chunk_response_parser.test.ts @@ -71,4 +71,18 @@ describe('chunk_response_parser', () => { }), ); }); + + it('lastChunkTimeout return empty when auto reconnect true', async () => { + const p = new ChunkResponseParser(eventEmiter, true); + // @ts-expect-error set private property + p.chunkTimeout = 10; + const result = p.parseResponse( + '{"jsonrpc":"2.0","id":"96aa3f13-077c-4c82-a64a-64b8626f8192","result":"0x141414141', + ); + const onError = jest.fn(); + eventEmiter.on('error', onError); + // eslint-disable-next-line no-promise-executor-return + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(result).toEqual([]) + }); }); diff --git a/packages/web3-utils/test/unit/converters.test.ts b/packages/web3-utils/test/unit/converters.test.ts index ae4a461ac08..7481d5008ce 100644 --- a/packages/web3-utils/test/unit/converters.test.ts +++ b/packages/web3-utils/test/unit/converters.test.ts @@ -387,7 +387,6 @@ describe('converters', () => { }); it.each(toWeiValidDataWarnings)('%s', (input, output) => { toWei(input[0], input[1]); - // expect(() => toWei(input[0], input[1])).toThrow(output); expect(console.warn).toHaveBeenCalledWith(output); }); }); diff --git a/packages/web3-utils/test/unit/formatter.test.ts b/packages/web3-utils/test/unit/formatter.test.ts index bed8b55de8e..4e235081ddb 100644 --- a/packages/web3-utils/test/unit/formatter.test.ts +++ b/packages/web3-utils/test/unit/formatter.test.ts @@ -24,9 +24,10 @@ import { HexString, Numbers, } from 'web3-types'; +import {FormatterError} from 'web3-errors'; import { expectTypeOf, typecheck } from '@humeris/espresso-shot'; import { isDataFormatValid, convertScalarValueValid } from '../fixtures/formatter'; -import { format, isDataFormat, convertScalarValue } from '../../src/formatter'; +import { format, isDataFormat, convertScalarValue, convert } from '../../src/formatter'; import { hexToBytes } from '../../src/converters'; type TestTransactionInfoType = { @@ -584,6 +585,40 @@ describe('formatter', () => { ).toEqual(result); }); + it('should format array of objects with', () => { + const schema = { + type: 'array', + items: { + type: 'object', + properties: { + prop1: { + oneOf: [{ format: 'address' }, { type: 'string' }], + }, + prop2: { + format: 'bytes', + }, + }, + }, + }; + + const data = [ + { + prop1: '0x7ed0e85b8e1e925600b4373e6d108f34ab38a401', + prop2: new Uint8Array(hexToBytes('FF')), + }, + { prop1: 'some string', prop2: new Uint8Array(hexToBytes('FF')) }, + ]; + + const result = [ + { prop1: '0x7ed0e85b8e1e925600b4373e6d108f34ab38a401', prop2: '0xff' }, + { prop1: 'some string', prop2: '0xff' }, + ]; + + expect( + format(schema, data, { number: FMT_NUMBER.HEX, bytes: FMT_BYTES.HEX }), + ).toEqual(result); + }); + it('should format array of different objects', () => { const schema = { type: 'array', @@ -740,6 +775,77 @@ describe('formatter', () => { }); describe('object values', () => { + + describe('convert', () => { + it('should convert array values correctly when schema is an array and items are objects', () => { + const schema = { + type: 'object', + properties: { + transactions: { + type: 'array', + oneOf: [ + { + items: { + type: 'object', + properties: { + type: { enum: ['A'] }, + value: { type: 'string' } + } + } + }, + { + items: { + type: 'object', + properties: { + type: { enum: ['B'] }, + value: { type: 'number' } + } + } + } + ] + } + } + }; + const data = { + transactions: [ + { type: 'B', value: 42 }, + { type: 'B', value: 43 } + ] + }; + expect(convert(data, schema, [], { + number: FMT_NUMBER.HEX, + bytes: FMT_BYTES.HEX, + })).toEqual(data) + }); + }); + + it('should delete the array property if schema for array items is nullish', () => { + const schema = { + type: 'object', + properties: { + transactions: { + type: 'array', + items: undefined // Simulate a missing or null schema for items + } + } + }; + + const data = { + transactions: [ + { type: 'A', value: 'some string' }, + { type: 'B', value: 42 } + ] + }; + + const result = convert(data, schema, [], { + number: FMT_NUMBER.HEX, + bytes: FMT_BYTES.HEX, + }); + + expect(result).toEqual({}); + }); + + it('should format simple object', () => { const schema = { type: 'object', @@ -776,6 +882,13 @@ describe('formatter', () => { expect(result).toEqual(expected); }); + it('should throw FormatterError when jsonSchema is invalid', () => { + const invalidSchema1 = {}; + const data = { key: 'value' }; + + expect(() => format(invalidSchema1, data)).toThrow(FormatterError); + }); + it('should format nested objects', () => { const schema = { type: 'object', @@ -820,7 +933,51 @@ describe('formatter', () => { expect(result).toEqual(expected); }); + + it('should return empty when no proerties or items', () => { + const data = { key: 'value' }; + const schema = { + type: 'object', + }; + const f = { number: FMT_NUMBER.NUMBER, bytes: FMT_BYTES.HEX }; + const result = convert(data, schema, [], f, []); + expect(result).toEqual({}); }); + it('should format nested objects with oneOf', () => { + const schema = { + type: 'object', + properties: { + details: { + type: 'object', + oneOf: [ + { properties: { type: { enum: ['A'] }, value: { type: 'string' } } }, + { properties: { type: { enum: ['B'] }, value: { type: 'number' } } } + ] + } + } + } + + const data = + { + details: { + type: 'B', + value: 42 + } + } + const result = convert(data, schema, [], { + number: FMT_NUMBER.BIGINT, + bytes: FMT_BYTES.UINT8ARRAY, + }, [['details', 1]]); + + expect(result).toEqual({ + details: { + type: 'B', + value: 42 + } + }); + + }) + }); describe('isDataFormat', () => { describe('valid cases', () => { it.each(isDataFormatValid)('%s', (input, output) => { diff --git a/packages/web3-utils/test/unit/hash.test.ts b/packages/web3-utils/test/unit/hash.test.ts index 13b4c6aa1df..9cff77de5a3 100644 --- a/packages/web3-utils/test/unit/hash.test.ts +++ b/packages/web3-utils/test/unit/hash.test.ts @@ -60,6 +60,20 @@ describe('hash', () => { expect(sha3(input)).toBe(`0x${keccak256(output)}`); }); }); + + describe('handle when array', () => { + it('should handle array input and convert it to Uint8Array for hashing', () => { + // Arrange + const input = [0x61, 0x62, 0x63]; // ['a', 'b', 'c'] + const expectedHash = '0x7f83b165f6e8dd98bfa2727c7e4d949b'; + + // Act + const result = keccak256(input); + + // Assert + expect(result).toEqual(expectedHash); + }); + }) }); describe('sha3Raw', () => { diff --git a/packages/web3-utils/test/unit/socket_provider.test.ts b/packages/web3-utils/test/unit/socket_provider.test.ts index b116111e3aa..9d178464e6a 100644 --- a/packages/web3-utils/test/unit/socket_provider.test.ts +++ b/packages/web3-utils/test/unit/socket_provider.test.ts @@ -31,6 +31,7 @@ import { SocketProvider } from '../../src/socket_provider'; const dummySocketConnection = { dummy: 'dummy' }; class TestProvider extends SocketProvider { + protected _socketConnection?: typeof dummySocketConnection; protected _openSocketConnection() { @@ -651,6 +652,17 @@ describe('SocketProvider', () => { }); describe('_onConnect', () => { + + beforeEach(() => { + jest.spyOn(console, 'error').mockImplementation(() => { + // do nothing + }); // Spy on console.error to suppress and check calls + }) + + afterEach(() => { + jest.restoreAllMocks(); // Restore all mocks after each test + }); + it('should set the connection status to "connected"', () => { const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); @@ -659,8 +671,119 @@ describe('SocketProvider', () => { expect(provider['_connectionStatus']).toBe('connected'); }); + it('should set _accounts and _chainId when _getAccounts and _getChainId resolve', async () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + jest.spyOn(provider as any, '_getAccounts').mockResolvedValueOnce([123]); + jest.spyOn(provider as any, '_getChainId').mockResolvedValueOnce("1"); + + await new Promise ((resolve) => { + provider['_onConnect'](); + resolve(""); + }) + expect((provider as any)._chainId).toBe('1'); + expect((provider as any)._accounts).toEqual([123]); + }); + it('chainID should change when connecting twice', async () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + + await new Promise ((resolve) => { + jest.spyOn(provider as any, '_getAccounts').mockResolvedValueOnce([123]); + jest.spyOn(provider as any, '_getChainId').mockResolvedValueOnce("1"); + provider['_onConnect'](); + resolve(""); + }) + expect((provider as any)._chainId).toBe('1'); + expect((provider as any)._accounts).toEqual([123]); + + await new Promise ((resolve) => { + jest.spyOn(provider as any, '_getAccounts').mockResolvedValueOnce([123]); + jest.spyOn(provider as any, '_getChainId').mockResolvedValueOnce("2"); + provider['_onConnect'](); + resolve(""); + }) + expect((provider as any)._chainId).toBe('2'); + expect((provider as any)._accounts).toEqual([123]); + }); + it('should catch errors when _getAccounts and _getChainId throws', async () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + jest.spyOn(provider as any, '_getChainId').mockRejectedValueOnce(new Error("")); + jest.spyOn(provider as any, '_getAccounts').mockRejectedValueOnce(new Error("")); + jest.spyOn(provider, 'request').mockReturnValue(new Error() as unknown as Promise); + + await new Promise ((resolve) => { + provider['_onConnect'](); + resolve(""); + }) + expect((provider as any)._chainId).toBe(''); + expect((provider as any)._accounts).toEqual([]); + }); + it('should catch when connect emit fails', async () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + jest.spyOn(provider as any, '_getChainId').mockResolvedValueOnce(1); + jest.spyOn(provider as any, '_getAccounts').mockResolvedValueOnce([]); + (provider as any)._eventEmitter.emit = jest.fn(() => { + throw new Error('event emitter failed'); + }) + + + await new Promise ((resolve) => { + provider['_onConnect'](); + resolve(""); + }) + // I would check if console.error is called, but facing a race condition + expect((provider as any)._eventEmitter.emit).toHaveBeenCalledTimes(1); + + }); }); + describe('_getChainId', () => { + it('should return data from the chainId method', async () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + const chainId = 1; + jest.spyOn(provider as any, 'request').mockResolvedValueOnce({result: chainId}); + const result = await provider['_getChainId'](); + expect(result).toBe(chainId); + }) + + it('should be returning undefined from the chainId method', async () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + jest.spyOn(provider as any, 'request').mockResolvedValueOnce({result: undefined}); + const result = await provider['_getChainId'](); + expect(result).toBe(""); + }) + + it('should return empty from the chainId method', async () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + jest.spyOn(provider as any, 'request').mockResolvedValueOnce(undefined); + const result = await provider['_getChainId'](); + expect(result).toBe(""); + }) + }) + + describe('_getAccounts', () => { + it('should return data from the _getAccounts method', async () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + const accounts = [1]; + jest.spyOn(provider as any, 'request').mockResolvedValueOnce({result: accounts}); + const result = await provider['_getAccounts'](); + expect(result).toBe(accounts); + }) + + it('should returning undefined from the _getAccounts method', async () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + jest.spyOn(provider as any, 'request').mockResolvedValueOnce({result: undefined}); + const result = await provider['_getAccounts'](); + expect(result).toEqual([]); + }) + + it('should return empty from the _getAccounts method', async () => { + const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); + jest.spyOn(provider as any, 'request').mockResolvedValueOnce(undefined); + const result = await provider['_getAccounts'](); + expect(result).toEqual([]); + }) + }) + describe('_onMessage', () => { it('should resolve the deferred promise for valid responses with errors', () => { const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); From 369776777d197789a82af88d1b21287edf2d7336 Mon Sep 17 00:00:00 2001 From: Alex Luu Date: Wed, 29 May 2024 17:57:23 -0400 Subject: [PATCH 14/17] fix failing tests --- packages/web3-utils/src/web3_eip1193_provider.ts | 1 - packages/web3-utils/test/fixtures/formatter.ts | 2 +- packages/web3-utils/test/unit/hash.test.ts | 14 -------------- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/web3-utils/src/web3_eip1193_provider.ts b/packages/web3-utils/src/web3_eip1193_provider.ts index 036ab03adc3..fbedb19ab22 100644 --- a/packages/web3-utils/src/web3_eip1193_provider.ts +++ b/packages/web3-utils/src/web3_eip1193_provider.ts @@ -98,7 +98,6 @@ export abstract class Eip1193Provider< } as ProviderConnectInfo), ) .catch(err => { - console.log("enters") // todo: add error handler // eslint-disable-next-line no-console console.error(err); diff --git a/packages/web3-utils/test/fixtures/formatter.ts b/packages/web3-utils/test/fixtures/formatter.ts index cc4c08c28ca..32c5d167536 100644 --- a/packages/web3-utils/test/fixtures/formatter.ts +++ b/packages/web3-utils/test/fixtures/formatter.ts @@ -55,5 +55,5 @@ export const convertScalarValueValid: [[any, any, any], any][] = [ hexToBytes('0x00000000000000000000000000000000000000000000000000000000000000ff'), ), ], - [[new Uint8Array(hexToBytes('FF')), 'bytes32', { bytes: "invalidFormat" }], [255]] // return original value when erroring + [[255, 'bytes32', { bytes: "invalidFormat" }], 255] // return original value when erroring ]; \ No newline at end of file diff --git a/packages/web3-utils/test/unit/hash.test.ts b/packages/web3-utils/test/unit/hash.test.ts index 9cff77de5a3..13b4c6aa1df 100644 --- a/packages/web3-utils/test/unit/hash.test.ts +++ b/packages/web3-utils/test/unit/hash.test.ts @@ -60,20 +60,6 @@ describe('hash', () => { expect(sha3(input)).toBe(`0x${keccak256(output)}`); }); }); - - describe('handle when array', () => { - it('should handle array input and convert it to Uint8Array for hashing', () => { - // Arrange - const input = [0x61, 0x62, 0x63]; // ['a', 'b', 'c'] - const expectedHash = '0x7f83b165f6e8dd98bfa2727c7e4d949b'; - - // Act - const result = keccak256(input); - - // Assert - expect(result).toEqual(expectedHash); - }); - }) }); describe('sha3Raw', () => { From c24c7984ebf0cccb604312b7cc6cccb89b4f9fea Mon Sep 17 00:00:00 2001 From: Alex Luu Date: Thu, 30 May 2024 10:38:45 -0400 Subject: [PATCH 15/17] update hash --- packages/web3-utils/src/hash.ts | 2 -- packages/web3-utils/test/unit/formatter.test.ts | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/web3-utils/src/hash.ts b/packages/web3-utils/src/hash.ts index f7acc4f4fb9..f828447b2c1 100644 --- a/packages/web3-utils/src/hash.ts +++ b/packages/web3-utils/src/hash.ts @@ -151,8 +151,6 @@ export const keccak256Wrapper = ( let processedData; if (typeof data === 'bigint' || typeof data === 'number') { processedData = utf8ToBytes(data.toString()); - } else if (Array.isArray(data)) { - processedData = new Uint8Array(data); } else if (typeof data === 'string' && !isHexStrict(data)) { processedData = utf8ToBytes(data); } else { diff --git a/packages/web3-utils/test/unit/formatter.test.ts b/packages/web3-utils/test/unit/formatter.test.ts index 4e235081ddb..8b18a901ef5 100644 --- a/packages/web3-utils/test/unit/formatter.test.ts +++ b/packages/web3-utils/test/unit/formatter.test.ts @@ -943,6 +943,7 @@ describe('formatter', () => { const result = convert(data, schema, [], f, []); expect(result).toEqual({}); }); + it('should format nested objects with oneOf', () => { const schema = { type: 'object', From 72e70950780025cbd265d345e9297cdea99971b6 Mon Sep 17 00:00:00 2001 From: Alex Luu Date: Thu, 30 May 2024 12:48:47 -0400 Subject: [PATCH 16/17] run prettier --- packages/web3-utils/src/converters.ts | 30 +- packages/web3-utils/src/formatter.ts | 4 +- packages/web3-utils/src/hash.ts | 22 +- packages/web3-utils/src/promise_helpers.ts | 37 ++- packages/web3-utils/src/socket_provider.ts | 28 +- .../web3-utils/src/web3_eip1193_provider.ts | 2 +- .../web3-utils/test/fixtures/converters.ts | 68 +++-- .../web3-utils/test/fixtures/formatter.ts | 4 +- packages/web3-utils/test/fixtures/hash.ts | 16 +- packages/web3-utils/test/fixtures/json_rpc.ts | 46 +-- .../test/fixtures/string_manipulation.ts | 4 +- .../test/unit/chunk_response_parser.test.ts | 2 +- .../web3-utils/test/unit/converters.test.ts | 17 +- .../web3-utils/test/unit/formatter.test.ts | 277 ++++++++---------- packages/web3-utils/test/unit/hash.test.ts | 2 +- .../web3-utils/test/unit/json_rpc.test.ts | 58 ++-- .../test/unit/socket_provider.test.ts | 104 +++---- .../test/unit/web3_deferred_promise.test.ts | 2 +- 18 files changed, 373 insertions(+), 350 deletions(-) diff --git a/packages/web3-utils/src/converters.ts b/packages/web3-utils/src/converters.ts index 5280a54551b..9c94a4332b0 100644 --- a/packages/web3-utils/src/converters.ts +++ b/packages/web3-utils/src/converters.ts @@ -79,7 +79,8 @@ export const ethUnitMap = { tether: BigInt('1000000000000000000000000000000'), }; -const PrecisionLossWarning = 'Warning: Using type `number` with values that are large or contain many decimals may cause loss of precision, it is recommended to use type `string` or `BigInt` when using conversion methods'; +const PrecisionLossWarning = + 'Warning: Using type `number` with values that are large or contain many decimals may cause loss of precision, it is recommended to use type `string` or `BigInt` when using conversion methods'; export type EtherUnits = keyof typeof ethUnitMap; /** @@ -366,7 +367,7 @@ export const toHex = ( return returnType ? 'bigint' : numberToHex(value); } - if(isUint8Array(value)) { + if (isUint8Array(value)) { return returnType ? 'bytes' : bytesToHex(value); } @@ -391,7 +392,7 @@ export const toHex = ( // But a value pass for those conditions: "101611154195520776335741463917853444671577865378275924493376429267637792638729" // Note that according to the docs: it is supposed to be treated as a string (https://docs.web3js.org/guides/web3_upgrade_guide/x/web3_utils_migration_guide#conversion-to-hex) // In short, the strange is that isInt(value) is false but isUInt(value) is true for the value above. - // So, isUInt(value) should be investigated. + // TODO: isUInt(value) should be investigated. // However, if `toHex('101611154195520776335741463917853444671577865378275924493376429267637792638729', true)` is called, it will return `true`. // But, if `toHex('101611154195520776335741463917853444671577865378275924493376429267637792638729')` is called, it will throw inside `numberToHex`. @@ -428,14 +429,14 @@ export const toHex = ( */ export const toNumber = (value: Numbers): number | bigint => { if (typeof value === 'number') { - if (value > 1e+20) { - console.warn(PrecisionLossWarning) - // JavaScript converts numbers >= 10^21 to scientific notation when coerced to strings, - // leading to potential parsing errors and incorrect representations. - // For instance, String(10000000000000000000000) yields '1e+22'. - // Using BigInt prevents this - return BigInt(value); - } + if (value > 1e20) { + console.warn(PrecisionLossWarning); + // JavaScript converts numbers >= 10^21 to scientific notation when coerced to strings, + // leading to potential parsing errors and incorrect representations. + // For instance, String(10000000000000000000000) yields '1e+22'. + // Using BigInt prevents this + return BigInt(value); + } return value; } @@ -515,10 +516,9 @@ export const fromWei = (number: Numbers, unit: EtherUnits | number): string => { if (unit < 0 || !Number.isInteger(unit)) { throw new InvalidIntegerError(unit); } - denomination = bigintPower(BigInt(10),BigInt(unit)); + denomination = bigintPower(BigInt(10), BigInt(unit)); } - // value in wei would always be integer // 13456789, 1234 const value = String(toNumber(number)); @@ -584,8 +584,8 @@ export const toWei = (number: Numbers, unit: EtherUnits | number): string => { if (unit < 0 || !Number.isInteger(unit)) { throw new InvalidIntegerError(unit); } - - denomination = bigintPower(BigInt(10),BigInt(unit)); + + denomination = bigintPower(BigInt(10), BigInt(unit)); } let parsedNumber = number; diff --git a/packages/web3-utils/src/formatter.ts b/packages/web3-utils/src/formatter.ts index 102d0dead4f..665965a8eed 100644 --- a/packages/web3-utils/src/formatter.ts +++ b/packages/web3-utils/src/formatter.ts @@ -58,7 +58,7 @@ const findSchemaByDataPath = ( for (const dataPart of dataPath) { if (result.oneOf && previousDataPath) { const currentDataPath = previousDataPath; - const path = oneOfPath.find(([key]) => key === currentDataPath); + const path = oneOfPath.find(([key]) => key === currentDataPath); if (path && path[0] === previousDataPath) { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access result = result.oneOf[path[1]]; @@ -72,7 +72,7 @@ const findSchemaByDataPath = ( result = (result.properties as Record)[dataPart]; } else if (result.items && (result.items as JsonSchema).properties) { const node = (result.items as JsonSchema).properties as Record; - + result = node[dataPart]; } else if (result.items && isObject(result.items)) { result = result.items; diff --git a/packages/web3-utils/src/hash.ts b/packages/web3-utils/src/hash.ts index f828447b2c1..0289a429a52 100644 --- a/packages/web3-utils/src/hash.ts +++ b/packages/web3-utils/src/hash.ts @@ -17,25 +17,25 @@ along with web3.js. If not, see . /** * This package provides utility functions for Ethereum dapps and other web3.js packages. - * + * * For using Utils functions, first install Web3 package using `npm i web3` or `yarn add web3`. - * After that, Web3 Utils functions will be available as mentioned below. + * After that, Web3 Utils functions will be available as mentioned below. * ```ts * import { Web3 } from 'web3'; * const web3 = new Web3(); - * + * * const value = web3.utils.fromWei("1", "ether") - * + * * ``` - * + * * For using individual package install `web3-utils` package using `npm i web3-utils` or `yarn add web3-utils` and only import required functions. - * This is more efficient approach for building lightweight applications. + * This is more efficient approach for building lightweight applications. * ```ts * import { fromWei, soliditySha3Raw } from 'web3-utils'; - * + * * console.log(fromWei("1", "ether")); * console.log(soliditySha3Raw({ type: "string", value: "helloworld" })) - * + * * ``` * @module Utils */ @@ -151,6 +151,8 @@ export const keccak256Wrapper = ( let processedData; if (typeof data === 'bigint' || typeof data === 'number') { processedData = utf8ToBytes(data.toString()); + } else if (Array.isArray(data)) { + processedData = new Uint8Array(data); } else if (typeof data === 'string' && !isHexStrict(data)) { processedData = utf8ToBytes(data); } else { @@ -167,7 +169,9 @@ export { keccak256Wrapper as keccak256 }; * @returns - the type and value of the input */ const getType = (arg: Sha3Input): [string, EncodingTypes] => { - + if (Array.isArray(arg)) { + throw new Error('Autodetection of array types is not supported.'); + } let type; let value; // if type is given diff --git a/packages/web3-utils/src/promise_helpers.ts b/packages/web3-utils/src/promise_helpers.ts index 1b9c37ffc3a..a2140a2d1c5 100644 --- a/packages/web3-utils/src/promise_helpers.ts +++ b/packages/web3-utils/src/promise_helpers.ts @@ -20,7 +20,6 @@ import { isNullish } from 'web3-validator'; export type Timer = ReturnType; export type Timeout = ReturnType; - /** * An alternative to the node function `isPromise` that exists in `util/types` because it is not available on the browser. * @param object - to check if it is a `Promise` @@ -74,7 +73,6 @@ export async function waitWithTimeout( return result; } - /** * Repeatedly calls an async function with a given interval until the result of the function is defined (not undefined or null), * or until a timeout is reached. It returns promise and intervalId. @@ -85,25 +83,27 @@ export function pollTillDefinedAndReturnIntervalId( func: AsyncFunction, interval: number, ): [Promise>, Timer] { - let intervalId: Timer | undefined; const polledRes = new Promise>((resolve, reject) => { - intervalId = setInterval(function intervalCallbackFunc(){ - (async () => { - try { - const res = await waitWithTimeout(func, interval); - - if (!isNullish(res)) { + intervalId = setInterval( + (function intervalCallbackFunc() { + (async () => { + try { + const res = await waitWithTimeout(func, interval); + + if (!isNullish(res)) { + clearInterval(intervalId); + resolve(res as unknown as Exclude); + } + } catch (error) { clearInterval(intervalId); - resolve(res as unknown as Exclude); + reject(error); } - } catch (error) { - clearInterval(intervalId); - reject(error); - } - })() as unknown; - return intervalCallbackFunc;}() // this will immediate invoke first call - , interval); + })() as unknown; + return intervalCallbackFunc; + })(), // this will immediate invoke first call + interval, + ); }); return [polledRes as unknown as Promise>, intervalId!]; @@ -113,7 +113,7 @@ export function pollTillDefinedAndReturnIntervalId( * Repeatedly calls an async function with a given interval until the result of the function is defined (not undefined or null), * or until a timeout is reached. * pollTillDefinedAndReturnIntervalId() function should be used instead of pollTillDefined if you need IntervalId in result. - * This function will be deprecated in next major release so use pollTillDefinedAndReturnIntervalId(). + * This function will be deprecated in next major release so use pollTillDefinedAndReturnIntervalId(). * @param func - The function to call. * @param interval - The interval in milliseconds. */ @@ -168,4 +168,3 @@ export function rejectIfConditionAtInterval( }); return [intervalId!, rejectIfCondition]; } - diff --git a/packages/web3-utils/src/socket_provider.ts b/packages/web3-utils/src/socket_provider.ts index 046fc69d0ad..0a73fb8378f 100644 --- a/packages/web3-utils/src/socket_provider.ts +++ b/packages/web3-utils/src/socket_provider.ts @@ -187,13 +187,13 @@ export abstract class SocketProvider< protected _validateProviderPath(path: string): boolean { return !!path; } - + /** * * @returns the pendingRequestQueue size */ // eslint-disable-next-line class-methods-use-this - public getPendingRequestQueueSize() { + public getPendingRequestQueueSize() { return this._pendingRequestsQueue.size; } @@ -350,32 +350,34 @@ export abstract class SocketProvider< /** * Safely disconnects the socket, async and waits for request size to be 0 before disconnecting - * @param forceDisconnect - If true, will clear queue after 5 attempts of waiting for both pending and sent queue to be 0 + * @param forceDisconnect - If true, will clear queue after 5 attempts of waiting for both pending and sent queue to be 0 * @param ms - Determines the ms of setInterval * @param code - The code to be sent to the server * @param data - The data to be sent to the server */ - public async safeDisconnect(code?: number, data?: string, forceDisconnect = false,ms = 1000) { + public async safeDisconnect(code?: number, data?: string, forceDisconnect = false, ms = 1000) { let retryAttempt = 0; - const checkQueue = async () => + const checkQueue = async () => new Promise(resolve => { const interval = setInterval(() => { if (forceDisconnect && retryAttempt >= 5) { this.clearQueues(); } - if (this.getPendingRequestQueueSize() === 0 && this.getSentRequestsQueueSize() === 0) { + if ( + this.getPendingRequestQueueSize() === 0 && + this.getSentRequestsQueueSize() === 0 + ) { clearInterval(interval); resolve(true); } - retryAttempt+=1; - }, ms) - }) - + retryAttempt += 1; + }, ms); + }); + await checkQueue(); this.disconnect(code, data); } - /** * Removes all listeners for the specified event type. * @param type - The event type to remove the listeners for @@ -512,7 +514,7 @@ export abstract class SocketProvider< if (isNullish(responses) || responses.length === 0) { return; } - + for (const response of responses) { if ( jsonRpc.isResponseWithNotification(response as JsonRpcNotification) && @@ -544,7 +546,7 @@ export abstract class SocketProvider< this._sentRequestsQueue.delete(requestId); } } - + public clearQueues(event?: ConnectionEvent) { this._clearQueues(event); } diff --git a/packages/web3-utils/src/web3_eip1193_provider.ts b/packages/web3-utils/src/web3_eip1193_provider.ts index fbedb19ab22..f0e2d4d30bf 100644 --- a/packages/web3-utils/src/web3_eip1193_provider.ts +++ b/packages/web3-utils/src/web3_eip1193_provider.ts @@ -68,7 +68,7 @@ export abstract class Eip1193Provider< this._chainId = chainId; this._eventEmitter.emit('chainChanged', this._chainId); } - }) + }) .catch(err => { // todo: add error handler console.error(err); diff --git a/packages/web3-utils/test/fixtures/converters.ts b/packages/web3-utils/test/fixtures/converters.ts index faa9f8bfe04..cecb0b3fafb 100644 --- a/packages/web3-utils/test/fixtures/converters.ts +++ b/packages/web3-utils/test/fixtures/converters.ts @@ -235,21 +235,20 @@ export const toHexValidData: [Numbers | Bytes | Address | boolean, [HexString, V ], ['-0x01', ['-0x1', 'int256']], ['123c', ['0x123c', 'bytes']], - [new Uint8Array([ - 221, 128, 128, 128, 148, 186, 248, - 242, 159, 130, 231, 84, 254, 199, - 252, 69, 21, 58, 104, 102, 201, - 137, 255, 3, 196, 10, 128, 128, - 128, 128 - ]), ['0xdd80808094baf8f29f82e754fec7fc45153a6866c989ff03c40a80808080', 'bytes']], - [Buffer.from([ - 221, 128, 128, 128, 148, 186, 248, - 242, 159, 130, 231, 84, 254, 199, - 252, 69, 21, 58, 104, 102, 201, - 137, 255, 3, 196, 10, 128, 128, - 128, 128 - ]), ['0xdd80808094baf8f29f82e754fec7fc45153a6866c989ff03c40a80808080', 'bytes']] - + [ + new Uint8Array([ + 221, 128, 128, 128, 148, 186, 248, 242, 159, 130, 231, 84, 254, 199, 252, 69, 21, 58, + 104, 102, 201, 137, 255, 3, 196, 10, 128, 128, 128, 128, + ]), + ['0xdd80808094baf8f29f82e754fec7fc45153a6866c989ff03c40a80808080', 'bytes'], + ], + [ + Buffer.from([ + 221, 128, 128, 128, 148, 186, 248, 242, 159, 130, 231, 84, 254, 199, 252, 69, 21, 58, + 104, 102, 201, 137, 255, 3, 196, 10, 128, 128, 128, 128, + ]), + ['0xdd80808094baf8f29f82e754fec7fc45153a6866c989ff03c40a80808080', 'bytes'], + ], ]; export const toHexInvalidData: [any, string][] = [ @@ -320,14 +319,14 @@ const conversionBaseData: [[Numbers, EtherUnits | number], string][] = [ export const fromWeiValidData: [[Numbers, EtherUnits | number], Numbers][] = [ ...conversionBaseData, - [['0xff', 'wei'], '255'], - [[1e+22, 'ether'], '10000'], - [[19999999999999991611392, 'ether'], '19999.999999999991611392'], - [[1.9999999999999991611392e+22, 'ether'], '19999.999999999991611392'], + [['0xff', 'wei'], '255'], + [[1e22, 'ether'], '10000'], + [[19999999999999991611392, 'ether'], '19999.999999999991611392'], + [[1.9999999999999991611392e22, 'ether'], '19999.999999999991611392'], [['1000000', 'ether'], '0.000000000001'], [['1123456789123456789', 'ether'], '1.123456789123456789'], [['1123', 'kwei'], '1.123'], - [['1234100' ,'kwei'], '1234.1'], + [['1234100', 'kwei'], '1234.1'], [['3308685546611893', 'ether'], '0.003308685546611893'], ]; @@ -339,17 +338,24 @@ export const toWeiValidData: [[Numbers, EtherUnits | number], Numbers][] = [ [['1000000', 'ether'], 0.000000000001], [['1123456789123456789', 'ether'], '1.123456789123456789123'], [['1123', 'kwei'], '1.12345'], - [['1234100' ,'kwei'], '1234.1'], + [['1234100', 'kwei'], '1234.1'], [['3308685546611893', 'ether'], '0.0033086855466118933'], [['1123', 'kwei'], 1.12345], - ]; export const toWeiValidDataWarnings: [[Numbers, EtherUnits], string][] = [ - [[0.0000000000000000000001, 'ether'], 'Warning: Using type `number` with values that are large or contain many decimals may cause loss of precision, it is recommended to use type `string` or `BigInt` when using conversion methods'], - [[0.0000000000000000000001, 'ether'], 'Warning: Using type `number` with values that are large or contain many decimals may cause loss of precision, it is recommended to use type `string` or `BigInt` when using conversion methods'], - [[1999999000000009900000, 'kwei'], 'Warning: Using type `number` with values that are large or contain many decimals may cause loss of precision, it is recommended to use type `string` or `BigInt` when using conversion methods'], - + [ + [0.0000000000000000000001, 'ether'], + 'Warning: Using type `number` with values that are large or contain many decimals may cause loss of precision, it is recommended to use type `string` or `BigInt` when using conversion methods', + ], + [ + [0.0000000000000000000001, 'ether'], + 'Warning: Using type `number` with values that are large or contain many decimals may cause loss of precision, it is recommended to use type `string` or `BigInt` when using conversion methods', + ], + [ + [1999999000000009900000, 'kwei'], + 'Warning: Using type `number` with values that are large or contain many decimals may cause loss of precision, it is recommended to use type `string` or `BigInt` when using conversion methods', + ], ]; export const fromWeiInvalidData: [[any, any], string][] = [ @@ -362,8 +368,14 @@ export const fromWeiInvalidData: [[any, any], string][] = [ [[{}, 'kwei'], 'Invalid value given "{}". Error: can not parse as number data'], [['data', 'kwei'], 'Invalid value given "data". Error: can not parse as number data.'], [['1234', 'uwei'], 'Invalid value given "uwei". Error: invalid unit.'], - [['1234', -1], 'Invalid value given "-1". Error: not a valid unit. Must be a positive integer.'], - [['1234', 3.3], 'Invalid value given "3.3". Error: not a valid unit. Must be a positive integer.'] + [ + ['1234', -1], + 'Invalid value given "-1". Error: not a valid unit. Must be a positive integer.', + ], + [ + ['1234', 3.3], + 'Invalid value given "3.3". Error: not a valid unit. Must be a positive integer.', + ], ]; export const toWeiInvalidData: [[any, any], string][] = [ diff --git a/packages/web3-utils/test/fixtures/formatter.ts b/packages/web3-utils/test/fixtures/formatter.ts index 32c5d167536..5d44b05df2d 100644 --- a/packages/web3-utils/test/fixtures/formatter.ts +++ b/packages/web3-utils/test/fixtures/formatter.ts @@ -55,5 +55,5 @@ export const convertScalarValueValid: [[any, any, any], any][] = [ hexToBytes('0x00000000000000000000000000000000000000000000000000000000000000ff'), ), ], - [[255, 'bytes32', { bytes: "invalidFormat" }], 255] // return original value when erroring -]; \ No newline at end of file + [[255, 'bytes32', { bytes: 'invalidFormat' }], 255], // return original value when erroring +]; diff --git a/packages/web3-utils/test/fixtures/hash.ts b/packages/web3-utils/test/fixtures/hash.ts index 4f337c191ae..1869dd10fec 100644 --- a/packages/web3-utils/test/fixtures/hash.ts +++ b/packages/web3-utils/test/fixtures/hash.ts @@ -286,9 +286,13 @@ export const encodePackedInvalidData: [any, string][] = [ { type: 'bytes32', value: '0x1' }, 'Invalid value given "0x1". Error: can not parse as byte data.', ], + [ + [[{ type: 'string', value: '31323334' }], [{ type: '', value: '31323334' }]], + 'Autodetection of array types is not supported.', + ], ]; -export const keccak256ValidData: [string | Uint8Array | bigint, string][] = [ +export const keccak256ValidData: [string | Uint8Array | bigint | number[], string][] = [ ['my data', '0x8e0c48154711500d6fa119cc31df4dec339091e8b426cf4109a769fe89baad31'], [ new Uint8Array(Buffer.from('my data')), @@ -299,6 +303,8 @@ export const keccak256ValidData: [string | Uint8Array | bigint, string][] = [ '0x2d19cd91fbcc44e6412f92c11da7907cdedb1ace04c47447b42a61f1cd63b85a', ], [BigInt(3), '0x2a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de'], + [[0x3], '0x69c322e3248a5dfc29d73c5b0553b0185a35cd5bb6386747517ef7e53b15e287'], + [new Uint8Array([0x3]), '0x69c322e3248a5dfc29d73c5b0553b0185a35cd5bb6386747517ef7e53b15e287'], ]; export const elementaryNameValidData: [any, string][] = [ @@ -351,7 +357,7 @@ export const soliditySha3BigIntValidData: [Sha3Input[], string][] = [ */ ]; -export const getStorageSlotNumForLongStringValidData: [string|number, string | undefined][] - = [ - [0, "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"] - ]; \ No newline at end of file +export const getStorageSlotNumForLongStringValidData: [string | number, string | undefined][] = [ + [0, '0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563'], + ['0', '0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563'], +]; diff --git a/packages/web3-utils/test/fixtures/json_rpc.ts b/packages/web3-utils/test/fixtures/json_rpc.ts index 0b87a35ec59..d801fa91587 100644 --- a/packages/web3-utils/test/fixtures/json_rpc.ts +++ b/packages/web3-utils/test/fixtures/json_rpc.ts @@ -28,13 +28,13 @@ const responseWithNotfication = { export const isResponseWithResultValidTest: [any, boolean][] = [ [responseWithResult, true], [responseWithError, false], - [{...responseWithResult, id: '1'}, true], + [{ ...responseWithResult, id: '1' }, true], ]; export const isResponseWithErrorValidTest: [any, boolean][] = [ [responseWithResult, false], [responseWithError, true], - [{...responseWithError, id: '1'}, true], + [{ ...responseWithError, id: '1' }, true], ]; export const isResponseWithNotificationValidTest: [JsonRpcNotification, boolean][] = [ @@ -67,32 +67,40 @@ export const toPayloadValidTest: [any, any][] = [ }, ], [ - { method: 'add', jsonrpc: '1.0', id: 1}, + { method: 'add', jsonrpc: '1.0', id: 1 }, { method: 'add', id: 1, jsonrpc: '1.0', - params: undefined + params: undefined, }, - ] + ], ]; export const isResponseRpcErrorValidData: [any, boolean][] = [ [responseWithRpcError, true], [responseWithError, false], -] +]; export const isBatchRequestValidData: [any, boolean][] = [ - [[{ - method: 'add', - id: 1, - jsonrpc: '1.0', - params: undefined - }], true], - [{ - method: 'add', - id: 1, - jsonrpc: '1.0', - params: undefined - }, false] -] \ No newline at end of file + [ + [ + { + method: 'add', + id: 1, + jsonrpc: '1.0', + params: undefined, + }, + ], + true, + ], + [ + { + method: 'add', + id: 1, + jsonrpc: '1.0', + params: undefined, + }, + false, + ], +]; diff --git a/packages/web3-utils/test/fixtures/string_manipulation.ts b/packages/web3-utils/test/fixtures/string_manipulation.ts index 86be918051d..dde49247677 100644 --- a/packages/web3-utils/test/fixtures/string_manipulation.ts +++ b/packages/web3-utils/test/fixtures/string_manipulation.ts @@ -69,7 +69,7 @@ export const toTwosComplementData: [[Numbers, number | undefined], HexString][] [['13', 32], '0x0000000000000000000000000000000d'], [['-13', 32], '0xfffffffffffffffffffffffffffffff3'], [[-16, 2], '0xf0'], - [['0x1', undefined], '0x0000000000000000000000000000000000000000000000000000000000000001'] + [['0x1', undefined], '0x0000000000000000000000000000000000000000000000000000000000000001'], ]; export const fromTwosComplementData: [[Numbers, number | undefined], number | bigint][] = [ @@ -82,7 +82,7 @@ export const fromTwosComplementData: [[Numbers, number | undefined], number | bi [[1000, 64], 1000], [[-1000, 64], -1000], [[BigInt(9), 1], -7], - [['0x0000000000000000000000000000000000000000000000000000000000000001', undefined], 1] + [['0x0000000000000000000000000000000000000000000000000000000000000001', undefined], 1], ]; export const toTwosComplementInvalidData: [[Numbers, number], string][] = [ diff --git a/packages/web3-utils/test/unit/chunk_response_parser.test.ts b/packages/web3-utils/test/unit/chunk_response_parser.test.ts index 707197e2889..bb4c70e8f1b 100644 --- a/packages/web3-utils/test/unit/chunk_response_parser.test.ts +++ b/packages/web3-utils/test/unit/chunk_response_parser.test.ts @@ -83,6 +83,6 @@ describe('chunk_response_parser', () => { eventEmiter.on('error', onError); // eslint-disable-next-line no-promise-executor-return await new Promise(resolve => setTimeout(resolve, 1000)); - expect(result).toEqual([]) + expect(result).toEqual([]); }); }); diff --git a/packages/web3-utils/test/unit/converters.test.ts b/packages/web3-utils/test/unit/converters.test.ts index 7481d5008ce..84073f4a4d7 100644 --- a/packages/web3-utils/test/unit/converters.test.ts +++ b/packages/web3-utils/test/unit/converters.test.ts @@ -330,7 +330,7 @@ describe('converters', () => { }); it('an interesting case that needs investigation', () => { - // This case is to be investigated further + // TODO: This case is to be investigated further expect( toHex( '101611154195520776335741463917853444671577865378275924493376429267637792638729', @@ -354,6 +354,14 @@ describe('converters', () => { }); describe('fromWei', () => { + beforeEach(() => { + jest.spyOn(console, 'warn').mockImplementation(() => { + // do nothing + }); + }); + afterAll(() => { + jest.restoreAllMocks(); + }); describe('valid cases', () => { it.each(fromWeiValidData)('%s', (input, output) => { expect(fromWei(input[0], input[1])).toEqual(output); @@ -385,6 +393,9 @@ describe('converters', () => { // do nothing }); }); + afterAll(() => { + jest.restoreAllMocks(); + }); it.each(toWeiValidDataWarnings)('%s', (input, output) => { toWei(input[0], input[1]); expect(console.warn).toHaveBeenCalledWith(output); @@ -407,7 +418,9 @@ describe('converters', () => { // mock utils.uint8ArrayToHexString to return an empty string jest.mock('web3-validator'); - jest.spyOn(utils, 'uint8ArrayToHexString').mockReturnValue(undefined as unknown as string); + jest.spyOn(utils, 'uint8ArrayToHexString').mockReturnValue( + undefined as unknown as string, + ); const result = toChecksumAddress(address); expect(result).toBe(''); diff --git a/packages/web3-utils/test/unit/formatter.test.ts b/packages/web3-utils/test/unit/formatter.test.ts index 8b18a901ef5..add9095596b 100644 --- a/packages/web3-utils/test/unit/formatter.test.ts +++ b/packages/web3-utils/test/unit/formatter.test.ts @@ -24,7 +24,7 @@ import { HexString, Numbers, } from 'web3-types'; -import {FormatterError} from 'web3-errors'; +import { FormatterError } from 'web3-errors'; import { expectTypeOf, typecheck } from '@humeris/espresso-shot'; import { isDataFormatValid, convertScalarValueValid } from '../fixtures/formatter'; import { format, isDataFormat, convertScalarValue, convert } from '../../src/formatter'; @@ -585,40 +585,6 @@ describe('formatter', () => { ).toEqual(result); }); - it('should format array of objects with', () => { - const schema = { - type: 'array', - items: { - type: 'object', - properties: { - prop1: { - oneOf: [{ format: 'address' }, { type: 'string' }], - }, - prop2: { - format: 'bytes', - }, - }, - }, - }; - - const data = [ - { - prop1: '0x7ed0e85b8e1e925600b4373e6d108f34ab38a401', - prop2: new Uint8Array(hexToBytes('FF')), - }, - { prop1: 'some string', prop2: new Uint8Array(hexToBytes('FF')) }, - ]; - - const result = [ - { prop1: '0x7ed0e85b8e1e925600b4373e6d108f34ab38a401', prop2: '0xff' }, - { prop1: 'some string', prop2: '0xff' }, - ]; - - expect( - format(schema, data, { number: FMT_NUMBER.HEX, bytes: FMT_BYTES.HEX }), - ).toEqual(result); - }); - it('should format array of different objects', () => { const schema = { type: 'array', @@ -773,79 +739,7 @@ describe('formatter', () => { ).toEqual(result); }); }); - describe('object values', () => { - - describe('convert', () => { - it('should convert array values correctly when schema is an array and items are objects', () => { - const schema = { - type: 'object', - properties: { - transactions: { - type: 'array', - oneOf: [ - { - items: { - type: 'object', - properties: { - type: { enum: ['A'] }, - value: { type: 'string' } - } - } - }, - { - items: { - type: 'object', - properties: { - type: { enum: ['B'] }, - value: { type: 'number' } - } - } - } - ] - } - } - }; - const data = { - transactions: [ - { type: 'B', value: 42 }, - { type: 'B', value: 43 } - ] - }; - expect(convert(data, schema, [], { - number: FMT_NUMBER.HEX, - bytes: FMT_BYTES.HEX, - })).toEqual(data) - }); - }); - - it('should delete the array property if schema for array items is nullish', () => { - const schema = { - type: 'object', - properties: { - transactions: { - type: 'array', - items: undefined // Simulate a missing or null schema for items - } - } - }; - - const data = { - transactions: [ - { type: 'A', value: 'some string' }, - { type: 'B', value: 42 } - ] - }; - - const result = convert(data, schema, [], { - number: FMT_NUMBER.HEX, - bytes: FMT_BYTES.HEX, - }); - - expect(result).toEqual({}); - }); - - it('should format simple object', () => { const schema = { type: 'object', @@ -885,9 +779,9 @@ describe('formatter', () => { it('should throw FormatterError when jsonSchema is invalid', () => { const invalidSchema1 = {}; const data = { key: 'value' }; - + expect(() => format(invalidSchema1, data)).toThrow(FormatterError); - }); + }); it('should format nested objects', () => { const schema = { @@ -933,52 +827,7 @@ describe('formatter', () => { expect(result).toEqual(expected); }); - - it('should return empty when no proerties or items', () => { - const data = { key: 'value' }; - const schema = { - type: 'object', - }; - const f = { number: FMT_NUMBER.NUMBER, bytes: FMT_BYTES.HEX }; - const result = convert(data, schema, [], f, []); - expect(result).toEqual({}); }); - - it('should format nested objects with oneOf', () => { - const schema = { - type: 'object', - properties: { - details: { - type: 'object', - oneOf: [ - { properties: { type: { enum: ['A'] }, value: { type: 'string' } } }, - { properties: { type: { enum: ['B'] }, value: { type: 'number' } } } - ] - } - } - } - - const data = - { - details: { - type: 'B', - value: 42 - } - } - const result = convert(data, schema, [], { - number: FMT_NUMBER.BIGINT, - bytes: FMT_BYTES.UINT8ARRAY, - }, [['details', 1]]); - - expect(result).toEqual({ - details: { - type: 'B', - value: 42 - } - }); - - }) - }); describe('isDataFormat', () => { describe('valid cases', () => { it.each(isDataFormatValid)('%s', (input, output) => { @@ -1004,4 +853,124 @@ describe('formatter', () => { }); }); }); + + describe('convert', () => { + it('should convert array values correctly when schema is an array and items are objects', () => { + const schema = { + type: 'object', + properties: { + transactions: { + type: 'array', + oneOf: [ + { + items: { + type: 'object', + properties: { + address: { format: 'string' }, + value: { type: 'string' }, + }, + }, + }, + { + items: { + type: 'object', + properties: { + address: { format: 'string' }, + value: { type: 'number' }, + }, + }, + }, + ], + }, + }, + }; + const data = { + transactions: [ + { address: 'B', value: '42' }, + { address: 'B', value: 43 }, + ], + }; + expect( + convert(data, schema, [], { + number: FMT_NUMBER.HEX, + bytes: FMT_BYTES.HEX, + }), + ).toEqual(data); + }); + + it('should delete the array property if schema for array items is nullish', () => { + const schema = { + type: 'object', + properties: { + transactions: { + type: 'array', + items: undefined, // Simulate a missing or null schema for items + }, + }, + }; + + const data = { + transactions: [ + { type: 'A', value: 'some string' }, + { type: 'B', value: 42 }, + ], + }; + + const result = convert(data, schema, [], { + number: FMT_NUMBER.HEX, + bytes: FMT_BYTES.HEX, + }); + + expect(result).toEqual({}); + }); + + it('should return empty when no proerties or items', () => { + const data = { key: 'value' }; + const schema = { + type: 'object', + }; + const f = { number: FMT_NUMBER.NUMBER, bytes: FMT_BYTES.HEX }; + const result = convert(data, schema, [], f, []); + expect(result).toEqual({}); + }); + + it('should format nested objects with oneOf', () => { + const schema = { + type: 'object', + properties: { + details: { + type: 'object', + oneOf: [ + { properties: { type: { enum: ['A'] }, value: { type: 'string' } } }, + { properties: { type: { enum: ['B'] }, value: { type: 'number' } } }, + ], + }, + }, + }; + + const data = { + details: { + type: 'B', + value: 42, + }, + }; + const result = convert( + data, + schema, + [], + { + number: FMT_NUMBER.BIGINT, + bytes: FMT_BYTES.UINT8ARRAY, + }, + [['details', 1]], + ); + + expect(result).toEqual({ + details: { + type: 'B', + value: 42, + }, + }); + }); + }); }); diff --git a/packages/web3-utils/test/unit/hash.test.ts b/packages/web3-utils/test/unit/hash.test.ts index 13b4c6aa1df..814dacd45f2 100644 --- a/packages/web3-utils/test/unit/hash.test.ts +++ b/packages/web3-utils/test/unit/hash.test.ts @@ -156,5 +156,5 @@ describe('hash', () => { it.each(getStorageSlotNumForLongStringValidData)('%s', (input, output) => { expect(getStorageSlotNumForLongString(input)).toEqual(output); }); - }) + }); }); diff --git a/packages/web3-utils/test/unit/json_rpc.test.ts b/packages/web3-utils/test/unit/json_rpc.test.ts index 27dee1a0354..ea16cd646d1 100644 --- a/packages/web3-utils/test/unit/json_rpc.test.ts +++ b/packages/web3-utils/test/unit/json_rpc.test.ts @@ -91,49 +91,57 @@ describe('json rpc tests', () => { }); describe('isBatchResponseValid', () => { describe('valid cases', () => { - it.each(isBatchResponseValidTest)('isBatchResponseValid valid test', (input, output) => { - const result = isBatchResponse(input); - expect(result).toBe(output); - }); + it.each(isBatchResponseValidTest)( + 'isBatchResponseValid valid test', + (input, output) => { + const result = isBatchResponse(input); + expect(result).toBe(output); + }, + ); }); }); describe('isBatchRequest', () => { describe('valid cases', () => { it.each(isBatchRequestValidData)('isBatchRqeuest valid data', (input, output) => { expect(isBatchRequest(input)).toBe(output); - }) - }) - }) + }); + }); + }); describe('toPayloadValid', () => { describe('valid cases', () => { - beforeEach (() => { - setRequestIdStart(undefined) + beforeEach(() => { + setRequestIdStart(undefined); }); it.each(toPayloadValidTest)('toPayload valid test', async (input, output) => { - const result = await new Promise((resolve) => { - resolve(toPayload(input)) - }) + const result = await new Promise(resolve => { + resolve(toPayload(input)); + }); expect(result).toStrictEqual(output); }); it('should give payload that has requestid set', async () => { - setRequestIdStart(1) - const result = await new Promise((resolve) => { - resolve(toPayload({ method: 'delete' })) - }) - expect(result).toStrictEqual({method: 'delete', id:2, params: undefined, jsonrpc: '2.0'}); + setRequestIdStart(1); + const result = await new Promise(resolve => { + resolve(toPayload({ method: 'delete' })); + }); + expect(result).toStrictEqual({ + method: 'delete', + id: 2, + params: undefined, + jsonrpc: '2.0', + }); }); }); }); describe('toBatchPayload', () => { it('should batch payload', async () => { - setRequestIdStart(0) - const result = await new Promise((resolve) => { - resolve(toBatchPayload([{ method: 'delete' }, {method: 'add'}])) - }) + setRequestIdStart(0); + const result = await new Promise(resolve => { + resolve(toBatchPayload([{ method: 'delete' }, { method: 'add' }])); + }); expect(result).toStrictEqual([ - {method: 'delete', id:1, params: undefined, jsonrpc: '2.0'}, - {method: 'add', id:2, params: undefined, jsonrpc: '2.0'} + { method: 'delete', id: 1, params: undefined, jsonrpc: '2.0' }, + { method: 'add', id: 2, params: undefined, jsonrpc: '2.0' }, ]); }); - }) -}); \ No newline at end of file + }); +}); diff --git a/packages/web3-utils/test/unit/socket_provider.test.ts b/packages/web3-utils/test/unit/socket_provider.test.ts index 9d178464e6a..c2c3756c755 100644 --- a/packages/web3-utils/test/unit/socket_provider.test.ts +++ b/packages/web3-utils/test/unit/socket_provider.test.ts @@ -31,7 +31,6 @@ import { SocketProvider } from '../../src/socket_provider'; const dummySocketConnection = { dummy: 'dummy' }; class TestProvider extends SocketProvider { - protected _socketConnection?: typeof dummySocketConnection; protected _openSocketConnection() { @@ -124,9 +123,9 @@ describe('SocketProvider', () => { it('should error when failing to _validateProviderPath', () => { expect(() => { // eslint-disable-next-line no-new - new TestProvider("", socketOption, { delay: 0 }); - }).toThrow(InvalidClientError) - }) + new TestProvider('', socketOption, { delay: 0 }); + }).toThrow(InvalidClientError); + }); }); describe('testing _reconnect() method', () => { it('should not be called when { autoReconnect: false }', () => { @@ -371,7 +370,9 @@ describe('SocketProvider', () => { const funcBSpy = jest // spy on provider.super._onDisconnect .spyOn( - Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(provider))), + Object.getPrototypeOf( + Object.getPrototypeOf(Object.getPrototypeOf(provider)), + ), '_onDisconnect', ) .mockReturnValue(new EventEmitter()); @@ -652,16 +653,15 @@ describe('SocketProvider', () => { }); describe('_onConnect', () => { - beforeEach(() => { - jest.spyOn(console, 'error').mockImplementation(() => { + jest.spyOn(console, 'error').mockImplementation(() => { // do nothing }); // Spy on console.error to suppress and check calls - }) + }); afterEach(() => { jest.restoreAllMocks(); // Restore all mocks after each test - }); + }); it('should set the connection status to "connected"', () => { const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); @@ -674,46 +674,46 @@ describe('SocketProvider', () => { it('should set _accounts and _chainId when _getAccounts and _getChainId resolve', async () => { const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); jest.spyOn(provider as any, '_getAccounts').mockResolvedValueOnce([123]); - jest.spyOn(provider as any, '_getChainId').mockResolvedValueOnce("1"); - - await new Promise ((resolve) => { + jest.spyOn(provider as any, '_getChainId').mockResolvedValueOnce('1'); + + await new Promise(resolve => { provider['_onConnect'](); - resolve(""); - }) + resolve(''); + }); expect((provider as any)._chainId).toBe('1'); expect((provider as any)._accounts).toEqual([123]); }); it('chainID should change when connecting twice', async () => { const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); - - await new Promise ((resolve) => { + + await new Promise(resolve => { jest.spyOn(provider as any, '_getAccounts').mockResolvedValueOnce([123]); - jest.spyOn(provider as any, '_getChainId').mockResolvedValueOnce("1"); + jest.spyOn(provider as any, '_getChainId').mockResolvedValueOnce('1'); provider['_onConnect'](); - resolve(""); - }) + resolve(''); + }); expect((provider as any)._chainId).toBe('1'); expect((provider as any)._accounts).toEqual([123]); - await new Promise ((resolve) => { + await new Promise(resolve => { jest.spyOn(provider as any, '_getAccounts').mockResolvedValueOnce([123]); - jest.spyOn(provider as any, '_getChainId').mockResolvedValueOnce("2"); + jest.spyOn(provider as any, '_getChainId').mockResolvedValueOnce('2'); provider['_onConnect'](); - resolve(""); - }) + resolve(''); + }); expect((provider as any)._chainId).toBe('2'); expect((provider as any)._accounts).toEqual([123]); }); it('should catch errors when _getAccounts and _getChainId throws', async () => { const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); - jest.spyOn(provider as any, '_getChainId').mockRejectedValueOnce(new Error("")); - jest.spyOn(provider as any, '_getAccounts').mockRejectedValueOnce(new Error("")); + jest.spyOn(provider as any, '_getChainId').mockRejectedValueOnce(new Error('')); + jest.spyOn(provider as any, '_getAccounts').mockRejectedValueOnce(new Error('')); jest.spyOn(provider, 'request').mockReturnValue(new Error() as unknown as Promise); - - await new Promise ((resolve) => { + + await new Promise(resolve => { provider['_onConnect'](); - resolve(""); - }) + resolve(''); + }); expect((provider as any)._chainId).toBe(''); expect((provider as any)._accounts).toEqual([]); }); @@ -723,16 +723,14 @@ describe('SocketProvider', () => { jest.spyOn(provider as any, '_getAccounts').mockResolvedValueOnce([]); (provider as any)._eventEmitter.emit = jest.fn(() => { throw new Error('event emitter failed'); - }) - + }); - await new Promise ((resolve) => { + await new Promise(resolve => { provider['_onConnect'](); - resolve(""); - }) + resolve(''); + }); // I would check if console.error is called, but facing a race condition expect((provider as any)._eventEmitter.emit).toHaveBeenCalledTimes(1); - }); }); @@ -740,49 +738,49 @@ describe('SocketProvider', () => { it('should return data from the chainId method', async () => { const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); const chainId = 1; - jest.spyOn(provider as any, 'request').mockResolvedValueOnce({result: chainId}); + jest.spyOn(provider as any, 'request').mockResolvedValueOnce({ result: chainId }); const result = await provider['_getChainId'](); expect(result).toBe(chainId); - }) + }); it('should be returning undefined from the chainId method', async () => { const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); - jest.spyOn(provider as any, 'request').mockResolvedValueOnce({result: undefined}); + jest.spyOn(provider as any, 'request').mockResolvedValueOnce({ result: undefined }); const result = await provider['_getChainId'](); - expect(result).toBe(""); - }) + expect(result).toBe(''); + }); it('should return empty from the chainId method', async () => { const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); jest.spyOn(provider as any, 'request').mockResolvedValueOnce(undefined); const result = await provider['_getChainId'](); - expect(result).toBe(""); - }) - }) + expect(result).toBe(''); + }); + }); describe('_getAccounts', () => { it('should return data from the _getAccounts method', async () => { const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); const accounts = [1]; - jest.spyOn(provider as any, 'request').mockResolvedValueOnce({result: accounts}); + jest.spyOn(provider as any, 'request').mockResolvedValueOnce({ result: accounts }); const result = await provider['_getAccounts'](); expect(result).toBe(accounts); - }) + }); it('should returning undefined from the _getAccounts method', async () => { const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); - jest.spyOn(provider as any, 'request').mockResolvedValueOnce({result: undefined}); + jest.spyOn(provider as any, 'request').mockResolvedValueOnce({ result: undefined }); const result = await provider['_getAccounts'](); expect(result).toEqual([]); - }) + }); it('should return empty from the _getAccounts method', async () => { const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); jest.spyOn(provider as any, 'request').mockResolvedValueOnce(undefined); const result = await provider['_getAccounts'](); expect(result).toEqual([]); - }) - }) + }); + }); describe('_onMessage', () => { it('should resolve the deferred promise for valid responses with errors', () => { @@ -846,7 +844,11 @@ describe('SocketProvider', () => { it('should emit "message" event for notifications', () => { const provider = new TestProvider(socketPath, socketOption, { delay: 0 }); const event = { - data: JSON.stringify({ jsonrpc: '2.0', method: 'notification_1_subscription', params: {} }), + data: JSON.stringify({ + jsonrpc: '2.0', + method: 'notification_1_subscription', + params: {}, + }), }; const eventEmitterSpy = jest.spyOn(provider['_eventEmitter'], 'emit'); @@ -860,4 +862,4 @@ describe('SocketProvider', () => { }); }); }); -}); \ No newline at end of file +}); diff --git a/packages/web3-utils/test/unit/web3_deferred_promise.test.ts b/packages/web3-utils/test/unit/web3_deferred_promise.test.ts index 044f558fb10..6fe87129e73 100644 --- a/packages/web3-utils/test/unit/web3_deferred_promise.test.ts +++ b/packages/web3-utils/test/unit/web3_deferred_promise.test.ts @@ -66,4 +66,4 @@ describe('Web3DeferredPromise', () => { expect(callbackExecuted).toBe(true); }); }); -}); \ No newline at end of file +}); From b9617eebb77c1905ff40337ad115d9872629d356 Mon Sep 17 00:00:00 2001 From: Alex Luu Date: Sun, 2 Jun 2024 22:38:45 -0400 Subject: [PATCH 17/17] remove unintended cases for formatter and convert --- .../web3-utils/test/unit/formatter.test.ts | 110 +----------------- 1 file changed, 1 insertion(+), 109 deletions(-) diff --git a/packages/web3-utils/test/unit/formatter.test.ts b/packages/web3-utils/test/unit/formatter.test.ts index add9095596b..aa1a14fb6c9 100644 --- a/packages/web3-utils/test/unit/formatter.test.ts +++ b/packages/web3-utils/test/unit/formatter.test.ts @@ -855,76 +855,7 @@ describe('formatter', () => { }); describe('convert', () => { - it('should convert array values correctly when schema is an array and items are objects', () => { - const schema = { - type: 'object', - properties: { - transactions: { - type: 'array', - oneOf: [ - { - items: { - type: 'object', - properties: { - address: { format: 'string' }, - value: { type: 'string' }, - }, - }, - }, - { - items: { - type: 'object', - properties: { - address: { format: 'string' }, - value: { type: 'number' }, - }, - }, - }, - ], - }, - }, - }; - const data = { - transactions: [ - { address: 'B', value: '42' }, - { address: 'B', value: 43 }, - ], - }; - expect( - convert(data, schema, [], { - number: FMT_NUMBER.HEX, - bytes: FMT_BYTES.HEX, - }), - ).toEqual(data); - }); - - it('should delete the array property if schema for array items is nullish', () => { - const schema = { - type: 'object', - properties: { - transactions: { - type: 'array', - items: undefined, // Simulate a missing or null schema for items - }, - }, - }; - - const data = { - transactions: [ - { type: 'A', value: 'some string' }, - { type: 'B', value: 42 }, - ], - }; - - const result = convert(data, schema, [], { - number: FMT_NUMBER.HEX, - bytes: FMT_BYTES.HEX, - }); - - expect(result).toEqual({}); - }); - - it('should return empty when no proerties or items', () => { + it('should return empty when no properties or items', () => { const data = { key: 'value' }; const schema = { type: 'object', @@ -933,44 +864,5 @@ describe('formatter', () => { const result = convert(data, schema, [], f, []); expect(result).toEqual({}); }); - - it('should format nested objects with oneOf', () => { - const schema = { - type: 'object', - properties: { - details: { - type: 'object', - oneOf: [ - { properties: { type: { enum: ['A'] }, value: { type: 'string' } } }, - { properties: { type: { enum: ['B'] }, value: { type: 'number' } } }, - ], - }, - }, - }; - - const data = { - details: { - type: 'B', - value: 42, - }, - }; - const result = convert( - data, - schema, - [], - { - number: FMT_NUMBER.BIGINT, - bytes: FMT_BYTES.UINT8ARRAY, - }, - [['details', 1]], - ); - - expect(result).toEqual({ - details: { - type: 'B', - value: 42, - }, - }); - }); }); });