This is an automated email from the ASF dual-hosted git repository.

jianbin pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/incubator-seata.git


The following commit(s) were added to refs/heads/2.x by this push:
     new b056604771 optimize: Add empty push protection for Configuration 
(#7576)
b056604771 is described below

commit b0566047717a485ba0b62e16d8724d50ffe77e6c
Author: xiaoyu <93440108+yvce...@users.noreply.github.com>
AuthorDate: Fri Aug 15 09:17:06 2025 +0800

    optimize: Add empty push protection for Configuration (#7576)
---
 changes/en-us/2.x.md                               |   5 +-
 changes/zh-cn/2.x.md                               |   1 +
 .../seata/common/util/Http5ClientUtilTest.java     |   5 +-
 .../seata/config/consul/ConsulConfiguration.java   |   4 +
 .../config/consul/ConsulConfigurationTest.java     |  27 ++++
 .../seata/config/processor/ProcessorYamlTest.java  |  67 +++++++++
 .../seata/config/etcd3/EtcdConfiguration.java      |   4 +
 .../seata/config/nacos/NacosConfiguration.java     |  17 ++-
 .../seata/config/nacos/NacosConfigurationTest.java | 163 +++++++++++++++++++++
 .../seata/config/zk/ZookeeperConfiguration.java    |  17 ++-
 .../seata/config/zk/ZkConfigurationTest.java       |  51 +++++++
 11 files changed, 343 insertions(+), 18 deletions(-)

diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md
index 4cfd3314b4..4033b5b6b0 100644
--- a/changes/en-us/2.x.md
+++ b/changes/en-us/2.x.md
@@ -40,8 +40,9 @@ Add changes here for all PR submitted to the 2.x branch.
 
 - [[#7478](https://github.com/apache/incubator-seata/pull/7484)] optimize: 
remove client id metric
 - [[#7557](https://github.com/seata/seata/pull/7557)] upgrade some npmjs 
dependencies
-- [[#7577](https://github.com/seata/seata/pull/7577)] remove the 4MB size 
limit when decompressing with zstd
-- [[#7578](https://github.com/seata/seata/pull/7578)] zstd decompression is 
changed from jni to ZstdInputStream
+- [[#7576](https://github.com/seata/seata/pull/7576)] Add empty push 
protection for Configuration
+- [[#7577](https://github.com/seata/seata/pull/7577)]  remove the 4MB size 
limit when decompressing with zstd
+- [[#7578](https://github.com/seata/seata/pull/7578)]  zstd decompression is 
changed from jni to ZstdInputStream
 
 ### security:
 
diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md
index c6e1db12f6..9515ca0a4a 100644
--- a/changes/zh-cn/2.x.md
+++ b/changes/zh-cn/2.x.md
@@ -39,6 +39,7 @@
 
 - [[#7478](https://github.com/apache/incubator-seata/pull/7484)] 删除client id指标
 - [[#7557](https://github.com/seata/seata/pull/7557)] 升级 npmjs 依赖
+- [[#7576](https://github.com/seata/seata/pull/7576)] 针对配置变更增加空推保护
 - [[#7577](https://github.com/seata/seata/pull/7577)]  去除zstd解压时4MB的限制
 
 ### security:
diff --git 
a/common/src/test/java/org/apache/seata/common/util/Http5ClientUtilTest.java 
b/common/src/test/java/org/apache/seata/common/util/Http5ClientUtilTest.java
index 058ea439e0..9b550067e7 100644
--- a/common/src/test/java/org/apache/seata/common/util/Http5ClientUtilTest.java
+++ b/common/src/test/java/org/apache/seata/common/util/Http5ClientUtilTest.java
@@ -26,7 +26,10 @@ import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 
 class Http5ClientUtilTest {
 
diff --git 
a/config/seata-config-consul/src/main/java/org/apache/seata/config/consul/ConsulConfiguration.java
 
b/config/seata-config-consul/src/main/java/org/apache/seata/config/consul/ConsulConfiguration.java
index 86ef1de6c2..f7d8d4be2e 100644
--- 
a/config/seata-config-consul/src/main/java/org/apache/seata/config/consul/ConsulConfiguration.java
+++ 
b/config/seata-config-consul/src/main/java/org/apache/seata/config/consul/ConsulConfiguration.java
@@ -351,6 +351,10 @@ public class ConsulConfiguration extends 
AbstractConfiguration {
                     String value = response.getValue().getDecodedValue();
                     consulIndex = currentIndex;
                     if (dataId.equals(getConsulConfigKey())) {
+                        if (StringUtils.isBlank(value)) {
+                            LOGGER.warn("Empty config from Consul, 
dataId='{}'. Skipped.", dataId);
+                            continue;
+                        }
                         // The new config change listener
                         Properties seataConfigNew;
                         try {
diff --git 
a/config/seata-config-consul/src/test/java/org/apache/seata/config/consul/ConsulConfigurationTest.java
 
b/config/seata-config-consul/src/test/java/org/apache/seata/config/consul/ConsulConfigurationTest.java
index 652a437f59..6dc8e84933 100644
--- 
a/config/seata-config-consul/src/test/java/org/apache/seata/config/consul/ConsulConfigurationTest.java
+++ 
b/config/seata-config-consul/src/test/java/org/apache/seata/config/consul/ConsulConfigurationTest.java
@@ -17,11 +17,13 @@
 package org.apache.seata.config.consul;
 
 import com.ecwid.consul.v1.ConsulClient;
+import com.ecwid.consul.v1.QueryParams;
 import com.ecwid.consul.v1.Response;
 import com.ecwid.consul.v1.kv.model.GetValue;
 import com.ecwid.consul.v1.kv.model.PutParams;
 import org.apache.seata.common.util.NetUtil;
 import org.apache.seata.config.Configuration;
+import org.apache.seata.config.ConfigurationChangeEvent;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -131,6 +133,31 @@ class ConsulConfigurationTest {
         assertEquals("val1", value, "KV should be visible after a short 
await");
     }
 
+    @Test
+    void testOnChangeEvent_skipWhenValueIsBlank() throws InterruptedException {
+
+        String dataId = "seata.properties";
+        ConsulConfiguration.ConsulListener listener = new 
ConsulConfiguration.ConsulListener(dataId, null);
+
+        GetValue blankValue = mock(GetValue.class);
+        when(blankValue.getDecodedValue()).thenReturn("");
+
+        Response<GetValue> blankResponse = new Response<>(blankValue, 2L, 
false, 2L);
+        when(mockConsulClient.getKVValue(eq(dataId), isNull(), 
any(QueryParams.class)))
+                .thenReturn(blankResponse);
+
+        // Run onChangeEvent in a separate thread since it loops indefinitely
+        Thread thread = new Thread(() -> listener.onChangeEvent(new 
ConfigurationChangeEvent()));
+        thread.start();
+        Thread.sleep(100);
+        // Interrupt to break the loop
+        thread.interrupt();
+        thread.join(500);
+
+        // Assert: Test passes as long as no exceptions are thrown
+        assertTrue(true);
+    }
+
     // Utility method to set private fields via reflection
     private void setField(Object target, String fieldName, Object value) {
         try {
diff --git 
a/config/seata-config-core/src/test/java/org/apache/seata/config/processor/ProcessorYamlTest.java
 
b/config/seata-config-core/src/test/java/org/apache/seata/config/processor/ProcessorYamlTest.java
new file mode 100644
index 0000000000..cb63334a18
--- /dev/null
+++ 
b/config/seata-config-core/src/test/java/org/apache/seata/config/processor/ProcessorYamlTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.seata.config.processor;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Properties;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ProcessorYamlTest {
+
+    @Test
+    void testProcessor_NormalYaml() {
+        String yamlConfig = "server:\n" + "  port: 8080\n"
+                + "  host: localhost\n"
+                + "spring:\n"
+                + "  datasource:\n"
+                + "    url: jdbc:mysql://localhost:3306/test\n"
+                + "    username: root";
+
+        ProcessorYaml processorYaml = new ProcessorYaml();
+        Properties props = processorYaml.processor(yamlConfig);
+
+        assertEquals(8080, props.get("server.port"));
+        assertEquals("", props.getProperty("server.port", ""));
+        assertEquals("localhost", props.getProperty("server.host"));
+        assertEquals("jdbc:mysql://localhost:3306/test", 
props.getProperty("spring.datasource.url"));
+        assertEquals("root", props.getProperty("spring.datasource.username"));
+    }
+
+    @Test
+    void testProcessor_InvalidYaml_ShouldThrowException() {
+
+        String invalidYaml = "server:\n" + "  port: 8080\n" + "::host 
localhost";
+
+        ProcessorYaml processorYaml = new ProcessorYaml();
+
+        Assertions.assertThrows(Exception.class, () -> {
+            processorYaml.processor(invalidYaml);
+        });
+    }
+
+    @Test
+    void testProcessor_EmptyYaml() {
+        String emptyYaml = "";
+        ProcessorYaml processorYaml = new ProcessorYaml();
+        Properties props = processorYaml.processor(emptyYaml);
+        assertTrue(props.size() == 1);
+    }
+}
diff --git 
a/config/seata-config-etcd3/src/main/java/org/apache/seata/config/etcd3/EtcdConfiguration.java
 
b/config/seata-config-etcd3/src/main/java/org/apache/seata/config/etcd3/EtcdConfiguration.java
index d911084e06..b4a9631674 100644
--- 
a/config/seata-config-etcd3/src/main/java/org/apache/seata/config/etcd3/EtcdConfiguration.java
+++ 
b/config/seata-config-etcd3/src/main/java/org/apache/seata/config/etcd3/EtcdConfiguration.java
@@ -400,6 +400,10 @@ public class EtcdConfiguration extends 
AbstractConfiguration {
                                 .getValue()
                                 .getBytes();
                         Properties seataConfigNew;
+                        if (bytes == null || bytes.length == 0) {
+                            LOGGER.warn("config '{}' value is empty from 
watchResponse", dataId);
+                            return;
+                        }
                         try {
                             seataConfigNew = ConfigProcessor.processConfig(
                                     new String(bytes, StandardCharsets.UTF_8), 
getEtcdDataType());
diff --git 
a/config/seata-config-nacos/src/main/java/org/apache/seata/config/nacos/NacosConfiguration.java
 
b/config/seata-config-nacos/src/main/java/org/apache/seata/config/nacos/NacosConfiguration.java
index 82eaf348f8..81b76f429b 100644
--- 
a/config/seata-config-nacos/src/main/java/org/apache/seata/config/nacos/NacosConfiguration.java
+++ 
b/config/seata-config-nacos/src/main/java/org/apache/seata/config/nacos/NacosConfiguration.java
@@ -442,14 +442,17 @@ public class NacosConfiguration extends 
AbstractConfiguration implements Dispose
         public void innerReceive(String dataId, String group, String 
configInfo) {
             // The new configuration method to puts all configurations into a 
dateId
             if (getNacosDataId().equals(dataId)) {
+                if (StringUtils.isBlank(configInfo)) {
+                    LOGGER.warn("Empty config from Nacos, dataId='{}'. 
Skipped.", dataId);
+                    return;
+                }
                 Properties seataConfigNew = new Properties();
-                if (StringUtils.isNotBlank(configInfo)) {
-                    try {
-                        seataConfigNew = 
ConfigProcessor.processConfig(configInfo, getNacosDataType());
-                    } catch (IOException e) {
-                        LOGGER.error("load config properties error", e);
-                        return;
-                    }
+
+                try {
+                    seataConfigNew = ConfigProcessor.processConfig(configInfo, 
getNacosDataType());
+                } catch (IOException e) {
+                    LOGGER.error("load config properties error", e);
+                    return;
                 }
 
                 // Get all the monitored dataids and judge whether it has been 
modified
diff --git 
a/config/seata-config-nacos/src/test/java/org/apache/seata/config/nacos/NacosConfigurationTest.java
 
b/config/seata-config-nacos/src/test/java/org/apache/seata/config/nacos/NacosConfigurationTest.java
index 7346ac5b49..470cce840d 100644
--- 
a/config/seata-config-nacos/src/test/java/org/apache/seata/config/nacos/NacosConfigurationTest.java
+++ 
b/config/seata-config-nacos/src/test/java/org/apache/seata/config/nacos/NacosConfigurationTest.java
@@ -19,14 +19,28 @@ package org.apache.seata.config.nacos;
 import com.alibaba.nacos.api.exception.NacosException;
 import org.apache.seata.common.util.ReflectionUtil;
 import org.apache.seata.config.Configuration;
+import org.apache.seata.config.ConfigurationChangeEvent;
+import org.apache.seata.config.ConfigurationChangeListener;
 import org.apache.seata.config.ConfigurationFactory;
 import org.apache.seata.config.Dispose;
+import org.apache.seata.config.processor.ConfigProcessor;
+import org.jetbrains.annotations.NotNull;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
 
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static org.mockito.ArgumentMatchers.anyString;
 
 /**
  * The type Nacos configuration test
@@ -58,4 +72,153 @@ public class NacosConfigurationTest {
         Assertions.assertEquals("/foo", properties.getProperty("contextPath"));
         System.clearProperty("contextPath");
     }
+
+    @Test
+    public void testInnerReceiveEmptyPushShouldNotUpdateConfig() throws 
Exception {
+
+        String dataId = "seata.properties";
+        String group = "SEATA_GROUP";
+        String configKey = "session.mode";
+
+        Properties oldConfig = new Properties();
+        oldConfig.setProperty(configKey, "db");
+
+        Field seataConfigField = 
NacosConfiguration.class.getDeclaredField("seataConfig");
+        seataConfigField.setAccessible(true);
+        seataConfigField.set(null, oldConfig);
+
+        TestListener listener = new TestListener();
+        NacosConfiguration.NacosListener nacosListener = 
getNacosListener(dataId, listener);
+
+        ConcurrentMap<ConfigurationChangeListener, 
NacosConfiguration.NacosListener> innerMap =
+                new ConcurrentHashMap<>();
+        innerMap.put(listener, nacosListener);
+
+        ConcurrentMap<String, ConcurrentMap<ConfigurationChangeListener, 
NacosConfiguration.NacosListener>> outerMap =
+                new ConcurrentHashMap<>();
+        outerMap.put(dataId, innerMap);
+
+        Field listenerMapField = 
NacosConfiguration.class.getDeclaredField("CONFIG_LISTENERS_MAP");
+        listenerMapField.setAccessible(true);
+        listenerMapField.set(null, outerMap);
+
+        // execute
+        nacosListener.innerReceive(dataId, group, "");
+
+        Properties actualConfig = (Properties) seataConfigField.get(null);
+        Assertions.assertEquals("db", actualConfig.getProperty(configKey));
+
+        Assertions.assertFalse(listener.invoked);
+    }
+
+    @Test
+    public void testInnerReceiveShouldReturn() throws Exception {
+
+        String dataId = "seata.properties";
+        String group = "SEATA_GROUP";
+        String configKey = "session.mode";
+
+        Properties oldConfig = new Properties();
+        oldConfig.setProperty(configKey, "db");
+
+        Field seataConfigField = 
NacosConfiguration.class.getDeclaredField("seataConfig");
+        seataConfigField.setAccessible(true);
+        seataConfigField.set(null, oldConfig);
+
+        TestListener listener = new TestListener();
+        NacosConfiguration.NacosListener nacosListener = 
getNacosListener(dataId, listener);
+
+        ConcurrentMap<ConfigurationChangeListener, 
NacosConfiguration.NacosListener> innerMap =
+                new ConcurrentHashMap<>();
+        innerMap.put(listener, nacosListener);
+
+        ConcurrentMap<String, ConcurrentMap<ConfigurationChangeListener, 
NacosConfiguration.NacosListener>> outerMap =
+                new ConcurrentHashMap<>();
+        outerMap.put(dataId, innerMap);
+
+        Field listenerMapField = 
NacosConfiguration.class.getDeclaredField("CONFIG_LISTENERS_MAP");
+        listenerMapField.setAccessible(true);
+        listenerMapField.set(null, outerMap);
+
+        // execute
+        nacosListener.innerReceive(dataId, group, "session.mode=redis");
+
+        Properties actualConfig = (Properties) seataConfigField.get(null);
+        Assertions.assertEquals("redis", actualConfig.getProperty(configKey));
+
+        Assertions.assertFalse(listener.invoked);
+    }
+
+    @Test
+    public void testInnerReceiveThrowException() throws Exception {
+
+        String dataId = "seata.properties";
+        String group = "SEATA_GROUP";
+        String configKey = "session.mode";
+
+        Properties oldConfig = new Properties();
+        oldConfig.setProperty(configKey, "db");
+
+        Field seataConfigField = 
NacosConfiguration.class.getDeclaredField("seataConfig");
+        seataConfigField.setAccessible(true);
+        seataConfigField.set(null, oldConfig);
+
+        TestListener listener = new TestListener();
+        NacosConfiguration.NacosListener nacosListener = 
getNacosListener(dataId, listener);
+
+        ConcurrentMap<ConfigurationChangeListener, 
NacosConfiguration.NacosListener> innerMap =
+                new ConcurrentHashMap<>();
+        innerMap.put(listener, nacosListener);
+
+        ConcurrentMap<String, ConcurrentMap<ConfigurationChangeListener, 
NacosConfiguration.NacosListener>> outerMap =
+                new ConcurrentHashMap<>();
+        outerMap.put(dataId, innerMap);
+
+        Field listenerMapField = 
NacosConfiguration.class.getDeclaredField("CONFIG_LISTENERS_MAP");
+        listenerMapField.setAccessible(true);
+        listenerMapField.set(null, outerMap);
+
+        try (MockedStatic<ConfigProcessor> processorMockedStatic = 
Mockito.mockStatic(ConfigProcessor.class)) {
+            processorMockedStatic
+                    .when(() -> 
ConfigProcessor.resolverConfigDataType(anyString()))
+                    .thenReturn("yaml");
+            processorMockedStatic
+                    .when(() -> ConfigProcessor.processConfig(anyString(), 
anyString()))
+                    .thenThrow(new IOException("mock io exception"));
+            // execute
+            nacosListener.innerReceive(dataId, group, "session.mode=redis");
+        }
+
+        Properties actualConfig = (Properties) seataConfigField.get(null);
+        Assertions.assertEquals("db", actualConfig.getProperty(configKey));
+
+        Assertions.assertFalse(listener.invoked);
+    }
+
+    @NotNull
+    private static NacosConfiguration.NacosListener getNacosListener(String 
dataId, TestListener listener)
+            throws ClassNotFoundException, NoSuchMethodException, 
InstantiationException, IllegalAccessException,
+                    InvocationTargetException {
+        Class<?> outerClass = 
Class.forName("org.apache.seata.config.nacos.NacosConfiguration");
+        Constructor<?> constructor = outerClass.getDeclaredConstructor();
+        constructor.setAccessible(true);
+        Object nacosConfigurationInstance = constructor.newInstance();
+        Class<?> innerClass = 
Class.forName("org.apache.seata.config.nacos.NacosConfiguration$NacosListener");
+
+        Constructor<?> innerConstructor =
+                innerClass.getDeclaredConstructor(outerClass, String.class, 
ConfigurationChangeListener.class);
+        innerConstructor.setAccessible(true);
+        NacosConfiguration.NacosListener nacosListener = 
(NacosConfiguration.NacosListener)
+                innerConstructor.newInstance(nacosConfigurationInstance, 
dataId, listener);
+        return nacosListener;
+    }
+
+    private static class TestListener implements ConfigurationChangeListener {
+        boolean invoked = false;
+
+        @Override
+        public void onChangeEvent(ConfigurationChangeEvent event) {
+            invoked = true;
+        }
+    }
 }
diff --git 
a/config/seata-config-zk/src/main/java/org/apache/seata/config/zk/ZookeeperConfiguration.java
 
b/config/seata-config-zk/src/main/java/org/apache/seata/config/zk/ZookeeperConfiguration.java
index eb9dcf2c21..1bf720de63 100644
--- 
a/config/seata-config-zk/src/main/java/org/apache/seata/config/zk/ZookeeperConfiguration.java
+++ 
b/config/seata-config-zk/src/main/java/org/apache/seata/config/zk/ZookeeperConfiguration.java
@@ -394,15 +394,16 @@ public class ZookeeperConfiguration extends 
AbstractConfiguration {
                 o = new String(data.getData());
             }
             if (path.equals(getConfigPath())) {
+                if (StringUtils.isBlank(o.toString())) {
+                    LOGGER.warn("Empty config from Zookeeper, path='{}'. 
Skipped.", path);
+                    return;
+                }
                 Properties seataConfigNew = new Properties();
-                if (StringUtils.isNotBlank(o.toString())) {
-                    try {
-                        seataConfigNew = 
ConfigProcessor.processConfig(o.toString(), getZkDataType());
-
-                    } catch (IOException e) {
-                        LOGGER.error("load config properties error", e);
-                        return;
-                    }
+                try {
+                    seataConfigNew = 
ConfigProcessor.processConfig(o.toString(), getZkDataType());
+                } catch (IOException e) {
+                    LOGGER.error("load config properties error", e);
+                    return;
                 }
 
                 for (Map.Entry<String, 
ConcurrentMap<ConfigurationChangeListener, NodeCacheListenerImpl>> entry :
diff --git 
a/config/seata-config-zk/src/test/java/org/apache/seata/config/zk/ZkConfigurationTest.java
 
b/config/seata-config-zk/src/test/java/org/apache/seata/config/zk/ZkConfigurationTest.java
index d34d863c51..f34b43f4e0 100644
--- 
a/config/seata-config-zk/src/test/java/org/apache/seata/config/zk/ZkConfigurationTest.java
+++ 
b/config/seata-config-zk/src/test/java/org/apache/seata/config/zk/ZkConfigurationTest.java
@@ -16,20 +16,32 @@
  */
 package org.apache.seata.config.zk;
 
+import org.apache.curator.framework.recipes.cache.ChildData;
+import org.apache.curator.framework.recipes.cache.CuratorCacheListener;
 import org.apache.curator.test.TestingServer;
 import org.apache.seata.config.ConfigurationChangeEvent;
 import org.apache.seata.config.ConfigurationChangeListener;
 import org.apache.seata.config.ConfigurationChangeType;
+import org.apache.seata.config.processor.ConfigProcessor;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 /**
  * The type zk configuration test
  */
@@ -124,4 +136,43 @@ public class ZkConfigurationTest {
         }
         Assertions.assertTrue(listened[0]);
     }
+
+    @Test
+    public void testEvent_pathEqualsConfigPath_blankValue() throws Exception {
+        Method getConfigPath = 
ZookeeperConfiguration.class.getDeclaredMethod("getConfigPath");
+        getConfigPath.setAccessible(true);
+
+        String configPath = getConfigPath.invoke(null).toString();
+
+        ZookeeperConfiguration.NodeCacheListenerImpl listener =
+                new ZookeeperConfiguration.NodeCacheListenerImpl(configPath, 
null);
+
+        ChildData mockData = mock(ChildData.class);
+        when(mockData.getData()).thenReturn(new byte[0]);
+
+        listener.event(CuratorCacheListener.Type.NODE_CHANGED, null, mockData);
+
+        // If it can run to this point, it indicates that the null value 
branch has been overwritten
+    }
+
+    @Test
+    public void testEvent_pathEqualsConfigPath_throwException() throws 
Exception {
+        Method getConfigPathMethod = 
ZookeeperConfiguration.class.getDeclaredMethod("getConfigPath");
+        getConfigPathMethod.setAccessible(true);
+        String configPath = getConfigPathMethod.invoke(null).toString();
+        ZookeeperConfiguration.NodeCacheListenerImpl listener =
+                new ZookeeperConfiguration.NodeCacheListenerImpl(configPath, 
null);
+        String invalidYaml = "server:\n" + "  port: 8080\n" + "::host 
localhost";
+        ChildData mockData = mock(ChildData.class);
+        
when(mockData.getData()).thenReturn(invalidYaml.getBytes(StandardCharsets.UTF_8));
+        try (MockedStatic<ConfigProcessor> processorMockedStatic = 
Mockito.mockStatic(ConfigProcessor.class)) {
+            processorMockedStatic
+                    .when(() -> 
ConfigProcessor.resolverConfigDataType(anyString()))
+                    .thenReturn("yaml");
+            processorMockedStatic
+                    .when(() -> ConfigProcessor.processConfig(anyString(), 
anyString()))
+                    .thenThrow(new IOException("mock io exception"));
+            listener.event(CuratorCacheListener.Type.NODE_CHANGED, null, 
mockData);
+        }
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@seata.apache.org
For additional commands, e-mail: notifications-h...@seata.apache.org

Reply via email to