fixing empty transaction
This commit is contained in:
@@ -15,12 +15,14 @@ import { randomUUID } from 'crypto';
|
||||
const mockOnLogs = vi.fn().mockReturnValue(12345);
|
||||
const mockRemoveOnLogsListener = vi.fn().mockResolvedValue(true);
|
||||
const mockGetTransaction = vi.fn();
|
||||
const mockGetSignaturesForAddress = vi.fn().mockResolvedValue([]);
|
||||
|
||||
vi.mock('@solana/web3.js', () => {
|
||||
class MockConnection {
|
||||
onLogs = mockOnLogs;
|
||||
removeOnLogsListener = mockRemoveOnLogsListener;
|
||||
getTransaction = mockGetTransaction;
|
||||
getSignaturesForAddress = mockGetSignaturesForAddress;
|
||||
}
|
||||
|
||||
class MockPublicKey {
|
||||
@@ -96,26 +98,17 @@ describe('Real Solana Observation Mode Unit Tests', () => {
|
||||
|
||||
// 2. RealWalletWatcher Subscription and Shutdown Behavior
|
||||
describe('RealWalletWatcher Lifecycle & Reconnect Behavior', () => {
|
||||
it('should subscribe to logs for all watched wallets on startup', async () => {
|
||||
it('should query signatures on startup to set checkpoint and poll periodically', async () => {
|
||||
const wallets = ['3yF9asA9B7G3Y1G7as78gHJKa7A', '4yF9asA9B7G3Y1G7as78gHJKa7B'];
|
||||
mockGetSignaturesForAddress.mockResolvedValue([
|
||||
{ signature: 'sig1', slot: 100, err: null }
|
||||
]);
|
||||
const watcher = new RealWalletWatcher(wallets, 'http://dummy-rpc', 'wss://dummy-wss');
|
||||
|
||||
await watcher.startWatching();
|
||||
|
||||
expect(connection.onLogs).toHaveBeenCalledTimes(2);
|
||||
expect(connection.onLogs).toHaveBeenNthCalledWith(1, expect.any(Object), expect.any(Function), 'confirmed');
|
||||
expect(connection.onLogs).toHaveBeenNthCalledWith(2, expect.any(Object), expect.any(Function), 'confirmed');
|
||||
});
|
||||
|
||||
it('should unsubscribe from all logs listeners on shutdown', async () => {
|
||||
const wallets = ['3yF9asA9B7G3Y1G7as78gHJKa7A'];
|
||||
const watcher = new RealWalletWatcher(wallets, 'http://dummy-rpc', 'wss://dummy-wss');
|
||||
|
||||
await watcher.startWatching();
|
||||
expect(mockGetSignaturesForAddress).toHaveBeenCalled();
|
||||
await watcher.stopWatching();
|
||||
|
||||
expect(connection.removeOnLogsListener).toHaveBeenCalledTimes(1);
|
||||
expect(connection.removeOnLogsListener).toHaveBeenCalledWith(12345);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -327,6 +320,9 @@ describe('Real Solana Observation Mode Unit Tests', () => {
|
||||
recordRpcRequestRate: vi.fn(),
|
||||
recordQueueProcessed: vi.fn(),
|
||||
recordRpcFetchError: vi.fn(),
|
||||
recordWalletState: vi.fn(),
|
||||
recordWalletIncomingRate: vi.fn(),
|
||||
recordWalletProcessedRate: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -724,6 +720,75 @@ describe('Real Solana Observation Mode Unit Tests', () => {
|
||||
loggerDebugSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should correctly transition wallet state to HOT under overload and maintain NORMAL for others', async () => {
|
||||
const { BoundedTransactionQueue } = await import('../src/solana/fetcher.js');
|
||||
const queue = new BoundedTransactionQueue(
|
||||
connection as any,
|
||||
1, // maxConcurrency = 1
|
||||
10, // minDelay = 10ms
|
||||
5, // maxQueueSize = 5
|
||||
mockMetrics,
|
||||
);
|
||||
queue.start();
|
||||
|
||||
// Push first item for walletA (goes active immediately)
|
||||
queue.push({
|
||||
signature: 'A1',
|
||||
walletAddress: 'walletA',
|
||||
slot: 100,
|
||||
detectedAt: new Date(),
|
||||
correlationId: 'corrA1',
|
||||
onSuccess: async () => {},
|
||||
onFailure: async () => {},
|
||||
});
|
||||
|
||||
// walletA gets 4 more items queued (total depth 4/5 = 80%, should trigger HOT)
|
||||
for (let i = 2; i <= 5; i++) {
|
||||
queue.push({
|
||||
signature: `A${i}`,
|
||||
walletAddress: 'walletA',
|
||||
slot: 100,
|
||||
detectedAt: new Date(),
|
||||
correlationId: `corrA${i}`,
|
||||
onSuccess: async () => {},
|
||||
onFailure: async () => {},
|
||||
});
|
||||
}
|
||||
|
||||
// walletB gets 1 item queued (depth 1/5, should stay NORMAL)
|
||||
queue.push({
|
||||
signature: 'B1',
|
||||
walletAddress: 'walletB',
|
||||
slot: 100,
|
||||
detectedAt: new Date(),
|
||||
correlationId: 'corrB1',
|
||||
onSuccess: async () => {},
|
||||
onFailure: async () => {},
|
||||
});
|
||||
|
||||
expect(queue.getWalletState('walletA')).toBe('HOT');
|
||||
expect(queue.getWalletState('walletB')).toBe('NORMAL');
|
||||
|
||||
queue.stop();
|
||||
});
|
||||
|
||||
it('should pre-filter obviously irrelevant transactions using the lightweight log filter', async () => {
|
||||
const { isLogIrrelevant } = await import('../src/solana/parser.js');
|
||||
|
||||
const irrelevantLogs = [
|
||||
'Program Vote111111111111111111111111111111111111111 invoke [1]',
|
||||
'Program Vote111111111111111111111111111111111111111 success',
|
||||
];
|
||||
|
||||
const normalLogs = [
|
||||
'Program ComputeBudget111111111111111111111111111111 invoke [1]',
|
||||
'Program swapInstruction invoke [2]',
|
||||
];
|
||||
|
||||
expect(isLogIrrelevant(irrelevantLogs)).toBe(true);
|
||||
expect(isLogIrrelevant(normalLogs)).toBe(false);
|
||||
});
|
||||
|
||||
it('should classify fetched transactions correctly based on SOL/SPL token balance changes', async () => {
|
||||
const { isTransactionRelevant } = await import('../src/solana/parser.js');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user