package com.ctrip.framework.apollo.configservice.controller;
import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.ctrip.framework.apollo.biz.config.BizConfig;
import com.ctrip.framework.apollo.biz.entity.ReleaseMessage;
import com.ctrip.framework.apollo.biz.message.Topics;
import com.ctrip.framework.apollo.biz.utils.EntityManagerUtil;
import com.ctrip.framework.apollo.configservice.service.ReleaseMessageServiceWithCache;
import com.ctrip.framework.apollo.configservice.util.NamespaceUtil;
import com.ctrip.framework.apollo.configservice.util.WatchKeysUtil;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.dto.ApolloConfigNotification;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.context.request.async.DeferredResult;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class NotificationControllerV2Test {
private NotificationControllerV2 controller;
private String someAppId;
private String someCluster;
private String defaultCluster;
private String defaultNamespace;
private String somePublicNamespace;
private String someDataCenter;
private long someNotificationId;
private String someClientIp;
@Mock
private ReleaseMessageServiceWithCache releaseMessageService;
@Mock
private EntityManagerUtil entityManagerUtil;
@Mock
private NamespaceUtil namespaceUtil;
@Mock
private WatchKeysUtil watchKeysUtil;
@Mock
private BizConfig bizConfig;
private Gson gson;
private Multimap<String, DeferredResult<ResponseEntity<List<ApolloConfigNotification>>>>
deferredResults;
@Before
public void setUp() throws Exception {
controller = new NotificationControllerV2();
gson = new Gson();
when(bizConfig.releaseMessageNotificationBatch()).thenReturn(100);
when(bizConfig.releaseMessageNotificationBatchIntervalInMilli()).thenReturn(5);
ReflectionTestUtils.setField(controller, "releaseMessageService", releaseMessageService);
ReflectionTestUtils.setField(controller, "entityManagerUtil", entityManagerUtil);
ReflectionTestUtils.setField(controller, "namespaceUtil", namespaceUtil);
ReflectionTestUtils.setField(controller, "watchKeysUtil", watchKeysUtil);
ReflectionTestUtils.setField(controller, "gson", gson);
ReflectionTestUtils.setField(controller, "bizConfig", bizConfig);
someAppId = "someAppId";
someCluster = "someCluster";
defaultCluster = ConfigConsts.CLUSTER_NAME_DEFAULT;
defaultNamespace = ConfigConsts.NAMESPACE_APPLICATION;
somePublicNamespace = "somePublicNamespace";
someDataCenter = "someDC";
someNotificationId = 1;
someClientIp = "someClientIp";
when(namespaceUtil.filterNamespaceName(defaultNamespace)).thenReturn(defaultNamespace);
when(namespaceUtil.filterNamespaceName(somePublicNamespace)).thenReturn(somePublicNamespace);
deferredResults =
(Multimap<String, DeferredResult<ResponseEntity<List<ApolloConfigNotification>>>>) ReflectionTestUtils
.getField(controller, "deferredResults");
}
@Test
public void testPollNotificationWithDefaultNamespace() throws Exception {
String someWatchKey = "someKey";
String anotherWatchKey = "anotherKey";
Multimap<String, String> watchKeysMap =
assembleMultiMap(defaultNamespace, Lists.newArrayList(someWatchKey, anotherWatchKey));
String notificationAsString =
transformApolloConfigNotificationsToString(defaultNamespace, someNotificationId);
when(watchKeysUtil
.assembleAllWatchKeys(someAppId, someCluster, Sets.newHashSet(defaultNamespace),
someDataCenter)).thenReturn(
watchKeysMap);
DeferredResult<ResponseEntity<List<ApolloConfigNotification>>>
deferredResult = controller
.pollNotification(someAppId, someCluster, notificationAsString, someDataCenter,
someClientIp);
assertEquals(watchKeysMap.size(), deferredResults.size());
for (String watchKey : watchKeysMap.values()) {
assertTrue(deferredResults.get(watchKey).contains(deferredResult));
}
}
@Test
public void testPollNotificationWithDefaultNamespaceAsFile() throws Exception {
String namespace = String.format("%s.%s", defaultNamespace, "properties");
when(namespaceUtil.filterNamespaceName(namespace)).thenReturn(defaultNamespace);
String someWatchKey = "someKey";
String anotherWatchKey = "anotherKey";
Multimap<String, String> watchKeysMap =
assembleMultiMap(defaultNamespace, Lists.newArrayList(someWatchKey, anotherWatchKey));
String notificationAsString =
transformApolloConfigNotificationsToString(namespace, someNotificationId);
when(watchKeysUtil
.assembleAllWatchKeys(someAppId, someCluster, Sets.newHashSet(defaultNamespace),
someDataCenter)).thenReturn(
watchKeysMap);
DeferredResult<ResponseEntity<List<ApolloConfigNotification>>>
deferredResult = controller
.pollNotification(someAppId, someCluster, notificationAsString, someDataCenter,
someClientIp);
assertEquals(watchKeysMap.size(), deferredResults.size());
for (String watchKey : watchKeysMap.values()) {
assertTrue(deferredResults.get(watchKey).contains(deferredResult));
}
}
@Test
public void testPollNotificationWithMultipleNamespaces() throws Exception {
String defaultNamespaceAsFile = defaultNamespace + ".properties";
String somePublicNamespaceAsFile = somePublicNamespace + ".xml";
when(namespaceUtil.filterNamespaceName(defaultNamespaceAsFile)).thenReturn(defaultNamespace);
when(namespaceUtil.filterNamespaceName(somePublicNamespaceAsFile))
.thenReturn(somePublicNamespaceAsFile);
String someWatchKey = "someKey";
String anotherWatchKey = "anotherKey";
String somePublicWatchKey = "somePublicWatchKey";
String somePublicFileWatchKey = "somePublicFileWatchKey";
Multimap<String, String> watchKeysMap =
assembleMultiMap(defaultNamespace, Lists.newArrayList(someWatchKey, anotherWatchKey));
watchKeysMap
.putAll(assembleMultiMap(somePublicNamespace, Lists.newArrayList(somePublicWatchKey)));
watchKeysMap
.putAll(assembleMultiMap(somePublicNamespaceAsFile,
Lists.newArrayList(somePublicFileWatchKey)));
String notificationAsString =
transformApolloConfigNotificationsToString(defaultNamespaceAsFile, someNotificationId,
somePublicNamespace, someNotificationId, somePublicNamespaceAsFile,
someNotificationId);
when(watchKeysUtil
.assembleAllWatchKeys(someAppId, someCluster,
Sets.newHashSet(defaultNamespace, somePublicNamespace, somePublicNamespaceAsFile),
someDataCenter)).thenReturn(
watchKeysMap);
DeferredResult<ResponseEntity<List<ApolloConfigNotification>>>
deferredResult = controller
.pollNotification(someAppId, someCluster, notificationAsString, someDataCenter,
someClientIp);
assertEquals(watchKeysMap.size(), deferredResults.size());
for (String watchKey : watchKeysMap.values()) {
assertTrue(deferredResults.get(watchKey).contains(deferredResult));
}
verify(watchKeysUtil, times(1)).assembleAllWatchKeys(someAppId, someCluster,
Sets.newHashSet(defaultNamespace, somePublicNamespace, somePublicNamespaceAsFile),
someDataCenter);
}
@Test
public void testPollNotificationWithMultipleNamespaceWithNotificationIdOutDated()
throws Exception {
String someWatchKey = "someKey";
String anotherWatchKey = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR)
.join(someAppId, someCluster, somePublicNamespace);
long notificationId = someNotificationId + 1;
Multimap<String, String> watchKeysMap =
assembleMultiMap(defaultNamespace, Lists.newArrayList(someWatchKey));
watchKeysMap
.putAll(assembleMultiMap(somePublicNamespace, Lists.newArrayList(anotherWatchKey)));
when(watchKeysUtil
.assembleAllWatchKeys(someAppId, someCluster,
Sets.newHashSet(defaultNamespace, somePublicNamespace), someDataCenter)).thenReturn(
watchKeysMap);
ReleaseMessage someReleaseMessage = mock(ReleaseMessage.class);
when(someReleaseMessage.getId()).thenReturn(notificationId);
when(someReleaseMessage.getMessage()).thenReturn(anotherWatchKey);
when(releaseMessageService
.findLatestReleaseMessagesGroupByMessages(Sets.newHashSet(watchKeysMap.values())))
.thenReturn(Lists.newArrayList(someReleaseMessage));
String notificationAsString =
transformApolloConfigNotificationsToString(defaultNamespace, someNotificationId,
somePublicNamespace, someNotificationId);
DeferredResult<ResponseEntity<List<ApolloConfigNotification>>>
deferredResult = controller
.pollNotification(someAppId, someCluster, notificationAsString, someDataCenter,
someClientIp);
ResponseEntity<List<ApolloConfigNotification>> result =
(ResponseEntity<List<ApolloConfigNotification>>) deferredResult.getResult();
assertEquals(HttpStatus.OK, result.getStatusCode());
assertEquals(1, result.getBody().size());
assertEquals(somePublicNamespace, result.getBody().get(0).getNamespaceName());
assertEquals(notificationId, result.getBody().get(0).getNotificationId());
}
@Test
public void testPollNotificationWithMultipleNamespacesAndHandleMessage() throws Exception {
String someWatchKey = "someKey";
String anotherWatchKey = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR)
.join(someAppId, someCluster, somePublicNamespace);
Multimap<String, String> watchKeysMap =
assembleMultiMap(defaultNamespace, Lists.newArrayList(someWatchKey));
watchKeysMap
.putAll(assembleMultiMap(somePublicNamespace, Lists.newArrayList(anotherWatchKey)));
when(watchKeysUtil
.assembleAllWatchKeys(someAppId, someCluster,
Sets.newHashSet(defaultNamespace, somePublicNamespace), someDataCenter)).thenReturn(
watchKeysMap);
String notificationAsString =
transformApolloConfigNotificationsToString(defaultNamespace, someNotificationId,
somePublicNamespace, someNotificationId);
DeferredResult<ResponseEntity<List<ApolloConfigNotification>>>
deferredResult = controller
.pollNotification(someAppId, someCluster, notificationAsString, someDataCenter,
someClientIp);
assertEquals(watchKeysMap.size(), deferredResults.size());
long someId = 1;
ReleaseMessage someReleaseMessage = new ReleaseMessage(anotherWatchKey);
someReleaseMessage.setId(someId);
controller.handleMessage(someReleaseMessage, Topics.APOLLO_RELEASE_TOPIC);
ResponseEntity<List<ApolloConfigNotification>> response =
(ResponseEntity<List<ApolloConfigNotification>>) deferredResult.getResult();
assertEquals(1, response.getBody().size());
ApolloConfigNotification notification = response.getBody().get(0);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(somePublicNamespace, notification.getNamespaceName());
assertEquals(someId, notification.getNotificationId());
}
@Test
public void testPollNotificationWithHandleMessageInBatch() throws Exception {
String someWatchKey = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR)
.join(someAppId, someCluster, defaultNamespace);
int someBatch = 1;
int someBatchInterval = 10;
Multimap<String, String> watchKeysMap =
assembleMultiMap(defaultNamespace, Lists.newArrayList(someWatchKey));
String notificationAsString =
transformApolloConfigNotificationsToString(defaultNamespace, someNotificationId);
when(watchKeysUtil
.assembleAllWatchKeys(someAppId, someCluster, Sets.newHashSet(defaultNamespace),
someDataCenter)).thenReturn(watchKeysMap);
when(bizConfig.releaseMessageNotificationBatch()).thenReturn(someBatch);
when(bizConfig.releaseMessageNotificationBatchIntervalInMilli()).thenReturn(someBatchInterval);
DeferredResult<ResponseEntity<List<ApolloConfigNotification>>>
deferredResult = controller
.pollNotification(someAppId, someCluster, notificationAsString, someDataCenter,
someClientIp);
DeferredResult<ResponseEntity<List<ApolloConfigNotification>>>
anotherDeferredResult = controller
.pollNotification(someAppId, someCluster, notificationAsString, someDataCenter,
someClientIp);
long someId = 1;
ReleaseMessage someReleaseMessage = new ReleaseMessage(someWatchKey);
someReleaseMessage.setId(someId);
controller.handleMessage(someReleaseMessage, Topics.APOLLO_RELEASE_TOPIC);
assertTrue(!anotherDeferredResult.hasResult());
TimeUnit.MILLISECONDS.sleep(someBatchInterval * 10);
assertTrue(anotherDeferredResult.hasResult());
}
private String transformApolloConfigNotificationsToString(
String namespace, long notificationId) {
List<ApolloConfigNotification> notifications =
Lists.newArrayList(assembleApolloConfigNotification(namespace, notificationId));
return gson.toJson(notifications);
}
private String transformApolloConfigNotificationsToString(String namespace, long notificationId,
String anotherNamespace,
long anotherNotificationId) {
List<ApolloConfigNotification> notifications =
Lists.newArrayList(assembleApolloConfigNotification(namespace, notificationId),
assembleApolloConfigNotification(anotherNamespace, anotherNotificationId));
return gson.toJson(notifications);
}
private String transformApolloConfigNotificationsToString(String namespace, long notificationId,
String anotherNamespace,
long anotherNotificationId,
String yetAnotherNamespace,
long yetAnotherNotificationId) {
List<ApolloConfigNotification> notifications =
Lists.newArrayList(assembleApolloConfigNotification(namespace, notificationId),
assembleApolloConfigNotification(anotherNamespace, anotherNotificationId),
assembleApolloConfigNotification(yetAnotherNamespace, yetAnotherNotificationId));
return gson.toJson(notifications);
}
private ApolloConfigNotification assembleApolloConfigNotification(String namespace,
long notificationId) {
ApolloConfigNotification notification = new ApolloConfigNotification();
notification.setNamespaceName(namespace);
notification.setNotificationId(notificationId);
return notification;
}
private Multimap<String, String> assembleMultiMap(String key, Iterable<String> values) {
Multimap<String, String> multimap = HashMultimap.create();
multimap.putAll(key, values);
return multimap;
}
}