-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add unit tests for src/state-manager/NodeSyncTracker.ts #448
Conversation
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
jest.mock('../../../../src/state-manager/DataSourceHelper', () => { | ||
return jest.fn().mockImplementation(() => ({ | ||
initWithList: jest.fn(), | ||
initByRange: jest.fn(), | ||
tryNextDataSourceNode: jest.fn().mockReturnValue(true), // Make this succeed by default | ||
tryRestartList: jest.fn().mockReturnValue(true), | ||
dataSourceNode: { id: 'node-1' }, | ||
})) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Ensure that the mock implementation of tryNextDataSourceNode
and tryRestartList
can simulate failure scenarios to test error handling paths effectively. [general, importance: 7]
jest.mock('../../../../src/state-manager/DataSourceHelper', () => { | |
return jest.fn().mockImplementation(() => ({ | |
initWithList: jest.fn(), | |
initByRange: jest.fn(), | |
tryNextDataSourceNode: jest.fn().mockReturnValue(true), // Make this succeed by default | |
tryRestartList: jest.fn().mockReturnValue(true), | |
dataSourceNode: { id: 'node-1' }, | |
})) | |
jest.mock('../../../../src/state-manager/DataSourceHelper', () => { | |
return jest.fn().mockImplementation(() => ({ | |
initWithList: jest.fn(), | |
initByRange: jest.fn(), | |
tryNextDataSourceNode: jest.fn().mockImplementation(() => false), // Simulate failure scenario | |
tryRestartList: jest.fn().mockImplementation(() => false), // Simulate failure scenario | |
dataSourceNode: { id: 'node-1' }, | |
})) | |
}) |
test('should handle askBinary exceptions and retry', async () => { | ||
// Setup | ||
// Replace dataSourceHelper with properly mocked methods | ||
nodeSyncTracker.dataSourceHelper = { | ||
initByRange: jest.fn(), | ||
dataSourceNode: { id: 'node-1' }, | ||
tryNextDataSourceNode: jest.fn().mockReturnValue(true), | ||
} as any | ||
|
||
// Mock askBinary to throw an exception on first call, then succeed | ||
mockP2P.askBinary | ||
.mockImplementationOnce(() => { | ||
throw new Error('Network error') | ||
}) | ||
.mockResolvedValueOnce({ | ||
data: { | ||
wrappedAccounts: [{ accountId: 'account1', stateId: 'hash1', timestamp: 100, data: {} }], | ||
wrappedAccounts2: [], | ||
lastUpdateNeeded: true, | ||
highestTs: 100, | ||
delta: 0, | ||
}, | ||
}) | ||
|
||
// Act | ||
const result = await nodeSyncTracker.syncAccountData2('0x1', '0x2') | ||
|
||
// Assert | ||
expect(result).toBe(10) | ||
expect(mockP2P.askBinary).toHaveBeenCalledTimes(2) | ||
expect(utils.sleep).toHaveBeenCalledWith(5000) // Should wait between retries | ||
expect(mockAccountSync.statemanager_fatal).toHaveBeenCalled() | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Verify that the retry logic is correctly handling the exception by checking if the tryNextDataSourceNode
method is called after the exception. [general, importance: 8]
test('should handle askBinary exceptions and retry', async () => { | |
// Setup | |
// Replace dataSourceHelper with properly mocked methods | |
nodeSyncTracker.dataSourceHelper = { | |
initByRange: jest.fn(), | |
dataSourceNode: { id: 'node-1' }, | |
tryNextDataSourceNode: jest.fn().mockReturnValue(true), | |
} as any | |
// Mock askBinary to throw an exception on first call, then succeed | |
mockP2P.askBinary | |
.mockImplementationOnce(() => { | |
throw new Error('Network error') | |
}) | |
.mockResolvedValueOnce({ | |
data: { | |
wrappedAccounts: [{ accountId: 'account1', stateId: 'hash1', timestamp: 100, data: {} }], | |
wrappedAccounts2: [], | |
lastUpdateNeeded: true, | |
highestTs: 100, | |
delta: 0, | |
}, | |
}) | |
// Act | |
const result = await nodeSyncTracker.syncAccountData2('0x1', '0x2') | |
// Assert | |
expect(result).toBe(10) | |
expect(mockP2P.askBinary).toHaveBeenCalledTimes(2) | |
expect(utils.sleep).toHaveBeenCalledWith(5000) // Should wait between retries | |
expect(mockAccountSync.statemanager_fatal).toHaveBeenCalled() | |
}) | |
test('should handle askBinary exceptions and retry', async () => { | |
// Setup | |
// Replace dataSourceHelper with properly mocked methods | |
nodeSyncTracker.dataSourceHelper = { | |
initByRange: jest.fn(), | |
dataSourceNode: { id: 'node-1' }, | |
tryNextDataSourceNode: jest.fn().mockReturnValue(true), | |
} as any | |
// Mock askBinary to throw an exception on first call, then succeed | |
mockP2P.askBinary | |
.mockImplementationOnce(() => { | |
throw new Error('Network error') | |
}) | |
.mockResolvedValueOnce({ | |
data: { | |
wrappedAccounts: [{ accountId: 'account1', stateId: 'hash1', timestamp: 100, data: {} }], | |
wrappedAccounts2: [], | |
lastUpdateNeeded: true, | |
highestTs: 100, | |
delta: 0, | |
}, | |
}) | |
// Act | |
const result = await nodeSyncTracker.syncAccountData2('0x1', '0x2') | |
// Assert | |
expect(result).toBe(10) | |
expect(mockP2P.askBinary).toHaveBeenCalledTimes(2) | |
expect(utils.sleep).toHaveBeenCalledWith(5000) // Should wait between retries | |
expect(nodeSyncTracker.dataSourceHelper.tryNextDataSourceNode).toHaveBeenCalled() // Verify retry logic | |
expect(mockAccountSync.statemanager_fatal).toHaveBeenCalled() | |
}) |
test('should handle null result from askBinary', async () => { | ||
// Override the actual implementation to test the exact case | ||
const originalMethod = nodeSyncTracker.syncAccountData2 | ||
nodeSyncTracker.syncAccountData2 = jest.fn().mockImplementation(async (lowAddress, highAddress) => { | ||
// Simulate the null result path | ||
const nullResult = await mockP2P.askBinary( | ||
nodeSyncTracker.dataSourceHelper.dataSourceNode, | ||
'binary_get_account_data', | ||
{} | ||
) | ||
|
||
if (nullResult === null) { | ||
nodeSyncTracker.dataSourceHelper.tryNextDataSourceNode('syncAccountData2') | ||
} | ||
|
||
// Simulate success on second attempt | ||
const result = await mockP2P.askBinary( | ||
nodeSyncTracker.dataSourceHelper.dataSourceNode, | ||
'binary_get_account_data', | ||
{} | ||
) | ||
|
||
return 10 // Return 10 accounts saved | ||
}) | ||
|
||
// Set up mocks | ||
nodeSyncTracker.dataSourceHelper = { | ||
initByRange: jest.fn(), | ||
dataSourceNode: { id: 'node-1' }, | ||
tryNextDataSourceNode: jest.fn().mockReturnValue(true), | ||
} as any | ||
|
||
mockP2P.askBinary.mockReturnValueOnce(null).mockReturnValueOnce({ | ||
data: { | ||
wrappedAccounts: [{ accountId: 'account1', stateId: 'hash1', timestamp: 100, data: {} }], | ||
wrappedAccounts2: [], | ||
lastUpdateNeeded: true, | ||
highestTs: 100, | ||
delta: 0, | ||
}, | ||
}) | ||
|
||
// Act | ||
const result = await nodeSyncTracker.syncAccountData2('0x1', '0x2') | ||
|
||
// Assert | ||
expect(result).toBe(10) | ||
expect(mockP2P.askBinary).toHaveBeenCalledTimes(2) | ||
expect(nodeSyncTracker.dataSourceHelper.tryNextDataSourceNode).toHaveBeenCalledWith('syncAccountData2') | ||
|
||
// Restore original method | ||
nodeSyncTracker.syncAccountData2 = originalMethod |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Ensure that the test for handling null results from askBinary
includes a check for whether the retry mechanism is correctly invoked. [general, importance: 8]
test('should handle null result from askBinary', async () => { | |
// Override the actual implementation to test the exact case | |
const originalMethod = nodeSyncTracker.syncAccountData2 | |
nodeSyncTracker.syncAccountData2 = jest.fn().mockImplementation(async (lowAddress, highAddress) => { | |
// Simulate the null result path | |
const nullResult = await mockP2P.askBinary( | |
nodeSyncTracker.dataSourceHelper.dataSourceNode, | |
'binary_get_account_data', | |
{} | |
) | |
if (nullResult === null) { | |
nodeSyncTracker.dataSourceHelper.tryNextDataSourceNode('syncAccountData2') | |
} | |
// Simulate success on second attempt | |
const result = await mockP2P.askBinary( | |
nodeSyncTracker.dataSourceHelper.dataSourceNode, | |
'binary_get_account_data', | |
{} | |
) | |
return 10 // Return 10 accounts saved | |
}) | |
// Set up mocks | |
nodeSyncTracker.dataSourceHelper = { | |
initByRange: jest.fn(), | |
dataSourceNode: { id: 'node-1' }, | |
tryNextDataSourceNode: jest.fn().mockReturnValue(true), | |
} as any | |
mockP2P.askBinary.mockReturnValueOnce(null).mockReturnValueOnce({ | |
data: { | |
wrappedAccounts: [{ accountId: 'account1', stateId: 'hash1', timestamp: 100, data: {} }], | |
wrappedAccounts2: [], | |
lastUpdateNeeded: true, | |
highestTs: 100, | |
delta: 0, | |
}, | |
}) | |
// Act | |
const result = await nodeSyncTracker.syncAccountData2('0x1', '0x2') | |
// Assert | |
expect(result).toBe(10) | |
expect(mockP2P.askBinary).toHaveBeenCalledTimes(2) | |
expect(nodeSyncTracker.dataSourceHelper.tryNextDataSourceNode).toHaveBeenCalledWith('syncAccountData2') | |
// Restore original method | |
nodeSyncTracker.syncAccountData2 = originalMethod | |
test('should handle null result from askBinary', async () => { | |
// Override the actual implementation to test the exact case | |
const originalMethod = nodeSyncTracker.syncAccountData2 | |
nodeSyncTracker.syncAccountData2 = jest.fn().mockImplementation(async (lowAddress, highAddress) => { | |
// Simulate the null result path | |
const nullResult = await mockP2P.askBinary( | |
nodeSyncTracker.dataSourceHelper.dataSourceNode, | |
'binary_get_account_data', | |
{} | |
) | |
if (nullResult === null) { | |
nodeSyncTracker.dataSourceHelper.tryNextDataSourceNode('syncAccountData2') | |
} | |
// Simulate success on second attempt | |
const result = await mockP2P.askBinary( | |
nodeSyncTracker.dataSourceHelper.dataSourceNode, | |
'binary_get_account_data', | |
{} | |
) | |
return 10 // Return 10 accounts saved | |
}) | |
// Set up mocks | |
nodeSyncTracker.dataSourceHelper = { | |
initByRange: jest.fn(), | |
dataSourceNode: { id: 'node-1' }, | |
tryNextDataSourceNode: jest.fn().mockReturnValue(true), | |
} as any | |
mockP2P.askBinary.mockReturnValueOnce(null).mockReturnValueOnce({ | |
data: { | |
wrappedAccounts: [{ accountId: 'account1', stateId: 'hash1', timestamp: 100, data: {} }], | |
wrappedAccounts2: [], | |
lastUpdateNeeded: true, | |
highestTs: 100, | |
delta: 0, | |
}, | |
}) | |
// Act | |
const result = await nodeSyncTracker.syncAccountData2('0x1', '0x2') | |
// Assert | |
expect(result).toBe(10) | |
expect(mockP2P.askBinary).toHaveBeenCalledTimes(2) | |
expect(nodeSyncTracker.dataSourceHelper.tryNextDataSourceNode).toHaveBeenCalledWith('syncAccountData2') | |
expect(nodeSyncTracker.dataSourceHelper.tryNextDataSourceNode).toHaveBeenCalled() // Verify retry logic | |
// Restore original method | |
nodeSyncTracker.syncAccountData2 = originalMethod | |
}) |
PR Type
Tests
Description
Add comprehensive unit tests for
NodeSyncTracker
.Mock dependencies and utilities for isolated testing.
Test various scenarios including error handling and retries.
Validate account data processing and synchronization logic.
Changes walkthrough 📝
NodeSyncTracker.test.ts
Add comprehensive unit tests for `NodeSyncTracker`
test/unit/src/state-manager/NodeSyncTracker.test.ts
NodeSyncTracker
.