Spaces:
Build error
Build error
Validify-testbot-1
/
botbuilder-python
/libraries
/botbuilder-dialogs
/tests
/test_dialogextensions.py
# Copyright (c) Microsoft Corporation. All rights reserved. | |
# Licensed under the MIT License. | |
# pylint: disable=ungrouped-imports | |
import enum | |
from typing import List | |
import uuid | |
import aiounittest | |
from botframework.connector.auth import ClaimsIdentity, AuthenticationConstants | |
from botbuilder.core import ( | |
TurnContext, | |
MessageFactory, | |
MemoryStorage, | |
ConversationState, | |
UserState, | |
AdapterExtensions, | |
BotAdapter, | |
) | |
from botbuilder.core.adapters import ( | |
TestFlow, | |
TestAdapter, | |
) | |
from botbuilder.core.skills import ( | |
SkillHandler, | |
SkillConversationReference, | |
) | |
from botbuilder.core.transcript_logger import ( | |
TranscriptLoggerMiddleware, | |
ConsoleTranscriptLogger, | |
) | |
from botbuilder.schema import ActivityTypes, Activity, EndOfConversationCodes | |
from botbuilder.dialogs import ( | |
ComponentDialog, | |
TextPrompt, | |
WaterfallDialog, | |
DialogInstance, | |
DialogReason, | |
WaterfallStepContext, | |
PromptOptions, | |
Dialog, | |
DialogExtensions, | |
DialogEvents, | |
) | |
class SimpleComponentDialog(ComponentDialog): | |
def __init__(self): | |
super().__init__("SimpleComponentDialog") | |
self.add_dialog(TextPrompt("TextPrompt")) | |
self.add_dialog( | |
WaterfallDialog("WaterfallDialog", [self.prompt_for_name, self.final_step]) | |
) | |
self.initial_dialog_id = "WaterfallDialog" | |
self.end_reason = DialogReason.BeginCalled | |
async def end_dialog( | |
self, context: TurnContext, instance: DialogInstance, reason: DialogReason | |
) -> None: | |
self.end_reason = reason | |
return await super().end_dialog(context, instance, reason) | |
async def prompt_for_name(self, step_context: WaterfallStepContext): | |
return await step_context.prompt( | |
"TextPrompt", | |
PromptOptions( | |
prompt=MessageFactory.text("Hello, what is your name?"), | |
retry_prompt=MessageFactory.text("Hello, what is your name again?"), | |
), | |
) | |
async def final_step(self, step_context: WaterfallStepContext): | |
await step_context.context.send_activity( | |
f"Hello {step_context.result}, nice to meet you!" | |
) | |
return await step_context.end_dialog(step_context.result) | |
class FlowTestCase(enum.Enum): | |
root_bot_only = 1 | |
root_bot_consuming_skill = 2 | |
middle_skill = 3 | |
leaf_skill = 4 | |
class DialogExtensionsTests(aiounittest.AsyncTestCase): | |
def __init__(self, methodName): | |
super().__init__(methodName) | |
self.eoc_sent: Activity = None | |
self.skill_bot_id = str(uuid.uuid4()) | |
self.parent_bot_id = str(uuid.uuid4()) | |
async def handles_bot_and_skills_test_cases( | |
self, test_case: FlowTestCase, send_eoc: bool | |
): | |
dialog = SimpleComponentDialog() | |
test_flow = self.create_test_flow(dialog, test_case) | |
await test_flow.send("Hi") | |
await test_flow.assert_reply("Hello, what is your name?") | |
await test_flow.send("SomeName") | |
await test_flow.assert_reply("Hello SomeName, nice to meet you!") | |
assert dialog.end_reason == DialogReason.EndCalled | |
if send_eoc: | |
self.assertIsNotNone( | |
self.eoc_sent, "Skills should send EndConversation to channel" | |
) | |
assert ActivityTypes.end_of_conversation == self.eoc_sent.type | |
assert EndOfConversationCodes.completed_successfully == self.eoc_sent.code | |
assert self.eoc_sent.value == "SomeName" | |
else: | |
self.assertIsNone( | |
self.eoc_sent, "Root bot should not send EndConversation to channel" | |
) | |
async def test_handles_root_bot_only(self): | |
return await self.handles_bot_and_skills_test_cases( | |
FlowTestCase.root_bot_only, False | |
) | |
async def test_handles_root_bot_consuming_skill(self): | |
return await self.handles_bot_and_skills_test_cases( | |
FlowTestCase.root_bot_consuming_skill, False | |
) | |
async def test_handles_middle_skill(self): | |
return await self.handles_bot_and_skills_test_cases( | |
FlowTestCase.middle_skill, True | |
) | |
async def test_handles_leaf_skill(self): | |
return await self.handles_bot_and_skills_test_cases( | |
FlowTestCase.leaf_skill, True | |
) | |
async def test_skill_handles_eoc_from_parent(self): | |
dialog = SimpleComponentDialog() | |
test_flow = self.create_test_flow(dialog, FlowTestCase.leaf_skill) | |
await test_flow.send("Hi") | |
await test_flow.assert_reply("Hello, what is your name?") | |
await test_flow.send( | |
Activity( | |
type=ActivityTypes.end_of_conversation, | |
caller_id=self.parent_bot_id, | |
) | |
) | |
self.assertIsNone( | |
self.eoc_sent, | |
"Skill should not send back EoC when an EoC is sent from a parent", | |
) | |
assert dialog.end_reason == DialogReason.CancelCalled | |
async def test_skill_handles_reprompt_from_parent(self): | |
dialog = SimpleComponentDialog() | |
test_flow = self.create_test_flow(dialog, FlowTestCase.leaf_skill) | |
await test_flow.send("Hi") | |
await test_flow.assert_reply("Hello, what is your name?") | |
await test_flow.send( | |
Activity( | |
type=ActivityTypes.event, | |
caller_id=self.parent_bot_id, | |
name=DialogEvents.reprompt_dialog, | |
) | |
) | |
await test_flow.assert_reply("Hello, what is your name?") | |
assert dialog.end_reason == DialogReason.BeginCalled | |
def create_test_flow(self, dialog: Dialog, test_case: FlowTestCase) -> TestFlow: | |
conversation_id = str(uuid.uuid4()) | |
storage = MemoryStorage() | |
convo_state = ConversationState(storage) | |
user_state = UserState(storage) | |
async def logic(context: TurnContext): | |
if test_case != FlowTestCase.root_bot_only: | |
claims_identity = ClaimsIdentity( | |
{ | |
AuthenticationConstants.VERSION_CLAIM: "2.0", | |
AuthenticationConstants.AUDIENCE_CLAIM: self.skill_bot_id, | |
AuthenticationConstants.AUTHORIZED_PARTY: self.parent_bot_id, | |
}, | |
True, | |
) | |
context.turn_state[BotAdapter.BOT_IDENTITY_KEY] = claims_identity | |
if test_case == FlowTestCase.root_bot_consuming_skill: | |
context.turn_state[ | |
SkillHandler.SKILL_CONVERSATION_REFERENCE_KEY | |
] = SkillConversationReference( | |
None, AuthenticationConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE | |
) | |
if test_case == FlowTestCase.middle_skill: | |
context.turn_state[ | |
SkillHandler.SKILL_CONVERSATION_REFERENCE_KEY | |
] = SkillConversationReference(None, self.parent_bot_id) | |
async def capture_eoc( | |
inner_context: TurnContext, activities: List[Activity], next | |
): # pylint: disable=unused-argument | |
for activity in activities: | |
if activity.type == ActivityTypes.end_of_conversation: | |
self.eoc_sent = activity | |
break | |
return await next() | |
context.on_send_activities(capture_eoc) | |
await DialogExtensions.run_dialog( | |
dialog, context, convo_state.create_property("DialogState") | |
) | |
adapter = TestAdapter( | |
logic, TestAdapter.create_conversation_reference(conversation_id) | |
) | |
AdapterExtensions.use_storage(adapter, storage) | |
AdapterExtensions.use_bot_state(adapter, user_state, convo_state) | |
adapter.use(TranscriptLoggerMiddleware(ConsoleTranscriptLogger())) | |
return TestFlow(None, adapter) | |