commit c543916200a58371e37da58b1b6d1343cefc162f Author: Meenal Bhave Date: Thu Jul 12 17:08:39 2018 +0100 Initial Commit for End to End tests for search Services and Insight Engine diff --git a/e2e-test/.gitignore b/e2e-test/.gitignore new file mode 100644 index 000000000..fba1991bb --- /dev/null +++ b/e2e-test/.gitignore @@ -0,0 +1,32 @@ +/target/ +/test-output/ +/.settings/ +bin/ +report.html +*.log +test-output* +*.class + +# Eclipse +.classpath +.settings +.project + +# Intellij +.idea/ +*.iml +*.iws + +# Mac +.DS_Store + +# Maven +target +*.log +*.log.* + + +# Package Files # +*.jar +*.war +*.ear \ No newline at end of file diff --git a/e2e-test/README.md b/e2e-test/README.md new file mode 100644 index 000000000..25480d516 --- /dev/null +++ b/e2e-test/README.md @@ -0,0 +1,36 @@ +# Setup +The automated test project requires a running instance of ACS Repo and Search Services at the least. +To run Insight Engine tests, minimum version of Repo is ACS 6.0, additionally running instance of Insight Engine would be necessary too. +For more details see: [Search-Discovery] (https://git.alfresco.com/search_discovery). + +# Prerequisites +Java 1.8 + +Maven 3.2.0 + +Alfresco Content Services 5.2.2 or above + +Search Services 1.2.0 or above or Insight Engine 1.0.0 or above + +# Bring the Test Environment up + +1. ACS + Search Services Healthcheck: Please ensure that repo admin console > Search Services uses the right port and shows tracking status. + +2. Solr Healthcheck can be performed using solr admin console: + + AT ://:/solr/# + + e.g. http://localhost:8983/solr/# + +# Compile the project +`mvn clean install -DskipTests` + +# Run the tests +`mvn clean install` + +# Run a specific class of tests +`mvn clean install -Dtest=` + +e.g. + +`mvn clean install -Dtest=org.alfresco.service.search.e2e.insightEngine.sql.CustomModelTest` diff --git a/e2e-test/pom.xml b/e2e-test/pom.xml new file mode 100644 index 000000000..2296ed919 --- /dev/null +++ b/e2e-test/pom.xml @@ -0,0 +1,108 @@ + + 4.0.0 + + org.alfresco + alfresco-super-pom + 8 + + Search-Analytics-e2e-test + Search-Analytics-e2e-test + Search Analytics E2E Tests + Test Project to test Search Service and Analytics Features on a complete setup of Alfresco, Share + + + 1.0.29-SNAPSHOT + 5.2.0-12-SNAPSHOT + 5.2.0-3-SNAPSHOT + 2.6.0 + src/test/resources/SearchSuite.xml + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${suiteXmlFile} + + + ${test.exclude} + + + ${test.include} + + + + + org.apache.maven.plugins + maven-remote-resources-plugin + 1.5 + + + org.alfresco.tas:utility:${tas.utility.version} + + + + + + process + + + + + + + + + org.alfresco.tas + utility + ${tas.utility.version} + + + org.alfresco.tas + restapi-test + ${tas.rest.api.version} + + + org.alfresco.tas + cmis-test + ${tas.cmis.api.version} + + + org.alfresco + alfresco-rm-automation-enterprise-rest-api + ${rm.version} + + + org.alfresco + alfresco-rm-automation-community-rest-api + ${rm.version} + tests + test + + + org.projectlombok + lombok + 1.16.10 + provided + + + io.rest-assured + rest-assured + 3.0.6 + + + \ No newline at end of file diff --git a/e2e-test/src/main/resources/model/finance-model.xml b/e2e-test/src/main/resources/model/finance-model.xml new file mode 100644 index 000000000..726c885f9 --- /dev/null +++ b/e2e-test/src/main/resources/model/finance-model.xml @@ -0,0 +1,105 @@ + + + Administrator + + + + + + + + + + + + Receipt + cm:content + + + ReceiptValue + d:double + false + + TRUE + true + + + + ReceiptNo + d:long + false + + TRUE + + + + + + + + + Expense + cm:content + + + No + d:long + false + + TRUE + true + + + + Emp + d:text + false + + TRUE + true + + + + Amount + d:double + false + + TRUE + true + + + + Title + d:text + false + + TRUE + false + + + + + + + + + + + Parking Expense + + + Location + d:text + true + + TRUE + true + + + + + + + + + \ No newline at end of file diff --git a/e2e-test/src/main/resources/model/music-model.xml b/e2e-test/src/main/resources/model/music-model.xml new file mode 100644 index 000000000..9406e7e78 --- /dev/null +++ b/e2e-test/src/main/resources/model/music-model.xml @@ -0,0 +1,107 @@ + + + Administrator + + + + + + + + + + + + song + cm:content + + + lyricist + d:text + false + + TRUE + true + + + + artist-singer-male + d:text + false + + TRUE + false + + + + genre + d:text + false + false + + BOTH + true + + + + lyrics + d:mltext + false + + TRUE + false + + + + popular + d:boolean + false + false + + TRUE + + + + artist-singer-female + d:text + false + + TRUE + false + + + + + + + + + + + AudioVisual + + + video + d:boolean + false + false + + TRUE + + + + audio + d:boolean + false + true + + TRUE + + + + + + + + + \ No newline at end of file diff --git a/e2e-test/src/test/java/org/alfresco/service/search/AbstractSearchServiceE2E.java b/e2e-test/src/test/java/org/alfresco/service/search/AbstractSearchServiceE2E.java new file mode 100644 index 000000000..543a0fd0d --- /dev/null +++ b/e2e-test/src/test/java/org/alfresco/service/search/AbstractSearchServiceE2E.java @@ -0,0 +1,151 @@ +package org.alfresco.service.search; + +/* + * Copyright 2018 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. + */ + +import static lombok.AccessLevel.PROTECTED; + + +import javax.naming.AuthenticationException; + +import org.alfresco.cmis.CmisWrapper; +import org.alfresco.dataprep.ContentService; +import org.alfresco.rest.core.RestProperties; +import org.alfresco.rest.core.RestWrapper; +import org.alfresco.rest.search.RestRequestQueryModel; +import org.alfresco.rest.search.SearchRequest; +import org.alfresco.rest.search.SearchResponse; +import org.alfresco.utility.LogFactory; +import org.alfresco.utility.TasProperties; +import org.alfresco.utility.Utility; +import org.alfresco.utility.data.DataContent; +import org.alfresco.utility.data.DataSite; +import org.alfresco.utility.data.DataUser; +import org.alfresco.utility.network.ServerHealth; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; +import org.testng.annotations.BeforeSuite; + +import lombok.Getter; + +/** + * @author meenal bhave + */ + +@ContextConfiguration("classpath:alfresco-search-e2e-context.xml") +public abstract class AbstractSearchServiceE2E extends AbstractTestNGSpringContextTests +{ + private static Logger LOG = LogFactory.getLogger(); + + @Autowired + protected RestProperties restProperties; + + @Autowired + protected TasProperties properties; + + @Autowired + protected ServerHealth serverHealth; + + @Autowired + protected DataSite dataSite; + + @Autowired + protected DataContent dataContent; + + @Autowired + protected RestWrapper restClient; + + @Autowired + protected CmisWrapper cmisApi; + + @Autowired + @Getter(value = PROTECTED) + protected DataUser dataUser; + + @Autowired + @Getter(value = PROTECTED) + private ContentService contentService; + + public static final String NODE_PREFIX = "workspace/SpacesStore/"; + + @BeforeSuite(alwaysRun = true) + public void beforeSuite() throws Exception + { + super.springTestContextPrepareTestInstance(); + + try + { + dataContent.usingAdmin().deployContentModel("model/music-model.xml"); + dataContent.usingAdmin().deployContentModel("model/finance-model.xml"); + } + catch (Exception e) + { + LOG.warn("Error Loading Custom Model", e); + } + } + + /** + * 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 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; + Boolean resultAsExpected = false; + String expectedStatusCode = HttpStatus.OK.toString(); + + SearchRequest query = new SearchRequest(); + RestRequestQueryModel queryReq = new RestRequestQueryModel(); + queryReq.setQuery(userQuery); + query.setQuery(queryReq); + + // Using adminUser just to confirm that the content is indexed + SearchResponse response = restClient.authenticateUser(dataUser.getAdminUser()).withSearchAPI().search(query); + + // Repeat search until the query results are as expected or Search Retry count is hit + for (int searchCount = 1; searchCount <= 3; searchCount++) + { + if (searchCount > 1) + { + // Wait for the solr indexing. + Utility.waitToLoopTime(properties.getSolrWaitTimeInSeconds(), "Wait For Indexing"); + } + + if (restClient.getStatusCode().matches(expectedStatusCode)) + { + if (response.getEntries().size() >= 1) + { + found = true; + } + else + { + found = false; + } + + // Loop again if result is not as expected: To cater for solr lag: eventual consistency + resultAsExpected = (expectedInResults.equals(found)); + if (resultAsExpected) + { + break; + } + } + else + { + throw new AuthenticationException("API returned status code:" + restClient.getStatusCode() + " Expected: " + expectedStatusCode); + } + } + + return resultAsExpected; + } +} diff --git a/e2e-test/src/test/java/org/alfresco/service/search/e2e/insightEngine/sql/CustomModelTest.java b/e2e-test/src/test/java/org/alfresco/service/search/e2e/insightEngine/sql/CustomModelTest.java new file mode 100644 index 000000000..6c905e536 --- /dev/null +++ b/e2e-test/src/test/java/org/alfresco/service/search/e2e/insightEngine/sql/CustomModelTest.java @@ -0,0 +1,113 @@ +/* + * Copyright 2018 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. + */ + +package org.alfresco.service.search.e2e.insightEngine.sql; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.rest.core.RestResponse; +import org.alfresco.rest.search.SearchSqlRequest; +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.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.hamcrest.Matchers; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * Purpose of this TestClass is to test that the SQL end point works as expected with CustomModels + * + * @author meenal bhave + */ + +public class CustomModelTest extends AbstractSearchServiceE2E +{ + @Autowired + protected DataSite dataSite; + + @Autowired + protected DataContent dataContent; + + protected SiteModel testSite; + + private UserModel testUser; + + private FolderModel testFolder; + + @BeforeClass(alwaysRun = true) + public void setupEnvironment() throws Exception + { + serverHealth.assertServerIsOnline(); + + testSite = dataSite.createPublicRandomSite(); + + // Create test user and add the user as a SiteContributor + testUser = dataUser.createRandomTestUser(); + + dataUser.addUserToSite(testUser, testSite, UserRole.SiteContributor); + + testFolder = dataContent.usingSite(testSite).usingUser(testUser).createFolder(); + } + + // Content of Custom Type is added to the Repo + @Test(priority = 1, groups = { TestGroup.INSIGHT_10 }) + public void testSqlListCustomFields() throws Exception + { + Long uniqueRef = System.currentTimeMillis(); + FileModel customFile = FileModel.getRandomFileModel(FileType.TEXT_PLAIN, "custom content"); + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, "D:finance:Receipt"); + properties.put(PropertyIds.NAME, customFile.getName()); + properties.put("finance:ReceiptNo", uniqueRef); + properties.put("finance:ReceiptValue", 30); + + cmisApi.authenticateUser(testUser).usingSite(testSite).usingResource(testFolder) + .createFile(customFile, properties, VersioningState.MAJOR).assertThat().existsInRepo(); + + // Wait for the file to be indexed + waitForIndexing(customFile.getName(), true); + + // Select distinct site: json format + SearchSqlRequest sqlRequest = new SearchSqlRequest(); + sqlRequest.setSql("select finance_ReceiptNo, finance_ReceiptValue from alfresco where finance_ReceiptNo = " + uniqueRef); + sqlRequest.setLimit(10); + + RestResponse response = restClient.authenticateUser(testUser).withSearchSqlAPI().searchSql(sqlRequest); + + restClient.assertStatusCodeIs(HttpStatus.OK); + response.assertThat().body("list.pagination.maxItems", Matchers.equalTo(10)); + response.assertThat().body("list.pagination.count", Matchers.equalTo(1)); + restClient.onResponse().assertThat().body("list.entries.entry[0][0].label", Matchers.equalToIgnoringCase("finance_ReceiptValue")); + restClient.onResponse().assertThat().body("list.entries.entry[0][1].label", Matchers.equalToIgnoringCase("finance_ReceiptNo")); + + restClient.onResponse().assertThat().body("list.entries.entry[0][0].value", Matchers.equalToIgnoringCase("30.0")); + restClient.onResponse().assertThat().body("list.entries.entry[0][1].value", Matchers.equalTo(uniqueRef.toString())); + + // Select distinct cm_name: solr format + sqlRequest = new SearchSqlRequest(); + sqlRequest.setSql("select finance_ReceiptNo, finance_ReceiptValue from alfresco where finance_ReceiptNo = " + uniqueRef + " limit 10"); + sqlRequest.setFormat("solr"); + + response = restClient.authenticateUser(testUser).withSearchSqlAPI().searchSql(sqlRequest); + + restClient.assertStatusCodeIs(HttpStatus.OK); + restClient.onResponse().assertThat().body("result-set.docs[0].finance_ReceiptValue", Matchers.equalTo(30)); + restClient.onResponse().assertThat().body("result-set.docs[0].finance_ReceiptNo", Matchers.equalTo(uniqueRef)); + } +} diff --git a/e2e-test/src/test/java/org/alfresco/service/search/unit/SetupTest.java b/e2e-test/src/test/java/org/alfresco/service/search/unit/SetupTest.java new file mode 100644 index 000000000..82d33999e --- /dev/null +++ b/e2e-test/src/test/java/org/alfresco/service/search/unit/SetupTest.java @@ -0,0 +1,158 @@ +/* + * Copyright 2018 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. + */ + +package org.alfresco.service.search.unit; + +import java.sql.ResultSet; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.dataprep.SiteService.Visibility; +import org.alfresco.rest.core.RestResponse; +import org.alfresco.rest.search.SearchSqlJDBCRequest; +import org.alfresco.rest.search.SearchSqlRequest; +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.data.RandomData; +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.UserModel; +import org.apache.chemistry.opencmis.commons.PropertyIds; +import org.apache.chemistry.opencmis.commons.enums.VersioningState; +import org.junit.Assert; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * Purpose of this Test is to test that the dependencies have been wired correctly and work well + * + * @author meenal bhave + */ +public class SetupTest extends AbstractSearchServiceE2E +{ + @Autowired + protected DataSite dataSite; + + @Autowired + protected DataContent dataContent; + + protected SiteModel testSite; + + protected FolderModel targetFolder; + + private UserModel testUser; + + private FolderModel testFolder; + + @BeforeClass(alwaysRun = true) + public void setUp() throws Exception + { + serverHealth.assertServerIsOnline(); + + testSite = dataSite.createPublicRandomSite(); + targetFolder = dataContent.usingSite(testSite).createFolder(FolderModel.getRandomFolderModel()); + + // Create test user and add the user as a SiteContributor + testUser = dataUser.createRandomTestUser(); + + dataUser.addUserToSite(testUser, testSite, UserRole.SiteContributor); + + testFolder = dataContent.usingSite(testSite).usingUser(testUser).createFolder(); + } + + // Test CMIS API works + @Test(priority = 1) + public void testCMISFileCreation() throws Exception + { + // Create document in a folder in a collaboration site + FileModel testFile = new FileModel("TestFile"); + cmisApi.authenticateUser(testUser).usingSite(testSite).usingResource(testFolder).createFile(testFile).assertThat().existsInRepo(); + } + + // Test Custom Model: Music can be used + @Test(priority = 2) + public void testModelMusicCanBeUsed() throws Exception + { + // Create document of custom type + FileModel customFile = FileModel.getRandomFileModel(FileType.TEXT_PLAIN, "searchContent-music"); + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, "D:music:song"); + properties.put(PropertyIds.NAME, customFile.getName()); + properties.put("music:genre", "pop"); + properties.put("music:lyricist", "Me"); + + cmisApi.authenticateUser(testUser).usingSite(testSite).usingResource(testFolder).createFile(customFile, properties, VersioningState.MAJOR).assertThat() + .existsInRepo(); + } + + // Test Custom Model: Finance can be used + @Test(priority = 3) + public void testModelFinanceCanBeUsed() throws Exception + { + // Create document of custom type + + FileModel customFile = FileModel.getRandomFileModel(FileType.TEXT_PLAIN, "searchContent-finance"); + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, "D:finance:Receipt"); + properties.put(PropertyIds.NAME, customFile.getName()); + properties.put("finance:ReceiptNo", 1); + properties.put("finance:ReceiptValue", 30); + + cmisApi.authenticateUser(testUser).usingSite(testSite).usingResource(testFolder).createFile(customFile, properties, VersioningState.MAJOR).assertThat() + .existsInRepo(); + + waitForIndexing(customFile.getName(), true); + } + + // Test sql API can be used + @Test(priority = 3) + public void testSQLAPICanBeUsed() throws Exception + { + // Select distinct site: json format + SearchSqlRequest sqlRequest = new SearchSqlRequest(); + sqlRequest.setSql("select cm_name from alfresco where finance_ReceiptValue > 0"); + sqlRequest.setLimit(10); + + RestResponse response = restClient.authenticateUser(testUser).withSearchSqlAPI().searchSql(sqlRequest); + + restClient.assertStatusCodeIs(HttpStatus.OK); + Assert.assertNotNull(response); + } + + // Test sql can be executed via jdbc + @Test(priority = 4) + public void testSQLViaJDBC() throws Exception + { + SiteModel publicSite = new SiteModel(RandomData.getRandomName("SiteSearch")); + publicSite.setVisibility(Visibility.PUBLIC); + + publicSite = dataSite.usingUser(testUser).createSite(publicSite); + + String sql = "select SITE,CM_OWNER from alfresco where SITE ='" + publicSite.getTitle() + "' group by SITE,CM_OWNER"; + SearchSqlJDBCRequest sqlRequest = new SearchSqlJDBCRequest(); + sqlRequest.setSql(sql); + sqlRequest.setAuthUser(testUser); + + ResultSet rs = restClient.withSearchSqlViaJDBC().executeQueryViaJDBC(sqlRequest); + + while (rs.next()) + { + // User can see the Public Site created by other user + Assert.assertNotNull(rs.getString("SITE")); + Assert.assertTrue(publicSite.getTitle().equalsIgnoreCase(rs.getString("SITE"))); + + Assert.assertNotNull(rs.getString("CM_OWNER")); + Assert.assertTrue(rs.getString("CM_OWNER").contains(testUser.getUsername())); + } + } +} diff --git a/e2e-test/src/test/resources/SearchSuite.xml b/e2e-test/src/test/resources/SearchSuite.xml new file mode 100644 index 000000000..fd33aea3d --- /dev/null +++ b/e2e-test/src/test/resources/SearchSuite.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/e2e-test/src/test/resources/alfresco-search-e2e-context.xml b/e2e-test/src/test/resources/alfresco-search-e2e-context.xml new file mode 100644 index 000000000..7f62a62bc --- /dev/null +++ b/e2e-test/src/test/resources/alfresco-search-e2e-context.xml @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/e2e-test/src/test/resources/config.properties b/e2e-test/src/test/resources/config.properties new file mode 100644 index 000000000..858a868f7 --- /dev/null +++ b/e2e-test/src/test/resources/config.properties @@ -0,0 +1,30 @@ +# Alfresco HTTP Server Settings +alfresco.scheme=http +alfresco.server=localhost +alfresco.port=8080 + +# sync service related +sync.scheme=http +sync.server=localhost +sync.port=9090 + +#CMIS Related: Set CMIS binding to 'browser' or 'atom' +cmis.binding=browser +cmis.basePath=/alfresco/api/-default-/public/cmis/versions/1.1/${cmis.binding} + +#Solr Indexing Time +solrWaitTimeInSeconds=20 + +#RM Specific +rest.rmPath=alfresco/api/-default-/public/gs/versions/1 + +# Administrator Credentials +admin.user=admin +admin.password=admin + +jmx.useJolokiaAgent=false + +# The location of the reports path +reports.path=./target/reports + +serverHealth.showTenants=false \ No newline at end of file diff --git a/e2e-test/src/test/resources/log4j.properties b/e2e-test/src/test/resources/log4j.properties new file mode 100644 index 000000000..a7c0104f0 --- /dev/null +++ b/e2e-test/src/test/resources/log4j.properties @@ -0,0 +1,15 @@ +# Root logger option +log4j.rootLogger=INFO, file, stdout + +# Direct log messages to a log file +log4j.appender.file=org.apache.log4j.RollingFileAppender +log4j.appender.file.File=./reports/search-analytics-e2e.log +log4j.appender.file.MaxBackupIndex=10 +log4j.appender.file.layout=org.apache.log4j.PatternLayout +log4j.appender.file.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n + +# Direct log messages to stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n \ No newline at end of file