From 152171b135d689df8c341d897fbc3e2a75a7da23 Mon Sep 17 00:00:00 2001
From: Daniel Wu <daniel.wu06@sap.com>
Date: Thu, 19 May 2022 10:48:33 +0800
Subject: [PATCH 1/5] allow key deletion to trigger configuration refresh

---
 .../cloud/consul/config/ConfigWatch.java      | 48 ++++++++-----------
 1 file changed, 20 insertions(+), 28 deletions(-)

diff --git a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java
index d28636dc3..174d143a6 100644
--- a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java
+++ b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java
@@ -157,34 +157,26 @@ public void watchConfigKeyValues() {
 				Response<List<GetValue>> response = this.consul.getKVValues(context, aclToken,
 						new QueryParams(this.properties.getWatch().getWaitTime(), currentIndex));
 
-				// if response.value == null, response was a 404, otherwise it was a
-				// 200, reducing churn if there wasn't anything
-				if (response.getValue() != null && !response.getValue().isEmpty()) {
-					Long newIndex = response.getConsulIndex();
-
-					if (newIndex != null && !newIndex.equals(currentIndex)) {
-						// don't publish the same index again, don't publish the first
-						// time (-1) so index can be primed
-						if (!this.consulIndexes.containsValue(newIndex) && !currentIndex.equals(-1L)) {
-							if (log.isTraceEnabled()) {
-								log.trace("Context " + context + " has new index " + newIndex);
-							}
-							RefreshEventData data = new RefreshEventData(context, currentIndex, newIndex);
-							this.publisher.publishEvent(new RefreshEvent(this, data, data.toString()));
-						}
-						else if (log.isTraceEnabled()) {
-							log.trace("Event for index already published for context " + context);
-						}
-						this.consulIndexes.put(context, newIndex);
-					}
-					else if (log.isTraceEnabled()) {
-						log.trace("Same index for context " + context);
-					}
-				}
-				else if (log.isTraceEnabled()) {
-					log.trace("No value for context " + context);
-				}
-
+				// if response.value == null, response was a 404, a key is deleted
+				 Long newIndex = response.getConsulIndex();
+				 if (newIndex != null && !newIndex.equals(currentIndex)) {
+					  // don't publish the same index again, don't publish the first
+					  // time (-1) so index can be primed
+					  if (!this.consulIndexes.containsValue(newIndex) && !currentIndex.equals(-1L)) {
+						  if (log.isTraceEnabled()) {
+							  log.trace("Context " + context + " has new index " + newIndex);
+						  }
+						  RefreshEventData data = new RefreshEventData(context, currentIndex, newIndex);
+						  this.publisher.publishEvent(new RefreshEvent(this, data, data.toString()));
+					  }
+					  else if (log.isTraceEnabled()) {
+						  log.trace("Event for index already published for context " + context);
+					  }
+					  this.consulIndexes.put(context, newIndex);
+				 }
+				 else if (log.isTraceEnabled()) {
+					  log.trace("Same index for context " + context);
+				 }
 			}
 			catch (Exception e) {
 				// only fail fast on the initial query, otherwise just log the error

From 6f5558e6695f84cda78569c9dd5121b51825d313 Mon Sep 17 00:00:00 2001
From: Daniel Wu <daniel.wu06@sap.com>
Date: Sat, 21 May 2022 12:18:24 +0800
Subject: [PATCH 2/5] allow deletion trigger, and fix some check style

---
 .../ConsulBinderConfigurationTests.java       |  6 +++
 .../binder/test/producer/TestProducer.java    |  3 ++
 .../cloud/consul/config/ConfigWatch.java      | 38 +++++++++----------
 .../consul/test/ConsulTestcontainers.java     |  3 ++
 .../discovery/ConsulHeartbeatTaskTests.java   |  2 +-
 ...nsulAutoServiceRegistrationRetryTests.java |  6 +++
 6 files changed, 38 insertions(+), 20 deletions(-)

diff --git a/spring-cloud-consul-binder/src/test/java/org/springframework/cloud/consul/binder/config/ConsulBinderConfigurationTests.java b/spring-cloud-consul-binder/src/test/java/org/springframework/cloud/consul/binder/config/ConsulBinderConfigurationTests.java
index 3da4e448b..1284ab3f7 100644
--- a/spring-cloud-consul-binder/src/test/java/org/springframework/cloud/consul/binder/config/ConsulBinderConfigurationTests.java
+++ b/spring-cloud-consul-binder/src/test/java/org/springframework/cloud/consul/binder/config/ConsulBinderConfigurationTests.java
@@ -34,6 +34,9 @@
  */
 public class ConsulBinderConfigurationTests {
 
+	/**
+	 * exception
+	 */
 	@Rule
 	public ExpectedException exception = ExpectedException.none();
 
@@ -55,6 +58,9 @@ public void consulDisabledDisablesBinder() {
 
 	interface Events {
 
+		/**
+		 * @return the channel
+		 */
 		// @Output
 		MessageChannel purchases();
 
diff --git a/spring-cloud-consul-binder/src/test/java/org/springframework/cloud/consul/binder/test/producer/TestProducer.java b/spring-cloud-consul-binder/src/test/java/org/springframework/cloud/consul/binder/test/producer/TestProducer.java
index e15a2a9e9..95fdaf4df 100644
--- a/spring-cloud-consul-binder/src/test/java/org/springframework/cloud/consul/binder/test/producer/TestProducer.java
+++ b/spring-cloud-consul-binder/src/test/java/org/springframework/cloud/consul/binder/test/producer/TestProducer.java
@@ -93,6 +93,9 @@ public boolean partitionStrategyInvoked() {
 
 	public static class StubPartitionSelectorStrategy implements PartitionSelectorStrategy {
 
+		/**
+		 * invoked
+		 */
 		public volatile boolean invoked = false;
 
 		@Override
diff --git a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java
index 174d143a6..ee99e9d0f 100644
--- a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java
+++ b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java
@@ -158,25 +158,25 @@ public void watchConfigKeyValues() {
 						new QueryParams(this.properties.getWatch().getWaitTime(), currentIndex));
 
 				// if response.value == null, response was a 404, a key is deleted
-				 Long newIndex = response.getConsulIndex();
-				 if (newIndex != null && !newIndex.equals(currentIndex)) {
-					  // don't publish the same index again, don't publish the first
-					  // time (-1) so index can be primed
-					  if (!this.consulIndexes.containsValue(newIndex) && !currentIndex.equals(-1L)) {
-						  if (log.isTraceEnabled()) {
-							  log.trace("Context " + context + " has new index " + newIndex);
-						  }
-						  RefreshEventData data = new RefreshEventData(context, currentIndex, newIndex);
-						  this.publisher.publishEvent(new RefreshEvent(this, data, data.toString()));
-					  }
-					  else if (log.isTraceEnabled()) {
-						  log.trace("Event for index already published for context " + context);
-					  }
-					  this.consulIndexes.put(context, newIndex);
-				 }
-				 else if (log.isTraceEnabled()) {
-					  log.trace("Same index for context " + context);
-				 }
+				Long newIndex = response.getConsulIndex();
+				if (newIndex != null && !newIndex.equals(currentIndex)) {
+					// don't publish the same index again, don't publish the first
+					// time (-1) so index can be primed
+					if (!this.consulIndexes.containsValue(newIndex) && !currentIndex.equals(-1L)) {
+						if (log.isTraceEnabled()) {
+							log.trace("Context " + context + " has new index " + newIndex);
+						}
+						RefreshEventData data = new RefreshEventData(context, currentIndex, newIndex);
+						this.publisher.publishEvent(new RefreshEvent(this, data, data.toString()));
+					}
+					else if (log.isTraceEnabled()) {
+						log.trace("Event for index already published for context " + context);
+					}
+					this.consulIndexes.put(context, newIndex);
+				}
+				else if (log.isTraceEnabled()) {
+					log.trace("Same index for context " + context);
+				}
 			}
 			catch (Exception e) {
 				// only fail fast on the initial query, otherwise just log the error
diff --git a/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/test/ConsulTestcontainers.java b/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/test/ConsulTestcontainers.java
index e6adada6f..2402387bc 100644
--- a/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/test/ConsulTestcontainers.java
+++ b/spring-cloud-consul-core/src/test/java/org/springframework/cloud/consul/test/ConsulTestcontainers.java
@@ -35,6 +35,9 @@ public class ConsulTestcontainers implements ApplicationContextInitializer<Confi
 
 	static final Logger logger = LoggerFactory.getLogger(ConsulTestcontainers.class);
 
+	/**
+	 * the consul server
+	 */
 	public static GenericContainer<?> consul = new GenericContainer<>("consul:1.7.2")
 			.withLogConsumer(new Slf4jLogConsumer(logger).withSeparateOutputStreams())
 			.waitingFor(Wait.forHttp("/v1/status/leader")).withExposedPorts(8500)
diff --git a/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/discovery/ConsulHeartbeatTaskTests.java b/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/discovery/ConsulHeartbeatTaskTests.java
index bda0b2cf7..deafc3279 100644
--- a/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/discovery/ConsulHeartbeatTaskTests.java
+++ b/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/discovery/ConsulHeartbeatTaskTests.java
@@ -35,7 +35,7 @@
 import static org.mockito.Mockito.verify;
 
 /**
- * Test for ConsulHeartbeatTask
+ * Test for ConsulHeartbeatTask.
  *
  * @author Toshiaki Maki
  */
diff --git a/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/serviceregistry/ConsulAutoServiceRegistrationRetryTests.java b/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/serviceregistry/ConsulAutoServiceRegistrationRetryTests.java
index 55debe762..f000fcf5e 100644
--- a/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/serviceregistry/ConsulAutoServiceRegistrationRetryTests.java
+++ b/spring-cloud-consul-discovery/src/test/java/org/springframework/cloud/consul/serviceregistry/ConsulAutoServiceRegistrationRetryTests.java
@@ -47,9 +47,15 @@
 @ContextConfiguration(initializers = ConsulTestcontainers.class)
 public class ConsulAutoServiceRegistrationRetryTests {
 
+	/**
+	 * the exception
+	 */
 	@Rule
 	public ExpectedException exception = ExpectedException.none();
 
+	/**
+	 * the output
+	 */
 	@Rule
 	public OutputCaptureRule output = new OutputCaptureRule();
 

From 69664c12fa1fb30cf925e29f1a414fd56843c18a Mon Sep 17 00:00:00 2001
From: Daniel Wu <daniel.wu06@sap.com>
Date: Sat, 21 May 2022 12:21:21 +0800
Subject: [PATCH 3/5] fix_ut

---
 .../springframework/cloud/consul/config/ConfigWatchTests.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java b/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java
index 7b337ec36..18d6ab595 100644
--- a/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java
+++ b/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java
@@ -75,12 +75,12 @@ public void watchPublishesEvent() {
 	}
 
 	@Test
-	public void watchWithNullValueDoesNotPublishEvent() {
+	public void watchForDeletedKeyPublishesEvent() {
 		ApplicationEventPublisher eventPublisher = mock(ApplicationEventPublisher.class);
 
 		setupWatch(eventPublisher, null, "/app/");
 
-		verify(eventPublisher, never()).publishEvent(any(RefreshEvent.class));
+		verify(eventPublisher, times(1)).publishEvent(any(RefreshEvent.class));
 	}
 
 	@Test

From 8e3a6ae1caec3c08eb1c9618dd3e924d48166b67 Mon Sep 17 00:00:00 2001
From: Daniel Wu <daniel.wu06@sap.com>
Date: Wed, 8 Jun 2022 15:04:15 +0800
Subject: [PATCH 4/5] fix checkstyle

---
 .../springframework/cloud/consul/config/ConfigWatchTests.java    | 1 -
 1 file changed, 1 deletion(-)

diff --git a/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java b/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java
index 18d6ab595..9dd4ce594 100644
--- a/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java
+++ b/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java
@@ -38,7 +38,6 @@
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;

From 0cdacf2b7f937ceaab4fffeb924e03058f43ecc1 Mon Sep 17 00:00:00 2001
From: Daniel Wu <daniel.wu06@sap.com>
Date: Wed, 8 Jun 2022 19:06:06 +0800
Subject: [PATCH 5/5] fix unit test on slow machines

---
 .../cloud/consul/config/ConfigWatch.java        |  5 ++---
 .../cloud/consul/config/ConfigWatchTests.java   | 17 +++++++++++------
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java
index ee99e9d0f..85309b339 100644
--- a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java
+++ b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConfigWatch.java
@@ -57,7 +57,7 @@ public class ConfigWatch implements ApplicationEventPublisherAware, SmartLifecyc
 
 	private final AtomicBoolean running = new AtomicBoolean(false);
 
-	private LinkedHashMap<String, Long> consulIndexes;
+	private final LinkedHashMap<String, Long> consulIndexes;
 
 	private ApplicationEventPublisher publisher;
 
@@ -150,13 +150,12 @@ public void watchConfigKeyValues() {
 
 				// use the consul ACL token if found
 				String aclToken = this.properties.getAclToken();
-				if (StringUtils.isEmpty(aclToken)) {
+				if (!StringUtils.hasLength(aclToken)) {
 					aclToken = null;
 				}
 
 				Response<List<GetValue>> response = this.consul.getKVValues(context, aclToken,
 						new QueryParams(this.properties.getWatch().getWaitTime(), currentIndex));
-
 				// if response.value == null, response was a 404, a key is deleted
 				Long newIndex = response.getConsulIndex();
 				if (newIndex != null && !newIndex.equals(currentIndex)) {
diff --git a/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java b/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java
index 9dd4ce594..36eb89b1e 100644
--- a/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java
+++ b/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConfigWatchTests.java
@@ -38,7 +38,7 @@
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.springframework.cloud.consul.config.ConsulConfigProperties.Format.FILES;
@@ -51,7 +51,7 @@ public class ConfigWatchTests {
 	private ConsulConfigProperties configProperties;
 
 	@Before
-	public void setUp() throws Exception {
+	public void setUp() {
 		this.configProperties = new ConsulConfigProperties();
 	}
 
@@ -61,6 +61,11 @@ public void watchPublishesEventWithAcl() {
 
 		setupWatch(eventPublisher, new GetValue(), "/app/", "2ee647bd-bd69-4118-9f34-b9a6e9e60746");
 
+		// there are two threads to publish events here in this UT, the unit test main
+		// thread and the thread started by
+		// config.start(). If you set a breakpoint or have a slow machine, you will see
+		// the test cases fail if we
+		// times(1) here. To work around this problem we use atLeastOnce() here.
 		verify(eventPublisher, atLeastOnce()).publishEvent(any(RefreshEvent.class));
 	}
 
@@ -70,7 +75,7 @@ public void watchPublishesEvent() {
 
 		setupWatch(eventPublisher, new GetValue(), "/app/");
 
-		verify(eventPublisher, times(1)).publishEvent(any(RefreshEvent.class));
+		verify(eventPublisher, atLeastOnce()).publishEvent(any(RefreshEvent.class));
 	}
 
 	@Test
@@ -79,7 +84,7 @@ public void watchForDeletedKeyPublishesEvent() {
 
 		setupWatch(eventPublisher, null, "/app/");
 
-		verify(eventPublisher, times(1)).publishEvent(any(RefreshEvent.class));
+		verify(eventPublisher, atLeastOnce()).publishEvent(any(RefreshEvent.class));
 	}
 
 	@Test
@@ -134,11 +139,11 @@ public void firstCallDoesNotPublishEvent() {
 		Response<List<GetValue>> response = new Response<>(getValues, 1L, false, 1L);
 		when(consul.getKVValues(eq(context), anyString(), any(QueryParams.class))).thenReturn(response);
 
-		ConfigWatch watch = new ConfigWatch(this.configProperties, consul, new LinkedHashMap<String, Long>());
+		ConfigWatch watch = new ConfigWatch(this.configProperties, consul, new LinkedHashMap<>());
 		watch.setApplicationEventPublisher(eventPublisher);
 
 		watch.watchConfigKeyValues();
-		verify(eventPublisher, times(0)).publishEvent(any(RefreshEvent.class));
+		verify(eventPublisher, never()).publishEvent(any(RefreshEvent.class));
 	}
 
 }