//TODO 切换CardManager状态 private void SwitchState() { if (currentState == CardManagerState.Init) { currentState = CardManagerState.Run; } else if (currentState == CardManagerState.Run) { currentState = CardManagerState.Init; } }
public async Task TestUnsaveActivityAsync() { const string ACTIVITYID1 = "activity ID 1"; const string ACTIVITYID2 = "activity ID 2"; var manager = CreateManager(); var turnContext = CreateTurnContext(); var state = new CardManagerState(); var activity1 = new Activity(id: ACTIVITYID1); var activity2 = new Activity(id: ACTIVITYID2); var activity3 = new Activity(id: ACTIVITYID2); await manager.StateAccessor.SetAsync(turnContext, state); await manager.UnsaveActivityAsync(turnContext, ACTIVITYID1); Assert.AreEqual(0, state.SavedActivities.Count); state.SavedActivities.Add(activity1); state.SavedActivities.Add(activity2); state.SavedActivities.Add(activity3); Assert.AreEqual(3, state.SavedActivities.Count); await manager.UnsaveActivityAsync(turnContext, ACTIVITYID1); Assert.AreEqual(2, state.SavedActivities.Count, "Did not remove one activity"); await manager.UnsaveActivityAsync(turnContext, ACTIVITYID2); Assert.AreEqual(0, state.SavedActivities.Count, "Did not remove two activities"); await Assert.ThrowsExceptionAsync <ArgumentNullException>(async() => await manager.UnsaveActivityAsync(turnContext, null)); await Assert.ThrowsExceptionAsync <ArgumentNullException>(async() => await manager.UnsaveActivityAsync(null, "ID")); }
public async Task TestDeleteAsync() { const string ACTIONID1 = "action ID 1"; const string ACTIONID2 = "action ID 2"; const string ACTIONID3 = "action ID 3"; const string ACTIONID4 = "action ID 4"; const string ACTIONID5 = "action ID 5"; const string ACTIONID6 = "action ID 6"; const string CARDID = "card ID"; const string CAROUSELID = "carousel ID"; const string BATCHID = "batch ID"; const string CHOICEINPUTID = "choice set input ID"; const string DATEINPUTID = "date input ID"; var manager = CreateManager(); var turnContext = CreateTurnContext(); var state = new CardManagerState(); var updated = false; var deletedCount = 0; var activity1 = new Activity { Id = "activity ID 1", Attachments = new List <Attachment> { // This attachment should be deleted after both actions in the actions property are deleted // despite having a select action new Attachment { Name = "0,0", ContentType = AdaptiveCard.ContentType, Content = new AdaptiveCard("1.0") { Actions = new List <AdaptiveAction> { new AdaptiveSubmitAction { Data = new Dictionary <string, string> { { DataIdScopes.Action, ACTIONID1 }, }.WrapLibraryData(), }, new AdaptiveSubmitAction { Data = new Dictionary <string, string> { { DataIdScopes.Action, ACTIONID2 }, }.WrapLibraryData(), }, }, SelectAction = new AdaptiveSubmitAction { Data = new Dictionary <string, string> { { DataIdScopes.Action, "Irrelevant action ID" }, }.WrapLibraryData(), }, }, }, // When this attachment is deleted, the whole activity should be deleted new Attachment { Name = "0,1", ContentType = HeroCard.ContentType, Content = new HeroCard(buttons: new List <CardAction> { new CardAction(ActionTypes.PostBack, value: new Dictionary <string, string> { { DataIdScopes.Action, ACTIONID3 }, }.WrapLibraryData()), new CardAction(ActionTypes.MessageBack, value: new Dictionary <string, string> { { DataIdScopes.Action, ACTIONID4 }, }.WrapLibraryData()), }), }, } }; var activity2 = new Activity { Id = "activity ID 2", Attachments = new List <Attachment> { // This attachment shouldn't be deleted because of its body new Attachment { Name = "1,0", ContentType = AdaptiveCard.ContentType, Content = new AdaptiveCard("1.2") { Body = new List <AdaptiveElement> { new AdaptiveChoiceSetInput { Id = CHOICEINPUTID, }, }, Actions = new List <AdaptiveAction> { new AdaptiveSubmitAction { Data = new Dictionary <string, string> { { DataIdScopes.Action, ACTIONID5 }, }.WrapLibraryData(), }, }, }, }, // This attachment shouldn't be deleted because of its title new Attachment { Name = "1,1", ContentType = HeroCard.ContentType, Content = new HeroCard(title: "Irrelevant title", buttons: new List <CardAction> { new CardAction(ActionTypes.PostBack, value: new Dictionary <string, string> { { DataIdScopes.Action, ACTIONID6 }, }.WrapLibraryData()), }), }, }, }; var activity3 = new Activity { Id = "activity ID 3", Attachments = new List <Attachment> { // When this attachment is deleted, the activity won't be deleted // but it should be removed from state new Attachment { Name = "2,0", ContentType = AdaptiveCard.ContentType, Content = new AdaptiveCard("1.1") { Body = new List <AdaptiveElement> { new AdaptiveDateInput { Id = DATEINPUTID, }, }, Actions = new List <AdaptiveAction> { new AdaptiveSubmitAction { Data = new Dictionary <string, string> { // Even though a carousel ID is used to identify an activity, // it can still be used to delete a single attachment { DataIdScopes.Carousel, CAROUSELID }, }.WrapLibraryData(), }, }, }, }, new Attachment { Name = "2,1", }, }, }; var activity4 = new Activity { Id = "activity ID 4", Attachments = new List <Attachment> { new Attachment { Name = "3,0", ContentType = HeroCard.ContentType, Content = new HeroCard(title: "Irrelevant title", buttons: new List <CardAction> { new CardAction(ActionTypes.MessageBack, value: new Dictionary <string, string> { // Even though a card ID is used to identify a single attachment, // it can still be used to delete an activity { DataIdScopes.Card, CARDID }, }.WrapLibraryData()), }), }, new Attachment { Name = "3,1", }, }, }; var activity5 = new Activity { Id = "activity ID 5", Attachments = new List <Attachment> { new Attachment { Name = "4,0", ContentType = HeroCard.ContentType, Content = new HeroCard(title: "Irrelevant title", buttons: new List <CardAction> { new CardAction(ActionTypes.PostBack, value: new Dictionary <string, string> { { DataIdScopes.Batch, BATCHID }, }.WrapLibraryData()), }), }, new Attachment { Name = "4,1", ContentType = AdaptiveCard.ContentType, Content = new AdaptiveCard("1.0") { Actions = new List <AdaptiveAction> { new AdaptiveSubmitAction { Data = new Dictionary <string, string> { { DataIdScopes.Action, "Another irrelevant action ID" }, { DataIdScopes.Card, "Irrelevant card ID" }, { DataIdScopes.Carousel, "Irrelevant carousel ID" }, { DataIdScopes.Batch, BATCHID }, }.WrapLibraryData(), }, }, }, }, }, }; var activity6 = new Activity { Id = "activity ID 6", Attachments = new List <Attachment> { // Not all attachments need to have the batch ID new Attachment { Name = "5,0", ContentType = AdaptiveCard.ContentType, Content = new AdaptiveCard("1.1") { Actions = new List <AdaptiveAction> { new AdaptiveSubmitAction { Data = new Dictionary <string, string> { { DataIdScopes.Action, "Yet another irrelevant action ID" }, }.WrapLibraryData(), }, }, }, }, // As long as one attachment contains the batch ID, // the activity will be included in the batch new Attachment { Name = "5,1", ContentType = HeroCard.ContentType, Content = new HeroCard(title: "Irrelevant title", buttons: new List <CardAction> { new CardAction(ActionTypes.MessageBack, value: new Dictionary <string, string> { { DataIdScopes.Batch, BATCHID }, }.WrapLibraryData()), }), }, }, }; var activity7 = new Activity { Id = "activity ID 7", Attachments = new List <Attachment> { new Attachment { Name = "6,0", ContentType = HeroCard.ContentType, Content = new HeroCard(buttons: new List <CardAction> { new CardAction(ActionTypes.PostBack, value: new Dictionary <string, string> { { DataIdScopes.Batch, "Irrelevant batch ID" }, }.WrapLibraryData()), }), }, }, }; var activity8 = new Activity { Id = "Irrelevant activity ID", }; // We need to use the active queue or else the test adapter will remove the ID // of each activity not in the queue when it tries to update them var queue = ((TestAdapter)turnContext.Adapter).ActiveQueue; queue.Enqueue(activity1); queue.Enqueue(activity2); queue.Enqueue(activity3); queue.Enqueue(activity4); queue.Enqueue(activity5); queue.Enqueue(activity6); queue.Enqueue(activity7); // The first 7 activities should be saved in state as well as the active queue state.SavedActivities.UnionWith(queue); // But this one shouldn't be queue.Enqueue(activity8); var expectedActivities = new[] { activity1 }; turnContext.OnUpdateActivity((turnContext, activity, next) => { Assert.AreSame(expectedActivities.Single(), activity); Assert.IsFalse(updated, "UpdateActivityAsync was called an extra time"); updated = true; return(next()); }); turnContext.OnDeleteActivity((turnContext, reference, next) => { Assert.IsTrue(expectedActivities.Any(activity => activity.Id == reference.ActivityId)); deletedCount++; return(next()); }); turnContext.Activity.Value = new Dictionary <string, object> { // This shouldn't match any action data { DataIdScopes.Card, ACTIONID1 }, }.WrapLibraryData(); await manager.StateAccessor.SetAsync(turnContext, state); await manager.DeleteActionSourceAsync(turnContext, DataIdScopes.Action); Assert.IsFalse(updated); Assert.AreEqual(0, deletedCount); Assert.IsFalse(state.SavedActivities.Contains(activity8)); Assert.AreEqual(7, state.SavedActivities.Count); Assert.IsTrue(queue.Contains(activity8)); Assert.AreEqual(8, queue.Count()); turnContext.Activity.Value = new Dictionary <string, object> { { DataIdScopes.Action, ACTIONID1 }, }.WrapLibraryData(); await manager.DeleteActionSourceAsync(turnContext, DataIdScopes.Action); var actionId = ((JObject)((AdaptiveSubmitAction)((AdaptiveCard)activity1.Attachments[0].Content) .Actions.Single()).Data).GetIdFromActionData(DataIdScopes.Action); Assert.IsTrue(updated); Assert.AreEqual(0, deletedCount); Assert.AreEqual(ACTIONID2, actionId, "Wrong action was deleted"); Assert.AreEqual(7, state.SavedActivities.Count); Assert.AreEqual(8, queue.Count()); updated = false; turnContext.Activity.Value = new Dictionary <string, object> { { DataIdScopes.Action, ACTIONID2 }, }.WrapLibraryData(); await manager.DeleteActionSourceAsync(turnContext, DataIdScopes.Action); Assert.IsTrue(updated); Assert.AreEqual(0, deletedCount); Assert.IsInstanceOfType(activity1.Attachments.Single().Content, typeof(HeroCard), "Card wasn't deleted"); Assert.AreEqual(7, state.SavedActivities.Count); Assert.AreEqual(8, queue.Count()); updated = false; turnContext.Activity.Value = new Dictionary <string, object> { { DataIdScopes.Action, ACTIONID3 }, }.WrapLibraryData(); await manager.DeleteActionSourceAsync(turnContext, DataIdScopes.Action); actionId = ((Dictionary <string, string>)((Dictionary <string, object>)((HeroCard)activity1.Attachments.Single().Content) .Buttons.Single().Value)[PropertyNames.LibraryData])[DataIdScopes.Action]; Assert.IsTrue(updated); Assert.AreEqual(0, deletedCount); Assert.AreEqual(ACTIONID4, actionId, "Wrong action was deleted"); Assert.AreEqual(7, state.SavedActivities.Count); Assert.AreEqual(8, queue.Count()); updated = false; turnContext.Activity.Value = new Dictionary <string, object> { { DataIdScopes.Action, ACTIONID4 }, }.WrapLibraryData(); await manager.DeleteActionSourceAsync(turnContext, DataIdScopes.Action); Assert.IsFalse(updated); Assert.AreEqual(1, deletedCount, "Activity wasn't deleted even though it was empty"); Assert.IsFalse(state.SavedActivities.Contains(activity1), "Activity wasn't removed from state"); Assert.AreEqual(6, state.SavedActivities.Count); Assert.IsFalse(queue.Contains(activity1), "Deleted activity not removed from the queue"); Assert.AreEqual(7, queue.Count(), "Deleted activity not removed from the queue"); deletedCount = 0; expectedActivities = new[] { activity2 }; turnContext.Activity.Value = new Dictionary <string, object> { { CHOICEINPUTID, "User-entered choice" }, { PropertyNames.LibraryData, new Dictionary <string, string> { { DataIdScopes.Action, ACTIONID5 }, } }, }; await manager.DeleteActionSourceAsync(turnContext, DataIdScopes.Action); Assert.IsTrue(updated); Assert.AreEqual(0, deletedCount); Assert.IsFalse(((AdaptiveCard)activity2.Attachments[0].Content).Actions.Any(), "Action wasn't deleted"); Assert.AreEqual(6, state.SavedActivities.Count); Assert.AreEqual(7, queue.Count()); updated = false; turnContext.Activity.Value = new Dictionary <string, object> { { DataIdScopes.Action, ACTIONID6 }, }.WrapLibraryData(); await manager.DeleteActionSourceAsync(turnContext, DataIdScopes.Action); Assert.IsTrue(updated); Assert.AreEqual(0, deletedCount, "Activity was deleted even though it still had attachments"); Assert.IsFalse(((AdaptiveCard)activity2.Attachments[0].Content).Actions.Any(), "Action wasn't deleted"); Assert.IsFalse(state.SavedActivities.Contains(activity2), "Activity wasn't removed from state"); Assert.AreEqual(5, state.SavedActivities.Count, "Saved activities weren't cleaned correctly"); Assert.IsTrue(queue.Contains(activity2), "Cleaned activity removed from the queue"); Assert.AreEqual(7, queue.Count(), "Cleaned activity removed from the queue"); updated = false; expectedActivities = new[] { activity3 }; turnContext.Activity.Value = new Dictionary <string, object> { { DATEINPUTID, "User-entered date" }, { PropertyNames.LibraryData, new Dictionary <string, string> { { DataIdScopes.Carousel, CAROUSELID }, } }, }; // We are deleting the card using a carousel ID await manager.DeleteActionSourceAsync(turnContext, DataIdScopes.Card); Assert.IsTrue(updated); Assert.AreEqual(0, deletedCount, "Activity was deleted even though it still had attachments"); Assert.AreEqual(1, activity3.Attachments.Count(), "Attachment wasn't deleted"); Assert.IsFalse(state.SavedActivities.Contains(activity3), "Activity wasn't removed from state"); Assert.AreEqual(4, state.SavedActivities.Count, "Saved activities weren't cleaned correctly"); Assert.IsTrue(queue.Contains(activity3), "Cleaned activity removed from the queue"); Assert.AreEqual(7, queue.Count(), "Cleaned activity removed from the queue"); updated = false; expectedActivities = new[] { activity4 }; turnContext.Activity.Value = new Dictionary <string, object> { { DataIdScopes.Card, CARDID }, }.WrapLibraryData(); // We are deleting the carousel using a card ID await manager.DeleteActionSourceAsync(turnContext, DataIdScopes.Carousel); Assert.IsFalse(updated); Assert.AreEqual(1, deletedCount); Assert.IsFalse(state.SavedActivities.Contains(activity4), "Activity wasn't removed from state"); Assert.AreEqual(3, state.SavedActivities.Count, "Saved activities weren't cleaned correctly"); Assert.IsFalse(queue.Contains(activity4), "Deleted activity not removed from the queue"); Assert.AreEqual(6, queue.Count(), "Deleted activity not removed from the queue"); deletedCount = 0; expectedActivities = new[] { activity5, activity6 }; turnContext.Activity.Value = new Dictionary <string, object> { { DataIdScopes.Batch, BATCHID }, }.WrapLibraryData(); // We are deleting the carousel using a card ID await manager.DeleteActionSourceAsync(turnContext, DataIdScopes.Batch); Assert.IsFalse(updated); Assert.AreEqual(2, deletedCount); Assert.AreEqual(activity7, state.SavedActivities.Single(), "Saved activities weren't cleaned correctly"); Assert.IsFalse(queue.Contains(activity5), "Activity 5 not removed from the queue"); Assert.IsFalse(queue.Contains(activity6), "Activity 6 activity not removed from the queue"); Assert.AreEqual(4, queue.Count(), "Deleted activities not removed from the queue"); await Assert.ThrowsExceptionAsync <ArgumentNullException>(async() => await manager.DeleteActionSourceAsync(null, DataIdScopes.Action)); await Assert.ThrowsExceptionAsync <ArgumentNullException>(async() => await manager.DeleteActionSourceAsync(turnContext, string.Empty)); }