package com.kostbot.zoodirector.zookeepersync; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.data.Stat; import org.junit.Assert; import org.junit.Test; import java.util.ArrayList; import java.util.List; import java.util.Set; public class ZookeeperSyncTest extends ZookeeperTestBase { private void assertEvent(ZookeeperSync.Event receivedEvent, ZookeeperSync.Event.Type type, String path) { Assert.assertEquals("should be a(n) [" + type + "] event", receivedEvent.type, type); Assert.assertEquals("should be for path [" + path + "]", receivedEvent.path, path); } @Test public void testGetParent() { Assert.assertNull(ZookeeperSync.getParent("/")); Assert.assertEquals("/", ZookeeperSync.getParent("/c")); Assert.assertEquals("/p", ZookeeperSync.getParent("/p/c")); Assert.assertEquals("/g/p", ZookeeperSync.getParent("/g/p/c")); } @Test public void testIsValidPath() { String[] validPaths = { "/", "/valid", "/valid/path", }; for (String validPath : validPaths) { Assert.assertTrue(validPath + " should be valid", ZookeeperSync.isValidPath(validPath, false)); Assert.assertTrue(validPath + " should be valid", ZookeeperSync.isValidPath(validPath)); } String[] inValidPaths = { "", "valid/", "/valid/path/", }; for (String validPath : inValidPaths) { Assert.assertFalse(validPath + " should not be valid", ZookeeperSync.isValidPath(validPath, false)); Assert.assertFalse(validPath + " should not be valid", ZookeeperSync.isValidPath(validPath)); } } @Test public void testIsValidSubPath() { String[] validPaths = { "/", "/valid", "valid", "/valid/path", "valid/path", }; for (String validPath : validPaths) { Assert.assertTrue(validPath + " should be valid", ZookeeperSync.isValidPath(validPath, true)); Assert.assertTrue(validPath + " should be valid", ZookeeperSync.isValidSubPath(validPath)); } String[] inValidPaths = { "", "valid/", "/valid/path/", }; for (String validPath : inValidPaths) { Assert.assertFalse(validPath + " should not be valid", ZookeeperSync.isValidPath(validPath, true)); Assert.assertFalse(validPath + " should not be valid", ZookeeperSync.isValidSubPath(validPath)); } } @Test public void testCreatePersistent() throws Exception { ZookeeperSync zookeeperSync = new ZookeeperSync(client); String path = "/test/all/parent/paths/are/created"; Assert.assertTrue("node should be created", zookeeperSync.create(path)); Stat stat = client.checkExists().forPath(path); Assert.assertNotNull("all parent paths should be created", stat); Assert.assertEquals("node should not have ephemeral owner", 0, stat.getEphemeralOwner()); Assert.assertFalse("node should not be created", zookeeperSync.create(path)); } @Test public void testCreatePersistentSequential() throws Exception { ZookeeperSync zookeeperSync = new ZookeeperSync(client); String path = "/test/all/parent/paths/are/created"; Assert.assertTrue(zookeeperSync.create(path, CreateMode.PERSISTENT_SEQUENTIAL)); Assert.assertTrue(zookeeperSync.create(path, CreateMode.PERSISTENT_SEQUENTIAL)); Assert.assertNull("base path should not be created", client.checkExists().forPath(path)); String[] createdPaths = { path + "0000000000", path + "0000000001", }; for (String createdPath : createdPaths) { Stat stat = client.checkExists().forPath(createdPath); Assert.assertNotNull("all parent paths should be created", stat); Assert.assertEquals("node should not have ephemeral owner", 0, stat.getEphemeralOwner()); } } @Test public void testCreateEphemeral() throws Exception { ZookeeperSync zookeeperSync = new ZookeeperSync(client); String path = "/test/all/parent/paths/are/created"; Assert.assertTrue("node should be created", zookeeperSync.create(path, CreateMode.EPHEMERAL)); Stat stat = client.checkExists().forPath(path); Assert.assertNotNull("all parent paths should be created", stat); Assert.assertNotEquals("node should have ephemeral owner", 0, stat.getEphemeralOwner()); Assert.assertFalse("node should not be created", zookeeperSync.create(path)); } @Test public void testCreateEphemeralSequential() throws Exception { ZookeeperSync zookeeperSync = new ZookeeperSync(client); String path = "/test/all/parent/paths/are/created"; Assert.assertTrue(zookeeperSync.create(path, CreateMode.EPHEMERAL_SEQUENTIAL)); Assert.assertTrue(zookeeperSync.create(path, CreateMode.EPHEMERAL_SEQUENTIAL)); Assert.assertNull("base path should not be created", client.checkExists().forPath(path)); String[] createdPaths = { path + "0000000000", path + "0000000001", }; for (String createdPath : createdPaths) { Stat stat = client.checkExists().forPath(createdPath); Assert.assertNotNull("all parent paths should be created", stat); Assert.assertNotEquals("node should not have ephemeral owner", 0, stat.getEphemeralOwner()); } } @Test public void testSetData() throws Exception { ZookeeperSync zookeeperSync = new ZookeeperSync(client); String path = "/test/all/parent/paths/are/created"; String data = "data!"; String data2 = "data2!"; zookeeperSync.create(path); zookeeperSync.setData(path, 0, data.getBytes()); Assert.assertEquals(data, new String(client.getData().forPath(path))); zookeeperSync.setData(path, 1, data2.getBytes()); Assert.assertEquals(data2, new String(client.getData().forPath(path))); } @Test public void testGetData() throws Exception { ZookeeperSync zookeeperSync = new ZookeeperSync(client); String path = "/test/all/parent/paths/are/created"; String data = "data!"; zookeeperSync.create(path); zookeeperSync.setData(path, 0, data.getBytes()); Assert.assertEquals(data, new String(zookeeperSync.getData(path))); } @Test public void testGetStat() throws Exception { ZookeeperSync zookeeperSync = new ZookeeperSync(client); String path = "/test"; zookeeperSync.create(path); Assert.assertNotNull(zookeeperSync.getStat(path)); } @Test public void testDelete() throws Exception { ZookeeperSync zookeeperSync = new ZookeeperSync(client); String path = "/test"; zookeeperSync.create(path + "/all/parent/paths/are/created"); zookeeperSync.delete(path); Assert.assertNull("all children should be deleted", client.checkExists().forPath(path)); } @Test public void testTrim() throws Exception { ZookeeperSync zookeeperSync = new ZookeeperSync(client); String path = "/test"; zookeeperSync.create(path + "/1"); zookeeperSync.create(path + "/2"); zookeeperSync.create(path + "/3"); zookeeperSync.create(path + "/4"); zookeeperSync.create(path + "/5"); Assert.assertEquals("children should exist", 5, client.getChildren().forPath(path).size()); zookeeperSync.trim(path); Assert.assertEquals("children should be deleted", 0, client.getChildren().forPath(path).size()); } @Test public void testPrune() throws Exception { ZookeeperSync zookeeperSync = new ZookeeperSync(client); String base = "/base"; String path = base + "/test"; zookeeperSync.create(base + "/1"); zookeeperSync.create(path + "/1/2/3"); Assert.assertEquals(base, zookeeperSync.prune(path)); Assert.assertNotNull(client.checkExists().forPath(base)); Assert.assertNull(client.checkExists().forPath(path)); } @Test public void testAddEventsOnLoad() throws Exception { ZookeeperSync zookeeperSync = new ZookeeperSync(client); final List<ZookeeperSync.Event> receivedEventList = new ArrayList<ZookeeperSync.Event>(3); // Register lister before watch to receive loading add events. zookeeperSync.addListener(new ZookeeperSync.Listener() { @Override public void process(ZookeeperSync.Event e) { receivedEventList.add(e); } }); zookeeperSync.watch(); // Wait a little while to ensure we receive events. ConditionRetry.checkCondition(new ConditionRetry.Condition() { @Override public boolean check() { return receivedEventList.size() == 3; } }); // Includes the add events for /, /zookeeper, /zookeeper/quota Assert.assertEquals("listener should receive events", 3, receivedEventList.size()); assertEvent(receivedEventList.get(0), ZookeeperSync.Event.Type.add, "/"); assertEvent(receivedEventList.get(1), ZookeeperSync.Event.Type.add, "/zookeeper"); assertEvent(receivedEventList.get(2), ZookeeperSync.Event.Type.add, "/zookeeper/quota"); Set<String> nodes = zookeeperSync.getNodes(); Assert.assertEquals("sync should contain initial nodes", 3, nodes.size()); Assert.assertTrue(nodes.contains("/")); Assert.assertTrue(nodes.contains("/zookeeper")); Assert.assertTrue(nodes.contains("/zookeeper/quota")); } @Test public void testAddEvent() throws Exception { ZookeeperSync zookeeperSync = new ZookeeperSync(client); final List<ZookeeperSync.Event> receivedEventList = new ArrayList<ZookeeperSync.Event>(3); zookeeperSync.watch(); zookeeperSync.addListener(new ZookeeperSync.Listener() { @Override public void process(ZookeeperSync.Event e) { receivedEventList.add(e); } }); zookeeperSync.create("/test/all/parent/events/received"); // Need to ensure nodes are created before setting a listener. ConditionRetry.checkCondition(new ConditionRetry.Condition() { @Override public boolean check() { return receivedEventList.size() == 5; } }); Assert.assertEquals("listener should receive events", 5, receivedEventList.size()); assertEvent(receivedEventList.get(0), ZookeeperSync.Event.Type.add, "/test"); assertEvent(receivedEventList.get(1), ZookeeperSync.Event.Type.add, "/test/all"); assertEvent(receivedEventList.get(2), ZookeeperSync.Event.Type.add, "/test/all/parent"); assertEvent(receivedEventList.get(3), ZookeeperSync.Event.Type.add, "/test/all/parent/events"); assertEvent(receivedEventList.get(4), ZookeeperSync.Event.Type.add, "/test/all/parent/events/received"); Set<String> nodes = zookeeperSync.getNodes(); Assert.assertEquals("sync should contain initial nodes and all added nodes", 8, nodes.size()); Assert.assertTrue(nodes.contains("/test")); Assert.assertTrue(nodes.contains("/test/all")); Assert.assertTrue(nodes.contains("/test/all/parent")); Assert.assertTrue(nodes.contains("/test/all/parent/events")); Assert.assertTrue(nodes.contains("/test/all/parent/events/received")); } @Test public void testUpdateEvent() throws Exception { ZookeeperSync zookeeperSync = new ZookeeperSync(client); final List<ZookeeperSync.Event> receivedEventList = new ArrayList<ZookeeperSync.Event>(3); zookeeperSync.watch(); zookeeperSync.addListener(new ZookeeperSync.Listener() { @Override public void process(ZookeeperSync.Event e) { receivedEventList.add(e); } }); zookeeperSync.create("/test/all/parent/events/received"); // Need to ensure nodes are created before setting a listener. ConditionRetry.checkCondition(new ConditionRetry.Condition() { @Override public boolean check() { return receivedEventList.size() == 5; } }); // Reset received event list. receivedEventList.clear(); // TODO there may be a problem with extremely fast same node update notifications being lost zookeeperSync.setData("/test/all/parent/events", 0, "updated".getBytes()); zookeeperSync.setData("/test/all/parent/events", 1, "updated again!".getBytes()); // Wait a little while to ensure we receive events. ConditionRetry.checkCondition(new ConditionRetry.Condition() { @Override public boolean check() { return receivedEventList.size() == 2; } }); Assert.assertEquals("listener should receive events", 2, receivedEventList.size()); assertEvent(receivedEventList.get(0), ZookeeperSync.Event.Type.update, "/test/all/parent/events"); assertEvent(receivedEventList.get(1), ZookeeperSync.Event.Type.update, "/test/all/parent/events"); } @Test public void testDeleteEvent() throws Exception { ZookeeperSync zookeeperSync = new ZookeeperSync(client); final List<ZookeeperSync.Event> receivedEventList = new ArrayList<ZookeeperSync.Event>(3); zookeeperSync.watch(); zookeeperSync.addListener(new ZookeeperSync.Listener() { @Override public void process(ZookeeperSync.Event e) { receivedEventList.add(e); } }); zookeeperSync.create("/test/all/parent/events/received"); // Need to ensure nodes are created before setting a listener. ConditionRetry.checkCondition(new ConditionRetry.Condition() { @Override public boolean check() { return receivedEventList.size() == 5; } }); // Reset received event list. receivedEventList.clear(); zookeeperSync.delete("/test"); // Wait a little while to ensure we receive events. ConditionRetry.checkCondition(new ConditionRetry.Condition() { @Override public boolean check() { return receivedEventList.size() == 5; } }); Assert.assertEquals("listener should receive events", 5, receivedEventList.size()); assertEvent(receivedEventList.get(0), ZookeeperSync.Event.Type.delete, "/test/all/parent/events/received"); assertEvent(receivedEventList.get(1), ZookeeperSync.Event.Type.delete, "/test/all/parent/events"); assertEvent(receivedEventList.get(2), ZookeeperSync.Event.Type.delete, "/test/all/parent"); assertEvent(receivedEventList.get(3), ZookeeperSync.Event.Type.delete, "/test/all"); assertEvent(receivedEventList.get(4), ZookeeperSync.Event.Type.delete, "/test"); Assert.assertEquals("sync should only contain initial nodes", 3, zookeeperSync.getNodes().size()); } }