Added BotAdapterBracketingTest, BotAdapterTests, BotStateSetTests, and renamed BotFrameworkAdaterTests.

This commit is contained in:
Tracy Boehrer 2019-09-17 09:58:28 -05:00
Родитель 13a3e99c1f
Коммит f148cafff2
14 изменённых файлов: 378 добавлений и 198 удалений

Просмотреть файл

@ -15,28 +15,31 @@ import java.util.function.Function;
/**
* Represents a bot adapter that can connect a bot to a service endpoint.
* This class is abstract.
* The bot adapter encapsulates authentication processes and sends
* <p>The bot adapter encapsulates authentication processes and sends
* activities to and receives activities from the Bot Connector Service. When your
* bot receives an activity, the adapter creates a context object, passes it to your
* bot's application logic, and sends responses back to the user's channel.
* bot's application logic, and sends responses back to the user's channel.</p>
* <p>Use {@link #use(Middleware)} to add {@link Middleware} objects
* to your adapters middleware collection. The adapter processes and directs
* incoming activities in through the bot middleware pipeline to your bots logic
* and then back out again. As each activity flows in and out of the bot, each piece
* of middleware can inspect or act upon the activity, both before and after the bot
* logic runs.</p>
* <p>
* {@linkalso TurnContext}
* {@linkalso Activity}
* {@linkalso Bot}
* {@linkalso Middleware}
*
* {@link TurnContext}
* {@link Activity}
* {@link Bot}
* {@link Middleware}
*/
public abstract class BotAdapter {
/**
* The collection of middleware in the adapter's pipeline.
*/
protected final MiddlewareSet _middlewareSet = new MiddlewareSet();
protected final MiddlewareSet middlewareSet = new MiddlewareSet();
/**
* Error handler that can catch exceptions in the middleware or application.
*/
private OnTurnErrorHandler onTurnError;
/**
@ -63,7 +66,7 @@ public abstract class BotAdapter {
* For each turn, the adapter calls middleware in the order in which you added it.
*/
public BotAdapter use(Middleware middleware) {
_middlewareSet.use(middleware);
middlewareSet.use(middleware);
return this;
}
@ -76,7 +79,7 @@ public abstract class BotAdapter {
* If the activities are successfully sent, the task result contains
* an array of {@link ResourceResponse} objects containing the IDs that
* the receiving channel assigned to the activities.
* {@linkalso TurnContext.OnSendActivities(SendActivitiesHandler)}
* {@link TurnContext#onSendActivities(SendActivitiesHandler)}
*/
public abstract CompletableFuture<ResourceResponse[]> sendActivities(TurnContext context, Activity[] activities);
@ -92,7 +95,7 @@ public abstract class BotAdapter {
* channel assigned to the activity.
* <p>Before calling this, set the ID of the replacement activity to the ID
* of the activity to replace.</p>
* {@linkalso TurnContext.OnUpdateActivity(UpdateActivityHandler)}
* {@link TurnContext#onUpdateActivity(UpdateActivityHandler)}
*/
public abstract CompletableFuture<ResourceResponse> updateActivity(TurnContext context, Activity activity);
@ -105,7 +108,7 @@ public abstract class BotAdapter {
* @return A task that represents the work queued to execute.
* The {@link ConversationReference#getActivityId} of the conversation
* reference identifies the activity to delete.
* {@linkalso TurnContext.OnDeleteActivity(DeleteActivityHandler)}
* {@link TurnContext#onDeleteActivity(DeleteActivityHandler)}
*/
public abstract CompletableFuture<Void> deleteActivity(TurnContext context, ConversationReference reference);
@ -113,30 +116,32 @@ public abstract class BotAdapter {
/**
* Starts activity processing for the current bot turn.
*
* The adapter calls middleware in the order in which you added it.
* The adapter passes in the context object for the turn and a next delegate,
* and the middleware calls the delegate to pass control to the next middleware
* in the pipeline. Once control reaches the end of the pipeline, the adapter calls
* the {@code callback} method. If a middleware component doesnt call
* the next delegate, the adapter does not call any of the subsequent middlewares
* {@link Middleware#onTurn(TurnContext, NextDelegate)}
* methods or the callback method, and the pipeline short circuits.
*
* <p>When the turn is initiated by a user activity (reactive messaging), the
* callback method will be a reference to the bot's
* {@link Bot#onTurn(TurnContext)} method. When the turn is
* initiated by a call to {@link #continueConversation(String, ConversationReference, BotCallbackHandler)}
* (proactive messaging), the callback method is the callback method that was provided in the call.</p>
*
* @param context The turn's context object.
* @param callback A callback method to run at the end of the pipeline.
* @return A task that represents the work queued to execute.
* @throws NullPointerException {@code context} is null.
* The adapter calls middleware in the order in which you added it.
* The adapter passes in the context object for the turn and a next delegate,
* and the middleware calls the delegate to pass control to the next middleware
* in the pipeline. Once control reaches the end of the pipeline, the adapter calls
* the {@code callback} method. If a middleware component doesnt call
* the next delegate, the adapter does not call any of the subsequent middlewares
* {@link Middleware#onTurn(TurnContext, NextDelegate)}
* methods or the callback method, and the pipeline short circuits.
* <p>When the turn is initiated by a user activity (reactive messaging), the
* callback method will be a reference to the bot's
* {@link Bot#onTurn(TurnContext)} method. When the turn is
* initiated by a call to {@link #continueConversation(String, ConversationReference, BotCallbackHandler)}
* (proactive messaging), the callback method is the callback method that was provided in the call.</p>
*/
protected CompletableFuture<Void> runPipeline(TurnContext context, BotCallbackHandler callback) {
BotAssert.contextNotNull(context);
// Call any registered Middleware Components looking for ReceiveActivity()
if (context.getActivity() != null) {
return _middlewareSet.receiveActivityWithStatus(context, callback)
return middlewareSet.receiveActivityWithStatus(context, callback)
.exceptionally(exception -> {
if (onTurnError != null) {
return onTurnError.invoke(context, exception);

Просмотреть файл

@ -192,7 +192,7 @@ public class BotFrameworkAdapter extends BotAdapter {
* @return The updated adapter object.
*/
public BotFrameworkAdapter use(Middleware middleware) {
super._middlewareSet.use(middleware);
super.middlewareSet.use(middleware);
return this;
}
@ -261,7 +261,7 @@ public class BotFrameworkAdapter extends BotAdapter {
// For all non-invoke scenarios, the HTTP layers above don't have to mess
// with the Body and return codes.
return null;
return CompletableFuture.completedFuture(null);
});
}

Просмотреть файл

@ -4,6 +4,8 @@
package com.microsoft.bot.builder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@ -14,6 +16,10 @@ import java.util.stream.Collectors;
public class BotStateSet {
private List<BotState> botStates = new ArrayList<>();
public BotStateSet(BotState... withBotStates) {
this(Arrays.asList(withBotStates));
}
/**
* Initializes a new instance of the BotStateSet class.
*

Просмотреть файл

@ -22,9 +22,9 @@ import java.util.stream.Collectors;
/**
* The memory transcript store stores transcripts in volatile memory in a Dictionary.
* <p>
* <p>
* Because this uses an unbounded volitile dictionary this should only be used for unit tests or non-production environments.
*
* Because this uses an unbounded volatile dictionary this should only be used for unit tests or
* non-production environments.
*/
public class MemoryTranscriptStore implements TranscriptStore {
private HashMap<String, HashMap<String, ArrayList<Activity>>> channels = new HashMap<String, HashMap<String, ArrayList<Activity>>>();

Просмотреть файл

@ -19,6 +19,6 @@ public class InspectionState extends BotState {
@Override
public String getStorageKey(TurnContext turnContext) {
return InspectionState.class.getName();
return InspectionState.class.getSimpleName();
}
}

Просмотреть файл

@ -0,0 +1,80 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.microsoft.bot.builder.adapters.TestAdapter;
import com.microsoft.bot.builder.adapters.TestFlow;
import org.junit.Test;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class BotAdapterBracketingTest {
@Test
public void Middleware_BracketingValidation() {
TestAdapter adapter = new TestAdapter()
.use(new BeforeAfterMiddleware());
BotCallbackHandler echo = (turnContext -> {
String toEcho = "ECHO:" + turnContext.getActivity().getText();
return turnContext.sendActivity(turnContext.getActivity().createReply(toEcho))
.thenApply(resourceResponse -> null);
});
new TestFlow(adapter, echo)
.send("test")
.assertReply("BEFORE")
.assertReply("ECHO:test")
.assertReply("AFTER")
.startTest();
}
@Test
public void Middleware_ThrowException() {
String uniqueId = UUID.randomUUID().toString();
TestAdapter adapter = new TestAdapter()
.use(new CatchExceptionMiddleware());
BotCallbackHandler echoWithException = (turnContext -> {
String toEcho = "ECHO:" + turnContext.getActivity().getText();
return turnContext.sendActivity(turnContext.getActivity().createReply(toEcho))
.thenApply(resourceResponse -> {
throw new RuntimeException(uniqueId);
});
});
new TestFlow(adapter, echoWithException)
.send("test")
.assertReply("BEFORE")
.assertReply("ECHO:test")
.assertReply("CAUGHT:" + uniqueId)
.assertReply("AFTER")
.startTest();
}
private static class CatchExceptionMiddleware implements Middleware {
@Override
public CompletableFuture<Void> onTurn(TurnContext turnContext, NextDelegate next) {
return turnContext.sendActivity(turnContext.getActivity().createReply("BEFORE"))
.thenCompose(resourceResponse -> next.next())
.exceptionally(exception -> {
turnContext.sendActivity(turnContext.getActivity().createReply("CAUGHT:" + exception.getMessage())).join();
return null;
})
.thenCompose(result -> turnContext.sendActivity(turnContext.getActivity().createReply("AFTER"))
.thenApply(resourceResponse -> null));
}
}
private static class BeforeAfterMiddleware implements Middleware {
@Override
public CompletableFuture<Void> onTurn(TurnContext turnContext, NextDelegate next) {
return turnContext.sendActivity(turnContext.getActivity().createReply("BEFORE"))
.thenCompose(result -> next.next())
.thenCompose(result -> turnContext.sendActivity(turnContext.getActivity().createReply("AFTER"))
.thenApply(resourceResponse -> null));
}
}
}

Просмотреть файл

@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.microsoft.bot.builder.adapters.TestAdapter;
import com.microsoft.bot.schema.*;
import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.Test;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
public class BotAdapterTests {
@Test
public void AdapterSingleUse() {
SimpleAdapter a = new SimpleAdapter();
a.use(new CallCountingMiddleware());
}
@Test
public void AdapterUseChaining() {
SimpleAdapter a = new SimpleAdapter();
a.use(new CallCountingMiddleware()).use(new CallCountingMiddleware());
}
@Test
public void PassResourceResponsesThrough() {
Consumer<Activity[]> validateResponse = (activities) -> {
// no need to do anything.
};
SimpleAdapter a = new SimpleAdapter(validateResponse);
TurnContextImpl c = new TurnContextImpl(a, new Activity(ActivityTypes.MESSAGE));
String activityId = UUID.randomUUID().toString();
Activity activity = TestMessage.Message();
activity.setId(activityId);
ResourceResponse resourceResponse = c.sendActivity(activity).join();
Assert.assertTrue("Incorrect response Id returned", StringUtils.equals(resourceResponse.getId(), activityId));
}
@Test
public void ContinueConversation_DirectMsgAsync() {
boolean[] callbackInvoked = new boolean[] { false };
TestAdapter adapter = new TestAdapter();
ConversationReference cr = new ConversationReference(){{
setActivityId("activityId");
setBot(new ChannelAccount(){{
setId("channelId");
setName("testChannelAccount");
setRole(RoleTypes.BOT);
}});
setChannelId("testChannel");
setServiceUrl("testUrl");
setConversation(new ConversationAccount() {{
setConversationType("");
setId("testConversationId");
setIsGroup(false);
setName("testConversationName");
setRole(RoleTypes.USER);
}});
setUser(new ChannelAccount() {{
setId("channelId");
setName("testChannelAccount");
setRole(RoleTypes.BOT);
}});
}};
BotCallbackHandler callback = (turnContext) -> {
callbackInvoked[0] = true;
return CompletableFuture.completedFuture(null);
};
adapter.continueConversation("MyBot", cr, callback).join();
Assert.assertTrue(callbackInvoked[0]);
}
}

Просмотреть файл

@ -1,45 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ActivityTypes;
import com.microsoft.bot.schema.ResourceResponse;
import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.Test;
import java.util.UUID;
import java.util.function.Consumer;
public class BotFrameworkAdapterTest {
@Test
public void AdapterSingleUse() {
SimpleAdapter a = new SimpleAdapter();
a.use(new CallCountingMiddleware());
}
@Test
public void AdapterUseChaining() {
SimpleAdapter a = new SimpleAdapter();
a.use(new CallCountingMiddleware()).use(new CallCountingMiddleware());
}
@Test
public void PassResourceResponsesThrough() throws Exception {
Consumer<Activity[]> validateResponse = (activities) -> {
// no need to do anything.
};
SimpleAdapter a = new SimpleAdapter(validateResponse);
TurnContextImpl c = new TurnContextImpl(a, new Activity(ActivityTypes.MESSAGE));
String activityId = UUID.randomUUID().toString();
Activity activity = TestMessage.Message();
activity.setId(activityId);
ResourceResponse resourceResponse = c.sendActivity(activity).join();
Assert.assertTrue("Incorrect response Id returned", StringUtils.equals(resourceResponse.getId(), activityId));
}
}

Просмотреть файл

@ -0,0 +1,64 @@
package com.microsoft.bot.builder;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.microsoft.bot.connector.Channels;
import com.microsoft.bot.connector.authentication.ClaimsIdentity;
import com.microsoft.bot.connector.authentication.CredentialProvider;
import com.microsoft.bot.connector.authentication.SimpleCredentialProvider;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ConversationAccount;
import org.junit.Assert;
import org.junit.Test;
import java.util.concurrent.CompletableFuture;
public class BotFrameworkAdapterTests {
@Test
public void TenantIdShouldBeSetInConversationForTeams() {
Activity activity = processActivity(Channels.MSTEAMS, "theTenantId", null);
Assert.assertEquals("theTenantId", activity.getConversation().getTenantId());
}
@Test
public void TenantIdShouldNotChangeInConversationForTeamsIfPresent() {
Activity activity = processActivity(Channels.MSTEAMS, "theTenantId", "shouldNotBeReplaced");
Assert.assertEquals("shouldNotBeReplaced", activity.getConversation().getTenantId());
}
@Test
public void TenantIdShouldNotBeSetInConversationIfNotTeams() {
Activity activity = processActivity(Channels.DIRECTLINE, "theTenantId", null);
Assert.assertNull(activity.getConversation().getTenantId());
}
private Activity processActivity(String channelId, String channelDataTenantId, String conversationTenantId) {
ClaimsIdentity mockClaims = new ClaimsIdentity("anonymous");
CredentialProvider mockCredentials = new SimpleCredentialProvider();
BotFrameworkAdapter sut = new BotFrameworkAdapter(mockCredentials);
ObjectNode channelData = new ObjectMapper().createObjectNode();
ObjectNode tenantId = new ObjectMapper().createObjectNode();
tenantId.put("id", channelDataTenantId);
channelData.set("tenant", tenantId);
Activity[] activity = new Activity[] { null };
sut.processActivity(
mockClaims,
new Activity("test") {{
setChannelId(channelId);
setServiceUrl("https://smba.trafficmanager.net/amer/");
setChannelData(channelData);
setConversation(new ConversationAccount() {{
setTenantId(conversationTenantId);
}});
}},
(context) -> {
activity[0] = context.getActivity();
return CompletableFuture.completedFuture(null);
}).join();
return activity[0];
}
}

Просмотреть файл

@ -0,0 +1,98 @@
package com.microsoft.bot.builder;
import org.junit.Assert;
import org.junit.Test;
public class BotStateSetTests {
@Test
public void BotStateSet_Properties() {
Storage storage = new MemoryStorage();
UserState userState = new UserState(storage);
ConversationState conversationState = new ConversationState(storage);
BotStateSet stateSet = new BotStateSet(userState, conversationState);
Assert.assertEquals(2, stateSet.getBotStates().size());
Assert.assertTrue(stateSet.getBotStates().get(0) instanceof UserState);
Assert.assertTrue(stateSet.getBotStates().get(1) instanceof ConversationState);
}
@Test
public void BotStateSet_LoadAsync() {
Storage storage = new MemoryStorage();
TurnContext turnContext = TestUtilities.createEmptyContext();
{
UserState userState = new UserState(storage);
StatePropertyAccessor<Integer> userProperty = userState.createProperty("userCount");
ConversationState convState = new ConversationState(storage);
StatePropertyAccessor<Integer> convProperty = convState.createProperty("convCount");
BotStateSet stateSet = new BotStateSet(userState, convState);
Assert.assertEquals(2, stateSet.getBotStates().size());
Integer userCount = userProperty.get(turnContext, () -> 0).join();
Assert.assertEquals(0, userCount.intValue());
Integer convCount = convProperty.get(turnContext, () -> 0).join();
Assert.assertEquals(0, convCount.intValue());
userProperty.set(turnContext, 10).join();
convProperty.set(turnContext, 20).join();
stateSet.saveAllChanges(turnContext).join();
}
{
UserState userState = new UserState(storage);
StatePropertyAccessor<Integer> userProperty = userState.createProperty("userCount");
ConversationState convState = new ConversationState(storage);
StatePropertyAccessor<Integer> convProperty = convState.createProperty("convCount");
BotStateSet stateSet = new BotStateSet(userState, convState);
stateSet.loadAll(turnContext).join();
Integer userCount = userProperty.get(turnContext, () -> 0).join();
Assert.assertEquals(10, userCount.intValue());
Integer convCount = convProperty.get(turnContext, () -> 0).join();
Assert.assertEquals(20, convCount.intValue());
}
}
@Test
public void BotStateSet_SaveAsync() {
Storage storage = new MemoryStorage();
UserState userState = new UserState(storage);
StatePropertyAccessor<Integer> userProperty = userState.createProperty("userCount");
ConversationState convState = new ConversationState(storage);
StatePropertyAccessor<Integer> convProperty = convState.createProperty("convCount");
BotStateSet stateSet = new BotStateSet(userState, convState);
Assert.assertEquals(2, stateSet.getBotStates().size());
TurnContext turnContext = TestUtilities.createEmptyContext();
stateSet.loadAll(turnContext).join();
Integer userCount = userProperty.get(turnContext, () -> 0).join();
Assert.assertEquals(0, userCount.intValue());
Integer convCount = convProperty.get(turnContext, () -> 0).join();
Assert.assertEquals(0, convCount.intValue());
userProperty.set(turnContext, 10).join();
convProperty.set(turnContext, 20).join();
stateSet.saveAllChanges(turnContext).join();
userCount = userProperty.get(turnContext, () -> 0).join();
Assert.assertEquals(10, userCount.intValue());
convCount = convProperty.get(turnContext, () -> 0).join();
Assert.assertEquals(20, convCount.intValue());
}
}

Просмотреть файл

@ -19,7 +19,7 @@ import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class BotStateTest {
public class BotStateTests {
@Test(expected = IllegalArgumentException.class)
public void State_EmptyName() {

Просмотреть файл

@ -5,6 +5,7 @@ package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
@FunctionalInterface
public interface CallOnException {
<T> CompletableFuture<Void> apply(TurnContext context, T t);
<T> CompletableFuture<Void> invoke(TurnContext context, T t);
}

Просмотреть файл

@ -14,26 +14,26 @@ import java.util.concurrent.CompletionException;
* multiple times to allow you to handle different exception types in different ways.
*/
public class CatchExceptionMiddleware<T extends Exception> implements Middleware {
private final CallOnException _handler;
private final Class<T> _exceptionType;
private CallOnException handler;
private Class<T> exceptionType;
public CatchExceptionMiddleware(CallOnException callOnException, Class<T> exceptionType) {
_handler = callOnException;
_exceptionType = exceptionType;
public CatchExceptionMiddleware(CallOnException withCallOnException, Class<T> withExceptionType) {
handler = withCallOnException;
exceptionType = withExceptionType;
}
@Override
public CompletableFuture<Void> onTurn(TurnContext context, NextDelegate next) {
Class c = _exceptionType.getDeclaringClass();
Class c = exceptionType.getDeclaringClass();
// Continue to route the activity through the pipeline
// any errors further down the pipeline will be caught by
// this try / catch
return next.next()
.exceptionally(exception -> {
if (_exceptionType.isInstance(exception)) {
_handler.apply(context, (T) exception);
if (exceptionType.isInstance(exception)) {
handler.invoke(context, exception);
} else {
throw new CompletionException(exception);
}

Просмотреть файл

@ -1,111 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.microsoft.bot.builder.adapters.TestAdapter;
import com.microsoft.bot.builder.adapters.TestFlow;
import com.microsoft.bot.connector.ExecutorFactory;
import com.microsoft.bot.schema.Activity;
import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.Test;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CatchException_MiddlewareTest {
@Test
public void CatchException_TestMiddleware_TestStackedErrorMiddleware() throws ExecutionException, InterruptedException {
TestAdapter adapter = new TestAdapter()
.use(new CatchExceptionMiddleware<Exception>(new CallOnException() {
@Override
public <T> CompletableFuture apply(TurnContext context, T t) {
return CompletableFuture.runAsync(() -> {
Activity activity = context.getActivity();
if (activity instanceof Activity) {
try {
context.sendActivity(activity.createReply(t.toString())).join();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(String.format("CatchException_TestMiddleware_TestStackedErrorMiddleware:SendActivity failed %s", e.toString()));
}
} else
Assert.assertTrue("Test was built for ActivityImpl", false);
}, ExecutorFactory.getExecutor());
}
}, Exception.class))
// Add middleware to catch NullReferenceExceptions before throwing up to the general exception instance
.use(new CatchExceptionMiddleware<NullPointerException>(new CallOnException() {
@Override
public <T> CompletableFuture apply(TurnContext context, T t) {
context.sendActivity("Sorry - Null Reference Exception").join();
return CompletableFuture.completedFuture(null);
}
}, NullPointerException.class));
new TestFlow(adapter, (context) -> {
if (StringUtils.equals(context.getActivity().getText(), "foo")) {
try {
context.sendActivity(context.getActivity().getText()).join();
} catch (Exception e) {
e.printStackTrace();
}
}
if (StringUtils.equals(context.getActivity().getText(), "UnsupportedOperationException")) {
throw new UnsupportedOperationException("Test");
}
return null;
})
.send("foo")
.assertReply("foo", "passthrough")
.send("UnsupportedOperationException")
.assertReply("Test")
.startTest();
}
/* @Test
// [TestCategory("Middleware")]
public void CatchException_TestMiddleware_SpecificExceptionType()
{
TestAdapter adapter = new TestAdapter()
.Use(new CatchExceptionMiddleware<Exception>((context, exception) =>
{
context.SendActivity("Generic Exception Caught");
return CompletableFuture.CompletedTask;
}))
.Use(new CatchExceptionMiddleware<NullReferenceException>((context, exception) =>
{
context.SendActivity(exception.Message);
return CompletableFuture.CompletedTask;
}));
await new TestFlow(adapter, (context) =>
{
if (context.Activity.AsMessageActivity().Text == "foo")
{
context.SendActivity(context.Activity.AsMessageActivity().Text);
}
if (context.Activity.AsMessageActivity().Text == "NullReferenceException")
{
throw new NullReferenceException("Test");
}
return CompletableFuture.CompletedTask;
})
.Send("foo")
.AssertReply("foo", "passthrough")
.Send("NullReferenceException")
.AssertReply("Test")
.StartTest();
}*/
}