Skip to content

Commit ca8c2d1

Browse files
authored
Merge pull request #511 from microsoft/axsuarez/integration-and-skills-tests
Axsuarez/integration and skills tests
2 parents a1a4ed6 + a275712 commit ca8c2d1

File tree

3 files changed

+377
-0
lines changed

3 files changed

+377
-0
lines changed
Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
import hashlib
2+
import json
3+
from uuid import uuid4
4+
from asyncio import Future
5+
from typing import Dict, List
6+
7+
from unittest.mock import Mock, MagicMock
8+
import aiounittest
9+
10+
from botbuilder.core import TurnContext, BotActionNotImplementedError
11+
from botbuilder.core.skills import ConversationIdFactoryBase, SkillHandler
12+
from botbuilder.schema import (
13+
Activity,
14+
ActivityTypes,
15+
AttachmentData,
16+
ChannelAccount,
17+
ConversationAccount,
18+
ConversationParameters,
19+
ConversationsResult,
20+
ConversationResourceResponse,
21+
ConversationReference,
22+
PagedMembersResult,
23+
ResourceResponse,
24+
Transcript,
25+
)
26+
from botframework.connector.auth import (
27+
AuthenticationConfiguration,
28+
AuthenticationConstants,
29+
ClaimsIdentity,
30+
)
31+
32+
33+
class ConversationIdFactoryForTest(ConversationIdFactoryBase):
34+
def __init__(self):
35+
self._conversation_refs: Dict[str, str] = {}
36+
37+
async def create_skill_conversation_id(
38+
self, conversation_reference: ConversationReference
39+
) -> str:
40+
cr_json = json.dumps(conversation_reference.serialize())
41+
42+
key = hashlib.md5(
43+
f"{conversation_reference.conversation.id}{conversation_reference.service_url}".encode()
44+
).hexdigest()
45+
46+
if key not in self._conversation_refs:
47+
self._conversation_refs[key] = cr_json
48+
49+
return key
50+
51+
async def get_conversation_reference(
52+
self, skill_conversation_id: str
53+
) -> ConversationReference:
54+
conversation_reference = ConversationReference().deserialize(
55+
json.loads(self._conversation_refs[skill_conversation_id])
56+
)
57+
return conversation_reference
58+
59+
async def delete_conversation_reference(self, skill_conversation_id: str):
60+
raise NotImplementedError()
61+
62+
63+
class SkillHandlerInstanceForTests(SkillHandler):
64+
async def test_on_get_conversations(
65+
self, claims_identity: ClaimsIdentity, continuation_token: str = "",
66+
) -> ConversationsResult:
67+
return await self.on_get_conversations(claims_identity, continuation_token)
68+
69+
async def test_on_create_conversation(
70+
self, claims_identity: ClaimsIdentity, parameters: ConversationParameters,
71+
) -> ConversationResourceResponse:
72+
return await self.on_create_conversation(claims_identity, parameters)
73+
74+
async def test_on_send_to_conversation(
75+
self, claims_identity: ClaimsIdentity, conversation_id: str, activity: Activity,
76+
) -> ResourceResponse:
77+
return await self.on_send_to_conversation(
78+
claims_identity, conversation_id, activity
79+
)
80+
81+
async def test_on_send_conversation_history(
82+
self,
83+
claims_identity: ClaimsIdentity,
84+
conversation_id: str,
85+
transcript: Transcript,
86+
) -> ResourceResponse:
87+
return await self.on_send_conversation_history(
88+
claims_identity, conversation_id, transcript
89+
)
90+
91+
async def test_on_update_activity(
92+
self,
93+
claims_identity: ClaimsIdentity,
94+
conversation_id: str,
95+
activity_id: str,
96+
activity: Activity,
97+
) -> ResourceResponse:
98+
return await self.on_update_activity(
99+
claims_identity, conversation_id, activity_id, activity
100+
)
101+
102+
async def test_on_reply_to_activity(
103+
self,
104+
claims_identity: ClaimsIdentity,
105+
conversation_id: str,
106+
activity_id: str,
107+
activity: Activity,
108+
) -> ResourceResponse:
109+
return await self.on_reply_to_activity(
110+
claims_identity, conversation_id, activity_id, activity
111+
)
112+
113+
async def test_on_delete_activity(
114+
self, claims_identity: ClaimsIdentity, conversation_id: str, activity_id: str,
115+
):
116+
return await self.on_delete_activity(
117+
claims_identity, conversation_id, activity_id
118+
)
119+
120+
async def test_on_get_conversation_members(
121+
self, claims_identity: ClaimsIdentity, conversation_id: str,
122+
) -> List[ChannelAccount]:
123+
return await self.on_get_conversation_members(claims_identity, conversation_id)
124+
125+
async def test_on_get_conversation_paged_members(
126+
self,
127+
claims_identity: ClaimsIdentity,
128+
conversation_id: str,
129+
page_size: int = None,
130+
continuation_token: str = "",
131+
) -> PagedMembersResult:
132+
return await self.on_get_conversation_paged_members(
133+
claims_identity, conversation_id, page_size, continuation_token
134+
)
135+
136+
async def test_on_delete_conversation_member(
137+
self, claims_identity: ClaimsIdentity, conversation_id: str, member_id: str,
138+
):
139+
return await self.on_delete_conversation_member(
140+
claims_identity, conversation_id, member_id
141+
)
142+
143+
async def test_on_get_activity_members(
144+
self, claims_identity: ClaimsIdentity, conversation_id: str, activity_id: str,
145+
) -> List[ChannelAccount]:
146+
return await self.on_get_activity_members(
147+
claims_identity, conversation_id, activity_id
148+
)
149+
150+
async def test_on_upload_attachment(
151+
self,
152+
claims_identity: ClaimsIdentity,
153+
conversation_id: str,
154+
attachment_upload: AttachmentData,
155+
) -> ResourceResponse:
156+
return await self.on_upload_attachment(
157+
claims_identity, conversation_id, attachment_upload
158+
)
159+
160+
161+
# pylint: disable=invalid-name
162+
# pylint: disable=attribute-defined-outside-init
163+
164+
165+
class TestSkillHandler(aiounittest.AsyncTestCase):
166+
@classmethod
167+
def setUpClass(cls):
168+
bot_id = str(uuid4())
169+
skill_id = str(uuid4())
170+
171+
cls._test_id_factory = ConversationIdFactoryForTest()
172+
173+
cls._claims_identity = ClaimsIdentity({}, False)
174+
175+
cls._claims_identity.claims[AuthenticationConstants.AUDIENCE_CLAIM] = bot_id
176+
cls._claims_identity.claims[AuthenticationConstants.APP_ID_CLAIM] = skill_id
177+
cls._claims_identity.claims[
178+
AuthenticationConstants.SERVICE_URL_CLAIM
179+
] = "http://testbot.com/api/messages"
180+
cls._conversation_reference = ConversationReference(
181+
conversation=ConversationAccount(id=str(uuid4())),
182+
service_url="http://testbot.com/api/messages",
183+
)
184+
185+
def create_skill_handler_for_testing(self, adapter) -> SkillHandlerInstanceForTests:
186+
return SkillHandlerInstanceForTests(
187+
adapter,
188+
Mock(),
189+
self._test_id_factory,
190+
Mock(),
191+
AuthenticationConfiguration(),
192+
)
193+
194+
async def test_on_send_to_conversation(self):
195+
self._conversation_id = await self._test_id_factory.create_skill_conversation_id(
196+
self._conversation_reference
197+
)
198+
199+
mock_adapter = Mock()
200+
mock_adapter.continue_conversation = MagicMock(return_value=Future())
201+
mock_adapter.continue_conversation.return_value.set_result(Mock())
202+
203+
sut = self.create_skill_handler_for_testing(mock_adapter)
204+
205+
activity = Activity(type=ActivityTypes.message, attachments=[], entities=[])
206+
TurnContext.apply_conversation_reference(activity, self._conversation_reference)
207+
208+
await sut.test_on_send_to_conversation(
209+
self._claims_identity, self._conversation_id, activity
210+
)
211+
212+
args, kwargs = mock_adapter.continue_conversation.call_args_list[0]
213+
214+
assert isinstance(args[0], ConversationReference)
215+
assert callable(args[1])
216+
assert isinstance(kwargs["claims_identity"], ClaimsIdentity)
217+
218+
async def test_on_reply_to_activity(self):
219+
self._conversation_id = await self._test_id_factory.create_skill_conversation_id(
220+
self._conversation_reference
221+
)
222+
223+
mock_adapter = Mock()
224+
mock_adapter.continue_conversation = MagicMock(return_value=Future())
225+
mock_adapter.continue_conversation.return_value.set_result(Mock())
226+
227+
sut = self.create_skill_handler_for_testing(mock_adapter)
228+
229+
activity = Activity(type=ActivityTypes.message, attachments=[], entities=[])
230+
activity_id = str(uuid4())
231+
TurnContext.apply_conversation_reference(activity, self._conversation_reference)
232+
233+
await sut.test_on_reply_to_activity(
234+
self._claims_identity, self._conversation_id, activity_id, activity
235+
)
236+
237+
args, kwargs = mock_adapter.continue_conversation.call_args_list[0]
238+
239+
assert isinstance(args[0], ConversationReference)
240+
assert callable(args[1])
241+
assert isinstance(kwargs["claims_identity"], ClaimsIdentity)
242+
243+
async def test_on_update_activity(self):
244+
self._conversation_id = ""
245+
246+
mock_adapter = Mock()
247+
248+
sut = self.create_skill_handler_for_testing(mock_adapter)
249+
250+
activity = Activity(type=ActivityTypes.message, attachments=[], entities=[])
251+
activity_id = str(uuid4())
252+
253+
with self.assertRaises(BotActionNotImplementedError):
254+
await sut.test_on_update_activity(
255+
self._claims_identity, self._conversation_id, activity_id, activity
256+
)
257+
258+
async def test_on_delete_activity(self):
259+
self._conversation_id = ""
260+
261+
mock_adapter = Mock()
262+
263+
sut = self.create_skill_handler_for_testing(mock_adapter)
264+
activity_id = str(uuid4())
265+
266+
with self.assertRaises(BotActionNotImplementedError):
267+
await sut.test_on_delete_activity(
268+
self._claims_identity, self._conversation_id, activity_id
269+
)
270+
271+
async def test_on_get_activity_members(self):
272+
self._conversation_id = ""
273+
274+
mock_adapter = Mock()
275+
276+
sut = self.create_skill_handler_for_testing(mock_adapter)
277+
activity_id = str(uuid4())
278+
279+
with self.assertRaises(BotActionNotImplementedError):
280+
await sut.test_on_get_activity_members(
281+
self._claims_identity, self._conversation_id, activity_id
282+
)
283+
284+
async def test_on_create_conversation(self):
285+
mock_adapter = Mock()
286+
287+
sut = self.create_skill_handler_for_testing(mock_adapter)
288+
conversation_parameters = ConversationParameters()
289+
290+
with self.assertRaises(BotActionNotImplementedError):
291+
await sut.test_on_create_conversation(
292+
self._claims_identity, conversation_parameters
293+
)
294+
295+
async def test_on_get_conversations(self):
296+
self._conversation_id = ""
297+
298+
mock_adapter = Mock()
299+
300+
sut = self.create_skill_handler_for_testing(mock_adapter)
301+
302+
with self.assertRaises(BotActionNotImplementedError):
303+
await sut.test_on_get_conversations(
304+
self._claims_identity, self._conversation_id
305+
)
306+
307+
async def test_on_get_conversation_members(self):
308+
self._conversation_id = ""
309+
310+
mock_adapter = Mock()
311+
312+
sut = self.create_skill_handler_for_testing(mock_adapter)
313+
314+
with self.assertRaises(BotActionNotImplementedError):
315+
await sut.test_on_get_conversation_members(
316+
self._claims_identity, self._conversation_id
317+
)
318+
319+
async def test_on_get_conversation_paged_members(self):
320+
self._conversation_id = ""
321+
322+
mock_adapter = Mock()
323+
324+
sut = self.create_skill_handler_for_testing(mock_adapter)
325+
326+
with self.assertRaises(BotActionNotImplementedError):
327+
await sut.test_on_get_conversation_paged_members(
328+
self._claims_identity, self._conversation_id
329+
)
330+
331+
async def test_on_delete_conversation_member(self):
332+
self._conversation_id = ""
333+
334+
mock_adapter = Mock()
335+
336+
sut = self.create_skill_handler_for_testing(mock_adapter)
337+
member_id = str(uuid4())
338+
339+
with self.assertRaises(BotActionNotImplementedError):
340+
await sut.test_on_delete_conversation_member(
341+
self._claims_identity, self._conversation_id, member_id
342+
)
343+
344+
async def test_on_send_conversation_history(self):
345+
self._conversation_id = ""
346+
347+
mock_adapter = Mock()
348+
349+
sut = self.create_skill_handler_for_testing(mock_adapter)
350+
transcript = Transcript()
351+
352+
with self.assertRaises(BotActionNotImplementedError):
353+
await sut.test_on_send_conversation_history(
354+
self._claims_identity, self._conversation_id, transcript
355+
)
356+
357+
async def test_on_upload_attachment(self):
358+
self._conversation_id = ""
359+
360+
mock_adapter = Mock()
361+
362+
sut = self.create_skill_handler_for_testing(mock_adapter)
363+
attachment_data = AttachmentData()
364+
365+
with self.assertRaises(BotActionNotImplementedError):
366+
await sut.test_on_upload_attachment(
367+
self._claims_identity, self._conversation_id, attachment_data
368+
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import aiounittest
2+
from botbuilder.core import BotFrameworkHttpClient
3+
4+
5+
class TestBotFrameworkHttpClient(aiounittest.AsyncTestCase):
6+
async def test_should_create_connector_client(self):
7+
with self.assertRaises(TypeError):
8+
BotFrameworkHttpClient(None)

libraries/botframework-connector/setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"cryptography==2.8.0",
1212
"PyJWT==1.5.3",
1313
"botbuilder-schema>=4.4.0b1",
14+
"adal==1.2.1",
1415
]
1516

1617
root = os.path.abspath(os.path.dirname(__file__))

0 commit comments

Comments
 (0)