From a5fc2b772596724384abd41eecb2573bc5b98d1b Mon Sep 17 00:00:00 2001 From: Tracy Boehrer Date: Fri, 20 Sep 2019 16:43:47 -0500 Subject: [PATCH] Added TelemetryMiddlewareTests and related corrections. --- STATUS.md | 28 + libraries/bot-builder/pom.xml | 4 + .../com/microsoft/bot/builder/BotAdapter.java | 2 +- .../bot/builder/OnTurnErrorHandler.java | 4 +- .../builder/TelemetryLoggerMiddleware.java | 4 + .../bot/builder/OnTurnErrorTests.java | 44 ++ .../bot/builder/TelemetryMiddlewareTests.java | 665 ++++++++++++++++++ .../bot/builder/TranscriptMiddlewareTest.java | 1 + pom.xml | 7 + 9 files changed, 757 insertions(+), 2 deletions(-) create mode 100644 STATUS.md create mode 100644 libraries/bot-builder/src/test/java/com/microsoft/bot/builder/OnTurnErrorTests.java create mode 100644 libraries/bot-builder/src/test/java/com/microsoft/bot/builder/TelemetryMiddlewareTests.java diff --git a/STATUS.md b/STATUS.md new file mode 100644 index 00000000..a3fb4635 --- /dev/null +++ b/STATUS.md @@ -0,0 +1,28 @@ +# Java Bot Framework + +## Status +The current release is **Preview 2**. + +| Package | Status +| ------------- |:------------- +| bot-schema | Preview 2 +| bot-connector | Preview 2 +| bot-integration-core | Preview 2 +| Servlet Sample | Preview 2 +| Spring Boot Sample | Preview 2 +| bot-builder | Preview 3 +| Teams Support | Possible Preview 3 +| bot-dialog | Incomplete +| bot-ai-luis-v3 | Not Started +| bot-ai-qna | Not Started +| bot-applicationinsights | Not Started +| bot-azure | Not Started +| bot-configuration | Not Started +| BotBuilder-Samples | Not Started + +## Build Prerequisites + +- [Java 1.8](https://docs.microsoft.com/en-us/azure/java/jdk/java-jdk-install) + - Should be able to execute `java -version` from command line. +- [Maven](https://maven.apache.org/install.html) + - Should be able to execute `mvn -version` from command line. diff --git a/libraries/bot-builder/pom.xml b/libraries/bot-builder/pom.xml index 1f02cbcb..2d40bf5e 100644 --- a/libraries/bot-builder/pom.xml +++ b/libraries/bot-builder/pom.xml @@ -54,6 +54,10 @@ org.slf4j slf4j-api + + org.mockito + mockito-core + com.microsoft.rest diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/BotAdapter.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/BotAdapter.java index 9cbcb726..167b3ee4 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/BotAdapter.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/BotAdapter.java @@ -145,7 +145,7 @@ public abstract class BotAdapter { return middlewareSet.receiveActivityWithStatus(context, callback) .exceptionally(exception -> { if (onTurnError != null) { - return onTurnError.invoke(context, exception); + return onTurnError.invoke(context, exception).join(); } throw new CompletionException(exception); diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/OnTurnErrorHandler.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/OnTurnErrorHandler.java index c370508d..c8a50522 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/OnTurnErrorHandler.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/OnTurnErrorHandler.java @@ -1,5 +1,7 @@ package com.microsoft.bot.builder; +import java.util.concurrent.CompletableFuture; + @FunctionalInterface public interface OnTurnErrorHandler { /** @@ -9,5 +11,5 @@ public interface OnTurnErrorHandler { * @param exception The exception thrown. * @return A task that represents the work queued to execute. */ - Void invoke(TurnContext turnContext, Throwable exception); + CompletableFuture invoke(TurnContext turnContext, Throwable exception); } diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/TelemetryLoggerMiddleware.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/TelemetryLoggerMiddleware.java index 66363612..12352380 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/TelemetryLoggerMiddleware.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/TelemetryLoggerMiddleware.java @@ -37,6 +37,10 @@ public class TelemetryLoggerMiddleware implements Middleware { logPersonalInformation = withLogPersonalInformation; } + public BotTelemetryClient getTelemetryClient() { + return telemetryClient; + } + /** * Logs events based on incoming and outgoing activities using the {@link BotTelemetryClient} interface. * diff --git a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/OnTurnErrorTests.java b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/OnTurnErrorTests.java new file mode 100644 index 00000000..5cc4018f --- /dev/null +++ b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/OnTurnErrorTests.java @@ -0,0 +1,44 @@ +package com.microsoft.bot.builder; + +import com.microsoft.bot.builder.adapters.TestAdapter; +import com.microsoft.bot.builder.adapters.TestFlow; +import org.apache.commons.lang3.NotImplementedException; +import org.apache.commons.lang3.StringUtils; +import org.junit.Test; + +import java.util.concurrent.CompletableFuture; + +public class OnTurnErrorTests { + @Test + public void OnTurnError_Test() { + TestAdapter adapter = new TestAdapter(); + adapter.setOnTurnError(((turnContext, exception) -> { + if (exception instanceof NotImplementedException) { + return turnContext.sendActivity(turnContext.getActivity().createReply(exception.getMessage())) + .thenApply(resourceResponse -> null); + } else { + return turnContext.sendActivity("Unexpected exception") + .thenApply(resourceResponse -> null); + } + })); + + new TestFlow(adapter, (turnContext -> { + if (StringUtils.equals(turnContext.getActivity().getText(), "foo")) { + turnContext.sendActivity(turnContext.getActivity().getText()); + } + + if (StringUtils.equals(turnContext.getActivity().getText(), "NotImplementedException")) { + CompletableFuture result = new CompletableFuture<>(); + result.completeExceptionally(new NotImplementedException("Test")); + return result; + } + + return CompletableFuture.completedFuture(null); + })) + .send("foo") + .assertReply("foo", "passthrough") + .send("NotImplementedException") + .assertReply("Test") + .startTest(); + } +} diff --git a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/TelemetryMiddlewareTests.java b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/TelemetryMiddlewareTests.java new file mode 100644 index 00000000..7e363d32 --- /dev/null +++ b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/TelemetryMiddlewareTests.java @@ -0,0 +1,665 @@ +package com.microsoft.bot.builder; + +import com.microsoft.bot.builder.adapters.TestAdapter; +import com.microsoft.bot.builder.adapters.TestFlow; +import com.microsoft.bot.schema.Activity; +import com.microsoft.bot.schema.ActivityTypes; +import com.microsoft.bot.schema.ResourceResponse; +import org.apache.commons.lang3.StringUtils; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class TelemetryMiddlewareTests { + @Captor + ArgumentCaptor eventNameCaptor; + + @Captor + ArgumentCaptor> propertiesCaptor; + + @Test + public void Telemetry_NullTelemetryClient() { + TelemetryLoggerMiddleware logger = new TelemetryLoggerMiddleware(null, true); + Assert.assertNotNull(logger.getTelemetryClient()); + } + + + @Test + public void Telemetry_LogActivities() { + BotTelemetryClient mockTelemetryClient = mock(BotTelemetryClient.class); + TestAdapter adapter = new TestAdapter() + .use(new TelemetryLoggerMiddleware(mockTelemetryClient, true)); + + String[] conversationId = new String[]{null}; + new TestFlow(adapter, (turnContext -> { + conversationId[0] = turnContext.getActivity().getConversation().getId(); + turnContext.sendActivity(new Activity() {{ + setType(ActivityTypes.TYPING); + setRelatesTo(turnContext.getActivity().getRelatesTo()); + }}).join(); + turnContext.sendActivity("echo:" + turnContext.getActivity().getText()).join(); + return CompletableFuture.completedFuture(null); + })) + .send("foo") + .assertReply(activity -> { + Assert.assertEquals(activity.getType(), ActivityTypes.TYPING); + return null; + }) + .assertReply("echo:foo") + .send("bar") + .assertReply(activity -> { + Assert.assertEquals(activity.getType(), ActivityTypes.TYPING); + return null; + }) + .assertReply("echo:bar") + .startTest(); + + // verify BotTelemetryClient was invoked 6 times, and capture arguments. + verify(mockTelemetryClient, times(6)).trackEvent(eventNameCaptor.capture(), propertiesCaptor.capture()); + List eventNames = eventNameCaptor.getAllValues(); + List> properties = propertiesCaptor.getAllValues(); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGRECEIVEEVENT, eventNames.get(0)); + Assert.assertEquals(7, properties.get(0).size()); + Assert.assertTrue(properties.get(0).containsKey("fromId")); + Assert.assertTrue(properties.get(0).containsKey("conversationName")); + Assert.assertTrue(properties.get(0).containsKey("locale")); + Assert.assertTrue(properties.get(0).containsKey("recipientId")); + Assert.assertTrue(properties.get(0).containsKey("recipientName")); + Assert.assertTrue(properties.get(0).containsKey("fromName")); + Assert.assertTrue(properties.get(0).containsKey("text")); + Assert.assertTrue(StringUtils.equals(properties.get(0).get("text"), "foo")); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGSENDEVENT, eventNames.get(1)); + Assert.assertEquals(5, properties.get(1).size()); + Assert.assertTrue(properties.get(1).containsKey("replyActivityId")); + Assert.assertTrue(properties.get(1).containsKey("recipientId")); + Assert.assertTrue(properties.get(1).containsKey("conversationName")); + Assert.assertTrue(properties.get(1).containsKey("locale")); + Assert.assertTrue(properties.get(1).containsKey("recipientName")); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGSENDEVENT, eventNames.get(2)); + Assert.assertEquals(6, properties.get(2).size()); + Assert.assertTrue(properties.get(2).containsKey("replyActivityId")); + Assert.assertTrue(properties.get(2).containsKey("recipientId")); + Assert.assertTrue(properties.get(2).containsKey("conversationName")); + Assert.assertTrue(properties.get(2).containsKey("locale")); + Assert.assertTrue(properties.get(2).containsKey("recipientName")); + Assert.assertTrue(properties.get(2).containsKey("text")); + Assert.assertTrue(StringUtils.equals(properties.get(2).get("text"), "echo:foo")); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGRECEIVEEVENT, eventNames.get(3)); + Assert.assertEquals(7, properties.get(3).size()); + Assert.assertTrue(properties.get(3).containsKey("fromId")); + Assert.assertTrue(properties.get(3).containsKey("conversationName")); + Assert.assertTrue(properties.get(3).containsKey("locale")); + Assert.assertTrue(properties.get(3).containsKey("recipientId")); + Assert.assertTrue(properties.get(3).containsKey("recipientName")); + Assert.assertTrue(properties.get(3).containsKey("fromName")); + Assert.assertTrue(properties.get(3).containsKey("text")); + Assert.assertTrue(StringUtils.equals(properties.get(3).get("text"), "bar")); + } + + @Test + public void Telemetry_NoPII() { + BotTelemetryClient mockTelemetryClient = mock(BotTelemetryClient.class); + TestAdapter adapter = new TestAdapter() + .use(new TelemetryLoggerMiddleware(mockTelemetryClient, false)); + + String[] conversationId = new String[]{null}; + new TestFlow(adapter, (turnContext -> { + conversationId[0] = turnContext.getActivity().getConversation().getId(); + turnContext.sendActivity(new Activity() {{ + setType(ActivityTypes.TYPING); + setRelatesTo(turnContext.getActivity().getRelatesTo()); + }}).join(); + turnContext.sendActivity("echo:" + turnContext.getActivity().getText()).join(); + return CompletableFuture.completedFuture(null); + })) + .send("foo") + .assertReply(activity -> { + Assert.assertEquals(activity.getType(), ActivityTypes.TYPING); + return null; + }) + .assertReply("echo:foo") + .send("bar") + .assertReply(activity -> { + Assert.assertEquals(activity.getType(), ActivityTypes.TYPING); + return null; + }) + .assertReply("echo:bar") + .startTest(); + + // verify BotTelemetryClient was invoked 6 times, and capture arguments. + verify(mockTelemetryClient, times(6)).trackEvent(eventNameCaptor.capture(), propertiesCaptor.capture()); + List eventNames = eventNameCaptor.getAllValues(); + List> properties = propertiesCaptor.getAllValues(); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGRECEIVEEVENT, eventNames.get(0)); + Assert.assertEquals(5, properties.get(0).size()); + Assert.assertTrue(properties.get(0).containsKey("fromId")); + Assert.assertTrue(properties.get(0).containsKey("conversationName")); + Assert.assertTrue(properties.get(0).containsKey("locale")); + Assert.assertTrue(properties.get(0).containsKey("recipientId")); + Assert.assertTrue(properties.get(0).containsKey("recipientName")); + Assert.assertFalse(properties.get(0).containsKey("fromName")); + Assert.assertFalse(properties.get(0).containsKey("text")); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGSENDEVENT, eventNames.get(1)); + Assert.assertEquals(4, properties.get(1).size()); + Assert.assertTrue(properties.get(1).containsKey("replyActivityId")); + Assert.assertTrue(properties.get(1).containsKey("recipientId")); + Assert.assertTrue(properties.get(1).containsKey("conversationName")); + Assert.assertTrue(properties.get(1).containsKey("locale")); + Assert.assertFalse(properties.get(1).containsKey("recipientName")); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGSENDEVENT, eventNames.get(2)); + Assert.assertEquals(4, properties.get(2).size()); + Assert.assertTrue(properties.get(2).containsKey("replyActivityId")); + Assert.assertTrue(properties.get(2).containsKey("recipientId")); + Assert.assertTrue(properties.get(2).containsKey("conversationName")); + Assert.assertTrue(properties.get(2).containsKey("locale")); + Assert.assertFalse(properties.get(2).containsKey("recipientName")); + Assert.assertFalse(properties.get(2).containsKey("text")); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGRECEIVEEVENT, eventNames.get(3)); + Assert.assertEquals(5, properties.get(3).size()); + Assert.assertTrue(properties.get(3).containsKey("fromId")); + Assert.assertTrue(properties.get(3).containsKey("conversationName")); + Assert.assertTrue(properties.get(3).containsKey("locale")); + Assert.assertTrue(properties.get(3).containsKey("recipientId")); + Assert.assertTrue(properties.get(3).containsKey("recipientName")); + Assert.assertFalse(properties.get(3).containsKey("fromName")); + Assert.assertFalse(properties.get(3).containsKey("text")); + } + + @Test + public void Transcript_LogUpdateActivities() { + BotTelemetryClient mockTelemetryClient = mock(BotTelemetryClient.class); + TestAdapter adapter = new TestAdapter() + .use(new TelemetryLoggerMiddleware(mockTelemetryClient, true)); + Activity[] activityToUpdate = new Activity[]{null}; + + String[] conversationId = new String[]{null}; + new TestFlow(adapter, (turnContext -> { + conversationId[0] = turnContext.getActivity().getConversation().getId(); + + if (StringUtils.equals(turnContext.getActivity().getText(), "update")) { + activityToUpdate[0].setText("new response"); + turnContext.updateActivity(activityToUpdate[0]).join(); + } else { + Activity activity = turnContext.getActivity().createReply("response"); + ResourceResponse response = turnContext.sendActivity(activity).join(); + activity.setId(response.getId()); + activityToUpdate[0] = Activity.clone(activity); + } + + return CompletableFuture.completedFuture(null); + })) + .send("foo") + .send("update") + .assertReply("new response") + .startTest(); + + // verify BotTelemetryClient was invoked 4 times, and capture arguments. + verify(mockTelemetryClient, times(4)).trackEvent(eventNameCaptor.capture(), propertiesCaptor.capture()); + List eventNames = eventNameCaptor.getAllValues(); + List> properties = propertiesCaptor.getAllValues(); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGUPDATEEVENT, eventNames.get(3)); + Assert.assertEquals(5, properties.get(3).size()); + Assert.assertTrue(properties.get(3).containsKey("recipientId")); + Assert.assertTrue(properties.get(3).containsKey("conversationId")); + Assert.assertTrue(properties.get(3).containsKey("conversationName")); + Assert.assertTrue(properties.get(3).containsKey("locale")); + Assert.assertTrue(properties.get(3).containsKey("text")); + Assert.assertTrue(StringUtils.equals(properties.get(3).get("text"), "new response")); + } + + @Test + public void Transcript_LogDeleteActivities() { + BotTelemetryClient mockTelemetryClient = mock(BotTelemetryClient.class); + TestAdapter adapter = new TestAdapter() + .use(new TelemetryLoggerMiddleware(mockTelemetryClient, true)); + + String[] activityId = new String[]{null}; + String[] conversationId = new String[]{null}; + new TestFlow(adapter, (turnContext -> { + conversationId[0] = turnContext.getActivity().getConversation().getId(); + + if (StringUtils.equals(turnContext.getActivity().getText(), "deleteIt")) { + turnContext.deleteActivity(activityId[0]).join(); + } else { + Activity activity = turnContext.getActivity().createReply("response"); + ResourceResponse response = turnContext.sendActivity(activity).join(); + activityId[0] = response.getId(); + } + + return CompletableFuture.completedFuture(null); + })) + .send("foo") + .assertReply("response") + .send("deleteIt") + .startTest(); + + // verify BotTelemetryClient was invoked 4 times, and capture arguments. + verify(mockTelemetryClient, times(4)).trackEvent(eventNameCaptor.capture(), propertiesCaptor.capture()); + List eventNames = eventNameCaptor.getAllValues(); + List> properties = propertiesCaptor.getAllValues(); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGDELETEEVENT, eventNames.get(3)); + Assert.assertEquals(3, properties.get(3).size()); + Assert.assertTrue(properties.get(3).containsKey("recipientId")); + Assert.assertTrue(properties.get(3).containsKey("conversationId")); + Assert.assertTrue(properties.get(3).containsKey("conversationName")); + } + + @Test + public void Telemetry_OverrideReceive() { + BotTelemetryClient mockTelemetryClient = mock(BotTelemetryClient.class); + TestAdapter adapter = new TestAdapter() + .use(new OverrideReceiveLogger(mockTelemetryClient, true)); + + String[] conversationId = new String[]{null}; + new TestFlow(adapter, (turnContext -> { + conversationId[0] = turnContext.getActivity().getConversation().getId(); + turnContext.sendActivity(new Activity() {{ + setType(ActivityTypes.TYPING); + setRelatesTo(turnContext.getActivity().getRelatesTo()); + }}).join(); + turnContext.sendActivity("echo:" + turnContext.getActivity().getText()).join(); + return CompletableFuture.completedFuture(null); + })) + .send("foo") + .assertReply(activity -> { + Assert.assertEquals(activity.getType(), ActivityTypes.TYPING); + return null; + }) + .assertReply("echo:foo") + .send("bar") + .assertReply(activity -> { + Assert.assertEquals(activity.getType(), ActivityTypes.TYPING); + return null; + }) + .assertReply("echo:bar") + .startTest(); + + // verify BotTelemetryClient was invoked 8 times, and capture arguments. + verify(mockTelemetryClient, times(8)).trackEvent(eventNameCaptor.capture(), propertiesCaptor.capture()); + List eventNames = eventNameCaptor.getAllValues(); + List> properties = propertiesCaptor.getAllValues(); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGRECEIVEEVENT, eventNames.get(0)); + Assert.assertEquals(2, properties.get(0).size()); + Assert.assertTrue(properties.get(0).containsKey("foo")); + Assert.assertTrue(StringUtils.equals(properties.get(0).get("foo"), "bar")); + Assert.assertTrue(properties.get(0).containsKey("ImportantProperty")); + Assert.assertTrue(StringUtils.equals(properties.get(0).get("ImportantProperty"), "ImportantValue")); + + Assert.assertEquals("MyReceive", eventNames.get(1)); + Assert.assertEquals(7, properties.get(1).size()); + Assert.assertTrue(properties.get(1).containsKey("fromId")); + Assert.assertTrue(properties.get(1).containsKey("conversationName")); + Assert.assertTrue(properties.get(1).containsKey("locale")); + Assert.assertTrue(properties.get(1).containsKey("recipientId")); + Assert.assertTrue(properties.get(1).containsKey("recipientName")); + Assert.assertTrue(properties.get(1).containsKey("fromName")); + Assert.assertTrue(properties.get(1).containsKey("text")); + Assert.assertTrue(StringUtils.equals(properties.get(1).get("text"), "foo")); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGSENDEVENT, eventNames.get(2)); + Assert.assertEquals(5, properties.get(2).size()); + Assert.assertTrue(properties.get(2).containsKey("replyActivityId")); + Assert.assertTrue(properties.get(2).containsKey("recipientId")); + Assert.assertTrue(properties.get(2).containsKey("conversationName")); + Assert.assertTrue(properties.get(2).containsKey("locale")); + Assert.assertTrue(properties.get(2).containsKey("recipientName")); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGSENDEVENT, eventNames.get(3)); + Assert.assertEquals(6, properties.get(3).size()); + Assert.assertTrue(properties.get(3).containsKey("replyActivityId")); + Assert.assertTrue(properties.get(3).containsKey("recipientId")); + Assert.assertTrue(properties.get(3).containsKey("conversationName")); + Assert.assertTrue(properties.get(3).containsKey("locale")); + Assert.assertTrue(properties.get(3).containsKey("recipientName")); + Assert.assertTrue(properties.get(3).containsKey("text")); + Assert.assertTrue(StringUtils.equals(properties.get(3).get("text"), "echo:foo")); + } + + @Test + public void Telemetry_OverrideSend() { + BotTelemetryClient mockTelemetryClient = mock(BotTelemetryClient.class); + TestAdapter adapter = new TestAdapter() + .use(new OverrideSendLogger(mockTelemetryClient, true)); + + String[] conversationId = new String[]{null}; + new TestFlow(adapter, (turnContext -> { + conversationId[0] = turnContext.getActivity().getConversation().getId(); + turnContext.sendActivity(new Activity() {{ + setType(ActivityTypes.TYPING); + setRelatesTo(turnContext.getActivity().getRelatesTo()); + }}).join(); + turnContext.sendActivity("echo:" + turnContext.getActivity().getText()).join(); + return CompletableFuture.completedFuture(null); + })) + .send("foo") + .assertReply(activity -> { + Assert.assertEquals(activity.getType(), ActivityTypes.TYPING); + return null; + }) + .assertReply("echo:foo") + .send("bar") + .assertReply(activity -> { + Assert.assertEquals(activity.getType(), ActivityTypes.TYPING); + return null; + }) + .assertReply("echo:bar") + .startTest(); + + // verify BotTelemetryClient was invoked 10 times, and capture arguments. + verify(mockTelemetryClient, times(10)).trackEvent(eventNameCaptor.capture(), propertiesCaptor.capture()); + List eventNames = eventNameCaptor.getAllValues(); + List> properties = propertiesCaptor.getAllValues(); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGRECEIVEEVENT, eventNames.get(0)); + Assert.assertEquals(7, properties.get(0).size()); + Assert.assertTrue(properties.get(0).containsKey("fromId")); + Assert.assertTrue(properties.get(0).containsKey("conversationName")); + Assert.assertTrue(properties.get(0).containsKey("locale")); + Assert.assertTrue(properties.get(0).containsKey("recipientId")); + Assert.assertTrue(properties.get(0).containsKey("recipientName")); + Assert.assertTrue(properties.get(0).containsKey("fromName")); + Assert.assertTrue(properties.get(0).containsKey("text")); + Assert.assertTrue(StringUtils.equals(properties.get(0).get("text"), "foo")); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGSENDEVENT, eventNames.get(1)); + Assert.assertEquals(2, properties.get(1).size()); + Assert.assertTrue(properties.get(1).containsKey("foo")); + Assert.assertTrue(StringUtils.equals(properties.get(1).get("foo"), "bar")); + Assert.assertTrue(properties.get(1).containsKey("ImportantProperty")); + Assert.assertTrue(StringUtils.equals(properties.get(1).get("ImportantProperty"), "ImportantValue")); + + Assert.assertEquals("MySend", eventNames.get(2)); + Assert.assertEquals(5, properties.get(2).size()); + Assert.assertTrue(properties.get(2).containsKey("replyActivityId")); + Assert.assertTrue(properties.get(2).containsKey("recipientId")); + Assert.assertTrue(properties.get(2).containsKey("conversationName")); + Assert.assertTrue(properties.get(2).containsKey("locale")); + Assert.assertTrue(properties.get(2).containsKey("recipientName")); + } + + @Test + public void Telemetry_OverrideUpdateDeleteActivities() { + BotTelemetryClient mockTelemetryClient = mock(BotTelemetryClient.class); + TestAdapter adapter = new TestAdapter() + .use(new OverrideUpdateDeleteLogger(mockTelemetryClient, true)); + + Activity[] activityToUpdate = new Activity[]{null}; + String[] conversationId = new String[]{null}; + new TestFlow(adapter, (turnContext -> { + conversationId[0] = turnContext.getActivity().getConversation().getId(); + + if (StringUtils.equals(turnContext.getActivity().getText(), "update")) { + activityToUpdate[0].setText("update"); + turnContext.updateActivity(activityToUpdate[0]).join(); + turnContext.deleteActivity(turnContext.getActivity().getId()).join(); + } else { + Activity activity = turnContext.getActivity().createReply("response"); + ResourceResponse response = turnContext.sendActivity(activity).join(); + activity.setId(response.getId()); + + activityToUpdate[0] = Activity.clone(activity); + } + + return CompletableFuture.completedFuture(null); + })) + .send("foo") + .send("update") + .assertReply("response") + .startTest(); + + // verify BotTelemetryClient was invoked 5 times, and capture arguments. + verify(mockTelemetryClient, times(5)).trackEvent(eventNameCaptor.capture(), propertiesCaptor.capture()); + List eventNames = eventNameCaptor.getAllValues(); + List> properties = propertiesCaptor.getAllValues(); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGUPDATEEVENT, eventNames.get(3)); + Assert.assertEquals(2, properties.get(3).size()); + Assert.assertTrue(properties.get(3).containsKey("foo")); + Assert.assertTrue(StringUtils.equals(properties.get(3).get("foo"), "bar")); + Assert.assertTrue(properties.get(3).containsKey("ImportantProperty")); + Assert.assertTrue(StringUtils.equals(properties.get(3).get("ImportantProperty"), "ImportantValue")); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGDELETEEVENT, eventNames.get(4)); + Assert.assertEquals(2, properties.get(4).size()); + Assert.assertTrue(properties.get(4).containsKey("foo")); + Assert.assertTrue(StringUtils.equals(properties.get(4).get("foo"), "bar")); + Assert.assertTrue(properties.get(4).containsKey("ImportantProperty")); + Assert.assertTrue(StringUtils.equals(properties.get(4).get("ImportantProperty"), "ImportantValue")); + } + + @Test + public void Telemetry_AdditionalProps() { + BotTelemetryClient mockTelemetryClient = mock(BotTelemetryClient.class); + TestAdapter adapter = new TestAdapter() + .use(new OverrideFillLogger(mockTelemetryClient, true)); + + Activity[] activityToUpdate = new Activity[]{null}; + String[] conversationId = new String[]{null}; + new TestFlow(adapter, (turnContext -> { + conversationId[0] = turnContext.getActivity().getConversation().getId(); + + if (StringUtils.equals(turnContext.getActivity().getText(), "update")) { + activityToUpdate[0].setText("new response"); + turnContext.updateActivity(activityToUpdate[0]).join(); + turnContext.deleteActivity(turnContext.getActivity().getId()).join(); + } else { + Activity activity = turnContext.getActivity().createReply("response"); + ResourceResponse response = turnContext.sendActivity(activity).join(); + activity.setId(response.getId()); + + activityToUpdate[0] = Activity.clone(activity); + } + + return CompletableFuture.completedFuture(null); + })) + .send("foo") + .send("update") + .assertReply("new response") + .startTest(); + + // verify BotTelemetryClient was invoked 5 times, and capture arguments. + verify(mockTelemetryClient, times(5)).trackEvent(eventNameCaptor.capture(), propertiesCaptor.capture()); + List eventNames = eventNameCaptor.getAllValues(); + List> properties = propertiesCaptor.getAllValues(); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGRECEIVEEVENT, eventNames.get(0)); + Assert.assertEquals(9, properties.get(0).size()); + Assert.assertTrue(properties.get(0).containsKey("fromId")); + Assert.assertTrue(properties.get(0).containsKey("conversationName")); + Assert.assertTrue(properties.get(0).containsKey("locale")); + Assert.assertTrue(properties.get(0).containsKey("recipientId")); + Assert.assertTrue(properties.get(0).containsKey("recipientName")); + Assert.assertTrue(properties.get(0).containsKey("fromName")); + Assert.assertTrue(properties.get(0).containsKey("text")); + Assert.assertTrue(StringUtils.equals(properties.get(0).get("text"), "foo")); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGSENDEVENT, eventNames.get(1)); + Assert.assertEquals(8, properties.get(1).size()); + Assert.assertTrue(properties.get(1).containsKey("foo")); + Assert.assertTrue(properties.get(1).containsKey("replyActivityId")); + Assert.assertTrue(properties.get(1).containsKey("recipientId")); + Assert.assertTrue(properties.get(1).containsKey("conversationName")); + Assert.assertTrue(properties.get(1).containsKey("locale")); + Assert.assertTrue(properties.get(1).containsKey("foo")); + Assert.assertTrue(properties.get(1).containsKey("text")); + Assert.assertTrue(StringUtils.equals(properties.get(1).get("text"), "response")); + Assert.assertTrue(StringUtils.equals(properties.get(1).get("foo"), "bar")); + Assert.assertTrue(StringUtils.equals(properties.get(1).get("ImportantProperty"), "ImportantValue")); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGUPDATEEVENT, eventNames.get(3)); + Assert.assertEquals(7, properties.get(3).size()); + Assert.assertTrue(properties.get(3).containsKey("conversationId")); + Assert.assertTrue(properties.get(3).containsKey("conversationName")); + Assert.assertTrue(properties.get(3).containsKey("locale")); + Assert.assertTrue(properties.get(3).containsKey("foo")); + Assert.assertTrue(properties.get(3).containsKey("text")); + Assert.assertTrue(StringUtils.equals(properties.get(3).get("text"), "new response")); + Assert.assertTrue(StringUtils.equals(properties.get(3).get("foo"), "bar")); + Assert.assertTrue(StringUtils.equals(properties.get(3).get("ImportantProperty"), "ImportantValue")); + + Assert.assertEquals(TelemetryLoggerConstants.BOTMSGDELETEEVENT, eventNames.get(4)); + Assert.assertEquals(5, properties.get(4).size()); + Assert.assertTrue(properties.get(4).containsKey("recipientId")); + Assert.assertTrue(properties.get(4).containsKey("conversationName")); + Assert.assertTrue(properties.get(4).containsKey("conversationId")); + Assert.assertTrue(properties.get(4).containsKey("foo")); + Assert.assertTrue(StringUtils.equals(properties.get(4).get("foo"), "bar")); + Assert.assertTrue(StringUtils.equals(properties.get(4).get("ImportantProperty"), "ImportantValue")); + } + + private static class OverrideReceiveLogger extends TelemetryLoggerMiddleware { + public OverrideReceiveLogger(BotTelemetryClient withTelemetryClient, boolean withLogPersonalInformation) { + super(withTelemetryClient, withLogPersonalInformation); + } + + @Override + protected CompletableFuture onReceiveActivity(Activity activity) { + Map customProperties = new HashMap() {{ + put("foo", "bar"); + put("ImportantProperty", "ImportantValue"); + }}; + + getTelemetryClient().trackEvent(TelemetryLoggerConstants.BOTMSGRECEIVEEVENT, customProperties); + + return fillReceiveEventProperties(activity, null) + .thenApply(eventProperties -> { + getTelemetryClient().trackEvent("MyReceive", eventProperties); + return null; + }); + } + } + + private static class OverrideSendLogger extends TelemetryLoggerMiddleware { + public OverrideSendLogger(BotTelemetryClient withTelemetryClient, boolean withLogPersonalInformation) { + super(withTelemetryClient, withLogPersonalInformation); + } + + @Override + protected CompletableFuture onSendActivity(Activity activity) { + Map customProperties = new HashMap() {{ + put("foo", "bar"); + put("ImportantProperty", "ImportantValue"); + }}; + + getTelemetryClient().trackEvent(TelemetryLoggerConstants.BOTMSGSENDEVENT, customProperties); + + return fillSendEventProperties(activity, null) + .thenApply(eventProperties -> { + getTelemetryClient().trackEvent("MySend", eventProperties); + return null; + }); + } + } + + private static class OverrideUpdateDeleteLogger extends TelemetryLoggerMiddleware { + public OverrideUpdateDeleteLogger(BotTelemetryClient withTelemetryClient, boolean withLogPersonalInformation) { + super(withTelemetryClient, withLogPersonalInformation); + } + + @Override + protected CompletableFuture onUpdateActivity(Activity activity) { + Map properties = new HashMap() {{ + put("foo", "bar"); + put("ImportantProperty", "ImportantValue"); + }}; + + getTelemetryClient().trackEvent(TelemetryLoggerConstants.BOTMSGUPDATEEVENT, properties); + return CompletableFuture.completedFuture(null); + } + + @Override + protected CompletableFuture onDeleteActivity(Activity activity) { + Map properties = new HashMap() {{ + put("foo", "bar"); + put("ImportantProperty", "ImportantValue"); + }}; + + getTelemetryClient().trackEvent(TelemetryLoggerConstants.BOTMSGDELETEEVENT, properties); + return CompletableFuture.completedFuture(null); + } + } + + private static class OverrideFillLogger extends TelemetryLoggerMiddleware { + public OverrideFillLogger(BotTelemetryClient withTelemetryClient, boolean withLogPersonalInformation) { + super(withTelemetryClient, withLogPersonalInformation); + } + + @Override + protected CompletableFuture onReceiveActivity(Activity activity) { + Map customProperties = new HashMap() {{ + put("foo", "bar"); + put("ImportantProperty", "ImportantValue"); + }}; + + return fillReceiveEventProperties(activity, customProperties) + .thenApply(allProperties -> { + getTelemetryClient().trackEvent(TelemetryLoggerConstants.BOTMSGRECEIVEEVENT, allProperties); + return null; + }); + } + + @Override + protected CompletableFuture onSendActivity(Activity activity) { + Map customProperties = new HashMap() {{ + put("foo", "bar"); + put("ImportantProperty", "ImportantValue"); + }}; + + return fillSendEventProperties(activity, customProperties) + .thenApply(allProperties -> { + getTelemetryClient().trackEvent(TelemetryLoggerConstants.BOTMSGSENDEVENT, allProperties); + return null; + }); + } + + @Override + protected CompletableFuture onUpdateActivity(Activity activity) { + Map customProperties = new HashMap() {{ + put("foo", "bar"); + put("ImportantProperty", "ImportantValue"); + }}; + + return fillUpdateEventProperties(activity, customProperties) + .thenApply(allProperties -> { + getTelemetryClient().trackEvent(TelemetryLoggerConstants.BOTMSGUPDATEEVENT, allProperties); + return null; + }); + } + + @Override + protected CompletableFuture onDeleteActivity(Activity activity) { + Map customProperties = new HashMap() {{ + put("foo", "bar"); + put("ImportantProperty", "ImportantValue"); + }}; + + return fillDeleteEventProperties(activity, customProperties) + .thenApply(allProperties -> { + getTelemetryClient().trackEvent(TelemetryLoggerConstants.BOTMSGDELETEEVENT, allProperties); + return null; + }); + } + } +} diff --git a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/TranscriptMiddlewareTest.java b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/TranscriptMiddlewareTest.java index c15a4a40..5f928cdd 100644 --- a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/TranscriptMiddlewareTest.java +++ b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/TranscriptMiddlewareTest.java @@ -185,6 +185,7 @@ public class TranscriptMiddlewareTest { MemoryTranscriptStore transcriptStore = new MemoryTranscriptStore(); TestAdapter adapter = (new TestAdapter()).use(new TranscriptLoggerMiddleware(transcriptStore)); + final String[] conversationId = {null}; final Activity[] activityToUpdate = {null}; new TestFlow(adapter, (context) -> { diff --git a/pom.xml b/pom.xml index 52f03aae..68771df3 100644 --- a/pom.xml +++ b/pom.xml @@ -59,6 +59,13 @@ 4.12 test + + org.mockito + mockito-core + 3.0.0 + test + + com.microsoft.rest client-runtime