package mousio.etcd4j; import java.io.IOException; import java.net.URI; import java.util.List; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import mousio.client.retry.RetryWithExponentialBackOff; import mousio.etcd4j.promises.EtcdResponsePromise; import mousio.etcd4j.responses.EtcdAuthenticationException; import mousio.etcd4j.responses.EtcdException; import mousio.etcd4j.responses.EtcdHealthResponse; import mousio.etcd4j.responses.EtcdKeyAction; import mousio.etcd4j.responses.EtcdKeysResponse; import mousio.etcd4j.responses.EtcdLeaderStatsResponse; import mousio.etcd4j.responses.EtcdMembersResponse; import mousio.etcd4j.responses.EtcdSelfStatsResponse; import mousio.etcd4j.responses.EtcdStoreStatsResponse; import mousio.etcd4j.responses.EtcdVersionResponse; import mousio.etcd4j.transport.EtcdNettyClient; import mousio.etcd4j.transport.EtcdNettyConfig; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * Performs tests on a real server at local address. All actions are performed in "etcd4j_test" dir */ public class TestFunctionality { private EtcdClient etcd; @Before public void setUp() throws Exception { this.etcd = new EtcdClient(); this.etcd.setRetryHandler(new RetryWithExponentialBackOff(20, 4, 10000)); try { etcd.deleteDir("/etcd4j_test").recursive().send().get(); etcd.deleteDir("/etcd4j_testGetAll_1").recursive().send().get(); etcd.deleteDir("/etcd4j_testGetAll_2").recursive().send().get(); } catch (EtcdException | IOException e) { } } @After public void tearDown() throws Exception { try { etcd.deleteDir("/etcd4j_test").recursive().send().get(); etcd.deleteDir("/etcd4j_testGetAll_1").recursive().send().get(); etcd.deleteDir("/etcd4j_testGetAll_2").recursive().send().get(); } catch (EtcdException | IOException e) { } this.etcd.close(); } /** * Test version * * @throws Exception */ @Test public void testOldVersion() { String version = etcd.getVersion(); assertNotNull(version); assertTrue(version.contains("etcd")); } /** * Test version * * @throws Exception */ @Test public void testVersion() { EtcdVersionResponse version = etcd.version(); assertNotNull(version); assertTrue(version.server.startsWith("2.") || version.server.startsWith("3.")); assertTrue(version.cluster.startsWith("2.") || version.cluster.startsWith("3.")); } /** * Test Self Stats * * @throws Exception */ @Test public void testSelfStats() { EtcdSelfStatsResponse stats = etcd.getSelfStats(); assertNotNull(stats); assertNotNull(stats.getLeaderInfo()); assertEquals(stats.getId(), stats.getLeaderInfo().getLeader()); } /** * Test leader Stats * * @throws Exception */ @Test public void testLeaderStats() { EtcdLeaderStatsResponse stats = etcd.getLeaderStats(); assertNotNull(stats); // stats assertNotNull(stats.getLeader()); assertNotNull(stats.getFollowers()); assertEquals(stats.getFollowers().size(), 0); } /** * Test Store Stats * * @throws Exception */ @Test public void testStoreStats() { EtcdStoreStatsResponse stats = etcd.getStoreStats(); assertNotNull(stats); } /** * Test Members * * @throws Exception */ @Test public void testMembers() { EtcdMembersResponse members = etcd.getMembers(); assertNotNull(members); assertTrue(members.getMembers().size() >= 1); } /** * Test Health * * @throws Exception */ @Test public void testHealth() { EtcdHealthResponse health = etcd.getHealth(); assertNotNull(health); assertTrue(health.getHealth().equals("true")); } @Test public void testTimeout() throws IOException, EtcdException, EtcdAuthenticationException { try { etcd.put("etcd4j_test/fooTO", "bar").timeout(1, TimeUnit.MILLISECONDS).send().get(); fail(); } catch (TimeoutException e) { // Should time out } } /** * Simple value tests */ @Test public void testKey() throws IOException, EtcdException, EtcdAuthenticationException, TimeoutException { EtcdKeysResponse response = etcd.put("etcd4j_test/foo", "bar").send().get(); assertEquals(EtcdKeyAction.set, response.action); response = etcd.put("etcd4j_test/foo2", "bar").prevExist(false).send().get(); assertEquals(EtcdKeyAction.create, response.action); response = etcd.put("etcd4j_test/foo", "bar1").ttl(40).prevExist(true).send().get(); assertEquals(EtcdKeyAction.update, response.action); assertNotNull(response.node.expiration); response = etcd.put("etcd4j_test/foo", "bar2").prevValue("bar1").send().get(); assertEquals(EtcdKeyAction.compareAndSwap, response.action); response = etcd.put("etcd4j_test/foo", "bar3").prevIndex(response.node.modifiedIndex).send().get(); assertEquals(EtcdKeyAction.compareAndSwap, response.action); response = etcd.get("etcd4j_test/foo").consistent().send().get(); assertEquals("bar3", response.node.value); // Test slash before key response = etcd.get("/etcd4j_test/foo").consistent().send().get(); assertEquals("bar3", response.node.value); response = etcd.delete("etcd4j_test/foo").send().get(); assertEquals(EtcdKeyAction.delete, response.action); } /** * Simple value tests */ @Test public void testError() throws IOException, EtcdAuthenticationException, TimeoutException { try { etcd.get("etcd4j_test/barf").send().get(); } catch (EtcdException e) { assertEquals(100, e.errorCode); } try { etcd.put("etcd4j_test/barf", "huh").prevExist(true).send().get(); } catch (EtcdException e) { assertEquals(100, e.errorCode); } } /** * Refresh test */ @Test public void testRefreshTtl() throws IOException, EtcdException, EtcdAuthenticationException, TimeoutException { EtcdKeysResponse initialResponse = etcd.put("etcd4j_test/foo", "bar").ttl(60).send().get(); assertEquals(EtcdKeyAction.set, initialResponse.action); final EtcdKeysResponse refreshedResponse = etcd.refresh("etcd4j_test/foo", 120).send().get(); assertEquals(initialResponse.node.createdIndex, refreshedResponse.node.createdIndex); assertTrue("expected ttl to be updated", refreshedResponse.node.ttl > 60); } /** * Tests redirect by sending a key with too many slashes. */ @Test public void testRedirect() throws IOException, EtcdException, EtcdAuthenticationException, TimeoutException { etcd.put("etcd4j_test/redirect", "bar").send().get(); // Test redirect with a double slash EtcdKeysResponse response = etcd.get("//etcd4j_test/redirect").consistent().send().get(); assertEquals("bar", response.node.value); } /** * Directory tests */ @Test public void testDir() throws IOException, EtcdException, EtcdAuthenticationException, TimeoutException { EtcdKeysResponse r = etcd.putDir("etcd4j_test/foo_dir").send().get(); assertEquals(r.action, EtcdKeyAction.set); r = etcd.getDir("etcd4j_test/foo_dir").consistent().send().get(); assertEquals(r.action, EtcdKeyAction.get); // Test slash before key r = etcd.getDir("/etcd4j_test/foo_dir").send().get(); assertEquals(r.action, EtcdKeyAction.get); r = etcd.put("etcd4j_test/foo_dir/foo", "bar").send().get(); assertEquals(r.node.value, "bar"); r = etcd.putDir("etcd4j_test/foo_dir/foo_subdir").ttl(20).send().get(); assertEquals(r.action, EtcdKeyAction.set); assertNotNull(r.node.expiration); r = etcd.deleteDir("etcd4j_test/foo_dir").recursive().send().get(); assertEquals(r.action, EtcdKeyAction.delete); } /** * Recursive */ @Test public void testRecursive() throws IOException, EtcdException, EtcdAuthenticationException, TimeoutException { etcd.put("etcd4j_test/nested/root/key-1", "key1").send().get(); etcd.put("etcd4j_test/nested/root/node-1/key-2", "key2").send().get(); etcd.put("etcd4j_test/nested/root/node-1/child/key-3", "key3").send().get(); etcd.put("etcd4j_test/nested/root/node-2/key-4", "key4").send().get(); EtcdKeysResponse r; r = etcd.get("etcd4j_test/nested").recursive().timeout(10, TimeUnit.SECONDS).send().get(); assertEquals(1, r.node.nodes.size()); assertEquals(3, r.node.nodes.get(0).nodes.size()); r = etcd.getDir("etcd4j_test/nested").recursive().timeout(10, TimeUnit.SECONDS).send().get(); assertEquals(1, r.node.nodes.size()); assertEquals(3, r.node.nodes.get(0).nodes.size()); r = etcd.deleteDir("etcd4j_test/nested").recursive().send().get(); assertEquals(r.action, EtcdKeyAction.delete); } /** * In order key tests */ @Test public void testInOrderKeys() throws IOException, EtcdException, EtcdAuthenticationException, TimeoutException { EtcdKeysResponse r = etcd.post("etcd4j_test/queue", "Job1").send().get(); assertEquals(r.action, EtcdKeyAction.create); r = etcd.post("etcd4j_test/queue", "Job2").ttl(20).send().get(); assertEquals(r.action, EtcdKeyAction.create); r = etcd.get(r.node.key).consistent().send().get(); assertTrue(r.node.key.endsWith(r.node.createdIndex+"")); assertEquals(r.node.value, "Job2"); r = etcd.get("etcd4j_test/queue").consistent().recursive().sorted().send().get(); assertEquals(2, r.node.nodes.size()); assertEquals("Job2", r.node.nodes.get(1).value); r = etcd.deleteDir("etcd4j_test/queue").recursive().send().get(); assertEquals(r.action, EtcdKeyAction.delete); } /** * In order key tests */ @Test public void testWait() throws IOException, EtcdException, EtcdAuthenticationException, InterruptedException, TimeoutException { EtcdResponsePromise<EtcdKeysResponse> p = etcd.get("etcd4j_test/test").waitForChange().send(); // Ensure the change is received after the listen command is received. new Timer().schedule(new TimerTask() { @Override public void run() { try { etcd.put("etcd4j_test/test", "changed").send().get(); } catch (IOException | EtcdException | EtcdAuthenticationException | TimeoutException e) { fail(); } } }, 20); EtcdKeysResponse r = p.get(); assertEquals("changed", r.node.value); } @Test(expected = TimeoutException.class) public void testWaitTimeout() throws IOException, EtcdException, EtcdAuthenticationException, InterruptedException, TimeoutException { etcd.get("etcd4j_test/test").waitForChange().timeout(1, TimeUnit.SECONDS).send().get(); // get should have thrown TimeoutException fail(); } @Test(timeout = 1000) public void testChunkedData() throws IOException, EtcdException, EtcdAuthenticationException, TimeoutException { //creating very long key to force content to be chunked StringBuilder stringBuilder = new StringBuilder(15000); for (int i = 0; i < 15000; i++) { stringBuilder.append("a"); } EtcdKeysResponse response = etcd.put("etcd4j_test/foo", stringBuilder.toString()).send().get(); assertEquals(EtcdKeyAction.set, response.action); } @Test public void testIfCleanClose() throws IOException, EtcdException, EtcdAuthenticationException, TimeoutException { EtcdClient client = new EtcdClient(); client.setRetryHandler(new RetryWithExponentialBackOff(20, 4, 1000)); EtcdResponsePromise<EtcdKeysResponse> p = client.get("etcd4j_test/test").waitForChange().send(); client.close(); try { p.get(); fail(); } catch (IOException e){ // should be catched because connection was canceled if (!(e.getCause() instanceof CancellationException)) { fail(); } } } @Test public void testGetAll() throws IOException, EtcdException, EtcdAuthenticationException, TimeoutException { List<EtcdKeysResponse.EtcdNode> nodes; EtcdClient client = new EtcdClient(); nodes = client.getAll().timeout(30, TimeUnit.SECONDS).send().get().getNode().getNodes(); assertNotNull(nodes); assertEquals(0, nodes.size()); client.put("etcd4j_testGetAll_1/foo1", "bar").prevExist(false).send().get(); client.put("etcd4j_testGetAll_2/foo1", "bar").prevExist(false).send().get(); nodes = client.getAll().timeout(30, TimeUnit.SECONDS).send().get().getNode().getNodes(); assertNotNull(nodes); assertEquals(2, nodes.size()); } @Test public void testGetHugeDir() throws IOException, EtcdException, EtcdAuthenticationException, TimeoutException { EtcdNettyConfig config = new EtcdNettyConfig(); config.setMaxFrameSize(1024 * 1024); // Desired max size EtcdNettyClient nettyClient = new EtcdNettyClient(config, URI.create("http://localhost:4001")); EtcdClient client = new EtcdClient(nettyClient); for (int i = 0; i < 2000; i++) { client.put("/etcd4j_test/huge-dir/node-" + i, "bar").send().get(); } List<EtcdKeysResponse.EtcdNode> nodes; nodes = client.getDir("/etcd4j_test/huge-dir/").send().get().getNode().getNodes(); assertNotNull(nodes); assertEquals(2000, nodes.size()); } }