Skip to content

Commit

Permalink
feat(repeater): address code coverage issues
Browse files Browse the repository at this point in the history
closes #196
  • Loading branch information
ostridm committed May 21, 2024
1 parent 589c90e commit 7caf3ee
Show file tree
Hide file tree
Showing 12 changed files with 1,062 additions and 179 deletions.
343 changes: 255 additions & 88 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,7 @@
"chalk": "^4.1.2",
"ci-info": "^3.3.0",
"content-type": "^1.0.4",
"find-up": "^5.0.0",
"form-data": "^4.0.0",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.4",
"reflect-metadata": "^0.1.13",
"semver": "^7.5.2",
"socket.io-client": "^4.7.5",
Expand Down Expand Up @@ -135,6 +132,7 @@
"nx": "14.5.6",
"prettier": "2.7.1",
"semantic-release": "~19.0.3",
"socket.io": "^4.7.5",
"ts-jest": "27.1.4",
"ts-mockito": "^2.6.1",
"typescript": "4.7.4"
Expand Down
229 changes: 229 additions & 0 deletions packages/repeater/src/bus/DefaultRepeaterBus.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import { DefaultRepeaterBus } from './DefaultRepeaterBus';
import { Protocol } from '../models/Protocol';
import { Request, Response } from '../request-runner';
import { RepeaterApplicationEvents } from './RepeaterApplicationEvents';
import {
RepeaterErrorCodes,
RepeaterServer,
RepeaterServerEvents,
RepeaterServerRequestEvent
} from './RepeaterServer';
import { RepeaterCommandHub } from './RepeaterCommandHub';
import { delay, Logger } from '@sectester/core';
import {
anything,
instance,
mock,
objectContaining,
reset,
verify,
when
} from 'ts-mockito';

describe('DefaultRepeaterBus', () => {
const RepeaterId = 'fooId';

let events!: RepeaterApplicationEvents;
let sut!: DefaultRepeaterBus;

const mockedRepeaterServer = mock<RepeaterServer>();
const mockedRepeaterCommandHub = mock<RepeaterCommandHub>();
const mockedLogger = mock<Logger>();

beforeEach(() => {
events = new RepeaterApplicationEvents();

when(mockedRepeaterServer.on(anything(), anything())).thenCall(
(event, handler) => events.on(event, handler)
);
when(mockedRepeaterServer.off(anything(), anything())).thenCall(
(event, handler) => events.off(event, handler)
);

sut = new DefaultRepeaterBus(
RepeaterId,
instance(mockedLogger),
instance(mockedRepeaterServer),
instance(mockedRepeaterCommandHub)
);
});

afterEach(() =>
reset<RepeaterServer | RepeaterCommandHub | Logger>(
mockedRepeaterServer,
mockedRepeaterCommandHub,
mockedLogger
)
);

describe('connect', () => {
it('should connect', async () => {
// act
await sut.connect();

// assert
verify(mockedRepeaterServer.connect(RepeaterId)).once();
verify(
mockedRepeaterServer.deploy(
objectContaining({ repeaterId: RepeaterId })
)
).once();
});

it('should throw when underlying connect throws', async () => {
// arrange
when(mockedRepeaterServer.connect(RepeaterId)).thenReject(
new Error('foo')
);

// act
const act = () => sut.connect();

// assert
await expect(act).rejects.toThrowError('foo');
});

it('should throw when underlying deploy throws', async () => {
// arrange
when(mockedRepeaterServer.deploy(anything())).thenReject(
new Error('foo')
);

// act
const act = () => sut.connect();

// assert
await expect(act).rejects.toThrowError('foo');
});
});

describe('close', () => {
it('should close', async () => {
// act
await sut.close();

// assert
verify(mockedRepeaterServer.disconnect()).once();
});
});

describe('events', () => {
it(`should subscribe to ${RepeaterServerEvents.UPDATE_AVAILABLE}`, async () => {
// arrange
await sut.connect();

// act
events.emit(RepeaterServerEvents.UPDATE_AVAILABLE, { version: '1.0.0' });

// assert
verify(
mockedLogger.warn(
'%s: A new Repeater version (%s) is available, for update instruction visit https://docs.brightsec.com/docs/installation-options',
anything(),
'1.0.0'
)
).once();
});

it(`should subscribe to ${RepeaterServerEvents.REQUEST}`, async () => {
// arrange
const requestEvent: RepeaterServerRequestEvent = {
protocol: Protocol.HTTP,
url: 'http://foo.bar',
method: 'GET'
};

const request = new Request(requestEvent);

when(mockedRepeaterCommandHub.sendRequest(anything())).thenResolve(
new Response({
protocol: Protocol.HTTP,
statusCode: 200
})
);

await sut.connect();

// act
events.emit(RepeaterServerEvents.REQUEST, requestEvent);

// assert
await delay(200);
verify(
mockedRepeaterCommandHub.sendRequest(objectContaining(request))
).once();
});

it(`should subscribe to ${RepeaterServerEvents.RECONNECT_ATTEMPT}`, async () => {
// arrange
await sut.connect();

// act
events.emit(RepeaterServerEvents.RECONNECT_ATTEMPT, {
attempt: 1,
maxAttempts: 3
});

// assert
verify(
mockedLogger.warn(
'Failed to connect to Bright cloud (attempt %d/%d)',
anything(),
anything()
)
).once();
});

it(`should subscribe to ${RepeaterServerEvents.ERROR} and proceed on error`, async () => {
// arrange
await sut.connect();

// act
events.emit(RepeaterServerEvents.ERROR, {
code: RepeaterErrorCodes.UNKNOWN_ERROR,
message: 'error'
});

// assert
verify(mockedLogger.error('error')).once();
});

it(`should subscribe to ${RepeaterServerEvents.ERROR} and proceed on critical error`, async () => {
// arrange
await sut.connect();

// act
events.emit(RepeaterServerEvents.ERROR, {
code: RepeaterErrorCodes.UNEXPECTED_ERROR,
message: 'unexpected error',
remediation: 'remediation'
});

// assert
verify(
mockedLogger.error(
'%s: %s. %s',
anything(),
'unexpected error',
'remediation'
)
).once();
verify(mockedRepeaterServer.disconnect()).once();
});

it(`should subscribe to ${RepeaterServerEvents.RECONNECTION_FAILED}`, async () => {
// arrange
const error = new Error('test error');
await sut.connect();

// act
events.emit(RepeaterServerEvents.RECONNECTION_FAILED, {
error
});

// assert
verify(mockedLogger.error(error)).once();
verify(mockedRepeaterServer.disconnect()).once();
});
});
});
17 changes: 12 additions & 5 deletions packages/repeater/src/bus/DefaultRepeaterBus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,22 @@ export class DefaultRepeaterBus implements RepeaterBus {

this.logger.log('Deploying the Repeater (%s)...', this.repeaterId);

await this.repeaterServer.deploy({
repeaterId: this.repeaterId
});
await this.deploy();

this.logger.log('The Repeater (%s) started', this.repeaterId);
}

private async deploy() {
await this.deployRepeater();
this.repeaterServer.on(RepeaterServerEvents.CONNECTED, this.deployRepeater);
}

private deployRepeater = async () => {
await this.repeaterServer.deploy({
repeaterId: this.repeaterId
});
};

private subscribeToEvents() {
this.repeaterServer.on(RepeaterServerEvents.ERROR, this.handleError);
this.repeaterServer.on(
Expand Down Expand Up @@ -118,15 +127,13 @@ export class DefaultRepeaterBus implements RepeaterBus {
remediation
);
this.close().catch(this.logger.error);
process.exitCode = 1;
}

private reconnectionFailed = ({
error
}: RepeaterServerReconnectionFailedEvent) => {
this.logger.error(error);
this.close().catch(this.logger.error);
process.exitCode = 1;
};

private requestReceived = async (event: RepeaterServerRequestEvent) => {
Expand Down
47 changes: 47 additions & 0 deletions packages/repeater/src/bus/DefaultRepeaterBusFactory.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { DefaultRepeaterBusFactory } from './DefaultRepeaterBusFactory';
import { RepeaterServer } from './RepeaterServer';
import { RepeaterCommandHub } from './RepeaterCommandHub';
import { DefaultRepeaterBus } from './DefaultRepeaterBus';
import { Configuration, Logger } from '@sectester/core';
import { instance, mock, reset } from 'ts-mockito';

describe('DefaultRepeaterBusFactory', () => {
const repeaterId = 'fooId';

const mockedLogger = mock<Logger>();
const mockedConfiguration = mock<Configuration>();
const mockedRepeaterServer = mock<RepeaterServer>();
const mockedRepeaterCommandHub = mock<RepeaterCommandHub>();

const configuration = instance(mockedConfiguration);

let sut!: DefaultRepeaterBusFactory;

beforeEach(() => {
sut = new DefaultRepeaterBusFactory(
instance(mockedLogger),
configuration,
instance(mockedRepeaterServer),
instance(mockedRepeaterCommandHub)
);
});

afterEach(() => {
reset<Logger | Configuration | RepeaterServer | RepeaterCommandHub>(
mockedLogger,
mockedConfiguration,
mockedRepeaterServer,
mockedRepeaterCommandHub
);
});

describe('create', () => {
it('should create', () => {
// act
const res = sut.create(repeaterId);

// assert
expect(res).toBeInstanceOf(DefaultRepeaterBus);
});
});
});
Loading

0 comments on commit 7caf3ee

Please sign in to comment.