content: package alien4cloud.component.dao;
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertNotNull;
  import static org.junit.Assert.assertTrue;
  
  import java.io.IOException;
  import java.nio.file.Path;
  import java.nio.file.Paths;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import javax.annotation.Resource;
  
  import org.alien4cloud.tosca.model.definitions.CapabilityDefinition;
  import org.alien4cloud.tosca.model.types.NodeType;
  import org.elasticsearch.action.get.GetResponse;
  import org.elasticsearch.index.query.QueryBuilder;
  import org.elasticsearch.index.query.QueryBuilders;
  import org.elasticsearch.mapping.MappingBuilder;
  import org.junit.Before;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  import org.springframework.test.annotation.DirtiesContext;
  import org.springframework.test.annotation.DirtiesContext.ClassMode;
  import org.springframework.test.context.ContextConfiguration;
  import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  
  import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
  import com.fasterxml.jackson.annotation.PropertyAccessor;
  import com.fasterxml.jackson.databind.ObjectMapper;
  
  import alien4cloud.dao.ElasticSearchDAO;
  import alien4cloud.dao.IGenericSearchDAO;
  import alien4cloud.dao.model.FacetedSearchFacet;
  import alien4cloud.dao.model.FacetedSearchResult;
  import alien4cloud.dao.model.FetchContext;
  import alien4cloud.dao.model.GetMultipleDataResult;
  import alien4cloud.exception.IndexingServiceException;
  import alien4cloud.utils.AlienConstants;
  import lombok.extern.slf4j.Slf4j;
  
  @RunWith(SpringJUnit4ClassRunner.class)
  @ContextConfiguration("classpath:application-context-test.xml")
  @DirtiesContext
  @Slf4j
  public class EsDaoPaginatedSearchTest extends AbstractDAOTest {
      private final ObjectMapper jsonMapper = new ObjectMapper();
  
      public EsDaoPaginatedSearchTest() {
          super();
          jsonMapper.setVisibility(PropertyAccessor.ALL, Visibility.PUBLIC_ONLY);
      }
  
      @Resource(name = "alien-es-dao")
      IGenericSearchDAO dao;
  
      List<NodeType> testDataList = new ArrayList<>();
      List<NodeType> jndiTestDataList = new ArrayList<>();
  
      @Override
      @Before
      public void before() throws Exception {
          super.before();
          saveDataToES();
      }
  
      @Test
      public void simpleFindPaginatedTest() throws IOException {
  
          int maxElement;
          int size;
  
          // test simple find all search
          maxElement = getCount(QueryBuilders.matchAllQuery());
          size = 11;
  
          assertTrue(maxElement > 0);
          testSimpleSearchWellPaginated(maxElement, size, null);
  
          // test simple find with filters
          QueryBuilder filter = QueryBuilders.termQuery("capabilities.type", "jndi");
          QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
          queryBuilder = QueryBuilders.boolQuery().filter(filter);
  
          maxElement = getCount(queryBuilder);
          size = 4;
          assertTrue(maxElement > 0);
          Map<String, String[]> filters = new HashMap<String, String[]>();
          filters.put("capabilities.type", new String[] { "jndi" });
          testSimpleSearchWellPaginated(maxElement, size, filters);
  
      }
  
      @Test
      public void textBasedSearchPaginatedTest() throws IndexingServiceException, IOException, InterruptedException {
  
          // text search based
          String searchText = "jndi";
          //int maxElement = getCount(QueryBuilders.matchPhrasePrefixQuery("_all", searchText).maxExpansions(10));
          int maxElement = getCount(QueryBuilders.matchPhrasePrefixQuery("all", searchText).maxExpansions(10));
          int size = 7;
          assertTrue(maxElement > 0);
          testTextBasedSearchWellPaginated(maxElement, size, searchText, null);
  
          // text search based with filters
          QueryBuilder filter = QueryBuilders.termQuery("capabilities.type", "jndi");
          QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
          queryBuilder = QueryBuilders.boolQuery().filter(filter);
          maxElement = getCount(queryBuilder);
          Map<String, String[]> filters = new HashMap<String, String[]>();
          filters.put("capabilities.type", new String[] { "jndi" });
          assertTrue(maxElement > 0);
          testTextBasedSearchWellPaginated(maxElement, size, searchText, filters);
  
          // test when nothing found
          searchText = "pacpac";
          //maxElement = getCount(QueryBuilders.matchPhrasePrefixQuery("_all", searchText).maxExpansions(10));
          maxElement = getCount(QueryBuilders.matchPhrasePrefixQuery("all", searchText).maxExpansions(10));
          assertEquals(0, maxElement);
          GetMultipleDataResult<NodeType> searchResp = dao.search(NodeType.class, searchText, null, 0, size);
          assertNotNull(searchResp);
          assertNotNull(searchResp.getData());
          assertNotNull(searchResp.getTypes());
          assertEquals(0, searchResp.getData().length);
          assertEquals(0, searchResp.getTypes().length);
  
      }
  
      // @Ignore
      @Test
      public void facetedSearchPaginatedTest() throws IndexingServiceException, IOException, InterruptedException {
          String searchText = "jndi";
          //int maxElement = getCount(QueryBuilders.matchPhrasePrefixQuery("_all", searchText).maxExpansions(10));
          int maxElement = getCount(QueryBuilders.matchPhrasePrefixQuery("all", searchText).maxExpansions(10));
          int size = 7;
  
          // simple faceted pagination
          assertTrue(maxElement > 0);
          testFacetedSearchWellPaginated(maxElement, size, searchText, null, null);
  
          // faceted search with filters
          QueryBuilder filter = QueryBuilders.termQuery("capabilities.type", "jndi");
          QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
          queryBuilder = QueryBuilders.boolQuery().filter(filter);
          maxElement = getCount(queryBuilder);
  
          Map<String, String[]> filters = new HashMap<String, String[]>();
          filters.put("capabilities.type", new String[] { "jndi" });
  
          assertTrue(maxElement > 0);
          testFacetedSearchWellPaginated(maxElement, size, searchText, filters, FetchContext.SUMMARY);
  
          // test nothing found
          // test when nothing found
          searchText = "pacpac";
          //maxElement = getCount(QueryBuilders.matchPhrasePrefixQuery("_all", searchText).maxExpansions(10));
          maxElement = getCount(QueryBuilders.matchPhrasePrefixQuery("all", searchText).maxExpansions(10));
          assertEquals(0, maxElement);
          GetMultipleDataResult<NodeType> searchResp = dao.facetedSearch(NodeType.class, searchText, null, null, 0, size);
          assertNotNull(searchResp);
          assertNotNull(searchResp.getData());
          assertNotNull(searchResp.getTypes());
          assertEquals(0, searchResp.getData().length);
          assertEquals(0, searchResp.getTypes().length);
  
      }
  
      private static boolean filterContainsValue(Map<String, String[]> filters, String filterValue) {
          for (String[] values : filters.values()) {
              for (String value : values) {
                  if ((value == null && filterValue == null) || (value != null && value.equals(filterValue))) {
                      return true;
                  }
              }
          }
          return false;
      }
  
      private void testSimpleSearchWellPaginated(int maxElement, int size, Map<String, String[]> filters) throws IOException {
          List<NodeType> expectedDataList = filters != null && filterContainsValue(filters, "jndi") ? new ArrayList<>(jndiTestDataList)
                  : new ArrayList<>(testDataList);
          GetMultipleDataResult<NodeType> searchResp;
          int expectedSize;
          for (int from = 0; from < maxElement; from += size) {
              expectedSize = (maxElement - from) > size ? size : maxElement - from;
              searchResp = dao.find(NodeType.class, filters, from, size);
              assertNotNull(searchResp);
              assertNotNull(searchResp.getTypes());
              assertNotNull(searchResp.getData());
              assertEquals(expectedSize, searchResp.getTypes().length);
              assertEquals(expectedSize, searchResp.getData().length);
  
              // testing the pertinence of returned data
              Object[] data = searchResp.getData();
              for (Object element : data) {
                  NodeType nt = jsonMapper.readValue(jsonMapper.writeValueAsString(element), NodeType.class);
                  assertTrue(expectedDataList.contains(nt));
                  expectedDataList.remove(nt);
              }
          }
  
          // assert the list is empty at the end.
          assertTrue(expectedDataList.isEmpty());
      }
  
      private void testFacetedSearchWellPaginated(int maxElement, int size, String searchText, Map<String, String[]> filters, String fetchContext)
              throws IOException {
          List<NodeType> expectedDataList = new ArrayList<>(jndiTestDataList);
          String facetNameToCheck;
          String factetValueToCheck;
          long expectedFacetCount = 0;
          FacetedSearchResult<?> searchResp;
          int expectedSize = 0;
          boolean facetToCheckExist = false;
          for (int from = 0; from < maxElement; from += size) {
              expectedSize = (maxElement - from) > size ? size : maxElement - from;
              searchResp = dao.facetedSearch(NodeType.class, searchText, filters, fetchContext, from, size);
              assertNotNull(searchResp);
              assertNotNull(searchResp.getTypes());
              assertNotNull(searchResp.getData());
              assertEquals(expectedSize, searchResp.getTypes().length);
              assertEquals(expectedSize, searchResp.getData().length);
  
              // testing the pertinence of returned data
              Object[] data = searchResp.getData();
              for (Object element : data) {
                  NodeType nt = jsonMapper.readValue(jsonMapper.writeValueAsString(element), NodeType.class);
  
                  log.info ("Got " + nt.getElementId() + "/" + nt.getArchiveName() + "/" + nt.getArchiveVersion());
  
                  // TODO assert fetch context result.
                  assertTrue(expectedDataList.contains(nt));
                  expectedDataList.remove(nt);
              }
  
              // test returned facets
              if (filters != null && filters.containsKey("capabilities.type")) {
                  facetNameToCheck = "requirements.type";
                  factetValueToCheck = "network";
                  expectedFacetCount = 6;
              } else {
                  facetNameToCheck = "capabilities.type";
                  factetValueToCheck = "war";
                  expectedFacetCount = 4;
              }
              assertNotNull(searchResp.getFacets());
              assertTrue(!searchResp.getFacets().isEmpty());
              FacetedSearchFacet[] facets = searchResp.getFacets().get(facetNameToCheck);
              assertNotNull(facets);
              long facetCount = 0;
              for (FacetedSearchFacet facet : facets) {
                  if (facet.getFacetValue().equals(factetValueToCheck)) {
                      facetToCheckExist = true;
                      facetCount = facet.getCount();
                  }
              }
  
              assertTrue(facetToCheckExist);
              assertEquals(expectedFacetCount, facetCount);
  
          }
  
          // assert the list is empty at the end.
          assertTrue(expectedDataList.isEmpty());
  
      }
  
      private void testTextBasedSearchWellPaginated(int maxElement, int size, String searchText, Map<String, String[]> filters) throws IOException {
          List<NodeType> expectedDataList = new ArrayList<>(jndiTestDataList);
          GetMultipleDataResult searchResp;
          int expectedSize;
          for (int from = 0; from < maxElement; from += size) {
              expectedSize = (maxElement - from) > size ? size : maxElement - from;
              searchResp = dao.search(NodeType.class, searchText, filters, from, size);
              assertNotNull(searchResp);
              assertNotNull(searchResp.getTypes());
              assertNotNull(searchResp.getData());
              assertEquals(expectedSize, searchResp.getTypes().length);
              assertEquals(expectedSize, searchResp.getData().length);
  
              // testing the pertinence of returned data
              Object[] data = searchResp.getData();
              for (Object element : data) {
                  NodeType nt = jsonMapper.readValue(jsonMapper.writeValueAsString(element), NodeType.class);
                  assertTrue(expectedDataList.contains(nt));
                  expectedDataList.remove(nt);
              }
          }
  
          // assert the list is empty at the end.
          assertTrue(expectedDataList.isEmpty());
      }
  
      private int getCount(QueryBuilder queryBuilder) {
          //return (int) nodeClient.prepareSearch(ElasticSearchDAO.TOSCA_ELEMENT_INDEX).setTypes(MappingBuilder.indexTypeFromClass(NodeType.class)).setSize(0)
          return (int) nodeClient.prepareSearch(MappingBuilder.indexTypeFromClass(NodeType.class)).setSize(0)
                  .setQuery(queryBuilder).execute().actionGet().getHits().getTotalHits();
      }
  
      private void saveDataToES() throws IOException, IndexingServiceException {
          testDataList.clear();
  
          Path path = Paths.get("src/test/resources/nodetypes-faceted-search-result.json");
          FacetedSearchResult res = jsonMapper.readValue(path.toFile(), FacetedSearchResult.class);
          Object[] data = res.getData();
          for (Object element : data) {
              String serializeDatum = jsonMapper.writeValueAsString(element);
              NodeType indexedNodeType = jsonMapper.readValue(serializeDatum, NodeType.class);
              indexedNodeType.setWorkspace(AlienConstants.GLOBAL_WORKSPACE_ID);
              String typeName = MappingBuilder.indexTypeFromClass(NodeType.class);
              dao.save(indexedNodeType);
              assertDocumentExisit(ElasticSearchDAO.TOSCA_ELEMENT_INDEX, typeName, indexedNodeType.getId(), true);
              testDataList.add(indexedNodeType);
              for (CapabilityDefinition capaDef : indexedNodeType.getCapabilities()) {
                  if (capaDef.getType().equals("jndi")) {
                      jndiTestDataList.add(indexedNodeType);
                  }
              }
          }
          refresh();
      }
  
      private void assertDocumentExisit(String indexName, String typeName, String id, boolean expected) {
          //GetResponse response = getDocument(indexName, typeName, id);
          GetResponse response = getDocument(typeName, ElasticSearchDAO.TYPE_NAME, id);
          assertEquals(expected, response.isExists());
          assertEquals(expected, !response.isSourceEmpty());
      }
  
      private GetResponse getDocument(String indexName, String typeName, String id) {
          return nodeClient.prepareGet(indexName, typeName, id).execute().actionGet();
      }
  }
  