Resolve "WIP: UTXO Selection and Storage"
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
import { describe, test, expect, vi, beforeEach } from 'vitest';
|
||||
import { EventEmitter } from '~/src/utils/event-emitter.js';
|
||||
|
||||
type TestEvents = {
|
||||
test: { data: string };
|
||||
count: { value: number };
|
||||
multiPayload: { first: string; second: number };
|
||||
};
|
||||
|
||||
describe('EventEmitter', () => {
|
||||
let emitter: EventEmitter<TestEvents>;
|
||||
|
||||
beforeEach(() => {
|
||||
emitter = new EventEmitter<TestEvents>();
|
||||
});
|
||||
|
||||
test('should register and trigger an event listener', () => {
|
||||
const mockCallback = vi.fn();
|
||||
emitter.on('test', mockCallback);
|
||||
|
||||
emitter.emit('test', { data: 'hello' });
|
||||
|
||||
expect(mockCallback).toHaveBeenCalledTimes(1);
|
||||
expect(mockCallback).toHaveBeenCalledWith({ data: 'hello' });
|
||||
});
|
||||
|
||||
test('should register multiple listeners for the same event', () => {
|
||||
const mockCallback1 = vi.fn();
|
||||
const mockCallback2 = vi.fn();
|
||||
|
||||
emitter.on('test', mockCallback1);
|
||||
emitter.on('test', mockCallback2);
|
||||
|
||||
emitter.emit('test', { data: 'hello' });
|
||||
|
||||
expect(mockCallback1).toHaveBeenCalledTimes(1);
|
||||
expect(mockCallback2).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should remove a specific listener', () => {
|
||||
const mockCallback1 = vi.fn();
|
||||
const mockCallback2 = vi.fn();
|
||||
|
||||
emitter.on('test', mockCallback1);
|
||||
emitter.on('test', mockCallback2);
|
||||
|
||||
emitter.off('test', mockCallback1);
|
||||
emitter.emit('test', { data: 'hello' });
|
||||
|
||||
expect(mockCallback1).not.toHaveBeenCalled();
|
||||
expect(mockCallback2).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should remove all listeners when removeAllListeners is called', () => {
|
||||
const mockCallback1 = vi.fn();
|
||||
const mockCallback2 = vi.fn();
|
||||
|
||||
emitter.on('test', mockCallback1);
|
||||
emitter.on('count', mockCallback2);
|
||||
|
||||
emitter.removeAllListeners();
|
||||
|
||||
emitter.emit('test', { data: 'hello' });
|
||||
emitter.emit('count', { value: 42 });
|
||||
|
||||
expect(mockCallback1).not.toHaveBeenCalled();
|
||||
expect(mockCallback2).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should call listener only once when using once', () => {
|
||||
const mockCallback = vi.fn();
|
||||
|
||||
emitter.once('test', mockCallback);
|
||||
|
||||
emitter.emit('test', { data: 'first' });
|
||||
emitter.emit('test', { data: 'second' });
|
||||
|
||||
expect(mockCallback).toHaveBeenCalledTimes(1);
|
||||
expect(mockCallback).toHaveBeenCalledWith({ data: 'first' });
|
||||
});
|
||||
|
||||
test('should handle multiple event types correctly', () => {
|
||||
const testCallback = vi.fn();
|
||||
const countCallback = vi.fn();
|
||||
|
||||
emitter.on('test', testCallback);
|
||||
emitter.on('count', countCallback);
|
||||
|
||||
emitter.emit('test', { data: 'test data' });
|
||||
emitter.emit('count', { value: 123 });
|
||||
|
||||
expect(testCallback).toHaveBeenCalledWith({ data: 'test data' });
|
||||
expect(countCallback).toHaveBeenCalledWith({ value: 123 });
|
||||
});
|
||||
|
||||
test('should debounce events correctly', async () => {
|
||||
vi.useFakeTimers();
|
||||
const mockCallback = vi.fn();
|
||||
|
||||
emitter.on('test', mockCallback, 100);
|
||||
|
||||
emitter.emit('test', { data: 'first' });
|
||||
emitter.emit('test', { data: 'second' });
|
||||
emitter.emit('test', { data: 'third' });
|
||||
|
||||
expect(mockCallback).not.toHaveBeenCalled();
|
||||
|
||||
await vi.runAllTimersAsync();
|
||||
|
||||
expect(mockCallback).toHaveBeenCalledTimes(1);
|
||||
expect(mockCallback).toHaveBeenCalledWith({ data: 'third' });
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
test('should handle complex payloads', () => {
|
||||
const mockCallback = vi.fn();
|
||||
|
||||
emitter.on('multiPayload', mockCallback);
|
||||
|
||||
const payload = { first: 'hello', second: 42 };
|
||||
emitter.emit('multiPayload', payload);
|
||||
|
||||
expect(mockCallback).toHaveBeenCalledWith(payload);
|
||||
});
|
||||
|
||||
test('should return true when event has listeners and false when it does not', () => {
|
||||
const mockCallback = vi.fn();
|
||||
|
||||
expect(emitter.emit('test', { data: 'hello' })).toBe(false);
|
||||
|
||||
emitter.on('test', mockCallback);
|
||||
|
||||
expect(emitter.emit('test', { data: 'hello' })).toBe(true);
|
||||
});
|
||||
|
||||
test('should handle debounced once events correctly', async () => {
|
||||
vi.useFakeTimers();
|
||||
const mockCallback = vi.fn();
|
||||
|
||||
emitter.once('test', mockCallback, 100);
|
||||
|
||||
emitter.emit('test', { data: 'first' });
|
||||
emitter.emit('test', { data: 'second' });
|
||||
emitter.emit('test', { data: 'third' });
|
||||
|
||||
expect(mockCallback).not.toHaveBeenCalled();
|
||||
|
||||
await vi.runAllTimersAsync();
|
||||
|
||||
expect(mockCallback).toHaveBeenCalledTimes(1);
|
||||
expect(mockCallback).toHaveBeenCalledWith({ data: 'third' });
|
||||
|
||||
// Emit again after debounce
|
||||
emitter.emit('test', { data: 'fourth' });
|
||||
|
||||
await vi.runAllTimersAsync();
|
||||
|
||||
expect(mockCallback).toHaveBeenCalledTimes(1);
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
test('waitFor should resolve when predicate is satisfied', async () => {
|
||||
const promise = emitter.waitFor(
|
||||
'test',
|
||||
(payload) => payload.data === 'success',
|
||||
);
|
||||
|
||||
emitter.emit('test', { data: 'not yet' });
|
||||
emitter.emit('test', { data: 'success' });
|
||||
|
||||
const result = await promise;
|
||||
expect(result).toEqual({ data: 'success' });
|
||||
});
|
||||
|
||||
test('waitFor should reject on timeout', async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const promise = emitter.waitFor(
|
||||
'test',
|
||||
(payload) => payload.data === 'success',
|
||||
1000,
|
||||
);
|
||||
|
||||
// Need to handle the rejection or wait for it
|
||||
const promiseResult = promise.catch((error) => {
|
||||
expect(error.message).toBe('Timeout waiting for event "test"');
|
||||
});
|
||||
|
||||
emitter.emit('test', { data: 'not yet' });
|
||||
|
||||
await vi.runAllTimersAsync();
|
||||
await promiseResult;
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
test('waitFor should clean up listeners after resolution', async () => {
|
||||
const promise = emitter.waitFor(
|
||||
'test',
|
||||
(payload) => payload.data === 'success',
|
||||
);
|
||||
|
||||
emitter.emit('test', { data: 'success' });
|
||||
await promise;
|
||||
|
||||
// The internal listener should be removed
|
||||
expect(emitter.emit('test', { data: 'another' })).toBe(false);
|
||||
});
|
||||
|
||||
test('waitFor should handle multiple concurrent waits', async () => {
|
||||
const promise1 = emitter.waitFor(
|
||||
'test',
|
||||
(payload) => payload.data === 'first',
|
||||
);
|
||||
const promise2 = emitter.waitFor(
|
||||
'test',
|
||||
(payload) => payload.data === 'second',
|
||||
);
|
||||
|
||||
emitter.emit('test', { data: 'second' });
|
||||
emitter.emit('test', { data: 'first' });
|
||||
|
||||
const [result1, result2] = await Promise.all([promise1, promise2]);
|
||||
|
||||
expect(result1).toEqual({ data: 'first' });
|
||||
expect(result2).toEqual({ data: 'second' });
|
||||
});
|
||||
|
||||
test('waitFor should clean up timeout on successful resolution', async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const promise = emitter.waitFor(
|
||||
'test',
|
||||
(payload) => payload.data === 'success',
|
||||
1000,
|
||||
);
|
||||
|
||||
// Emit success before timeout
|
||||
emitter.emit('test', { data: 'success' });
|
||||
|
||||
// Fast-forward time - should not cause rejection
|
||||
await vi.runAllTimersAsync();
|
||||
|
||||
const result = await promise;
|
||||
expect(result).toEqual({ data: 'success' });
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user