Skip to content

Commit

Permalink
Merge pull request #942 from microsoft/johtaylo/inspection-unit-test
Browse files Browse the repository at this point in the history
Johtaylo/inspection unit test
  • Loading branch information
johnataylor committed May 24, 2019
2 parents 76ae6c5 + 3e23c59 commit c1ce919
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 20 deletions.
2 changes: 1 addition & 1 deletion libraries/botbuilder-ai/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"codelyzer": "^4.1.0",
"fs-extra": "^7.0.1",
"mocha": "^5.2.0",
"nock": "^10.0.1",
"nock": "^10.0.3",
"nyc": "^11.4.1",
"source-map-support": "^0.5.3",
"ts-node": "^4.1.0"
Expand Down
2 changes: 1 addition & 1 deletion libraries/botbuilder-azure/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"codelyzer": "^4.1.0",
"mocha": "^5.2.0",
"nyc": "^11.4.1",
"nock": "^10.0.1",
"nock": "^10.0.3",
"source-map-support": "^0.5.3",
"ts-node": "^4.1.0"
},
Expand Down
1 change: 1 addition & 0 deletions libraries/botbuilder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"chatdown": "^1.0.2",
"codelyzer": "^4.1.0",
"mocha": "^5.2.0",
"nock": "^10.0.3",
"nyc": "^11.4.1",
"source-map-support": "^0.5.3",
"ts-node": "^4.1.0",
Expand Down
36 changes: 20 additions & 16 deletions libraries/botbuilder/src/inspectionMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,13 @@ class TraceActivity {
};
}

public static fromState(botState: BotState, turnContext: TurnContext, name: string, label: string): Partial<Activity> {
var obj = botState.get(turnContext);
public static fromState(botState: BotState): Partial<Activity> {
return {
type: ActivityTypes.Trace,
timestamp: new Date(),
name: name,
label: label,
value: obj,
name: 'BotState',
label: 'Bot State',
value: botState,
valueType: 'https://www.botframework.com/schemas/botState'
};
}
Expand Down Expand Up @@ -166,7 +165,7 @@ export class InspectionMiddleware extends InterceptionMiddleware {
private static readonly command = "/INSPECT";

private readonly inspectionState: InspectionState;
private readonly inspectionStateAccessor: StatePropertyAccessor<InspectionSessionByStatus>;
private readonly inspectionStateAccessor: StatePropertyAccessor<InspectionSessionsByStatus>;
private readonly userState: UserState;
private readonly conversationState: ConversationState;
private readonly credentials: MicrosoftAppCredentials;
Expand Down Expand Up @@ -253,24 +252,29 @@ export class InspectionMiddleware extends InterceptionMiddleware {
await this.conversationState.load(turnContext, false);
}

var botState: any = {};

if (this.userState !== undefined) {
await this.invokeSend(turnContext, session, TraceActivity.fromState(this.userState, turnContext, 'UserState', 'User State'));
botState.userState = this.userState.get(turnContext);
}

if (this.conversationState !== undefined) {
await this.invokeSend(turnContext, session, TraceActivity.fromState(this.conversationState, turnContext, 'ConversationState', 'Conversation State'));
botState.conversationState = this.conversationState.get(turnContext);
}

await this.invokeSend(turnContext, session, TraceActivity.fromState(botState));
}
}

private async processOpenCommand(turnContext: TurnContext): Promise<any> {
var sessions = await this.inspectionStateAccessor.get(turnContext, InspectionSessionByStatus.DefaultValue);
var sessions = await this.inspectionStateAccessor.get(turnContext, InspectionSessionsByStatus.DefaultValue);
var sessionId = this.openCommand(sessions, TurnContext.getConversationReference(turnContext.activity));
await turnContext.sendActivity(TraceActivity.makeCommandActivity(`${InspectionMiddleware.command} attach ${sessionId}`));
await this.inspectionState.saveChanges(turnContext, false);
}

private async processAttachCommand(turnContext: TurnContext, sessionId: string): Promise<any> {
var sessions = await this.inspectionStateAccessor.get(turnContext, InspectionSessionByStatus.DefaultValue);
var sessions = await this.inspectionStateAccessor.get(turnContext, InspectionSessionsByStatus.DefaultValue);

if (this.attachComamnd(turnContext.activity.conversation.id, sessions, sessionId)) {
await turnContext.sendActivity('Attached to session, all traffic is being replicated for inspection.');
Expand All @@ -282,7 +286,7 @@ export class InspectionMiddleware extends InterceptionMiddleware {
await this.inspectionState.saveChanges(turnContext, false);
}

private openCommand(sessions: InspectionSessionByStatus, conversationReference: Partial<ConversationReference>): string {
private openCommand(sessions: InspectionSessionsByStatus, conversationReference: Partial<ConversationReference>): string {
function generate_guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
Expand All @@ -298,7 +302,7 @@ export class InspectionMiddleware extends InterceptionMiddleware {
return sessionId;
}

private attachComamnd(conversationId: string, sessions: InspectionSessionByStatus, sessionId: string): boolean {
private attachComamnd(conversationId: string, sessions: InspectionSessionsByStatus, sessionId: string): boolean {

var inspectionSessionState = sessions.openedSessions[sessionId];
if (inspectionSessionState !== undefined) {
Expand All @@ -311,7 +315,7 @@ export class InspectionMiddleware extends InterceptionMiddleware {
}

private async findSession(turnContext: TurnContext): Promise<any> {
var sessions = await this.inspectionStateAccessor.get(turnContext, InspectionSessionByStatus.DefaultValue);
var sessions = await this.inspectionStateAccessor.get(turnContext, InspectionSessionsByStatus.DefaultValue);

var conversationReference = sessions.attachedSessions[turnContext.activity.conversation.id];
if (conversationReference !== undefined) {
Expand All @@ -332,7 +336,7 @@ export class InspectionMiddleware extends InterceptionMiddleware {
}

private async cleanUpSession(turnContext: TurnContext): Promise<any> {
var sessions = await this.inspectionStateAccessor.get(turnContext, InspectionSessionByStatus.DefaultValue);
var sessions = await this.inspectionStateAccessor.get(turnContext, InspectionSessionsByStatus.DefaultValue);

delete sessions.attachedSessions[turnContext.activity.conversation.id];
await this.inspectionState.saveChanges(turnContext, false);
Expand Down Expand Up @@ -365,9 +369,9 @@ class InspectionSession {
}

/** @private */
class InspectionSessionByStatus {
class InspectionSessionsByStatus {

public static DefaultValue: InspectionSessionByStatus = new InspectionSessionByStatus();
public static DefaultValue: InspectionSessionsByStatus = new InspectionSessionsByStatus();

public openedSessions: { [id: string]: Partial<ConversationReference>; } = {};

Expand Down
120 changes: 120 additions & 0 deletions libraries/botbuilder/tests/inspectionMiddleware.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@

/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

const nock = require('nock');
const assert = require('assert');
const { TestAdapter, MemoryStorage, MessageFactory, UserState, ConversationState } = require('botbuilder-core');
const { InspectionMiddleware, InspectionState } = require('../');

beforeEach(function(done){
nock.cleanAll();

done();
});

afterEach(function(done){
nock.cleanAll();
done();
});

describe('InspectionMiddleware', function() {

it('should not change behavior when inspection middleware is added', async function() {

var inspectionState = new InspectionState(new MemoryStorage());
var inspectionMiddleware = new InspectionMiddleware(inspectionState);

const adapter = new TestAdapter(async (turnContext) => {

await turnContext.sendActivity(MessageFactory.text('hi'));
});

adapter.use(inspectionMiddleware);

await adapter.receiveActivity(MessageFactory.text('hello'));

assert(adapter.activityBuffer.length === 1, 'expected a single adapter response');
assert(adapter.activityBuffer[0].type === 'message', 'expected a message activity');
assert(adapter.activityBuffer[0].text === 'hi', `expected text saying 'hi'`);
});
it('should replicate activity data to listening emulator following open and attach', async function() {

// set up our expectations in nock - each corresponds to a trace message we expect to receive in the emulator

const inboundExpectation = nock('https://test.com')
.post('/v3/conversations/Convo1/activities', activity => activity.type === 'trace'
&& activity.value.text == 'hi')
.reply(200, { id: 'test' });

const outboundExpectation = nock('https://test.com')
.post('/v3/conversations/Convo1/activities', activity => activity.type === 'trace'
&& activity.value.text == 'echo: hi')
.reply(200, { id: 'test' });

const stateExpectation = nock('https://test.com')
.post('/v3/conversations/Convo1/activities', activity => activity.type === 'trace'
&& activity.value.userState && activity.value.userState.x.property == 'hello'
&& activity.value.conversationState && activity.value.conversationState.y.property == 'world')
.reply(200, { id: 'test' });

// create the various storage and middleware objects we will be using

var storage = new MemoryStorage();
var inspectionState = new InspectionState(storage);
var userState = new UserState(storage);
var conversationState = new ConversationState(storage);
var inspectionMiddleware = new InspectionMiddleware(inspectionState, userState, conversationState);

// the emulator sends an /INSPECT open command - we can use another adapter here

var openActivity = MessageFactory.text('/INSPECT open');

const inspectionAdapter = new TestAdapter(async (turnContext) => {
await inspectionMiddleware.processCommand(turnContext);
}, null, true);

await inspectionAdapter.receiveActivity(openActivity);

var inspectionOpenResultActivity = inspectionAdapter.activityBuffer[0];
var attachCommand = inspectionOpenResultActivity.value;

// the logic of teh bot including replying with a message and updating user and conversation state

var x = userState.createProperty('x');
var y = conversationState.createProperty('y');

var applicationAdapter = new TestAdapter(async (turnContext) => {

await turnContext.sendActivity(MessageFactory.text(`echo: ${ turnContext.activity.text }`));

(await x.get(turnContext, { property: '' })).property = 'hello';
(await y.get(turnContext, { property: '' })).property = 'world';

await userState.saveChanges(turnContext);
await conversationState.saveChanges(turnContext);

}, null, true);

// IMPORTANT add the InspectionMiddleware to the adapter that is running our bot

applicationAdapter.use(inspectionMiddleware);

await applicationAdapter.receiveActivity(MessageFactory.text(attachCommand));

// the attach command response is a informational message

await applicationAdapter.receiveActivity(MessageFactory.text('hi'));

// trace activities should be sent to the emulator using the connector and the conversation reference

// verify that all our expectations have been met

assert(inboundExpectation.isDone(), 'The expectation of a trace message for the inbound activity was not met');
assert(outboundExpectation.isDone(), 'The expectation of a trace message for the outbound activity was not met');
assert(stateExpectation.isDone(), 'The expectation of a trace message for the bot state was not met');
});
});

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"test": "lerna run build && nyc mocha \"libraries/bot*/tests/*.test.js\"",
"clean": "lerna run clean",
"test:coveralls": "lerna run build && nyc mocha \"libraries/bot*/tests/*.test.js\" && nyc report --reporter=text-lcov | coveralls",
"test-coverage": "nyc mocha \"libraries/bot*/tests/*.test.js\" ",
"test-coverage": "nyc mocha \"libraries/bot*/tests/*.test.js\" ",
"upload-coverage": "nyc report --reporter=text-lcov | coveralls",
"build-docs": "lerna run build-docs",
"eslint": "eslint ./libraries/*/src/*.ts ./libraries/*/src/**/*.ts",
Expand Down
2 changes: 1 addition & 1 deletion tools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"jshint": "^2.9.6",
"minami": "github:devigned/minami#master",
"mocha": "^5.2.0",
"nock": "0.16",
"nock": "^10.0.3",
"node-forge": "0.6.30",
"nyc": "^11.9.0",
"promise": "^7.1.1",
Expand Down

0 comments on commit c1ce919

Please sign in to comment.