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.ai.constant.AiConstants;
  import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo;
  import com.alibaba.nacos.api.ai.model.mcp.McpServerVersionInfo;
  import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail;
  import com.alibaba.nacos.api.model.Page;
  import com.alibaba.nacos.api.model.response.Namespace;
  import com.alibaba.nacos.common.utils.JacksonUtils;
  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.AfterEach;
  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 java.util.LinkedList;
  import java.util.List;
  import java.util.UUID;
  
  import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
  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.anyMap;
  import static org.mockito.ArgumentMatchers.eq;
  import static org.mockito.ArgumentMatchers.isNull;
  import static org.mockito.Mockito.when;
  
  @ExtendWith(MockitoExtension.class)
  class PlainMcpServerIndexTest {
      
      @Mock
      private ConfigDetailService configDetailService;
      
      @Mock
      private NamespaceOperationService namespaceOperationService;
      
      @Mock
      private ConfigQueryChainService configQueryChainService;
      
      PlainMcpServerIndex plainMcpServerIndex;
      
      @BeforeEach
      void setUp() {
          plainMcpServerIndex = new PlainMcpServerIndex(namespaceOperationService, configDetailService,
                  configQueryChainService);
      }
      
      @AfterEach
      void tearDown() {
      }
      
      @Test
      void searchMcpServerByNameWithNamespaceIdByAccurateNotFound() {
          Page<ConfigInfo> searchPage = mockConfigInfo(0, 0, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE);
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(10), isNull(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE),
                  anyMap())).thenReturn(searchPage);
          Page<McpServerIndexData> result = plainMcpServerIndex.searchMcpServerByName(
                  AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName", Constants.MCP_LIST_SEARCH_ACCURATE, 0, 10);
          assertEquals(0, result.getTotalCount());
          assertEquals(1, result.getPageNumber());
          assertEquals(0, result.getPagesAvailable());
          assertEquals(0, result.getPageItems().size());
      }
      
      @Test
      void searchMcpServerByNameWithNamespaceIdByAccurate() {
          Page<ConfigInfo> searchPage = mockConfigInfo(1, 1, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE);
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(10), isNull(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE),
                  anyMap())).thenReturn(searchPage);
          Page<McpServerIndexData> result = plainMcpServerIndex.searchMcpServerByName(
                  AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName", Constants.MCP_LIST_SEARCH_ACCURATE, 0, 10);
          assertEquals(1, result.getTotalCount());
          assertEquals(1, result.getPageNumber());
          assertEquals(1, result.getPagesAvailable());
          assertEquals(1, result.getPageItems().size());
      }
      
      @Test
      void searchMcpServerByNameWithNamespaceIdByBlur() {
          Page<ConfigInfo> searchPage = mockConfigInfo(10, 10, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE);
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), eq("*"),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE),
                  anyMap())).thenReturn(searchPage);
          Page<McpServerIndexData> result = plainMcpServerIndex.searchMcpServerByName(
                  AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, Constants.MCP_LIST_SEARCH_BLUR, 0, 10);
          assertEquals(10, result.getTotalCount());
          assertEquals(1, result.getPageNumber());
          assertEquals(1, result.getPagesAvailable());
          assertEquals(10, result.getPageItems().size());
      }
      
      @Test
      void searchMcpServerByNameWithMultiplePagesFirstPage() {
          Page<ConfigInfo> searchPage = mockConfigInfoWithPagination(25, 10, 1, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE);
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), eq("*"),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE),
                  anyMap())).thenReturn(searchPage);
          
          Page<McpServerIndexData> result = plainMcpServerIndex.searchMcpServerByName(
                  AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, Constants.MCP_LIST_SEARCH_BLUR, 0, 10);
          
          assertEquals(25, result.getTotalCount());
          assertEquals(1, result.getPageNumber());
          assertEquals(3, result.getPagesAvailable());
          assertEquals(10, result.getPageItems().size());
      }
      
      @Test
      void searchMcpServerByNameWithMultiplePagesSecondPage() {
          Page<ConfigInfo> searchPage = mockConfigInfoWithPagination(25, 10, 2, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE);
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(2), eq(10), eq("*"),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE),
                  anyMap())).thenReturn(searchPage);
          
          Page<McpServerIndexData> result = plainMcpServerIndex.searchMcpServerByName(
                  AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, Constants.MCP_LIST_SEARCH_BLUR, 10, 10);
          
          assertEquals(25, result.getTotalCount());
          assertEquals(2, result.getPageNumber());
          assertEquals(3, result.getPagesAvailable());
          assertEquals(10, result.getPageItems().size());
      }
      
      @Test
      void searchMcpServerByNameWithMultiplePagesLastPage() {
          Page<ConfigInfo> searchPage = mockConfigInfoWithPagination(25, 5, 3, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE);
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(3), eq(10), eq("*"),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE),
                  anyMap())).thenReturn(searchPage);
          
          Page<McpServerIndexData> result = plainMcpServerIndex.searchMcpServerByName(
                  AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, Constants.MCP_LIST_SEARCH_BLUR, 20, 10);
          
          assertEquals(25, result.getTotalCount());
          assertEquals(3, result.getPageNumber());
          assertEquals(3, result.getPagesAvailable());
          assertEquals(5, result.getPageItems().size());
      }
      
      @Test
      void getMcpServerByIdWithEmptyId() {
          assertNull(plainMcpServerIndex.getMcpServerById(""));
      }
      
      @Test
      void getMcpServerByIdNotFound() {
          String id = UUID.randomUUID().toString();
          when(namespaceOperationService.getNamespaceList()).thenReturn(mockNamespaceList(1, false));
          when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(
                  mockConfigQueryChainResponse(null));
          assertNull(plainMcpServerIndex.getMcpServerById(id));
      }
      
      @Test
      void getMcpServerByIdFound() {
          String id = UUID.randomUUID().toString();
          when(namespaceOperationService.getNamespaceList()).thenReturn(mockNamespaceList(1, false));
          McpServerBasicInfo mcpServerBasicInfo = mockServerVersionInfo(id);
          when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(
                  mockConfigQueryChainResponse(mcpServerBasicInfo));
          McpServerIndexData result = plainMcpServerIndex.getMcpServerById(id);
          assertEquals(id, result.getId());
          assertEquals("namespaceId-0", result.getNamespaceId());
      }
      
      @Test
      void getMcpServerByNameNotFound() {
          Page<ConfigInfo> countPage = mockConfigInfo(0, 0, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE);
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE),
                  anyMap())).thenReturn(countPage);
          assertNull(plainMcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName"));
      }
      
      @Test
      void getMcpServerByNameFound() {
          Page<ConfigInfo> countPage = mockConfigInfo(1, 1, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE);
          when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(),
                  eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE),
                  anyMap())).thenReturn(countPage);
          McpServerIndexData result = plainMcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE,
                  "mcpName");
          assertNotNull(result);
          assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, result.getNamespaceId());
          assertDoesNotThrow(() -> UUID.fromString(result.getId()));
      }
      
      @Test
      void removeMcpServerByName() {
          assertDoesNotThrow(
                  () -> plainMcpServerIndex.removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName"));
      }
      
      @Test
      void removeMcpServerById() {
          assertDoesNotThrow(() -> plainMcpServerIndex.removeMcpServerById(UUID.randomUUID().toString()));
      }
      
      private ConfigQueryChainResponse mockConfigQueryChainResponse(Object obj) {
          ConfigQueryChainResponse mockResponse = new ConfigQueryChainResponse();
          if (null != obj) {
              mockResponse.setContent(JacksonUtils.toJson(obj));
              mockResponse.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL);
          } else {
              mockResponse.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND);
          }
          return mockResponse;
      }
      
      private List<Namespace> mockNamespaceList(int size, boolean withDefaultNs) {
          List<Namespace> list = new LinkedList<>();
          if (withDefaultNs) {
              Namespace namespace = new Namespace();
              namespace.setNamespace(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE);
              namespace.setNamespaceShowName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE);
              list.add(namespace);
          }
          size = withDefaultNs ? size - 1 : size;
          for (int i = 0; i < size; i++) {
              Namespace namespace = new Namespace();
              namespace.setNamespace("namespaceId-" + i);
              namespace.setNamespaceShowName("namespace-" + i);
              list.add(namespace);
          }
          return list;
      }
      
      private Page<ConfigInfo> mockConfigInfo(int total, int size, String namespaceId) {
          Page<ConfigInfo> mockConfigInfo = new Page<>();
          mockConfigInfo.setTotalCount(total);
          mockConfigInfo.setPagesAvailable(size == 0 ? 0 : total / size);
          mockConfigInfo.setPageNumber(1);
          List<ConfigInfo> list = new LinkedList<>();
          for (int i = 0; i < size; i++) {
              ConfigInfo configInfo = new ConfigInfo();
              configInfo.setTenant(namespaceId);
              configInfo.setContent(JacksonUtils.toJson(mockServerVersionInfo(UUID.randomUUID().toString())));
              list.add(configInfo);
          }
          mockConfigInfo.setPageItems(list);
          return mockConfigInfo;
      }
      
      private Page<ConfigInfo> mockConfigInfoWithPagination(int total, int currentPageSize, int pageNumber,
              String namespaceId) {
          Page<ConfigInfo> mockConfigInfo = new Page<>();
          mockConfigInfo.setTotalCount(total);
          mockConfigInfo.setPageNumber(pageNumber);
          mockConfigInfo.setPagesAvailable((int) Math.ceil((double) total / (double) currentPageSize));
          
          List<ConfigInfo> list = new LinkedList<>();
          for (int i = 0; i < currentPageSize; i++) {
              ConfigInfo configInfo = new ConfigInfo();
              configInfo.setTenant(namespaceId);
              configInfo.setContent(JacksonUtils.toJson(mockServerVersionInfo(UUID.randomUUID().toString())));
              list.add(configInfo);
          }
          mockConfigInfo.setPageItems(list);
          return mockConfigInfo;
      }
      
      private McpServerVersionInfo mockServerVersionInfo(String id) {
          McpServerVersionInfo serverVersionInfo = new McpServerVersionInfo();
          serverVersionInfo.setId(id);
          serverVersionInfo.setName("mcpName");
          serverVersionInfo.setLatestPublishedVersion("9.9.9");
          List<ServerVersionDetail> versionDetails = new LinkedList<>();
          versionDetails.add(mockVersion("1.0.0"));
          versionDetails.add(mockVersion("9.9.9"));
          serverVersionInfo.setVersions(versionDetails);
          return serverVersionInfo;
      }
      
      private ServerVersionDetail mockVersion(String version) {
          ServerVersionDetail versionDetail = new ServerVersionDetail();
          versionDetail.setVersion(version);
          return versionDetail;
      }
  }