Skip to content

Commit

Permalink
added tests for services
Browse files Browse the repository at this point in the history
  • Loading branch information
denysoblohin-okta committed May 4, 2022
1 parent 704aacf commit 4f88408
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 45 deletions.
31 changes: 22 additions & 9 deletions test/spec/services/LeaderElectionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@


import { LeaderElectionService } from '../../../lib/services/LeaderElectionService';
import { BroadcastChannel } from 'broadcast-channel';

jest.mock('broadcast-channel', () => {
const actual = jest.requireActual('broadcast-channel');
Expand All @@ -30,36 +29,30 @@ const mocked = {
};

describe('LeaderElectionService', () => {
let channel;
let service;
beforeEach(function() {
jest.useFakeTimers();
channel = null;
service = null;
});
afterEach(() => {
jest.useRealTimers();
if (service) {
service.stop();
}
if (channel) {
channel.close();
}
});

function createService(options?) {
service = new LeaderElectionService({
...options,
electionChannelName: 'electionChannel'
});
channel = new BroadcastChannel('electionChannel');
return service;
}

function createElectorWithLeadership() {
return {
isLeader: true,
awaitLeadership: () => new Promise(() => {}),
awaitLeadership: jest.fn().mockReturnValue(new Promise(() => {})),
die: jest.fn(),
};
}
Expand All @@ -81,6 +74,16 @@ describe('LeaderElectionService', () => {


describe('start', () => {
it('creates elector and awaits leadership', () => {
const elector = createElectorWithLeadership();
jest.spyOn(mocked.broadcastChannel, 'createLeaderElection').mockReturnValue(elector);
const service = createService();
service.start();
expect(service.isStarted()).toBeTruthy();
expect((service as any).elector).toStrictEqual(elector);
expect(elector.awaitLeadership).toHaveBeenCalledTimes(1);
});

it('stops service if already started', async () => {
const elector = createElectorWithLeadership();
jest.spyOn(mocked.broadcastChannel, 'createLeaderElection').mockReturnValue(elector);
Expand All @@ -93,6 +96,16 @@ describe('LeaderElectionService', () => {
});

describe('stop', () => {
it('should kill elector', () => {
const elector = createElectorWithLeadership();
jest.spyOn(mocked.broadcastChannel, 'createLeaderElection').mockReturnValue(elector);
const service = createService();
service.start();
service.stop();
expect(service.isStarted()).toBeFalsy();
expect(elector.die).toHaveBeenCalledTimes(1);
});

it('can be called twice without error', async () => {
const elector = createElectorWithLeadership();
jest.spyOn(mocked.broadcastChannel, 'createLeaderElection').mockReturnValue(elector);
Expand All @@ -109,7 +122,7 @@ describe('LeaderElectionService', () => {
});

describe('isLeader', () => {
it('returns true if elected as leader', () => {
it('returns true if current tab is elected as leader', () => {
const elector = createElectorWithLeadership();
jest.spyOn(mocked.broadcastChannel, 'createLeaderElection').mockReturnValue(elector);
const service = createService();
Expand Down
171 changes: 135 additions & 36 deletions test/spec/services/SyncStorageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,24 @@ const Emitter = require('tiny-emitter');

describe('SyncStorageService', () => {
let sdkMock;
let instance;
let tokenManager;
let channel;
let service;
let storage;
let tokenStorage;
beforeEach(function() {
instance = null;
tokenManager = null;
channel = null;
service = null;
const emitter = new Emitter();
storage = {
idToken: tokens.standardIdTokenParsed
};
tokenStorage = {
getStorage: jest.fn().mockImplementation(() => storage),
setStorage: jest.fn().mockImplementation(() => {})
getStorage: jest.fn().mockImplementation(() => storage),
setStorage: jest.fn().mockImplementation((newStorage) => {
storage = newStorage;
})
};
sdkMock = {
options: {},
Expand All @@ -51,8 +53,8 @@ describe('SyncStorageService', () => {
jest.spyOn(features, 'isLocalhost').mockReturnValue(true);
});
afterEach(() => {
if (instance) {
instance.stop();
if (tokenManager) {
tokenManager.stop();
}
if (service) {
service.stop();
Expand All @@ -63,49 +65,146 @@ describe('SyncStorageService', () => {
});

function createInstance(options?) {
instance = new TokenManager(sdkMock, options);
instance.start();
service = new SyncStorageService(instance, {
...instance.getOptions(),
tokenManager = new TokenManager(sdkMock, options);
tokenManager.start();
service = new SyncStorageService(tokenManager, {
...tokenManager.getOptions(),
syncChannelName: 'syncChannel'
});
service.start();
// Create another channel with same name for communication
channel = new BroadcastChannel('syncChannel');
return instance;
return tokenManager;
}

it('should emit "added" event if new token is added', async () => {
createInstance();
jest.spyOn(sdkMock.emitter, 'emit');
await channel.postMessage({
type: 'added',
key: 'idToken',
token: tokens.standardIdTokenParsed
describe('start', () => {
it('stops service if already started, closes and recreates channel', async () => {
createInstance();
const oldChannel = (service as any).channel;
jest.spyOn(oldChannel, 'close');
service.start(); // restart
const newChannel = (service as any).channel;
expect(service.isStarted()).toBeTruthy();
expect(oldChannel.close).toHaveBeenCalledTimes(1);
expect(newChannel).not.toStrictEqual(oldChannel);
});
expect(sdkMock.emitter.emit).toHaveBeenCalledWith('added', 'idToken', tokens.standardIdTokenParsed);
});

it('should emit "renewed" event if token is changed', async () => {
createInstance();
jest.spyOn(sdkMock.emitter, 'emit');
await channel.postMessage({
type: 'renewed',
key: 'idToken',
token: tokens.standardIdToken2Parsed,
oldToken: tokens.standardIdTokenParsed
describe('stop', () => {
it('can be called twice without error', async () => {
createInstance();
const serviceChannel = (service as any).channel;
jest.spyOn(serviceChannel, 'close');
await Promise.race([
service.stop(),
service.stop()
]);
expect(service.isStarted()).toBeFalsy();
expect(serviceChannel.close).toHaveBeenCalledTimes(1);
expect((service as any).channel).not.toBeDefined();
});
expect(sdkMock.emitter.emit).toHaveBeenCalledWith('renewed', 'idToken', tokens.standardIdToken2Parsed, tokens.standardIdTokenParsed);
});

it('should emit "removed" event if token is removed', async () => {
createInstance();
jest.spyOn(sdkMock.emitter, 'emit');
await channel.postMessage({
type: 'removed',
key: 'idToken',
token: tokens.standardIdTokenParsed
describe('handling sync message', () => {
it('should emit "added" event if new token is added from another tab', async () => {
createInstance();
jest.spyOn(sdkMock.emitter, 'emit');
await channel.postMessage({
type: 'added',
key: 'idToken',
token: tokens.standardIdToken2Parsed
});
expect(sdkMock.emitter.emit).toHaveBeenCalledWith('added', 'idToken', tokens.standardIdToken2Parsed);
});

it('should emit "removed" event if new token is removed from another tab', async () => {
createInstance();
jest.spyOn(sdkMock.emitter, 'emit');
await channel.postMessage({
type: 'removed',
key: 'idToken',
token: tokens.standardIdTokenParsed
});
expect(sdkMock.emitter.emit).toHaveBeenCalledWith('removed', 'idToken', tokens.standardIdTokenParsed);
});

it('should emit "renewed" event if new token is chnaged from another tab', async () => {
createInstance();
jest.spyOn(sdkMock.emitter, 'emit');
await channel.postMessage({
type: 'renewed',
key: 'idToken',
token: tokens.standardIdToken2Parsed,
oldToken: tokens.standardIdTokenParsed
});
expect(sdkMock.emitter.emit).toHaveBeenCalledWith('renewed', 'idToken', tokens.standardIdToken2Parsed, tokens.standardIdTokenParsed);
});

it('should not post sync message to other tabs', async () => {
createInstance();
const serviceChannel = (service as any).channel;
jest.spyOn(serviceChannel, 'postMessage');
await channel.postMessage({
type: 'removed',
key: 'idToken',
token: tokens.standardIdTokenParsed
});
expect(serviceChannel.postMessage).toHaveBeenCalledTimes(0);
});
});

describe('posting sync messages', () => {
it('should post "added" sync message', () => {
createInstance();
const serviceChannel = (service as any).channel;
jest.spyOn(serviceChannel, 'postMessage');
tokenManager.add('idToken', tokens.standardIdToken2Parsed);
expect(serviceChannel.postMessage).toHaveBeenCalledWith({
type: 'added',
key: 'idToken',
token: tokens.standardIdToken2Parsed
});
});

it('should not post set_storage event on storage change (for non-IE)', () => {
createInstance();
const serviceChannel = (service as any).channel;
jest.spyOn(serviceChannel, 'postMessage');
tokenManager.add('idToken', tokens.standardIdTokenParsed);
expect(serviceChannel.postMessage).toHaveBeenCalledTimes(1); // only "added"
});
});

describe('IE11', () => {
beforeEach(function() {
jest.spyOn(features, 'isIE11OrLess').mockReturnValue(true);
});

it('should post "set_storage" event on any storage change', () => {
createInstance();
const serviceChannel = (service as any).channel;
jest.spyOn(serviceChannel, 'postMessage');
tokenManager.add('idToken', tokens.standardIdToken2Parsed);
expect(serviceChannel.postMessage).toHaveBeenCalledTimes(2); // ""set_storage" + "added"
expect(serviceChannel.postMessage).toHaveBeenNthCalledWith(1, {
type: 'set_storage',
storage: {
idToken: tokens.standardIdToken2Parsed
},
});
});

it('should update storage excplicitly on "set_storage" event', async () => {
createInstance();
const newStorage = {
idToken: tokens.standardIdToken2Parsed
};
await channel.postMessage({
type: 'set_storage',
storage: newStorage,
});
expect(storage).toEqual(newStorage);
});
expect(sdkMock.emitter.emit).toHaveBeenCalledWith('removed', 'idToken', tokens.standardIdTokenParsed);
});

});

0 comments on commit 4f88408

Please sign in to comment.