diff --git a/pom.xml b/pom.xml
index 994a57d7..8b56f29b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -203,6 +203,12 @@
1.7.0
test
+
+ com.toomuchcoding.jsonassert
+ jsonassert
+ 0.4.1
+ test
+
diff --git a/spring-cloud-zookeeper-discovery/pom.xml b/spring-cloud-zookeeper-discovery/pom.xml
index ae27ad5c..f9246e72 100644
--- a/spring-cloud-zookeeper-discovery/pom.xml
+++ b/spring-cloud-zookeeper-discovery/pom.xml
@@ -169,6 +169,11 @@
awaitility
test
+
+ com.toomuchcoding.jsonassert
+ jsonassert
+ test
+
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/PollingUtils.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/PollingUtils.groovy
deleted file mode 100644
index 86269bd3..00000000
--- a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/PollingUtils.groovy
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2013-2015 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.cloud.zookeeper.discovery
-
-trait PollingUtils {
- Closure willPass(Closure closure) {
- return {
- try {
- closure()
- } catch (Exception e) {
- AssertionError assertionError = new AssertionError("Exception occurred while evaluating closure", e)
- System.err.println(assertionError)
- throw assertionError
- }
- }
- }
-
-}
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryClientSpec.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryClientSpec.groovy
deleted file mode 100644
index 91925063..00000000
--- a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryClientSpec.groovy
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.springframework.cloud.zookeeper.discovery
-
-import spock.lang.Specification
-
-/**
- * @author Marcin Grzejszczak
- */
-class ZookeeperDiscoveryClientSpec extends Specification {
-
- def "should return an empty list of services if service discovery is null"() {
- given:
- ZookeeperServiceDiscovery serviceDiscovery = Stub()
- ZookeeperDiscoveryClient zookeeperDiscoveryClient = new ZookeeperDiscoveryClient(serviceDiscovery, null)
- when:
- List services = zookeeperDiscoveryClient.getServices()
- then:
- assert services.empty
- }
-}
-
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryClientTests.java b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryClientTests.java
new file mode 100644
index 00000000..0559f668
--- /dev/null
+++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryClientTests.java
@@ -0,0 +1,24 @@
+package org.springframework.cloud.zookeeper.discovery;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.BDDAssertions.then;
+import static org.mockito.Mockito.mock;
+
+/**
+ * @author Marcin Grzejszczak
+ */
+public class ZookeeperDiscoveryClientTests {
+
+ @Test public void should_return_an_empty_list_of_services_if_service_discovery_is_null() {
+ // given:
+ ZookeeperServiceDiscovery serviceDiscovery = mock(ZookeeperServiceDiscovery.class);
+ ZookeeperDiscoveryClient zookeeperDiscoveryClient = new ZookeeperDiscoveryClient(serviceDiscovery, null);
+ // when:
+ List services = zookeeperDiscoveryClient.getServices();
+ // then:
+ then(services).isEmpty();
+ }
+}
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryHealthIndicatorWithNestedStructureISpec.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryHealthIndicatorWithNestedStructureISpec.groovy
deleted file mode 100644
index 1f75b2c9..00000000
--- a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryHealthIndicatorWithNestedStructureISpec.groovy
+++ /dev/null
@@ -1,79 +0,0 @@
-package org.springframework.cloud.zookeeper.discovery
-
-import groovy.json.JsonSlurper
-import org.apache.curator.framework.CuratorFramework
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.beans.factory.annotation.Value
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration
-import org.springframework.boot.test.SpringApplicationContextLoader
-import org.springframework.boot.test.WebIntegrationTest
-import org.springframework.cloud.client.discovery.EnableDiscoveryClient
-import org.springframework.cloud.client.loadbalancer.LoadBalanced
-import org.springframework.cloud.zookeeper.discovery.test.CommonTestConfig
-import org.springframework.cloud.zookeeper.discovery.test.TestRibbonClient
-import org.springframework.context.annotation.Bean
-import org.springframework.context.annotation.Configuration
-import org.springframework.context.annotation.Import
-import org.springframework.context.annotation.Profile
-import org.springframework.test.context.ActiveProfiles
-import org.springframework.test.context.ContextConfiguration
-import org.springframework.web.client.RestTemplate
-import spock.lang.Issue
-import spock.lang.Specification
-
-import javax.annotation.PostConstruct
-import javax.annotation.PreDestroy
-
-@ContextConfiguration(classes = Config, loader = SpringApplicationContextLoader)
-@ActiveProfiles('nestedstructure')
-@WebIntegrationTest(randomPort = true)
-class ZookeeperDiscoveryHealthIndicatorWithNestedStructureISpec extends Specification implements PollingUtils {
-
- @Autowired TestRibbonClient testRibbonClient
-
- @Issue("#54 - ZookeeperDiscoveryHealthIndicator fails on nested structure")
- def 'should return a response that app is in a healthy state when nested folders in zookeeper are present'() {
- when:
- String response = testRibbonClient.callService('me', 'health')
- then:
- twoServicesArePresentedInHealthEndpoint(response)
- }
-
- private boolean twoServicesArePresentedInHealthEndpoint(String response) {
- def services = new JsonSlurper().parseText(response).zookeeperDiscovery.services
- assert ['me', '/a/b/c/d/anotherservice'].every { expectedServiceName ->
- services.any { service ->
- expectedServiceName == service.name
- }
- }
- return true
- }
-
- @Configuration
- @EnableAutoConfiguration
- @EnableDiscoveryClient
- @Import(CommonTestConfig)
- @Profile('nestedstructure')
- static class Config {
-
- @Autowired CuratorFramework curatorFramework
- CustomZookeeperServiceDiscovery customZookeeperServiceDiscovery
-
- @PostConstruct
- void registerNestedDependency() {
- customZookeeperServiceDiscovery = new CustomZookeeperServiceDiscovery("/a/b/c/d/anotherservice",
- '/services', curatorFramework).build()
- }
-
- @PreDestroy
- void unregisterServiceDiscovery() {
- customZookeeperServiceDiscovery?.close()
- }
-
- @Bean
- TestRibbonClient testRibbonClient(@LoadBalanced RestTemplate restTemplate,
- @Value('${spring.application.name}') String springAppName) {
- return new TestRibbonClient(restTemplate, springAppName)
- }
- }
-}
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryHealthIndicatorWithNestedStructureTests.java b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryHealthIndicatorWithNestedStructureTests.java
new file mode 100644
index 00000000..0a9fd2fc
--- /dev/null
+++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryHealthIndicatorWithNestedStructureTests.java
@@ -0,0 +1,88 @@
+package org.springframework.cloud.zookeeper.discovery;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+import com.toomuchcoding.jsonassert.JsonAssertion;
+
+import org.apache.curator.framework.CuratorFramework;
+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.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.boot.test.WebIntegrationTest;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.cloud.zookeeper.discovery.test.CommonTestConfig;
+import org.springframework.cloud.zookeeper.discovery.test.TestRibbonClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.Profile;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.web.client.RestTemplate;
+
+import groovy.json.JsonSlurper;
+import spock.lang.Issue;
+
+import static com.toomuchcoding.jsonassert.JsonAssertion.*;
+
+/**
+ * @author Marcin Grzejszczak
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(classes = ZookeeperDiscoveryHealthIndicatorWithNestedStructureTests.Config.class)
+@ActiveProfiles("nestedstructure")
+@WebIntegrationTest(randomPort = true)
+public class ZookeeperDiscoveryHealthIndicatorWithNestedStructureTests {
+
+ @Autowired TestRibbonClient testRibbonClient;
+
+ // Issue: #54 - ZookeeperDiscoveryHealthIndicator fails on nested structure
+ @Test public void should_return_a_response_that_app_is_in_a_healthy_state_when_nested_folders_in_zookeeper_are_present() {
+ // when:
+ String response = this.testRibbonClient.callService("me", "health");
+ // then:
+ twoServicesArePresentedInHealthEndpoint(response);
+ }
+
+ private boolean twoServicesArePresentedInHealthEndpoint(String response) {
+ assertThat(response).field("zookeeperDiscovery").array("services").field("name").isEqualTo("me");
+ assertThat(response).field("zookeeperDiscovery").array("services").field("name").isEqualTo("/a/b/c/d/anotherservice");
+ return true;
+ }
+
+
+ @Configuration
+ @EnableAutoConfiguration
+ @EnableDiscoveryClient
+ @Import(CommonTestConfig.class)
+ @Profile("nestedstructure")
+ static class Config {
+
+ @Autowired CuratorFramework curatorFramework;
+ CustomZookeeperServiceDiscovery customZookeeperServiceDiscovery;
+
+ @PostConstruct
+ void registerNestedDependency() {
+ this.customZookeeperServiceDiscovery = new CustomZookeeperServiceDiscovery("/a/b/c/d/anotherservice",
+ "/services", this.curatorFramework);
+ this.customZookeeperServiceDiscovery.build();
+ }
+
+ @PreDestroy
+ void unregisterServiceDiscovery() {
+ if (this.customZookeeperServiceDiscovery != null) {
+ this.customZookeeperServiceDiscovery.close();
+ }
+ }
+
+ @Bean TestRibbonClient testRibbonClient(@LoadBalanced RestTemplate restTemplate,
+ @Value("${spring.application.name}") String springAppName) {
+ return new TestRibbonClient(restTemplate, springAppName);
+ }
+ }
+}
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryISpec.groovy b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryISpec.groovy
deleted file mode 100644
index 409eacd3..00000000
--- a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryISpec.groovy
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright 2013-2015 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.cloud.zookeeper.discovery
-
-import groovy.json.JsonSlurper
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.beans.factory.annotation.Value
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration
-import org.springframework.boot.test.SpringApplicationContextLoader
-import org.springframework.boot.test.WebIntegrationTest
-import org.springframework.cloud.client.ServiceInstance
-import org.springframework.cloud.client.discovery.DiscoveryClient
-import org.springframework.cloud.client.discovery.EnableDiscoveryClient
-import org.springframework.cloud.client.loadbalancer.LoadBalanced
-import org.springframework.cloud.netflix.feign.EnableFeignClients
-import org.springframework.cloud.netflix.feign.FeignClient
-import org.springframework.cloud.zookeeper.discovery.test.CommonTestConfig
-import org.springframework.cloud.zookeeper.discovery.test.TestRibbonClient
-import org.springframework.context.annotation.Bean
-import org.springframework.context.annotation.Configuration
-import org.springframework.context.annotation.Import
-import org.springframework.context.annotation.Profile
-import org.springframework.stereotype.Controller
-import org.springframework.test.context.ActiveProfiles
-import org.springframework.test.context.ContextConfiguration
-import org.springframework.web.bind.annotation.RequestMapping
-import org.springframework.web.bind.annotation.RequestMethod
-import org.springframework.web.client.RestTemplate
-import spock.lang.Specification
-import spock.util.concurrent.PollingConditions
-
-@ContextConfiguration(classes = Config, loader = SpringApplicationContextLoader)
-@ActiveProfiles('ribbon')
-@WebIntegrationTest(randomPort = true)
-class ZookeeperDiscoveryISpec extends Specification implements PollingUtils {
-
- @Autowired TestRibbonClient testRibbonClient
- @Autowired DiscoveryClient discoveryClient
- @Autowired ZookeeperServiceDiscovery serviceDiscovery
- @Value('${spring.application.name}') String springAppName
- @Autowired IdUsingFeignClient idUsingFeignClient
- PollingConditions conditions = new PollingConditions()
-
-
- def 'should find the app by its name via Ribbon'() {
- expect:
- 'UP' == registeredServiceStatusViaServiceName()
- }
-
- def 'should find a collaborator via discovery client'() {
- given:
- List instances = discoveryClient.getInstances(springAppName)
- ServiceInstance instance = instances.first()
- expect:
- 'UP' == registeredServiceStatus(instance)
- 'testMetadataValue' == instance.metadata.get('testMetadataKey')
- }
-
- def 'should present application name as id of the service instance'() {
- given:
- ServiceInstance instance = discoveryClient.getLocalServiceInstance()
- expect:
- springAppName == instance.serviceId
- }
-
- def 'should find an instance using feign via service id'() {
- expect:
- conditions.eventually willPass {
- assert idUsingFeignClient.beans
- }
- }
-
- private String registeredServiceStatusViaServiceName() {
- return new JsonSlurper().parseText(testRibbonClient.thisHealthCheck()).status
- }
-
- private String registeredServiceStatus(ServiceInstance instance) {
- return new JsonSlurper().parseText(testRibbonClient.callOnUrl("${instance.host}:${instance.port}", 'health')).status
- }
-
- def 'should properly find local instance'() {
- expect:
- serviceDiscovery.serviceInstance.address == discoveryClient.localServiceInstance.host
- }
-
- @FeignClient("ribbonApp")
- public static interface IdUsingFeignClient {
- @RequestMapping(method = RequestMethod.GET, value = "/beans")
- String getBeans();
- }
-
- @Configuration
- @EnableAutoConfiguration
- @Import(CommonTestConfig)
- @EnableDiscoveryClient
- @EnableFeignClients(clients = [IdUsingFeignClient])
- @Profile('ribbon')
- static class Config {
-
- @Bean
- TestRibbonClient testRibbonClient(@LoadBalanced RestTemplate restTemplate,
- @Value('${spring.application.name}') String springAppName) {
- return new TestRibbonClient(restTemplate, springAppName)
- }
- }
-
- @Controller
- @Profile('ribbon')
- class PingController {
-
- @RequestMapping('/ping') String ping() {
- return 'pong'
- }
- }
-}
diff --git a/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryTests.java b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryTests.java
new file mode 100644
index 00000000..be273acf
--- /dev/null
+++ b/spring-cloud-zookeeper-discovery/src/test/groovy/org/springframework/cloud/zookeeper/discovery/ZookeeperDiscoveryTests.java
@@ -0,0 +1,127 @@
+package org.springframework.cloud.zookeeper.discovery;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import com.jayway.awaitility.Awaitility;
+import com.toomuchcoding.jsonassert.JsonAssertion;
+import com.toomuchcoding.jsonassert.JsonPath;
+
+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.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.boot.test.WebIntegrationTest;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.cloud.netflix.feign.EnableFeignClients;
+import org.springframework.cloud.netflix.feign.FeignClient;
+import org.springframework.cloud.zookeeper.discovery.test.CommonTestConfig;
+import org.springframework.cloud.zookeeper.discovery.test.TestRibbonClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Controller;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.client.RestTemplate;
+
+import static org.assertj.core.api.BDDAssertions.then;
+
+/**
+ * @author Marcin Grzejszczak
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(ZookeeperDiscoveryTests.Config.class)
+@ActiveProfiles("ribbon")
+@WebIntegrationTest(randomPort = true)
+public class ZookeeperDiscoveryTests {
+
+ @Autowired TestRibbonClient testRibbonClient;
+ @Autowired DiscoveryClient discoveryClient;
+ @Autowired ZookeeperServiceDiscovery serviceDiscovery;
+ @Value("${spring.application.name}") String springAppName;
+ @Autowired IdUsingFeignClient idUsingFeignClient;
+
+ @Test public void should_find_the_app_by_its_name_via_Ribbon() {
+ //expect:
+ then(registeredServiceStatusViaServiceName()).isEqualTo("UP");
+ }
+
+ @Test public void should_find_a_collaborator_via_discovery_client() {
+ //given:
+ List instances = this.discoveryClient.getInstances(this.springAppName);
+ ServiceInstance instance = instances.get(0);
+ //expect:
+ then(registeredServiceStatus(instance)).isEqualTo("UP");
+ then(instance.getMetadata().get("testMetadataKey")).isEqualTo("testMetadataValue");
+ }
+
+ @Test public void should_present_application_name_as_id_of_the_service_instance() {
+ //given:
+ ServiceInstance instance = this.discoveryClient.getLocalServiceInstance();
+ //expect:
+ then(this.springAppName).isEqualTo(instance.getServiceId());
+ }
+
+ @Test public void should_find_an_instance_using_feign_via_service_id() {
+ final IdUsingFeignClient idUsingFeignClient = this.idUsingFeignClient;
+ //expect:
+ Awaitility.await().until(new Callable() {
+ @Override public Boolean call() throws Exception {
+ then(idUsingFeignClient.getBeans()).isNotEmpty();
+ return true;
+ }
+ });
+ }
+
+ private String registeredServiceStatusViaServiceName() {
+ return JsonPath.builder(this.testRibbonClient.thisHealthCheck()).field("status").read(String.class);
+ }
+
+ private String registeredServiceStatus(ServiceInstance instance) {
+ return JsonPath.builder(this.testRibbonClient.callOnUrl(instance.getHost()+":"+instance.getPort(), "health")).field("status").read(String.class);
+ }
+
+ @Test public void should_properly_find_local_instance() {
+ //expect:
+ then(this.serviceDiscovery.getServiceInstance().getAddress()).isEqualTo(this.discoveryClient.getLocalServiceInstance().getHost());
+ }
+
+
+ @FeignClient("ribbonApp")
+ public static interface IdUsingFeignClient {
+ @RequestMapping(method = RequestMethod.GET, value = "/beans")
+ String getBeans();
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @Import(CommonTestConfig.class)
+ @EnableDiscoveryClient
+ @EnableFeignClients(clients = { IdUsingFeignClient.class })
+ @Profile("ribbon")
+ static class Config {
+
+ @Bean TestRibbonClient testRibbonClient(@LoadBalanced RestTemplate restTemplate,
+ @Value("${spring.application.name}") String springAppName) {
+ return new TestRibbonClient(restTemplate, springAppName);
+ }
+ }
+
+ @Controller
+ @Profile("ribbon")
+ class PingController {
+
+ @RequestMapping("/ping") String ping() {
+ return "pong";
+ }
+ }
+}