Corrected compile errors in tests. Rewrote BotStateTests, and applied corrections to pass tests.

This commit is contained in:
Tracy Boehrer 2019-09-11 14:40:14 -05:00
Родитель 5478ce945a
Коммит d13a995ac6
39 изменённых файлов: 2442 добавлений и 1868 удалений

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

@ -0,0 +1,52 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
package com.microsoft.bot.builder;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
/**
* Middleware to automatically call .SaveChanges() at the end of the turn for all BotState class it is managing.
*/
public class AutoSaveStateMiddleware implements Middleware {
private BotStateSet botStateSet;
public AutoSaveStateMiddleware(BotState ... botStates) {
botStateSet = new BotStateSet(Arrays.asList(botStates));
}
public AutoSaveStateMiddleware(BotStateSet withBotStateSet) {
botStateSet = withBotStateSet;
}
public BotStateSet getBotStateSet() {
return botStateSet;
}
public void setBotStateSet(BotStateSet withBotStateSet) {
botStateSet = withBotStateSet;
}
public AutoSaveStateMiddleware add(BotState botState) {
if (botState == null) {
throw new IllegalArgumentException("botState cannot be null");
}
botStateSet.add(botState);
return this;
}
/**
* Middleware implementation which calls savesChanges automatically at the end of the turn.
*
* @param turnContext The context object for this turn.
* @param next The delegate to call to continue the bot middleware pipeline.
* @return A task representing the asynchronous operation.
*/
@Override
public CompletableFuture<Void> onTurn(TurnContext turnContext, NextDelegate next) {
return next.next()
.thenCompose(result -> botStateSet.saveAllChanges(turnContext));
}
}

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

@ -3,6 +3,7 @@
package com.microsoft.bot.builder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
@ -10,6 +11,7 @@ import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
/**
@ -79,7 +81,7 @@ public abstract class BotState implements PropertyManager {
if (force || cachedState == null || cachedState.getState() == null) {
return storage.read(new String[]{storageKey})
.thenApply(val -> {
turnContext.getTurnState().put(contextServiceKey, new CachedBotState(val));
turnContext.getTurnState().put(contextServiceKey, new CachedBotState((Map<String, Object>)val.get(storageKey)));
return null;
});
}
@ -120,7 +122,7 @@ public abstract class BotState implements PropertyManager {
return storage.write(changes)
.thenApply(val -> {
cachedState.setHashCode(cachedState.computeHashCode(cachedState.state));
cachedState.setHash(cachedState.computeHash(cachedState.state));
return null;
});
}
@ -139,7 +141,7 @@ public abstract class BotState implements PropertyManager {
throw new IllegalArgumentException("turnContext cannot be null");
}
turnContext.getTurnState().put(contextServiceKey, new CachedBotState(new HashMap<>()));
turnContext.getTurnState().put(contextServiceKey, new CachedBotState());
return CompletableFuture.completedFuture(null);
}
@ -154,13 +156,16 @@ public abstract class BotState implements PropertyManager {
throw new IllegalArgumentException("turnContext cannot be null");
}
CachedBotState cachedState = turnContext.getTurnState().get(contextServiceKey);
if (cachedState != null) {
turnContext.getTurnState().remove(contextServiceKey);
}
String storageKey = getStorageKey(turnContext);
return storage.delete(new String[]{storageKey});
return storage.delete(new String[]{storageKey})
.thenApply(result -> {
CachedBotState cachedState = turnContext.getTurnState().get(contextServiceKey);
if (cachedState != null) {
turnContext.getTurnState().remove(contextServiceKey);
}
return null;
});
}
/**
@ -259,11 +264,16 @@ public abstract class BotState implements PropertyManager {
*/
private static class CachedBotState {
private Map<String, Object> state;
private int hash;
private String hash;
private ObjectMapper mapper = new ObjectMapper();
public CachedBotState() {
this(null);
}
public CachedBotState(Map<String, Object> withState) {
state = withState;
hash = computeHashCode(withState);
state = withState != null ? withState : new ConcurrentHashMap<>();
hash = computeHash(withState);
}
public Map<String, Object> getState() {
@ -274,22 +284,28 @@ public abstract class BotState implements PropertyManager {
state = withState;
}
@Override
public int hashCode() {
public String getHash() {
return hash;
}
public void setHashCode(int witHashCode) {
public void setHash(String witHashCode) {
hash = witHashCode;
}
public boolean isChanged() {
return hash != computeHashCode(state);
return !StringUtils.equals(hash, computeHash(state));
}
public int computeHashCode(Object obj) {
//TODO: this may not be the same as in dotnet
return obj.hashCode();
public String computeHash(Object obj) {
if (obj == null) {
return "";
}
try {
return mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
return null;
}
}
}
@ -319,17 +335,22 @@ public abstract class BotState implements PropertyManager {
* @param defaultValueFactory Defines the default value. Invoked when no value been set for the requested
* state property. If defaultValueFactory is defined as null,
* the MissingMemberException will be thrown if the underlying property is not set.
* @param <T> type of value the propertyAccessor accesses.
* @return A task that represents the work queued to execute.
*/
@Override
public <T, S> CompletableFuture<T> get(TurnContext turnContext, Supplier<S> defaultValueFactory) {
public CompletableFuture<T> get(TurnContext turnContext, Supplier<T> defaultValueFactory) {
return botState.load(turnContext)
.thenCombine(botState.getPropertyValue(turnContext, name), (loadResult, value) -> {
if (value == null) {
value = defaultValueFactory.get();
if (value != null) {
return (T) value;
}
if (defaultValueFactory == null) {
return null;
}
value = defaultValueFactory.get();
set(turnContext, (T) value).join();
return (T) value;
});
}

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

@ -17,10 +17,10 @@ public class BotStateSet {
/**
* Initializes a new instance of the BotStateSet class.
*
* @param botStates initial list of {@link BotState} objects to manage.
* @param withBotStates initial list of {@link BotState} objects to manage.
*/
public BotStateSet(List<BotState> botStates) {
botStates.addAll(botStates);
public BotStateSet(List<BotState> withBotStates) {
botStates.addAll(withBotStates);
}
/**
@ -96,7 +96,7 @@ public class BotStateSet {
*/
public CompletableFuture<Void> saveAllChanges(TurnContext turnContext, boolean force) {
List<CompletableFuture<Void>> saveFutures = botStates.stream()
.map(future -> future.saveChanges(turnContext, force))
.map(botState -> botState.saveChanges(turnContext, force))
.collect(Collectors.toList());
return CompletableFuture.allOf(saveFutures.toArray(new CompletableFuture[saveFutures.size()]));

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

@ -13,7 +13,7 @@ public class ConversationState extends BotState {
* Creates a new {@link ConversationState} object.
*/
public ConversationState(Storage withStorage) {
super(withStorage, ConversationState.class.getName());
super(withStorage, ConversationState.class.getSimpleName());
}
@Override

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

@ -8,28 +8,33 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
public class MemoryStorage implements Storage {
private static final String TYPENAMEFORNONENTITY = "__type_name_";
private final Object syncroot = new Object();
private ObjectMapper objectMapper;
private Map<String, JsonNode> memory = new HashMap<>();
private Map<String, JsonNode> memory;
private Logger logger = LoggerFactory.getLogger(MemoryStorage.class);
private int _eTag = 0;
public MemoryStorage() {
this(null);
}
public MemoryStorage(Map<String, JsonNode> values) {
objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.findAndRegisterModules();
objectMapper.enableDefaultTyping();
if (values != null)
memory = values;
memory = values != null ? values : new ConcurrentHashMap<>();
}
@Override
@ -38,7 +43,7 @@ public class MemoryStorage implements Storage {
throw new IllegalArgumentException("keys cannot be null");
}
Map<String, Object> storeItems = new HashMap<String, Object>(keys.length);
Map<String, Object> storeItems = new ConcurrentHashMap<>(keys.length);
synchronized (this.syncroot) {
for (String key : keys) {
if (memory.containsKey(key)) {
@ -83,15 +88,15 @@ public class MemoryStorage implements Storage {
@Override
public CompletableFuture<Void> write(Map<String, Object> changes) {
synchronized (this.syncroot) {
for (Map.Entry change : changes.entrySet()) {
for (Map.Entry<String, Object> change : changes.entrySet()) {
Object newValue = change.getValue();
String oldStateETag = null;
if (memory.containsValue(change.getKey())) {
Map oldState = (Map) memory.get(change.getKey());
if (oldState.containsValue("eTag")) {
Map.Entry eTagToken = (Map.Entry) oldState.get("eTag");
oldStateETag = (String) eTagToken.getValue();
if (memory.containsKey(change.getKey())) {
JsonNode oldState = memory.get(change.getKey());
if (oldState.has("eTag")) {
JsonNode eTagToken = oldState.get("eTag");
oldStateETag = eTagToken.asText();
}
}
@ -102,8 +107,10 @@ public class MemoryStorage implements Storage {
// Set ETag if applicable
if (newValue instanceof StoreItem) {
StoreItem newStoreItem = (StoreItem) newValue;
if (oldStateETag != null && newStoreItem.getETag() != "*" &&
newStoreItem.getETag() != oldStateETag) {
if (oldStateETag != null
&& !StringUtils.equals(newStoreItem.getETag(), "*")
&& !StringUtils.equals(newStoreItem.getETag(), oldStateETag)) {
String msg = String.format("Etag conflict. Original: %s, Current: %s",
newStoreItem.getETag(), oldStateETag);
logger.error(msg);
@ -113,7 +120,7 @@ public class MemoryStorage implements Storage {
((ObjectNode) newState).put("eTag", newTag.toString());
}
memory.put((String) change.getKey(), newState);
memory.put(change.getKey(), newState);
}
}
@ -128,8 +135,7 @@ public class MemoryStorage implements Storage {
synchronized (this.syncroot) {
for (String key : keys) {
Object o = memory.get(key);
memory.remove(o);
memory.remove(key);
}
}

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

@ -15,7 +15,7 @@ public class PrivateConversationState extends BotState {
* @param storage The storage provider to use.
*/
public PrivateConversationState(Storage storage) {
super(storage, PrivateConversationState.class.getName());
super(storage, PrivateConversationState.class.getSimpleName());
}
/**

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

@ -7,7 +7,11 @@ import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
public interface StatePropertyAccessor<T> extends StatePropertyInfo {
<T extends Object, S> CompletableFuture<T> get(TurnContext turnContext, Supplier<S> defaultValueFactory);
default CompletableFuture<T> get(TurnContext turnContext) {
return get(turnContext, null);
}
CompletableFuture<T> get(TurnContext turnContext, Supplier<T> defaultValueFactory);
CompletableFuture<Void> delete(TurnContext turnContext);

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

@ -15,7 +15,7 @@ public class TurnContextStateCollection extends HashMap<String, Object> implemen
throw new IllegalArgumentException("key");
}
Object service = get(key);
Object service = super.get(key);
try {
T result = (T) service;
} catch (ClassCastException e) {

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

@ -16,7 +16,7 @@ public class UserState extends BotState {
* @param withStorage The storage provider to use.
*/
public UserState(Storage withStorage) {
super(withStorage, UserState.class.getName());
super(withStorage, UserState.class.getSimpleName());
}
@Override

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

@ -14,7 +14,7 @@ public class InspectionState extends BotState {
* @param withStorage The storage provider to use.
*/
public InspectionState(Storage withStorage) {
super(withStorage, InspectionState.class.getName());
super(withStorage, InspectionState.class.getSimpleName());
}
@Override

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

@ -1,7 +1,10 @@
package com.microsoft.bot.builder;
// This is a proxy for some previous tests using Action bindings
@FunctionalInterface
public interface ActionDel {
void CallMe();
}
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
// This is a proxy for some previous tests using Action bindings
@FunctionalInterface
public interface ActionDel {
void CallMe();
}

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

@ -1,3 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
@ -5,18 +8,17 @@ import java.util.concurrent.CompletableFuture;
/**
* Helper class for defining middleware by using a delegate or anonymous method.
*/
public class AnonymousReceiveMiddleware implements Middleware
{
public class AnonymousReceiveMiddleware implements Middleware {
private MiddlewareCall _toCall;
/**
* Creates a middleware object that uses the provided method as its
* process request handler.
*
* @param anonymousMethod The method to use as the middleware's process
* request handler.
* request handler.
*/
public AnonymousReceiveMiddleware(MiddlewareCall anonymousMethod)
{
public AnonymousReceiveMiddleware(MiddlewareCall anonymousMethod) {
if (anonymousMethod == null)
throw new NullPointerException("MiddlewareCall anonymousMethod");
else
@ -26,8 +28,9 @@ public class AnonymousReceiveMiddleware implements Middleware
/**
* Uses the method provided in the {@link AnonymousReceiveMiddleware} to
* process an incoming activity.
*
* @param context The context object for this turn.
* @param next The delegate to call to continue the bot middleware pipeline.
* @param next The delegate to call to continue the bot middleware pipeline.
* @return A task that represents the work queued to execute.
*/
@Override

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

@ -1,8 +1,11 @@
// 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 com.microsoft.bot.schema.*;
import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.Test;
@ -12,22 +15,20 @@ import java.util.function.Consumer;
public class BotFrameworkAdapterTest {
@Test
public void AdapterSingleUse()
{
public void AdapterSingleUse() {
SimpleAdapter a = new SimpleAdapter();
a.use(new CallCountingMiddleware());
}
@Test
public void AdapterUseChaining()
{
public void AdapterUseChaining() {
SimpleAdapter a = new SimpleAdapter();
a.use(new CallCountingMiddleware()).use(new CallCountingMiddleware());
}
@Test
public void PassResourceResponsesThrough() throws Exception {
Consumer<Activity[]> validateResponse = (activities) -> {
Consumer<Activity[]> validateResponse = (activities) -> {
// no need to do anything.
};

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -1,3 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;

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

@ -1,3 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;

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

@ -1,7 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
public interface CallOnException {
<T> CompletableFuture<Void> apply(TurnContext context, T t );
<T> CompletableFuture<Void> apply(TurnContext context, T t);
}

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

@ -1,4 +1,5 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
@ -11,7 +12,6 @@ import java.util.concurrent.CompletionException;
* send an appropriate message to the user to let them know that something has gone wrong.
* You can specify the type of exception the middleware should catch and this middleware can be added
* 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;
@ -33,7 +33,7 @@ public class CatchExceptionMiddleware<T extends Exception> implements Middleware
return next.next()
.exceptionally(exception -> {
if (_exceptionType.isInstance(exception)) {
_handler.<T>apply(context, (T) exception);
_handler.apply(context, (T) exception);
} else {
throw new CompletionException(exception);
}

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

@ -1,3 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.microsoft.bot.builder.adapters.TestAdapter;
@ -17,54 +20,54 @@ public class CatchException_MiddlewareTest {
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) 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);
.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());
}, 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));
}
}, 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");
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();
return null;
})
.send("foo")
.assertReply("foo", "passthrough")
.send("UnsupportedOperationException")
.assertReply("Test")
.startTest();
}

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

@ -1,15 +0,0 @@
package com.microsoft.bot.builder;
public class CustomKeyState extends BotState {
public static final String PROPERTY_NAME = "Microsoft.Bot.Builder.Tests.CustomKeyState";
public CustomKeyState(Storage storage) {
super(storage, PROPERTY_NAME);
}
@Override
public String getStorageKey(TurnContext turnContext) {
return "CustomKey";
}
}

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

@ -1,22 +0,0 @@
package com.microsoft.bot.builder;
public class CustomState implements StoreItem {
private String _customString;
private String _eTag;
public String getCustomString() {
return _customString;
}
public void setCustomString(String customString) {
this._customString = customString;
}
public String getETag() {
return _eTag;
}
public void setETag(String eTag) {
this._eTag = eTag;
}
}

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

@ -1,3 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;

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

@ -1,3 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
@ -10,13 +13,10 @@ import org.junit.Test;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
// [TestCategory("Russian Doll Middleware, Nested Middleware sets")]
public class MiddlewareSetTest extends TestBase
{
public class MiddlewareSetTest extends TestBase {
protected RestConnectorClient connector;
protected ChannelAccount bot;
protected ChannelAccount user;
@ -75,7 +75,7 @@ public class MiddlewareSetTest extends TestBase
MiddlewareSet m = new MiddlewareSet();
wasCalled = false;
BotCallbackHandler cb = (ctx) ->{
BotCallbackHandler cb = (ctx) -> {
wasCalled = true;
return null;
};
@ -101,7 +101,7 @@ public class MiddlewareSetTest extends TestBase
Assert.assertFalse(simple.getCalled());
m.receiveActivityWithStatus(null, cb).join();
Assert.assertTrue(simple.getCalled());
Assert.assertTrue( "Delegate was not called", wasCalled);
Assert.assertTrue("Delegate was not called", wasCalled);
}
@Test
@ -126,7 +126,7 @@ public class MiddlewareSetTest extends TestBase
try {
m.receiveActivityWithStatus(null, null).join();
Assert.assertFalse("Should never have gotten here", true);
} catch(CompletionException ce) {
} catch (CompletionException ce) {
Assert.assertTrue(ce.getCause() instanceof IllegalStateException);
}
}
@ -150,7 +150,7 @@ public class MiddlewareSetTest extends TestBase
WasCalledMiddleware one = new WasCalledMiddleware();
WasCalledMiddleware two = new WasCalledMiddleware();
final int called[] = {0};
final int[] called = {0};
BotCallbackHandler cb = (context) -> {
called[0]++;
return null;
@ -163,16 +163,16 @@ public class MiddlewareSetTest extends TestBase
m.receiveActivityWithStatus(null, cb).join();
Assert.assertTrue(one.getCalled());
Assert.assertTrue(two.getCalled());
Assert.assertTrue("Incorrect number of calls to Delegate", called[0] == 1 );
Assert.assertTrue("Incorrect number of calls to Delegate", called[0] == 1);
}
@Test
public void TwoMiddlewareItemsInOrder() throws Exception {
final boolean called1[] = {false};
final boolean called2[] = {false};
final boolean[] called1 = {false};
final boolean[] called2 = {false};
CallMeMiddleware one = new CallMeMiddleware(() -> {
Assert.assertFalse( "Second Middleware was called", called2[0]);
Assert.assertFalse("Second Middleware was called", called2[0]);
called1[0] = true;
});
@ -193,7 +193,7 @@ public class MiddlewareSetTest extends TestBase
@Test
public void Status_OneMiddlewareRan() {
final boolean called1[] = {false};
final boolean[] called1 = {false};
CallMeMiddleware one = new CallMeMiddleware(() -> called1[0] = true);
@ -201,8 +201,8 @@ public class MiddlewareSetTest extends TestBase
m.use(one);
// The middlware in this pipeline calls next(), so the delegate should be called
final boolean didAllRun[] = {false};
BotCallbackHandler cb = (context) -> {
final boolean[] didAllRun = {false};
BotCallbackHandler cb = (context) -> {
didAllRun[0] = true;
return null;
};
@ -215,8 +215,8 @@ public class MiddlewareSetTest extends TestBase
@Test
public void Status_RunAtEndEmptyPipeline() {
MiddlewareSet m = new MiddlewareSet();
final boolean didAllRun[] = {false};
BotCallbackHandler cb = (context)-> {
final boolean[] didAllRun = {false};
BotCallbackHandler cb = (context) -> {
didAllRun[0] = true;
return null;
};
@ -231,8 +231,8 @@ public class MiddlewareSetTest extends TestBase
@Test
public void Status_TwoItemsOneDoesNotCallNext() {
final boolean called1[] = {false};
final boolean called2[] = {false};
final boolean[] called1 = {false};
final boolean[] called2 = {false};
CallMeMiddleware one = new CallMeMiddleware(() -> {
Assert.assertFalse("Second Middleware was called", called2[0]);
@ -248,8 +248,8 @@ public class MiddlewareSetTest extends TestBase
m.use(one);
m.use(two);
boolean didAllRun[] = {false};
BotCallbackHandler cb= (context) -> {
boolean[] didAllRun = {false};
BotCallbackHandler cb = (context) -> {
didAllRun[0] = true;
return null;
};
@ -265,7 +265,7 @@ public class MiddlewareSetTest extends TestBase
@Test
public void Status_OneEntryThatDoesNotCallNext() {
final boolean called1[] = {false};
final boolean[] called1 = {false};
DoNotCallNextMiddleware one = new DoNotCallNextMiddleware(() -> called1[0] = true);
@ -273,7 +273,7 @@ public class MiddlewareSetTest extends TestBase
m.use(one);
// The middleware in this pipeline DOES NOT call next(), so this must not be called
boolean didAllRun[] = {false};
boolean[] didAllRun = {false};
BotCallbackHandler cb = (context) -> {
didAllRun[0] = true;
return null;
@ -289,7 +289,7 @@ public class MiddlewareSetTest extends TestBase
@Test
public void AnonymousMiddleware() {
final boolean didRun[] = {false};
final boolean[] didRun = {false};
MiddlewareSet m = new MiddlewareSet();
MiddlewareCall mwc = (tc, nd) -> {
@ -305,8 +305,8 @@ public class MiddlewareSetTest extends TestBase
@Test
public void TwoAnonymousMiddleware() throws Exception {
final boolean didRun1[] = {false};
final boolean didRun2[] = {false};
final boolean[] didRun1 = {false};
final boolean[] didRun2 = {false};
MiddlewareSet m = new MiddlewareSet();
@ -330,8 +330,8 @@ public class MiddlewareSetTest extends TestBase
@Test
public void TwoAnonymousMiddlewareInOrder() {
final boolean didRun1[] = {false};
final boolean didRun2[] = {false};
final boolean[] didRun1 = {false};
final boolean[] didRun2 = {false};
MiddlewareSet m = new MiddlewareSet();
MiddlewareCall mwc1 = (tc, nd) -> {
@ -356,8 +356,8 @@ public class MiddlewareSetTest extends TestBase
@Test
public void MixedMiddlewareInOrderAnonymousFirst() throws Exception {
final boolean didRun1[] = {false};
final boolean didRun2[] = {false};
final boolean[] didRun1 = {false};
final boolean[] didRun2 = {false};
MiddlewareSet m = new MiddlewareSet();
MiddlewareCall mwc1 = new MiddlewareCall() {
@ -386,8 +386,8 @@ public class MiddlewareSetTest extends TestBase
@Test
public void MixedMiddlewareInOrderAnonymousLast() {
final boolean didRun1[] = {false};
final boolean didRun2[] = {false};
final boolean[] didRun1 = {false};
final boolean[] didRun2 = {false};
MiddlewareSet m = new MiddlewareSet();
@ -413,9 +413,9 @@ public class MiddlewareSetTest extends TestBase
@Test
public void RunCodeBeforeAndAfter() throws Exception {
final boolean didRun1[] = {false};
final boolean codeafter2run[] = {false};
final boolean didRun2[] = {false};
final boolean[] didRun1 = {false};
final boolean[] codeafter2run = {false};
final boolean[] didRun2 = {false};
MiddlewareSet m = new MiddlewareSet();
@ -446,7 +446,7 @@ public class MiddlewareSetTest extends TestBase
@Test
public void CatchAnExceptionViaMiddlware() {
MiddlewareSet m = new MiddlewareSet();
final boolean caughtException[] = {false};
final boolean[] caughtException = {false};
MiddlewareCall mwc1 = new MiddlewareCall() {
public CompletableFuture<Void> requestHandler(TurnContext tc, NextDelegate nd) {
@ -456,7 +456,8 @@ public class MiddlewareSetTest extends TestBase
caughtException[0] = true;
return null;
});
}};
}
};
m.use(new AnonymousReceiveMiddleware(mwc1));

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

@ -1,3 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.microsoft.bot.schema.Activity;
@ -8,7 +11,6 @@ import org.junit.Assert;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
public class SimpleAdapter extends BotAdapter {

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

@ -1,15 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
public class StateSettings
{
private boolean lastWriterWins = true;
public boolean getLastWriterWins() {
return this.lastWriterWins;
}
public void setLast(boolean lastWriterWins) {
this.lastWriterWins = lastWriterWins;
}
}
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
public class StateSettings {
private boolean lastWriterWins = true;
public boolean getLastWriterWins() {
return this.lastWriterWins;
}
public void setLast(boolean lastWriterWins) {
this.lastWriterWins = lastWriterWins;
}
}

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

@ -1,3 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.microsoft.bot.schema.Activity;

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

@ -1,14 +0,0 @@
package com.microsoft.bot.builder;
public class TestPocoState
{
private String value;
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value = value;
}
}

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

@ -1,25 +0,0 @@
package com.microsoft.bot.builder;
public class TestState implements StoreItem {
private String etag;
private String value;
@Override
public String getETag() {
return this.etag;
}
@Override
public void setETag(String etag) {
this.etag = etag;
}
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value = value;
}
}

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

@ -0,0 +1,28 @@
// 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.Activity;
import com.microsoft.bot.schema.ActivityTypes;
import com.microsoft.bot.schema.ChannelAccount;
import com.microsoft.bot.schema.ConversationAccount;
public final class TestUtilities {
public static TurnContext createEmptyContext() {
TestAdapter adapter = new TestAdapter();
Activity activity = new Activity() {{
setType(ActivityTypes.MESSAGE);
setChannelId("EmptyContext");
setConversation(new ConversationAccount() {{
setId("test");
}});
setFrom(new ChannelAccount() {{
setId("empty@empty.context.org");
}});
}};
return new TurnContextImpl(adapter, activity);
}
}

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

@ -21,45 +21,45 @@ public class TranscriptMiddlewareTest {
@Test
public final void Transcript_SimpleReceive() throws Exception {
MemoryTranscriptStore transcriptStore = new MemoryTranscriptStore();
TestAdapter adapter = (new TestAdapter()).Use(new TranscriptLoggerMiddleware(transcriptStore));
TestAdapter adapter = (new TestAdapter()).use(new TranscriptLoggerMiddleware(transcriptStore));
final String[] conversationId = {null};
new TestFlow(adapter, (ctxt) ->
{
TurnContextImpl context = (TurnContextImpl) ctxt;
conversationId[0] = context.getActivity().getConversation().getId();
Activity typingActivity = new Activity(ActivityTypes.TYPING) {{
setRelatesTo(context.getActivity().getRelatesTo());
}};
try {
ResourceResponse response = context.sendActivity(typingActivity).join();
System.out.printf("Here's the response:");
} catch (Exception e) {
e.printStackTrace();
Assert.fail();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
Assert.fail();
}
try {
context.sendActivity("echo:" + context.getActivity().getText()).join();
} catch (Exception e) {
e.printStackTrace();
Assert.fail();
}
TurnContextImpl context = (TurnContextImpl) ctxt;
conversationId[0] = context.getActivity().getConversation().getId();
Activity typingActivity = new Activity(ActivityTypes.TYPING) {{
setRelatesTo(context.getActivity().getRelatesTo());
}};
try {
ResourceResponse response = context.sendActivity(typingActivity).join();
System.out.printf("Here's the response:");
} catch (Exception e) {
e.printStackTrace();
Assert.fail();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
Assert.fail();
}
try {
context.sendActivity("echo:" + context.getActivity().getText()).join();
} catch (Exception e) {
e.printStackTrace();
Assert.fail();
}
return CompletableFuture.completedFuture(null);
}).Send("foo")
.AssertReply((activity) -> {
Assert.assertEquals(activity.getType(), ActivityTypes.TYPING);
return null;
}).StartTest();
//.AssertReply("echo:foo").StartTest();
return CompletableFuture.completedFuture(null);
}).send("foo")
.assertReply((activity) -> {
Assert.assertEquals(activity.getType(), ActivityTypes.TYPING);
return null;
}).startTest();
//.AssertReply("echo:foo").StartTest();
}
@ -99,66 +99,65 @@ public class TranscriptMiddlewareTest {
@Test
public final void Transcript_LogActivities() throws ExecutionException, InterruptedException {
MemoryTranscriptStore transcriptStore = new MemoryTranscriptStore();
TestAdapter adapter = (new TestAdapter()).Use(new TranscriptLoggerMiddleware(transcriptStore));
TestAdapter adapter = (new TestAdapter()).use(new TranscriptLoggerMiddleware(transcriptStore));
final String[] conversationId = {null};
String result = new TestFlow(adapter, (context) ->
{
//TurnContextImpl context = (TurnContextImpl) ctxt;
conversationId[0] = context.getActivity().getConversation().getId();
Activity typingActivity = new Activity(ActivityTypes.TYPING) {{
setRelatesTo(context.getActivity().getRelatesTo());
}};
try {
context.sendActivity((Activity)typingActivity).join();
} catch (Exception e) {
e.printStackTrace();
Assert.fail();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
Assert.fail();
}
try {
context.sendActivity("echo:" + context.getActivity().getText()).join();
} catch (Exception e) {
e.printStackTrace();
Assert.fail();
}
//TurnContextImpl context = (TurnContextImpl) ctxt;
conversationId[0] = context.getActivity().getConversation().getId();
Activity typingActivity = new Activity(ActivityTypes.TYPING) {{
setRelatesTo(context.getActivity().getRelatesTo());
}};
try {
context.sendActivity(typingActivity).join();
} catch (Exception e) {
e.printStackTrace();
Assert.fail();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
Assert.fail();
}
try {
context.sendActivity("echo:" + context.getActivity().getText()).join();
} catch (Exception e) {
e.printStackTrace();
Assert.fail();
}
return CompletableFuture.completedFuture(null);
}).Send("foo")
.AssertReply((activity) -> {
Assert.assertEquals(activity.getType(), ActivityTypes.TYPING);
return null;
})
.AssertReply("echo:foo")
.Send("bar")
.AssertReply((activity) -> {
Assert.assertEquals(activity.getType(), ActivityTypes.TYPING);
return null;
})
.AssertReply("echo:bar")
.StartTest();
return CompletableFuture.completedFuture(null);
}).send("foo")
.assertReply((activity) -> {
Assert.assertEquals(activity.getType(), ActivityTypes.TYPING);
return null;
})
.assertReply("echo:foo")
.send("bar")
.assertReply((activity) -> {
Assert.assertEquals(activity.getType(), ActivityTypes.TYPING);
return null;
})
.assertReply("echo:bar")
.startTest();
PagedResult pagedResult = transcriptStore.getTranscriptActivities("test", conversationId[0]).join();
Assert.assertEquals(6, pagedResult.getItems().length);
Assert.assertEquals( "foo", ((Activity)pagedResult.getItems()[0]).getText());
Assert.assertNotEquals(((Activity)pagedResult.getItems()[1]), null);
Assert.assertEquals("foo", ((Activity) pagedResult.getItems()[0]).getText());
Assert.assertNotEquals(pagedResult.getItems()[1], null);
Assert.assertEquals("echo:foo", ((Activity) pagedResult.getItems()[2]).getText());
Assert.assertEquals("bar", ((Activity)pagedResult.getItems()[3]).getText());
Assert.assertEquals("bar", ((Activity) pagedResult.getItems()[3]).getText());
Assert.assertTrue(pagedResult.getItems()[4] != null);
Assert.assertEquals("echo:bar", ((Activity)pagedResult.getItems()[5]).getText());
for (Object activity : pagedResult.getItems())
{
Assert.assertEquals("echo:bar", ((Activity) pagedResult.getItems()[5]).getText());
for (Object activity : pagedResult.getItems()) {
Assert.assertFalse(StringUtils.isBlank(((Activity) activity).getId()));
Assert.assertTrue(((Activity)activity).getTimestamp().isAfter(Long.MIN_VALUE));
Assert.assertTrue(((Activity) activity).getTimestamp().isAfter(Long.MIN_VALUE));
}
System.out.printf("Complete");
}
@ -166,7 +165,7 @@ public class TranscriptMiddlewareTest {
@Test
public void Transcript_LogUpdateActivities() throws InterruptedException, ExecutionException {
MemoryTranscriptStore transcriptStore = new MemoryTranscriptStore();
TestAdapter adapter = (new TestAdapter()).Use(new TranscriptLoggerMiddleware(transcriptStore));
TestAdapter adapter = (new TestAdapter()).use(new TranscriptLoggerMiddleware(transcriptStore));
final String[] conversationId = {null};
final Activity[] activityToUpdate = {null};
ObjectMapper mapper = new ObjectMapper();
@ -181,7 +180,7 @@ public class TranscriptMiddlewareTest {
e.printStackTrace();
}
} else {
Activity activity = ((Activity) context.getActivity()).createReply("response");
Activity activity = context.getActivity().createReply("response");
ResourceResponse response = null;
try {
response = context.sendActivity(activity).join();
@ -198,16 +197,16 @@ public class TranscriptMiddlewareTest {
return null;
})
.Send("foo")
.Send("update")
.AssertReply("new response")
.StartTest();
.send("foo")
.send("update")
.assertReply("new response")
.startTest();
Thread.sleep(500);
PagedResult pagedResult = transcriptStore.getTranscriptActivities("test", conversationId[0]).join();
Assert.assertEquals(4, pagedResult.getItems().length);
Assert.assertEquals("foo", ((Activity)pagedResult.getItems()[0]).getText());
Assert.assertEquals( "response", ((Activity)pagedResult.getItems()[1]).getText());
Assert.assertEquals("foo", ((Activity) pagedResult.getItems()[0]).getText());
Assert.assertEquals("response", ((Activity) pagedResult.getItems()[1]).getText());
// TODO: Fix the following 3 asserts so they work correctly. They succeed in the travis builds and fail in the
// BotBuilder-Java 4.0 master build.
//Assert.assertEquals( "new response", ((Activity)pagedResult.getItems()[2]).text());
@ -218,7 +217,7 @@ public class TranscriptMiddlewareTest {
@Test
public final void Transcript_LogDeleteActivities() throws InterruptedException, ExecutionException {
MemoryTranscriptStore transcriptStore = new MemoryTranscriptStore();
TestAdapter adapter = (new TestAdapter()).Use(new TranscriptLoggerMiddleware(transcriptStore));
TestAdapter adapter = (new TestAdapter()).use(new TranscriptLoggerMiddleware(transcriptStore));
final String[] conversationId = {null};
final String[] activityId = {null};
new TestFlow(adapter, (context) -> {
@ -231,7 +230,7 @@ public class TranscriptMiddlewareTest {
Assert.fail();
}
} else {
Activity activity = ((Activity) context.getActivity()).createReply("response");
Activity activity = context.getActivity().createReply("response");
ResourceResponse response = null;
try {
response = context.sendActivity(activity).join();
@ -244,26 +243,26 @@ public class TranscriptMiddlewareTest {
return null;
})
.Send("foo")
.AssertReply("response")
.Send("deleteIt")
.StartTest();
.send("foo")
.assertReply("response")
.send("deleteIt")
.startTest();
Thread.sleep(1500);
PagedResult pagedResult = transcriptStore.getTranscriptActivities("test", conversationId[0]).join();
for (Object act : pagedResult.getItems()) {
System.out.printf("Here is the object: %s : Type: %s\n", act.getClass().getTypeName(), ((Activity)act).getType());
System.out.printf("Here is the object: %s : Type: %s\n", act.getClass().getTypeName(), ((Activity) act).getType());
}
for (Object activity : pagedResult.getItems() ) {
System.out.printf("Recipient: %s\nText: %s\n", ((Activity) activity).getRecipient().getName(), ((Activity)activity).getText());
for (Object activity : pagedResult.getItems()) {
System.out.printf("Recipient: %s\nText: %s\n", ((Activity) activity).getRecipient().getName(), ((Activity) activity).getText());
}
Assert.assertEquals(4, pagedResult.getItems().length);
Assert.assertEquals("foo", ((Activity)pagedResult.getItems()[0]).getText());
Assert.assertEquals("response", ((Activity)pagedResult.getItems()[1]).getText());
Assert.assertEquals("deleteIt", ((Activity)pagedResult.getItems()[2]).getText());
Assert.assertEquals(ActivityTypes.MESSAGE_DELETE, ((Activity)pagedResult.getItems()[3]).getType());
Assert.assertEquals(((Activity)pagedResult.getItems()[1]).getId(), ((Activity) pagedResult.getItems()[3]).getId());
Assert.assertEquals("foo", ((Activity) pagedResult.getItems()[0]).getText());
Assert.assertEquals("response", ((Activity) pagedResult.getItems()[1]).getText());
Assert.assertEquals("deleteIt", ((Activity) pagedResult.getItems()[2]).getText());
Assert.assertEquals(ActivityTypes.MESSAGE_DELETE, ((Activity) pagedResult.getItems()[3]).getType());
Assert.assertEquals(((Activity) pagedResult.getItems()[1]).getId(), ((Activity) pagedResult.getItems()[3]).getId());
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -1,17 +0,0 @@
package com.microsoft.bot.builder;
import com.fasterxml.jackson.annotation.JsonProperty;
public class TypedObject {
@JsonProperty
private String name;
public String name() {
return this.name;
}
public TypedObject withName(String name) {
this.name = name;
return this;
}
}

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

@ -1,12 +1,17 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
public class WasCalledMiddleware implements Middleware {
boolean called = false;
public boolean getCalled() {
return this.called;
}
public void setCalled(boolean called) {
this.called = called;
}

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

@ -1,22 +1,20 @@
package com.microsoft.bot.builder.adapters;
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder.adapters;
import com.microsoft.bot.builder.*;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.*;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Function;
public class TestAdapter extends BotAdapter {
private int nextId = 0;
private final Queue<Activity> botReplies = new LinkedList<>();
private int nextId = 0;
private ConversationReference conversationReference;
public TestAdapter() {
@ -53,13 +51,14 @@ public class TestAdapter extends BotAdapter {
return botReplies;
}
public TestAdapter Use(Middleware middleware) {
@Override
public TestAdapter use(Middleware middleware) {
super.use(middleware);
return this;
}
public void ProcessActivity(Activity activity,
BotCallbackHandler callback
BotCallbackHandler callback
) throws Exception {
synchronized (this.conversationReference()) {
// ready for next reply
@ -117,7 +116,10 @@ public class TestAdapter extends BotAdapter {
// to keep the behavior as close as possible to facillitate
// more realistic tests.
int delayMs = (int) activity.getValue();
try { Thread.sleep(delayMs); } catch (InterruptedException e) {}
try {
Thread.sleep(delayMs);
} catch (InterruptedException e) {
}
} else {
synchronized (this.botReplies) {
this.botReplies.add(activity);

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

@ -1,7 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder.adapters;
import com.microsoft.bot.builder.BotCallbackHandler;
import com.microsoft.bot.builder.TurnContext;
import com.microsoft.bot.connector.ExecutorFactory;
import com.microsoft.bot.schema.Activity;
import org.apache.commons.lang3.StringUtils;
@ -11,7 +13,6 @@ import org.junit.Assert;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@ -23,11 +24,9 @@ public class TestFlow {
BotCallbackHandler callback;
ArrayList<Supplier<String>> tasks = new ArrayList<Supplier<String>>();
ForkJoinPool.ForkJoinWorkerThreadFactory factory = new ForkJoinPool.ForkJoinWorkerThreadFactory()
{
ForkJoinPool.ForkJoinWorkerThreadFactory factory = new ForkJoinPool.ForkJoinWorkerThreadFactory() {
@Override
public ForkJoinWorkerThread newThread(ForkJoinPool pool)
{
public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
worker.setName("TestFlow-" + worker.getPoolIndex());
return worker;
@ -62,7 +61,7 @@ public class TestFlow {
*
* @return
*/
public String StartTest() throws ExecutionException, InterruptedException {
public String startTest() {
System.out.printf("+------------------------------------------+\n");
int count = 0;
@ -84,7 +83,7 @@ public class TestFlow {
* @param userSays
* @return
*/
public TestFlow Send(String userSays) throws IllegalArgumentException {
public TestFlow send(String userSays) throws IllegalArgumentException {
if (userSays == null)
throw new IllegalArgumentException("You have to pass a userSays parameter");
@ -108,7 +107,7 @@ public class TestFlow {
* @param userActivity
* @return
*/
public TestFlow Send(Activity userActivity) {
public TestFlow send(Activity userActivity) {
if (userActivity == null)
throw new IllegalArgumentException("You have to pass an Activity");
@ -118,7 +117,7 @@ public class TestFlow {
try {
this.adapter.ProcessActivity((Activity) userActivity, this.callback);
this.adapter.ProcessActivity(userActivity, this.callback);
return "TestFlow: Send() -> ProcessActivity: " + userActivity.getText();
} catch (Exception e) {
return e.getMessage();
@ -134,13 +133,13 @@ public class TestFlow {
* @param ms
* @return
*/
public TestFlow Delay(int ms) {
public TestFlow delay(int ms) {
return new TestFlow(() ->
{
System.out.printf("TestFlow(%s): Delay(%s ms) called. ", Thread.currentThread().getId(), ms);
System.out.flush();
try {
Thread.sleep((int) ms);
Thread.sleep(ms);
} catch (InterruptedException e) {
return e.getMessage();
}
@ -154,16 +153,16 @@ public class TestFlow {
* @param expected
* @return
*/
public TestFlow AssertReply(String expected) {
return this.AssertReply(expected, null, 3000);
public TestFlow assertReply(String expected) {
return this.assertReply(expected, null, 3000);
}
public TestFlow AssertReply(String expected, String description) {
return this.AssertReply(expected, description, 3000);
public TestFlow assertReply(String expected, String description) {
return this.assertReply(expected, description, 3000);
}
public TestFlow AssertReply(String expected, String description, int timeout) {
return this.AssertReply(this.adapter.MakeActivity(expected), description, timeout);
public TestFlow assertReply(String expected, String description, int timeout) {
return this.assertReply(this.adapter.MakeActivity(expected), description, timeout);
}
/**
@ -172,16 +171,16 @@ public class TestFlow {
* @param expected
* @return
*/
public TestFlow AssertReply(Activity expected) {
public TestFlow assertReply(Activity expected) {
String description = Thread.currentThread().getStackTrace()[1].getMethodName();
return AssertReply(expected, description, 3000);
return assertReply(expected, description, 3000);
}
public TestFlow AssertReply(Activity expected, String description, int timeout) {
public TestFlow assertReply(Activity expected, String description, int timeout) {
if (description == null)
description = Thread.currentThread().getStackTrace()[1].getMethodName();
String finalDescription = description;
return this.AssertReply((reply) -> {
return this.assertReply((reply) -> {
if (expected.getType() != reply.getType())
return String.format("%s: Type should match", finalDescription);
if (expected.getText().equals(reply.getText())) {
@ -201,16 +200,16 @@ public class TestFlow {
* @param validateActivity
* @return
*/
public TestFlow AssertReply(Function<Activity, String> validateActivity) {
public TestFlow assertReply(Function<Activity, String> validateActivity) {
String description = Thread.currentThread().getStackTrace()[1].getMethodName();
return AssertReply(validateActivity, description, 3000);
return assertReply(validateActivity, description, 3000);
}
public TestFlow AssertReply(Function<Activity, String> validateActivity, String description) {
return AssertReply(validateActivity, description, 3000);
public TestFlow assertReply(Function<Activity, String> validateActivity, String description) {
return assertReply(validateActivity, description, 3000);
}
public TestFlow AssertReply(Function<Activity, String> validateActivity, String description, int timeout) {
public TestFlow assertReply(Function<Activity, String> validateActivity, String description, int timeout) {
return new TestFlow(() -> {
System.out.println(String.format("AssertReply: Starting loop : %s (Thread:%s)", description, Thread.currentThread().getId()));
System.out.flush();
@ -240,7 +239,7 @@ public class TestFlow {
System.out.printf("AssertReply(tid:%s): Received Reply: %s ", Thread.currentThread().getId(), (replyActivity.getText() == null) ? "No Text set" : replyActivity.getText());
System.out.flush();
System.out.printf("=============\n From: %s\n To:%s\n ==========\n", (replyActivity.getFrom() == null) ? "No from set" : replyActivity.getFrom().getName(),
(replyActivity.getRecipient() == null) ? "No recipient set" : replyActivity.getRecipient().getName());
(replyActivity.getRecipient() == null) ? "No recipient set" : replyActivity.getRecipient().getName());
System.out.flush();
// if we have a reply
@ -274,7 +273,7 @@ public class TestFlow {
* @param expected
* @return
*/
public TestFlow Turn(String userSays, String expected, String description, int timeout) {
public TestFlow turn(String userSays, String expected, String description, int timeout) {
String result = null;
try {
@ -294,58 +293,58 @@ public class TestFlow {
}
}, ExecutorFactory.getExecutor())
.thenApply(arg -> { // Assert Reply
int finalTimeout = Integer.MAX_VALUE;
if (isDebug())
finalTimeout = Integer.MAX_VALUE;
Function<Activity, String> validateActivity = activity -> {
if (activity.getText().equals(expected)) {
System.out.println(String.format("TestTurn(tid:%s): Validated text is: %s", Thread.currentThread().getId(), expected));
System.out.flush();
return "SUCCESS";
}
System.out.println(String.format("TestTurn(tid:%s): Failed validate text is: %s", Thread.currentThread().getId(), expected));
.thenApply(arg -> { // Assert Reply
int finalTimeout = Integer.MAX_VALUE;
if (isDebug())
finalTimeout = Integer.MAX_VALUE;
Function<Activity, String> validateActivity = activity -> {
if (activity.getText().equals(expected)) {
System.out.println(String.format("TestTurn(tid:%s): Validated text is: %s", Thread.currentThread().getId(), expected));
System.out.flush();
return String.format("FAIL: %s received in Activity.text (%s expected)", activity.getText(), expected);
};
System.out.println(String.format("TestTurn(tid:%s): Started receive loop: %s", Thread.currentThread().getId(), description));
System.out.flush();
DateTime start = DateTime.now();
while (true) {
DateTime current = DateTime.now();
if ((current.getMillis() - start.getMillis()) > (long) finalTimeout)
return String.format("TestTurn: %d ms Timed out waiting for:'%s'", finalTimeout, description);
Activity replyActivity = this.adapter.GetNextReply();
if (replyActivity != null) {
// if we have a reply
System.out.println(String.format("TestTurn(tid:%s): Received Reply: %s",
Thread.currentThread().getId(),
String.format("\n========\n To:%s\n From:%s\n Msg:%s\n=======", replyActivity.getRecipient().getName(), replyActivity.getFrom().getName(), replyActivity.getText())
));
System.out.flush();
return validateActivity.apply(replyActivity);
} else {
System.out.println(String.format("TestTurn(tid:%s): No reply..", Thread.currentThread().getId()));
System.out.flush();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "SUCCESS";
}
})
.get(timeout, TimeUnit.MILLISECONDS);
System.out.println(String.format("TestTurn(tid:%s): Failed validate text is: %s", Thread.currentThread().getId(), expected));
System.out.flush();
return String.format("FAIL: %s received in Activity.text (%s expected)", activity.getText(), expected);
};
System.out.println(String.format("TestTurn(tid:%s): Started receive loop: %s", Thread.currentThread().getId(), description));
System.out.flush();
DateTime start = DateTime.now();
while (true) {
DateTime current = DateTime.now();
if ((current.getMillis() - start.getMillis()) > (long) finalTimeout)
return String.format("TestTurn: %d ms Timed out waiting for:'%s'", finalTimeout, description);
Activity replyActivity = this.adapter.GetNextReply();
if (replyActivity != null) {
// if we have a reply
System.out.println(String.format("TestTurn(tid:%s): Received Reply: %s",
Thread.currentThread().getId(),
String.format("\n========\n To:%s\n From:%s\n Msg:%s\n=======", replyActivity.getRecipient().getName(), replyActivity.getFrom().getName(), replyActivity.getText())
));
System.out.flush();
return validateActivity.apply(replyActivity);
} else {
System.out.println(String.format("TestTurn(tid:%s): No reply..", Thread.currentThread().getId()));
System.out.flush();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
})
.get(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
@ -364,20 +363,20 @@ public class TestFlow {
* @param expected
* @return
*/
public TestFlow Test(String userSays, String expected) {
return Test(userSays, expected, null, 3000);
public TestFlow test(String userSays, String expected) {
return test(userSays, expected, null, 3000);
}
public TestFlow Test(String userSays, String expected, String description) {
return Test(userSays, expected, description, 3000);
public TestFlow test(String userSays, String expected, String description) {
return test(userSays, expected, description, 3000);
}
public TestFlow Test(String userSays, String expected, String description, int timeout) {
public TestFlow test(String userSays, String expected, String description, int timeout) {
if (expected == null)
throw new IllegalArgumentException("expected");
return this.Send(userSays)
.AssertReply(expected, description, timeout);
return this.send(userSays)
.assertReply(expected, description, timeout);
}
/**
@ -387,20 +386,20 @@ public class TestFlow {
* @param expected
* @return
*/
public TestFlow Test(String userSays, Activity expected) {
return Test(userSays, expected, null, 3000);
public TestFlow test(String userSays, Activity expected) {
return test(userSays, expected, null, 3000);
}
public TestFlow Test(String userSays, Activity expected, String description) {
return Test(userSays, expected, description, 3000);
public TestFlow test(String userSays, Activity expected, String description) {
return test(userSays, expected, description, 3000);
}
public TestFlow Test(String userSays, Activity expected, String description, int timeout) {
public TestFlow test(String userSays, Activity expected, String description, int timeout) {
if (expected == null)
throw new IllegalArgumentException("expected");
return this.Send(userSays)
.AssertReply(expected, description, timeout);
return this.send(userSays)
.assertReply(expected, description, timeout);
}
/**
@ -410,20 +409,20 @@ public class TestFlow {
* @param expected
* @return
*/
public TestFlow Test(String userSays, Function<Activity, String> expected) {
return Test(userSays, expected, null, 3000);
public TestFlow test(String userSays, Function<Activity, String> expected) {
return test(userSays, expected, null, 3000);
}
public TestFlow Test(String userSays, Function<Activity, String> expected, String description) {
return Test(userSays, expected, description, 3000);
public TestFlow test(String userSays, Function<Activity, String> expected, String description) {
return test(userSays, expected, description, 3000);
}
public TestFlow Test(String userSays, Function<Activity, String> expected, String description, int timeout) {
public TestFlow test(String userSays, Function<Activity, String> expected, String description, int timeout) {
if (expected == null)
throw new IllegalArgumentException("expected");
return this.Send(userSays)
.AssertReply(expected, description, timeout);
return this.send(userSays)
.assertReply(expected, description, timeout);
}
/**
@ -432,19 +431,19 @@ public class TestFlow {
* @param candidates
* @return
*/
public TestFlow AssertReplyOneOf(String[] candidates) {
return AssertReplyOneOf(candidates, null, 3000);
public TestFlow assertReplyOneOf(String[] candidates) {
return assertReplyOneOf(candidates, null, 3000);
}
public TestFlow AssertReplyOneOf(String[] candidates, String description) {
return AssertReplyOneOf(candidates, description, 3000);
public TestFlow assertReplyOneOf(String[] candidates, String description) {
return assertReplyOneOf(candidates, description, 3000);
}
public TestFlow AssertReplyOneOf(String[] candidates, String description, int timeout) {
public TestFlow assertReplyOneOf(String[] candidates, String description, int timeout) {
if (candidates == null)
throw new IllegalArgumentException("candidates");
return this.AssertReply((reply) -> {
return this.assertReply((reply) -> {
for (String candidate : candidates) {
if (StringUtils.equals(reply.getText(), candidate))
return null;

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

@ -1,327 +1,326 @@
package com.microsoft.bot.builder.base;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.common.io.BaseEncoding;
import okhttp3.*;
import okhttp3.internal.Util;
import okio.Buffer;
import okio.BufferedSource;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
public class InterceptorManager {
private final static String RECORD_FOLDER = "session-records/";
private Map<String, String> textReplacementRules = new HashMap<String, String>();
// Stores a map of all the HTTP properties in a session
// A state machine ensuring a test is always reset before another one is setup
protected RecordedData recordedData;
private final String testName;
private final TestBase.TestMode testMode;
private InterceptorManager(String testName, TestBase.TestMode testMode) {
this.testName = testName;
this.testMode = testMode;
}
public void addTextReplacementRule(String regex, String replacement) {
textReplacementRules.put(regex, replacement);
}
// factory method
public static InterceptorManager create(String testName, TestBase.TestMode testMode) throws IOException {
InterceptorManager interceptorManager = new InterceptorManager(testName, testMode);
return interceptorManager;
}
public boolean isRecordMode() {
return testMode == TestBase.TestMode.RECORD;
}
public boolean isPlaybackMode() {
return testMode == TestBase.TestMode.PLAYBACK;
}
public Interceptor initInterceptor() throws IOException {
switch (testMode) {
case RECORD:
recordedData = new RecordedData();
return new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
return record(chain);
}
};
case PLAYBACK:
readDataFromFile();
return new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
return playback(chain);
}
};
default:
System.out.println("==> Unknown AZURE_TEST_MODE: " + testMode);
};
return null;
}
public void finalizeInterceptor() throws IOException {
switch (testMode) {
case RECORD:
writeDataToFile();
break;
case PLAYBACK:
// Do nothing
break;
default:
System.out.println("==> Unknown AZURE_TEST_MODE: " + testMode);
};
}
private Response record(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
NetworkCallRecord networkCallRecord = new NetworkCallRecord();
networkCallRecord.Headers = new HashMap<>();
if (request.header("Content-Type") != null) {
networkCallRecord.Headers.put("Content-Type", request.header("Content-Type"));
}
if (request.header("x-ms-version") != null) {
networkCallRecord.Headers.put("x-ms-version", request.header("x-ms-version"));
}
if (request.header("User-Agent") != null) {
networkCallRecord.Headers.put("User-Agent", request.header("User-Agent"));
}
networkCallRecord.Method = request.method();
networkCallRecord.Uri = applyReplacementRule(request.url().toString().replaceAll("\\?$", ""));
networkCallRecord.Body = bodyToString(request);
Response response = chain.proceed(request);
networkCallRecord.Response = new HashMap<>();
networkCallRecord.Response.put("StatusCode", Integer.toString(response.code()));
extractResponseData(networkCallRecord.Response, response);
// remove pre-added header if this is a waiting or redirection
if (networkCallRecord.Response.get("Body") != null) {
if (networkCallRecord.Response.get("Body").contains("<Status>InProgress</Status>")
|| Integer.parseInt(networkCallRecord.Response.get("StatusCode")) == 307) {
// Do nothing
} else {
synchronized (recordedData.getNetworkCallRecords()) {
recordedData.getNetworkCallRecords().add(networkCallRecord);
}
}
}
return response;
}
private String bodyToString(final Request request) {
try {
final Buffer buffer = new Buffer();
request.newBuilder().build().body().writeTo(buffer);
return buffer.readUtf8();
} catch (final Exception e) {
return "";
}
}
private Response playback(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
String incomingUrl = applyReplacementRule(request.url().toString());
String incomingMethod = request.method();
incomingUrl = removeHost(incomingUrl);
NetworkCallRecord networkCallRecord = null;
synchronized (recordedData) {
for (Iterator<NetworkCallRecord> iterator = recordedData.getNetworkCallRecords().iterator(); iterator.hasNext(); ) {
NetworkCallRecord record = iterator.next();
if (record.Method.equalsIgnoreCase(incomingMethod) && removeHost(record.Uri).equalsIgnoreCase(incomingUrl)) {
networkCallRecord = record;
iterator.remove();
break;
}
}
}
if (networkCallRecord == null) {
System.out.println("NOT FOUND - " + incomingMethod + " " + incomingUrl);
System.out.println("Remaining records " + recordedData.getNetworkCallRecords().size());
throw new IOException("==> Unexpected request: " + incomingMethod + " " + incomingUrl);
}
int recordStatusCode = Integer.parseInt(networkCallRecord.Response.get("StatusCode"));
//Response originalResponse = chain.proceed(request);
//originalResponse.body().close();
Response.Builder responseBuilder = new Response.Builder()
.request(request.newBuilder().build())
.protocol(Protocol.HTTP_2)
.code(recordStatusCode).message("-");
for (Map.Entry<String, String> pair : networkCallRecord.Response.entrySet()) {
if (!pair.getKey().equals("StatusCode") && !pair.getKey().equals("Body") && !pair.getKey().equals("Content-Length")) {
String rawHeader = pair.getValue();
for (Map.Entry<String, String> rule : textReplacementRules.entrySet()) {
if (rule.getValue() != null) {
rawHeader = rawHeader.replaceAll(rule.getKey(), rule.getValue());
}
}
responseBuilder.addHeader(pair.getKey(), rawHeader);
}
}
String rawBody = networkCallRecord.Response.get("Body");
if (rawBody != null) {
for (Map.Entry<String, String> rule : textReplacementRules.entrySet()) {
if (rule.getValue() != null) {
rawBody = rawBody.replaceAll(rule.getKey(), rule.getValue());
}
}
String rawContentType = networkCallRecord.Response.get("content-type");
String contentType = rawContentType == null
? "application/json; charset=utf-8"
: rawContentType;
ResponseBody responseBody;
if (contentType.toLowerCase().contains("application/json")) {
responseBody = ResponseBody.create(MediaType.parse(contentType), rawBody.getBytes());
} else {
responseBody = ResponseBody.create(MediaType.parse(contentType), BaseEncoding.base64().decode(rawBody));
}
responseBuilder.body(responseBody);
responseBuilder.addHeader("Content-Length", String.valueOf(rawBody.getBytes("UTF-8").length));
}
Response newResponse = responseBuilder.build();
return newResponse;
}
private void extractResponseData(Map<String, String> responseData, Response response) throws IOException {
Map<String, List<String>> headers = response.headers().toMultimap();
boolean addedRetryAfter = false;
for (Map.Entry<String, List<String>> header : headers.entrySet()) {
String headerValueToStore = header.getValue().get(0);
if (header.getKey().equalsIgnoreCase("location") || header.getKey().equalsIgnoreCase("azure-asyncoperation")) {
headerValueToStore = applyReplacementRule(headerValueToStore);
}
if (header.getKey().equalsIgnoreCase("retry-after")) {
headerValueToStore = "0";
addedRetryAfter = true;
}
responseData.put(header.getKey().toLowerCase(), headerValueToStore);
}
if (!addedRetryAfter) {
responseData.put("retry-after", "0");
}
BufferedSource bufferedSource = response.body().source();
bufferedSource.request(9223372036854775807L);
Buffer buffer = bufferedSource.buffer().clone();
String content = null;
if (response.header("Content-Encoding") == null) {
String contentType = response.header("Content-Type");
if (contentType != null) {
if (contentType.startsWith("application/json"))
{
content = buffer.readString(Util.UTF_8);
} else {
content = BaseEncoding.base64().encode(buffer.readByteArray());
}
}
} else if (response.header("Content-Encoding").equalsIgnoreCase("gzip")) {
GZIPInputStream gis = new GZIPInputStream(buffer.inputStream());
content = "";
responseData.remove("Content-Encoding".toLowerCase());
responseData.put("Content-Length".toLowerCase(), Integer.toString(content.length()));
}
if (content != null) {
content = applyReplacementRule(content);
responseData.put("Body", content);
}
}
private void readDataFromFile() throws IOException {
File recordFile = getRecordFile(testName);
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
recordedData = mapper.readValue(recordFile, RecordedData.class);
System.out.println("Total records " + recordedData.getNetworkCallRecords().size());
}
private void writeDataToFile() throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
File recordFile = getRecordFile(testName);
recordFile.createNewFile();
mapper.writeValue(recordFile, recordedData);
}
private File getRecordFile(String testName) {
URL folderUrl = InterceptorManager.class.getClassLoader().getResource(".");
File folderFile = new File(folderUrl.getPath() + RECORD_FOLDER);
if (!folderFile.exists()) {
folderFile.mkdir();
}
String filePath = folderFile.getPath() + "/" + testName + ".json";
System.out.println("==> Playback file path: " + filePath);
return new File(filePath);
}
private String applyReplacementRule(String text) {
for (Map.Entry<String, String> rule : textReplacementRules.entrySet()) {
if (rule.getValue() != null) {
text = text.replaceAll(rule.getKey(), rule.getValue());
}
}
return text;
}
private String removeHost(String url) {
URI uri = URI.create(url);
return String.format("%s?%s", uri.getPath(), uri.getQuery());
}
public void pushVariable(String variable) {
if (isRecordMode()) {
synchronized (recordedData.getVariables()) {
recordedData.getVariables().add(variable);
}
}
}
public String popVariable() {
synchronized (recordedData.getVariables()) {
return recordedData.getVariables().remove();
}
}
}
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder.base;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.common.io.BaseEncoding;
import okhttp3.*;
import okhttp3.internal.Util;
import okio.Buffer;
import okio.BufferedSource;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
public class InterceptorManager {
private final static String RECORD_FOLDER = "session-records/";
private final String testName;
// Stores a map of all the HTTP properties in a session
// A state machine ensuring a test is always reset before another one is setup
private final TestBase.TestMode testMode;
protected RecordedData recordedData;
private Map<String, String> textReplacementRules = new HashMap<String, String>();
private InterceptorManager(String testName, TestBase.TestMode testMode) {
this.testName = testName;
this.testMode = testMode;
}
// factory method
public static InterceptorManager create(String testName, TestBase.TestMode testMode) throws IOException {
InterceptorManager interceptorManager = new InterceptorManager(testName, testMode);
return interceptorManager;
}
public void addTextReplacementRule(String regex, String replacement) {
textReplacementRules.put(regex, replacement);
}
public boolean isRecordMode() {
return testMode == TestBase.TestMode.RECORD;
}
public boolean isPlaybackMode() {
return testMode == TestBase.TestMode.PLAYBACK;
}
public Interceptor initInterceptor() throws IOException {
switch (testMode) {
case RECORD:
recordedData = new RecordedData();
return new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
return record(chain);
}
};
case PLAYBACK:
readDataFromFile();
return new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
return playback(chain);
}
};
default:
System.out.println("==> Unknown AZURE_TEST_MODE: " + testMode);
}
return null;
}
public void finalizeInterceptor() throws IOException {
switch (testMode) {
case RECORD:
writeDataToFile();
break;
case PLAYBACK:
// Do nothing
break;
default:
System.out.println("==> Unknown AZURE_TEST_MODE: " + testMode);
}
}
private Response record(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
NetworkCallRecord networkCallRecord = new NetworkCallRecord();
networkCallRecord.Headers = new HashMap<>();
if (request.header("Content-Type") != null) {
networkCallRecord.Headers.put("Content-Type", request.header("Content-Type"));
}
if (request.header("x-ms-version") != null) {
networkCallRecord.Headers.put("x-ms-version", request.header("x-ms-version"));
}
if (request.header("User-Agent") != null) {
networkCallRecord.Headers.put("User-Agent", request.header("User-Agent"));
}
networkCallRecord.Method = request.method();
networkCallRecord.Uri = applyReplacementRule(request.url().toString().replaceAll("\\?$", ""));
networkCallRecord.Body = bodyToString(request);
Response response = chain.proceed(request);
networkCallRecord.Response = new HashMap<>();
networkCallRecord.Response.put("StatusCode", Integer.toString(response.code()));
extractResponseData(networkCallRecord.Response, response);
// remove pre-added header if this is a waiting or redirection
if (networkCallRecord.Response.get("Body") != null) {
if (networkCallRecord.Response.get("Body").contains("<Status>InProgress</Status>")
|| Integer.parseInt(networkCallRecord.Response.get("StatusCode")) == 307) {
// Do nothing
} else {
synchronized (recordedData.getNetworkCallRecords()) {
recordedData.getNetworkCallRecords().add(networkCallRecord);
}
}
}
return response;
}
private String bodyToString(final Request request) {
try {
final Buffer buffer = new Buffer();
request.newBuilder().build().body().writeTo(buffer);
return buffer.readUtf8();
} catch (final Exception e) {
return "";
}
}
private Response playback(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
String incomingUrl = applyReplacementRule(request.url().toString());
String incomingMethod = request.method();
incomingUrl = removeHost(incomingUrl);
NetworkCallRecord networkCallRecord = null;
synchronized (recordedData) {
for (Iterator<NetworkCallRecord> iterator = recordedData.getNetworkCallRecords().iterator(); iterator.hasNext(); ) {
NetworkCallRecord record = iterator.next();
if (record.Method.equalsIgnoreCase(incomingMethod) && removeHost(record.Uri).equalsIgnoreCase(incomingUrl)) {
networkCallRecord = record;
iterator.remove();
break;
}
}
}
if (networkCallRecord == null) {
System.out.println("NOT FOUND - " + incomingMethod + " " + incomingUrl);
System.out.println("Remaining records " + recordedData.getNetworkCallRecords().size());
throw new IOException("==> Unexpected request: " + incomingMethod + " " + incomingUrl);
}
int recordStatusCode = Integer.parseInt(networkCallRecord.Response.get("StatusCode"));
//Response originalResponse = chain.proceed(request);
//originalResponse.body().close();
Response.Builder responseBuilder = new Response.Builder()
.request(request.newBuilder().build())
.protocol(Protocol.HTTP_2)
.code(recordStatusCode).message("-");
for (Map.Entry<String, String> pair : networkCallRecord.Response.entrySet()) {
if (!pair.getKey().equals("StatusCode") && !pair.getKey().equals("Body") && !pair.getKey().equals("Content-Length")) {
String rawHeader = pair.getValue();
for (Map.Entry<String, String> rule : textReplacementRules.entrySet()) {
if (rule.getValue() != null) {
rawHeader = rawHeader.replaceAll(rule.getKey(), rule.getValue());
}
}
responseBuilder.addHeader(pair.getKey(), rawHeader);
}
}
String rawBody = networkCallRecord.Response.get("Body");
if (rawBody != null) {
for (Map.Entry<String, String> rule : textReplacementRules.entrySet()) {
if (rule.getValue() != null) {
rawBody = rawBody.replaceAll(rule.getKey(), rule.getValue());
}
}
String rawContentType = networkCallRecord.Response.get("content-type");
String contentType = rawContentType == null
? "application/json; charset=utf-8"
: rawContentType;
ResponseBody responseBody;
if (contentType.toLowerCase().contains("application/json")) {
responseBody = ResponseBody.create(MediaType.parse(contentType), rawBody.getBytes());
} else {
responseBody = ResponseBody.create(MediaType.parse(contentType), BaseEncoding.base64().decode(rawBody));
}
responseBuilder.body(responseBody);
responseBuilder.addHeader("Content-Length", String.valueOf(rawBody.getBytes(StandardCharsets.UTF_8).length));
}
Response newResponse = responseBuilder.build();
return newResponse;
}
private void extractResponseData(Map<String, String> responseData, Response response) throws IOException {
Map<String, List<String>> headers = response.headers().toMultimap();
boolean addedRetryAfter = false;
for (Map.Entry<String, List<String>> header : headers.entrySet()) {
String headerValueToStore = header.getValue().get(0);
if (header.getKey().equalsIgnoreCase("location") || header.getKey().equalsIgnoreCase("azure-asyncoperation")) {
headerValueToStore = applyReplacementRule(headerValueToStore);
}
if (header.getKey().equalsIgnoreCase("retry-after")) {
headerValueToStore = "0";
addedRetryAfter = true;
}
responseData.put(header.getKey().toLowerCase(), headerValueToStore);
}
if (!addedRetryAfter) {
responseData.put("retry-after", "0");
}
BufferedSource bufferedSource = response.body().source();
bufferedSource.request(9223372036854775807L);
Buffer buffer = bufferedSource.buffer().clone();
String content = null;
if (response.header("Content-Encoding") == null) {
String contentType = response.header("Content-Type");
if (contentType != null) {
if (contentType.startsWith("application/json")) {
content = buffer.readString(Util.UTF_8);
} else {
content = BaseEncoding.base64().encode(buffer.readByteArray());
}
}
} else if (response.header("Content-Encoding").equalsIgnoreCase("gzip")) {
GZIPInputStream gis = new GZIPInputStream(buffer.inputStream());
content = "";
responseData.remove("Content-Encoding".toLowerCase());
responseData.put("Content-Length".toLowerCase(), Integer.toString(content.length()));
}
if (content != null) {
content = applyReplacementRule(content);
responseData.put("Body", content);
}
}
private void readDataFromFile() throws IOException {
File recordFile = getRecordFile(testName);
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
recordedData = mapper.readValue(recordFile, RecordedData.class);
System.out.println("Total records " + recordedData.getNetworkCallRecords().size());
}
private void writeDataToFile() throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
File recordFile = getRecordFile(testName);
recordFile.createNewFile();
mapper.writeValue(recordFile, recordedData);
}
private File getRecordFile(String testName) {
URL folderUrl = InterceptorManager.class.getClassLoader().getResource(".");
File folderFile = new File(folderUrl.getPath() + RECORD_FOLDER);
if (!folderFile.exists()) {
folderFile.mkdir();
}
String filePath = folderFile.getPath() + "/" + testName + ".json";
System.out.println("==> Playback file path: " + filePath);
return new File(filePath);
}
private String applyReplacementRule(String text) {
for (Map.Entry<String, String> rule : textReplacementRules.entrySet()) {
if (rule.getValue() != null) {
text = text.replaceAll(rule.getKey(), rule.getValue());
}
}
return text;
}
private String removeHost(String url) {
URI uri = URI.create(url);
return String.format("%s?%s", uri.getPath(), uri.getQuery());
}
public void pushVariable(String variable) {
if (isRecordMode()) {
synchronized (recordedData.getVariables()) {
recordedData.getVariables().add(variable);
}
}
}
public String popVariable() {
synchronized (recordedData.getVariables()) {
return recordedData.getVariables().remove();
}
}
}

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

@ -1,12 +1,15 @@
package com.microsoft.bot.builder.base;
import java.util.Map;
public class NetworkCallRecord {
public String Method;
public String Uri;
public String Body;
public Map<String, String> Headers;
public Map<String, String> Response;
}
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder.base;
import java.util.Map;
public class NetworkCallRecord {
public String Method;
public String Uri;
public String Body;
public Map<String, String> Headers;
public Map<String, String> Response;
}

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

@ -1,22 +1,25 @@
package com.microsoft.bot.builder.base;
import java.util.LinkedList;
public class RecordedData {
private LinkedList<NetworkCallRecord> networkCallRecords;
private LinkedList<String> variables;
public RecordedData() {
networkCallRecords = new LinkedList<>();
variables = new LinkedList<>();
}
public LinkedList<NetworkCallRecord> getNetworkCallRecords() {
return networkCallRecords;
}
public LinkedList<String> getVariables() {
return variables;
}
}
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder.base;
import java.util.LinkedList;
public class RecordedData {
private LinkedList<NetworkCallRecord> networkCallRecords;
private LinkedList<String> variables;
public RecordedData() {
networkCallRecords = new LinkedList<>();
variables = new LinkedList<>();
}
public LinkedList<NetworkCallRecord> getNetworkCallRecords() {
return networkCallRecords;
}
public LinkedList<String> getVariables() {
return variables;
}
}

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

@ -1,212 +1,206 @@
package com.microsoft.bot.builder.base;
import com.microsoft.bot.connector.authentication.MicrosoftAppCredentials;
import com.microsoft.rest.LogLevel;
import com.microsoft.rest.RestClient;
import com.microsoft.rest.ServiceResponseBuilder;
import com.microsoft.rest.credentials.ServiceClientCredentials;
import com.microsoft.rest.credentials.TokenCredentials;
import com.microsoft.rest.interceptors.LoggingInterceptor;
import com.microsoft.rest.serializer.JacksonAdapter;
import org.junit.*;
import org.junit.rules.TestName;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
public abstract class TestBase {
private PrintStream out;
protected enum RunCondition {
MOCK_ONLY,
LIVE_ONLY,
BOTH
}
public enum TestMode {
PLAYBACK,
RECORD
}
private final RunCondition runCondition;
protected TestBase() {
this(RunCondition.BOTH);
}
protected TestBase(RunCondition runCondition) {
this.runCondition = runCondition;
}
private String shouldCancelTest(boolean isPlaybackMode) {
// Determine whether to run the test based on the condition the test has been configured with
switch (this.runCondition) {
case MOCK_ONLY:
return (!isPlaybackMode) ? "Test configured to run only as mocked, not live." : null;
case LIVE_ONLY:
return (isPlaybackMode) ? "Test configured to run only as live, not mocked." : null;
default:
return null;
}
}
private static TestMode testMode = null;
protected final static String ZERO_CLIENT_ID = "00000000-0000-0000-0000-000000000000";
protected final static String ZERO_CLIENT_SECRET = "00000000000000000000000";
protected final static String ZERO_USER_ID = "<--dummy-user-id-->";
protected final static String ZERO_BOT_ID = "<--dummy-bot-id-->";
protected final static String ZERO_TOKEN = "<--dummy-token-->";
private static final String PLAYBACK_URI = "http://localhost:1234";
protected static String hostUri = null;
protected static String clientId = null;
protected static String clientSecret = null;
protected static String userId = null;
protected static String botId = null;
private static void initTestMode() throws IOException {
String azureTestMode = System.getenv("AZURE_TEST_MODE");
if (azureTestMode != null) {
if (azureTestMode.equalsIgnoreCase("Record")) {
testMode = TestMode.RECORD;
} else if (azureTestMode.equalsIgnoreCase("Playback")) {
testMode = TestMode.PLAYBACK;
} else {
throw new IOException("Unknown AZURE_TEST_MODE: " + azureTestMode);
}
} else {
System.out.print("Environment variable 'AZURE_TEST_MODE' has not been set yet. Using 'PLAYBACK' mode.");
testMode = TestMode.RECORD;
}
}
private static void initParams() {
try {
Properties mavenProps = new Properties();
InputStream in = TestBase.class.getResourceAsStream("/maven.properties");
if (in == null) {
throw new IOException("The file \"maven.properties\" has not been generated yet. Please execute \"mvn compile\" to generate the file.");
}
mavenProps.load(in);
clientId = mavenProps.getProperty("clientId");
clientSecret = mavenProps.getProperty("clientSecret");
hostUri = mavenProps.getProperty("hostUrl");
userId = mavenProps.getProperty("userId");
botId = mavenProps.getProperty("botId");
} catch (IOException e) {
clientId = ZERO_CLIENT_ID;
clientSecret = ZERO_CLIENT_SECRET;
hostUri = PLAYBACK_URI;
userId = ZERO_USER_ID;
botId = ZERO_BOT_ID;
}
}
public static boolean isPlaybackMode() {
if (testMode == null) try {
initTestMode();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Can't init test mode.");
}
return testMode == TestMode.PLAYBACK;
}
public static boolean isRecordMode() {
return !isPlaybackMode();
}
@Rule
public TestName testName = new TestName();
protected InterceptorManager interceptorManager = null;
private static void printThreadInfo(String what) {
long id = Thread.currentThread().getId();
String name = Thread.currentThread().getName();
System.out.println(String.format("\n***\n*** [%s:%s] - %s\n***\n", name, id, what));
}
@BeforeClass
public static void beforeClass() throws IOException {
printThreadInfo("beforeClass");
initTestMode();
initParams();
}
@Before
public void beforeTest() throws IOException {
printThreadInfo(String.format("%s: %s", "beforeTest", testName.getMethodName()));
final String skipMessage = shouldCancelTest(isPlaybackMode());
Assume.assumeTrue(skipMessage, skipMessage == null);
interceptorManager = InterceptorManager.create(testName.getMethodName(), testMode);
ServiceClientCredentials credentials;
RestClient restClient;
if (isPlaybackMode()) {
credentials = new TokenCredentials(null, ZERO_TOKEN);
restClient = buildRestClient(new RestClient.Builder()
.withBaseUrl(hostUri + "/")
.withSerializerAdapter(new JacksonAdapter())
.withResponseBuilderFactory(new ServiceResponseBuilder.Factory())
.withCredentials(credentials)
.withLogLevel(LogLevel.NONE)
.withNetworkInterceptor(new LoggingInterceptor(LogLevel.BODY_AND_HEADERS))
.withInterceptor(interceptorManager.initInterceptor())
,true);
out = System.out;
System.setOut(new PrintStream(new OutputStream() {
public void write(int b) {
//DO NOTHING
}
}));
}
else { // Record mode
credentials = new MicrosoftAppCredentials(clientId, clientSecret);
restClient = buildRestClient(new RestClient.Builder()
.withBaseUrl(hostUri + "/")
.withSerializerAdapter(new JacksonAdapter())
.withResponseBuilderFactory(new ServiceResponseBuilder.Factory())
.withCredentials(credentials)
.withLogLevel(LogLevel.NONE)
.withReadTimeout(3, TimeUnit.MINUTES)
.withNetworkInterceptor(new LoggingInterceptor(LogLevel.BODY_AND_HEADERS))
.withInterceptor(interceptorManager.initInterceptor())
,false);
//interceptorManager.addTextReplacementRule(hostUri, PLAYBACK_URI);
}
initializeClients(restClient, botId, userId);
}
@After
public void afterTest() throws IOException {
if(shouldCancelTest(isPlaybackMode()) != null) {
return;
}
cleanUpResources();
interceptorManager.finalizeInterceptor();
}
protected void addTextReplacementRule(String from, String to ) {
interceptorManager.addTextReplacementRule(from, to);
}
protected RestClient buildRestClient(RestClient.Builder builder, boolean isMocked) {
return builder.build();
}
protected abstract void initializeClients(RestClient restClient, String botId, String userId) throws IOException;
protected abstract void cleanUpResources();
}
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder.base;
import com.microsoft.bot.connector.authentication.MicrosoftAppCredentials;
import com.microsoft.rest.LogLevel;
import com.microsoft.rest.RestClient;
import com.microsoft.rest.ServiceResponseBuilder;
import com.microsoft.rest.credentials.ServiceClientCredentials;
import com.microsoft.rest.credentials.TokenCredentials;
import com.microsoft.rest.interceptors.LoggingInterceptor;
import com.microsoft.rest.serializer.JacksonAdapter;
import org.junit.*;
import org.junit.rules.TestName;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
public abstract class TestBase {
protected final static String ZERO_CLIENT_ID = "00000000-0000-0000-0000-000000000000";
protected final static String ZERO_CLIENT_SECRET = "00000000000000000000000";
protected final static String ZERO_USER_ID = "<--dummy-user-id-->";
protected final static String ZERO_BOT_ID = "<--dummy-bot-id-->";
protected final static String ZERO_TOKEN = "<--dummy-token-->";
private static final String PLAYBACK_URI = "http://localhost:1234";
protected static String hostUri = null;
protected static String clientId = null;
protected static String clientSecret = null;
protected static String userId = null;
protected static String botId = null;
private static TestMode testMode = null;
private final RunCondition runCondition;
@Rule
public TestName testName = new TestName();
protected InterceptorManager interceptorManager = null;
private PrintStream out;
protected TestBase() {
this(RunCondition.BOTH);
}
protected TestBase(RunCondition runCondition) {
this.runCondition = runCondition;
}
private static void initTestMode() throws IOException {
String azureTestMode = System.getenv("AZURE_TEST_MODE");
if (azureTestMode != null) {
if (azureTestMode.equalsIgnoreCase("Record")) {
testMode = TestMode.RECORD;
} else if (azureTestMode.equalsIgnoreCase("Playback")) {
testMode = TestMode.PLAYBACK;
} else {
throw new IOException("Unknown AZURE_TEST_MODE: " + azureTestMode);
}
} else {
System.out.print("Environment variable 'AZURE_TEST_MODE' has not been set yet. Using 'PLAYBACK' mode.");
testMode = TestMode.RECORD;
}
}
private static void initParams() {
try {
Properties mavenProps = new Properties();
InputStream in = TestBase.class.getResourceAsStream("/maven.properties");
if (in == null) {
throw new IOException("The file \"maven.properties\" has not been generated yet. Please execute \"mvn compile\" to generate the file.");
}
mavenProps.load(in);
clientId = mavenProps.getProperty("clientId");
clientSecret = mavenProps.getProperty("clientSecret");
hostUri = mavenProps.getProperty("hostUrl");
userId = mavenProps.getProperty("userId");
botId = mavenProps.getProperty("botId");
} catch (IOException e) {
clientId = ZERO_CLIENT_ID;
clientSecret = ZERO_CLIENT_SECRET;
hostUri = PLAYBACK_URI;
userId = ZERO_USER_ID;
botId = ZERO_BOT_ID;
}
}
public static boolean isPlaybackMode() {
if (testMode == null) try {
initTestMode();
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Can't init test mode.");
}
return testMode == TestMode.PLAYBACK;
}
public static boolean isRecordMode() {
return !isPlaybackMode();
}
private static void printThreadInfo(String what) {
long id = Thread.currentThread().getId();
String name = Thread.currentThread().getName();
System.out.println(String.format("\n***\n*** [%s:%s] - %s\n***\n", name, id, what));
}
@BeforeClass
public static void beforeClass() throws IOException {
printThreadInfo("beforeClass");
initTestMode();
initParams();
}
private String shouldCancelTest(boolean isPlaybackMode) {
// Determine whether to run the test based on the condition the test has been configured with
switch (this.runCondition) {
case MOCK_ONLY:
return (!isPlaybackMode) ? "Test configured to run only as mocked, not live." : null;
case LIVE_ONLY:
return (isPlaybackMode) ? "Test configured to run only as live, not mocked." : null;
default:
return null;
}
}
@Before
public void beforeTest() throws IOException {
printThreadInfo(String.format("%s: %s", "beforeTest", testName.getMethodName()));
final String skipMessage = shouldCancelTest(isPlaybackMode());
Assume.assumeTrue(skipMessage, skipMessage == null);
interceptorManager = InterceptorManager.create(testName.getMethodName(), testMode);
ServiceClientCredentials credentials;
RestClient restClient;
if (isPlaybackMode()) {
credentials = new TokenCredentials(null, ZERO_TOKEN);
restClient = buildRestClient(new RestClient.Builder()
.withBaseUrl(hostUri + "/")
.withSerializerAdapter(new JacksonAdapter())
.withResponseBuilderFactory(new ServiceResponseBuilder.Factory())
.withCredentials(credentials)
.withLogLevel(LogLevel.NONE)
.withNetworkInterceptor(new LoggingInterceptor(LogLevel.BODY_AND_HEADERS))
.withInterceptor(interceptorManager.initInterceptor())
, true);
out = System.out;
System.setOut(new PrintStream(new OutputStream() {
public void write(int b) {
//DO NOTHING
}
}));
} else { // Record mode
credentials = new MicrosoftAppCredentials(clientId, clientSecret);
restClient = buildRestClient(new RestClient.Builder()
.withBaseUrl(hostUri + "/")
.withSerializerAdapter(new JacksonAdapter())
.withResponseBuilderFactory(new ServiceResponseBuilder.Factory())
.withCredentials(credentials)
.withLogLevel(LogLevel.NONE)
.withReadTimeout(3, TimeUnit.MINUTES)
.withNetworkInterceptor(new LoggingInterceptor(LogLevel.BODY_AND_HEADERS))
.withInterceptor(interceptorManager.initInterceptor())
, false);
//interceptorManager.addTextReplacementRule(hostUri, PLAYBACK_URI);
}
initializeClients(restClient, botId, userId);
}
@After
public void afterTest() throws IOException {
if (shouldCancelTest(isPlaybackMode()) != null) {
return;
}
cleanUpResources();
interceptorManager.finalizeInterceptor();
}
protected void addTextReplacementRule(String from, String to) {
interceptorManager.addTextReplacementRule(from, to);
}
protected RestClient buildRestClient(RestClient.Builder builder, boolean isMocked) {
return builder.build();
}
protected abstract void initializeClients(RestClient restClient, String botId, String userId) throws IOException;
protected abstract void cleanUpResources();
protected enum RunCondition {
MOCK_ONLY,
LIVE_ONLY,
BOTH
}
public enum TestMode {
PLAYBACK,
RECORD
}
}