You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shenyu.apache.org by de...@apache.org on 2021/07/05 13:03:04 UTC

[incubator-shenyu] branch master updated: [ISSUE #1709] Add test case for shenyu-admin module (#1710)

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

dengliming pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-shenyu.git


The following commit(s) were added to refs/heads/master by this push:
     new 943154b  [ISSUE #1709] Add test case for shenyu-admin module (#1710)
943154b is described below

commit 943154ba805f5f552622b404803ea39ff77910b2
Author: renzhuyan <40...@qq.com>
AuthorDate: Mon Jul 5 21:02:56 2021 +0800

    [ISSUE #1709] Add test case for shenyu-admin module (#1710)
    
    * Add test case for shenyu-admin module
    
    * add verify what we expect
---
 .../shenyu/admin/listener/etcd/EtcdClientTest.java |  91 ++++++++++
 .../etcd/EtcdDataDataChangedListenerTest.java      | 142 +++++++++++++++
 .../admin/listener/etcd/EtcdDataInitTest.java      |  82 +++++++++
 .../nacos/NacosDataChangedListenerTest.java        | 190 +++++++++++++++++++++
 .../admin/listener/nacos/NacosDataInitTest.java    |  83 +++++++++
 5 files changed, 588 insertions(+)

diff --git a/shenyu-admin/src/test/java/org/apache/shenyu/admin/listener/etcd/EtcdClientTest.java b/shenyu-admin/src/test/java/org/apache/shenyu/admin/listener/etcd/EtcdClientTest.java
new file mode 100644
index 0000000..718b436
--- /dev/null
+++ b/shenyu-admin/src/test/java/org/apache/shenyu/admin/listener/etcd/EtcdClientTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.shenyu.admin.listener.etcd;
+
+import io.etcd.jetcd.ByteSequence;
+import io.etcd.jetcd.Client;
+import io.etcd.jetcd.KV;
+import io.etcd.jetcd.kv.DeleteResponse;
+import io.etcd.jetcd.kv.PutResponse;
+import io.etcd.jetcd.options.DeleteOption;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.CompletableFuture;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * The testCase for {@link EtcdClient}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class EtcdClientTest {
+
+    private static final String TEST_KEY = "KEY";
+
+    private static final String TEST_VALUE = "VALUE";
+
+    @Mock
+    private Client client;
+
+    private EtcdClient etcdClient;
+
+    @Before
+    public void setUp() {
+        KV kvClient = mock(KV.class);
+        when(client.getKVClient()).thenReturn(kvClient);
+        etcdClient = new EtcdClient(client);
+        Assert.assertNotNull(etcdClient);
+    }
+
+    @Test
+    public void close() {
+        etcdClient.close();
+    }
+
+    @Test
+    public void put() {
+        CompletableFuture<PutResponse> put = mock(CompletableFuture.class);
+        when(client.getKVClient().put(ByteSequence.from(TEST_KEY, StandardCharsets.UTF_8), ByteSequence.from(TEST_VALUE, StandardCharsets.UTF_8))).thenReturn(put);
+        etcdClient.put(TEST_KEY, TEST_VALUE);
+        verify(client.getKVClient(), times(1)).put(any(ByteSequence.class), any(ByteSequence.class));
+    }
+
+    @Test
+    public void delete() {
+        CompletableFuture<DeleteResponse> delete = mock(CompletableFuture.class);
+        when(client.getKVClient().delete(ByteSequence.from(TEST_KEY, StandardCharsets.UTF_8))).thenReturn(delete);
+        etcdClient.delete(TEST_KEY);
+        verify(client.getKVClient(), times(1)).delete(any(ByteSequence.class));
+    }
+
+    @Test
+    public void deleteEtcdPathRecursive() {
+        etcdClient.deleteEtcdPathRecursive(TEST_KEY);
+        verify(client.getKVClient(), times(1)).delete(any(ByteSequence.class), any(DeleteOption.class));
+    }
+}
diff --git a/shenyu-admin/src/test/java/org/apache/shenyu/admin/listener/etcd/EtcdDataDataChangedListenerTest.java b/shenyu-admin/src/test/java/org/apache/shenyu/admin/listener/etcd/EtcdDataDataChangedListenerTest.java
new file mode 100644
index 0000000..542bcf1
--- /dev/null
+++ b/shenyu-admin/src/test/java/org/apache/shenyu/admin/listener/etcd/EtcdDataDataChangedListenerTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.shenyu.admin.listener.etcd;
+
+import com.google.common.collect.ImmutableList;
+import lombok.SneakyThrows;
+import org.apache.shenyu.common.dto.AppAuthData;
+import org.apache.shenyu.common.dto.MetaData;
+import org.apache.shenyu.common.dto.PluginData;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.dto.RuleData;
+import org.apache.shenyu.common.enums.DataEventTypeEnum;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * The testCase for {@link EtcdDataDataChangedListener}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class EtcdDataDataChangedListenerTest {
+
+    private static final String MOCK_APP_KEY = "MOCK_APP_KEY";
+
+    private static final String MOCK_APP_SECRET = "MOCK_APP_SECRET";
+
+    private static final String MOCK_ID = "MOCK_ID";
+
+    private static final String MOCK_PATH = "MOCK_PATH";
+
+    private static final String MOCK_APP_NAME = "MOCK_APP_NAME";
+
+    private static final String MOCK_NAME = "MOCK_NAME";
+
+    private static final String MOCK_CONFIG = "MOCK_CONFIG";
+
+    private static final String MOCK_PLUGIN_NAME = "MOCK_PLUGIN_NAME";
+
+    private static final String MOCK_SELECTOR_ID = "MOCK_SELECTOR_ID";
+
+    @InjectMocks
+    private EtcdDataDataChangedListener etcdDataDataChangedListener;
+
+    @Mock
+    private EtcdClient etcdClient;
+
+    /**
+     * test case onAppAuthChanged event.
+     */
+    @Test
+    public void testOnAppAuthChanged() {
+        AppAuthData appAuthData = AppAuthData.builder().appKey(MOCK_APP_KEY).appSecret(MOCK_APP_SECRET).build();
+        etcdDataDataChangedListener.onAppAuthChanged(ImmutableList.of(appAuthData), DataEventTypeEnum.CREATE);
+        etcdDataDataChangedListener.onAppAuthChanged(ImmutableList.of(appAuthData), DataEventTypeEnum.UPDATE);
+        etcdDataDataChangedListener.onAppAuthChanged(ImmutableList.of(appAuthData), DataEventTypeEnum.DELETE);
+        verify(etcdClient, times(2)).put(any(String.class), any(String.class));
+        verify(etcdClient, times(1)).delete(any(String.class));
+    }
+
+    /**
+     * test case onMetaDataChanged event.
+     */
+    @SneakyThrows
+    @Test
+    public void testOnMetaDataChanged() {
+        MetaData metaData = MetaData.builder().id(MOCK_ID).path(MOCK_PATH).appName(MOCK_APP_NAME).build();
+        etcdDataDataChangedListener.onMetaDataChanged(ImmutableList.of(metaData), DataEventTypeEnum.CREATE);
+        etcdDataDataChangedListener.onMetaDataChanged(ImmutableList.of(metaData), DataEventTypeEnum.UPDATE);
+        etcdDataDataChangedListener.onMetaDataChanged(ImmutableList.of(metaData), DataEventTypeEnum.DELETE);
+        verify(etcdClient, times(2)).put(any(String.class), any(String.class));
+        verify(etcdClient, times(1)).delete(any(String.class));
+    }
+
+    /**
+     * test case onPluginChanged event.
+     */
+    @Test
+    public void testOnPluginChanged() {
+        PluginData pluginData = PluginData.builder().id(MOCK_ID).name(MOCK_NAME).config(MOCK_CONFIG).build();
+        etcdDataDataChangedListener.onPluginChanged(ImmutableList.of(pluginData), DataEventTypeEnum.CREATE);
+        etcdDataDataChangedListener.onPluginChanged(ImmutableList.of(pluginData), DataEventTypeEnum.UPDATE);
+        etcdDataDataChangedListener.onPluginChanged(ImmutableList.of(pluginData), DataEventTypeEnum.DELETE);
+        verify(etcdClient, times(2)).put(any(String.class), any(String.class));
+        verify(etcdClient, times(3)).deleteEtcdPathRecursive(any(String.class));
+    }
+
+    /**
+     * test case onSelectorChanged event.
+     */
+    @Test
+    public void testOnSelectorChanged() {
+        SelectorData selectorData = SelectorData.builder().id(MOCK_ID).name(MOCK_NAME).pluginName(MOCK_PLUGIN_NAME).build();
+        etcdDataDataChangedListener.onSelectorChanged(ImmutableList.of(selectorData), DataEventTypeEnum.CREATE);
+        etcdDataDataChangedListener.onSelectorChanged(ImmutableList.of(selectorData), DataEventTypeEnum.UPDATE);
+        etcdDataDataChangedListener.onSelectorChanged(ImmutableList.of(selectorData), DataEventTypeEnum.REFRESH);
+        etcdDataDataChangedListener.onSelectorChanged(ImmutableList.of(selectorData), DataEventTypeEnum.DELETE);
+        verify(etcdClient, times(3)).put(any(String.class), any(String.class));
+        verify(etcdClient, times(1)).delete(any(String.class));
+        verify(etcdClient, times(1)).deleteEtcdPathRecursive(any(String.class));
+    }
+
+    /**
+     * test case onRuleChanged event.
+     */
+    @Test
+    public void testOnRuleChanged() {
+        RuleData ruleData = RuleData.builder()
+                .id(MOCK_ID)
+                .name(MOCK_NAME)
+                .pluginName(MOCK_PLUGIN_NAME)
+                .selectorId(MOCK_SELECTOR_ID)
+                .build();
+        etcdDataDataChangedListener.onRuleChanged(ImmutableList.of(ruleData), DataEventTypeEnum.CREATE);
+        etcdDataDataChangedListener.onRuleChanged(ImmutableList.of(ruleData), DataEventTypeEnum.UPDATE);
+        etcdDataDataChangedListener.onRuleChanged(ImmutableList.of(ruleData), DataEventTypeEnum.REFRESH);
+        etcdDataDataChangedListener.onRuleChanged(ImmutableList.of(ruleData), DataEventTypeEnum.DELETE);
+        verify(etcdClient, times(3)).put(any(String.class), any(String.class));
+        verify(etcdClient, times(1)).delete(any(String.class));
+        verify(etcdClient, times(1)).deleteEtcdPathRecursive(any(String.class));
+    }
+}
diff --git a/shenyu-admin/src/test/java/org/apache/shenyu/admin/listener/etcd/EtcdDataInitTest.java b/shenyu-admin/src/test/java/org/apache/shenyu/admin/listener/etcd/EtcdDataInitTest.java
new file mode 100644
index 0000000..4d5fc7d
--- /dev/null
+++ b/shenyu-admin/src/test/java/org/apache/shenyu/admin/listener/etcd/EtcdDataInitTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.shenyu.admin.listener.etcd;
+
+import lombok.SneakyThrows;
+import org.apache.shenyu.admin.service.SyncDataService;
+import org.apache.shenyu.common.constant.DefaultPathConstants;
+import org.apache.shenyu.common.enums.DataEventTypeEnum;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test cases for {@link EtcdDataInit}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class EtcdDataInitTest {
+
+    @Mock
+    private EtcdClient etcdClient;
+
+    @Mock
+    private SyncDataService syncDataService;
+
+    @Test
+    @SneakyThrows
+    public void testRun() {
+        EtcdDataInit etcdDataInit = new EtcdDataInit(etcdClient, syncDataService);
+        Assert.assertNotNull(etcdDataInit);
+
+        when(etcdClient.exists(Mockito.anyString())).thenReturn(false);
+        etcdDataInit.run();
+
+        when(etcdClient.exists(Mockito.anyString()))
+                .then(invocation -> pathExist(invocation, Collections.singletonList(
+                        DefaultPathConstants.APP_AUTH_PARENT
+                )));
+        etcdDataInit.run();
+
+        when(etcdClient.exists(Mockito.anyString()))
+                .thenAnswer(invocation -> pathExist(invocation, Arrays.asList(
+                        DefaultPathConstants.PLUGIN_PARENT,
+                        DefaultPathConstants.APP_AUTH_PARENT
+                )));
+        etcdDataInit.run();
+
+        verify(syncDataService, times(1)).syncAll(any(DataEventTypeEnum.class));
+    }
+
+    private boolean pathExist(final InvocationOnMock invocation, final List<String> pathList) {
+        String arg = invocation.getArgument(0);
+        return pathList.contains(arg);
+    }
+}
diff --git a/shenyu-admin/src/test/java/org/apache/shenyu/admin/listener/nacos/NacosDataChangedListenerTest.java b/shenyu-admin/src/test/java/org/apache/shenyu/admin/listener/nacos/NacosDataChangedListenerTest.java
new file mode 100644
index 0000000..b7d87b8
--- /dev/null
+++ b/shenyu-admin/src/test/java/org/apache/shenyu/admin/listener/nacos/NacosDataChangedListenerTest.java
@@ -0,0 +1,190 @@
+/*
+ * 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.shenyu.admin.listener.nacos;
+
+import com.alibaba.nacos.api.config.ConfigService;
+import com.google.common.collect.ImmutableList;
+import lombok.SneakyThrows;
+import org.apache.shenyu.common.constant.NacosPathConstants;
+import org.apache.shenyu.common.dto.AppAuthData;
+import org.apache.shenyu.common.dto.MetaData;
+import org.apache.shenyu.common.dto.PluginData;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.dto.RuleData;
+import org.apache.shenyu.common.enums.DataEventTypeEnum;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * The testCase for {@link NacosDataChangedListener}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class NacosDataChangedListenerTest {
+
+    private static final String MOCK_APP_KEY = "MOCK_APP_KEY";
+
+    private static final String MOCK_APP_SECRET = "MOCK_APP_SECRET";
+
+    private static final String MOCK_ID = "MOCK_ID";
+
+    private static final String MOCK_PATH = "MOCK_PATH";
+
+    private static final String MOCK_APP_NAME = "MOCK_APP_NAME";
+
+    private static final String MOCK_NAME = "MOCK_NAME";
+
+    private static final String MOCK_CONFIG = "MOCK_CONFIG";
+
+    private static final String MOCK_PLUGIN_NAME = "MOCK_PLUGIN_NAME";
+
+    private static final String MOCK_SELECTOR_ID = "MOCK_SELECTOR_ID";
+
+    @Mock
+    private ConfigService configService;
+
+    @InjectMocks
+    private NacosDataChangedListener nacosDataChangedListener;
+
+    @Test
+    @SneakyThrows
+    public void testOnAppAuthChanged() {
+        String config = "{\"divide\":{\"appKey\":\"appKey\",\"appSecret\":\"appSecret\",\"open\":true}}";
+        AppAuthData appAuthData = AppAuthData.builder().appKey(MOCK_APP_KEY).appSecret(MOCK_APP_SECRET).build();
+
+        when(configService.getConfig(NacosPathConstants.AUTH_DATA_ID, NacosPathConstants.GROUP,
+                NacosPathConstants.DEFAULT_TIME_OUT)).thenReturn(config);
+        nacosDataChangedListener.onAppAuthChanged(ImmutableList.of(appAuthData), DataEventTypeEnum.DELETE);
+        nacosDataChangedListener.onAppAuthChanged(ImmutableList.of(appAuthData), DataEventTypeEnum.REFRESH);
+        nacosDataChangedListener.onAppAuthChanged(ImmutableList.of(appAuthData), DataEventTypeEnum.MYSELF);
+        nacosDataChangedListener.onAppAuthChanged(ImmutableList.of(appAuthData), DataEventTypeEnum.CREATE);
+        verify(configService, times(4)).publishConfig(any(String.class), any(String.class), any(String.class));
+
+        when(configService.getConfig(NacosPathConstants.AUTH_DATA_ID, NacosPathConstants.GROUP,
+                NacosPathConstants.DEFAULT_TIME_OUT)).thenReturn(NacosPathConstants.EMPTY_CONFIG_DEFAULT_VALUE);
+        nacosDataChangedListener.onAppAuthChanged(ImmutableList.of(appAuthData), DataEventTypeEnum.DELETE);
+        nacosDataChangedListener.onAppAuthChanged(ImmutableList.of(appAuthData), DataEventTypeEnum.REFRESH);
+        nacosDataChangedListener.onAppAuthChanged(ImmutableList.of(appAuthData), DataEventTypeEnum.MYSELF);
+        nacosDataChangedListener.onAppAuthChanged(ImmutableList.of(appAuthData), DataEventTypeEnum.CREATE);
+        verify(configService, times(8)).publishConfig(any(String.class), any(String.class), any(String.class));
+    }
+
+    @Test
+    @SneakyThrows
+    public void testOnPluginChanged() {
+        String config = "{\"divide\":{\"id\":\"id\",\"name\":\"name\",\"enabled\":true}}";
+        PluginData pluginData = PluginData.builder().id(MOCK_ID).name(MOCK_NAME).config(MOCK_CONFIG).build();
+
+        when(configService.getConfig(NacosPathConstants.PLUGIN_DATA_ID, NacosPathConstants.GROUP,
+                NacosPathConstants.DEFAULT_TIME_OUT)).thenReturn(config);
+        nacosDataChangedListener.onPluginChanged(ImmutableList.of(pluginData), DataEventTypeEnum.DELETE);
+        nacosDataChangedListener.onPluginChanged(ImmutableList.of(pluginData), DataEventTypeEnum.REFRESH);
+        nacosDataChangedListener.onPluginChanged(ImmutableList.of(pluginData), DataEventTypeEnum.MYSELF);
+        nacosDataChangedListener.onPluginChanged(ImmutableList.of(pluginData), DataEventTypeEnum.CREATE);
+        verify(configService, times(4)).publishConfig(any(String.class), any(String.class), any(String.class));
+
+        when(configService.getConfig(NacosPathConstants.PLUGIN_DATA_ID, NacosPathConstants.GROUP,
+                NacosPathConstants.DEFAULT_TIME_OUT)).thenReturn(NacosPathConstants.EMPTY_CONFIG_DEFAULT_VALUE);
+        nacosDataChangedListener.onPluginChanged(ImmutableList.of(pluginData), DataEventTypeEnum.DELETE);
+        nacosDataChangedListener.onPluginChanged(ImmutableList.of(pluginData), DataEventTypeEnum.REFRESH);
+        nacosDataChangedListener.onPluginChanged(ImmutableList.of(pluginData), DataEventTypeEnum.MYSELF);
+        nacosDataChangedListener.onPluginChanged(ImmutableList.of(pluginData), DataEventTypeEnum.CREATE);
+        verify(configService, times(8)).publishConfig(any(String.class), any(String.class), any(String.class));
+    }
+
+    @Test
+    @SneakyThrows
+    public void testOnSelectorChanged() {
+        String config = "{\"divide\":[{\"id\":\"id\",\"name\":\"name\",\"enabled\":true}]}";
+        SelectorData selectorData = SelectorData.builder().id(MOCK_ID).name(MOCK_NAME).pluginName(MOCK_PLUGIN_NAME).build();
+
+        when(configService.getConfig(NacosPathConstants.SELECTOR_DATA_ID, NacosPathConstants.GROUP,
+                NacosPathConstants.DEFAULT_TIME_OUT)).thenReturn(config);
+        nacosDataChangedListener.onSelectorChanged(ImmutableList.of(selectorData), DataEventTypeEnum.DELETE);
+        nacosDataChangedListener.onSelectorChanged(ImmutableList.of(selectorData), DataEventTypeEnum.REFRESH);
+        nacosDataChangedListener.onSelectorChanged(ImmutableList.of(selectorData), DataEventTypeEnum.MYSELF);
+        nacosDataChangedListener.onSelectorChanged(ImmutableList.of(selectorData), DataEventTypeEnum.CREATE);
+        verify(configService, times(4)).publishConfig(any(String.class), any(String.class), any(String.class));
+
+        when(configService.getConfig(NacosPathConstants.SELECTOR_DATA_ID, NacosPathConstants.GROUP,
+                NacosPathConstants.DEFAULT_TIME_OUT)).thenReturn(NacosPathConstants.EMPTY_CONFIG_DEFAULT_VALUE);
+        nacosDataChangedListener.onSelectorChanged(ImmutableList.of(selectorData), DataEventTypeEnum.DELETE);
+        nacosDataChangedListener.onSelectorChanged(ImmutableList.of(selectorData), DataEventTypeEnum.REFRESH);
+        nacosDataChangedListener.onSelectorChanged(ImmutableList.of(selectorData), DataEventTypeEnum.MYSELF);
+        nacosDataChangedListener.onSelectorChanged(ImmutableList.of(selectorData), DataEventTypeEnum.CREATE);
+        verify(configService, times(8)).publishConfig(any(String.class), any(String.class), any(String.class));
+    }
+
+    @Test
+    @SneakyThrows
+    public void testOnMetaDataChanged() {
+        String config = "{\"divide\":{\"id\":\"id\",\"appName\":\"appName\",\"enabled\":true}}";
+        MetaData metaData = MetaData.builder().id(MOCK_ID).path(MOCK_PATH).appName(MOCK_APP_NAME).build();
+
+        when(configService.getConfig(NacosPathConstants.META_DATA_ID, NacosPathConstants.GROUP,
+                NacosPathConstants.DEFAULT_TIME_OUT)).thenReturn(config);
+        nacosDataChangedListener.onMetaDataChanged(ImmutableList.of(metaData), DataEventTypeEnum.DELETE);
+        nacosDataChangedListener.onMetaDataChanged(ImmutableList.of(metaData), DataEventTypeEnum.REFRESH);
+        nacosDataChangedListener.onMetaDataChanged(ImmutableList.of(metaData), DataEventTypeEnum.MYSELF);
+        nacosDataChangedListener.onMetaDataChanged(ImmutableList.of(metaData), DataEventTypeEnum.CREATE);
+        verify(configService, times(4)).publishConfig(any(String.class), any(String.class), any(String.class));
+
+        when(configService.getConfig(NacosPathConstants.META_DATA_ID, NacosPathConstants.GROUP,
+                NacosPathConstants.DEFAULT_TIME_OUT)).thenReturn(NacosPathConstants.EMPTY_CONFIG_DEFAULT_VALUE);
+        nacosDataChangedListener.onMetaDataChanged(ImmutableList.of(metaData), DataEventTypeEnum.DELETE);
+        nacosDataChangedListener.onMetaDataChanged(ImmutableList.of(metaData), DataEventTypeEnum.REFRESH);
+        nacosDataChangedListener.onMetaDataChanged(ImmutableList.of(metaData), DataEventTypeEnum.MYSELF);
+        nacosDataChangedListener.onMetaDataChanged(ImmutableList.of(metaData), DataEventTypeEnum.CREATE);
+        verify(configService, times(8)).publishConfig(any(String.class), any(String.class), any(String.class));
+    }
+
+    @Test
+    @SneakyThrows
+    public void testOnRuleChanged() {
+        String config = "{\"divide\":[{\"id\":\"id\",\"appName\":\"appName\",\"enabled\":true}]}";
+        RuleData ruleData = RuleData.builder()
+                .id(MOCK_ID)
+                .name(MOCK_NAME)
+                .pluginName(MOCK_PLUGIN_NAME)
+                .selectorId(MOCK_SELECTOR_ID)
+                .build();
+
+        when(configService.getConfig(NacosPathConstants.RULE_DATA_ID, NacosPathConstants.GROUP,
+                NacosPathConstants.DEFAULT_TIME_OUT)).thenReturn(config);
+        nacosDataChangedListener.onRuleChanged(ImmutableList.of(ruleData), DataEventTypeEnum.DELETE);
+        nacosDataChangedListener.onRuleChanged(ImmutableList.of(ruleData), DataEventTypeEnum.REFRESH);
+        nacosDataChangedListener.onRuleChanged(ImmutableList.of(ruleData), DataEventTypeEnum.MYSELF);
+        nacosDataChangedListener.onRuleChanged(ImmutableList.of(ruleData), DataEventTypeEnum.CREATE);
+        verify(configService, times(4)).publishConfig(any(String.class), any(String.class), any(String.class));
+
+        when(configService.getConfig(NacosPathConstants.RULE_DATA_ID, NacosPathConstants.GROUP,
+                NacosPathConstants.DEFAULT_TIME_OUT)).thenReturn(NacosPathConstants.EMPTY_CONFIG_DEFAULT_VALUE);
+        nacosDataChangedListener.onRuleChanged(ImmutableList.of(ruleData), DataEventTypeEnum.DELETE);
+        nacosDataChangedListener.onRuleChanged(ImmutableList.of(ruleData), DataEventTypeEnum.REFRESH);
+        nacosDataChangedListener.onRuleChanged(ImmutableList.of(ruleData), DataEventTypeEnum.MYSELF);
+        nacosDataChangedListener.onRuleChanged(ImmutableList.of(ruleData), DataEventTypeEnum.CREATE);
+        verify(configService, times(8)).publishConfig(any(String.class), any(String.class), any(String.class));
+    }
+}
diff --git a/shenyu-admin/src/test/java/org/apache/shenyu/admin/listener/nacos/NacosDataInitTest.java b/shenyu-admin/src/test/java/org/apache/shenyu/admin/listener/nacos/NacosDataInitTest.java
new file mode 100644
index 0000000..6be8c6e
--- /dev/null
+++ b/shenyu-admin/src/test/java/org/apache/shenyu/admin/listener/nacos/NacosDataInitTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.shenyu.admin.listener.nacos;
+
+import com.alibaba.nacos.api.config.ConfigService;
+import lombok.SneakyThrows;
+import org.apache.shenyu.admin.service.SyncDataService;
+import org.apache.shenyu.common.constant.NacosPathConstants;
+import org.apache.shenyu.common.enums.DataEventTypeEnum;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test cases for {@link NacosDataInit}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class NacosDataInitTest {
+
+    private static final String PLUGIN_DATA_ID = "PLUGIN_DATA_ID";
+
+    private static final String AUTH_DATA_ID = "AUTH_DATA_ID";
+
+    private static final String META_DATA_ID = "META_DATA_ID";
+
+    @Mock
+    private ConfigService configService;
+
+    @Mock
+    private SyncDataService syncDataService;
+
+    @Test
+    @SneakyThrows
+    public void testRun() {
+        String group = NacosPathConstants.GROUP;
+        long timeout = NacosPathConstants.DEFAULT_TIME_OUT;
+        NacosDataInit nacosDataInit = new NacosDataInit(configService, syncDataService);
+
+        String pluginDataId = NacosPathConstants.PLUGIN_DATA_ID;
+        when(configService.getConfig(pluginDataId, group, timeout)).thenReturn(null);
+        nacosDataInit.run();
+        when(configService.getConfig(pluginDataId, group, timeout)).thenReturn(PLUGIN_DATA_ID);
+        nacosDataInit.run();
+
+        reset(configService);
+        String authDataId = NacosPathConstants.AUTH_DATA_ID;
+        when(configService.getConfig(authDataId, group, timeout)).thenReturn(null);
+        nacosDataInit.run();
+        when(configService.getConfig(authDataId, group, timeout)).thenReturn(AUTH_DATA_ID);
+        nacosDataInit.run();
+
+        reset(configService);
+        String metaDataId = NacosPathConstants.META_DATA_ID;
+        when(configService.getConfig(metaDataId, group, timeout)).thenReturn(null);
+        nacosDataInit.run();
+        when(configService.getConfig(metaDataId, group, timeout)).thenReturn(META_DATA_ID);
+        nacosDataInit.run();
+
+        verify(syncDataService, times(3)).syncAll(any(DataEventTypeEnum.class));
+    }
+}