diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/ComponentRegistration.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/ComponentRegistration.java index 8c808fe95..c19a39fc7 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/ComponentRegistration.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/ComponentRegistration.java @@ -11,8 +11,8 @@ @SuppressWarnings("checkstyle:HideUtilityClassConstructor") public class ComponentRegistration { - private static final ConcurrentHashMap, ComponentRegistration> COMPONENTS = - new ConcurrentHashMap, ComponentRegistration>(); + private static final ConcurrentHashMap, Object> COMPONENTS = + new ConcurrentHashMap, Object>(); /** * Add a component which implements registration methods. @@ -28,7 +28,7 @@ public static void add(ComponentRegistration componentRegistration) { * * @return A array of ComponentRegistration objects. */ - public static Iterable getComponents() { + public static Iterable getComponents() { return COMPONENTS.values(); } } diff --git a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/ChannelServiceHandlerTests.java b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/ChannelServiceHandlerTests.java index 77f201fec..beaf5bacf 100644 --- a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/ChannelServiceHandlerTests.java +++ b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/ChannelServiceHandlerTests.java @@ -22,7 +22,7 @@ public class ChannelServiceHandlerTests { @Test public void AuthenticateSetsAnonymousSkillClaim() { TestChannelServiceHandler sut = new TestChannelServiceHandler(); - sut.handleReplyToActivity(null, "123", "456", new Activity(ActivityTypes.MESSAGE)); + sut.handleReplyToActivity(null, "123", "456", new Activity(ActivityTypes.MESSAGE)); Assert.assertEquals(AuthenticationConstants.ANONYMOUS_AUTH_TYPE, sut.getClaimsIdentity().getType()); @@ -30,6 +30,18 @@ public void AuthenticateSetsAnonymousSkillClaim() { JwtTokenValidation.getAppIdFromClaims(sut.getClaimsIdentity().claims())); } + @Test + public void testHandleSendToConversation() { + TestChannelServiceHandler sut = new TestChannelServiceHandler(); + sut.handleSendToConversation(null, "456", new Activity(ActivityTypes.MESSAGE)); + + Assert.assertEquals(AuthenticationConstants.ANONYMOUS_AUTH_TYPE, + sut.getClaimsIdentity().getType()); + Assert.assertEquals(AuthenticationConstants.ANONYMOUS_SKILL_APPID, + JwtTokenValidation.getAppIdFromClaims(sut.getClaimsIdentity().claims())); + } + + /** * A {@link ChannelServiceHandler} with overrides for testings. */ @@ -50,6 +62,17 @@ protected CompletableFuture onReplyToActivity( this.claimsIdentity = claimsIdentity; return CompletableFuture.completedFuture(new ResourceResponse()); } + + @Override + protected CompletableFuture onSendToConversation( + ClaimsIdentity claimsIdentity, + String activityId, + Activity activity + ) { + this.claimsIdentity = claimsIdentity; + return CompletableFuture.completedFuture(new ResourceResponse()); + } + /** * Gets the {@link ClaimsIdentity} sent to the different methods after * auth is done. diff --git a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/adapters/TestAdapter.java b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/adapters/TestAdapter.java index 7b9045caf..dcb458ebf 100644 --- a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/adapters/TestAdapter.java +++ b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/adapters/TestAdapter.java @@ -139,6 +139,10 @@ public TestAdapter(ConversationReference reference) { setConversationReference(conversationReference); } } + public TestAdapter(ConversationReference reference, boolean sendTraceActivity) { + this(reference); + this.sendTraceActivity = sendTraceActivity; + } public Queue activeQueue() { return botReplies; diff --git a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/adapters/TestFlow.java b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/adapters/TestFlow.java index 8672bc282..0b817e8f7 100644 --- a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/adapters/TestFlow.java +++ b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/adapters/TestFlow.java @@ -76,7 +76,7 @@ public TestFlow send(String userSays) throws IllegalArgumentException { * Creates a conversation update activity and process it the activity. * @return A new TestFlow Object */ - public TestFlow sendConverationUpdate() { + public TestFlow sendConversationUpdate() { return new TestFlow(testTask.thenCompose(result -> { Activity cu = Activity.createConversationUpdateActivity(); cu.getMembersAdded().add(this.adapter.conversationReference().getUser()); diff --git a/libraries/bot-dialogs/pom.xml b/libraries/bot-dialogs/pom.xml index 0d4cd4ed9..7b7e377d2 100644 --- a/libraries/bot-dialogs/pom.xml +++ b/libraries/bot-dialogs/pom.xml @@ -101,6 +101,11 @@ snakeyaml 1.27 + + org.mockito + mockito-core + test + @@ -157,7 +162,6 @@ - diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/ObjectPath.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/ObjectPath.java index ce3c6e002..114c2ffc6 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/ObjectPath.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/ObjectPath.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.NullNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.microsoft.bot.builder.TurnContextStateCollection; import com.microsoft.bot.schema.Serialization; import java.lang.reflect.Array; import java.lang.reflect.Field; @@ -599,6 +600,8 @@ private static Object resolveSegment(Object current, Object segment) { if (current instanceof List) { current = ((List) current).get(index); + } else if (current instanceof ArrayNode) { + current = ((ArrayNode) current).get(index); } else { current = Array.get(current, index); } @@ -633,6 +636,22 @@ private static Object getObjectProperty(Object obj, String property) { return null; } + // Because TurnContextStateCollection is not implemented as a Map we need to + // set obj to the Map which holds the state values which is retrieved from calling + // getTurnStateServices() + if (obj instanceof TurnContextStateCollection) { + Map dict = ((TurnContextStateCollection) obj).getTurnStateServices(); + List> matches = dict.entrySet().stream() + .filter(key -> key.getKey().equalsIgnoreCase(property)) + .collect(Collectors.toList()); + + if (matches.size() > 0) { + return matches.get(0).getValue(); + } + + return null; + } + if (obj instanceof Map) { Map dict = (Map) obj; List> matches = dict.entrySet().stream() @@ -652,7 +671,7 @@ private static Object getObjectProperty(Object obj, String property) { while (fields.hasNext()) { String field = fields.next(); if (field.equalsIgnoreCase(property)) { - return node.findValue(property); + return node.findValue(field); } } return null; diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/DialogStateManager.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/DialogStateManager.java index 817a5dfb6..ced24ab76 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/DialogStateManager.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/DialogStateManager.java @@ -5,6 +5,7 @@ import java.util.AbstractMap; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; @@ -12,6 +13,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.AbstractMap.SimpleEntry; import java.util.concurrent.CompletableFuture; @@ -91,7 +93,17 @@ public DialogStateManager(DialogContext dc, DialogStateManagerConfiguration conf if (this.configuration == null) { this.configuration = new DialogStateManagerConfiguration(); - Iterable components = ComponentRegistration.getComponents(); + Map turnStateServices = dc.getContext().getTurnState().getTurnStateServices(); + for (Map.Entry entry : turnStateServices.entrySet()) { + if (entry.getValue() instanceof MemoryScope[]) { + this.configuration.getMemoryScopes().addAll(Arrays.asList((MemoryScope[]) entry.getValue())); + } + if (entry.getValue() instanceof PathResolver[]) { + this.configuration.getPathResolvers().addAll(Arrays.asList((PathResolver[]) entry.getValue())); + } + } + + Iterable components = ComponentRegistration.getComponents(); components.forEach((component) -> { if (component instanceof ComponentMemoryScopes) { @@ -166,7 +178,7 @@ public void setElement(String key, Object element) { e.printStackTrace(); } } else { - throw new IllegalArgumentException(); + throw new IllegalArgumentException(getBadScopeMessage(key)); } } } @@ -181,8 +193,10 @@ public MemoryScope getMemoryScope(String name) { if (name == null) { throw new IllegalArgumentException("name cannot be null."); } - return configuration.getMemoryScopes().stream().filter((scope) -> scope.getName().equalsIgnoreCase(name)) - .findFirst().get(); + Optional result = configuration.getMemoryScopes().stream() + .filter((scope) -> scope.getName().equalsIgnoreCase(name)) + .findFirst(); + return result.isPresent() ? result.get() : null; } /** @@ -638,12 +652,13 @@ public Iterable> getEnumerator() { */ public List trackPaths(Iterable paths) { List allPaths = new ArrayList(); - for (String path : allPaths) { + for (String path : paths) { String tpath = transformPath(path); // Track any path that resolves to a constant path - Object resolved = ObjectPath.tryResolvePath(this, tpath); + ArrayList resolved = ObjectPath.tryResolvePath(this, tpath); + String[] segments = resolved.toArray(new String[resolved.size()]); if (resolved != null) { - String npath = String.join("_", resolved.toString()); + String npath = String.join("_", segments); setValue(pathTracker + "." + npath, 0); allPaths.add(npath); } @@ -721,7 +736,7 @@ private Boolean trackChange(String path, Object value) { // Convert to a simple path with _ between segments String pathName = String.join("_", stringSegments); String trackedPath = String.format("%s.%s", pathTracker, pathName); - Integer counter = getValue(DialogPath.EVENTCOUNTER, 0, Integer.class); + Integer counter = null; /** * */ @@ -777,7 +792,6 @@ private void checkChildren(String property, Object instance, String path, Intege checkChildren(field, node.findValue(field), trackedPath, counter); } } - } @Override @@ -807,12 +821,13 @@ public final Object get(Object key) { @Override public final Object put(String key, Object value) { - return null; + setElement(key, value); + return value; } @Override public final Object remove(Object key) { - return null; + throw new UnsupportedOperationException(); } @Override diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/DialogMemoryScope.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/DialogMemoryScope.java index 24c1c0f28..bbaa23134 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/DialogMemoryScope.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/DialogMemoryScope.java @@ -42,7 +42,7 @@ public final Object getMemory(DialogContext dialogContext) { return dialogContext.getParent().getActiveDialog().getState(); } } else if (dialogContext.getActiveDialog() != null) { - return dialogContext.getActiveDialog().getStackIndex(); + return dialogContext.getActiveDialog().getState(); } return null; } diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/ReadOnlyObject.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/ReadOnlyObject.java index 429ff88b3..1c43f012e 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/ReadOnlyObject.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/ReadOnlyObject.java @@ -3,10 +3,13 @@ package com.microsoft.bot.dialogs.memory.scopes; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; -import java.util.Collections; -import java.util.Dictionary; -import java.util.Enumeration; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import com.microsoft.bot.dialogs.ObjectPath; @@ -14,9 +17,9 @@ * ReadOnlyObject is a wrapper around any Object to prevent setting of * properties on the Object. */ -public class ReadOnlyObject extends Dictionary { +public final class ReadOnlyObject implements Map { - private final String notSupported = "This Object is final."; + private final String notSupported = "This Object is final and cannot be modified."; private Object obj; @@ -37,13 +40,6 @@ public int size() { return ObjectPath.getProperties(obj).size(); } - /** - * @return The number of items. - */ - public int count() { - return size(); - } - /** * */ @@ -52,34 +48,17 @@ public boolean isEmpty() { return false; } - /** - * - */ @Override - public Enumeration keys() { - return Collections.enumeration(ObjectPath.getProperties(obj)); + public boolean containsKey(Object key) { + return ObjectPath.containsProperty(obj, (String) key); } /** * */ @Override - public Enumeration elements() { - Enumeration keys = this.keys(); - ArrayList elements = new ArrayList(); - while (keys.hasMoreElements()) { - String key = keys.nextElement(); - elements.add(getValue(key)); - } - return Collections.enumeration(elements); - } - - /** - * - * @return The values. - */ - public Enumeration values() { - return elements(); + public Set keySet() { + return new HashSet(ObjectPath.getProperties(obj)); } /** @@ -111,19 +90,45 @@ public Object remove(Object key) { throw new UnsupportedOperationException(notSupported); } - /** - * Get a value based on a key. - * - * @param name Key of the value. - * @return The value associated with the provided key. - */ - public Object getValue(String name) { - Object value = ObjectPath.tryGetPathValue(obj, name, Object.class); - if (value != null) { - return new ReadOnlyObject(value); - } else { - return null; + @Override + public boolean containsValue(Object value) { + return false; + } + + @Override + public void putAll(Map m) { + throw new UnsupportedOperationException(notSupported); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(notSupported); + } + + @Override + public Collection values() { + Set keys = this.keySet(); + List objectList = new ArrayList(); + for (String key : keys) { + Object foundValue = ObjectPath.tryGetPathValue(obj, key, Object.class); + if (foundValue != null) { + objectList.add(foundValue); + } + } + return objectList; + } + + @Override + public Set> entrySet() { + Set> items = new HashSet>(); + Set keys = this.keySet(); + for (String key : keys) { + Object foundValue = ObjectPath.tryGetPathValue(obj, key, Object.class); + if (foundValue != null) { + items.add(new SimpleEntry(key, foundValue)); + } } + return items; } } diff --git a/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/Bar.java b/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/Bar.java new file mode 100644 index 000000000..e17ee6215 --- /dev/null +++ b/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/Bar.java @@ -0,0 +1,29 @@ +package com.microsoft.bot.dialogs; + +public class Bar { + private String name; + private int age; + private boolean cool; + + public Bar() { + + } + + public Bar(String name, int age, boolean cool) { + this.name = name; + this.age = age; + this.cool = cool; + } + + public String getName() { + return this.name; + } + + public int getAge() { + return this.age; + } + + public boolean getCool() { + return cool; + } +} diff --git a/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/DialogStateManagerTests.java b/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/DialogStateManagerTests.java index 776dcb3ac..20a540afd 100644 --- a/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/DialogStateManagerTests.java +++ b/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/DialogStateManagerTests.java @@ -5,27 +5,36 @@ import static org.junit.Assert.fail; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.microsoft.bot.builder.ConversationState; import com.microsoft.bot.builder.MemoryStorage; +import com.microsoft.bot.builder.TraceTranscriptLogger; +import com.microsoft.bot.builder.TranscriptLoggerMiddleware; import com.microsoft.bot.builder.UserState; import com.microsoft.bot.builder.adapters.TestAdapter; import com.microsoft.bot.builder.adapters.TestFlow; import com.microsoft.bot.dialogs.memory.DialogStateManager; import com.microsoft.bot.dialogs.memory.DialogStateManagerConfiguration; import com.microsoft.bot.dialogs.memory.PathResolver; +import com.microsoft.bot.dialogs.memory.pathresolvers.AliasPathResolver; import com.microsoft.bot.dialogs.memory.pathresolvers.AtAtPathResolver; import com.microsoft.bot.dialogs.memory.pathresolvers.AtPathResolver; import com.microsoft.bot.dialogs.memory.pathresolvers.DollarPathResolver; import com.microsoft.bot.dialogs.memory.pathresolvers.HashPathResolver; import com.microsoft.bot.dialogs.memory.pathresolvers.PercentPathResolver; import com.microsoft.bot.dialogs.memory.scopes.MemoryScope; +import com.microsoft.bot.schema.ResultPair; import org.junit.Rule; import org.junit.Test; @@ -182,22 +191,11 @@ public void TestEntitiesRetrieval() { ObjectMapper mapper = new ObjectMapper(); mapper.findAndRegisterModules(); - String[] array = new String[] { - "test1", - "test2", - "test3" - }; + String[] array = new String[] { "test1", "test2", "test3" }; - String[] array2 = new String[] { - "testx", - "testy", - "testz" - }; + String[] array2 = new String[] { "testx", "testy", "testz" }; - String[][] arrayarray = new String[][] { - array2, - array - }; + String[][] arrayarray = new String[][] { array2, array }; JsonNode arrayNode = mapper.valueToTree(array); JsonNode arrayArrayNode = mapper.valueToTree(arrayarray); @@ -207,12 +205,10 @@ public void TestEntitiesRetrieval() { Assert.assertEquals("test1", dc.getState().getValue("@single", new String(), String.class)); Assert.assertEquals("testx", dc.getState().getValue("@double", new String(), String.class)); - Assert.assertEquals("test1", dc.getState().getValue("turn.recognized.entities.single.First()", - new String(), String.class)); - Assert.assertEquals("testx", dc.getState().getValue("turn.recognized.entities.double.First()", - new String(), String.class)); - - + Assert.assertEquals("test1", + dc.getState().getValue("turn.recognized.entities.single.First()", new String(), String.class)); + Assert.assertEquals("testx", + dc.getState().getValue("turn.recognized.entities.double.First()", new String(), String.class)); // arrayarray = new JArray(); ArrayNode secondArray = mapper.createArrayNode(); @@ -234,22 +230,563 @@ public void TestEntitiesRetrieval() { node3a.put("name", "testz"); array2Node.addAll(Arrays.asList(node1a, node2a, node3a)); secondArray.addAll(Arrays.asList(array2Node, array1Node)); - dc.getState().setValue("turn.recognized.entities.single", array1Node); - dc.getState().setValue("turn.recognized.entities.double", secondArray); + dc.getState().setValue("turn.recognized.entities.single", array1Node); + dc.getState().setValue("turn.recognized.entities.double", secondArray); Assert.assertEquals("test1", dc.getState().getValue("@single.name", new String(), String.class)); - Assert.assertEquals("test1", dc.getState().getValue("turn.recognized.entities.single.First().name", - new String(), String.class)); + Assert.assertEquals("test1", + dc.getState().getValue("turn.recognized.entities.single.First().name", new String(), String.class)); Assert.assertEquals("testx", dc.getState().getValue("@double.name", new String(), String.class)); - Assert.assertEquals("testx", dc.getState().getValue("turn.recognized.entities.double.First().name", - new String(), String.class)); + Assert.assertEquals("testx", + dc.getState().getValue("turn.recognized.entities.double.First().name", new String(), String.class)); + return CompletableFuture.completedFuture(null); + }; + + createDialogContext(testFunction).startTest().join(); + } + + private Foo getFoo() { + return new Foo("Tom", 15, true, new Bar("bob", 122, false)); + } + + @Test + public void TestComplexValuePaths() { + DialogTestFunction testFunction = dc -> { + // complex type paths + dc.getState().setValue("UseR.fOo", getFoo()); + Assert.assertEquals("bob", dc.getState().getValue("user.foo.SuBname.name", "", String.class)); + + // complex type paths + dc.getState().setValue("ConVerSation.FOo", getFoo()); + Assert.assertEquals("bob", dc.getState().getValue("conversation.foo.SuBname.name", "", String.class)); + + // complex type paths + dc.getState().setValue("TurN.fOo", getFoo()); + Assert.assertEquals("bob", dc.getState().getValue("TuRN.foo.SuBname.name", "", String.class)); return CompletableFuture.completedFuture(null); + }; + createDialogContext(testFunction).startTest().join(); + } + @Test + public void TestComplexPathExpressions() { + DialogTestFunction testFunction = dc -> { + // complex type paths + dc.getState().setValue("user.name", "joe"); + dc.getState().setValue("conversation.stuff[user.name]", "test"); + dc.getState().setValue("conversation.stuff['frank']", "test2"); + dc.getState().setValue("conversation.stuff[\"susan\"]", "test3"); + dc.getState().setValue("conversation.stuff['Jo.Bob']", "test4"); + Assert.assertEquals("test", dc.getState().getValue("conversation.stuff.joe", "", String.class)); + Assert.assertEquals("test", dc.getState().getValue("conversation.stuff[user.name]", "", String.class)); + Assert.assertEquals("test2", dc.getState().getValue("conversation.stuff['frank']", "", String.class)); + Assert.assertEquals("test3", dc.getState().getValue("conversation.stuff[\"susan\"]", "", String.class)); + Assert.assertEquals("test4", dc.getState().getValue("conversation.stuff[\"Jo.Bob\"]", "", String.class)); + return CompletableFuture.completedFuture(null); }; + createDialogContext(testFunction).startTest().join(); + } + @Test + public void TestGetValue() { + DialogTestFunction testFunction = dc -> { + // complex type paths + dc.getState().setValue("user.name.first", "joe"); + Assert.assertEquals("joe", dc.getState().getValue("user.name.first", "", String.class)); + + Assert.assertNull(dc.getState().getValue("user.xxx", null, String.class)); + Assert.assertEquals("default", dc.getState().getValue("user.xxx", "default", String.class)); + + for (String key : dc.getState().keySet()) { + if (key.equals("dialogContext")) { + Object expected = dc.getState().get(key); + Object actual = dc.getState().getValue(key, null, Object.class); + Assert.assertTrue(expected.equals(actual)); + } + } + return CompletableFuture.completedFuture(null); + }; createDialogContext(testFunction).startTest().join(); } + @Test + public void TestTryGetValueWithWrongType() { + createDialogContext(dc -> { + dc.getState().setValue("user.name.first", "joe"); + Assert.assertFalse(dc.getState().tryGetValue("user.name.first", Integer.class).getLeft()); + ResultPair result = dc.getState().tryGetValue("user.name.first", String.class); + Assert.assertTrue(result.getLeft()); + Assert.assertEquals("joe", result.getRight()); + + dc.getState().setValue("user.age", 19); + ResultPair result2 = dc.getState().tryGetValue("user.age", String.class); + Assert.assertTrue(result2.getLeft()); + Assert.assertEquals("19", result2.getRight()); + ResultPair result3 = dc.getState().tryGetValue("user.age", Integer.class); + Assert.assertTrue(result3.getLeft()); + Assert.assertTrue(19 == result3.getRight()); + + dc.getState().setValue("user.salary", "10000"); + ResultPair result4 = dc.getState().tryGetValue("user.salary", String.class); + Assert.assertTrue(result4.getLeft()); + Assert.assertEquals("10000", result4.getRight()); + ResultPair result5 = dc.getState().tryGetValue("user.salary", Integer.class); + Assert.assertTrue(result5.getLeft()); + Assert.assertTrue(10000 == result5.getRight()); + + dc.getState().setValue("user.foo", getFoo()); + ResultPair result6 = dc.getState().tryGetValue("user.foo", String.class); + Assert.assertFalse(result6.getLeft()); + ResultPair result7 = dc.getState().tryGetValue("user.foo", Foo.class); + Assert.assertTrue(result7.getLeft()); + ResultPair result8 = dc.getState().tryGetValue("user.foo", Map.class); + Assert.assertTrue(result8.getLeft()); + ResultPair result9 = dc.getState().tryGetValue("user.foo", Bar.class); + Assert.assertTrue(result9.getLeft()); + return CompletableFuture.completedFuture(null); + }).startTest().join(); + } + + @Test + public void TestGetValueT() { + createDialogContext(dc -> { + // complex type paths + dc.getState().setValue("UseR.fOo", getFoo()); + Assert.assertEquals("bob", dc.getState().getValue("user.foo", null, Foo.class).getSubname().getName()); + + // complex type paths + dc.getState().setValue("ConVerSation.FOo", getFoo()); + Assert.assertEquals("bob", + dc.getState().getValue("conversation.foo", null, Foo.class).getSubname().getName()); + + // complex type paths + dc.getState().setValue("TurN.fOo", getFoo()); + Assert.assertEquals("bob", dc.getState().getValue("turn.foo", null, Foo.class).getSubname().getName()); + return CompletableFuture.completedFuture(null); + }).startTest().join(); + } + + @Test + public void TestSetValue_RootScope() { + createDialogContext(dc -> { + try { + dc.getState().setValue(null, 13); + fail("Should have thrown with null memory scope"); + } catch (IllegalArgumentException err) { + Assert.assertTrue(err.getMessage().contains("path")); + } + + try { + // complex type paths + dc.getState().setValue("xxx", 13); + fail("Should have thrown with unknown memory scope"); + } catch (Exception err) { + Assert.assertTrue(err.getMessage().contains("does not match memory scope")); + } + return CompletableFuture.completedFuture(null); + + }).startTest().join(); + } + + @Test + public void TestRemoveValue_RootScope() { + createDialogContext(dc -> { + try { + dc.getState().removeValue(null); + fail("Should have thrown with null memory scope"); + } catch (IllegalArgumentException err) { + Assert.assertTrue(err.getMessage().toLowerCase().contains("path")); + } + + try { + dc.getState().removeValue("user"); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException ex) { + } + + try { + dc.getState().removeValue("xxx"); + fail("Should have thrown UnsupportedOperationException"); + } catch (UnsupportedOperationException ex) { + System.out.println(ex.getMessage()); + } + return CompletableFuture.completedFuture(null); + }).startTest().join(); + } + + @Test + public void TestHashResolver() { + createDialogContext(dc -> { + // test HASH + dc.getState().setValue("turn.recognized.intents.test", "intent1"); + dc.getState().setValue("#test2", "intent2"); + + Assert.assertEquals("intent1", dc.getState().getValue("turn.recognized.intents.test", "", String.class)); + Assert.assertEquals("intent1", dc.getState().getValue("#test", "", String.class)); + Assert.assertEquals("intent2", dc.getState().getValue("turn.recognized.intents.test2", "", String.class)); + Assert.assertEquals("intent2", dc.getState().getValue("#test2", "", String.class)); + return CompletableFuture.completedFuture(null); + }).startTest().join(); + } + + @Test + public void TestEntityResolvers() { + createDialogContext(dc -> { + // test @ and @@ + String[] testEntities = new String[] { "entity1", "entity2" }; + String[] testEntities2 = new String[] { "entity3", "entity4" }; + dc.getState().setValue("turn.recognized.entities.test", testEntities); + dc.getState().setValue("@@test2", testEntities2); + + Assert.assertEquals(testEntities[0], + dc.getState().getValue("turn.recognized.entities.test[0]", "", String.class)); + Assert.assertEquals(testEntities[0], dc.getState().getValue("@test", "", String.class)); + Assert.assertArrayEquals(testEntities, + dc.getState().getValue("turn.recognized.entities.test", null, String[].class)); + Assert.assertArrayEquals(testEntities, dc.getState().getValue("@@test", null, String[].class)); + + Assert.assertEquals(testEntities2[0], + dc.getState().getValue("turn.recognized.entities.test2[0]", "", String.class)); + Assert.assertEquals(testEntities2[0], dc.getState().getValue("@test2", "", String.class)); + Assert.assertArrayEquals(testEntities2, + dc.getState().getValue("turn.recognized.entities.test2", null, String[].class)); + Assert.assertArrayEquals(testEntities2, dc.getState().getValue("@@test2", null, String[].class)); + return CompletableFuture.completedFuture(null); + }).startTest().join(); + } + + @Test + public void TestDollarScope() { + createFlow(new D1Dialog(), null, null, false).sendConversationUpdate() + // d1 + .assertReply("dialog") + .assertReply("dialog") + .assertReply("dialog2") + .assertReply("dialog2") + .assertReply("10") + + // d2 + .assertReply("bbb") + .assertReply("123") + .assertReply("20") + .assertReply("bbb") + .startTest().join(); + } + + @Test + public void TestNestedContainerDialogs() { + createFlow(new NestedContainerDialog(), null, null, false) + .sendConversationUpdate() + .assertReply("testDialog") + .assertReply("testDialog") + .assertReply("nested d1") + .assertReply("nested d1") + .assertReply("testDialog") + .assertReply("testDialog") + .assertReply("nested d2") + .startTest() + .join(); + } + + @Test + public void TestExpressionSet() { + createDialogContext(dc -> { + dc.getState().setValue("turn.x.y.z", null); + Assert.assertNull(dc.getState().getValue("turn.x.y.z", null, Object.class)); + return CompletableFuture.completedFuture(null); + }).startTest(); + } + + @Test + public void TestConversationResetOnException() { + MemoryStorage storage = new MemoryStorage(); + UserState userState = new UserState(storage); + ConversationState conversationState = new ConversationState(storage); + + TestAdapter adapter = new TestAdapter() + .useStorage(storage) + .useBotState(userState, conversationState) + .use(new TranscriptLoggerMiddleware(new TraceTranscriptLogger())); + + adapter.setOnTurnError((turnContext, exception) -> { + conversationState.delete(turnContext); + return turnContext.sendActivity(exception.getMessage()).thenApply(result -> null); + }); + + DialogManager dm = new DialogManager(new TestDialog(), null); + + new TestFlow((TestAdapter) adapter, (turnContext) -> { + return dm.onTurn(turnContext).thenApply(result -> null); + }) + .send("yo1") + .assertReply("unknown") + .send("yo2") + .assertReply("havedata") + .send("throw") + .assertReply("java.lang.RuntimeException: throwing") + .send("yo3") + .assertReply("unknown") + .send("yo4") + .assertReply("havedata") + .startTest() + .join(); + } + + @Test + public void TestConversationResetOnExpiration() { + MemoryStorage storage = new MemoryStorage(); + UserState userState = new UserState(storage); + ConversationState conversationState = new ConversationState(storage); + + TestAdapter adapter = new TestAdapter() + .useStorage(storage) + .useBotState(userState, conversationState); + + adapter.setOnTurnError((turnContext, exception) -> { + conversationState.delete(turnContext); + return turnContext.sendActivity(exception.getMessage()).thenApply(result -> null); + }); + + DialogManager dm = new DialogManager(new TestDialog(), null); + dm.setExpireAfter(1000); + + new TestFlow((TestAdapter)adapter, (turnContext) -> { + return dm.onTurn(turnContext).thenApply(result -> null); + }) + .send("yo") + .assertReply("unknown") + .send("yo") + .assertReply("havedata") + .delay(1100) + .send("yo") + .assertReply("unknown", "Should have expired conversation and ended up with yo->unknown") + .send("yo") + .assertReply("havedata") + .send("yo") + .assertReply("havedata") + .startTest() + .join(); + } + + @Test + public void TestChangeTracking() { + createDialogContext(dc -> { + + DialogStateManager state = dc.getState(); + List pathList = new ArrayList(); + pathList.add("dialog.user.first"); + pathList.add("dialog.user.last"); + List dialogPaths = state.trackPaths(pathList); + + state.setValue("dialog.eventCounter", 0); + Assert.assertFalse(state.anyPathChanged(0, dialogPaths)); + + state.setValue("dialog.eventCounter", 1); + state.setValue("dialog.foo", 3); + Assert.assertFalse(state.anyPathChanged(0, dialogPaths)); + + state.setValue("dialog.eventCounter", 2); + state.setValue("dialog.user.first", "bart"); + Assert.assertTrue(state.anyPathChanged(1, dialogPaths)); + + state.setValue("dialog.eventCounter", 3); + Map testMap = new HashMap(); + testMap.put("first", "tom"); + testMap.put("last", "starr"); + state.setValue("dialog.user", testMap); + Assert.assertTrue(state.anyPathChanged(2, dialogPaths)); + + state.setValue("dialog.eventCounter", 4); + Assert.assertFalse(state.anyPathChanged(3, dialogPaths)); + return CompletableFuture.completedFuture(null); + }).startTest().join(); + } + + @Test + public void TestMemoryScope_PathResolver_Registration() { + final String key = "testKey"; + String fullKey = String.format("test.%s", key); + String shortKey = String.format("^^%s", key); + final String testValue = "testValue"; + + createDialogContext(dc -> { + DialogStateManager state = dc.getState(); + state.setValue(fullKey, testValue); + Assert.assertEquals(testValue, state.getValue(fullKey, "", String.class)); + Assert.assertEquals(testValue, state.getStringValue(shortKey, "")); + return CompletableFuture.completedFuture(null); + }).startTest().join(); + } + + class TestDialog extends Dialog { + + public TestDialog() { + super("TestDialog"); + } + + @Override + public CompletableFuture beginDialog(DialogContext dc, Object options) { + String data = dc.getState().getValue("conversation.test", "unknown", String.class); + dc.getContext().sendActivity(data).join(); + dc.getState().setValue("conversation.test", "havedata"); + return CompletableFuture.completedFuture(new DialogTurnResult(DialogTurnStatus.WAITING)); + } + + @Override + public CompletableFuture continueDialog(DialogContext dc) { + switch (dc.getContext().getActivity().getText()) { + case "throw": + throw new RuntimeException("throwing"); + case "end": + return dc.endDialog(); + default: + } + + String data = dc.getState().getValue("conversation.test", "unknown", String.class); + dc.getContext().sendActivity(data).join(); + return CompletableFuture.completedFuture(new DialogTurnResult(DialogTurnStatus.WAITING)); + } + } + + public class NestedContainerDialog extends ComponentDialog implements DialogDependencies { + public NestedContainerDialog() { + super("NestedContainerDialog"); + addDialog(new NestedContainerDialog1()); + addDialog(new NestedContainerDialog2()); + } + + @Override + public CompletableFuture beginDialog(DialogContext dc, Object options) { + dc.getState().setValue("$name", "testDialog"); + String nameResult = dc.getState().getValue("$name", "", String.class); + dc.getContext().sendActivity(nameResult).join(); + nameResult = dc.getState().getValue("dialog.name", "", String.class); + dc.getContext().sendActivity(nameResult).join(); + return dc.beginDialog("d1"); + } + + @Override + public CompletableFuture resumeDialog(DialogContext dc, DialogReason reason, Object result) { + if (((String) result).equals("d2")) { + return dc.endDialog(); + } + + String nameResult = dc.getState().getValue("$name", "", String.class); + dc.getContext().sendActivity(nameResult).join(); + nameResult = dc.getState().getValue("dialog.name", "", String.class); + dc.getContext().sendActivity(nameResult).join(); + return dc.beginDialog("d2"); + } + + @Override + public List getDependencies() { + return new ArrayList(getDialogs().getDialogs()); + } + } + + public class NestedContainerDialog2 extends ComponentDialog { + public NestedContainerDialog2() { + super("d2"); + } + + @Override + public CompletableFuture beginDialog(DialogContext outerDc, Object options) { + outerDc.getState().setValue("$name", "d2"); + String nameResult = outerDc.getState().getValue("$name", "", String.class); + outerDc.getContext().sendActivity(String.format("nested %s", nameResult)); + return outerDc.endDialog(getId()); + } + } + + public class NestedContainerDialog1 extends ComponentDialog { + public NestedContainerDialog1() { + super("d1"); + } + + @Override + public CompletableFuture beginDialog(DialogContext dc, Object options) { + dc.getState().setValue("$name", "d1"); + String nameResult = dc.getState().getStringValue("$name", ""); + dc.getContext().sendActivity(String.format("nested %s", nameResult)).join(); + nameResult = dc.getState().getValue("dialog.name", "", String.class); + dc.getContext().sendActivity(String.format("nested %s", nameResult)).join(); + return dc.endDialog(getId()); + } + } + + public class D1Dialog extends ComponentDialog implements DialogDependencies { + public D1Dialog() { + super("d1"); + addDialog(new D2Dialog()); + } + + public int MaxValue = 10; + + @Override + public CompletableFuture beginDialog(DialogContext dc, Object options) { + dc.getState().setValue("dialog.xyz", "dialog"); + dc.getContext().sendActivity(dc.getState().getValue("dialog.xyz", "", String.class)).join(); + dc.getContext().sendActivity(dc.getState().getValue("$xyz", "", String.class)).join(); + dc.getState().setValue("$aaa", "dialog2"); + dc.getContext().sendActivity(dc.getState().getValue("dialog.aaa", "", String.class)).join(); + dc.getContext().sendActivity(dc.getState().getStringValue("$aaa", "")).join(); + dc.getContext().sendActivity(dc.getState().getStringValue("%MaxValue", "")).join(); + String json = "{ \"test\" : 123 }"; + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = null; + try { + jsonNode = objectMapper.readTree(json); + } catch (JsonProcessingException e) { + jsonNode = null; + } + return dc.beginDialog("d2", jsonNode); + } + + @Override + public List getDependencies() { + return new ArrayList(getDialogs().getDialogs()); + } + + @Override + public CompletableFuture resumeDialog(DialogContext dc, DialogReason reason, Object result) { + dc.getState().setValue("$xyz", result); + dc.getContext().sendActivity(dc.getState().getStringValue("$xyz", "")).join(); + return dc.endDialog(result); + } + } + + public class D2Dialog extends Dialog { + public D2Dialog() { + super("d2"); + } + + public int MaxValue = 20; + + @Override + public CompletableFuture beginDialog(DialogContext dc, Object options) { + dc.getState().setValue("dialog.options", options); + dc.getState().setValue("$bbb", "bbb"); + dc.getContext().sendActivity(dc.getState().getValue("$bbb", "", String.class)).join(); + dc.getContext().sendActivity(dc.getState().getValue("dialog.options.test", "", String.class)).join(); + dc.getContext().sendActivity(dc.getState().getValue("%MaxValue", "", String.class)).join(); + return dc.endDialog(dc.getState().getValue("$bbb", "", String.class)); + } + } + + private TestFlow createFlow(Dialog dialog, ConversationState convoState, UserState userState, boolean sendTrace) { + TestAdapter adapter = new TestAdapter( + TestAdapter.createConversationReference(name.getMethodName(), "testuser", "testBot"), sendTrace) + .useStorage(new MemoryStorage()).useBotState(new UserState(new MemoryStorage())) + .useBotState(convoState != null ? convoState : new ConversationState(new MemoryStorage())) + .use(new TranscriptLoggerMiddleware(new TraceTranscriptLogger())); + + DialogManager dm = new DialogManager(dialog, null); + + return new TestFlow(adapter, (turnContext -> { + dm.onTurn(turnContext).join(); + return CompletableFuture.completedFuture(null); + })); + } private TestFlow createDialogContext(DialogTestFunction handler) { TestAdapter adapter = new TestAdapter( @@ -258,10 +795,46 @@ private TestFlow createDialogContext(DialogTestFunction handler) { .useBotState(new ConversationState(new MemoryStorage())); DialogManager dm = new DialogManager(new LamdbaDialog(name.getMethodName(), handler), name.getMethodName()); - // dm.getInitialTurnState().add(new ResourceExplorer()); + dm.getInitialTurnState().add(new MemoryScope[] { new TestMemoryScope() }); + dm.getInitialTurnState().add(new PathResolver[] { new DoubleCaratPathResolver() }); return new TestFlow(adapter, (turnContext -> { - dm.onTurn(turnContext); + dm.onTurn(turnContext).join(); return CompletableFuture.completedFuture(null); - })).sendConverationUpdate(); + })).sendConversationUpdate(); + } + + protected class DoubleCaratPathResolver extends AliasPathResolver { + public DoubleCaratPathResolver() { + super("^^", "test.", ""); + } + } + + protected class TestMemoryScope extends MemoryScope { + + public TestMemoryScope() { + super("test", false); + } + + @Override + public Object getMemory(DialogContext dc) { + if (dc == null) { + throw new IllegalArgumentException("dialogContext cannot be null"); + } + Object result = dc.getContext().getTurnState().get(ScopePath.TURN); + if (result == null) { + result = new HashMap(); + dc.getContext().getTurnState().add(ScopePath.TURN, result); + } + return result; + } + + @Override + public void setMemory(DialogContext dc, Object memory) { + if (dc == null) { + throw new IllegalArgumentException("dialogContext cannot be null"); + } + dc.getContext().getTurnState().add(ScopePath.TURN, memory); + } + } } diff --git a/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/Foo.java b/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/Foo.java new file mode 100644 index 000000000..0007b0ed4 --- /dev/null +++ b/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/Foo.java @@ -0,0 +1,36 @@ +package com.microsoft.bot.dialogs; + +public class Foo { + private String name; + private int age; + private boolean cool; + private Bar subname; + + public Foo() { + + } + + public Foo(String name, int age, boolean cool, Bar subname) { + this.name = name; + this.age = age; + this.cool = cool; + this.subname = subname; + } + + public String getName() { + return this.name; + } + + public int getAge() { + return this.age; + } + + public boolean getCool() { + return this.cool; + } + + public Bar getSubname() { + return this.subname; + } + +} diff --git a/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/MemoryScopeTests.java b/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/MemoryScopeTests.java index dd04e1af6..10fa22c5b 100644 --- a/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/MemoryScopeTests.java +++ b/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/MemoryScopeTests.java @@ -8,6 +8,7 @@ import java.util.concurrent.CompletableFuture; import com.microsoft.bot.builder.BotState; +import com.microsoft.bot.builder.ComponentRegistration; import com.microsoft.bot.builder.ConversationState; import com.microsoft.bot.builder.MemoryStorage; import com.microsoft.bot.builder.Storage; @@ -29,6 +30,7 @@ import org.javatuples.Pair; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; @@ -37,7 +39,6 @@ public class MemoryScopeTests { @Rule public TestName testName = new TestName(); - public TestFlow CreateDialogContext(DialogTestFunction handler) { TestAdapter adapter = new TestAdapter( TestAdapter.createConversationReference(testName.getMethodName(), "User1", "Bot")); @@ -46,7 +47,7 @@ public TestFlow CreateDialogContext(DialogTestFunction handler) { DialogManager dm = new DialogManager(new LamdbaDialog(testName.getMethodName(), handler), null); return new TestFlow(adapter, (turnContext) -> { return dm.onTurn(turnContext).thenApply(dialogManagerResult -> null); - }).sendConverationUpdate(); + }).sendConversationUpdate(); } @Test diff --git a/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/RecognizerTest.java b/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/RecognizerTest.java new file mode 100644 index 000000000..997efdd1c --- /dev/null +++ b/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/RecognizerTest.java @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MT License. + +package com.microsoft.bot.dialogs; + +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.microsoft.bot.builder.BotTelemetryClient; +import com.microsoft.bot.builder.IntentScore; +import com.microsoft.bot.builder.MessageFactory; +import com.microsoft.bot.builder.RecognizerResult; +import com.microsoft.bot.builder.TurnContextImpl; +import com.microsoft.bot.builder.adapters.TestAdapter; +import com.microsoft.bot.schema.Activity; + +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(MockitoJUnitRunner.class) +public class RecognizerTest { + + @Mock + BotTelemetryClient telemetryClient; + + @Test + public void LogsTelemetry() { + + MyRecognizerSubclass recognizer = new MyRecognizerSubclass(); + recognizer.setTelemetryClient(telemetryClient); + TestAdapter adapter = new TestAdapter( + TestAdapter.createConversationReference("RecognizerLogsTelemetry", "testUser", "testBot")); + Activity activity = MessageFactory.text("hi"); + TurnContextImpl context = new TurnContextImpl(adapter, activity); + DialogContext dc = new DialogContext(new DialogSet(), context, new DialogState()); + + RecognizerResult result = recognizer.recognize(dc, activity).join(); + + Map expectedProperties = new HashMap(); + expectedProperties.put("TopIntent", "myTestIntent"); + expectedProperties.put("Intents", "{\"myTestIntent\":{\"score\":1.0}}"); + expectedProperties.put("Text", "hi"); + expectedProperties.put("AlteredText", null); + expectedProperties.put("AdditionalProperties", null); + expectedProperties.put("TopIntentScore", "1.0"); + expectedProperties.put("Entities", null); + + + verify(telemetryClient, atLeastOnce()).trackEvent("MyRecognizerSubclassResult", expectedProperties, null); + } + + /** + * Subclass to test + * {@link Recognizer#fillRecognizerResultTelemetryProperties(RecognizerResult, + * Dictionary{String,String}, DialogContext)} functionality. + */ + private class MyRecognizerSubclass extends Recognizer { + + @Override + public CompletableFuture recognize(DialogContext dialogContext, + Activity activity, + Map telemetryProperties, + Map telemetryMetrics + ) { + + String text = activity.getText() != null ? activity.getText() : ""; + + RecognizerResult recognizerResult = new RecognizerResult(); + recognizerResult.setText(text); + recognizerResult.setAlteredText(null); + Map intentMap = new HashMap(); + IntentScore intentScore = new IntentScore(); + intentScore.setScore(1.0); + intentMap.put("myTestIntent", intentScore); + recognizerResult.setIntents(intentMap); + + trackRecognizerResult(dialogContext, "MyRecognizerSubclassResult", + fillRecognizerResultTelemetryProperties(recognizerResult, + telemetryProperties, + dialogContext), + telemetryMetrics); + + return CompletableFuture.completedFuture(recognizerResult); + } + } +} diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Activity.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Activity.java index 57f282ae0..9d283c192 100644 --- a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Activity.java +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Activity.java @@ -399,24 +399,11 @@ public static Activity clone(Activity activity) { cloned.setProperties(entry.getKey(), entry.getValue()); } - cloned = ensureActivityHasId(cloned); - - return cloned; - } - - private static Activity ensureActivityHasId(Activity activity) { - Activity activityWithId = activity; - - if (activity == null) { - throw new IllegalArgumentException("Cannot check or add Id on a null Activity."); - } - - if (activity.getId() == null) { - String generatedId = String.format("g_%s", UUID.randomUUID().toString()); - activity.setId(generatedId); + if (cloned.getId() == null) { + cloned.setId(String.format("g_%s", UUID.randomUUID().toString())); } - return activityWithId; + return cloned; } /** @@ -1567,11 +1554,8 @@ protected boolean isActivity(String activityType) { result = thisType.length() == activityType.length(); if (!result) { - // Finally, if the type is longer than the type they're looking for then we need - // to check if there's - // a / separator right after the type they're looking for - result = thisType.length() > activityType.length() - && thisType.indexOf(activityType.length()) == '/'; + // If there is a / separator right after the type they're looking for + result = thisType.charAt(activityType.length()) == '/'; } } @@ -1833,10 +1817,10 @@ public void teamsNotifyUser() { if (teamsChannelData == null) { teamsChannelData = new TeamsChannelData(); - setChannelData(teamsChannelData); } teamsChannelData.setNotification(new NotificationInfo(true)); + setChannelData(teamsChannelData); } /** @@ -1855,10 +1839,10 @@ public void teamsNotifyUser(boolean alertInMeeting, String externalResourceUrl) if (teamsChannelData == null) { teamsChannelData = new TeamsChannelData(); - setChannelData(teamsChannelData); } teamsChannelData.setNotification(new NotificationInfo(true, externalResourceUrl)); + setChannelData(teamsChannelData); } /** diff --git a/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/ActivityTest.java b/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/ActivityTest.java index 8966a4226..883f44a3a 100644 --- a/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/ActivityTest.java +++ b/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/ActivityTest.java @@ -4,14 +4,20 @@ package com.microsoft.bot.schema; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.microsoft.bot.schema.teams.TeamInfo; import com.microsoft.bot.schema.teams.TeamsChannelData; +import com.microsoft.bot.schema.teams.TeamsMeetingInfo; import org.junit.Assert; import org.junit.Test; import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.List; @@ -114,6 +120,81 @@ public void ApplyConversationReference() { Assert.assertEquals(conversationReference.getActivityId(), activity.getReplyToId()); } + @Test + public void ApplyConversationReferenceOverload() { + Activity activity = createActivity(); + + ConversationReference conversationReference = new ConversationReference(); + conversationReference.setChannelId("123"); + conversationReference.setServiceUrl("serviceUrl"); + ConversationAccount conversation = new ConversationAccount(); + conversation.setId("456"); + conversationReference.setConversation(conversation); + ChannelAccount userAccount = new ChannelAccount(); + userAccount.setId("abc"); + conversationReference.setUser(userAccount); + ChannelAccount botAccount = new ChannelAccount(); + botAccount.setId("def"); + conversationReference.setBot(botAccount); + conversationReference.setActivityId("12345"); + // Intentionally oddly-cased to check that it isn't defaulted somewhere, but + // tests stay in English + conversationReference.setLocale("en-uS"); + + activity.applyConversationReference(conversationReference); + + Assert.assertEquals(conversationReference.getChannelId(), activity.getChannelId()); + Assert.assertEquals(conversationReference.getLocale(), activity.getLocale()); + Assert.assertEquals(conversationReference.getServiceUrl(), activity.getServiceUrl()); + Assert.assertEquals(conversationReference.getConversation().getId(), activity.getConversation().getId()); + + Assert.assertEquals(conversationReference.getBot().getId(), activity.getFrom().getId()); + Assert.assertEquals(conversationReference.getUser().getId(), activity.getRecipient().getId()); + Assert.assertEquals(conversationReference.getActivityId(), activity.getReplyToId()); + } + + @Test + public void ApplyConversationReferenceOverloadAlternatePaths() { + Activity activity = createActivity(); + + ConversationReference conversationReference = new ConversationReference(); + conversationReference.setChannelId("123"); + conversationReference.setServiceUrl("serviceUrl"); + ConversationAccount conversation = new ConversationAccount(); + conversation.setId("456"); + conversationReference.setConversation(conversation); + ChannelAccount userAccount = new ChannelAccount(); + userAccount.setId("abc"); + conversationReference.setUser(userAccount); + ChannelAccount botAccount = new ChannelAccount(); + botAccount.setId("def"); + conversationReference.setBot(botAccount); + conversationReference.setActivityId(null); + conversationReference.setLocale(null); + + activity.applyConversationReference(conversationReference, false); + + Assert.assertEquals(conversationReference.getChannelId(), activity.getChannelId()); + Assert.assertEquals("en-uS", activity.getLocale()); + Assert.assertEquals(conversationReference.getServiceUrl(), activity.getServiceUrl()); + Assert.assertEquals(conversationReference.getConversation().getId(), activity.getConversation().getId()); + + Assert.assertEquals(conversationReference.getBot().getId(), activity.getFrom().getId()); + Assert.assertEquals(conversationReference.getUser().getId(), activity.getRecipient().getId()); + Assert.assertEquals(conversationReference.getActivityId(), activity.getReplyToId()); + + activity.applyConversationReference(conversationReference, true); + + Assert.assertEquals(conversationReference.getChannelId(), activity.getChannelId()); + Assert.assertEquals("en-uS", activity.getLocale()); + Assert.assertEquals(conversationReference.getServiceUrl(), activity.getServiceUrl()); + Assert.assertEquals(conversationReference.getConversation().getId(), activity.getConversation().getId()); + + Assert.assertEquals(conversationReference.getUser().getId(), activity.getFrom().getId()); + Assert.assertEquals(conversationReference.getBot().getId(), activity.getRecipient().getId()); + Assert.assertEquals(conversationReference.getActivityId(), activity.getReplyToId()); + } + @Test public void CreateTraceAllowsNullRecipient() { Activity activity = createActivity(); @@ -240,6 +321,11 @@ public void DeserializeActivityWithDifferentTimeZone() throws IOException { + " \"eventType\":\"teamMemberAdded\", " + " \"tenant\": {" + " \"id\": \"tenant_id\"" + " }" + " }" + "}"; + private static final String serializedActivityFromTeamsWithoutNotificationTeamsChannelIdOrTeamId = "{ " + + " \"channelId\": \"msteams\", \"channelData\": { \"channel\": { \"id\": \"channel_id\", \"name\": " + + "\"channel_name\" }, \"team\": { \"id\": \"team_id\", \"name\": \"team_name\", \"aadGroupId\": " + + "\"aad_groupid\" }, \"eventType\": \"teamMemberAdded\", \"tenant\": { \"id\": \"tenant_id\" } } }"; + @Test public void GetInformationForMicrosoftTeams() throws JsonProcessingException, IOException { ObjectMapper objectMapper = new ObjectMapper(); @@ -266,6 +352,136 @@ public void GetInformationForMicrosoftTeams() throws JsonProcessingException, IO Assert.assertEquals("tenant_id", teamsChannelData.getTenant().getId()); } + @Test + public void GetTeamsChannelIdBadChannelData() { + Activity activity = new Activity(); + activity.setChannelData("badChannelData"); + String channelId = activity.teamsGetChannelId(); + Assert.assertNull(channelId); + } + + @Test + public void GetTeamsTeamIdBadChannelData() { + Activity activity = new Activity(); + activity.setChannelData("badChannelData"); + String channelId = activity.teamsGetTeamId(); + Assert.assertNull(channelId); + } + + @Test + public void GetTeamsTeamIdNullChannelData() { + Activity activity = new Activity(); + String channelId = activity.teamsGetTeamId(); + Assert.assertNull(channelId); + } + + @Test + public void GetTeamsGetInfo() throws JsonProcessingException, IOException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.findAndRegisterModules(); + + Activity activity = objectMapper.readValue( + ActivityTest.serializedActivityFromTeamsWithoutTeamsChannelIdorTeamId, Activity.class); + + TeamInfo teamsInfo = activity.teamsGetTeamInfo(); + Assert.assertNotNull(teamsInfo); + } + + @Test + public void GetTeamsGetInfoBadChannelData() { + Activity activity = new Activity(); + activity.setChannelData("badChannelData"); + TeamInfo teamInfo = activity.teamsGetTeamInfo(); + Assert.assertNull(teamInfo); + } + @Test + public void TeamsNotifyUser() throws JsonProcessingException, IOException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.findAndRegisterModules(); + Activity activity = objectMapper.readValue( + ActivityTest.serializedActivityFromTeamsWithoutNotificationTeamsChannelIdOrTeamId, Activity.class); + + TeamsChannelData channelData = activity.teamsGetChannelData(); + Assert.assertNull(channelData.getNotification()); + activity.teamsNotifyUser(); + Assert.assertNotNull(activity.teamsGetChannelData().getNotification()); + } + + @Test + public void TeamsNotifyUserBadChannelData() throws JsonProcessingException, IOException { + Activity activity = new Activity(); + activity.setChannelData("badChannelData"); + + TeamsChannelData channelData = activity.teamsGetChannelData(); + Assert.assertNull(channelData); + activity.teamsNotifyUser(); + Assert.assertNotNull(activity.teamsGetChannelData().getNotification()); + } + + @Test + public void TeamsNotifyUserAlertInMeeting() throws JsonProcessingException, IOException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.findAndRegisterModules(); + Activity activity = objectMapper.readValue( + ActivityTest.serializedActivityFromTeamsWithoutNotificationTeamsChannelIdOrTeamId, Activity.class); + + TeamsChannelData channelData = activity.teamsGetChannelData(); + Assert.assertNull(channelData.getNotification()); + activity.teamsNotifyUser(true, "externalresourceURL"); + Assert.assertNotNull(activity.teamsGetChannelData().getNotification()); + Assert.assertEquals(activity.teamsGetChannelData().getNotification().getExternalResourceUrl(), + "externalresourceURL"); + Assert.assertTrue(activity.teamsGetChannelData().getNotification().getAlertInMeeting()); + } + + @Test + public void TeamsNotifyUserAlertInMeetingBadChannelData() throws JsonProcessingException, IOException { + Activity activity = new Activity(); + activity.setChannelData("badChannelData"); + + Assert.assertNull(activity.teamsGetChannelData()); + activity.teamsNotifyUser(true, "externalresourceURL"); + Assert.assertNotNull(activity.teamsGetChannelData().getNotification()); + Assert.assertEquals(activity.teamsGetChannelData().getNotification().getExternalResourceUrl(), + "externalresourceURL"); + Assert.assertTrue(activity.teamsGetChannelData().getNotification().getAlertInMeeting()); + } + + + @Test + public void TeamsGetMeetingInfoNull() throws JsonProcessingException, IOException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.findAndRegisterModules(); + Activity activity = objectMapper.readValue( + ActivityTest.serializedActivityFromTeamsWithoutNotificationTeamsChannelIdOrTeamId, Activity.class); + + TeamsMeetingInfo meetingInfo = activity.teamsGetMeetingInfo(); + Assert.assertNull(meetingInfo); + } + + @Test + public void TeamsGetMeetingInfo() throws JsonProcessingException, IOException { + Activity activity = new Activity(); + TeamsChannelData channelData = new TeamsChannelData(); + TeamsMeetingInfo meeting = new TeamsMeetingInfo(); + meeting.setId("meetingId"); + channelData.setMeeting(meeting); + activity.setChannelData(channelData); + + TeamsMeetingInfo meetingInfo = activity.teamsGetMeetingInfo(); + Assert.assertNotNull(meetingInfo); + Assert.assertEquals(meetingInfo.getId(), "meetingId"); + } + + @Test + public void TeamsGetMeetingInfoBadChannelData() throws JsonProcessingException, IOException { + Activity activity = new Activity(); + activity.setChannelData("badChannelData"); + + TeamsMeetingInfo meetingInfo = activity.teamsGetMeetingInfo(); + Assert.assertNull(meetingInfo); + } + @Test public void CreateMessageActivity() { Activity activity = Activity.createMessageActivity(); @@ -336,6 +552,16 @@ public void CreateTraceActivity() { Assert.assertEquals(activity.getValueType(), valueType); Assert.assertEquals(activity.getValue(), value); Assert.assertEquals(activity.getLabel(), label); + + Activity secondActivity = Activity.createTraceActivity(name); + Assert.assertEquals(secondActivity.getType(), ActivityTypes.TRACE); + Assert.assertEquals(secondActivity.getName(), name); + + Activity thirdActivity = Activity.createTraceActivity(name, null, value, label); + Assert.assertEquals(thirdActivity.getType(), ActivityTypes.TRACE); + Assert.assertEquals(thirdActivity.getName(), name); + + Assert.assertTrue(thirdActivity.isType(ActivityTypes.TRACE)); } @Test @@ -363,6 +589,17 @@ public void CreateReply() { Assert.assertEquals(reply.getType(), ActivityTypes.MESSAGE); Assert.assertEquals(reply.getText(), text); Assert.assertEquals(reply.getLocale(), locale); + + activity.setFrom(null); + activity.setRecipient(null); + activity.setConversation(null); + Activity reply2 = activity.createReply(text); + Assert.assertEquals(reply2.getType(), ActivityTypes.MESSAGE); + Assert.assertEquals(reply2.getText(), text); + Assert.assertEquals(reply2.getLocale(), "en-uS"); + Assert.assertTrue(reply2.getFrom() != null); + Assert.assertTrue(reply2.getRecipient() != null); + Assert.assertTrue(reply2.getConversation() != null); } @Test @@ -457,6 +694,13 @@ public void GetMentions() { Assert.assertEquals(mentionsResult.get(0).getType(), "mention"); } + @Test + public void GetMentionsNull() { + Activity activity = createActivity(); + activity.setEntities(null); + Assert.assertTrue(activity.getMentions() != null); + } + @Test public void CreateTrace() { Activity activity = createActivity(); @@ -473,6 +717,18 @@ public void CreateTrace() { Assert.assertEquals(trace.getValue(), value); Assert.assertEquals(trace.getValueType(), valueType); Assert.assertEquals(trace.getLabel(), label); + + Activity secondActivity = createActivity(); + secondActivity.setRecipient(null); + secondActivity.setFrom(null); + Activity secondTrace = secondActivity.createTrace(name, value, null, label); + Assert.assertEquals(secondTrace.getType(), ActivityTypes.TRACE); + Assert.assertEquals(secondTrace.getName(), name); + Assert.assertEquals(secondTrace.getValue(), value); + Assert.assertEquals(secondTrace.getValueType(), value.getClass().getTypeName()); + Assert.assertEquals(secondTrace.getLabel(), label); + Assert.assertTrue(secondTrace.getRecipient() != null); + Assert.assertTrue(secondTrace.getFrom() != null); } @Test @@ -506,4 +762,458 @@ public void IsFromStreamingConnection() { Assert.assertTrue(activity.isFromStreamingConnection()); }); } + + private JsonNode getTestNode() { + String json = "{ \"item1\" : \"value1\" } "; + ObjectMapper objectMapper = new ObjectMapper(); + try { + return objectMapper.readTree(json); + } catch (JsonProcessingException e) { + return null; + } + } + + @Test + public void ActivityCloneTest() throws JsonProcessingException { + Activity activity = new Activity(ActivityTypes.MESSAGE); + activity.setAction("TestAction"); + + Attachment attachment = new Attachment(); + attachment.setContentType("testContentType"); + attachment.setContentUrl("testContentUrl"); + attachment.setContent("testContent"); + attachment.setName("testName"); + attachment.setThumbnailUrl("testThumbnailUrl"); + attachment.setProperties("testProperty", getTestNode()); + activity.setAttachment(attachment); + + activity.setCallerId("testCallerId"); + activity.setChannelData("testChannelData"); + activity.setCode(EndOfConversationCodes.BOT_TIMED_OUT); + + ConversationAccount conversation = new ConversationAccount("testConversation"); + activity.setConversation(conversation); + + activity.setDeliveryMode("testDeliveryMode"); + + List entityList = new ArrayList(); + Entity entity1 = new Entity(); + entity1.setType("testEntity"); + entityList.add(entity1); + activity.setEntities(entityList); + + LocalDateTime expiration = LocalDateTime.now(); + activity.setExpiration(expiration); + + ChannelAccount fromChannel = new ChannelAccount("fromChannel"); + activity.setFrom(fromChannel); + + activity.setHistoryDisclosed(true); + activity.setId("testId"); + activity.setImportance("testImportance"); + activity.setInputHint(InputHints.ACCEPTING_INPUT); + activity.setLabel("testLabel"); + + List listen = new ArrayList(); + listen.add("listen1"); + listen.add("listen2"); + activity.setListenFor(listen); + + activity.setLocalTimeZone("testLocalTimeZone"); + OffsetDateTime offsetDateTime = OffsetDateTime.now(); + activity.setLocalTimestamp(offsetDateTime); + activity.setLocale("testLocale"); + + List membersAdded = new ArrayList(); + ChannelAccount firstMember = new ChannelAccount("firstMember"); + ChannelAccount secondMember = new ChannelAccount("secondMember"); + membersAdded.add(firstMember); + membersAdded.add(secondMember); + activity.setMembersAdded(membersAdded); + + List membersRemoved = new ArrayList(); + ChannelAccount firstMemberRemoved = new ChannelAccount("firstMember"); + ChannelAccount secondMemberRemoved = new ChannelAccount("secondMember"); + membersRemoved.add(firstMemberRemoved); + membersRemoved.add(secondMemberRemoved); + activity.setMembersRemoved(membersRemoved); + + List mentions = new ArrayList(); + Mention firstMention = new Mention(); + firstMention.setText("testTest"); + firstMention.setMentioned(firstMember); + Mention secondMention = new Mention(); + secondMention.setText("testTest"); + secondMention.setMentioned(firstMember); + mentions.add(secondMention); + activity.setMentions(mentions); + + activity.setName("testName"); + activity.setProperties("testProperty", getTestNode()); + + List reactionsAdded = new ArrayList(); + MessageReaction firstReaction = new MessageReaction(); + firstReaction.setType("testType"); + reactionsAdded.add(firstReaction); + MessageReaction secondReaction = new MessageReaction(); + secondReaction.setType("testType"); + reactionsAdded.add(secondReaction); + activity.setReactionsAdded(reactionsAdded); + + List reactionsRemoved = new ArrayList(); + MessageReaction firstReactionRemoved = new MessageReaction(); + firstReactionRemoved.setType("testType"); + reactionsRemoved.add(firstReactionRemoved); + MessageReaction secondReactionRemoved = new MessageReaction(); + secondReactionRemoved.setType("testType"); + reactionsRemoved.add(secondReactionRemoved); + activity.setReactionsRemoved(reactionsRemoved); + + ChannelAccount recipientRemoved = new ChannelAccount(); + recipientRemoved.setId("testRecipient"); + activity.setRecipient(recipientRemoved); + + ConversationReference relatesToReference = new ConversationReference(); + relatesToReference.setActivityId("testActivityId"); + activity.setRelatesTo(relatesToReference); + + activity.setReplyToId("testReplyToId"); + activity.setServiceUrl("testServiceUrl"); + activity.setText("testText"); + activity.setTextFormat(TextFormatTypes.MARKDOWN); + + List textHighlights = new ArrayList(); + TextHighlight firstTextHighlight = new TextHighlight(); + firstTextHighlight.setText("testText"); + textHighlights.add(firstTextHighlight); + TextHighlight secondTextHighlight = new TextHighlight(); + secondTextHighlight.setText("testText"); + textHighlights.add(secondTextHighlight); + activity.setTextHighlights(textHighlights); + + OffsetDateTime timestamp = OffsetDateTime.now(); + activity.setTimestamp(timestamp); + + activity.setTopicName("testTopicName"); + activity.setType("testType"); + activity.setValue("testValue"); + activity.setValueType("testValueType"); + + Activity clonedActivity = Activity.clone(activity); + + Assert.assertEquals(activity.getAction(), clonedActivity.getAction()); + Assert.assertEquals(activity.getCallerId(), clonedActivity.getCallerId()); + Assert.assertEquals(activity.getChannelData(), clonedActivity.getChannelData()); + Assert.assertEquals(activity.getDeliveryMode(), clonedActivity.getDeliveryMode()); + Assert.assertEquals(activity.getId(), clonedActivity.getId()); + Assert.assertEquals(activity.getImportance(), clonedActivity.getImportance()); + Assert.assertEquals(activity.getLabel(), clonedActivity.getLabel()); + Assert.assertEquals(activity.getLocalTimezone(), clonedActivity.getLocalTimezone()); + Assert.assertEquals(activity.getLocale(), clonedActivity.getLocale()); + Assert.assertEquals(activity.getName(), clonedActivity.getName()); + Assert.assertEquals(activity.getReplyToId(), clonedActivity.getReplyToId()); + Assert.assertEquals(activity.getServiceUrl(), clonedActivity.getServiceUrl()); + Assert.assertEquals(activity.getSpeak(), clonedActivity.getSpeak()); + Assert.assertEquals(activity.getSummary(), clonedActivity.getSummary()); + Assert.assertEquals(activity.getText(), clonedActivity.getText()); + Assert.assertEquals(activity.getTopicName(), clonedActivity.getTopicName()); + Assert.assertEquals(activity.getType(), clonedActivity.getType()); + Assert.assertEquals(activity.getValue(), clonedActivity.getValue()); + Assert.assertEquals(activity.getValueType(), clonedActivity.getValueType()); + Assert.assertEquals(activity.getAttachmentLayout(), clonedActivity.getAttachmentLayout()); + Assert.assertEquals(activity.getAttachments().get(0).getName(), + clonedActivity.getAttachments().get(0).getName()); + Assert.assertEquals(activity.getChannelData(ChannelAccount.class).getId(), + clonedActivity.getChannelData(ChannelAccount.class).getId()); + Assert.assertEquals(activity.getCode(), clonedActivity.getCode()); + Assert.assertEquals(activity.getConversation().getName(), clonedActivity.getConversation().getName()); + Assert.assertEquals(activity.getConversationReference().getChannelId(), + clonedActivity.getConversationReference().getChannelId()); + Assert.assertEquals(activity.getEntities().get(0).getType(), clonedActivity.getEntities().get(0).getType()); + Assert.assertEquals(activity.getExpiration(), clonedActivity.getExpiration()); + Assert.assertEquals(activity.getFrom().getId(), clonedActivity.getFrom().getId()); + Assert.assertEquals(activity.getInputHint(), clonedActivity.getInputHint()); + Assert.assertEquals(activity.getListenFor(), clonedActivity.getListenFor()); + Assert.assertEquals(activity.getLocalTimestamp(), clonedActivity.getLocalTimestamp()); + Assert.assertEquals(activity.getMembersAdded().get(0).getId(), clonedActivity.getMembersAdded().get(0).getId()); + Assert.assertEquals(activity.getMembersRemoved().get(0).getId(), + clonedActivity.getMembersRemoved().get(0).getId()); + Assert.assertEquals(activity.getMentions().get(0).getText(), clonedActivity.getMentions().get(0).getText()); + Assert.assertEquals(activity.getProperties(), clonedActivity.getProperties()); + Assert.assertEquals(activity.getReactionsAdded().get(0).getType(), + clonedActivity.getReactionsAdded().get(0).getType()); + Assert.assertEquals(activity.getReactionsRemoved().get(0).getType(), + clonedActivity.getReactionsRemoved().get(0).getType()); + Assert.assertEquals(activity.getRecipient().getId(), clonedActivity.getRecipient().getId()); + Assert.assertEquals(activity.getRelatesTo().getActivityId(), clonedActivity.getRelatesTo().getActivityId()); + // add activity.getReplyConversationReference(reply) + Assert.assertEquals(activity.getSuggestedActions(), clonedActivity.getSuggestedActions()); + Assert.assertEquals(activity.getTextFormat(), clonedActivity.getTextFormat()); + Assert.assertEquals(activity.getTextHighlights(), clonedActivity.getTextHighlights()); + Assert.assertEquals(activity.getTimestamp(), clonedActivity.getTimestamp()); + } + + @Test + public void EnsureCloneAddsIdIfMissing() { + Activity testActivity = new Activity(ActivityTypes.COMMAND); + Assert.assertTrue(testActivity.getId() == null); + Activity clonedActivity = Activity.clone(testActivity); + Assert.assertTrue(clonedActivity.getId() != null); + } + + @Test + public void TryGetChannelData() { + Activity activity = createActivity(); + ResultPair channelData = activity.tryGetChannelData( + TeamsChannelData.class + ); + + activity.setChannelData(new TeamsChannelData()); + channelData = activity.tryGetChannelData( + TeamsChannelData.class + ); + Assert.assertTrue(channelData.getLeft()); + + activity.setChannelData(null); + Assert.assertNull(activity.teamsGetChannelData()); + } + + @Test + public void TryGetChannelDataBadChannelData() { + Activity activity = createActivity(); + activity.setChannelData("badChannelData"); + ResultPair channelData = activity.tryGetChannelData( + TeamsChannelData.class + ); + Assert.assertFalse(channelData.getLeft()); + Assert.assertNull(channelData.getRight()); + } + + @Test + public void RemoveRecipientMention() { + Activity activity = createActivity(); + activity.setText("firstName lastName\n"); + String expectedStrippedName = "lastName"; + + List mentionList = new ArrayList(); + Mention mention = new Mention(); + ChannelAccount channelAccount = new ChannelAccount(); + channelAccount.setId(activity.getRecipient().getId()); + channelAccount.setName("firstName"); + mention.setMentioned(channelAccount); + mentionList.add(mention); + activity.setMentions(mentionList); + + String strippedActivityText = activity.removeRecipientMention(); + Assert.assertEquals(strippedActivityText, expectedStrippedName); + } + + @Test + public void RemoveRecipientMentionImmutable() { + Activity activity = createActivity(); + activity.setText("firstName lastName\n"); + String expectedStrippedName = "lastName"; + + List mentionList = new ArrayList(); + Mention mention = new Mention(); + ChannelAccount channelAccount = new ChannelAccount(); + channelAccount.setId(activity.getRecipient().getId()); + channelAccount.setName("firstName"); + mention.setMentioned(channelAccount); + mentionList.add(mention); + activity.setMentions(mentionList); + + String strippedActivityText = Activity.removeRecipientMentionImmutable(activity); + Assert.assertEquals(strippedActivityText, expectedStrippedName); + } + + @Test + public void RemoveRecipientMentionNoRecipient() { + Activity activity = createActivity(); + activity.setText("firstName lastName\n"); + String expectedStrippedName = "firstName lastName\n"; + + List mentionList = new ArrayList(); + Mention mention = new Mention(); + ChannelAccount channelAccount = new ChannelAccount(); + channelAccount.setId(activity.getRecipient().getId()); + channelAccount.setName("firstName"); + mention.setMentioned(channelAccount); + mentionList.add(mention); + activity.setMentions(mentionList); + activity.setRecipient(null); + + String strippedActivityText = activity.removeRecipientMention(); + Assert.assertEquals(strippedActivityText, expectedStrippedName); + } + + @Test + public void RemoveRecipientMentionImmutableNoRecipient() { + Activity activity = createActivity(); + activity.setText("firstName lastName\n"); + String expectedStrippedName = "firstName lastName\n"; + + List mentionList = new ArrayList(); + Mention mention = new Mention(); + ChannelAccount channelAccount = new ChannelAccount(); + channelAccount.setId(activity.getRecipient().getId()); + channelAccount.setName("firstName"); + mention.setMentioned(channelAccount); + mentionList.add(mention); + activity.setMentions(mentionList); + activity.setRecipient(null); + + String strippedActivityText = Activity.removeRecipientMentionImmutable(activity); + Assert.assertEquals(strippedActivityText, expectedStrippedName); + } + + @Test + public void RemoveRecipientMentionText() { + Activity activity = createActivity(); + activity.setText("firstName lastName\n"); + String expectedStrippedName = "firstName"; + + List mentionList = new ArrayList(); + Mention mention = new Mention(); + mention.setText("lastName"); + ChannelAccount channelAccount = new ChannelAccount(); + channelAccount.setId(activity.getRecipient().getId()); + channelAccount.setName("firstName"); + mention.setMentioned(channelAccount); + mentionList.add(mention); + activity.setMentions(mentionList); + + String strippedActivityText = activity.removeRecipientMention(); + Assert.assertEquals(strippedActivityText, expectedStrippedName); + } + + @Test + public void RemoveRecipientMentionTextNoId() { + Activity activity = createActivity(); + activity.setText("firstName lastName\n"); + String expectedStrippedName = "firstName lastName\n"; + + List mentionList = new ArrayList(); + Mention mention = new Mention(); + mention.setText("lastName"); + ChannelAccount channelAccount = new ChannelAccount(); + channelAccount.setId(activity.getRecipient().getId()); + channelAccount.setName("firstName"); + mention.setMentioned(channelAccount); + mentionList.add(mention); + activity.setMentions(mentionList); + + String strippedActivityText = Activity.removeMentionTextImmutable(activity, null); + Assert.assertEquals(strippedActivityText, expectedStrippedName); + } + + @Test + public void RemoveRecipientMentionTextNoText() { + Activity activity = createActivity(); + activity.setText(""); + String expectedStrippedName = ""; + + List mentionList = new ArrayList(); + Mention mention = new Mention(); + mention.setText("lastName"); + ChannelAccount channelAccount = new ChannelAccount(); + channelAccount.setId(activity.getRecipient().getId()); + channelAccount.setName("firstName"); + mention.setMentioned(channelAccount); + mentionList.add(mention); + activity.setMentions(mentionList); + + String strippedActivityText = Activity.removeMentionTextImmutable(activity, "lastName"); + Assert.assertEquals(strippedActivityText, expectedStrippedName); + } + + + @Test + public void IsActivity() { + class MyActivity extends Activity { + @Override + public boolean isActivity(String activityType) { + return super.isActivity(activityType); + } + } + + MyActivity activity = new MyActivity(); + activity.setType(ActivityTypes.COMMAND); + + Assert.assertTrue(activity.isActivity(ActivityTypes.COMMAND)); + } + + @Test + public void IsActivityNoType() { + class MyActivity extends Activity { + @Override + public boolean isActivity(String activityType) { + return super.isActivity(activityType); + } + } + + MyActivity activity = new MyActivity(); + + Assert.assertFalse(activity.isActivity(ActivityTypes.COMMAND)); + } + + @Test + public void IsActivityExtendedType() { + class MyActivity extends Activity { + @Override + public boolean isActivity(String activityType) { + return super.isActivity(activityType); + } + } + + MyActivity activity = new MyActivity(); + activity.setType("TestType/subtype"); + + Assert.assertTrue(activity.isActivity("TestType")); + } + + @Test + public void IsActivityExtendedTypeNoMatch() { + class MyActivity extends Activity { + @Override + public boolean isActivity(String activityType) { + return super.isActivity(activityType); + } + } + + MyActivity activity = new MyActivity(); + activity.setType("TestTypesubtype"); + + Assert.assertFalse(activity.isActivity("TestType")); + } + + @Test + public void IsActivityNoMatch() { + class MyActivity extends Activity { + @Override + public boolean isActivity(String activityType) { + return super.isActivity(activityType); + } + } + + MyActivity activity = new MyActivity(); + activity.setType("DifferentType"); + + Assert.assertFalse(activity.isActivity("TestType")); + } + + @Test + public void IsActivityShorterTypeName() { + class MyActivity extends Activity { + @Override + public boolean isActivity(String activityType) { + return super.isActivity(activityType); + } + } + + MyActivity activity = new MyActivity(); + activity.setType("Test"); + + Assert.assertFalse(activity.isActivity("TestType")); + } } diff --git a/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/CardActionTest.java b/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/CardActionTest.java index 4e56f9905..f396bdbe3 100644 --- a/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/CardActionTest.java +++ b/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/CardActionTest.java @@ -22,4 +22,50 @@ public void TestImplicitConversation() { Assert.assertEquals("z", actions.getActions().get(2).getTitle()); Assert.assertEquals("z", actions.getActions().get(2).getValue()); } + + @Test + public void TestClone() { + + CardAction cardAction = new CardAction(); + cardAction.setChannelData("channelData"); + cardAction.setDisplayText("displayTest"); + cardAction.setImage("image"); + cardAction.setImageAltText("imageAltText"); + cardAction.setText("text"); + cardAction.setTitle("title"); + cardAction.setType(ActionTypes.CALL); + cardAction.setValue("value"); + + CardAction newCardAction = CardAction.clone(cardAction); + + Assert.assertEquals(cardAction.getChannelData(), newCardAction.getChannelData()); + Assert.assertEquals(cardAction.getDisplayText(), newCardAction.getDisplayText()); + Assert.assertEquals(cardAction.getImage(), newCardAction.getImage()); + Assert.assertEquals(cardAction.getImageAltText(), newCardAction.getImageAltText()); + Assert.assertEquals(cardAction.getText(), newCardAction.getText()); + Assert.assertEquals(cardAction.getTitle(), newCardAction.getTitle()); + Assert.assertEquals(cardAction.getType(), newCardAction.getType()); + Assert.assertEquals(cardAction.getValue(), newCardAction.getValue()); + } + @Test + public void TestCloneNull() { + CardAction newCardAction = CardAction.clone(null); + Assert.assertNull(newCardAction); + } + + @Test + public void TestConstructorTwoParams() { + CardAction cardAction = new CardAction(ActionTypes.CALL, "title"); + Assert.assertEquals(cardAction.getType(), ActionTypes.CALL); + Assert.assertEquals(cardAction.getTitle(), "title"); + } + + @Test + public void TestConstructorThreeParams() { + CardAction cardAction = new CardAction(ActionTypes.CALL, "title", "value"); + Assert.assertEquals(cardAction.getType(), ActionTypes.CALL); + Assert.assertEquals(cardAction.getTitle(), "title"); + Assert.assertEquals(cardAction.getValue(), "value"); + } + } diff --git a/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/MediaCardTest.java b/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/MediaCardTest.java new file mode 100644 index 000000000..2c59c3a21 --- /dev/null +++ b/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/MediaCardTest.java @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.schema; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +public class MediaCardTest { + // this really isn't an implicit conversion. it just matches the dotnet + // test. This tests the CardAction[] SuggestedActions constructor. + @Test + public void TestPropertySetterGetter() { + MediaCard mediaCard = new MediaCard(); + mediaCard.setAspect("aspect"); + mediaCard.setAutoloop(true); + mediaCard.setAutostart(true); + + List buttons = new ArrayList(); + CardAction cardAction1 = new CardAction(ActionTypes.CALL, "test1"); + CardAction cardAction2 = new CardAction(ActionTypes.DOWNLOAD_FILE, "test2"); + buttons.add(cardAction1); + buttons.add(cardAction2); + mediaCard.setButtons(buttons); + + mediaCard.setDuration("duration"); + + ThumbnailUrl thumbnailUrl = new ThumbnailUrl(); + thumbnailUrl.setAlt("alt"); + thumbnailUrl.setUrl("testUrl"); + mediaCard.setImage(thumbnailUrl); + + mediaCard.setShareable(true); + mediaCard.setSubtitle("subTitle"); + mediaCard.setText("text"); + mediaCard.setTitle("title"); + mediaCard.setValue("value"); + + Assert.assertEquals(mediaCard.getAspect(), "aspect"); + Assert.assertEquals(mediaCard.getAutoloop(), true); + Assert.assertEquals(mediaCard.getAutostart(), true); + Assert.assertEquals(mediaCard.getButtons().size(), 2); + Assert.assertEquals(mediaCard.getButtons().get(0).getType(), ActionTypes.CALL); + Assert.assertEquals(mediaCard.getButtons().get(0).getTitle(), "test1"); + Assert.assertEquals(mediaCard.getButtons().get(1).getType(), ActionTypes.DOWNLOAD_FILE); + Assert.assertEquals(mediaCard.getButtons().get(1).getTitle(), "test2"); + Assert.assertEquals(mediaCard.getDuration(), "duration"); + Assert.assertEquals(mediaCard.getImage().getUrl(), "testUrl"); + Assert.assertEquals(mediaCard.getImage().getAlt(), "alt"); + Assert.assertEquals(mediaCard.getShareable(), true); + Assert.assertEquals(mediaCard.getSubtitle(), "subTitle"); + Assert.assertEquals(mediaCard.getText(), "text"); + Assert.assertEquals(mediaCard.getTitle(), "title"); + Assert.assertEquals(mediaCard.getValue(), "value"); + } +} diff --git a/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/SerializationTest.java b/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/SerializationTest.java new file mode 100644 index 000000000..b9f6ff4d1 --- /dev/null +++ b/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/SerializationTest.java @@ -0,0 +1,209 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.schema; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import org.junit.Assert; +import org.junit.Test; + +/*** + * Tests to ensure the HeroCard methods work as expected. + */ +public class SerializationTest { + + @Test + public void testGetAs() { + Activity activity = createActivity(); + JsonNode activityNode = Serialization.objectToTree(activity); + Activity resultActivity = Serialization.getAs(activityNode, Activity.class); + Assert.assertEquals(activity.getId(), resultActivity.getId()); + Assert.assertEquals(activity.getFrom().getId(), resultActivity.getFrom().getId()); + Assert.assertEquals(activity.getConversation().getId(), resultActivity.getConversation().getId()); + } + + @Test + public void testGetAsNull() { + Activity resultActivity = Serialization.getAs(null, Activity.class); + Assert.assertNull(resultActivity); + } + + @Test + public void testClone() { + Activity activity = createActivity(); + Activity resultActivity = (Activity) Serialization.clone((Object) activity); + Assert.assertEquals(activity.getId(), resultActivity.getId()); + Assert.assertEquals(activity.getFrom().getId(), resultActivity.getFrom().getId()); + Assert.assertEquals(activity.getConversation().getId(), resultActivity.getConversation().getId()); + } + + @Test + public void testCloneNull() { + Activity resultActivity = (Activity) Serialization.clone((Object) null); + Assert.assertNull(resultActivity); + } + + @Test + public void testTreeToValue() { + Activity activity = createActivity(); + JsonNode activityNode = Serialization.objectToTree(activity); + Activity resultActivity = Serialization.treeToValue(activityNode, Activity.class); + Assert.assertEquals(activity.getId(), resultActivity.getId()); + Assert.assertEquals(activity.getFrom().getId(), resultActivity.getFrom().getId()); + Assert.assertEquals(activity.getConversation().getId(), resultActivity.getConversation().getId()); + } + + @Test + public void testFutureGetAs() { + Activity activity = createActivity(); + JsonNode activityNode = Serialization.objectToTree(activity); + Activity resultActivity = Serialization.futureGetAs(activityNode, Activity.class).join(); + Assert.assertEquals(activity.getId(), resultActivity.getId()); + Assert.assertEquals(activity.getFrom().getId(), resultActivity.getFrom().getId()); + Assert.assertEquals(activity.getConversation().getId(), resultActivity.getConversation().getId()); + } + + @Test + public void testToString() throws IOException { + Activity activity = createActivity(); + JsonNode activityNode = Serialization.objectToTree(activity); + String resultString = Serialization.toString(activityNode); + JsonNode jsonResult = Serialization.jsonToTree(resultString); + Activity resultActivity = Serialization.treeToValue(jsonResult, Activity.class); + Assert.assertEquals(activity.getId(), resultActivity.getId()); + Assert.assertEquals(activity.getFrom().getId(), resultActivity.getFrom().getId()); + Assert.assertEquals(activity.getConversation().getId(), resultActivity.getConversation().getId()); + } + + @Test + public void testToStringSilent() throws IOException { + Activity activity = createActivity(); + JsonNode activityNode = Serialization.objectToTree(activity); + String resultString = Serialization.toStringSilent(activityNode); + JsonNode jsonResult = Serialization.jsonToTree(resultString); + Activity resultActivity = Serialization.treeToValue(jsonResult, Activity.class); + Assert.assertEquals(activity.getId(), resultActivity.getId()); + Assert.assertEquals(activity.getFrom().getId(), resultActivity.getFrom().getId()); + Assert.assertEquals(activity.getConversation().getId(), resultActivity.getConversation().getId()); + } + + @Test + public void testAsNodeString() { + String testValue = "Hello world!"; + JsonNode node = Serialization.asNode(testValue); + Assert.assertEquals(node.asText(), testValue); + } + + @Test + public void testAsNodeInt() { + int testValue = 42; + JsonNode node = Serialization.asNode(testValue); + Assert.assertEquals(node.asInt(), testValue); + } + + @Test + public void testAsNodeLong() { + long testValue = 42; + JsonNode node = Serialization.asNode(testValue); + Assert.assertEquals(node.asLong(), testValue); + } + + @Test + public void testAsNodeFloat() { + float testValue = 42.42f; + JsonNode node = Serialization.asNode(testValue); + Assert.assertTrue(node.asDouble() == testValue); + } + + @Test + public void testAsNodeDouble() { + double testValue = 42.42; + JsonNode node = Serialization.asNode(testValue); + Assert.assertTrue(node.asDouble() == testValue); + } + + @Test + public void testAsNodeShort() { + short testValue = 42; + JsonNode node = Serialization.asNode(testValue); + Assert.assertEquals(node.asInt(), testValue); + } + + @Test + public void testAsNodeByte() { + byte testValue = 42; + JsonNode node = Serialization.asNode(testValue); + Assert.assertEquals(node.asInt(), testValue); + } + + @Test + public void testAsNodeBoolean() { + boolean testValue = true; + JsonNode node = Serialization.asNode(testValue); + Assert.assertEquals(node.asBoolean(), testValue); + } + + @Test + public void testCreateObjectNode() { + ObjectNode node = Serialization.createObjectNode(); + Assert.assertTrue(node instanceof ObjectNode); + } + + @Test + public void testCreateArraytNode() { + ArrayNode node = Serialization.createArrayNode(); + Assert.assertTrue(node instanceof ArrayNode); + } + + @Test + public void testConvert() { + String stringValue = "42"; + Integer result = Serialization.convert(stringValue, Integer.class); + Assert.assertTrue(result == 42); + Integer integerValue = 42; + String stringResult = Serialization.convert(integerValue, String.class); + Assert.assertTrue(stringResult.equals("42")); + } + + + private Activity createActivity() { + ChannelAccount account1 = new ChannelAccount(); + account1.setId("ChannelAccount_Id_1"); + account1.setName("ChannelAccount_Name_1"); + account1.setProperties("Name", JsonNodeFactory.instance.objectNode().put("Name", "Value")); + account1.setRole(RoleTypes.USER); + + ChannelAccount account2 = new ChannelAccount(); + account2.setId("ChannelAccount_Id_2"); + account2.setName("ChannelAccount_Name_2"); + account2.setProperties("Name", JsonNodeFactory.instance.objectNode().put("Name", "Value")); + account2.setRole(RoleTypes.USER); + + ConversationAccount conversationAccount = new ConversationAccount(); + conversationAccount.setConversationType("a"); + conversationAccount.setId("123"); + conversationAccount.setIsGroup(true); + conversationAccount.setName("Name"); + conversationAccount.setProperties("Name", JsonNodeFactory.instance.objectNode().put("Name", "Value")); + + Activity activity = new Activity(); + activity.setId("123"); + activity.setFrom(account1); + activity.setRecipient(account2); + activity.setConversation(conversationAccount); + activity.setChannelId("ChannelId123"); + // Intentionally oddly-cased to check that it isn't defaulted somewhere, but + // tests stay in English + activity.setLocale("en-uS"); + activity.setServiceUrl("ServiceUrl123"); + + return activity; + } +} diff --git a/pom.xml b/pom.xml index 8307b10ac..be92d4331 100644 --- a/pom.xml +++ b/pom.xml @@ -20,6 +20,7 @@ 1.8 3.1.0 3.14.0 + %regex[.*recognizers.*] @@ -540,6 +541,9 @@ **/*Test*.java + + ${exclude.tests} +