提交 2c4800f4 编写于 作者: Z zhangzheng

add apollo mockserver

(cherry picked from commit 9de272b)
上级 44a6a7d2
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>apollo</artifactId>
<groupId>com.ctrip.framework.apollo</groupId>
<version>1.1.5-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-mockserver</artifactId>
<dependencies>
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>3.11.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.11.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>1.3.8.RELEASE</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
package com.ctrip.framework.apollo.mockserver;
import com.ctrip.framework.apollo.core.dto.ApolloConfig;
import com.ctrip.framework.apollo.core.dto.ApolloConfigNotification;
import com.ctrip.framework.apollo.core.dto.ServiceDTO;
import com.ctrip.framework.apollo.core.utils.ResourceUtils;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import okhttp3.mockwebserver.Dispatcher;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.rules.ExternalResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Create by zhangzheng on 8/22/18
* Email:zhangzheng@youzan.com
*/
public class EmbeddedApollo extends ExternalResource {
private Gson gson = new Gson();
private Logger logger = LoggerFactory.getLogger(EmbeddedApollo.class);
private Type notificationType = new TypeToken<List<ApolloConfigNotification>>(){}.getType();
private String listenningUrl;
private MockWebServer server;
@Override
protected void before() throws Throwable {
server = new MockWebServer();
final Dispatcher dispatcher = new Dispatcher() {
@Override
public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
if (request.getPath().startsWith("/services/config")) {
return new MockResponse().setResponseCode(200)
.setBody(mockConfigServiceAddr(listenningUrl));
} else if (request.getPath().startsWith("/notifications/v2")) {
String notifications = request.getRequestUrl().queryParameter("notifications");
MockResponse response = new MockResponse().setResponseCode(200).setBody(mockLongPollBody(notifications));
return response;
} else if (request.getPath().startsWith("/configs")) {
List<String> pathSegments = request.getRequestUrl().pathSegments();
String appId = pathSegments.get(1);
String cluster = pathSegments.get(2);
String namespace = pathSegments.get(3);
return new MockResponse().setResponseCode(200)
.setBody(loadConfigFor(namespace));
}
return new MockResponse().setResponseCode(404);
}
};
server.setDispatcher(dispatcher);
server.start();
//指定apollo的metaserver地址为localhost
int port = server.getPort();
this.listenningUrl = "http://localhost:"+port;
System.setProperty("apollo.env","mock");
System.setProperty("mock_meta",listenningUrl);
System.setProperty("apollo.longPollingInitialDelayInMills","1");
super.before();
}
@Override
protected void after() {
try {
server.close();
} catch (IOException e) {
logger.error("stop apollo server error", e);
}
}
private String loadConfigFor(String namespace){
String filename = String.format("mockdata-%s.properties", namespace);
final Properties prop = ResourceUtils.readConfigFile(filename, new Properties());
Map<String,String> configurations = prop.stringPropertyNames().stream().collect(
Collectors.toMap(key -> key, key -> prop.getProperty(key)));
ApolloConfig apolloConfig = new ApolloConfig("someAppId", "someCluster",namespace,"someReleaseKey");
Map<String,String> mergedConfigurations = mergeModifyByUser(namespace, configurations);
apolloConfig.setConfigurations(mergedConfigurations);
return gson.toJson(apolloConfig);
}
private String mockLongPollBody(String notificationsStr){
List<ApolloConfigNotification> oldNotifications = gson.fromJson(notificationsStr, notificationType);
List<ApolloConfigNotification> newNotifications = new ArrayList<>();
for(ApolloConfigNotification noti: oldNotifications){
newNotifications.add(new ApolloConfigNotification(noti.getNamespaceName(), noti.getNotificationId()+1));
}
return gson.toJson(newNotifications);
}
private String mockConfigServiceAddr(String addr){
ServiceDTO serviceDTO = new ServiceDTO();
serviceDTO.setAppName("someAppName");
serviceDTO.setInstanceId("someInstanceId");
serviceDTO.setHomepageUrl(addr);
return gson.toJson(Arrays.asList(serviceDTO));
}
/**
* 合并用户对namespace的修改
* @param configurations
* @return
*/
private Map<String,String> mergeModifyByUser(String namespace
, Map<String,String> configurations){
if(addedPropertyOfNamespace.containsKey(namespace)){
configurations.putAll(addedPropertyOfNamespace.get(namespace));
}
if(deletedKeysOfNamespace.containsKey(namespace)){
for(String k: deletedKeysOfNamespace.get(namespace)){
configurations.remove(k);
}
}
return configurations;
}
private Map<String,Map<String,String>> addedPropertyOfNamespace = new HashMap<>();
public void addOrModifyPropery(String namespace, String someKey, String someValue) {
if(addedPropertyOfNamespace.containsKey(namespace)){
addedPropertyOfNamespace.get(namespace).put(someKey, someValue);
}else{
addedPropertyOfNamespace.put(namespace, ImmutableMap.of(someKey, someValue));
}
}
private Map<String,Set<String>> deletedKeysOfNamespace = new HashMap<>();
public void delete(String namespace, String someKey) {
if(deletedKeysOfNamespace.containsKey(namespace)){
deletedKeysOfNamespace.get(namespace).add(someKey);
}else{
deletedKeysOfNamespace.put(namespace, ImmutableSet.of(someKey));
}
}
}
package com.ctrip.framework.apollo.mockserver;
import static org.junit.Assert.assertEquals;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.enums.PropertyChangeType;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.google.common.util.concurrent.SettableFuture;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.springframework.boot.test.WebIntegrationTest;
/**
* Create by zhangzheng on 8/10/18
* Email:zhangzheng@youzan.com
*/
public class ApolloMockServerTest {
@ClassRule
public static EmbeddedApollo embeddedApollo = new EmbeddedApollo();
@Test
public void testPropertyInject() {
String namespace = "application";
Config config = ConfigService.getConfig(namespace);
assertEquals("value1",config.getProperty("key1",""));
assertEquals("value2",config.getProperty("key2",""));
String otherNamespace = "othernamespace";
config = ConfigService.getConfig(otherNamespace);
assertEquals("othervalue1",config.getProperty("key1",""));
assertEquals("othervalue2",config.getProperty("key2",""));
}
@Test
public void testListennerTriggeredByAdd()
throws InterruptedException, ExecutionException, TimeoutException {
System.setProperty("apollo.longPollingInitialDelayInMills","1");
String someNamespace = "application";
Config config = ConfigService.getConfig(someNamespace);
SettableFuture<Boolean> changed = SettableFuture.create();
String someKey = "someKey";
String someValue = "someValue";
config.addChangeListener(new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
assertEquals(true, changeEvent.isChanged(someKey));
assertEquals(someValue, changeEvent.getChange(someKey).getNewValue());
changed.set(true);
}
});
embeddedApollo.addOrModifyPropery(someNamespace, someKey, someValue);
assertEquals(true, changed.get(1000, TimeUnit.MILLISECONDS));
}
@Test
public void testListennerTriggeredByDel()
throws InterruptedException, ExecutionException, TimeoutException {
String someNamespace = "application";
Config config = ConfigService.getConfig(someNamespace);
SettableFuture<Boolean> changed = SettableFuture.create();
String someKey = "key1";
config.addChangeListener(new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
assertEquals(true, changeEvent.isChanged(someKey));
assertEquals(PropertyChangeType.DELETED, changeEvent.getChange(someKey).getChangeType());
changed.set(true);
}
});
embeddedApollo.delete(someNamespace, someKey);
assertEquals(true, changed.get(1000, TimeUnit.MILLISECONDS));
}
}
package com.ctrip.framework.apollo.mockserver;
import com.ctrip.framework.apollo.mockserver.SpringIntegrationTest.TestConfiguration;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
import static org.junit.Assert.*;
import com.google.common.util.concurrent.SettableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Create by zhangzheng on 8/16/18
* Email:zhangzheng@youzan.com
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
public class SpringIntegrationTest {
@Autowired
TestBean testBean;
@ClassRule
public static EmbeddedApollo embeddedApollo = new EmbeddedApollo();
@Test
public void testPropertyInjectAndListener()
throws InterruptedException, ExecutionException, TimeoutException {
assertEquals("value1", testBean.key1);
assertEquals("value2", testBean.key2);
String otherNamespace = "othernamespace";
embeddedApollo.addOrModifyPropery(otherNamespace,"someKey","someValue");
ConfigChangeEvent changeEvent = testBean.futureData.get(5000, TimeUnit.MILLISECONDS);
assertEquals(otherNamespace, changeEvent.getNamespace());
assertEquals("someValue", changeEvent.getChange("someKey").getNewValue());
}
@EnableApolloConfig("application")
@Configuration
static class TestConfiguration{
@Bean
public TestBean testBean(){
return new TestBean();
}
}
static class TestBean{
@Value("${key1:default}")
String key1;
@Value("${key2:default}")
String key2;
SettableFuture<ConfigChangeEvent> futureData = SettableFuture.create();
@ApolloConfigChangeListener("othernamespace")
private void onChange(ConfigChangeEvent changeEvent) {
futureData.set(changeEvent);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册