content: /*
   * Copyright 1999-2025 Alibaba Group Holding Ltd.
   *
   * 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 com.alibaba.nacos.ai.index;
  
  import com.alibaba.nacos.ai.constant.Constants;
  import com.alibaba.nacos.ai.model.mcp.McpServerIndexData;
  import com.alibaba.nacos.api.model.Page;
  import com.alibaba.nacos.config.server.model.ConfigInfo;
  import com.alibaba.nacos.config.server.service.ConfigDetailService;
  import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService;
  import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest;
  import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse;
  import com.alibaba.nacos.core.service.NamespaceOperationService;
  import org.junit.jupiter.api.BeforeEach;
  import org.junit.jupiter.api.Test;
  import org.junit.jupiter.api.extension.ExtendWith;
  import org.mockito.Mock;
  import org.mockito.junit.jupiter.MockitoExtension;
  import org.mockito.junit.jupiter.MockitoSettings;
  import org.mockito.quality.Strictness;
  import org.mockito.stubbing.Answer;
  
  import java.util.ArrayList;
  import java.util.List;
  import java.util.concurrent.ScheduledExecutorService;
  import java.util.concurrent.ScheduledFuture;
  import java.util.concurrent.TimeUnit;
  
  import static org.junit.jupiter.api.Assertions.assertEquals;
  import static org.junit.jupiter.api.Assertions.assertNotNull;
  import static org.junit.jupiter.api.Assertions.assertNull;
  import static org.mockito.ArgumentMatchers.any;
  import static org.mockito.ArgumentMatchers.anyInt;
  import static org.mockito.ArgumentMatchers.anyLong;
  import static org.mockito.ArgumentMatchers.anyString;
  import static org.mockito.ArgumentMatchers.eq;
  import static org.mockito.ArgumentMatchers.isNull;
  import static org.mockito.Mockito.doThrow;
  import static org.mockito.Mockito.mock;
  import static org.mockito.Mockito.never;
  import static org.mockito.Mockito.verify;
  import static org.mockito.Mockito.when;
  
  /**
   * Unit tests for CachedMcpServerIndex.
   */
  @ExtendWith(MockitoExtension.class)
  @MockitoSettings(strictness = Strictness.LENIENT)
  class CachedMcpServerIndexTest {
      
      @Mock
      private ConfigDetailService configDetailService;
      
      @Mock
      private NamespaceOperationService namespaceOperationService;
      
      @Mock
      private ConfigQueryChainService configQueryChainService;
      
      @Mock
      private McpCacheIndex cacheIndex;
      
      @Mock
      private ScheduledExecutorService scheduledExecutor;
      
      private CachedMcpServerIndex cachedIndex;
      
      @BeforeEach
      void setUp() {
          // Set system properties to enable cache
          System.setProperty("nacos.mcp.cache.enabled", "true");
          System.setProperty("nacos.mcp.cache.sync.interval", "300");
          
          cachedIndex = new CachedMcpServerIndex(configDetailService, namespaceOperationService, configQueryChainService,
                  cacheIndex, scheduledExecutor, true, 300);
      }
      
      @Test
      void testGetMcpServerByIdWithCacheHit() {
          final String mcpId = "test-id-123";
          final String namespaceId = "test-namespace";
          
          // 模拟缓存命中
          McpServerIndexData cachedData = new McpServerIndexData();
          cachedData.setId(mcpId);
          cachedData.setNamespaceId(namespaceId);
          when(cacheIndex.getMcpServerById(mcpId)).thenReturn(cachedData);
          
          // 执行查询
          McpServerIndexData result = cachedIndex.getMcpServerById(mcpId);
          
          // 验证结果
          assertNotNull(result);
          assertEquals(mcpId, result.getId());
          assertEquals(namespaceId, result.getNamespaceId());
          
          // 验证缓存被调用，数据库查询没有被调用
          verify(cacheIndex).getMcpServerById(mcpId);
          verify(configQueryChainService, never()).handle(any());
      }
      
      @Test
      void testGetMcpServerByIdWithCacheMiss() {
          final String mcpId = "test-id-123";
          final String namespaceId = "test-namespace";
          
          // 模拟缓存未命中
          when(cacheIndex.getMcpServerById(mcpId)).thenReturn(null);
          
          // 模拟数据库查询结果
          ConfigQueryChainResponse mockResponse = mock(ConfigQueryChainResponse.class);
          when(mockResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL);
          when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(mockResponse);
          
          // 模拟命名空间列表
          List<com.alibaba.nacos.api.model.response.Namespace> namespaceList = new ArrayList<>();
          com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace();
          namespace.setNamespace(namespaceId);
          namespaceList.add(namespace);
          when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList);
          
          // 执行查询
          McpServerIndexData result = cachedIndex.getMcpServerById(mcpId);
          
          // 验证结果
          assertNotNull(result);
          assertEquals(mcpId, result.getId());
          assertEquals(namespaceId, result.getNamespaceId());
          
          // 验证缓存被调用，数据库查询也被调用
          verify(cacheIndex).getMcpServerById(mcpId);
          verify(configQueryChainService).handle(any(ConfigQueryChainRequest.class));
          
          // 验证缓存被更新
          verify(cacheIndex).updateIndex(eq(namespaceId), eq(mcpId), eq(mcpId));
      }
      
      @Test
      void testGetMcpServerByNameWithCacheHit() {
          final String namespaceId = "test-namespace";
          final String mcpName = "test-mcp";
          final String mcpId = "test-id-123";
          
          // 模拟缓存命中
          McpServerIndexData cachedData = new McpServerIndexData();
          cachedData.setId(mcpId);
          cachedData.setNamespaceId(namespaceId);
          when(cacheIndex.getMcpServerByName(namespaceId, mcpName)).thenReturn(cachedData);
          
          // 执行查询
          McpServerIndexData result = cachedIndex.getMcpServerByName(namespaceId, mcpName);
          
          // 验证结果
          assertNotNull(result);
          assertEquals(mcpId, result.getId());
          assertEquals(namespaceId, result.getNamespaceId());
          
          // 验证缓存被调用
          verify(cacheIndex).getMcpServerByName(namespaceId, mcpName);
      }
      
      @Test
      void testGetMcpServerByNameWithCacheMiss() {
          final String namespaceId = "test-namespace";
          final String mcpName = "test-mcp";
          final String mcpId = "test-id-123";
          
          // 模拟缓存未命中
          when(cacheIndex.getMcpServerByName(namespaceId, mcpName)).thenReturn(null);
          
          // 模拟数据库查询结果
          final Page<ConfigInfo> mockPage = new Page<>();
          List<ConfigInfo> configList = new ArrayList<>();
          ConfigInfo configInfo = new ConfigInfo();
          configInfo.setDataId(mcpId + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX);
          configInfo.setTenant(namespaceId);
          configList.add(configInfo);
          mockPage.setPageItems(configList);
          mockPage.setTotalCount(1);
          
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage);
          
          // 执行查询
          McpServerIndexData result = cachedIndex.getMcpServerByName(namespaceId, mcpName);
          
          // 验证结果
          assertNotNull(result);
          assertEquals(mcpId, result.getId());
          assertEquals(namespaceId, result.getNamespaceId());
          
          // 验证缓存被调用，数据库查询也被调用
          verify(cacheIndex).getMcpServerByName(namespaceId, mcpName);
          verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any());
          
          // 验证缓存被更新
          verify(cacheIndex).updateIndex(eq(namespaceId), eq(mcpName), eq(mcpId));
      }
      
      @Test
      void testSearchMcpServerByName() {
          final String namespaceId = "test-namespace";
          final String mcpName = "test-mcp";
          final String mcpId = "test-id-123";
          
          // 模拟数据库查询结果
          final Page<ConfigInfo> mockPage = new Page<>();
          List<ConfigInfo> configList = new ArrayList<>();
          ConfigInfo configInfo = new ConfigInfo();
          configInfo.setDataId(mcpId + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX);
          configInfo.setTenant(namespaceId);
          configList.add(configInfo);
          mockPage.setPageItems(configList);
          mockPage.setTotalCount(1);
          
          // 使用正确的参数匹配，匹配实际的调用参数
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(10), isNull(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage);
          
          // 执行搜索
          Page<McpServerIndexData> result = cachedIndex.searchMcpServerByName(namespaceId, mcpName,
                  Constants.MCP_LIST_SEARCH_ACCURATE, 0, 10);
          
          // 验证结果
          assertNotNull(result);
          assertEquals(1, result.getTotalCount());
          assertEquals(1, result.getPageItems().size());
          
          McpServerIndexData indexData = result.getPageItems().get(0);
          assertEquals(mcpId, indexData.getId());
          assertEquals(namespaceId, indexData.getNamespaceId());
          
          // 验证数据库查询被调用
          verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(10), isNull(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any());
          
          // 验证缓存被更新
          verify(cacheIndex).updateIndex(eq(namespaceId), eq(mcpName), eq(mcpId));
      }
      
      @Test
      void testCacheDisabled() {
          // 设置系统属性以禁用缓存
          System.setProperty("nacos.mcp.cache.enabled", "false");
          
          // 重新创建实例
          final CachedMcpServerIndex disabledIndex = new CachedMcpServerIndex(configDetailService,
                  namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, false, 0);
          
          final String mcpId = "test-id-123";
          final String namespaceId = "test-namespace";
          
          // 模拟数据库查询结果
          ConfigQueryChainResponse mockResponse = mock(ConfigQueryChainResponse.class);
          when(mockResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL);
          when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(mockResponse);
          
          // 模拟命名空间列表
          List<com.alibaba.nacos.api.model.response.Namespace> namespaceList = new ArrayList<>();
          com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace();
          namespace.setNamespace(namespaceId);
          namespaceList.add(namespace);
          when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList);
          
          // 执行查询
          McpServerIndexData result = disabledIndex.getMcpServerById(mcpId);
          
          // 验证结果
          assertNotNull(result);
          assertEquals(mcpId, result.getId());
          assertEquals(namespaceId, result.getNamespaceId());
          
          // 验证缓存没有被调用
          verify(cacheIndex, never()).getMcpServerById(anyString());
          verify(cacheIndex, never()).updateIndex(anyString(), anyString(), anyString());
          
          // 验证数据库查询被调用
          verify(configQueryChainService).handle(any(ConfigQueryChainRequest.class));
      }
      
      @Test
      void testGetCacheStats() {
          // 模拟缓存统计
          McpCacheIndex.CacheStats mockStats = new McpCacheIndex.CacheStats(10, 5, 2, 100);
          when(cacheIndex.getStats()).thenReturn(mockStats);
          
          // 获取统计信息
          McpCacheIndex.CacheStats result = cachedIndex.getCacheStats();
          
          // 验证结果
          assertNotNull(result);
          assertEquals(10, result.getHitCount());
          assertEquals(5, result.getMissCount());
          assertEquals(2, result.getEvictionCount());
          assertEquals(100, result.getSize());
          assertEquals(2.0 / 3.0, result.getHitRate(), 0.001);
      }
      
      @Test
      void testClearCache() {
          // 执行清空缓存
          cachedIndex.clearCache();
          
          // 验证缓存被清空
          verify(cacheIndex).clear();
      }
      
      @Test
      void testTriggerCacheSync() {
          // 模拟命名空间列表
          List<com.alibaba.nacos.api.model.response.Namespace> namespaceList = new ArrayList<>();
          com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace();
          namespace.setNamespace("test-namespace");
          namespaceList.add(namespace);
          when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList);
          
          // 模拟搜索结果
          final Page<ConfigInfo> mockPage = new Page<>();
          mockPage.setPageItems(new ArrayList<>());
          mockPage.setTotalCount(0);
          
          when(configDetailService.findConfigInfoPage(anyString(), anyInt(), anyInt(), anyString(), anyString(),
                  anyString(), any())).thenReturn(mockPage);
          
          // 执行手动同步
          cachedIndex.triggerCacheSync();
          
          // 验证数据库查询被调用
          verify(configDetailService).findConfigInfoPage(anyString(), anyInt(), anyInt(), anyString(), anyString(),
                  anyString(), any());
      }
      
      // 新增缓存删除功能测试
      
      @Test
      void testRemoveMcpServerByNameWhenCacheEnabled() {
          final String namespaceId = "test-namespace";
          final String mcpName = "test-mcp-name";
          
          // 执行缓存删除
          cachedIndex.removeMcpServerByName(namespaceId, mcpName);
          
          // 验证缓存删除方法被调用
          verify(cacheIndex).removeIndex(namespaceId, mcpName);
      }
      
      @Test
      void testRemoveMcpServerByIdWhenCacheEnabled() {
          final String mcpId = "test-mcp-id-123";
          
          // 执行缓存删除
          cachedIndex.removeMcpServerById(mcpId);
          
          // 验证缓存删除方法被调用
          verify(cacheIndex).removeIndex(mcpId);
      }
      
      @Test
      void testRemoveMcpServerByNameWhenCacheDisabled() {
          // 创建禁用缓存的实例
          final CachedMcpServerIndex disabledIndex = new CachedMcpServerIndex(configDetailService,
                  namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, false, 0);
          
          final String namespaceId = "test-namespace";
          final String mcpName = "test-mcp-name";
          
          // 执行缓存删除
          disabledIndex.removeMcpServerByName(namespaceId, mcpName);
          
          // 验证缓存删除方法没有被调用（因为缓存被禁用）
          verify(cacheIndex, never()).removeIndex(namespaceId, mcpName);
      }
      
      @Test
      void testRemoveMcpServerByIdWhenCacheDisabled() {
          // 创建禁用缓存的实例
          final CachedMcpServerIndex disabledIndex = new CachedMcpServerIndex(configDetailService,
                  namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, false, 0);
          
          final String mcpId = "test-mcp-id-123";
          
          // 执行缓存删除
          disabledIndex.removeMcpServerById(mcpId);
          
          // 验证缓存删除方法没有被调用（因为缓存被禁用）
          verify(cacheIndex, never()).removeIndex(mcpId);
      }
      
      @Test
      void testRemoveMcpServerByNameWithNullParameters() {
          // 测试 null 参数
          cachedIndex.removeMcpServerByName(null, null);
          cachedIndex.removeMcpServerByName("namespace", null);
          cachedIndex.removeMcpServerByName(null, "mcpName");
          
          // 验证缓存删除方法没有被调用（因为参数为 null 或空）
          verify(cacheIndex, never()).removeIndex(anyString(), anyString());
      }
      
      @Test
      void testRemoveMcpServerByIdWithNullParameter() {
          // 测试 null 参数
          cachedIndex.removeMcpServerById(null);
          
          // 验证缓存删除方法没有被调用（因为参数为 null）
          verify(cacheIndex, never()).removeIndex(anyString());
      }
      
      @Test
      void testRemoveMcpServerByNameWithEmptyParameters() {
          // 测试空字符串参数
          cachedIndex.removeMcpServerByName("", "");
          cachedIndex.removeMcpServerByName("namespace", "");
          cachedIndex.removeMcpServerByName("", "mcpName");
          
          // 空字符串应该仍然调用缓存删除方法
          verify(cacheIndex).removeIndex("", "");
          verify(cacheIndex).removeIndex("namespace", "");
          verify(cacheIndex).removeIndex("", "mcpName");
      }
      
      @Test
      void testRemoveMcpServerByIdWithEmptyParameter() {
          // 测试空字符串参数
          cachedIndex.removeMcpServerById("");
          
          // 空字符串应该仍然调用缓存删除方法
          verify(cacheIndex).removeIndex("");
      }
      
      // 补充的测试用例
      
      @Test
      void testGetMcpServerByIdWithCacheDisabledAndNotFound() {
          // 创建禁用缓存的实例
          final CachedMcpServerIndex disabledIndex = new CachedMcpServerIndex(configDetailService,
                  namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, false, 0);
          
          final String mcpId = "test-id-123";
          
          // 模拟数据库查询结果为null
          ConfigQueryChainResponse mockResponse = mock(ConfigQueryChainResponse.class);
          when(mockResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND);
          when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(mockResponse);
          
          // 模拟命名空间列表
          List<com.alibaba.nacos.api.model.response.Namespace> namespaceList = new ArrayList<>();
          com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace();
          namespace.setNamespace("test-namespace");
          namespaceList.add(namespace);
          when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList);
          
          // 执行查询
          McpServerIndexData result = disabledIndex.getMcpServerById(mcpId);
          
          // 验证结果为null
          assertNull(result);
          
          // 验证缓存没有被调用
          verify(cacheIndex, never()).getMcpServerById(anyString());
          verify(cacheIndex, never()).updateIndex(anyString(), anyString(), anyString());
          
          // 验证数据库查询被调用
          verify(configQueryChainService).handle(any(ConfigQueryChainRequest.class));
      }
      
      @Test
      void testGetMcpServerByIdWithCacheMissAndNotFound() {
          final String mcpId = "test-id-123";
          
          // 模拟缓存未命中
          when(cacheIndex.getMcpServerById(mcpId)).thenReturn(null);
          
          // 模拟数据库查询结果为null（未找到）
          ConfigQueryChainResponse mockResponse = mock(ConfigQueryChainResponse.class);
          when(mockResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND);
          when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(mockResponse);
          
          // 模拟命名空间列表
          List<com.alibaba.nacos.api.model.response.Namespace> namespaceList = new ArrayList<>();
          com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace();
          namespace.setNamespace("test-namespace");
          namespaceList.add(namespace);
          when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList);
          
          // 执行查询
          McpServerIndexData result = cachedIndex.getMcpServerById(mcpId);
          
          // 验证结果为null
          assertNull(result);
          
          // 验证缓存被调用，数据库查询也被调用
          verify(cacheIndex).getMcpServerById(mcpId);
          verify(configQueryChainService).handle(any(ConfigQueryChainRequest.class));
          
          // 验证缓存未被更新（因为未找到）
          verify(cacheIndex, never()).updateIndex(anyString(), anyString(), anyString());
      }
      
      @Test
      void testGetMcpServerByNameWithInvalidParameters() {
          // 测试null参数
          McpServerIndexData result1 = cachedIndex.getMcpServerByName(null, "test-name");
          assertNull(result1);
          
          McpServerIndexData result2 = cachedIndex.getMcpServerByName("test-namespace", null);
          assertNull(result2);
          
          McpServerIndexData result3 = cachedIndex.getMcpServerByName(null, null);
          assertNull(result3);
          
          // 测试空字符串参数
          McpServerIndexData result4 = cachedIndex.getMcpServerByName("", "test-name");
          assertNull(result4);
          
          McpServerIndexData result5 = cachedIndex.getMcpServerByName("test-namespace", "");
          assertNull(result5);
          
          McpServerIndexData result6 = cachedIndex.getMcpServerByName("", "");
          assertNull(result6);
          
          // 验证缓存未被调用
          verify(cacheIndex, never()).getMcpServerByName(anyString(), anyString());
      }
      
      @Test
      void testGetMcpServerByNameWithCacheDisabledAndNotFound() {
          // 创建禁用缓存的实例
          final CachedMcpServerIndex disabledIndex = new CachedMcpServerIndex(configDetailService,
                  namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, false, 0);
          
          final String namespaceId = "test-namespace";
          final String mcpName = "test-mcp";
          
          // 模拟数据库查询结果为null
          final Page<ConfigInfo> mockPage = new Page<>();
          mockPage.setPageItems(new ArrayList<>());
          mockPage.setTotalCount(0);
          
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage);
          
          // 执行查询
          McpServerIndexData result = disabledIndex.getMcpServerByName(namespaceId, mcpName);
          
          // 验证结果为null
          assertNull(result);
          
          // 验证缓存没有被调用
          verify(cacheIndex, never()).getMcpServerByName(anyString(), anyString());
          verify(cacheIndex, never()).updateIndex(anyString(), anyString(), anyString());
          
          // 验证数据库查询被调用
          verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any());
      }
      
      @Test
      void testGetMcpServerByNameWithCacheMissAndNotFound() {
          final String namespaceId = "test-namespace";
          final String mcpName = "test-mcp";
          
          // 模拟缓存未命中
          when(cacheIndex.getMcpServerByName(namespaceId, mcpName)).thenReturn(null);
          
          // 模拟数据库查询结果为null
          final Page<ConfigInfo> mockPage = new Page<>();
          mockPage.setPageItems(new ArrayList<>());
          mockPage.setTotalCount(0);
          
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage);
          
          // 执行查询
          McpServerIndexData result = cachedIndex.getMcpServerByName(namespaceId, mcpName);
          
          // 验证结果为null
          assertNull(result);
          
          // 验证缓存被调用，数据库查询也被调用
          verify(cacheIndex).getMcpServerByName(namespaceId, mcpName);
          verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any());
          
          // 验证缓存未被更新（因为未找到）
          verify(cacheIndex, never()).updateIndex(anyString(), anyString(), anyString());
      }
      
      @Test
      void testSearchMcpServerByNameWithNullName() {
          final String namespaceId = "test-namespace";
          final String mcpId = "test-id-123";
          
          // 模拟数据库查询结果
          final Page<ConfigInfo> mockPage = new Page<>();
          List<ConfigInfo> configList = new ArrayList<>();
          ConfigInfo configInfo = new ConfigInfo();
          configInfo.setDataId(mcpId + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX);
          configInfo.setTenant(namespaceId);
          configList.add(configInfo);
          mockPage.setPageItems(configList);
          mockPage.setTotalCount(1);
          
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), anyString(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage);
          
          // 执行搜索，name为null
          Page<McpServerIndexData> result = cachedIndex.searchMcpServerByName(namespaceId, null,
                  Constants.MCP_LIST_SEARCH_BLUR, 0, 10);
          
          // 验证结果
          assertNotNull(result);
          assertEquals(1, result.getTotalCount());
          assertEquals(1, result.getPageItems().size());
          
          McpServerIndexData indexData = result.getPageItems().get(0);
          assertEquals(mcpId, indexData.getId());
          assertEquals(namespaceId, indexData.getNamespaceId());
          
          // 验证数据库查询被调用
          verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), anyString(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any());
      }
      
      @Test
      void testSearchMcpServerByNameWithEmptyName() {
          final String namespaceId = "test-namespace";
          final String mcpId = "test-id-123";
          
          // 模拟数据库查询结果
          final Page<ConfigInfo> mockPage = new Page<>();
          List<ConfigInfo> configList = new ArrayList<>();
          ConfigInfo configInfo = new ConfigInfo();
          configInfo.setDataId(mcpId + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX);
          configInfo.setTenant(namespaceId);
          configList.add(configInfo);
          mockPage.setPageItems(configList);
          mockPage.setTotalCount(1);
          
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), anyString(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage);
          
          // 执行搜索，name为空字符串
          Page<McpServerIndexData> result = cachedIndex.searchMcpServerByName(namespaceId, "",
                  Constants.MCP_LIST_SEARCH_ACCURATE, 0, 10);
          
          // 验证结果
          assertNotNull(result);
          assertEquals(1, result.getTotalCount());
          assertEquals(1, result.getPageItems().size());
          
          McpServerIndexData indexData = result.getPageItems().get(0);
          assertEquals(mcpId, indexData.getId());
          assertEquals(namespaceId, indexData.getNamespaceId());
          
          // 验证数据库查询被调用
          verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), anyString(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any());
      }
      
      @Test
      void testSearchMcpServerByNameWithBlurSearch() {
          final String namespaceId = "test-namespace";
          final String mcpName = "test-mcp";
          final String mcpId = "test-id-123";
          
          // 模拟数据库查询结果
          final Page<ConfigInfo> mockPage = new Page<>();
          List<ConfigInfo> configList = new ArrayList<>();
          ConfigInfo configInfo = new ConfigInfo();
          configInfo.setDataId(mcpId + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX);
          configInfo.setTenant(namespaceId);
          configList.add(configInfo);
          mockPage.setPageItems(configList);
          mockPage.setTotalCount(1);
          
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), anyString(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage);
          
          // 执行搜索
          Page<McpServerIndexData> result = cachedIndex.searchMcpServerByName(namespaceId, mcpName,
                  Constants.MCP_LIST_SEARCH_BLUR, 0, 10);
          
          // 验证结果
          assertNotNull(result);
          assertEquals(1, result.getTotalCount());
          assertEquals(1, result.getPageItems().size());
          
          McpServerIndexData indexData = result.getPageItems().get(0);
          assertEquals(mcpId, indexData.getId());
          assertEquals(namespaceId, indexData.getNamespaceId());
          
          // 验证数据库查询被调用
          verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), anyString(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any());
      }
      
      @Test
      void testSearchMcpServerByNameWithPagination() {
          final String namespaceId = "test-namespace";
          final String mcpName = "test-mcp";
          final String mcpId = "test-id-123";
          
          // 模拟数据库查询结果
          final Page<ConfigInfo> mockPage = new Page<>();
          List<ConfigInfo> configList = new ArrayList<>();
          ConfigInfo configInfo = new ConfigInfo();
          configInfo.setDataId(mcpId + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX);
          configInfo.setTenant(namespaceId);
          configList.add(configInfo);
          mockPage.setPageItems(configList);
          mockPage.setTotalCount(15); // 总数15，测试分页
          
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(3), eq(5), isNull(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage);
          
          // 执行搜索，offset=10, limit=5，应该查询第3页
          Page<McpServerIndexData> result = cachedIndex.searchMcpServerByName(namespaceId, mcpName,
                  Constants.MCP_LIST_SEARCH_ACCURATE, 10, 5);
          
          // 验证结果
          assertNotNull(result);
          assertEquals(15, result.getTotalCount());
          assertEquals(1, result.getPageItems().size());
          assertEquals(3, result.getPageNumber());
          assertEquals(3, result.getPagesAvailable()); // ceil(15/5) = 3
          
          McpServerIndexData indexData = result.getPageItems().get(0);
          assertEquals(mcpId, indexData.getId());
          assertEquals(namespaceId, indexData.getNamespaceId());
          
          // 验证数据库查询被调用
          verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(3), eq(5), isNull(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any());
      }
      
      @Test
      void testFetchOrderedNamespaceList() {
          // 模拟命名空间列表（无序）
          final List<com.alibaba.nacos.api.model.response.Namespace> namespaceList = new ArrayList<>();
          com.alibaba.nacos.api.model.response.Namespace namespace1 = new com.alibaba.nacos.api.model.response.Namespace();
          namespace1.setNamespace("b-namespace");
          com.alibaba.nacos.api.model.response.Namespace namespace2 = new com.alibaba.nacos.api.model.response.Namespace();
          namespace2.setNamespace("a-namespace");
          com.alibaba.nacos.api.model.response.Namespace namespace3 = new com.alibaba.nacos.api.model.response.Namespace();
          namespace3.setNamespace("c-namespace");
          namespaceList.add(namespace1);
          namespaceList.add(namespace2);
          namespaceList.add(namespace3);
          when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList);
          
          // 通过调用依赖该方法的函数来间接测试
          final String mcpId = "test-id-123";
          
          // 模拟缓存未命中
          when(cacheIndex.getMcpServerById(mcpId)).thenReturn(null);
          
          // 模拟数据库查询结果
          ConfigQueryChainResponse mockResponse = mock(ConfigQueryChainResponse.class);
          when(mockResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL);
          when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(mockResponse);
          
          // 执行查询
          cachedIndex.getMcpServerById(mcpId);
          
          // 验证命名空间服务被调用
          verify(namespaceOperationService).getNamespaceList();
      }
      
      @Test
      void testMapMcpServerVersionConfigToIndexData() {
          // 通过调用依赖该方法的函数来间接测试
          final String namespaceId = "test-namespace";
          final String mcpName = "test-mcp";
          final String mcpId = "test-id-123";
          
          // 模拟缓存未命中
          when(cacheIndex.getMcpServerByName(namespaceId, mcpName)).thenReturn(null);
          
          // 模拟数据库查询结果
          final Page<ConfigInfo> mockPage = new Page<>();
          List<ConfigInfo> configList = new ArrayList<>();
          ConfigInfo configInfo = new ConfigInfo();
          configInfo.setDataId(mcpId + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX);
          configInfo.setTenant(namespaceId);
          configList.add(configInfo);
          mockPage.setPageItems(configList);
          mockPage.setTotalCount(1);
          
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage);
          
          // 执行查询
          McpServerIndexData result = cachedIndex.getMcpServerByName(namespaceId, mcpName);
          
          // 验证结果，确保mapMcpServerVersionConfigToIndexData方法正确执行
          assertNotNull(result);
          assertEquals(mcpId, result.getId());
          assertEquals(namespaceId, result.getNamespaceId());
      }
      
      @Test
      void testTriggerCacheSyncWhenCacheDisabled() {
          // 创建禁用缓存的实例
          final CachedMcpServerIndex disabledIndex = new CachedMcpServerIndex(configDetailService,
                  namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, false, 0);
          
          // 执行手动同步
          disabledIndex.triggerCacheSync();
          
          // 验证数据库查询没有被调用
          verify(configDetailService, never()).findConfigInfoPage(anyString(), anyInt(), anyInt(), anyString(),
                  anyString(), anyString(), any());
      }
      
      @Test
      void testStartSyncTask() {
          when(scheduledExecutor.scheduleWithFixedDelay(any(Runnable.class), eq(10L), eq(10L), any(TimeUnit.class))).then(
                  (Answer<ScheduledFuture>) invocation -> {
                      invocation.getArgument(0, Runnable.class).run();
                      return null;
                  });
          // 创建一个新的实例来测试startSyncTask方法
          CachedMcpServerIndex newIndex = new CachedMcpServerIndex(configDetailService, namespaceOperationService,
                  configQueryChainService, cacheIndex, scheduledExecutor, true, 10);
          
          // 验证调度任务已启动
          verify(scheduledExecutor).scheduleWithFixedDelay(any(Runnable.class), eq(10L), eq(10L), any(TimeUnit.class));
          verify(namespaceOperationService).getNamespaceList();
      }
      
      @Test
      void testStartSyncTaskWithException() {
          when(scheduledExecutor.scheduleWithFixedDelay(any(Runnable.class), eq(10L), eq(10L), any(TimeUnit.class))).then(
                  (Answer<ScheduledFuture>) invocation -> {
                      invocation.getArgument(0, Runnable.class).run();
                      return null;
                  });
          when(namespaceOperationService.getNamespaceList()).thenThrow(new RuntimeException("test"));
          // 创建一个新的实例来测试startSyncTask方法
          CachedMcpServerIndex newIndex = new CachedMcpServerIndex(configDetailService, namespaceOperationService,
                  configQueryChainService, cacheIndex, scheduledExecutor, true, 10);
          
          // 验证调度任务已启动
          verify(scheduledExecutor).scheduleWithFixedDelay(any(Runnable.class), eq(10L), eq(10L), any(TimeUnit.class));
      }
      
      @Test
      void testDestroy() {
          // 模拟一个已经存在的任务
          ScheduledFuture mockTask = mock(ScheduledFuture.class);
          when(scheduledExecutor.scheduleWithFixedDelay(any(Runnable.class), anyLong(), anyLong(),
                  any(TimeUnit.class))).thenReturn(mockTask);
          
          // 创建一个新的实例来测试destroy方法
          CachedMcpServerIndex indexToDestroy = new CachedMcpServerIndex(configDetailService, namespaceOperationService,
                  configQueryChainService, cacheIndex, scheduledExecutor, true, 300);
          
          // 调用destroy方法
          indexToDestroy.destroy();
          
          // 验证任务被取消并且线程池被关闭
          verify(mockTask).cancel(true);
          verify(scheduledExecutor).shutdown();
      }
      
      @Test
      void testDestroyWithExceptionHandling() {
          // 模拟scheduledExecutor.shutdown()抛出异常
          doThrow(new RuntimeException("Shutdown failed")).when(scheduledExecutor).shutdown();
          
          // 创建一个新的实例来测试destroy方法
          CachedMcpServerIndex indexToDestroy = new CachedMcpServerIndex(configDetailService, namespaceOperationService,
                  configQueryChainService, cacheIndex, scheduledExecutor, true, 300);
          
          // 调用destroy方法不应该抛出异常
          indexToDestroy.destroy();
      }
      
      @Test
      void testSyncCacheFromDatabase() {
          // 模拟命名空间列表
          List<com.alibaba.nacos.api.model.response.Namespace> namespaceList = new ArrayList<>();
          com.alibaba.nacos.api.model.response.Namespace namespace1 = new com.alibaba.nacos.api.model.response.Namespace();
          namespace1.setNamespace("namespace-1");
          com.alibaba.nacos.api.model.response.Namespace namespace2 = new com.alibaba.nacos.api.model.response.Namespace();
          namespace2.setNamespace("namespace-2");
          namespaceList.add(namespace1);
          namespaceList.add(namespace2);
          when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList);
          
          // 模拟每个命名空间的搜索结果
          final Page<ConfigInfo> mockPage1 = new Page<>();
          List<ConfigInfo> configList1 = new ArrayList<>();
          ConfigInfo configInfo1 = new ConfigInfo();
          configInfo1.setDataId("server1" + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX);
          configInfo1.setTenant("namespace-1");
          configList1.add(configInfo1);
          mockPage1.setPageItems(configList1);
          mockPage1.setTotalCount(1);
          
          final Page<ConfigInfo> mockPage2 = new Page<>();
          List<ConfigInfo> configList2 = new ArrayList<>();
          ConfigInfo configInfo2 = new ConfigInfo();
          configInfo2.setDataId("server2" + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX);
          configInfo2.setTenant("namespace-2");
          configList2.add(configInfo2);
          mockPage2.setPageItems(configList2);
          mockPage2.setTotalCount(1);
          
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(1000), anyString(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq("namespace-1"), any())).thenReturn(mockPage1);
          
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(1000), anyString(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq("namespace-2"), any())).thenReturn(mockPage2);
          
          // 调用syncCacheFromDatabase方法（通过triggerCacheSync）
          cachedIndex.triggerCacheSync();
          
          // 验证为每个命名空间调用了搜索
          verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(1000), anyString(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq("namespace-1"), any());
          
          verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(1000), anyString(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq("namespace-2"), any());
          
          // 验证缓存被更新
          verify(cacheIndex).updateIndex(eq("namespace-1"), eq("server1"), eq("server1"));
          verify(cacheIndex).updateIndex(eq("namespace-2"), eq("server2"), eq("server2"));
      }
      
      @Test
      void testSyncCacheFromDatabaseWithSearchException() {
          // 模拟命名空间列表
          List<com.alibaba.nacos.api.model.response.Namespace> namespaceList = new ArrayList<>();
          com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace();
          namespace.setNamespace("namespace-1");
          namespaceList.add(namespace);
          when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList);
          
          // 模拟搜索时抛出异常
          when(configDetailService.findConfigInfoPage(anyString(), anyInt(), anyInt(), anyString(), anyString(),
                  anyString(), any())).thenThrow(new RuntimeException("Database error"));
          
          // 调用syncCacheFromDatabase方法（通过triggerCacheSync）
          cachedIndex.triggerCacheSync();
          
          // 即使出现异常也应该继续执行而不会中断
          verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(1000), anyString(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq("namespace-1"), any());
      }
      
      @Test
      void testSyncCacheFromDatabaseWithEmptyResult() {
          // 模拟命名空间列表
          List<com.alibaba.nacos.api.model.response.Namespace> namespaceList = new ArrayList<>();
          com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace();
          namespace.setNamespace("namespace-1");
          namespaceList.add(namespace);
          when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList);
          
          // 模拟空的搜索结果
          Page<ConfigInfo> mockPage = new Page<>();
          mockPage.setPageItems(new ArrayList<>());
          mockPage.setTotalCount(0);
          
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(1000), anyString(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq("namespace-1"), any())).thenReturn(mockPage);
          
          // 调用syncCacheFromDatabase方法（通过triggerCacheSync）
          cachedIndex.triggerCacheSync();
          
          // 验证搜索被调用但缓存未更新
          verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(1000), anyString(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq("namespace-1"), any());
          
          // 没有数据所以不需要更新缓存
          verify(cacheIndex, never()).updateIndex(anyString(), anyString(), anyString());
      }
      
      @Test
      void testSyncCacheFromDatabaseWithException() {
          // 模拟命名空间列表
          List<com.alibaba.nacos.api.model.response.Namespace> namespaceList = new ArrayList<>();
          com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace();
          namespace.setNamespace("test-namespace");
          namespaceList.add(namespace);
          when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList);
          
          // 模拟搜索时抛出异常
          when(configDetailService.findConfigInfoPage(anyString(), anyInt(), anyInt(), anyString(), anyString(),
                  anyString(), any())).thenThrow(new RuntimeException("Test exception"));
          
          // 通过调用triggerCacheSync来触发syncCacheFromDatabase
          cachedIndex.triggerCacheSync();
          
          // 验证异常被处理，不会导致程序崩溃
          verify(namespaceOperationService).getNamespaceList();
      }
  }