UNPKG

@hashgraph/solo

Version:

An opinionated CLI tool to deploy and manage private Hedera Networks.

222 lines 11 kB
// SPDX-License-Identifier: Apache-2.0 import sinon from 'sinon'; import { expect } from 'chai'; import { describe, it, beforeEach } from 'mocha'; import { DefaultSoloEventBus } from '../../../../src/core/events/default-solo-event-bus.js'; import { SoloEventType } from '../../../../src/core/events/event-types/solo-event.js'; import { NetworkDeployedEvent } from '../../../../src/core/events/event-types/network-deployed-event.js'; import { MirrorNodeDeployedEvent } from '../../../../src/core/events/event-types/mirror-node-deployed-event.js'; import { container } from 'tsyringe-neo'; import { InjectTokens } from '../../../../src/core/dependency-injection/inject-tokens.js'; import { Duration } from '../../../../src/core/time/duration.js'; import { SoloError } from '../../../../src/core/errors/solo-error.js'; describe('SoloEventBus', () => { let bus; const networkEvent = new NetworkDeployedEvent('my-deployment'); const mirrorEvent = new MirrorNodeDeployedEvent('my-deployment'); beforeEach(() => { // resolve the test logger from the DI container const testLogger = container.resolve(InjectTokens.SoloLogger); bus = new DefaultSoloEventBus(testLogger); }); it('should call a registered handler when the matching event is emitted', () => { const handler = sinon.spy(); bus.on(SoloEventType.NetworkDeployed, handler); bus.emit(networkEvent); expect(handler).to.have.been.calledOnceWithExactly(networkEvent); }); it('should not call a handler after it has been removed with off()', () => { const handler = sinon.spy(); bus.on(SoloEventType.NetworkDeployed, handler); bus.off(SoloEventType.NetworkDeployed, handler); bus.emit(networkEvent); expect(handler).not.to.have.been.called; }); it('should call all registered handlers for the same event type', () => { const handlerA = sinon.spy(); const handlerB = sinon.spy(); bus.on(SoloEventType.NetworkDeployed, handlerA); bus.on(SoloEventType.NetworkDeployed, handlerB); bus.emit(networkEvent); expect(handlerA).to.have.been.calledOnceWithExactly(networkEvent); expect(handlerB).to.have.been.calledOnceWithExactly(networkEvent); }); it('should not call a handler registered for a different event type', () => { const handler = sinon.spy(); bus.on(SoloEventType.MirrorNodeDeployed, handler); bus.emit(networkEvent); expect(handler).not.to.have.been.called; }); it('should call handlers for different event types independently', () => { const networkHandler = sinon.spy(); const mirrorHandler = sinon.spy(); bus.on(SoloEventType.NetworkDeployed, networkHandler); bus.on(SoloEventType.MirrorNodeDeployed, mirrorHandler); bus.emit(networkEvent); bus.emit(mirrorEvent); expect(networkHandler).to.have.been.calledOnceWithExactly(networkEvent); expect(mirrorHandler).to.have.been.calledOnceWithExactly(mirrorEvent); }); it('should only remove the specific handler passed to off(), leaving others intact', () => { const handlerA = sinon.spy(); const handlerB = sinon.spy(); bus.on(SoloEventType.NetworkDeployed, handlerA); bus.on(SoloEventType.NetworkDeployed, handlerB); bus.off(SoloEventType.NetworkDeployed, handlerA); bus.emit(networkEvent); expect(handlerA).not.to.have.been.called; expect(handlerB).to.have.been.calledOnceWithExactly(networkEvent); }); it('should call a handler each time the event is emitted', () => { const handler = sinon.spy(); bus.on(SoloEventType.NetworkDeployed, handler); bus.emit(networkEvent); bus.emit(networkEvent); expect(handler).to.have.been.calledTwice; }); it('waitFor() should resolve with the event when it is emitted', async () => { const promise = bus.waitFor(SoloEventType.NetworkDeployed); bus.emit(networkEvent); const result = await promise; expect(result).to.equal(networkEvent); }); it('waitFor() should resolve only once even if the event is emitted multiple times', async () => { const results = []; const promise = bus.waitFor(SoloEventType.NetworkDeployed); promise.then((soloEvent) => results.push(soloEvent)); bus.emit(networkEvent); bus.emit(networkEvent); await promise; expect(results).to.have.lengthOf(1); }); it('waitFor() should not resolve for a different event type', async () => { let resolved = false; bus.waitFor(SoloEventType.NetworkDeployed).then(() => { resolved = true; }); bus.emit(mirrorEvent); await Promise.resolve(); // flush microtask queue expect(resolved).to.be.false; }); it('waitFor() with predicate should resolve when the predicate returns true', async () => { const target = new NetworkDeployedEvent('target'); const other = new NetworkDeployedEvent('other'); const promise = bus.waitFor(SoloEventType.NetworkDeployed, (soloEvent) => soloEvent.deployment === 'target'); bus.emit(other); bus.emit(target); expect(await promise).to.equal(target); }); it('waitFor() with predicate should skip events that do not match', async () => { let resolved = false; bus .waitFor(SoloEventType.NetworkDeployed, (soloEvent) => soloEvent.deployment === 'target') .then(() => { resolved = true; }); bus.emit(new NetworkDeployedEvent('other')); await Promise.resolve(); expect(resolved).to.be.false; }); it('waitFor() with predicate should resolve only once even if multiple matching events are emitted', async () => { const results = []; const promise = bus.waitFor(SoloEventType.NetworkDeployed, () => true); promise.then((soloEvent) => results.push(soloEvent)); bus.emit(networkEvent); bus.emit(networkEvent); await promise; expect(results).to.have.lengthOf(1); }); it('waitFor() for different types resolves each independently', async () => { const networkPromise = bus.waitFor(SoloEventType.NetworkDeployed); const mirrorPromise = bus.waitFor(SoloEventType.MirrorNodeDeployed); bus.emit(networkEvent); bus.emit(mirrorEvent); expect(await networkPromise).to.equal(networkEvent); expect(await mirrorPromise).to.equal(mirrorEvent); }); it('NetworkDeployedEvent should have the correct type and deployment', () => { const event = new NetworkDeployedEvent('solo-deployment'); expect(event.type).to.equal(SoloEventType.NetworkDeployed); expect(event.deployment).to.equal('solo-deployment'); }); it('MirrorNodeDeployedEvent should have the correct type and deployment', () => { const event = new MirrorNodeDeployedEvent('solo-deployment'); expect(event.type).to.equal(SoloEventType.MirrorNodeDeployed); expect(event.deployment).to.equal('solo-deployment'); }); it('waitFor() should resolve if event was emitted before waitFor is called', async () => { // Emit the event first (before waitFor is called) bus.emit(networkEvent); // Now call waitFor; it should resolve immediately with the past event const promise = bus.waitFor(SoloEventType.NetworkDeployed); const result = await promise; expect(result).to.equal(networkEvent); }); describe('waitFor() timeout', () => { it('should reject with a SoloError when the timeout elapses before the event is emitted', async () => { const promise = bus.waitFor(SoloEventType.NetworkDeployed, undefined, Duration.ofMillis(10)); await expect(promise).to.be.rejectedWith(SoloError, /timed out/); }); it('should resolve before the timeout if the event arrives in time', async () => { const promise = bus.waitFor(SoloEventType.NetworkDeployed, undefined, Duration.ofMillis(100)); bus.emit(networkEvent); expect(await promise).to.equal(networkEvent); }); it('should resolve immediately from history before the timeout fires', async () => { bus.emit(networkEvent); const result = await bus.waitFor(SoloEventType.NetworkDeployed, undefined, Duration.ofMillis(10)); expect(result).to.equal(networkEvent); }); }); describe('clearHistory()', () => { it('should prevent waitFor() from resolving against a cleared event type', async () => { bus.emit(networkEvent); bus.clearHistory(SoloEventType.NetworkDeployed); let resolved = false; bus.waitFor(SoloEventType.NetworkDeployed).then(() => { resolved = true; }); await Promise.resolve(); expect(resolved).to.be.false; }); it('should not affect history for other event types when clearing a specific type', async () => { bus.emit(networkEvent); bus.emit(mirrorEvent); bus.clearHistory(SoloEventType.NetworkDeployed); // mirror history should still resolve waitFor immediately const result = await bus.waitFor(SoloEventType.MirrorNodeDeployed); expect(result).to.equal(mirrorEvent); }); it('should clear all event type histories when called with no argument', async () => { bus.emit(networkEvent); bus.emit(mirrorEvent); bus.clearHistory(); let networkResolved = false; let mirrorResolved = false; bus.waitFor(SoloEventType.NetworkDeployed).then(() => { networkResolved = true; }); bus.waitFor(SoloEventType.MirrorNodeDeployed).then(() => { mirrorResolved = true; }); await Promise.resolve(); expect(networkResolved).to.be.false; expect(mirrorResolved).to.be.false; }); it('should allow new events to be recorded after history is cleared', async () => { bus.emit(networkEvent); bus.clearHistory(SoloEventType.NetworkDeployed); const newEvent = new NetworkDeployedEvent('new-deployment'); bus.emit(newEvent); const result = await bus.waitFor(SoloEventType.NetworkDeployed); expect(result).to.equal(newEvent); }); it('should be a no-op when called on a type with no recorded history', () => { expect(() => bus.clearHistory(SoloEventType.NetworkDeployed)).not.to.throw(); }); it('should be a no-op when called with no argument on an empty bus', () => { expect(() => bus.clearHistory()).not.to.throw(); }); }); }); //# sourceMappingURL=solo-event-bus.test.js.map