package org.togglz.zookeeper;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryOneTime;
import org.apache.curator.test.TestingServer;
import org.apache.zookeeper.client.ZooKeeperSaslClient;
import org.junit.After;
import org.junit.Test;
import org.togglz.core.Feature;
import org.togglz.core.activation.UsernameActivationStrategy;
import org.togglz.core.repository.FeatureState;
import org.togglz.core.util.FeatureStateStorageWrapper;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.apache.commons.lang.builder.EqualsBuilder.reflectionEquals;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
/**
* @author Ryan Gardner
* @date 5/26/16
*/
public class ZookeeperStateRepositoryTest {
public static final String TEST_ZNODE = "/test/features";
private ZookeeperStateRepository stateRepository;
static class ServerClientPair {
public final TestingServer server;
public final CuratorFramework client;
ServerClientPair(TestingServer server, CuratorFramework client) {
this.server = server;
this.client = client;
}
}
public static ServerClientPair startServer(Map<String, String> data) throws Exception {
TestingServer server = new TestingServer();
CuratorFramework client;
// for environments where sasl is set, null it our for our test
System.setProperty(ZooKeeperSaslClient.ENABLE_CLIENT_SASL_KEY, "false");
client = CuratorFrameworkFactory.builder().connectString(server.getConnectString()).retryPolicy(new RetryOneTime(2000)).build();
client.start();
for (Map.Entry<String, String> initialData : data.entrySet()) {
client.create().creatingParentContainersIfNeeded().forPath(initialData.getKey(), initialData.getValue().getBytes("UTF-8"));
}
return new ServerClientPair(server, client);
}
public static void stopServer(ServerClientPair pair) throws IOException {
pair.client.close();
pair.server.stop();
System.clearProperty(ZooKeeperSaslClient.ENABLE_CLIENT_SASL_KEY);
}
ServerClientPair serverClientPair;
public void setupTestWithEmptyDatastore() throws Exception {
setupTestWithData(Collections.EMPTY_MAP);
}
public void setupTestWithData(Map<String,String> initialParameters) throws Exception {
serverClientPair = startServer(initialParameters);
stateRepository = ZookeeperStateRepository.newBuilder(serverClientPair.client, TEST_ZNODE).build();
}
@After
public void cleanUp() throws Exception {
stopServer(serverClientPair);
}
@Test
public void testFeatureSavingAndLoading() throws Exception {
setupTestWithEmptyDatastore();
assertNull(stateRepository.getFeatureState(TestFeature.FEATURE));
stateRepository.setFeatureState(new FeatureState(TestFeature.FEATURE));
assertNotNull(stateRepository.getFeatureState(TestFeature.FEATURE));
}
@Test
public void testActivationStrategySavingAndLoading() throws Exception {
setupTestWithEmptyDatastore();
FeatureState savedFeatureState = new FeatureState(TestFeature.FEATURE);
savedFeatureState.setStrategyId(UsernameActivationStrategy.ID);
savedFeatureState.setParameter(UsernameActivationStrategy.PARAM_USERS, "user1, user2, user3");
stateRepository.setFeatureState(savedFeatureState);
FeatureState loadedFeatureState = stateRepository.getFeatureState(TestFeature.FEATURE);
assertThat(reflectionEquals(savedFeatureState, loadedFeatureState), is(true));
}
@Test
public void testEnabledStateSavingAndLoading() throws Exception {
setupTestWithEmptyDatastore();
FeatureState savedFeatureState = new FeatureState(TestFeature.FEATURE).enable();
stateRepository.setFeatureState(savedFeatureState);
FeatureState loadedFeatureState = stateRepository.getFeatureState(TestFeature.FEATURE);
assertThat(loadedFeatureState.isEnabled(), is(true));
stateRepository.setFeatureState(savedFeatureState.disable());
loadedFeatureState = stateRepository.getFeatureState(TestFeature.FEATURE);
assertThat(loadedFeatureState.isEnabled(), is(false));
}
@Test
public void testZkNodeChangesUpdateFeatureState() throws Exception {
setupTestWithEmptyDatastore();
FeatureState savedFeatureState = new FeatureState(TestFeature.FEATURE);
savedFeatureState.setStrategyId(UsernameActivationStrategy.ID);
savedFeatureState.setParameter(UsernameActivationStrategy.PARAM_USERS, "user1, user2, user3");
stateRepository.setFeatureState(savedFeatureState);
FeatureState loadedFeatureState = stateRepository.getFeatureState(TestFeature.FEATURE);
assertThat(reflectionEquals(savedFeatureState, loadedFeatureState), is(true));
// Modify data in ZK
FeatureStateStorageWrapper externallySetStateWrapper = new FeatureStateStorageWrapper();
FeatureState externallySetState = new FeatureState(TestFeature.FEATURE);
ObjectMapper objectMapper = new ObjectMapper();
final String json = objectMapper.writeValueAsString(externallySetStateWrapper);
final CountDownLatch latch = new CountDownLatch(1);
new Thread(new Runnable() {
@Override
public void run() {
try {
serverClientPair.client.setData().forPath(TEST_ZNODE + "/FEATURE", json.getBytes("UTF-8"));
latch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
latch.await(2, TimeUnit.SECONDS);
Thread.sleep(25);
loadedFeatureState = stateRepository.getFeatureState(TestFeature.FEATURE);
assertThat(reflectionEquals(externallySetState, loadedFeatureState), is(true));
}
@Test
public void testLoadingWithSavedState() throws Exception {
// re-setup the server
Map<String, String> initialData = new HashMap<>();
initialData.put(TEST_ZNODE + "/FEATURE", "{\"enabled\":true,\"strategyId\":null,\"parameters\":{}}");
// recreate the zookeeper server with the data we want for this test
setupTestWithData(initialData);
FeatureState expectedFeatureState = new FeatureState(TestFeature.FEATURE);
expectedFeatureState.setEnabled(true);
FeatureState loadedFeatureState = stateRepository.getFeatureState(TestFeature.FEATURE);
assertThat(reflectionEquals(expectedFeatureState, loadedFeatureState), is(true));
}
private static enum TestFeature implements Feature {
FEATURE,
}
}