package mousio.etcd4j.responses; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.HttpHeaders; import org.junit.Before; import org.junit.Test; import static mousio.etcd4j.EtcdUtil.convertDate; import static mousio.etcd4j.responses.EtcdResponseDecoders.*; import static org.junit.Assert.*; /** * Examples are taken out of the api.md of etcd project. */ public class EtcdKeysResponseParserTest { private HttpHeaders headers; @Before public void setup() { this.headers = new DefaultHttpHeaders(); this.headers.add(X_ETCD_CLUSTER_ID, "test"); this.headers.add(X_ETCD_INDEX, 208); this.headers.add(X_RAFT_INDEX, 5); this.headers.add(X_RAFT_TERM, 15); } @Test public void testParseSetKey() throws Exception { EtcdKeysResponse action = EtcdKeysResponse.DECODER.decode(headers, Unpooled.copiedBuffer(("{\n" + " \"action\": \"set\",\n" + " \"node\": {\n" + " \"createdIndex\": 2,\n" + " \"key\": \"/message\",\n" + " \"modifiedIndex\": 2,\n" + " \"value\": \"Hello world\"\n" + " }\n" + "}").getBytes())); assertEquals(EtcdKeyAction.set, action.action); assertEquals(2, action.node.createdIndex.intValue()); assertEquals("/message", action.node.key); assertEquals(2, action.node.modifiedIndex.intValue()); assertEquals("Hello world", action.node.value); assertEquals("test", action.etcdClusterId); assertEquals(208, action.etcdIndex.longValue()); assertEquals(5, action.raftIndex.longValue()); assertEquals(15, action.raftTerm.longValue()); } @Test public void testParseGetKey() throws Exception { EtcdKeysResponse action = EtcdKeysResponse.DECODER.decode(headers, Unpooled.copiedBuffer(("{\n" + " \"action\": \"get\",\n" + " \"node\": {\n" + " \"createdIndex\": 2,\n" + " \"key\": \"/message\",\n" + " \"modifiedIndex\": 2,\n" + " \"value\": \"Hello world\"\n" + " }\n" + "}").getBytes())); assertEquals(EtcdKeyAction.get, action.action); assertEquals(2, action.node.createdIndex.intValue()); assertEquals("/message", action.node.key); assertEquals(2, action.node.modifiedIndex.intValue()); assertEquals("Hello world", action.node.value); } @Test public void testParseChangeKey() throws Exception { EtcdKeysResponse action = EtcdKeysResponse.DECODER.decode(headers, Unpooled.copiedBuffer(("{\n" + " \"action\": \"set\",\n" + " \"node\": {\n" + " \"createdIndex\": 3,\n" + " \"key\": \"/message\",\n" + " \"modifiedIndex\": 3,\n" + " \"value\": \"Hello etcd\"\n" + " },\n" + " \"prevNode\": {\n" + " \"createdIndex\": 2,\n" + " \"key\": \"/message\",\n" + " \"value\": \"Hello world\",\n" + " \"modifiedIndex\": 2\n" + " }\n" + "}").getBytes())); assertEquals(EtcdKeyAction.set, action.action); assertEquals(3, action.node.createdIndex.intValue()); assertEquals("/message", action.node.key); assertEquals(3, action.node.modifiedIndex.intValue()); assertEquals("Hello etcd", action.node.value); assertEquals(2, action.prevNode.createdIndex.intValue()); assertEquals("/message", action.prevNode.key); assertEquals(2, action.prevNode.modifiedIndex.intValue()); assertEquals("Hello world", action.prevNode.value); } @Test public void testParseDeleteKey() throws Exception { EtcdKeysResponse action = EtcdKeysResponse.DECODER.decode(headers, Unpooled.copiedBuffer(("{\n" + " \"action\": \"delete\",\n" + " \"node\": {\n" + " \"createdIndex\": 3,\n" + " \"key\": \"/message\",\n" + " \"modifiedIndex\": 4\n" + " },\n" + " \"prevNode\": {\n" + " \"key\": \"/message\",\n" + " \"value\": \"Hello etcd\",\n" + " \"modifiedIndex\": 3,\n" + " \"createdIndex\": 3\n" + " }\n" + "}").getBytes())); assertEquals(EtcdKeyAction.delete, action.action); assertEquals(3, action.node.createdIndex.intValue()); assertEquals("/message", action.node.key); assertEquals(4, action.node.modifiedIndex.intValue()); assertEquals(3, action.prevNode.createdIndex.intValue()); assertEquals("/message", action.prevNode.key); assertEquals(3, action.prevNode.modifiedIndex.intValue()); assertEquals("Hello etcd", action.prevNode.value); } @Test public void testParseSetKeyTtl() throws Exception { EtcdKeysResponse action = EtcdKeysResponse.DECODER.decode(headers, Unpooled.copiedBuffer(("{\n" + " \"action\": \"set\",\n" + " \"node\": {\n" + " \"createdIndex\": 5,\n" + " \"expiration\": \"2013-12-04T12:01:21.874888581-08:00\",\n" + " \"key\": \"/foo\",\n" + " \"modifiedIndex\": 5,\n" + " \"ttl\": 5,\n" + " \"value\": \"bar\"\n" + " }\n" + "}").getBytes())); assertEquals(EtcdKeyAction.set, action.action); assertEquals(5, action.node.createdIndex.intValue()); assertEquals("/foo", action.node.key); assertEquals(5, action.node.modifiedIndex.intValue()); assertEquals("bar", action.node.value); assertEquals(5, action.node.ttl.intValue()); assertEquals(convertDate("2013-12-04T12:01:21.874888581-08:00"), action.node.expiration); } @Test public void testParseTtlExpiredException() throws Exception { EtcdException e = EtcdException.DECODER.decode(headers, Unpooled.copiedBuffer(("{\n" + " \"cause\": \"/foo\",\n" + " \"errorCode\": 100,\n" + " \"index\": 6,\n" + " \"message\": \"Key Not Found\"\n" + "}").getBytes())); assertEquals(100, e.errorCode); assertEquals("/foo", e.etcdCause); assertEquals(6, e.index.intValue()); assertEquals("Key Not Found", e.etcdMessage); } @Test public void testParseUpdateKeyTtl() throws Exception { EtcdKeysResponse action = EtcdKeysResponse.DECODER.decode(headers, Unpooled.copiedBuffer(("{\n" + " \"action\": \"update\",\n" + " \"node\": {\n" + " \"createdIndex\": 5,\n" + " \"key\": \"/foo\",\n" + " \"modifiedIndex\": 6,\n" + " \"value\": \"bar\"\n" + " },\n" + " \"prevNode\": {\n" + " \"createdIndex\": 5,\n" + " \"expiration\": \"2013-12-04T12:01:21.874888581-08:00\",\n" + " \"key\": \"/foo\",\n" + " \"modifiedIndex\": 5,\n" + " \"ttl\": 3,\n" + " \"value\": \"bar\"\n" + " }\n" + "}").getBytes())); assertEquals(EtcdKeyAction.update, action.action); assertEquals(5, action.node.createdIndex.intValue()); assertEquals("/foo", action.node.key); assertEquals(6, action.node.modifiedIndex.intValue()); assertEquals("bar", action.node.value); assertEquals(5, action.prevNode.createdIndex.intValue()); assertEquals("/foo", action.prevNode.key); assertEquals(5, action.prevNode.modifiedIndex.intValue()); assertEquals("bar", action.prevNode.value); assertEquals(3, action.prevNode.ttl.intValue()); assertEquals(convertDate("2013-12-04T12:01:21.874888581-08:00"), action.prevNode.expiration); } @Test public void testParseCreateKey() throws Exception { EtcdKeysResponse action = EtcdKeysResponse.DECODER.decode(headers, Unpooled.copiedBuffer(("{\n" + " \"action\": \"create\",\n" + " \"node\": {\n" + " \"createdIndex\": 6,\n" + " \"key\": \"/queue/6\",\n" + " \"modifiedIndex\": 6,\n" + " \"value\": \"Job1\"\n" + " }\n" + "}").getBytes())); assertEquals(EtcdKeyAction.create, action.action); assertEquals(6, action.node.createdIndex.intValue()); assertEquals("/queue/6", action.node.key); assertEquals(6, action.node.modifiedIndex.intValue()); assertEquals("Job1", action.node.value); } @Test public void testParseGetOrderedKeys() throws Exception { EtcdKeysResponse action = EtcdKeysResponse.DECODER.decode(headers, Unpooled.copiedBuffer(("{\n" + " \"action\": \"get\",\n" + " \"node\": {\n" + " \"createdIndex\": 2,\n" + " \"dir\": true,\n" + " \"key\": \"/queue\",\n" + " \"modifiedIndex\": 2,\n" + " \"nodes\": [\n" + " {\n" + " \"createdIndex\": 2,\n" + " \"key\": \"/queue/2\",\n" + " \"modifiedIndex\": 2,\n" + " \"value\": \"Job1\"\n" + " },\n" + " {\n" + " \"createdIndex\": 3,\n" + " \"key\": \"/queue/3\",\n" + " \"modifiedIndex\": 3,\n" + " \"value\": \"Job2\"\n" + " }\n" + " ]\n" + " }\n" + "}").getBytes())); assertEquals(EtcdKeyAction.get, action.action); assertEquals(2, action.node.createdIndex.intValue()); assertEquals("/queue", action.node.key); assertEquals(2, action.node.modifiedIndex.intValue()); assertTrue(action.node.dir); assertEquals(2, action.node.nodes.size()); assertEquals(2, action.node.nodes.get(0).createdIndex.intValue()); assertEquals(2, action.node.nodes.get(0).modifiedIndex.intValue()); assertEquals("Job1", action.node.nodes.get(0).value); assertEquals("/queue/2", action.node.nodes.get(0).key); assertEquals(3, action.node.nodes.get(1).createdIndex.intValue()); assertEquals(3, action.node.nodes.get(1).modifiedIndex.intValue()); assertEquals("Job2", action.node.nodes.get(1).value); assertEquals("/queue/3", action.node.nodes.get(1).key); } @Test public void testParseExpiredDir() throws Exception { EtcdKeysResponse action = EtcdKeysResponse.DECODER.decode(headers, Unpooled.copiedBuffer(("{\n" + " \"action\": \"expire\",\n" + " \"node\": {\n" + " \"createdIndex\": 8,\n" + " \"key\": \"/dir\",\n" + " \"modifiedIndex\": 15\n" + " },\n" + " \"prevNode\": {\n" + " \"createdIndex\": 8,\n" + " \"key\": \"/dir\",\n" + " \"dir\":true,\n" + " \"modifiedIndex\": 17,\n" + " \"expiration\": \"2013-12-11T10:39:35.689275857-08:00\"\n" + " }\n" + "}").getBytes())); assertEquals(EtcdKeyAction.expire, action.action); assertEquals(8, action.node.createdIndex.intValue()); assertEquals("/dir", action.node.key); assertEquals(15, action.node.modifiedIndex.intValue()); assertEquals(8, action.prevNode.createdIndex.intValue()); assertEquals("/dir", action.prevNode.key); assertEquals(17, action.prevNode.modifiedIndex.intValue()); assertEquals(convertDate("2013-12-11T10:39:35.689275857-08:00"), action.prevNode.expiration); assertTrue(action.prevNode.dir); } @Test public void testParseCompareAndSwap() throws Exception { EtcdKeysResponse action = EtcdKeysResponse.DECODER.decode(headers, Unpooled.copiedBuffer(("{\n" + " \"action\": \"compareAndSwap\",\n" + " \"node\": {\n" + " \"createdIndex\": 8,\n" + " \"key\": \"/foo\",\n" + " \"modifiedIndex\": 9,\n" + " \"value\": \"two\"\n" + " },\n" + " \"prevNode\": {\n" + " \"createdIndex\": 8,\n" + " \"key\": \"/foo\",\n" + " \"modifiedIndex\": 8,\n" + " \"value\": \"one\"\n" + " }\n" + "}").getBytes())); assertEquals(EtcdKeyAction.compareAndSwap, action.action); assertEquals(8, action.node.createdIndex.intValue()); assertEquals("/foo", action.node.key); assertEquals(9, action.node.modifiedIndex.intValue()); assertEquals("two", action.node.value); assertEquals(8, action.prevNode.createdIndex.intValue()); assertEquals("/foo", action.prevNode.key); assertEquals(8, action.prevNode.modifiedIndex.intValue()); assertEquals("one", action.prevNode.value); } @Test public void testParseCompareAndDelete() throws Exception { EtcdKeysResponse action = EtcdKeysResponse.DECODER.decode(headers, Unpooled.copiedBuffer(("{\n" + " \"action\": \"compareAndDelete\",\n" + " \"node\": {\n" + " \"key\": \"/foo\",\n" + " \"modifiedIndex\": 9,\n" + " \"createdIndex\": 8\n" + " },\n" + " \"prevNode\": {\n" + " \"key\": \"/foo\",\n" + " \"value\": \"one\",\n" + " \"modifiedIndex\": 8,\n" + " \"createdIndex\": 8\n" + " }\n" + "}").getBytes())); assertEquals(EtcdKeyAction.compareAndDelete, action.action); assertEquals(8, action.node.createdIndex.intValue()); assertEquals("/foo", action.node.key); assertEquals(9, action.node.modifiedIndex.intValue()); assertEquals(8, action.prevNode.createdIndex.intValue()); assertEquals("/foo", action.prevNode.key); assertEquals(8, action.prevNode.modifiedIndex.intValue()); assertEquals("one", action.prevNode.value); } @Test public void testParseRecursiveGet() throws Exception { EtcdKeysResponse action = EtcdKeysResponse.DECODER.decode(headers, Unpooled.copiedBuffer(("{\n" + " \"action\": \"get\",\n" + " \"node\": {\n" + " \"dir\": true,\n" + " \"key\": \"/\",\n" + " \"nodes\": [\n" + " {\n" + " \"createdIndex\": 2,\n" + " \"dir\": true,\n" + " \"key\": \"/foo_dir\",\n" + " \"modifiedIndex\": 2,\n" + " \"nodes\": [\n" + " {\n" + " \"createdIndex\": 2,\n" + " \"key\": \"/foo_dir/foo\",\n" + " \"modifiedIndex\": 2,\n" + " \"value\": \"bar\"\n" + " }\n" + " ]\n" + " }\n" + " ]\n" + " }\n" + "}").getBytes())); assertEquals(EtcdKeyAction.get, action.action); assertTrue(action.node.dir); assertEquals(1, action.node.nodes.size()); assertEquals(2, action.node.nodes.get(0).createdIndex.intValue()); assertEquals("/foo_dir", action.node.nodes.get(0).key); assertEquals(2, action.node.nodes.get(0).modifiedIndex.intValue()); assertTrue(action.node.nodes.get(0).dir); assertEquals(2, action.node.nodes.get(0).nodes.get(0).createdIndex.intValue()); assertEquals("/foo_dir/foo", action.node.nodes.get(0).nodes.get(0).key); assertEquals(2, action.node.nodes.get(0).nodes.get(0).modifiedIndex.intValue()); assertEquals("bar", action.node.nodes.get(0).nodes.get(0).value); } @Test public void testErrorCode() throws Exception { EtcdException e = EtcdException.DECODER.decode(headers, Unpooled.copiedBuffer(( "{\n" + " \"errorCode\": 105,\n" + " \"message\": \"Key already exists\",\n" + " \"cause\": \"/foo/bar\",\n" + " \"index\": 1024\n" + "}").getBytes())); assertTrue(e.isErrorCode(EtcdErrorCode.NodeExist)); assertNotEquals(e.getErrorCode(), EtcdErrorCode.KeyNotFound); } }