Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increase code coverage in botbuilder library - InspectionMiddleware class #2541

271 changes: 258 additions & 13 deletions libraries/botbuilder/tests/inspectionMiddleware.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@ const nock = require('nock');
const assert = require('assert');
const { TestAdapter, MemoryStorage, MessageFactory, UserState, ConversationState } = require('botbuilder-core');
const { InspectionMiddleware, InspectionState } = require('../');
const sinon = require('sinon');

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

done();
});

afterEach(function(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());
Expand All @@ -39,7 +39,7 @@ describe('InspectionMiddleware', function() {
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
Expand All @@ -55,7 +55,7 @@ describe('InspectionMiddleware', function() {
.reply(200, { id: 'test' });

const stateExpectation = nock('https://test.com')
.post('/v3/conversations/Convo1/activities', activity => activity.type === 'trace'
.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' });
Expand Down Expand Up @@ -92,7 +92,7 @@ describe('InspectionMiddleware', function() {

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

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

Expand All @@ -115,7 +115,7 @@ describe('InspectionMiddleware', function() {
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');
});
});
it('should replicate activity data to listening emulator following open and attach with at mention', async function() {

// set up our expectations in nock - each corresponds to a trace message we expect to receive in the emulator
Expand All @@ -131,7 +131,7 @@ describe('InspectionMiddleware', function() {
.reply(200, { id: 'test' });

const stateExpectation = nock('https://test.com')
.post('/v3/conversations/Convo1/activities', activity => activity.type === 'trace'
.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' });
Expand Down Expand Up @@ -170,7 +170,7 @@ describe('InspectionMiddleware', function() {

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

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

Expand Down Expand Up @@ -225,7 +225,7 @@ describe('InspectionMiddleware', function() {
.reply(200, { id: 'test' });

const stateExpectation = nock('https://test.com')
.post('/v3/conversations/Convo1/activities', activity => activity.type === 'trace'
.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' });
Expand Down Expand Up @@ -262,7 +262,7 @@ describe('InspectionMiddleware', function() {

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

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

Expand Down Expand Up @@ -291,6 +291,251 @@ describe('InspectionMiddleware', function() {
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');
});
});
});

it('should update activity message to trigger turnContext.onUpdateActivity', async () => {
// set up our expectations in nock - each corresponds to a trace message we expect to receive in the emulator

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

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

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

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

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

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

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

await inspectionAdapter.receiveActivity(openActivity);

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

// Updating the activity message to trigger turnContext.onUpdateActivity

const activity = MessageFactory.text('hi');

activity.id = '0';

const adapter = new TestAdapter(async (turnContext) => {
activity.text = 'new text';
await turnContext.updateActivity(activity);
await userState.saveChanges(turnContext);
await conversationState.saveChanges(turnContext);
}, null, true);

adapter.use(inspectionMiddleware);

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

await adapter.receiveActivity(activity);

assert(adapter.updatedActivities.length === 1, `no activities updated.`);
assert(adapter.updatedActivities[0].text === activity.text, `invalid update activity text.`);
});

it('should delete activity to trigger turnContext.onDeleteActivity', async () => {
// set up our expectations in nock - each corresponds to a trace message we expect to receive in the emulator

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

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

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

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

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

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

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

await inspectionAdapter.receiveActivity(openActivity);

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

// Updating the activity message to trigger turnContext.onDeleteActivity

const activity = MessageFactory.text('hi');

activity.id = '0';

const adapter = new TestAdapter(async (turnContext) => {
await turnContext.deleteActivity(activity.id);
await userState.saveChanges(turnContext);
await conversationState.saveChanges(turnContext);
}, null, true);

adapter.use(inspectionMiddleware);

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

await adapter.receiveActivity(activity);

assert(adapter.deletedActivities.length === 1, `no activities deleted.`);
assert(adapter.deletedActivities[0].activityId === activity.id, `invalid delete activity.`);
});

it('should throw an error when onTurn next parameter is null', async () => {
// set up our expectations in nock - each corresponds to a trace message we expect to receive in the emulator

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

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

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

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

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

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

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

await inspectionAdapter.receiveActivity(openActivity);

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

const adapter = new TestAdapter(async (turnContext) => {
try {
await inspectionMiddleware.onTurn(turnContext, null);
throw new Error('should have thrown an error.');
} catch (error) {
assert.strictEqual(error.message, 'next is not a function', 'next function should be null');
}
}, null, true);

adapter.use(inspectionMiddleware);

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

await adapter.receiveActivity();
});

it('should invokeInbound throw an error', async () => {
const storage = new MemoryStorage();
const inspectionState = new InspectionState(storage);
const userState = new UserState(storage);
const conversationState = new ConversationState(storage);
const inspectionMiddleware = new InspectionMiddleware(inspectionState, userState, conversationState);

// Override warn current behavior to intercept when it's called.
const warn = sinon.stub(console, 'warn');

await inspectionMiddleware.invokeInbound();

assert(warn.called, 'invokeInbound should have throw a warning');

// Revert warn to original behavior.
warn.restore();
});

it('should invokeOutbound throw an error', async () => {
const storage = new MemoryStorage();
const inspectionState = new InspectionState(storage);
const userState = new UserState(storage);
const conversationState = new ConversationState(storage);
const inspectionMiddleware = new InspectionMiddleware(inspectionState, userState, conversationState);

// Override warn current behavior to intercept when it's called.
const warn = sinon.stub(console, 'warn');

await inspectionMiddleware.invokeOutbound();

assert(warn.called, 'invokeOutbound should have throw a warning');

// Revert warn to original behavior.
warn.restore();
});

it('should invokeTraceState throw an error', async () => {
const storage = new MemoryStorage();
const inspectionState = new InspectionState(storage);
const userState = new UserState(storage);
const conversationState = new ConversationState(storage);
const inspectionMiddleware = new InspectionMiddleware(inspectionState, userState, conversationState);

// Override warn current behavior to intercept when it's called.
const warn = sinon.stub(console, 'warn');

await inspectionMiddleware.invokeTraceState();

assert(warn.called, 'invokeTraceState should have throw a warning');

// Revert warn to original behavior.
warn.restore();
});

it('should attachCommand return false', async () => {
const storage = new MemoryStorage();
const inspectionState = new InspectionState(storage);
const userState = new UserState(storage);
const conversationState = new ConversationState(storage);
const inspectionMiddleware = new InspectionMiddleware(inspectionState, userState, conversationState);

const result = await inspectionMiddleware.attachCommand('', { openedSessions: { 'session-1': undefined } }, 'session-1');
assert.strictEqual(result, false, 'should be returning false');
});
});