Merge branch 'fix/Search-1493' into 'master'

Fix/search 1493

See merge request search_discovery/SearchAnalyticsE2ETest!13
This commit is contained in:
Meenal Bhave
2019-02-25 09:38:54 +00:00
2 changed files with 206 additions and 97 deletions
@@ -1,7 +1,7 @@
package org.alfresco.service.search;
/*
* Copyright 2018 Alfresco Software, Ltd. All rights reserved.
* Copyright 2019 Alfresco Software, Ltd. All rights reserved.
* License rights for this program may be obtained from Alfresco Software, Ltd.
* pursuant to a written agreement and any use of this program without such an
* agreement is prohibited.
@@ -13,6 +13,7 @@ import org.alfresco.dataprep.SiteService.Visibility;
import org.alfresco.rest.core.RestProperties;
import org.alfresco.rest.core.RestWrapper;
import org.alfresco.rest.search.RestRequestQueryModel;
import org.alfresco.rest.search.SearchNodeModel;
import org.alfresco.rest.search.SearchRequest;
import org.alfresco.rest.search.SearchResponse;
import org.alfresco.utility.LogFactory;
@@ -39,6 +40,8 @@ import org.testng.annotations.BeforeSuite;
import lombok.Getter;
import static lombok.AccessLevel.PROTECTED;
import java.util.List;
/**
* @author meenal bhave
*/
@@ -102,8 +105,10 @@ public abstract class AbstractSearchServiceE2E extends AbstractTestNGSpringConte
}
@BeforeClass(alwaysRun = true)
public void beforeClass() throws Exception
{
public void dataPreparation() throws Exception
{
serverHealth.assertServerIsOnline();
adminUserModel = dataUser.getAdminUser();
testUser = dataUser.createRandomTestUser("UserSearch");
@@ -223,42 +228,150 @@ public abstract class AbstractSearchServiceE2E extends AbstractTestNGSpringConte
return customModel;
}
protected SearchRequest createQuery(String term)
{
SearchRequest query = new SearchRequest();
RestRequestQueryModel queryReq = new RestRequestQueryModel();
queryReq.setQuery(term);
query.setQuery(queryReq);
return query;
}
/**
*
* Helper method which create an http post request to Search API end point.
* Executes the given search request without throwing checked exceptions (a {@link RuntimeException} will be thrown in case).
* @param query the search request.
* @return {@link SearchResponse} response.
*
*/
protected SearchResponse query(SearchRequest query) throws Exception
{
try
{
return restClient.authenticateUser(testUser).withSearchAPI().search(query);
}
catch (final Exception exception)
{
throw new RuntimeException(exception);
}
}
/**
* Wait for Solr to finish indexing and search to return appropriate results
*
* @param userQuery: Search Query
* @param contentName that's expected to be included / excluded from the results
* @param expectedInResults
* @return true if search returns expected results, i.e. is given content is found or excluded from the results
* @throws Exception
*/
public boolean waitForContent(String userQuery, String contentName, boolean expectedInResults) throws Exception
{
boolean resultAsExpected = false;
boolean found = !expectedInResults;
String expectedStatusCode = HttpStatus.OK.toString();
// Repeat search until the query results are as expected or Search Retry count is hit
for (int searchCount = 1; searchCount <= 6; searchCount++)
{
SearchRequest searchRequest = createQuery(userQuery);
SearchResponse response = query(searchRequest);
if (restClient.getStatusCode().matches(expectedStatusCode))
{
List<SearchNodeModel> entries = response.getEntries();
if (!entries.isEmpty())
{
for (SearchNodeModel entry : entries)
{
found = (contentName.equalsIgnoreCase(entry.getModel().getName()));
}
}
// Loop again if result is not as expected: To cater for solr lag: eventual consistency
resultAsExpected = (expectedInResults == found);
if (resultAsExpected)
{
break;
}
else
{
// Wait for the solr indexing.
Utility.waitToLoopTime(properties.getSolrWaitTimeInSeconds(), "Wait For Indexing. Retry Attempt: " + searchCount);
}
}
else
{
throw new RuntimeException("API returned status code:" + restClient.getStatusCode() + " Expected: " + expectedStatusCode);
}
}
return resultAsExpected;
}
/**
* Wait for Solr to finish indexing: Indexing has caught up = true if search returns appropriate results
*
* @param userQuery: string to search for, unique search string will guarantee accurate results
* @param userQuery: search query, this can include the fieldname, unique search string will guarantee accurate results
* @param expectedInResults, true if entry is expected in the results set
* @return true (indexing is finished) if search returns appropriate results
* @throws Exception
*/
public boolean waitForIndexing(String userQuery, boolean expectedInResults) throws Exception
{
boolean found = false;
// Use the search query as is: fieldname(s) may or may not be specified within the userQuery
return waitForIndexing(null, userQuery, expectedInResults);
}
/**
* waitForIndexing method that matches / waits for filename, metadata to be indexed.
* @param userQuery
* @param expectedInResults
* @return
* @throws Exception
*/
public boolean waitForMetadataIndexing(String userQuery, boolean expectedInResults) throws Exception
{
return waitForIndexing("name", userQuery, expectedInResults);
}
/**
* waitForIndexing method that matches / waits for content to be indexed, this can take longer than metadata indexing.
* Since Metadata is indexed first, use this method where tests, queries need content to be indexed too.
* @param userQuery
* @param expectedInResults
* @return
* @throws Exception
*/
public boolean waitForContentIndexing(String userQuery, boolean expectedInResults) throws Exception
{
return waitForIndexing("cm:content", userQuery, expectedInResults);
}
/**
* Wait for Solr to finish indexing: Indexing has caught up = true if search returns appropriate results
*
* @param fieldName: specific field to search for, e.g. name. When specified, the query will become: name:'userQuery'
* @param userQuery: search string, unique search string will guarantee accurate results
* @param expectedInResults, true if entry is expected in the results set
* @return true (indexing is finished) if search returns appropriate results
* @throws Exception
*/
private boolean waitForIndexing(String fieldName, String userQuery, boolean expectedInResults) throws Exception
{
boolean resultAsExpected = false;
String expectedStatusCode = HttpStatus.OK.toString();
Integer retryCount = 3;
SearchRequest query = new SearchRequest();
RestRequestQueryModel queryReq = new RestRequestQueryModel();
queryReq.setQuery(userQuery);
query.setQuery(queryReq);
String query = (fieldName == null)? userQuery: String.format("%s:'%s'", fieldName, userQuery);
// Repeat search until the query results are as expected or Search Retry count is hit
for (int searchCount = 1; searchCount <= retryCount; searchCount++)
for (int searchCount = 1; searchCount <= 3; searchCount++)
{
// Using adminUser just to confirm that the content is indexed
SearchResponse response = restClient.authenticateUser(dataUser.getAdminUser()).withSearchAPI().search(query);
SearchRequest searchRequest = createQuery(query);
SearchResponse response = query(searchRequest);
if (restClient.getStatusCode().matches(expectedStatusCode))
{
if (response.getEntries().size() >= 1)
{
found = true;
}
else
{
found = false;
}
boolean found = !response.getEntries().isEmpty();
// Loop again if result is not as expected: To cater for solr lag: eventual consistency
resultAsExpected = (expectedInResults == found);
@@ -269,7 +382,7 @@ public abstract class AbstractSearchServiceE2E extends AbstractTestNGSpringConte
else
{
// Wait for the solr indexing.
Utility.waitToLoopTime(properties.getSolrWaitTimeInSeconds(), "Wait For Indexing");
Utility.waitToLoopTime(properties.getSolrWaitTimeInSeconds(), "Wait For Indexing. Retry Attempt: " + searchCount);
}
}
else
@@ -1,5 +1,5 @@
/*
* Copyright 2018 Alfresco Software, Ltd. All rights reserved.
* Copyright 2019 Alfresco Software, Ltd. All rights reserved.
* License rights for this program may be obtained from Alfresco Software, Ltd.
* pursuant to a written agreement and any use of this program without such an
* agreement is prohibited.
@@ -10,119 +10,115 @@ import java.util.HashMap;
import java.util.Map;
import org.alfresco.service.search.AbstractSearchServiceE2E;
import org.alfresco.utility.constants.UserRole;
import org.alfresco.utility.data.DataContent;
import org.alfresco.utility.data.DataSite;
import org.alfresco.utility.model.ContentModel;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FileType;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.TestGroup;
import org.alfresco.utility.model.UserModel;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.enums.VersioningState;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
* Test class tests cascading updates for a child node when parent node is updated
*
* @author Alessandro Benedetti
* @author Meenal Bhave
*/
public class CascadingTrackerIntegrationTest extends AbstractSearchServiceE2E
{
@Autowired
protected DataSite dataSite;
@Autowired
protected DataContent dataContent;
private SiteModel testSite;
private UserModel testUser;
private FolderModel testFolder;
@BeforeClass(alwaysRun = true)
public void setupEnvironment() throws Exception
{
serverHealth.assertServerIsOnline();
testSite = dataSite.createPublicRandomSite();
testUser = dataUser.createRandomTestUser();
dataUser.addUserToSite(testUser, testSite, UserRole.SiteContributor);
}
private FolderModel parentFolder, grandParentFolder, childFolder;
private FileModel childFile, grandChildFile;
@Test(groups = { TestGroup.ASS_13 })
public void testCascadingTracking_parentFolderRenaming_shouldReIndexChildren() throws Exception
@Test(priority = 1, groups = { TestGroup.ASS_13 })
public void testChildPathWhenParentRenamed() throws Exception
{
testFolder = dataContent.usingSite(testSite).usingUser(testUser).createFolder();
// Create Parent folder
parentFolder = dataContent.usingSite(testSite).usingUser(testUser).createFolder();
// Create a file in the parent folder
childFile = FileModel.getRandomFileModel(FileType.TEXT_PLAIN, "custom content");
FileModel customFile = FileModel.getRandomFileModel(FileType.TEXT_PLAIN, "custom content");
Map<String, Object> properties = new HashMap<>();
properties.put(PropertyIds.NAME, customFile.getName());
cmisApi.authenticateUser(testUser).usingSite(testSite).usingResource(testFolder)
.createFile(customFile, properties, VersioningState.MAJOR).assertThat().existsInRepo();
waitForIndexing(customFile.getName(), true);
String parentQuery = "NPATH:\"4/Company Home/Sites/" + testSite.getTitle() + "/documentLibrary/" + testFolder.getName() + "\"";
int initialDescendantCount = query(parentQuery).getPagination().getCount();
properties.put(PropertyIds.NAME, childFile.getName());
properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document");
cmisApi.authenticateUser(testUser).usingSite(testSite).usingResource(parentFolder)
.createFile(childFile, properties, VersioningState.MAJOR).assertThat().existsInRepo();
// Query to find nodes where Path with original folder name matches
String parentQuery = "NPATH:\"4/Company Home/Sites/" + testSite.getTitle() + "/documentLibrary/" + parentFolder.getName() + "\"";
// Rename parent folder
String parentNewName = "parentRenamed";
parentFolder.setName(parentNewName);
ContentModel parentNewNameModel = new ContentModel(parentNewName);
dataContent.usingUser(testUser).usingResource(parentFolder).renameContent(parentNewNameModel);
this.dataContent.usingUser(testUser).usingResource(testFolder).renameContent(parentNewNameModel);
testFolder.setName(parentNewName);
waitForIndexing(testFolder.getName(), true);
waitForMetadataIndexing(parentNewName, true);
// Find nodes where Path with new folder name matches
String parentQueryAfterRename = "NPATH:\"4/Company Home/Sites/" + testSite.getTitle() + "/documentLibrary/" + parentNewName + "\"";
int descendantCountOfDismissedName = query(parentQuery).getPagination().getCount();
Boolean indexingInProgress = !waitForContent(parentQueryAfterRename, childFile.getName(), true);
// Query using new parent name: Expect parent folder and child file
int descendantCountOfNewName = query(parentQueryAfterRename).getPagination().getCount();
Assert.assertEquals(initialDescendantCount, descendantCountOfNewName);
//Assert.assertThat("New renamed path has not the same descendants as before renaming: " + parentQueryAfterRename,descendantCountOfNewName,is(initialDescendantCount));
Assert.assertEquals(descendantCountOfDismissedName, 0);
//Assert.assertThat("Old path still has descendants: " + parentQuery,descendantCountOfDismissedName,is(0));
Assert.assertEquals(descendantCountOfNewName, 2, String.format("Indexing in progress: %s New renamed path has not the same descendants as before renaming: %s", indexingInProgress.toString(), parentQueryAfterRename));
// Query using old parent name: Expect no descendant after rename
int descendantCountOfOriginalName = query(parentQuery).getPagination().getCount();
Assert.assertEquals(descendantCountOfOriginalName, 0, "Old path still has descendants: " + parentQuery);
}
@Test(groups = { TestGroup.ASS_13 })
public void testCascadingTracking_granParentFolderRenaming_shouldReIndexChildren() throws Exception
@Test(priority = 2, groups = { TestGroup.ASS_13 })
public void testGrandChildPathWhenGrandParentRenamed() throws Exception
{
testFolder = dataContent.usingSite(testSite).usingUser(testUser).createFolder();
// Create grand parent folder
grandParentFolder = dataContent.usingSite(testSite).usingUser(testUser).createFolder();
// Create child folder
FolderModel childFolder = dataContent.usingUser(testUser).usingResource(testFolder).createFolder();
childFolder = dataContent.usingUser(testUser).usingResource(grandParentFolder).createFolder();
// Create grandchild file
FileModel customFile = FileModel.getRandomFileModel(FileType.TEXT_PLAIN, "custom content");
// Create grand child file
grandChildFile = FileModel.getRandomFileModel(FileType.TEXT_PLAIN, "custom content");
Map<String, Object> properties = new HashMap<>();
properties.put(PropertyIds.NAME, customFile.getName());
properties.put(PropertyIds.NAME, grandChildFile.getName());
properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document");
cmisApi.authenticateUser(testUser).usingSite(testSite).usingResource(childFolder)
.createFile(customFile, properties, VersioningState.MAJOR).assertThat().existsInRepo();
waitForIndexing(customFile.getName(), true);
String parentQuery = "NPATH:\"4/Company Home/Sites/" + testSite.getTitle() + "/documentLibrary/" + testFolder.getName() + "\"";
int initialDescendantCount = query(parentQuery).getPagination().getCount();
.createFile(grandChildFile, properties, VersioningState.MAJOR).assertThat().existsInRepo();
// Edit grand parent folder name
String granParentNewName = "granParentRenamed";
ContentModel granParentNewNameModel = new ContentModel(granParentNewName);
this.dataContent.usingUser(testUser).usingResource(testFolder).renameContent(granParentNewNameModel);
waitForIndexing(granParentNewName, true);
// Wait for file to be indexed
waitForMetadataIndexing(grandChildFile.getName(), true);
String parentQueryAfterRename = "NPATH:\"4/Company Home/Sites/" + testSite.getTitle() + "/documentLibrary/" + granParentNewName + "\"";
int descendantCountOfDismissedName = query(parentQuery).getPagination().getCount();
// Query to find nodes where Path with original folder name matches
String parentQuery = "NPATH:\"4/Company Home/Sites/" + testSite.getTitle() + "/documentLibrary/" + grandParentFolder.getName() + "\"";
// Rename grand parent folder
String grandParentNewName = "grandParentRenamed";
grandParentFolder.setName(grandParentNewName);
ContentModel grandParentFolderRenamed = new ContentModel(grandParentNewName);
dataContent.usingUser(testUser).usingResource(grandParentFolder).renameContent(grandParentFolderRenamed);
// Find nodes where Path with new folder name matches
String parentQueryAfterRename = "NPATH:\"4/Company Home/Sites/" + testSite.getTitle() + "/documentLibrary/" + grandParentNewName + "\"";
Boolean indexingInProgress = !waitForContent(parentQueryAfterRename, grandChildFile.getName(), true);
// Query using new parent name: Expect grand parent, child folder, grand child file
int descendantCountOfNewName = query(parentQueryAfterRename).getPagination().getCount();
Assert.assertEquals(descendantCountOfNewName, 3, String.format("Indexing in progress: %s New renamed path has not the same descendants as before renaming: %s", indexingInProgress.toString(), parentQueryAfterRename));
Assert.assertEquals(descendantCountOfNewName, initialDescendantCount);
//Assert.assertThat("New renamed path has not the same descendants as before renaming: " + parentQueryAfterRename,descendantCountOfNewName,is(initialDescendantCount));
Assert.assertEquals(descendantCountOfDismissedName, 0);
//Assert.assertThat("Old path still has descendants: " + parentQuery,descendantCountOfDismissedName,is(0));
// Query using old parent name: Expect no descendant after rename
int descendantCountOfOriginalName = query(parentQuery).getPagination().getCount();
Assert.assertEquals(descendantCountOfOriginalName, 0, "Old path still has descendants: " + parentQuery);
}
}