Skip to content

Commit

Permalink
Updated ngrokTUnnelAction and ngrokTunnel to get parity
Browse files Browse the repository at this point in the history
Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

Test cases updated for ngrokTunnelAction and ngrokTunnel reducer

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>
  • Loading branch information
Srinaath Ravichandran committed Jan 30, 2020
1 parent 182ec55 commit 78c9403
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 21 deletions.
50 changes: 48 additions & 2 deletions packages/app/shared/src/state/actions/ngrokTunnelActions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ import {
TunnelInfo,
TunnelError,
TunnelStatus,
checkOnTunnel,
setTimeIntervalSinceLastPing,
TunnelCheckTimeInterval,
clearAllNotifications,
addNotification,
} from './ngrokTunnelActions';

describe('Ngrok Tunnel Actions', () => {
Expand Down Expand Up @@ -68,9 +73,50 @@ describe('Ngrok Tunnel Actions', () => {
const mockDate = new Date(1466424490000);
jest.spyOn(global, 'Date').mockImplementation(() => mockDate as any);
const expectedStatus: TunnelStatus = TunnelStatus.Active;
const action = updateTunnelStatus(expectedStatus);
const action = updateTunnelStatus({
tunnelStatus: expectedStatus,
});
expect(action.type).toBe(NgrokTunnelActions.setStatus);
expect(action.payload.timestamp).toBe(new Date().toLocaleString());
expect(action.payload.timestamp).toBe(new Date().getTime());
expect(action.payload.status).toBe(expectedStatus);
});

it('should create a tunnel status update action on TunnelError', () => {
const mockDate = new Date(1466424490000);
jest.spyOn(global, 'Date').mockImplementation(() => mockDate as any);
const expectedStatus: TunnelStatus = TunnelStatus.Error;
const action = updateTunnelStatus({
tunnelStatus: expectedStatus,
});
expect(action.type).toBe(NgrokTunnelActions.setStatus);
expect(action.payload.timestamp).toBe(new Date().getTime());
expect(action.payload.status).toBe(expectedStatus);
});

it('should create a checkOnTunnel action', () => {
const action = checkOnTunnel({
onTunnelPingError: jest.fn(),
onTunnelPingSuccess: jest.fn(),
});
expect(action.type).toBe(NgrokTunnelActions.checkOnTunnel);
});

it('should create a setTimeIntervalSinceLastPing action', () => {
const action = setTimeIntervalSinceLastPing(TunnelCheckTimeInterval.SecondInterval);
expect(action.type).toBe(NgrokTunnelActions.setTimeIntervalSinceLastPing);
expect(action.payload).toBe(TunnelCheckTimeInterval.SecondInterval);
});

it('should create a clear notifications action', () => {
const action = clearAllNotifications();
expect(action.type).toBe(NgrokTunnelActions.clearAllNotifications);
expect(action.payload).toBeNull;
});

it('should create add notification action', () => {
const notificationId = 'notification-1';
const action = addNotification(notificationId);
expect(action.type).toBe(NgrokTunnelActions.addNotification);
expect(action.payload).toBe(notificationId);
});
});
68 changes: 62 additions & 6 deletions packages/app/shared/src/state/actions/ngrokTunnelActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,20 @@ import { Action } from 'redux';
export enum NgrokTunnelActions {
setDetails = 'NgrokTunnel/SET_DETAILS',
updateOnError = 'NgrokTunnel/TUNNEL_ERROR',
setStatus = 'NgrokTunnel/STATUS_CHECK',
setStatus = 'NgrokTunnel/SET_STATUS',
checkOnTunnel = 'NgrokTunnel/CHECK_ON_TUNNEL',
setTimeIntervalSinceLastPing = 'NgrokTunnel/TIME_INTERVAL_SINCE_LAST_PING',
clearAllNotifications = 'NgrokTunnel/CLEAR_NOTIFICATIONS',
addNotification = 'NgrokTunnel/ADD_NOTIFICATION',
}

export enum TunnelCheckTimeInterval {
// 0 - 20 seconds
Now,
// 20 - 40 seconds
FirstInterval,
// 40 - 60 seconds
SecondInterval,
}

export enum TunnelStatus {
Expand All @@ -58,15 +71,26 @@ export interface TunnelError {

export interface TunnelStatusAndTimestamp {
status: TunnelStatus;
timestamp: string;
timestamp: number;
}

export interface StatusCheckOnTunnel {
onTunnelPingSuccess: Function;
onTunnelPingError: Function;
}

export interface NgrokTunnelAction<T> extends Action {
type: NgrokTunnelActions;
payload: T;
}

export type NgrokTunnelPayloadTypes = TunnelError | TunnelInfo | TunnelStatusAndTimestamp;
export type NgrokTunnelPayloadTypes =
| TunnelError
| TunnelInfo
| TunnelStatusAndTimestamp
| StatusCheckOnTunnel
| TunnelCheckTimeInterval
| string;

export function updateNewTunnelInfo(payload: TunnelInfo): NgrokTunnelAction<TunnelInfo> {
return {
Expand All @@ -75,12 +99,14 @@ export function updateNewTunnelInfo(payload: TunnelInfo): NgrokTunnelAction<Tunn
};
}

export function updateTunnelStatus(tunnelStatus: TunnelStatus): NgrokTunnelAction<TunnelStatusAndTimestamp> {
export function updateTunnelStatus(payload: {
tunnelStatus: TunnelStatus;
}): NgrokTunnelAction<TunnelStatusAndTimestamp> {
return {
type: NgrokTunnelActions.setStatus,
payload: {
status: tunnelStatus,
timestamp: new Date().toLocaleString(),
status: payload.tunnelStatus,
timestamp: new Date().getTime(),
},
};
}
Expand All @@ -91,3 +117,33 @@ export function updateTunnelError(payload: TunnelError): NgrokTunnelAction<Tunne
payload,
};
}

export function checkOnTunnel(payload: StatusCheckOnTunnel): NgrokTunnelAction<StatusCheckOnTunnel> {
return {
type: NgrokTunnelActions.checkOnTunnel,
payload,
};
}

export function setTimeIntervalSinceLastPing(
payload: TunnelCheckTimeInterval
): NgrokTunnelAction<TunnelCheckTimeInterval> {
return {
type: NgrokTunnelActions.setTimeIntervalSinceLastPing,
payload,
};
}

export function clearAllNotifications(): NgrokTunnelAction<void> {
return {
type: NgrokTunnelActions.clearAllNotifications,
payload: null,
};
}

export function addNotification(payload: string): NgrokTunnelAction<string> {
return {
type: NgrokTunnelActions.addNotification,
payload,
};
}
70 changes: 61 additions & 9 deletions packages/app/shared/src/state/reducers/ngrokTunnel.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ import {
NgrokTunnelPayloadTypes,
NgrokTunnelActions,
TunnelError,
TunnelStatusAndTs,
TunnelStatusAndTimestamp,
TunnelCheckTimeInterval,
clearAllNotifications,
} from '../actions/ngrokTunnelActions';

import { ngrokTunnel, NgrokTunnelState } from './ngrokTunnel';
Expand All @@ -51,7 +53,9 @@ describe('Ngrok Tunnel reducer', () => {
postmanCollectionPath: '',
errors: {} as TunnelError,
tunnelStatus: TunnelStatus.Inactive,
lastTunnelStatusCheckTS: '',
lastPingedTimestamp: Date.now(),
timeIntervalSinceLastPing: TunnelCheckTimeInterval.Now,
ngrokNotificationIds: [],
};

afterEach(() => {
Expand Down Expand Up @@ -99,14 +103,50 @@ describe('Ngrok Tunnel reducer', () => {
expect(endingState.errors.errorMessage).toBe(payload.errorMessage);
});

it('Last Ping time interval should be set from payload', () => {
const action = {
type: NgrokTunnelActions.setTimeIntervalSinceLastPing,
payload: TunnelCheckTimeInterval.SecondInterval,
};
const startingState = { ...DEFAULT_STATE };
const transientState = ngrokTunnel(startingState, action);
expect(transientState.timeIntervalSinceLastPing).toBe(action.payload);
});

it('should add notifications with add notification and clear should remove all ngrok notifications', () => {
const getNotificationAction = payload => ({
type: NgrokTunnelActions.addNotification,
payload,
});
const startingState = { ...DEFAULT_STATE };
const notifications: string[] = ['notification-1', 'notification-2', 'notification-3', 'notification-4'];
let transientState = ngrokTunnel(startingState, getNotificationAction(notifications[0]));
transientState = ngrokTunnel(transientState, getNotificationAction(notifications[1]));
transientState = ngrokTunnel(transientState, getNotificationAction(notifications[2]));
transientState = ngrokTunnel(transientState, getNotificationAction(notifications[3]));
expect(transientState.ngrokNotificationIds).toEqual(notifications);
transientState = ngrokTunnel(transientState, {
type: NgrokTunnelActions.clearAllNotifications,
payload: null,
});
expect(transientState.ngrokNotificationIds.length).toBe(0);
transientState = ngrokTunnel(transientState, getNotificationAction(notifications[3]));
expect(transientState.ngrokNotificationIds.length).toBe(1);
const finalState = ngrokTunnel(transientState, {
type: NgrokTunnelActions.clearAllNotifications,
payload: null,
});
expect(finalState.ngrokNotificationIds.length).toBe(0);
});

it('Tunnel status should be set from payload', () => {
const payload: TunnelStatusAndTs = {
const payload: TunnelStatusAndTimestamp = {
status: TunnelStatus.Active,
ts: '12/27/2019, 1:30:00 PM',
timestamp: Date.now(),
};
const nextPayload: TunnelStatusAndTs = {
const nextPayload: TunnelStatusAndTimestamp = {
status: TunnelStatus.Error,
ts: '12/27/2019, 1:33:00 PM',
timestamp: Date.now(),
};
const actions: NgrokTunnelAction<NgrokTunnelPayloadTypes>[] = [
{
Expand All @@ -119,10 +159,22 @@ describe('Ngrok Tunnel reducer', () => {
},
];
const startingState = { ...DEFAULT_STATE };
const transientState = ngrokTunnel(startingState, actions[0]);
let transientState = ngrokTunnel(startingState, actions[0]);
expect(transientState.tunnelStatus).toBe(payload.status);

const finalState = ngrokTunnel(startingState, actions[1]);
expect(finalState.tunnelStatus).toBe(nextPayload.status);
transientState = ngrokTunnel(transientState, actions[1]);
expect(transientState.tunnelStatus).toBe(nextPayload.status);

transientState = ngrokTunnel(transientState, {
type: NgrokTunnelActions.updateOnError,
payload: {
statusCode: 422,
errorMessage: 'Tunnel has too many connections',
},
});
expect(transientState.errors.statusCode).toBe(422);
transientState = ngrokTunnel(transientState, actions[0]);
expect(transientState.tunnelStatus).toEqual(TunnelStatus.Active);
expect(transientState.errors).toEqual({});
});
});
40 changes: 36 additions & 4 deletions packages/app/shared/src/state/reducers/ngrokTunnel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import {
TunnelError,
TunnelStatus,
TunnelStatusAndTimestamp,
TunnelInfo,
TunnelCheckTimeInterval,
} from '../actions/ngrokTunnelActions';

export interface NgrokTunnelState {
Expand All @@ -47,7 +49,9 @@ export interface NgrokTunnelState {
logPath: string;
postmanCollectionPath: string;
tunnelStatus: TunnelStatus;
lastPingedTimestamp: string;
lastPingedTimestamp: number;
timeIntervalSinceLastPing: TunnelCheckTimeInterval;
ngrokNotificationIds: string[];
}

const DEFAULT_STATE: NgrokTunnelState = {
Expand All @@ -57,20 +61,24 @@ const DEFAULT_STATE: NgrokTunnelState = {
postmanCollectionPath: '',
errors: {} as TunnelError,
tunnelStatus: TunnelStatus.Inactive,
lastPingedTimestamp: '',
lastPingedTimestamp: Date.now(),
timeIntervalSinceLastPing: TunnelCheckTimeInterval.Now,
ngrokNotificationIds: [],
};

export const ngrokTunnel = (
state: NgrokTunnelState = DEFAULT_STATE,
action: NgrokTunnelAction<NgrokTunnelPayloadTypes>
): NgrokTunnelState => {
switch (action.type) {
case NgrokTunnelActions.setDetails:
case NgrokTunnelActions.setDetails: {
const payload: TunnelInfo = action.payload as TunnelInfo;
state = {
...state,
...action.payload,
...payload,
};
break;
}

case NgrokTunnelActions.updateOnError:
state = {
Expand All @@ -85,6 +93,30 @@ export const ngrokTunnel = (
tunnelStatus: (action.payload as TunnelStatusAndTimestamp).status,
lastPingedTimestamp: (action.payload as TunnelStatusAndTimestamp).timestamp,
};
if (state.tunnelStatus !== TunnelStatus.Error && state.errors) {
state.errors = {} as TunnelError;
}
break;

case NgrokTunnelActions.setTimeIntervalSinceLastPing:
state = {
...state,
timeIntervalSinceLastPing: action.payload as TunnelCheckTimeInterval,
};
break;

case NgrokTunnelActions.clearAllNotifications:
state = {
...state,
ngrokNotificationIds: [],
};
break;

case NgrokTunnelActions.addNotification:
state = {
...state,
ngrokNotificationIds: [...state.ngrokNotificationIds, action.payload as string],
};
break;
}
return state;
Expand Down

0 comments on commit 78c9403

Please sign in to comment.