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

[Feature] Add functional test to check bot deployed answer #919

Merged
merged 24 commits into from
Jun 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e9a7d43
Add ARM bot template
Bill7zz May 1, 2019
7aa6b12
Add directLine test
Bill7zz May 7, 2019
f80e0af
Update test logic to handle error 502
Bill7zz May 9, 2019
83ffa10
Merge branch 'master' into feature/functional-test-bot
gasper-az May 9, 2019
00fdefa
Merge branch 'master' into feature/functional-test-bot
gasper-az May 14, 2019
cf89be7
Merge branch 'master' into feature/functional-test-bot
Bill7zz May 14, 2019
abb0948
Increase directline test timeout to 1 minute
ParadoxARG May 15, 2019
b5c9722
Merge master changes
Bill7zz May 16, 2019
1117d98
Merge branch 'feature/functional-test-bot' of https://github.com/Micr…
Bill7zz May 16, 2019
d92c2fd
- Update the test bot to deploy it in azure
Bill7zz May 20, 2019
5e644fd
Update test logic
Bill7zz May 21, 2019
5199dfe
Merge branch 'master' into feature/functional-test-bot
Bill7zz May 21, 2019
c165471
Merge branch 'master' into feature/functional-test-bot
gasper-az May 27, 2019
bf14ef7
Create deployment-script folder
Bill7zz May 29, 2019
5c6eeb1
Merge branch 'feature/functional-test-bot' of https://github.com/Micr…
Bill7zz May 29, 2019
6fdc081
Add windows folder for templates
Bill7zz May 29, 2019
c58c3f5
Merge branch 'testbot-refactor-dotnet-parity' into feature/functional…
Bill7zz May 30, 2019
f815101
Update bot endpoint
Bill7zz May 30, 2019
25bbd58
Merge branch 'master' into feature/functional-test-bot
cleemullins Jun 5, 2019
6edc740
Add Copyright header
Bill7zz Jun 6, 2019
1425439
Update functional-test command
Bill7zz Jun 6, 2019
0160297
Merge branch 'master' into feature/functional-test-bot
Bill7zz Jun 6, 2019
dd6b17e
Merge branch 'master' into feature/functional-test-bot
gasper-az Jun 11, 2019
a61bbec
Add Copyright header to libraries/testbot/bots/myBot.js file
gasper-az Jun 11, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"libraries/botframework-config",
"libraries/botframework-connector",
"libraries/botframework-schema",
"libraries/functional-tests",
"libraries/testbot",
"transcripts"
],
Expand Down
18 changes: 18 additions & 0 deletions libraries/functional-tests/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "functional-tests",
"version": "1.0.0",
"description": "Test that hits services",
"main": "",
"dependencies": {
"swagger-client": "^2.1.18"
},
"directories": {
"test": "tests"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "MIT"
}
66 changes: 66 additions & 0 deletions libraries/functional-tests/tests/directLine.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

const assert = require('assert');
gasper-az marked this conversation as resolved.
Show resolved Hide resolved
const directLineSpec = require('./directline-swagger.json');
const Swagger = require('swagger-client');

const directLineClientName = 'DirectLineClient';
const userMessage = 'Contoso';
const directLineSecret = process.env.DIRECT_LINE_KEY || null;

const auths = {
AuthorizationBotConnector: new Swagger.ApiKeyAuthorization('Authorization', 'BotConnector ' + directLineSecret, 'header'),
};

function getDirectLineClient() {
return new Swagger({
spec: directLineSpec,
usePromise: true,
authorizations: auths
});
}

async function sendMessage(client, conversationId) {
let status;
do{
await client.Conversations.Conversations_PostMessage({
conversationId: conversationId,
message: {
from: directLineClientName,
text: userMessage
}
}).then((result) => {
status = result.status;
}).catch((err)=>{
status = err.status;
});
}while(status == 502);
}

function getMessages(client, conversationId) {
let watermark = null;
return client.Conversations.Conversations_GetMessages({ conversationId: conversationId, watermark: watermark })
.then((response) => {
return response.obj.messages.filter((message) => message.from !== directLineClientName);
});
}

function getConversationId(client) {
return client.Conversations.Conversations_NewConversation()
.then((response) => response.obj.conversationId);
}

describe('Test Azure Bot', function(){
this.timeout(60000);
it('Check deployed bot answer', async function(){
const directLineClient = await getDirectLineClient();
const conversationId = await getConversationId(directLineClient);
await sendMessage(directLineClient, conversationId);
const messages = await getMessages(directLineClient, conversationId);
const result = messages.filter((message) => message.text.includes('you said'));
assert(result[0].text == `you said "${ userMessage }" 0`, `test fail`);
});
});
304 changes: 304 additions & 0 deletions libraries/functional-tests/tests/directline-swagger.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
{
"swagger": "2.0",
"info": {
"version": "v1",
"title": "Bot Connector - Direct Line API - V1.0",
"description": "Direct Line\r\n===========\r\n\r\n\r\nThe Direct Line API is a simple REST API for connecting directly to a single bot. This API is intended for developers\r\nwriting their own client applications, web chat controls, or mobile apps that will talk to their bot.\r\n\r\nCredentials for the Direct Line API may be obtained from the Bot Framework developer portal, and will only allow the\r\ncaller to connect to the bot for which they were generated. If you are writing a server-to-server application,\r\nthe Direct Line secret may be used directly against the API. If instead you are writing an application where a client\r\nconnects directly (and possibly insecurely) to the Direct Line API, you may exchange the secret for a token that will\r\nwork only for a single conversation and only for a limited amount of time. Tokens expire by default after 30 minutes\r\nalthough they may be renewed up until their expiration.\r\n\r\nThe secret or token (depending on the authorization model) are supplied as basic auth with the \"BotConnector\" scheme\r\nand no further encoding. Example auth header:\r\n\r\n -- connect to directline.botframework.com --\r\n GET /api/tokens\r\n Authorization: BotConnector RCurR_XV9ZA.cwA.BKA.iaJrC8xpy8qbOF5xnR2vtCX7CZj0LdjAPGfiCpg4Fv0\r\n\r\nDon't include the Ocp-Apim-Subscription-Key header. The contents of this header are your bot's secret key. The\r\nbot's secret key is not necessary when using the Direct Line API.\r\n\r\nEach conversation on the Direct Line channel must be explicitly started using a POST to the\r\nhttps://directline.botframework.com/api/conversations endpoint.\r\nIf the call was authorized with a token, the conversation ID is the conversation ID in the scoped token. If a\r\nsecret was used to start the conversation, the conversation will be started with a new, random ID.\r\n\r\nThe client may send messages to the bot by calling POST on https://directline.botframework.com/api/conversations/{conversationId}/messages.\r\n\r\nThe client may retrieve messages sent by the bot by calling GET on https://directline.botframework.com/api/conversations/{conversationId}/messages.\r\nThe JSON structure returned contains a watermark that can be sent on subsequent requests to skip old messages.\r\n\r\nThe Direct Line API does not store messages indefinitely. Your client application must pick them up quickly before\r\nthey are deleted.\r\n\r\n# Client libraries for the Direct Line API\r\n\r\n* [Direct Line Nuget package](https://www.nuget.org/packages/Microsoft.Bot.Connector.DirectLine)\r\n* Generate your own from the [Direct Line Swagger file](swagger.json)\r\n",
"termsOfService": "https://www.microsoft.com/en-us/legal/intellectualproperty/copyright/default.aspx",
"contact": {
"name": "Bot Framework",
"url": "https://botframework.com",
"email": "botframework@microsoft.com"
},
"license": {
"name": "The MIT License (MIT)",
"url": "https://opensource.org/licenses/MIT"
}
},
"host": "directline.botframework.com",
"schemes": [ "https" ],
"paths": {
"/api/tokens/{conversationId}/renew": {
"get": {
"tags": [ "Tokens" ],
"summary": "Renew a token for a conversation",
"operationId": "Tokens_RenewToken",
"consumes": [ ],
"produces": [ "application/json", "text/json", "text/html", "application/xml", "text/xml" ],
"parameters": [
{
"name": "conversationId",
"in": "path",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "A string is returned\r\n",
"schema": { "type": "string" }
},
"401": { "description": "No content is returned with this status code.\r\n\r\nThe BasicAuth header is missing for this request.\r\n" },
"403": { "description": "You are forbidden from performing this action because your token or secret is invalid.\r\n\r\n" },
"404": { "description": "The response is an ErrorMessage response. Look at the Code field for a breakdown and message field for description of the error.\r\n\r\nresource was not found" }
},
"deprecated": false
}
},
"/api/tokens/conversation": {
"post": {
"tags": [ "Tokens" ],
"summary": "Generate a token for a new conversation",
"operationId": "Tokens_GenerateTokenForNewConversation",
"consumes": [ ],
"produces": [ "application/json", "text/json", "text/html", "application/xml", "text/xml" ],
"responses": {
"200": {
"description": "A string is returned\r\n",
"schema": { "type": "string" }
},
"401": { "description": "No content is returned with this status code.\r\n\r\nThe BasicAuth header is missing for this request.\r\n" },
"403": { "description": "You are forbidden from performing this action because your token or secret is invalid.\r\n\r\n" },
"404": { "description": "The response is an ErrorMessage response. Look at the Code field for a breakdown and message field for description of the error.\r\n\r\nresource was not found" }
},
"deprecated": false
}
},
"/api/conversations": {
"post": {
"tags": [ "Conversations" ],
"summary": "Start a new conversation",
"operationId": "Conversations_NewConversation",
"consumes": [ ],
"produces": [ "application/json", "text/json", "text/html", "application/xml", "text/xml" ],
"responses": {
"200": {
"description": "A conversation object is returned\r\n",
"schema": { "$ref": "#/definitions/Conversation" }
},
"401": { "description": "No content is returned with this status code.\r\n\r\nThe BasicAuth header is missing for this request.\r\n" },
"403": { "description": "You are forbidden from performing this action because your token or secret is invalid.\r\n\r\n" },
"404": { "description": "The response is an ErrorMessage response. Look at the Code field for a breakdown and message field for description of the error.\r\n\r\nresource was not found" },
"409": { "description": "You are trying to create an object that already exists." }
},
"deprecated": false
}
},
"/api/conversations/{conversationId}/messages": {
"get": {
"tags": [ "Conversations" ],
"summary": "Get messages in this conversation. This method is paged with the 'watermark' parameter.",
"operationId": "Conversations_GetMessages",
"consumes": [ ],
"produces": [ "application/json", "text/json", "text/html", "application/xml", "text/xml" ],
"parameters": [
{
"name": "conversationId",
"in": "path",
"description": "Conversation ID",
"required": true,
"type": "string"
},
{
"name": "watermark",
"in": "query",
"description": "(Optional) only returns messages newer than this watermark",
"required": false,
"type": "string"
}
],
"responses": {
"200": {
"description": "A set of messages is returned",
"schema": { "$ref": "#/definitions/MessageSet" }
},
"401": { "description": "No content is returned with this status code.\r\n\r\nThe BasicAuth header is missing for this request.\r\n" },
"403": { "description": "You are forbidden from performing this action because your token or secret is invalid.\r\n\r\n" },
"404": { "description": "The response is an ErrorMessage response. Look at the Code field for a breakdown and message field for description of the error.\r\n\r\nresource was not found" }
},
"deprecated": false
},
"post": {
"tags": [ "Conversations" ],
"summary": "Send a message",
"operationId": "Conversations_PostMessage",
"consumes": [ "application/json", "text/json", "text/html", "application/xml", "text/xml", "application/x-www-form-urlencoded" ],
"produces": [ ],
"parameters": [
{
"name": "conversationId",
"in": "path",
"description": "Conversation ID",
"required": true,
"type": "string"
},
{
"name": "message",
"in": "body",
"description": "Message to send",
"required": true,
"schema": { "$ref": "#/definitions/Message" }
}
],
"responses": {
"204": {
"description": "Success - no content",
"schema": { "$ref": "#/definitions/ErrorMessage" }
},
"401": { "description": "No content is returned with this status code.\r\n\r\nThe BasicAuth header is missing for this request.\r\n" },
"403": { "description": "You are forbidden from performing this action because your token or secret is invalid.\r\n\r\n" },
"404": { "description": "The response is an ErrorMessage response. Look at the Code field for a breakdown and message field for description of the error.\r\n\r\nresource was not found" }
},
"deprecated": false
}
},
"/api/conversations/{conversationId}/upload": {
"post": {
"tags": [ "Conversations" ],
"summary": "Upload file(s) and send as attachment(s)",
"operationId": "Conversations_Upload",
"consumes": [ ],
"produces": [ ],
"parameters": [
{
"name": "conversationId",
"in": "path",
"description": "",
"required": true,
"type": "string"
}
],
"responses": {
"204": { "description": "Success - no content" },
"401": { "description": "No content is returned with this status code.\r\n\r\nThe BasicAuth header is missing for this request.\r\n" },
"403": { "description": "You are forbidden from performing this action because your token or secret is invalid.\r\n\r\n" },
"404": { "description": "The response is an ErrorMessage response. Look at the Code field for a breakdown and message field for description of the error.\r\n\r\nresource was not found" }
},
"deprecated": false
}
}
},
"definitions": {
"Conversation": {
"description": "A conversation object returned by POST /api/conversations",
"type": "object",
"properties": {
"conversationId": {
"description": "ID for this conversation",
"type": "string"
},
"token": {
"description": "Token scoped to this conversation",
"type": "string"
},
"eTag": { "type": "string" }
}
},
"MessageSet": {
"description": "A collection of messages",
"type": "object",
"properties": {
"messages": {
"description": "Messages",
"type": "array",
"items": { "$ref": "#/definitions/Message" }
},
"watermark": {
"description": "Maximum watermark included in this set of messages",
"type": "string"
},
"eTag": { "type": "string" }
}
},
"Message": {
"description": "A communication message sent to/from Direct Line",
"type": "object",
"properties": {
"id": {
"description": "ID for this message",
"type": "string"
},
"conversationId": {
"description": "Conversation ID for this message",
"type": "string"
},
"created": {
"format": "date-time",
"description": "UTC timestamp when this message was created",
"type": "string"
},
"from": {
"description": "Identity of the sender of this message",
"type": "string"
},
"text": {
"description": "Text in this message",
"type": "string"
},
"channelData": {
"$ref": "#/definitions/Object",
"description": "Opaque block of data passed to/from bot via the ChannelData field"
},
"images": {
"description": "Array of URLs for images included in this message",
"type": "array",
"items": { "type": "string" }
},
"attachments": {
"description": "Array of non-image attachments included in this message",
"type": "array",
"items": { "$ref": "#/definitions/Attachment" }
},
"eTag": { "type": "string" }
}
},
"Object": {
"type": "object",
"properties": { }
},
"Attachment": {
"description": "An attachment",
"type": "object",
"properties": {
"url": {
"description": "URL for this attachment",
"type": "string"
},
"contentType": {
"description": "Content type for this attachment",
"type": "string"
}
}
},
"ErrorMessage": {
"description": "A standardized message error payload",
"type": "object",
"properties": { "error": { "$ref": "#/definitions/Error" } }
},
"Error": {
"type": "object",
"properties": {
"code": {
"description": "Error code",
"enum": [ "MissingProperty", "MalformedData", "NotFound", "ServiceError", "Internal", "InvalidRange", "NotSupported", "NotAllowed", "BadCertificate" ],
"type": "string"
},
"message": {
"description": "Error message",
"type": "string"
},
"statusCode": {
"format": "int32",
"description": "Status code",
"type": "integer"
}
}
}
},
"securityDefinitions": {
"basic": {
"type": "basic",
"description": "Basic HTTP Authentication using BotConnector auth scheme and secret/token as password"
}
}
}
Loading