async def test_memory_storage_write_should_overwrite_when_new_e_tag_is_an_asterisk_2( self): storage = MemoryStorage() await storage.write({'user': SimpleStoreItem(e_tag='1')}) await storage.write({'user': SimpleStoreItem(counter=5, e_tag='1')}) data = await storage.read(['user']) assert data['user'].counter == 5
def test_state_none_name(self): #Arrange dictionary = {} user_state = UserState(MemoryStorage(dictionary)) #Act with self.assertRaises(TypeError) as _: user_state.create_property(None)
async def test_memory_storage_write_should_overwrite_when_new_e_tag_is_an_asterisk_1( self, ): storage = MemoryStorage() await storage.write({"user": SimpleStoreItem(e_tag="1")}) await storage.write({"user": SimpleStoreItem(counter=10, e_tag="*")}) data = await storage.read(["user"]) assert data["user"].counter == 10
async def test_memory_storage_write_should_add_new_value(self): storage = MemoryStorage() aux = {"user": SimpleStoreItem(counter=1)} await storage.write(aux) data = await storage.read(["user"]) assert "user" in data assert data["user"].counter == 1
async def test_conversation_state_bad_converation_throws(self): dictionary = {} user_state = ConversationState(MemoryStorage(dictionary)) context = TestUtilities.create_empty_context() context.activity.conversation = None test_property = user_state.create_property("test") with self.assertRaises(AttributeError): await test_property.get(context)
async def test_should_call_oauth_prompt(self): connection_name = "myConnection" token = "abc123" async def callback_handler(turn_context: TurnContext): dialog_context = await dialogs.create_context(turn_context) results = await dialog_context.continue_dialog() if results.status == DialogTurnStatus.Empty: await dialog_context.prompt("prompt", PromptOptions()) elif results.status == DialogTurnStatus.Complete: if results.result.token: await turn_context.send_activity("Logged in.") else: await turn_context.send_activity("Failed") await convo_state.save_changes(turn_context) # Initialize TestAdapter. adapter = TestAdapter(callback_handler) # Create ConversationState with MemoryStorage and register the state as middleware. convo_state = ConversationState(MemoryStorage()) # Create a DialogState property, DialogSet and AttachmentPrompt. dialog_state = convo_state.create_property("dialogState") dialogs = DialogSet(dialog_state) dialogs.add( OAuthPrompt( "prompt", OAuthPromptSettings(connection_name, "Login", None, 300000))) async def inspector(activity: Activity, description: str = None): # pylint: disable=unused-argument self.assertTrue(len(activity.attachments) == 1) self.assertTrue(activity.attachments[0].content_type == CardFactory.content_types.oauth_card) # send a mock EventActivity back to the bot with the token adapter.add_user_token(connection_name, activity.channel_id, activity.recipient.id, token) event_activity = create_reply(activity) event_activity.type = ActivityTypes.event event_activity.from_property, event_activity.recipient = ( event_activity.recipient, event_activity.from_property, ) event_activity.name = "tokens/response" event_activity.value = TokenResponse( connection_name=connection_name, token=token) context = TurnContext(adapter, event_activity) await callback_handler(context) step1 = await adapter.send("Hello") step2 = await step1.assert_reply(inspector) await step2.assert_reply("Logged in.")
async def test_memory_storage_delete_invalid_key_should_do_nothing_and_not_affect_cached_data( self, ): storage = MemoryStorage({"test": "test"}) await storage.delete(["foo"]) data = await storage.read(["test"]) assert len(data.keys()) == 1 data = await storage.read(["foo"]) assert not data.keys()
async def test_memory_storage_delete_invalid_key_should_do_nothing_and_not_affect_cached_data( self): storage = MemoryStorage({'test': 'test'}) await storage.delete(['foo']) data = await storage.read(['test']) assert len(data.keys()) == 1 data = await storage.read(['foo']) assert len(data.keys()) == 0
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)
async def test_should_end_oauth_prompt_on_invalid_message_when_end_on_invalid_message( self, ): connection_name = "myConnection" token = "abc123" magic_code = "888999" async def exec_test(turn_context: TurnContext): dialog_context = await dialogs.create_context(turn_context) results = await dialog_context.continue_dialog() if results.status == DialogTurnStatus.Empty: await dialog_context.prompt("prompt", PromptOptions()) elif results.status == DialogTurnStatus.Complete: if results.result and results.result.token: await turn_context.send_activity("Failed") else: await turn_context.send_activity("Ended") await convo_state.save_changes(turn_context) # Initialize TestAdapter. adapter = TestAdapter(exec_test) # Create ConversationState with MemoryStorage and register the state as middleware. convo_state = ConversationState(MemoryStorage()) # Create a DialogState property, DialogSet and AttachmentPrompt. dialog_state = convo_state.create_property("dialog_state") dialogs = DialogSet(dialog_state) dialogs.add( OAuthPrompt( "prompt", OAuthPromptSettings(connection_name, "Login", None, 300000, None, True), )) def inspector(activity: Activity, description: str = None): # pylint: disable=unused-argument assert len(activity.attachments) == 1 assert (activity.attachments[0].content_type == CardFactory.content_types.oauth_card) # send a mock EventActivity back to the bot with the token adapter.add_user_token( connection_name, activity.channel_id, activity.recipient.id, token, magic_code, ) step1 = await adapter.send("Hello") step2 = await step1.assert_reply(inspector) step3 = await step2.send("test invalid message") await step3.assert_reply("Ended")
async def test_waterfall_with_class(self): convo_state = ConversationState(MemoryStorage()) TestAdapter() # TODO: Fix Autosave Middleware dialog_state = convo_state.create_property("dialogState") dialogs = DialogSet(dialog_state) dialogs.add(MyWaterfallDialog("test")) self.assertNotEqual(dialogs, None) self.assertEqual(len(dialogs._dialogs), 1) # pylint: disable=protected-access
async def test_memory_storage_delete_should_delete_multiple_values_when_given_multiple_valid_keys( self): storage = MemoryStorage({ 'test': SimpleStoreItem(), 'test2': SimpleStoreItem(2, '2') }) await storage.delete(['test', 'test2']) data = await storage.read(['test', 'test2']) assert len(data.keys()) == 0
async def test_state_set_no_load(self): """Should be able to set a property with no Load""" # Arrange dictionary = {} user_state = UserState(MemoryStorage(dictionary)) context = TestUtilities.create_empty_context() # Act property_a = user_state.create_property("property_a") await property_a.set(context, "hello")
async def test_memory_storage_read_should_return_data_with_valid_key(self): storage = MemoryStorage() await storage.write({'user': SimpleStoreItem()}) data = await storage.read(['user']) assert 'user' in data assert data['user'].counter == 1 assert len(data.keys()) == 1 assert storage._e_tag == 1 assert int(data['user'].e_tag) == 0
async def test_memory_storage_delete_should_delete_multiple_values_when_given_multiple_valid_keys( self, ): storage = MemoryStorage({ "test": SimpleStoreItem(), "test2": SimpleStoreItem(2, "2") }) await storage.delete(["test", "test2"]) data = await storage.read(["test", "test2"]) assert not data.keys()
async def test_memory_storage_read_should_return_data_with_valid_key(self): storage = MemoryStorage() await storage.write({"user": SimpleStoreItem()}) data = await storage.read(["user"]) assert "user" in data assert data["user"].counter == 1 assert len(data.keys()) == 1 assert storage._e_tag == 1 # pylint: disable=protected-access assert int(data["user"].e_tag) == 0
async def test_number_prompt_validator(self): async def exec_test(turn_context: TurnContext) -> None: dialog_context = await dialogs.create_context(turn_context) results = await dialog_context.continue_dialog() if results.status == DialogTurnStatus.Empty: options = PromptOptions( prompt=Activity(type=ActivityTypes.message, text="Enter a number."), retry_prompt=Activity( type=ActivityTypes.message, text="You must enter a positive number less than 100.", ), ) await dialog_context.prompt("NumberPrompt", options) elif results.status == DialogTurnStatus.Complete: number_result = int(results.result) await turn_context.send_activity( MessageFactory.text(f"Bot received the number '{number_result}'.") ) await conver_state.save_changes(turn_context) # Create new ConversationState with MemoryStorage and register the state as middleware. conver_state = ConversationState(MemoryStorage()) # Create a DialogState property, DialogSet and register the WaterfallDialog. dialog_state = conver_state.create_property("dialogState") dialogs = DialogSet(dialog_state) # Create and add number prompt to DialogSet. async def validator(prompt_context: PromptValidatorContext): result = prompt_context.recognized.value if 0 < result < 100: return True return False number_prompt = NumberPrompt( "NumberPrompt", validator, default_locale=Culture.English ) dialogs.add(number_prompt) adapter = TestAdapter(exec_test) step1 = await adapter.send("hello") step2 = await step1.assert_reply("Enter a number.") step3 = await step2.send("150") step4 = await step3.assert_reply( "You must enter a positive number less than 100." ) step5 = await step4.send("64") await step5.assert_reply("Bot received the number '64'.")
async def test_should_send_ignore_retry_rompt_if_validator_replies(self): async def exec_test(turn_context: TurnContext): dialog_context = await dialogs.create_context(turn_context) results = await dialog_context.continue_dialog() if results.status == DialogTurnStatus.Empty: options = PromptOptions( prompt=Activity( type=ActivityTypes.message, text="please add an attachment." ), retry_prompt=Activity( type=ActivityTypes.message, text="please try again." ), ) await dialog_context.prompt("AttachmentPrompt", options) elif results.status == DialogTurnStatus.Complete: attachment = results.result[0] content = MessageFactory.text(attachment.content) await turn_context.send_activity(content) await convo_state.save_changes(turn_context) # Initialize TestAdapter. adapter = TestAdapter(exec_test) # Create ConversationState with MemoryStorage and register the state as middleware. convo_state = ConversationState(MemoryStorage()) # Create a DialogState property, DialogSet and AttachmentPrompt. dialog_state = convo_state.create_property("dialog_state") dialogs = DialogSet(dialog_state) async def aux_validator(prompt_context: PromptValidatorContext): assert prompt_context, "Validator missing prompt_context" if not prompt_context.recognized.succeeded: await prompt_context.context.send_activity("Bad input.") return prompt_context.recognized.succeeded dialogs.add(AttachmentPrompt("AttachmentPrompt", aux_validator)) # Create incoming activity with attachment. attachment = Attachment(content="some content", content_type="text/plain") attachment_activity = Activity( type=ActivityTypes.message, attachments=[attachment] ) invalid_activty = Activity(type=ActivityTypes.message, text="invalid") step1 = await adapter.send("hello") step2 = await step1.assert_reply("please add an attachment.") step3 = await step2.send(invalid_activty) step4 = await step3.assert_reply("Bad input.") step5 = await step4.send(attachment_activity) await step5.assert_reply("some content")
async def test_state_get_no_load_with_default(self): """Should be able to get a property with no Load and default""" # Arrange dictionary = {} user_state = UserState(MemoryStorage(dictionary)) context = TestUtilities.create_empty_context() # Act property_a = user_state.create_property("property_a") value_a = await property_a.get(context, lambda: "Default!") self.assertEqual("Default!", value_a)
async def test_state_multiple_loads(self): """Should be able to load multiple times""" # Arrange dictionary = {} user_state = UserState(MemoryStorage(dictionary)) context = TestUtilities.create_empty_context() # Act user_state.create_property("property_a") await user_state.load(context) await user_state.load(context)
async def test_should_display_choices_on_hero_card_with_additional_attachment(self): size_choices = ["large", "medium", "small"] card = CardFactory.adaptive_card( { "type": "AdaptiveCard", "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "version": "1.2", "body": [], } ) card_activity = Activity(attachments=[card]) async def exec_test(turn_context: TurnContext): dialog_context = await dialogs.create_context(turn_context) results: DialogTurnResult = await dialog_context.continue_dialog() if results.status == DialogTurnStatus.Empty: options = PromptOptions(prompt=card_activity, choices=size_choices) await dialog_context.prompt("prompt", options) elif results.status == DialogTurnStatus.Complete: selected_choice = results.result await turn_context.send_activity(selected_choice.value) await convo_state.save_changes(turn_context) def assert_expected_activity( activity: Activity, description ): # pylint: disable=unused-argument assert len(activity.attachments) == 2 assert ( activity.attachments[0].content_type == CardFactory.content_types.adaptive_card ) assert ( activity.attachments[1].content_type == CardFactory.content_types.hero_card ) adapter = TestAdapter(exec_test) convo_state = ConversationState(MemoryStorage()) dialog_state = convo_state.create_property("dialogState") dialogs = DialogSet(dialog_state) choice_prompt = ChoicePrompt("prompt") # Change the ListStyle of the prompt to ListStyle.none. choice_prompt.style = ListStyle.hero_card dialogs.add(choice_prompt) step1 = await adapter.send("Hello") await step1.assert_reply(assert_expected_activity)
async def test_memory_storage_delete_should_delete_values_when_given_multiple_valid_keys_and_ignore_other_data( self, ): storage = MemoryStorage({ "test": SimpleStoreItem(), "test2": SimpleStoreItem(2, "2"), "test3": SimpleStoreItem(3, "3"), }) await storage.delete(["test", "test2"]) data = await storage.read(["test", "test2", "test3"]) assert len(data.keys()) == 1
async def test_confirm_prompt_should_default_to_english_locale(self): async def exec_test(turn_context: TurnContext): dialog_context = await dialogs.create_context(turn_context) results: DialogTurnResult = await dialog_context.continue_dialog() if results.status == DialogTurnStatus.Empty: options = PromptOptions( prompt=Activity(type=ActivityTypes.message, text="Please confirm."), retry_prompt=Activity( type=ActivityTypes.message, text= "Please confirm, say 'yes' or 'no' or something like that.", ), ) await dialog_context.prompt("ConfirmPrompt", options) elif results.status == DialogTurnStatus.Complete: message_text = "Confirmed" if results.result else "Not confirmed" await turn_context.send_activity( MessageFactory.text(message_text)) await convo_state.save_changes(turn_context) locales = [None, "", "not-supported"] for locale in locales: # Initialize TestAdapter. adapter = TestAdapter(exec_test) # Create new ConversationState with MemoryStorage and register the state as middleware. convo_state = ConversationState(MemoryStorage()) # Create a DialogState property, DialogSet, and ChoicePrompt. dialog_state = convo_state.create_property("dialogState") dialogs = DialogSet(dialog_state) confirm_prompt = ConfirmPrompt("ConfirmPrompt") confirm_prompt.choice_options = ChoiceFactoryOptions( include_numbers=True) dialogs.add(confirm_prompt) step1 = await adapter.send( Activity(type=ActivityTypes.message, text="Hello", locale=locale)) step2 = await step1.assert_reply( "Please confirm. (1) Yes or (2) No") step3 = await step2.send("lala") step4 = await step3.assert_reply( "Please confirm, say 'yes' or 'no' or something like that. (1) Yes or (2) No" ) step5 = await step4.send("2") await step5.assert_reply("Not confirmed")
async def test_on_prompt_with_null_options_fails(self): conver_state = ConversationState(MemoryStorage()) dialog_state = conver_state.create_property("dialogState") dialogs = DialogSet(dialog_state) number_prompt_mock = NumberPromptMock( dialog_id="NumberPromptMock", validator=None, default_locale=Culture.English ) dialogs.add(number_prompt_mock) with self.assertRaises(TypeError): await number_prompt_mock.on_recognize_null_context()
def __init__( self, channel_or_adapter: Union[str, TestAdapter], target_dialog: Dialog, initial_dialog_options: object = None, middlewares: List[Middleware] = None, conversation_state: ConversationState = None, ): """ Create a DialogTestClient to test a dialog without having to create a full-fledged adapter. ```python client = DialogTestClient("test", MY_DIALOG, MY_OPTIONS) reply = await client.send_activity("first message") self.assertEqual(reply.text, "first reply", "reply failed") ``` :param channel_or_adapter: The channel Id or test adapter to be used for the test. For channel Id, use 'emulator' or 'test' if you are uncertain of the channel you are targeting. Otherwise, it is recommended that you use the id for the channel(s) your bot will be using and write a test case for each channel. Or, a test adapter instance can be used. :type channel_or_adapter: Union[str, TestAdapter] :param target_dialog: The dialog to be tested. This will be the root dialog for the test client. :type target_dialog: Dialog :param initial_dialog_options: (Optional) additional argument(s) to pass to the dialog being started. :type initial_dialog_options: object :param middlewares: (Optional) The test adapter to use. If this parameter is not provided, the test client will use a default TestAdapter. :type middlewares: List[Middleware] :param conversation_state: (Optional) A ConversationState instance to use in the test client. :type conversation_state: ConversationState """ self.dialog_turn_result: DialogTurnResult = None self.conversation_state: ConversationState = (ConversationState( MemoryStorage()) if conversation_state is None else conversation_state) dialog_state = self.conversation_state.create_property("DialogState") self._callback = self._get_default_callback(target_dialog, initial_dialog_options, dialog_state) if isinstance(channel_or_adapter, str): conversation_reference = ConversationReference( channel_id=channel_or_adapter) self.test_adapter = TestAdapter(self._callback, conversation_reference) self.test_adapter.use(AutoSaveStateMiddleware().add( self.conversation_state)) else: self.test_adapter = channel_or_adapter self._add_user_middlewares(middlewares)
async def test_begin_dialog_calls_skill(self): activity_sent = None from_bot_id_sent = None to_bot_id_sent = None to_url_sent = None async def capture( from_bot_id: str, to_bot_id: str, to_url: str, service_url: str, # pylint: disable=unused-argument conversation_id: str, # pylint: disable=unused-argument activity: Activity, ): nonlocal from_bot_id_sent, to_bot_id_sent, to_url_sent, activity_sent from_bot_id_sent = from_bot_id to_bot_id_sent = to_bot_id to_url_sent = to_url activity_sent = activity mock_skill_client = self._create_mock_skill_client(capture) conversation_state = ConversationState(MemoryStorage()) dialog_options = self._create_skill_dialog_options( conversation_state, mock_skill_client) sut = SkillDialog(dialog_options, "dialog_id") activity_to_send = MessageFactory.text(str(uuid.uuid4())) client = DialogTestClient( "test", sut, BeginSkillDialogOptions(activity=activity_to_send), conversation_state=conversation_state, ) await client.send_activity(MessageFactory.text("irrelevant")) assert dialog_options.bot_id == from_bot_id_sent assert dialog_options.skill.app_id == to_bot_id_sent assert dialog_options.skill.skill_endpoint == to_url_sent assert activity_to_send.text == activity_sent.text assert DialogTurnStatus.Waiting == client.dialog_turn_result.status await client.send_activity(MessageFactory.text("Second message")) assert activity_sent.text == "Second message" assert DialogTurnStatus.Waiting == client.dialog_turn_result.status await client.send_activity( Activity(type=ActivityTypes.end_of_conversation)) assert DialogTurnStatus.Complete == client.dialog_turn_result.status
async def test_memory_storage_delete_should_delete_according_cached_data( self): storage = MemoryStorage({"test": "test"}) try: await storage.delete(["test"]) except Exception as error: raise error else: data = await storage.read(["test"]) assert isinstance(data, dict) assert not data.keys()
async def test_memory_storage_delete_should_delete_according_cached_data( self): storage = MemoryStorage({'test': 'test'}) try: await storage.delete(['test']) except Exception as e: raise e else: data = await storage.read(['test']) assert type(data) == dict assert len(data.keys()) == 0
async def test_oauth_prompt_doesnt_detect_code_in_begin_dialog(self): connection_name = "myConnection" token = "abc123" magic_code = "888999" async def exec_test(turn_context: TurnContext): # Add a magic code to the adapter preemptively so that we can test if the message that triggers # BeginDialogAsync uses magic code detection adapter.add_user_token( connection_name, turn_context.activity.channel_id, turn_context.activity.from_property.id, token, magic_code, ) dialog_context = await dialogs.create_context(turn_context) results = await dialog_context.continue_dialog() if results.status == DialogTurnStatus.Empty: # If magicCode is detected when prompting, this will end the dialog and return the token in tokenResult token_result = await dialog_context.prompt("prompt", PromptOptions()) if isinstance(token_result.result, TokenResponse): self.assertTrue(False) # pylint: disable=redundant-unittest-assert await convo_state.save_changes(turn_context) # Initialize TestAdapter. adapter = TestAdapter(exec_test) # Create ConversationState with MemoryStorage and register the state as middleware. convo_state = ConversationState(MemoryStorage()) # Create a DialogState property, DialogSet and AttachmentPrompt. dialog_state = convo_state.create_property("dialog_state") dialogs = DialogSet(dialog_state) dialogs.add( OAuthPrompt( "prompt", OAuthPromptSettings(connection_name, "Login", None, 300000) ) ) def inspector(activity: Activity, description: str = None): # pylint: disable=unused-argument assert len(activity.attachments) == 1 assert ( activity.attachments[0].content_type == CardFactory.content_types.oauth_card ) step1 = await adapter.send(magic_code) await step1.assert_reply(inspector)
async def test_number_uses_locale_specified_in_constructor(self): # Create new ConversationState with MemoryStorage and register the state as middleware. conver_state = ConversationState(MemoryStorage()) # Create a DialogState property, DialogSet and register the WaterfallDialog. dialog_state = conver_state.create_property("dialogState") dialogs = DialogSet(dialog_state) # Create and add number prompt to DialogSet. number_prompt = NumberPrompt( "NumberPrompt", None, default_locale=Culture.Spanish ) dialogs.add(number_prompt) async def exec_test(turn_context: TurnContext) -> None: dialog_context = await dialogs.create_context(turn_context) results = await dialog_context.continue_dialog() if results.status == DialogTurnStatus.Empty: await dialog_context.begin_dialog( "NumberPrompt", PromptOptions( prompt=MessageFactory.text( "How much money is in your gaming account?" ) ), ) else: if results.status == DialogTurnStatus.Complete: number_result = results.result await turn_context.send_activity( MessageFactory.text( f"You say you have ${number_result} in your gaming account." ) ) await conver_state.save_changes(turn_context) adapter = TestAdapter(exec_test) test_flow = TestFlow(None, adapter) test_flow2 = await test_flow.send("Hello") test_flow3 = await test_flow2.assert_reply( "How much money is in your gaming account?" ) test_flow4 = await test_flow3.send("I've got $1.200.555,42 in my account.") await test_flow4.assert_reply( "You say you have $1200555.42 in your gaming account." )