4
4
5
5
using System ;
6
6
using System . Collections . Generic ;
7
+ using System . Linq ;
7
8
using System . Security . Claims ;
8
9
using System . Threading ;
9
10
using System . Threading . Tasks ;
@@ -27,9 +28,38 @@ public class DialogManagerTests
27
28
// An App ID for a skill bot.
28
29
private readonly string _skillBotId = Guid . NewGuid ( ) . ToString ( ) ;
29
30
31
+ // Captures an EndOfConversation if it was sent to help with assertions.
32
+ private Activity _eocSent ;
33
+
30
34
// Property to capture the DialogManager turn results and do assertions.
31
35
private DialogManagerResult _dmTurnResult ;
32
36
37
+ /// <summary>
38
+ /// Enum to handle different skill test cases.
39
+ /// </summary>
40
+ public enum SkillFlowTestCase
41
+ {
42
+ /// <summary>
43
+ /// DialogManager is executing on a root bot with no skills (typical standalone bot).
44
+ /// </summary>
45
+ RootBotOnly ,
46
+
47
+ /// <summary>
48
+ /// DialogManager is executing on a root bot handling replies from a skill.
49
+ /// </summary>
50
+ RootBotConsumingSkill ,
51
+
52
+ /// <summary>
53
+ /// DialogManager is executing in a skill that is called from a root and calling another skill.
54
+ /// </summary>
55
+ MiddleSkill ,
56
+
57
+ /// <summary>
58
+ /// DialogManager is executing in a skill that is called from a parent (a root or another skill) but doesn't call another skill.
59
+ /// </summary>
60
+ LeafSkill
61
+ }
62
+
33
63
public TestContext TestContext { get ; set ; }
34
64
35
65
[ TestMethod ]
@@ -210,26 +240,35 @@ public async Task DialogManager_DialogSet()
210
240
}
211
241
212
242
[ TestMethod ]
213
- public async Task SkillSendsEoCAndValuesAtDialogEnd ( )
243
+ [ DataRow ( SkillFlowTestCase . RootBotOnly , false ) ]
244
+ [ DataRow ( SkillFlowTestCase . RootBotConsumingSkill , false ) ]
245
+ [ DataRow ( SkillFlowTestCase . MiddleSkill , true ) ]
246
+ [ DataRow ( SkillFlowTestCase . LeafSkill , true ) ]
247
+ public async Task HandlesBotAndSkillsTestCases ( SkillFlowTestCase testCase , bool shouldSendEoc )
214
248
{
215
249
var firstConversationId = Guid . NewGuid ( ) . ToString ( ) ;
216
250
var storage = new MemoryStorage ( ) ;
217
251
218
252
var adaptiveDialog = CreateTestDialog ( property : "conversation.name" ) ;
219
-
220
- await CreateFlow ( adaptiveDialog , storage , firstConversationId , isSkillFlow : true , locale : "en-GB" )
221
- . Send ( "hi" )
253
+ await CreateFlow ( adaptiveDialog , storage , firstConversationId , testCase : testCase , locale : "en-GB" ) . Send ( "Hi" )
222
254
. AssertReply ( "Hello, what is your name?" )
223
- . Send ( "Carlos" )
224
- . AssertReply ( "Hello Carlos, nice to meet you!" )
225
- . AssertReply ( activity =>
226
- {
227
- Assert . AreEqual ( activity . Type , ActivityTypes . EndOfConversation ) ;
228
- Assert . AreEqual ( ( ( Activity ) activity ) . Value , "Carlos" ) ;
229
- Assert . AreEqual ( ( ( Activity ) activity ) . Locale , "en-GB" ) ;
230
- } )
255
+ . Send ( "SomeName" )
256
+ . AssertReply ( "Hello SomeName, nice to meet you!" )
231
257
. StartTestAsync ( ) ;
258
+
232
259
Assert . AreEqual ( DialogTurnStatus . Complete , _dmTurnResult . TurnResult . Status ) ;
260
+
261
+ if ( shouldSendEoc )
262
+ {
263
+ Assert . IsNotNull ( _eocSent , "Skills should send EndConversation to channel" ) ;
264
+ Assert . AreEqual ( ActivityTypes . EndOfConversation , _eocSent . Type ) ;
265
+ Assert . AreEqual ( "SomeName" , _eocSent . Value ) ;
266
+ Assert . AreEqual ( "en-GB" , _eocSent . Locale ) ;
267
+ }
268
+ else
269
+ {
270
+ Assert . IsNull ( _eocSent , "Root bot should not send EndConversation to channel" ) ;
271
+ }
233
272
}
234
273
235
274
[ TestMethod ]
@@ -242,7 +281,7 @@ public async Task SkillHandlesEoCFromParent()
242
281
243
282
var eocActivity = new Activity ( ActivityTypes . EndOfConversation ) ;
244
283
245
- await CreateFlow ( adaptiveDialog , storage , firstConversationId , isSkillFlow : true , isSkillResponse : false )
284
+ await CreateFlow ( adaptiveDialog , storage , firstConversationId , testCase : SkillFlowTestCase . LeafSkill )
246
285
. Send ( "hi" )
247
286
. AssertReply ( "Hello, what is your name?" )
248
287
. Send ( eocActivity )
@@ -261,7 +300,7 @@ public async Task SkillHandlesRepromptFromParent()
261
300
262
301
var repromptEvent = new Activity ( ActivityTypes . Event ) { Name = DialogEvents . RepromptDialog } ;
263
302
264
- await CreateFlow ( adaptiveDialog , storage , firstConversationId , isSkillFlow : true )
303
+ await CreateFlow ( adaptiveDialog , storage , firstConversationId , testCase : SkillFlowTestCase . LeafSkill )
265
304
. Send ( "hi" )
266
305
. AssertReply ( "Hello, what is your name?" )
267
306
. Send ( repromptEvent )
@@ -281,7 +320,7 @@ public async Task SkillShouldReturnEmptyOnRepromptWithNoDialog()
281
320
282
321
var repromptEvent = new Activity ( ActivityTypes . Event ) { Name = DialogEvents . RepromptDialog } ;
283
322
284
- await CreateFlow ( adaptiveDialog , storage , firstConversationId , isSkillFlow : true )
323
+ await CreateFlow ( adaptiveDialog , storage , firstConversationId , testCase : SkillFlowTestCase . LeafSkill )
285
324
. Send ( repromptEvent )
286
325
. StartTestAsync ( ) ;
287
326
@@ -293,7 +332,7 @@ private Dialog CreateTestDialog(string property)
293
332
return new AskForNameDialog ( property . Replace ( "." , string . Empty ) , property ) ;
294
333
}
295
334
296
- private TestFlow CreateFlow ( Dialog dialog , IStorage storage , string conversationId , string dialogStateProperty = null , bool isSkillFlow = false , bool isSkillResponse = true , string locale = null )
335
+ private TestFlow CreateFlow ( Dialog dialog , IStorage storage , string conversationId , string dialogStateProperty = null , SkillFlowTestCase testCase = SkillFlowTestCase . RootBotOnly , string locale = null )
297
336
{
298
337
var convoState = new ConversationState ( storage ) ;
299
338
var userState = new UserState ( storage ) ;
@@ -312,7 +351,7 @@ private TestFlow CreateFlow(Dialog dialog, IStorage storage, string conversation
312
351
var dm = new DialogManager ( dialog , dialogStateProperty : dialogStateProperty ) ;
313
352
return new TestFlow ( adapter , async ( turnContext , cancellationToken ) =>
314
353
{
315
- if ( isSkillFlow )
354
+ if ( testCase != SkillFlowTestCase . RootBotOnly )
316
355
{
317
356
// Create a skill ClaimsIdentity and put it in TurnState so SkillValidation.IsSkillClaim() returns true.
318
357
var claimsIdentity = new ClaimsIdentity ( ) ;
@@ -321,14 +360,28 @@ private TestFlow CreateFlow(Dialog dialog, IStorage storage, string conversation
321
360
claimsIdentity . AddClaim ( new Claim ( AuthenticationConstants . AuthorizedParty , _parentBotId ) ) ;
322
361
turnContext . TurnState . Add ( BotAdapter . BotIdentityKey , claimsIdentity ) ;
323
362
324
- if ( isSkillResponse )
363
+ if ( testCase == SkillFlowTestCase . RootBotConsumingSkill )
364
+ {
365
+ // Simulate the SkillConversationReference with a channel OAuthScope stored in TurnState.
366
+ // This emulates a response coming to a root bot through SkillHandler.
367
+ turnContext . TurnState . Add ( SkillHandler . SkillConversationReferenceKey , new SkillConversationReference { OAuthScope = AuthenticationConstants . ToChannelFromBotOAuthScope } ) ;
368
+ }
369
+
370
+ if ( testCase == SkillFlowTestCase . MiddleSkill )
325
371
{
326
372
// Simulate the SkillConversationReference with a parent Bot ID stored in TurnState.
327
373
// This emulates a response coming to a skill from another skill through SkillHandler.
328
374
turnContext . TurnState . Add ( SkillHandler . SkillConversationReferenceKey , new SkillConversationReference { OAuthScope = _parentBotId } ) ;
329
375
}
330
376
}
331
377
378
+ // Interceptor to capture the EoC activity if it was sent so we can assert it in the tests.
379
+ turnContext . OnSendActivities ( async ( tc , activities , next ) =>
380
+ {
381
+ _eocSent = activities . FirstOrDefault ( activity => activity . Type == ActivityTypes . EndOfConversation ) ;
382
+ return await next ( ) . ConfigureAwait ( false ) ;
383
+ } ) ;
384
+
332
385
// Capture the last DialogManager turn result for assertions.
333
386
_dmTurnResult = await dm . OnTurnAsync ( turnContext , cancellationToken : cancellationToken ) . ConfigureAwait ( false ) ;
334
387
} ) ;
@@ -368,7 +421,7 @@ public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext oute
368
421
Text = "Hello, what is your name?"
369
422
}
370
423
} ,
371
- cancellationToken : cancellationToken )
424
+ cancellationToken )
372
425
. ConfigureAwait ( false ) ;
373
426
}
374
427
0 commit comments