diff --git a/packaging/tests/tas-cmis/.gitignore b/packaging/tests/tas-cmis/.gitignore index a1c2a238a9..5bcc0634bd 100644 --- a/packaging/tests/tas-cmis/.gitignore +++ b/packaging/tests/tas-cmis/.gitignore @@ -1,23 +1,37 @@ -# Compiled class file *.class -# Log file -*.log +# Eclipse +.classpath +.settings +.project -# BlueJ files -*.ctxt +# Intellij +.idea/ +*.iml +*.iws + +# Mac +.DS_Store + +# Maven +target +*.log +*.log.* # Mobile Tools for Java (J2ME) -.mtj.tmp/ + +.mtj +.tmp/ # Package Files # + *.jar *.war -*.nar *.ear -*.zip -*.tar.gz -*.rar # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml + hs_err_pid* +alf_data +/src/main/resources/alfresco-global.properties +/src/main/resources/alfresco/extension/custom-log4j.properties diff --git a/packaging/tests/tas-cmis/.travis.settings.xml b/packaging/tests/tas-cmis/.travis.settings.xml new file mode 100644 index 0000000000..e8f1196935 --- /dev/null +++ b/packaging/tests/tas-cmis/.travis.settings.xml @@ -0,0 +1,10 @@ + + + + + alfresco-public + ${env.MAVEN_USERNAME} + ${env.MAVEN_PASSWORD} + + + diff --git a/packaging/tests/tas-cmis/.travis.yml b/packaging/tests/tas-cmis/.travis.yml new file mode 100644 index 0000000000..cd4f735fde --- /dev/null +++ b/packaging/tests/tas-cmis/.travis.yml @@ -0,0 +1,37 @@ +dist: trusty +sudo: required +language: java +jdk: + - openjdk11 + +cache: + directories: + - $HOME/.m2/repository + +branches: + only: + - master + +install: travis_retry mvn install -DskipTests=true -B -V + +stages: + - test + - release + +jobs: + include: + - stage: test + name: "Build and test" + script: travis_retry mvn test + - stage: release + name: "Push to Nexus" + if: fork = false AND branch = master AND type != pull_request AND commit_message !~ /\[no-release\]/ + before_install: + - "cp .travis.settings.xml $HOME/.m2/settings.xml" + script: + # Use full history for release + - git checkout -B "${TRAVIS_BRANCH}" + # Add email to link commits to user + - git config user.email "${GIT_EMAIL}" + # Skip building of release commits + - mvn --batch-mode -DscmCommentPrefix="[maven-release-plugin][skip ci] " -Dusername="${GIT_USERNAME}" -Dpassword="${GIT_PASSWORD}" -DskipTests -Darguments=-DskipTests release:clean release:prepare release:perform diff --git a/packaging/tests/tas-cmis/CODE_OF_CONDUCT.md b/packaging/tests/tas-cmis/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..2ff9dbad09 --- /dev/null +++ b/packaging/tests/tas-cmis/CODE_OF_CONDUCT.md @@ -0,0 +1,13 @@ +# Contributor Code of Conduct + +As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. + +Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. + +This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) \ No newline at end of file diff --git a/packaging/tests/tas-cmis/README.md b/packaging/tests/tas-cmis/README.md index b6692266a6..aa099eab6a 100644 --- a/packaging/tests/tas-cmis/README.md +++ b/packaging/tests/tas-cmis/README.md @@ -1 +1,495 @@ -# alfresco-tas-cmis \ No newline at end of file +![in progress](https://img.shields.io/badge/Document_Level-In_Progress-yellow.svg?style=flat-square) + +:paw_prints: Back to [TAS Master Documentation](https://gitlab.alfresco.com/tas/documentation/wikis/home) + +--- +## Table of Contents +* [Synopsis](#synopsis) +* [Prerequisite](#prerequisite) +* [Installation](#installation-if-you-want-to-contribute) +* [Package Presentation](#package-presentation) +* [Sample Usage](#sample-usage) + * [How to write a test](#how-to-write-a-test) + * [How to run tests?](#how-to-run-tests) + * [from IDE](#from-ide) + * [from command line](#from-command-line) + * [Perform CMIS Queries](#perform-cmis-queries) +* [Listeners](#listeners) +* [Test Results](#test-results) +* [Test Rail Integration](#test-rail-integration) + * [Configuration](#configuration) + * [How to enable Test Rail Integration?](#how-to-enable-test-rail-integration) +* [Change Log](docs/CHANGELOG.md) :glowing_star: +* [Reference](#reference) +* [Releasing](#releasing) +* [Contributors](#contributors) +* [License](#license) + +## Synopsis + +**TAS**( **T**est **A**utomation **S**ystem)- **CMIS** is the project that handles the automated tests related only to CMIS API integrated with Alfresco One [Alfresco CMIS API](http://docs.alfresco.com/5.1/pra/1/topics/cmis-welcome.html). + +It is based on Apache Maven, compatible with major IDEs and is using also Spring capabilities for dependency injection. + +As a high level overview, this project makes use of the following functionality useful in automation testing as: +* reading/defining test environment settings (e.g. alfresco server details, authentication, etc.) +* managing resource (i.e. creating files and folders) +* test data generators (for site, users, content, etc) +* helpers (i.e. randomizers, test environment information) +* test logging generated on runtime and test reporting capabilities +* test management tool integration (at this point we support integration with [Test Rail](https://alfresco.testrail.net) (v5.2.1) +* health checks (verify if server is reachable, if server is online) +* generic Internal-DSL (Domain Specific Language) + +Using Nexus -Release Repository, everyone will be able to use individual interfaces in their projects by extending the automation core functionalities. + +**[Back to Top ^](#table-of-contents)** + +## Prerequisite +(tested on unix/non-unix distribution) +* [Java SE 1.8](http://www.oracle.com/technetwork/java/javase/downloads/index.html). +* [Maven 3.3](https://maven.apache.org/download.cgi) installed and configure according to [Windows OS](https://maven.apache.org/guides/getting-started/windows-prerequisites.html) or [Mac OS](https://maven.apache.org/install.html). +* Configure Maven to use Alfresco alfresco-internal repository following this [Guide](https://ts.alfresco.com/share/page/site/eng/wiki-page?title=Maven_Setup). +* Your favorite IDE as [Eclipse](https://eclipse.org/downloads/) or [IntelliJ](https://www.jetbrains.com/idea). +* Access to [Nexus](https://nexus.alfresco.com/nexus/) repository. +* Access to GitLab [TAS](https://gitlab.alfresco.com/tas/) repository. +* GitLab client for your operating system. (we recommend [SourceTree](https://www.sourcetreeapp.com) - use your google account for initial setup). +* Getting familiar with [Basic Git Commands](http://docs.gitlab.com/ee/gitlab-basics/basic-git-commands.html). +* Getting familiar with [Maven](https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html). +* Getting familiar with [Spring](http://docs.spring.io). +* Getting familiar with [TestNG](http://testng.org/doc/index.html) + +**[Back to Top ^](#table-of-contents)** + +## Installation (if you want to contribute) + +* Open your GitLab client and clone the repository of this project. +* You can do this also from command line (or in your terminal) adding: + +```bash +$ git clone https://gitlab.alfresco.com/tas/alfresco-tas-cmis-test.git +# this clone will have the latest changes from repository. If you want to checkout a specific version released, take a look at the [Change Log](docs/CHANGELOG.md) page +$ cd alfresco-tas-cmis-test +# this command will checkout the remove v1.0.0 tagged repository and create locally a new branch v1.0.0 +$ git checkout tags/v1.0.0 -b v1.0.0 +``` + +* Install and check if all dependencies are downloaded + +```bash +$ mvn clean install -DskipTests +# you should see one [INFO] BUILD SUCCESS message displayed +``` +**[Back to Top ^](#table-of-contents)** + +## Package Presentation + +The project uses a maven layout [archetype](https://maven.apache.org/plugins-archives/maven-archetype-plugin-1.0-alpha-7/examples/simple.html): +```ruby +├── pom.xml +├── src +│   ├── main +│   │   └── java +│   │   └── org +│   │   └── alfresco +│   │   └── cmis +│   │   ├── (...) +│   │   ├── CmisProperties.java #handles all properties from default.properties +│   │   ├── CmisWrapper.java #wrapper around CMIS API +│   │   └── exception +│   │   └── (...) +│   ├── test +│   │   ├── java +│   │   │   └── org +│   │   │   └── alfresco +│   │   │   └── cmis +│   │   │   ├── CmisDemoTests.java #demo example +│   │   │   └── CmisTest.java #abstract base class that should be inherited by all tests +│   │   └── resources +│   │   ├── alfresco-cmis-context.xml #spring configuration +│   │   ├── default.properties #all settings related to environment, protocol +│   │   ├── log4j.properties +│   │   └── sanity-cmis.xml # default suite of tests +``` + +**[Back to Top ^](#table-of-contents)** + +## Sample Usage + +Following the standard layout for Maven projects, the application sources locate in src/main/java and test sources locate in src/test/java. +Application sources consist in defining the CMIS object that simulates the API calls. +The tests are based on an abstract object: CmisTest.java that handles the common behavior: checking the health status of the test server, configuration settings, getting the general properties, etc. + +Please take a look at [CmisDemoTests.java](src/test/java/org/alfresco/cmis/CmisDemoTests.java) class for an example. + +Common configuration settings required for this project are stored in properties file, see [default.properties](src/test/resources/default.properties). +Please analyze and update it accordingly with Alfresco test server IP, port, credentials, etc. + +Example: +```java +# Alfresco HTTP Server Settings +alfresco.scheme=http +alfresco.server= +alfresco.port= +``` + +* optional update the logging level in [log4j](src/test/resources/log4j.properties) file (you can increase/decrease the deails of the [logging file](https://logging.apache.org/log4j/1.2/manual.html), setting the ```log4j.rootLogger=DEBUG``` if you want.) +* go to [running](#how-to-run-tests) section for more information on how to run this tests. + +**[Back to Top ^](#table-of-contents)** + +### How to write a test + +* Tests are organized in java classes and located on src/test/java as per maven layout. +* One test class should contain the tests that cover one functionality as we want to have a clear separation of test scope: tests for sanity/core/full, tests that verify manage of folder/files etc. +* These are the conventions that need to follow when you write a test: + * The test class has @Test annotation with the group defined: protocols, cmis. You can add more groups like sanity, regression + + ```java + @Test(groups={ "sanity"} + ``` + + * The test has @TestRail annotation in order to assure that the details and results will be submitted on TestRail. The fields for TestRail annotation will be explained on next chapter. + + + ```java + @TestRail(section = { "cmis-api" }, executionType=ExecutionType.SANITY, + description = "Verify admin user creates folder in DocumentLibrary with CMIS") + public void adminShouldCreateFolderInSite() throws Exception + { cmisApi.usingSite(testSite).createFolder(testFolder).assertExistsInRepo(); } + + ``` + + * Use Spring capabilities to initialize the objects(Models, Wrappers) with @Autowired + * We followed Builder pattern to develop specific DSL for simple and clear usage of protocol client in test: + + ```java + cmisApi.usingSite(testSite) .createFolder(testFolder) .assertExistsInRepo(); + ``` + * To view a simple class that is using this utility, just browse on [CmisDemoTests.java](src/test/java/org/alfresco/cmis/CmisDemoTests.java) + Notice the class definition and inheritance value: + + ```java + public class CmisDemoTests extends CmisTest + ``` + + * as a convention, before running your test, check if the test environment is reachable and your alfresco test server is online. + (this will stop the test if the server defined in your property file is not healthy - method available in parent class) + + ```java + @BeforeClass(alwaysRun = true) + public void setupCmisTest() throws Exception { + serverHealth.assertServerIsOnline(); + } + ``` + * the test name are self explanatory: + + ```java + @TestRail(section = { "cmis-api" }, executionType=ExecutionType.SANITY, description = "Verify admin user creates folder in DocumentLibrary with CMIS") + public void adminShouldCreateFolderInSite() throws Exception + { + cmisApi.usingSite(testSite) + .createFolder(testFolder) + .assertExistsInRepo(); + } + ``` + + ```java + @TestRail(section = { "cmis-api" }, executionType=ExecutionType.SANITY, description = "Verify admin user creates and renames folder in DocumentLibrary with CMIS") + public void adminShouldRenameFolderInSite() throws Exception + { + cmisApi.usingSite(testSite).createFolder(testFolder) + .and().rename("renamed") + .assertExistsInRepo(); + } + ``` + +**[Back to Top ^](#table-of-contents)** + +### How to run tests + +#### from IDE + +* The project can be imported into a development environment tool (Eclipse or IntelliJ). You have the possibility to execute tests or suite of tests using [TestNG plugin](http://testng.org/doc/eclipse.html) previously installed in IDE. + From Eclipse, just right click on the testNG class (something similar to [CmisDemoTests.java](src/test/java/org/alfresco/cmis/CmisDemoTests.java)), select Run As - TestNG Test + You should see your test passed. + +* In case you are using the default settings that points to localhost (127.0.0.1) and you don't have Alfresco installed on your machine, you will see one exception thrown (as expected): + ```java + org.alfresco.utility.exception.ServerUnreachableException: Server {127.0.0.1} is unreachable. + ``` + +#### from command line + +* In terminal or CMD, navigate (with CD) to root folder of your project (you can use the sample project): + + + + The tests can be executed on command line/terminal using Maven command + + ```bash + mvn test + ``` + + This command with trigger the tests specified in the default testNG suite from POM file: src/main/resources/shared-resources/cmis-suites.xml + + You can use -Dtest parameter to run the test/suites through command line (http://maven.apache.org/surefire/maven-surefire-plugin/examples/single-test.html). + + You can also specify a different suiteXMLFile like: + + ```bash + mvn test -DsuiteXmlFile=src/resources/your-custom-suite.xml + ``` + + Or even a single test: + + ```bash + mvn test -Dtest=org.alfresco.cmis.CmisDemoTests + ``` + But pay attention that you will not have enabled all the [listeners](#listeners) in this case (the Reporting listener or TestRail integration one) + +### Perform CMIS Queries +(:glowing_star: please notice that at this point we assert only the results count returned by the query: we plan to extend the functionality to assert on QueryResult iterable objects also: simple modification on [QueryExecutor.java](src/main/java/org/alfresco/cmis/dsl/QueryExecutor.java) + +There are a couple of ways to test the results count after performing CMIS queries, choose the one that you like the most: + +a) direct queries using a simple TestNG test: + +(see example [here](src/test/java/org/alfresco/cmis/search/SorlSearchSimpleQueryTests.java)) +```java +public class SorlSearchSimpleQueryTests extends CmisTest +{ + @Test + public void simpleQueryOnFolderDesc() throws Exception + { + // create here multiple folder as data preparation + cmisApi.authenticateUser(dataUser.getAdminUser()) + .withQuery("SELECT * FROM cmis:folder ORDER BY cmis:createdBy DESC").assertResultsCount().isLowerThan(101); + } +} +``` +- just extend CmisTest +- authenticate with your UserModel and perform the query. The DSL will allow you to assert the result count if is equal, lower or greater than to a particular value. You can update the methods in [QueryResultAssertion](src/main/java/org/alfresco/cmis/dsl/QueryExecutor.java) class. + +b) define one set of test data (folders, files, etc. ) that you will search in all tests then execute all CMIS queris from one common XML file +- see test class [SolrSearchInFolderTests](src/test/java/org/alfresco/cmis/search/SolrSearchInFolderTests.java) +- see [XML test data](src/main/resources/shared-resources/testdata/search-in-folder.xml) used in [SolrSearchInFolderTests](src/test/java/org/alfresco/cmis/search/SolrSearchInFolderTests.java) into one DataProvider. Notice that XML file has two parameter: the query that will be executed and the expected result count returned. + +c) define test data (user, sites, folder, files, aspects, comments, custom models, etc) all into one XML file with all cmis queries related. +- see example on [SolrSearchByIdTests](https://gitlab.alfresco.com/tas/alfresco-tas-cmis-test/blob/master/src/test/java/org/alfresco/cmis/search/SolrSearchByIdTests.java) +- notice the 'NODE_REF[x]'; 'NODE_REF[y]' keywords that will dynamically take the test data identified by id: x, y (you will figure it out based on examples). + +**Info**: all search test queries are found [org.alfresco.cmis.search](src/test/java/org/alfresco/cmis/search) package. + +**[Back to Top ^](#table-of-contents)** + +## Listeners + + With the help of Listeners we can modify the behaviour of TestNG framework. There are a lot of testNG listener interfaces that we can override in order to provide new functionalities. + The tas framework provides out of the box a couple of listeners that you could use. These could be enabled and added at the class level or suite level. + +### a) org.alfresco.utility.report.ReportListenerAdapter + + * if added at the class level: + + ```java + @Listeners(value=ReportListenerAdapter.class) + public class MyTestClass extends CmisTest + { + (...) + } + ``` + + * or suite xml level + + ```java + + + + + (...) + + ``` + It will automatically generate one html named "report.html" in ./target/report folder. + Please also take a look at [Test Results](#test-results) section. + +### b) org.alfresco.utility.testrail.TestRailExecutorListener + It will automatically update Test Rail application with the test cases that you've automated. + Please take a look at [Test Rail Integration](#test-rail-integration) section for more details. + +### c) org.alfresco.utility.report.log.LogsListener +This is a new listener that will generate further details in one XML format of the automated test steps that you will write. + +Example: + +```java +public void myDSLMethod1() +{ + STEP("Lorem ipsum dolor sit amet"); + //code for first step + + STEP("consectetur adipiscing elit"); + //code for the next description +} + +public void myDSLMethod2() +{ + STEP("sed do eiusmod tempor incididunt ut labore"); + //code for first step + + STEP("et dolore magna aliqua"); + //code for the next description +} +``` + +If these methods will be executed insite a test method, all those steps will be automatically logged in the XML report generated. +Example: + +```java +@Test +public void adminShouldCreateFileInSite() +{ + myDSLMethod1(); + myDSLMethod2() +} +``` + +So if "testingSomething" will be executed this is what you will see on the XML file generated. (please take a look at [Test Results](#test-results) section for defining the defaul location) + +Here is one example of XML file generated with these steps: + +![](docs/pics/xml-steps-report.JPG) + +**[Back to Top ^](#table-of-contents)** + +## Test Results + We already executed a couple of tests using command line as indicated above. Sweet! Please take a look at [sanity-cmis.xml](src/test/resources/sanity-cmis.xml) one more time. + You will see there that we have one listener added: + + ```java + + ``` + This will tell our framework, after we run all tests, to generate one HTML report file with graphs and metrics. + + Take a look at the target/reports folder (created after running the tests) and open the report.html file. + + ![](docs/pics/html-report-sample.JPG) + + Playing with this report, you will notice that you will be able to: + * search tests cases by name + * filter test cases by errors, labels, groups, test types, date when it was executed, protocol used, etc. + * view overall pass/fail metrics of current test suite, history of tests execution, etc. + + The report path can be configured in default.properties): + + ``` + # The location of the reports path + reports.path=your-new-location-of-reports + ``` + +**[Back to Top ^](#table-of-contents)** + +## Test Rail Integration + +Alfresco is using now https://alfresco.testrail.net (v5.3.0.3601). + +We aim to accelerate the delivery of automated test by minimizing the interaction with the test management tool - TestRail. In this scope we developed the following capabilities: +* creating automatically the manual tests in TestRail +* submitting the test results (with stack trace) after each execution into TestRail Test Runs +* adding the test steps for each test. + +### Configuration +In order to use Test Rail Integration you will need to add a couple of information in [default.properties](src/test/resources/default.properties) file: +(the document is pretty self explanatory) + +```java +# Example of configuration: +# ------------------------------------------------------ +# testManagement.endPoint=https://alfresco.testrail.com/ +# testManagement.username= +# testManagement.apiKey= +# testManagement.project= +``` +!This settings are already defined in default.properties for you. + + +For generating a new API Key take a look at the official documentation, TestRail [APIv2](http://docs.gurock.com/testrail-api2) +* _testManagement.project= **_" this represents the name of the Test Run from your project. +* In Test Rail, navigating to Test Runs & Results, create a new Test Run and include all/particular test cases. If this test run name is "Automation", update _testManagement.testRun= **Automation**_. + All test results will be updated only on this test run at runtime as each test is executed by TAS framework. + +### How to enable Test Rail Integration? + +We wanted to simplify the Test Rail integration, so we used listeners in order to enable/disable the integration of Test Rail. +* first configure your default.properties as indicated above + +* now on your TestNG test, add the @TestRail annotation, so let's say you will have this test: + + ```java + @Test(groups="sample-tests") + public void thisAutomatedTestWillBePublishedInTestRail() + { + } + ``` + add now @TestRail integration with mandatory field ```section```. This means that this tests annotated, will be uploaded in TestRail: + + ```java + @Test(groups="sample-tests") + @TestRail(section = { "protocols", "TBD" }) + public void thisAutomatedTestWillBePublishedInTestRail() + { + } + ``` + The section field, represents an array of strings, the hierarchy of sections that SHOULD be found on TestRail under the project you've selected in default.properties. Follow the TestRail [user-guide](http://docs.gurock.com/testrail-userguide/start) for more information regarding sections. + In our example we created in Test Rail one root section "protocols" with a child section: "TBD" (you can go further and add multiple section as you wish) + +* now, lets add the listener, the TestRailExecutorListener that will handle this TC Management interaction. + This listener can be added at the class level or suite level (approach that we embrace) + Take a look at [sanity-cmis.xml](src/test/resources/sanity-cmis.xml) for further example. + + ```xml + + + (...) + + ``` + + Right click on cmis-suites.xml file and run it, or just "mvn test" from root if this sample project. + After everything passes, go in Test Rail, open your project and navigate to "Test Cases" section. Notice that under protocols/TBD section, you will see your test case published. + + If you defined also the "testManagement.testRun" correctly, you will see under Test Runs, the status of this case marked as passed. + + The @TestRail annotation offers also other options like: + - "description" this is the description that will be updated in Test Rail for your test case + - "testType", the default value is set to Functional test + - "executionType", default value is set to ExecutionType.REGRESSION, but you can also use ExecutionType.SMOKE, ExecutionType.SANITY, etc + + Take a look at the demo scenarios in this project for further examples. + +**[Back to Top ^](#table-of-contents)** + +## Reference + +* For any improvements, bugs, please use Jira - [TAS](https://issues.alfresco.com/jira/browse/TAS) project. +* Setup the environment using [docker](https://gitlab.alfresco.com/tas/alfresco-docker-provisioning/blob/master/Readme.md). +* [Bamboo Test Plan](https://bamboo.alfresco.com/bamboo/browse/TAS-CMIS) + +## Contributors + +As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other... [more](CODE_OF_CONDUCT.md) + +## Releasing + +Any commit done on this project should be automatically executed by [TAS Build Plan](https://bamboo.alfresco.com/bamboo/browse/TAS-TAS) +If the build passes, then you didn't broke anything. + +If you want to perform a release, open [TAS-CMIS](https://bamboo.alfresco.com/bamboo/browse/TAS-CMIS) Bamboo Build. +Run the Default stage and if it passes, then manually perform the Release stage (this will auto-increment the version in pom.xml) + +## License + +TBD diff --git a/packaging/tests/tas-cmis/docs/CHANGELOG.md b/packaging/tests/tas-cmis/docs/CHANGELOG.md new file mode 100644 index 0000000000..a873dd833e --- /dev/null +++ b/packaging/tests/tas-cmis/docs/CHANGELOG.md @@ -0,0 +1,20 @@ +:paw_prints: Back to Utility [README](README.md). + +--- +# Change Log +All notable changes to this project will be documented in this file. + +Each tag bellow has a corresponded version released in [Nexus](https://nexus.alfresco.com/nexus/#welcome). +Currently we are testing [CMIS v1.1](http://docs.oasis-open.org/cmis/CMIS/v1.1/CMIS-v1.1.html) with Alfresco One. + +(if you need to update/polish tests please branch from the release tags) + +## [[v5.2.0-1] - 2016-12-12](/tas/alfresco-tas-cmis-test/commits/v5.2.0-1) +### TBD + +## [[v5.2.0-0] - 2016-12-12](/tas/alfresco-tas-cmis-test/commits/v5.2.0-0) +- works with 5.2 alfresco +- 100% Core tests for CMIS +- 100% Sanity test for CMIS +- use released v1.0.7 utility + diff --git a/packaging/tests/tas-cmis/docs/pics/html-report-sample.JPG b/packaging/tests/tas-cmis/docs/pics/html-report-sample.JPG new file mode 100644 index 0000000000..d03f6e2086 Binary files /dev/null and b/packaging/tests/tas-cmis/docs/pics/html-report-sample.JPG differ diff --git a/packaging/tests/tas-cmis/docs/pics/html-report-sample.png b/packaging/tests/tas-cmis/docs/pics/html-report-sample.png new file mode 100644 index 0000000000..576422e34f Binary files /dev/null and b/packaging/tests/tas-cmis/docs/pics/html-report-sample.png differ diff --git a/packaging/tests/tas-cmis/docs/pics/xml-steps-report.JPG b/packaging/tests/tas-cmis/docs/pics/xml-steps-report.JPG new file mode 100644 index 0000000000..e670430aaf Binary files /dev/null and b/packaging/tests/tas-cmis/docs/pics/xml-steps-report.JPG differ diff --git a/packaging/tests/tas-cmis/pom.xml b/packaging/tests/tas-cmis/pom.xml new file mode 100644 index 0000000000..814aac7edb --- /dev/null +++ b/packaging/tests/tas-cmis/pom.xml @@ -0,0 +1,171 @@ + + + 4.0.0 + org.alfresco.tas + cmis + alfresco-tas-cmis + 1.0-SNAPSHOT + + org.alfresco + alfresco-super-pom + 10 + + + + Alfresco Software + http://www.alfresco.com/ + + + + Paul Brodner + + Test Automation Architect + + + + + + src/main/resources/shared-resources/cmis-suites.xml + UTF-8 + 3.0.8 + 1.5 + 1.0.0 + 3.1.1 + 2.5.3 + 11 + + + + scm:git:https://github.com/Alfresco/alfresco-tas-cmis.git + scm:git:https://github.com/Alfresco/alfresco-tas-cmis.git + https://github.com/Alfresco/alfresco-tas-cmis + HEAD + + + + JIRA + https://issues.alfresco.com/jira/browse/ + + + + + alfresco-public + https://artifacts.alfresco.com/nexus/content/repositories/releases + + + alfresco-public + https://artifacts.alfresco.com/nexus/content/repositories/snapshots + + + + + + + alfresco-public + https://artifacts.alfresco.com/nexus/content/groups/public + + + + + + + maven-release-plugin + ${maven-release.version} + + v@{project.version} + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + + test-jar + + + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + + org.apache.maven.plugins + maven-remote-resources-plugin + ${maven-remote-resources.version} + + + shared-resources/**/* + + + org.alfresco.tas:utility:${tas.utility.version} + + + + + + process + bundle + + + + + + + + + + org.jboss.resteasy + resteasy-jackson2-provider + 3.6.3.Final + + + + + org.alfresco.tas + utility + ${tas.utility.version} + + + + + org.apache.chemistry.opencmis + chemistry-opencmis-commons-api + ${chemistry-opencmis-commons-api} + + + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 2.9 + + + + dependencies + issue-tracking + scm + + + + + + + + diff --git a/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/AuthParameterProviderFactory.java b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/AuthParameterProviderFactory.java new file mode 100644 index 0000000000..d6b9202f77 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/AuthParameterProviderFactory.java @@ -0,0 +1,130 @@ +package org.alfresco.cmis; + +import org.alfresco.utility.data.AisToken; +import org.alfresco.utility.data.auth.DataAIS; +import org.alfresco.utility.model.UserModel; +import org.apache.chemistry.opencmis.commons.SessionParameter; +import org.keycloak.authorization.client.util.HttpResponseException; +import org.keycloak.representations.AccessTokenResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import static org.alfresco.utility.report.log.Step.STEP; + +@Service +public class AuthParameterProviderFactory +{ + public static String STEP_PREFIX = "CMIS AuthParameterProvider:"; + + @Autowired + private DataAIS dataAIS; + + @Autowired + private CmisProperties cmisProperties; + + /** + * + * The default provider uses AIS if support for Alfresco Identity Service is enabled. + * Otherwise a provider which uses Basic authentication is returned. + * + * @return Function which takes a {@link UserModel} and returns a map of + * authentication parameters to be used with {@link CmisWrapper#authenticateUser(UserModel, Function)} + */ + public Function> getDefaultProvider() + { + if (dataAIS.isEnabled()) + { + STEP(String.format("%s Retrieved default AIS auth parameter provider.", STEP_PREFIX)); + return new AisAuthParameterProvider(); + } + else + { + STEP(String.format("%s Retrieved default Basic auth parameter provider.", STEP_PREFIX)); + return new BasicAuthParameterProvider(); + } + } + + public Function> getAISProvider() + { + return new AisAuthParameterProvider(); + } + + public Function> getBasicProvider() + { + return new BasicAuthParameterProvider(); + } + + private class BasicAuthParameterProvider implements Function> + { + @Override + public Map apply(UserModel userModel) + { + STEP(String.format("%s Using Basic auth parameter provider.", STEP_PREFIX)); + Map parameters = new HashMap<>(); + parameters.put(SessionParameter.USER, userModel.getUsername()); + parameters.put(SessionParameter.PASSWORD, userModel.getPassword()); + return parameters; + } + } + + private class AisAuthParameterProvider implements Function> + { + @Override + public Map apply(UserModel userModel) + { + Map parameters = new HashMap<>(); + + STEP(String.format("%s Using AIS auth parameter provider.", STEP_PREFIX)); + AisToken aisToken = getAisAccessToken(userModel); + + parameters.put(SessionParameter.AUTHENTICATION_PROVIDER_CLASS, "org.apache.chemistry.opencmis.client.bindings.spi.OAuthAuthenticationProvider"); + parameters.put(SessionParameter.OAUTH_ACCESS_TOKEN, aisToken.getToken()); + parameters.put(SessionParameter.OAUTH_REFRESH_TOKEN, aisToken.getRefreshToken()); + parameters.put(SessionParameter.OAUTH_EXPIRATION_TIMESTAMP, String.valueOf(System.currentTimeMillis() + + (aisToken.getExpiresIn() * 1000))); // getExpiresIn is in seconds + parameters.put(SessionParameter.OAUTH_TOKEN_ENDPOINT, cmisProperties.aisProperty().getAdapterConfig().getAuthServerUrl() + + "/realms/alfresco/protocol/openid-connect/token"); + parameters.put(SessionParameter.OAUTH_CLIENT_ID, cmisProperties.aisProperty().getAdapterConfig().getResource()); + return parameters; + } + + /** + * Returns a valid access token for valid user credentials in userModel. + * An invalid access token is returned for invalid user credentials, + * which can be used for tests involving non existing or unauthorized users. + * @param userModel + * @return + */ + private AisToken getAisAccessToken(UserModel userModel) + { + String badToken = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJUazFPZ2JqVlo1UEw2bmtsNWFvTUlacTZ4cW9PZzc5WGtzdnJTTUcxLUFZIn0.eyJqdGkiOiI3NTVkMGZiOS03NzI5LTQ1NzYtYWM4Ny1hZWZjZWNiZDE0ZGEiLCJleHAiOjE1NTM2MjQ1NDgsIm5iZiI6MCwiaWF0IjoxNTUzNjI0MjQ4LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0L2F1dGgvcmVhbG1zL2FsZnJlc2NvIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6Ijk4NDE0Njg4LTUwMDUtNDVmOS05YTVjLTlkMDRlODMyYTNkMiIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFsZnJlc2NvIiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiNjJlN2U5YzktZmFlNS00N2RhLTk5MDItMTZjYTJhZWUwMWMwIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vbG9jYWxob3N0KiIsImh0dHBzOi8vbG9jYWxob3N0KiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoidXNlci12eGlrcXd3cG5jYmpzeHgifQ.PeLGCNCzj-P2m0knwUU9Vfx4dzLLQER9IdV7GyLel9LRN-3J9nh7GBDRQsyDJ0pqhObQyMg4V3wSsrsXRQ6gKhmUyDemmD-w1YMC2a2HKX6GlxsTEF_f1K_R15lIQOawNVErlWjZWORJGCvCYZOJ99SOmeOC6PGY79zLL94MMnf6dXcegePPMOKG-59eNjBkOylTipYebvM40nbbKrS5vzNHQlvUh4ALFeBoMSKGnLSjQd06Dj4SWojG0p1BrxurqDjW0zz6pQlEAm4vcWApRZ6qBLZcMH8adYix07zCDb87GOn1pmfEBWpwd3BEgC_LLu06guaCPHC9tpeIaDTHLg"; + String badRefreshToken = "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJmM2YyMjhjYS1jMzg5LTQ5MGUtOGU1Zi02YWI1MmJhZDVjZGEifQ.eyJqdGkiOiIyNmExZWNhYy00Zjk0LTQwYzctYjJjNS04NTlhZmQ3NjBiYWMiLCJleHAiOjE1NTM2MjYwNDgsIm5iZiI6MCwiaWF0IjoxNTUzNjI0MjQ4LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0L2F1dGgvcmVhbG1zL2FsZnJlc2NvIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdC9hdXRoL3JlYWxtcy9hbGZyZXNjbyIsInN1YiI6Ijk4NDE0Njg4LTUwMDUtNDVmOS05YTVjLTlkMDRlODMyYTNkMiIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJhbGZyZXNjbyIsImF1dGhfdGltZSI6MCwic2Vzc2lvbl9zdGF0ZSI6IjYyZTdlOWM5LWZhZTUtNDdkYS05OTAyLTE2Y2EyYWVlMDFjMCIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJwcm9maWxlIGVtYWlsIn0.lRBJQc7tj0rk7JBC0zpM0dDdZgDKjm9wcxP8nzLnXe4"; + + AisToken aisToken; + try + { + // Attempt to get an access token for userModel from AIS + aisToken = dataAIS.perform().getAccessToken(userModel); + } + catch (HttpResponseException e) + { + // Trying to authenticate with invalid user credentials so return an invalid access token + if (e.getStatusCode() == 401) + { + STEP(String.format("%s Invalid user credentials were provided %s:%s. Using invalid token for reqest.", + STEP_PREFIX, userModel.getUsername(), userModel.getPassword())); + aisToken = new AisToken(badToken, badRefreshToken, System.currentTimeMillis(), 300000); + } + else + { + throw e; + } + } + return aisToken; + } + } +} diff --git a/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/CmisProperties.java b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/CmisProperties.java new file mode 100644 index 0000000000..033f2f7ab4 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/CmisProperties.java @@ -0,0 +1,64 @@ +package org.alfresco.cmis; + +import org.alfresco.utility.TasAisProperties; +import org.alfresco.utility.TasProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; + +@Configuration +@PropertySource("classpath:default.properties") +@PropertySource(value = "classpath:${environment}.properties", ignoreResourceNotFound = true) +public class CmisProperties +{ + @Autowired + private TasProperties properties; + + @Autowired + private TasAisProperties aisProperties; + + public TasProperties envProperty() + { + return properties; + } + + public TasAisProperties aisProperty() + { + return aisProperties; + } + + @Bean + public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() + { + return new PropertySourcesPlaceholderConfigurer(); + } + + @Value("${cmis.binding}") + private String cmisBinding; + + @Value("${cmis.basePath}") + private String basePath; + + public String getCmisBinding() + { + return cmisBinding; + } + + public String getBasePath() + { + return basePath; + } + + public void setBasePath(String basePath) + { + this.basePath = basePath; + } + + public void setCmisBinding(String cmisBinding) + { + this.cmisBinding = cmisBinding; + } +} diff --git a/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/CmisWrapper.java b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/CmisWrapper.java new file mode 100644 index 0000000000..7338000bbb --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/CmisWrapper.java @@ -0,0 +1,1111 @@ +package org.alfresco.cmis; + +import static org.alfresco.utility.Utility.checkObjectIsInitialized; +import static org.alfresco.utility.report.log.Step.STEP; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import org.alfresco.cmis.dsl.BaseObjectType; +import org.alfresco.cmis.dsl.CheckIn; +import org.alfresco.cmis.dsl.CmisAssertion; +import org.alfresco.cmis.dsl.CmisUtil; +import org.alfresco.cmis.dsl.DocumentVersioning; +import org.alfresco.cmis.dsl.JmxUtil; +import org.alfresco.cmis.dsl.QueryExecutor; +import org.alfresco.utility.LogFactory; +import org.alfresco.utility.Utility; +import org.alfresco.utility.constants.UserRole; +import org.alfresco.utility.dsl.DSLContentModelAction; +import org.alfresco.utility.dsl.DSLFile; +import org.alfresco.utility.dsl.DSLFolder; +import org.alfresco.utility.dsl.DSLProtocol; +import org.alfresco.utility.exception.TestConfigurationException; +import org.alfresco.utility.model.ContentModel; +import org.alfresco.utility.model.DataListItemModel; +import org.alfresco.utility.model.DataListModel; +import org.alfresco.utility.model.FileModel; +import org.alfresco.utility.model.FolderModel; +import org.alfresco.utility.model.GroupModel; +import org.alfresco.utility.model.SiteModel; +import org.alfresco.utility.model.UserModel; +import org.apache.chemistry.opencmis.client.api.CmisObject; +import org.apache.chemistry.opencmis.client.api.Document; +import org.apache.chemistry.opencmis.client.api.FileableCmisObject; +import org.apache.chemistry.opencmis.client.api.Folder; +import org.apache.chemistry.opencmis.client.api.ObjectId; +import org.apache.chemistry.opencmis.client.api.Repository; +import org.apache.chemistry.opencmis.client.api.SecondaryType; +import org.apache.chemistry.opencmis.client.api.Session; +import org.apache.chemistry.opencmis.client.api.SessionFactory; +import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl; +import org.apache.chemistry.opencmis.commons.PropertyIds; +import org.apache.chemistry.opencmis.commons.SessionParameter; +import org.apache.chemistry.opencmis.commons.data.AclCapabilities; +import org.apache.chemistry.opencmis.commons.data.ContentStream; +import org.apache.chemistry.opencmis.commons.data.PermissionMapping; +import org.apache.chemistry.opencmis.commons.data.RepositoryInfo; +import org.apache.chemistry.opencmis.commons.enums.AclPropagation; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.enums.BindingType; +import org.apache.chemistry.opencmis.commons.enums.UnfileObject; +import org.apache.chemistry.opencmis.commons.enums.VersioningState; +import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException; +import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException; +import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException; +import org.apache.chemistry.opencmis.commons.exceptions.CmisVersioningException; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; + +@Service +@Scope(value = "prototype") +public class CmisWrapper extends DSLProtocol implements DSLContentModelAction, DSLFile, DSLFolder +{ + protected Logger LOG = LogFactory.getLogger(); + public static String STEP_PREFIX = "CMIS:"; + + private Session session; + + @Autowired + private AuthParameterProviderFactory authParameterProviderFactory; + + @Autowired + CmisProperties cmisProperties; + + public List deleteTreeFailedObjects = new ArrayList(); + + @Override + public CmisWrapper authenticateUser(UserModel userModel) + { + return authenticateUser(userModel, authParameterProviderFactory.getDefaultProvider()); + } + + public CmisWrapper authenticateUser(UserModel userModel, Function> authParameterProvider) + { + disconnect(); + STEP(String.format("%s Connect with %s/%s", STEP_PREFIX, userModel.getUsername(), userModel.getPassword())); + SessionFactory factory = SessionFactoryImpl.newInstance(); + + // Initialise a new session parameter map with session authentication parameters for userModel + Map parameter = new HashMap<>(authParameterProvider.apply(userModel)); + + String binding = cmisProperties.getCmisBinding().toLowerCase(); + String cmisURLPath = cmisProperties.envProperty().getFullServerUrl() + cmisProperties.getBasePath(); + if (binding.equals(BindingType.BROWSER.value())) + { + parameter.put(SessionParameter.BROWSER_URL, cmisURLPath); + parameter.put(SessionParameter.BINDING_TYPE, BindingType.BROWSER.value()); + LOG.info("Using binding type [{}] to [{}] and credentials: {}", BindingType.BROWSER.value(), cmisURLPath, userModel.toString()); + } + else if (binding.equals(BindingType.ATOMPUB.value().replace("pub", ""))) + { + parameter.put(SessionParameter.ATOMPUB_URL, cmisURLPath); + parameter.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value()); + LOG.info("Using binding type [{}] to [{}] and credentials: {}", BindingType.ATOMPUB.value(), cmisURLPath, userModel.toString()); + } + parameter.put(SessionParameter.CONNECT_TIMEOUT, "20000"); + parameter.put(SessionParameter.READ_TIMEOUT, "60000"); + List repositories = factory.getRepositories(parameter); + parameter.put(SessionParameter.REPOSITORY_ID, repositories.get(0).getId()); + session = repositories.get(0).createSession(); + setTestUser(userModel); + return this; + } + + public CmisWrapper authUserUsingBrowserUrlAndBindingType(UserModel userModel, String urlPath, String bindingType) + { + STEP(String.format("%s Setting binding type %s to %s", STEP_PREFIX, bindingType, urlPath)); + STEP(String.format("%s Connect with %s/%s", STEP_PREFIX, userModel.getUsername(), userModel.getPassword())); + SessionFactory factory = SessionFactoryImpl.newInstance(); + // Initialise a new session parameter map with session authentication parameters for userModel + Map parameter = new HashMap<>(authParameterProviderFactory.getDefaultProvider().apply(userModel)); + + parameter.put(SessionParameter.BROWSER_URL, urlPath); + parameter.put(SessionParameter.BINDING_TYPE, bindingType); + LOG.info("Using binding type [{}] to [{}] and credentials: {}", bindingType, urlPath, userModel.toString()); + List repositories = factory.getRepositories(parameter); + parameter.put(SessionParameter.REPOSITORY_ID, repositories.get(0).getId()); + session = repositories.get(0).createSession(); + setTestUser(userModel); + return this; + } + + public AuthParameterProviderFactory getAuthParameterProviderFactory() + { + return this.authParameterProviderFactory; + } + + @Override + public CmisWrapper disconnect() + { + if (session != null) + { + getSession().clear(); + } + return this; + } + + @Override + public String buildPath(String parent, String... paths) + { + return Utility.convertBackslashToSlash(super.buildPath(parent, paths)).replace("//", "/"); + } + + /** + * Get the current session + * + * @return Session + */ + public synchronized Session getSession() + { + return session; + } + + @Override + public CmisWrapper createFile(FileModel fileModel) + { + return createFile(fileModel, BaseTypeId.CMIS_DOCUMENT.value(), VersioningState.MAJOR); + } + + /** + * Create a new file + * + * @param fileModel {@link FileModel} file model to be created + * @param versioningState {@link VersioningState} + * @return CmisWrapper + */ + public CmisWrapper createFile(FileModel fileModel, VersioningState versioningState) + { + return createFile(fileModel, BaseTypeId.CMIS_DOCUMENT.value(), versioningState); + } + + /** + * Create a new file + * + * @param fileModel {@link FileModel} file model to be created + * @param cmisBaseTypeId base type id (e.g. 'cmis:document') + * @param versioningState {@link VersioningState} + * @return CmisWrapper + */ + public CmisWrapper createFile(FileModel fileModel, String cmisBaseTypeId, VersioningState versioningState) + { + return createFile(fileModel, withCMISUtil().getProperties(fileModel, cmisBaseTypeId), versioningState); + } + + public CmisWrapper createFile(FileModel fileModel, Map properties, VersioningState versioningState) + { + ContentStream contentStream = dataContent.getContentStream(fileModel.getName(), fileModel.getContent()); + STEP(String.format("%s Create file '%s' in '%s'", STEP_PREFIX, fileModel.getName(), getCurrentSpace())); + Document doc = null; + try + { + doc = withCMISUtil().getCmisFolder(getCurrentSpace()).createDocument(properties, contentStream, versioningState); + } + catch (CmisStorageException | CmisRuntimeException re) + { + doc = withCMISUtil().getCmisFolder(getCurrentSpace()).createDocument(properties, contentStream, versioningState); + } + fileModel.setNodeRef(doc.getId()); + String location = buildPath(getCurrentSpace(), fileModel.getName()); + setLastResource(location); + fileModel.setProtocolLocation(location); + fileModel.setCmisLocation(location); + dataContent.closeContentStream(contentStream); + return this; + } + + /** + * Create new file from existing one (that was set in last resource) + * + * @param newfileModel {@link FileModel} file model to be created + * @param sourceFileModel {@link ContentModel} source file model + * @return CmisWrapper + */ + public CmisWrapper createFileFromSource(FileModel newfileModel, ContentModel sourceFileModel) + { + return createFileFromSource(newfileModel, sourceFileModel, BaseTypeId.CMIS_DOCUMENT.value()); + } + + /** + * Create new file from existing one with versioning state set to Major(that was set in last resource) + * + * @param newfileModel {@link FileModel} file model to be created + * @param sourceFileModel {@link ContentModel} source file model + * @param cmisBaseTypeId base type id (e.g. 'cmis:document') + * @return CmisWrapper + */ + public CmisWrapper createFileFromSource(FileModel newfileModel, ContentModel sourceFileModel, String cmisBaseTypeId) + { + return createFileFromSource(newfileModel, sourceFileModel, cmisBaseTypeId, VersioningState.MAJOR); + } + + /** + * Create new file from existing one (that was set in last resource) + * + * @param newfileModel {@link FileModel} file model to be created + * @param sourceFileModel {@link ContentModel} source file model + * @param versioningState version(e.g. 'VersioningState.MAJOR') + * @return CmisWrapper + */ + public CmisWrapper createFileFromSource(FileModel newfileModel, ContentModel sourceFileModel, VersioningState versioningState) + { + return createFileFromSource(newfileModel, sourceFileModel, BaseTypeId.CMIS_DOCUMENT.value(), versioningState); + } + + /** + * Create new file from existing one (that was set in last resource) + * + * @param newfileModel {@link FileModel} file model to be created + * @param sourceFileModel {@link ContentModel} source file model + * @param cmisBaseTypeId base type id (e.g. 'cmis:document') + * @param versioningState (e.g. 'VersioningState.MAJOR') + * @return CmisWrapper + */ + public CmisWrapper createFileFromSource(FileModel newfileModel, ContentModel sourceFileModel, String cmisBaseTypeId, VersioningState versioningState) + { + String resourcePath = getLastResource(); + STEP(String.format("%s Create new file '%s' from source '%s' in '%s'", STEP_PREFIX, newfileModel.getName(), sourceFileModel.getName(), resourcePath)); + Document source = withCMISUtil().getCmisDocument(sourceFileModel.getCmisLocation()); + Map properties = withCMISUtil().getProperties(newfileModel, cmisBaseTypeId); + Document doc = withCMISUtil().getCmisFolder(resourcePath).createDocumentFromSource(source, properties, versioningState); + doc.refresh(); + newfileModel.setNodeRef(doc.getId()); + String location = buildPath(resourcePath, doc.getName()); + setLastResource(location); + newfileModel.setProtocolLocation(location); + newfileModel.setCmisLocation(location); + return this; + } + + @Override + public CmisWrapper createFolder(FolderModel folderModel) + { + return createFolder(folderModel, BaseTypeId.CMIS_FOLDER.value()); + } + + public CmisWrapper createFolder(FolderModel folderModel, String cmisBaseTypeId) + { + Map properties = withCMISUtil().getProperties(folderModel, cmisBaseTypeId); + createFolder(folderModel, properties); + return this; + } + + public CmisWrapper createFolder(FolderModel folderModel, Map properties) + { + STEP(String.format("%s Create folder '%s' in '%s'", STEP_PREFIX, folderModel.getName(), getCurrentSpace())); + Folder folder = withCMISUtil().getCmisFolder(getCurrentSpace()).createFolder(properties); + String location = buildPath(getCurrentSpace(), folderModel.getName()); + setLastResource(location); + folderModel.setProtocolLocation(location); + folderModel.setCmisLocation(location); + folderModel.setNodeRef(folder.getId()); + return this; + } + + /** + * Deletes this folder and all subfolders with all versions and continue on failure + * + * @return current wrapper + */ + public CmisWrapper deleteFolderTree() + { + return deleteFolderTree(true, UnfileObject.DELETE, true); + } + + /** + * Deletes this folder and all subfolders with specific parameters + * + * @param allVersions + * @param unfile {@link UnfileObject} + * @param continueOnFailure + * @return current wrapper + */ + public CmisWrapper deleteFolderTree(boolean allVersions, UnfileObject unfile, boolean continueOnFailure) + { + String path = getLastResource(); + Folder parent = withCMISUtil().getCmisFolder(Utility.convertBackslashToSlash(new File(path).getParent())); + STEP(String.format("%s Delete parent folder from '%s'", STEP_PREFIX, path)); + Folder folder = withCMISUtil().getCmisFolder(path); + folder.refresh(); + deleteTreeFailedObjects.clear(); + deleteTreeFailedObjects = folder.deleteTree(allVersions, unfile, continueOnFailure); + for (String failedObj : deleteTreeFailedObjects) + { + LOG.error(String.format("Failed to delete object %s", failedObj)); + } + if (!deleteTreeFailedObjects.isEmpty()) + { + LOG.info(String.format("Retry: delete parent folder from %s", path)); + Utility.waitToLoopTime(2); + folder.refresh(); + folder.deleteTree(allVersions, unfile, continueOnFailure); + } + else + { + parent.refresh(); + dataContent.waitUntilContentIsDeleted(path); + } + return this; + } + + @Override + public String getRootPath() throws TestConfigurationException + { + return "/"; + } + + @Override + public String getSitesPath() throws TestConfigurationException + { + return String.format("%s/%s", getPrefixSpace(), "Sites"); + } + + @Override + public String getUserHomesPath() throws TestConfigurationException + { + return String.format("%s/%s", getPrefixSpace(), "User Homes"); + } + + @Override + public String getDataDictionaryPath() throws TestConfigurationException + { + return String.format("%s/%s", getPrefixSpace(), "Data Dictionary"); + } + + public String getSharedPath() throws TestConfigurationException + { + return String.format("%s/%s", getPrefixSpace(), "Shared"); + } + + @Override + public CmisWrapper usingSite(String siteId) + { + STEP(String.format("%s Navigate to site '%s/%s'", STEP_PREFIX, siteId, "documentLibrary")); + checkObjectIsInitialized(siteId, "SiteID"); + setCurrentSpace(buildSiteDocumentLibraryPath(siteId, "")); + return this; + } + + @Override + public CmisWrapper usingSite(SiteModel siteModel) + { + STEP(String.format("%s Navigate to site '%s/%s'", STEP_PREFIX, siteModel.getId(), "documentLibrary")); + checkObjectIsInitialized(siteModel, "SiteModel"); + String path = buildSiteDocumentLibraryPath(siteModel.getId(), ""); + setCurrentSpace(path); + return this; + } + + @Override + public CmisWrapper usingUserHome(String username) + { + STEP(String.format("%s Navigate to 'User Home' folder", STEP_PREFIX)); + checkObjectIsInitialized(username, "username"); + setCurrentSpace(buildUserHomePath(username, "")); + return this; + } + + @Override + public CmisWrapper usingUserHome() + { + STEP(String.format("%s Navigate to 'User Home' folder", STEP_PREFIX)); + checkObjectIsInitialized(getTestUser().getUsername(), "username"); + setCurrentSpace(buildUserHomePath(getTestUser().getUsername(), "")); + return this; + } + + public CmisWrapper usingShared() + { + STEP(String.format("%s Navigate to 'Shared' folder", STEP_PREFIX)); + setCurrentSpace(getSharedPath()); + return this; + } + + @Override + public CmisWrapper usingResource(ContentModel model) + { + STEP(String.format("%s Navigate to '%s'", STEP_PREFIX, model.getName())); + checkObjectIsInitialized(model, "contentName"); + setCurrentSpace(model.getCmisLocation()); + return this; + } + + @Override + protected String getProtocolJMXConfigurationStatus() + { + return ""; + } + + @Override + public String getPrefixSpace() + { + return ""; + } + + @Override + public CmisWrapper rename(String newName) + { + String resourcePath = getLastResource(); + CmisObject objToRename = withCMISUtil().getCmisObject(resourcePath); + STEP(String.format("%s Rename '%s' to '%s'", STEP_PREFIX, objToRename.getName(), newName)); + objToRename.rename(newName); + setLastResource(buildPath(new File(resourcePath).getParent(), newName)); + return this; + } + + @Override + public CmisWrapper update(String content) + { + return update(content, true); + } + + public CmisWrapper update(String content, boolean isLastChunk) + { + Document doc = withCMISUtil().getCmisDocument(getLastResource()); + doc.refresh(); + Utility.waitToLoopTime(2); + STEP(String.format("%s Update content from '%s' by appending '%s'", STEP_PREFIX, doc.getName(), content)); + ContentStream contentStream = dataContent.getContentStream(doc.getName(), content); + doc.appendContentStream(contentStream, isLastChunk); + dataContent.closeContentStream(contentStream); + return this; + } + + /** + * Update the properties of the last resource {@link ContentModel}. + * Example updateProperty("cmis:name", "test1234") + * + * @param property + * @param value + * @return + */ + public CmisWrapper updateProperty(String property, Object value) + { + String lastResource = getLastResource(); + CmisObject objSource = withCMISUtil().getCmisObject(lastResource); + STEP(String.format("%s Update '%s' property for '%s'", STEP_PREFIX, property, objSource.getName())); + Map properties = new HashMap<>(); + properties.put(property, value); + objSource.updateProperties(properties, true); + + if (property.equals("cmis:name")) + { + if (objSource instanceof Document) + { + setLastResource(buildPath(new File(getLastResource()).getParent(), objSource.getName())); + } + else if (objSource instanceof Folder) + { + setLastResource(buildPath(((Folder) objSource).getFolderParent().getPath(), value.toString())); + } + } + return this; + } + + @Override + public CmisWrapper delete() + { + String resourcePath = getLastResource(); + STEP(String.format("%s Delete content from '%s'", STEP_PREFIX, resourcePath)); + withCMISUtil().getCmisObject(resourcePath).delete(); + return this; + } + + /** + * Deletes all versions if parameter is set to true, otherwise deletes only last version + * + * @param allVersions + * @return + */ + public CmisWrapper deleteAllVersions(boolean allVersions) + { + String resourcePath = getLastResource(); + if (allVersions) + STEP(String.format("%s Delete all content '%s' versions", STEP_PREFIX, resourcePath)); + else + STEP(String.format("%s Delete only the last content '%s' version", STEP_PREFIX, resourcePath)); + withCMISUtil().getCmisObject(getLastResource()).delete(allVersions); + return this; + } + + /** + * Delete content stream + * + * @return + */ + public CmisWrapper deleteContent() + { + String resourcePath = getLastResource(); + STEP(String.format("%s Delete document content from '%s'", STEP_PREFIX, resourcePath)); + withCMISUtil().getCmisDocument(getLastResource()).deleteContentStream(); + return this; + } + + /** + * Delete content stream and refresh document + * + * @param refresh boolean refresh resource + * @return + */ + public CmisWrapper deleteContent(boolean refresh) + { + String resourcePath = getLastResource(); + STEP(String.format("%s Delete document content from '%s'", STEP_PREFIX, resourcePath)); + withCMISUtil().getCmisDocument(getLastResource()).deleteContentStream(refresh); + return this; + } + + /** + * Set the content stream for a document + * + * @param content String content to set + * @param overwrite + * @return + */ + public CmisWrapper setContent(String content, boolean overwrite) + { + Utility.waitToLoopTime(1); + Document doc = withCMISUtil().getCmisDocument(getLastResource()); + doc.refresh(); + STEP(String.format("%s Set '%s' content to '%s' - node: %s", STEP_PREFIX, content, doc.getName(), doc.getId())); + ContentStream contentStream = dataContent.getContentStream(doc.getName(), content); + try + { + doc.setContentStream(contentStream, overwrite, true); + } + catch (CmisStorageException cs) + { + doc.setContentStream(contentStream, overwrite, true); + } + dataContent.closeContentStream(contentStream); + return this; + } + + /** + * Set the content stream for a document with overwrite set to TRUE + * + * @param content + * @return + */ + public CmisWrapper setContent(String content) + { + return setContent(content, true); + } + + /** + * Create a 'R:cm:basis' relationship between a source document and a target document + * + * @param targetContent + * @return + */ + public CmisWrapper createRelationshipWith(ContentModel targetContent) + { + return createRelationshipWith(targetContent, "R:cm:basis"); + } + + /** + * Create relationship between a source document and a target document + * + * @param targetContent {@link ContentModel} + * @param relationType + * @return + */ + public CmisWrapper createRelationshipWith(ContentModel targetContent, String relationType) + { + STEP(String.format("%s Set %s relationship between source from '%s' and target '%s'", STEP_PREFIX, relationType, getLastResource(), + targetContent.getName())); + Map properties = new HashMap<>(); + properties.put(PropertyIds.OBJECT_TYPE_ID, relationType); + properties.put(PropertyIds.SOURCE_ID, withCMISUtil().getCmisObject(getLastResource()).getId()); + properties.put(PropertyIds.TARGET_ID, targetContent.getNodeRef()); + getSession().createRelationship(properties); + return this; + } + + /** + * Method allows you to file a document object in more than one folder. + * + * @param destination - the destination folder to which this document will be added + * @param allVersions - if this parameter is true, then all versions of the document will be added to the destination folder + * @return + */ + public CmisWrapper addDocumentToFolder(FolderModel destination, boolean allVersions) + { + CmisObject objSource = withCMISUtil().getCmisObject(getLastResource()); + Folder objDestination = withCMISUtil().getCmisFolder(destination.getCmisLocation()); + STEP(String.format("%s Add object '%s' to '%s'", STEP_PREFIX, objSource.getName(), destination.getCmisLocation())); + ((FileableCmisObject) objSource).addToFolder(objDestination, allVersions); + setLastResource(buildPath(destination.getCmisLocation(), objSource.getName())); + return this; + } + + /** + * Method allows you to remove a document object from the given folder. + * + * @param parentFolder - the folder from which this object should be removed + * @return + */ + public CmisWrapper removeDocumentFromFolder(FolderModel parentFolder) + { + CmisObject objSource = withCMISUtil().getCmisObject(getLastResource()); + Folder parentObj = withCMISUtil().getCmisFolder(parentFolder.getCmisLocation()); + STEP(String.format("%s Remove object '%s' from '%s'", STEP_PREFIX, objSource.getName(), parentFolder.getCmisLocation())); + ((FileableCmisObject) objSource).removeFromFolder(parentObj); + return this; + } + + /** + * Get child folders from a parent folder + * + * @return List + */ + @Override + public List getFolders() + { + STEP(String.format("%s Get the folder children from '%s'", STEP_PREFIX, getLastResource())); + return withCMISUtil().getFolders(); + } + + /** + * Get child documents from a parent folder + * + * @return List + */ + @Override + public List getFiles() + { + STEP(String.format("%s Get the file children from '%s'", STEP_PREFIX, getLastResource())); + return withCMISUtil().getFiles(); + } + + @Override + public CmisWrapper copyTo(ContentModel destination) + { + String source = getLastResource(); + String sourceName = new File(source).getName(); + STEP(String.format("%s Copy '%s' to '%s'", STEP_PREFIX, sourceName, destination.getCmisLocation())); + CmisObject objSource = withCMISUtil().getCmisObject(source); + + CmisObject objDestination = withCMISUtil().getCmisObject(destination.getCmisLocation()); + if (objSource instanceof Document) + { + Document d = (Document) objSource; + d.copy(objDestination); + } + else if (objSource instanceof Folder) + { + Folder fFrom = (Folder) objSource; + Folder toFolder = (Folder) objDestination; + withCMISUtil().copyFolder(fFrom, toFolder); + } + setLastResource(buildPath(destination.getCmisLocation(), sourceName)); + return this; + } + + @Override + public CmisWrapper moveTo(ContentModel destination) + { + String source = getLastResource(); + String sourceName = new File(source).getName(); + STEP(String.format("%s Move '%s' to '%s'", STEP_PREFIX, sourceName, destination.getCmisLocation())); + CmisObject objSource = withCMISUtil().getCmisObject(source); + CmisObject objDestination = withCMISUtil().getCmisObject(destination.getCmisLocation()); + if (objSource instanceof Document) + { + Document d = (Document) objSource; + List parents = d.getParents(); + CmisObject parent = getSession().getObject(parents.get(0).getId()); + d.move(parent, objDestination); + } + else if (objSource instanceof Folder) + { + Folder f = (Folder) objSource; + List parents = f.getParents(); + CmisObject parent = getSession().getObject(parents.get(0).getId()); + f.move(parent, objDestination); + } + setLastResource(buildPath(destination.getCmisLocation(), sourceName)); + return this; + } + + public RepositoryInfo getRepositoryInfo() + { + STEP(String.format("Get repository information for user %s", getCurrentUser().getUsername())); + return getSession().getRepositoryInfo(); + } + + public AclCapabilities getAclCapabilities() + { + return getRepositoryInfo().getAclCapabilities(); + } + + /** + * Checks out the document + */ + public CmisWrapper checkOut() + { + Document document = withCMISUtil().getCmisDocument(getLastResource()); + STEP(String.format("%s Check out document '%s'", STEP_PREFIX, document.getName())); + try + { + document.checkOut(); + } + catch (CmisRuntimeException e) + { + document.checkOut(); + } + return this; + } + + /** + * If this is a PWC (private working copy) the check out will be reversed. + */ + public CmisWrapper cancelCheckOut() + { + Document document = withCMISUtil().getCmisDocument(getLastResource()); + STEP(String.format("%s Cancel document '%s' check out", STEP_PREFIX, document.getName())); + document.cancelCheckOut(); + return this; + } + + /** + * Starts the process to check in a document + */ + public CheckIn prepareDocumentForCheckIn() + { + return new CheckIn(this); + } + + /** + * Reloads the resource from the repository + */ + public CmisWrapper refreshResource() + { + CmisObject cmisObject = withCMISUtil().getCmisObject(getLastResource()); + STEP(String.format("%s Reload '%s'", STEP_PREFIX, cmisObject.getName())); + cmisObject.refresh(); + return this; + } + + /** + * @return utilities that are used by CMIS + */ + public CmisUtil withCMISUtil() + { + return new CmisUtil(this); + } + + /** + * @return JMX DSL for this wrapper + */ + public JmxUtil withJMX() + { + return new JmxUtil(this, jmxBuilder.getJmxClient()); + } + + @Override + public CmisAssertion assertThat() + { + return new CmisAssertion(this); + } + + /** + * Starts the process to work with a version of a document + */ + public DocumentVersioning usingVersion() + { + return new DocumentVersioning(this, withCMISUtil().getCmisObject(getLastResource())); + } + + /** + * Add new permission for user + * + * @param user UserModel user + * @param role UserRole role to add + * @param aclPropagation AclPropagation propagation + * @return + */ + public CmisWrapper addAcl(UserModel user, UserRole role, AclPropagation aclPropagation) + { + STEP(String.format("%s Add permission '%s' for user %s ", STEP_PREFIX, role.name(), user.getUsername())); + withCMISUtil().getCmisObject(getLastResource(), withCMISUtil().setIncludeAclContext()).addAcl(withCMISUtil().createAce(user, role), aclPropagation); + return this; + } + + /** + * Add new permission for a group + * + * @param group GroupModel group + * @param role UserRole role to add + * @param aclPropagation AclPropagation propagation + * @return + */ + public CmisWrapper addAcl(GroupModel group, UserRole role, AclPropagation aclPropagation) + { + STEP(String.format("%s Add permission '%s' for user %s ", STEP_PREFIX, role.name(), group.getDisplayName())); + withCMISUtil().getCmisObject(getLastResource(), withCMISUtil().setIncludeAclContext()).addAcl(withCMISUtil().createAce(group, role), aclPropagation); + return this; + } + + /** + * Add new permission for a group + * + * @param group GroupModel group + * @param role UserRole role to add + * @return + */ + public CmisWrapper addAcl(GroupModel group, UserRole role) + { + return addAcl(group, role, null); + } + + /** + * Add new permissions to user + * + * @param user {@link UserModel} + * @param permissions to add ({@link PermissionMapping} can be used) + * @return + */ + public CmisWrapper addAcl(UserModel user, String... permissions) + { + withCMISUtil().getCmisObject(getLastResource(), withCMISUtil().setIncludeAclContext()).addAcl(withCMISUtil().createAce(user, permissions), null); + return this; + } + + /** + * Add new permission for user + * + * @param user UserModel user + * @param role UserRole role to add + * @return + */ + public CmisWrapper addAcl(UserModel user, UserRole role) + { + return addAcl(user, role, null); + } + + /** + * Update permission for user. + * If the role to remove is invalid a {@link CmisConstraintException} is thrown. + * + * @param user UserModel user + * @param newRole UserRole new role to add + * @param removeRole UserRole remove already added role + * @param aclPropagation AclPropagation + * @return + */ + public CmisWrapper applyAcl(UserModel user, UserRole newRole, UserRole removeRole, AclPropagation aclPropagation) + { + STEP(String.format("%s Edit permission for user %s from %s to %s ", STEP_PREFIX, user.getUsername(), removeRole.name(), newRole.name())); + withCMISUtil().getCmisObject(getLastResource(), withCMISUtil().setIncludeAclContext()).applyAcl(withCMISUtil().createAce(user, newRole), + withCMISUtil().createAce(user, removeRole), aclPropagation); + return this; + } + + /** + * Update permission for user. + * If the role to remove is invalid a {@link CmisConstraintException} is thrown. + * + * @param user UserModel user + * @param newRole UserRole new role to add + * @param removeRole UserRole remove already added role + * @return + */ + public CmisWrapper applyAcl(UserModel user, UserRole newRole, UserRole removeRole) + { + return applyAcl(user, newRole, removeRole, null); + } + + /** + * Update permission for user. + * If the permission to remove is invalid a {@link CmisConstraintException} is thrown. + * + * @param user {@link UserModel } + * @param newPermission permissions to add ({@link PermissionMapping} can be used) + * @param removePermission permissions to remove ({@link PermissionMapping} can be used) + * @return + */ + public CmisWrapper applyAcl(UserModel user, String newPermission, String removePermission) + { + STEP(String.format("%s Edit permission for user %s from %s to %s ", STEP_PREFIX, user.getUsername(), removePermission, newPermission)); + withCMISUtil().getCmisObject(getLastResource(), withCMISUtil().setIncludeAclContext()).applyAcl(withCMISUtil().createAce(user, newPermission), + withCMISUtil().createAce(user, removePermission), null); + return this; + } + + /** + * Remove permission from user + * + * @param user UserModel user + * @param removeRole UserRole role to remove + * @param aclPropagation AclPropagation + * @return + */ + public CmisWrapper removeAcl(UserModel user, UserRole removeRole, AclPropagation aclPropagation) + { + STEP(String.format("%s Remove permission '%s' from user %s ", STEP_PREFIX, removeRole.name(), user.getUsername())); + withCMISUtil().getCmisObject(getLastResource(), withCMISUtil().setIncludeAclContext()).removeAcl(withCMISUtil().createAce(user, removeRole), + aclPropagation); + return this; + } + + /** + * Remove permission from user + * + * @param user UserModel user + * @param removeRole UserRole role to remove + * @return + */ + public CmisWrapper removeAcl(UserModel user, UserRole removeRole) + { + return removeAcl(user, removeRole, null); + } + + public CmisWrapper removeAcl(UserModel user, String permissionToRemove) + { + STEP(String.format("%s Remove permission '%s' from user %s ", STEP_PREFIX, permissionToRemove, user.getUsername())); + withCMISUtil().getCmisObject(getLastResource(), withCMISUtil().setIncludeAclContext()).removeAcl(withCMISUtil().createAce(user, permissionToRemove), + null); + return this; + } + + /** + * Pass a string CMIS query, that will be handled by {@link QueryExecutor} using {@link org.apache.chemistry.opencmis.client.api.Session#query(String, boolean)} + * + * @param query + * @return {@link QueryExecutor} will all DSL assertions on returned restult + */ + public QueryExecutor withQuery(String query) + { + return new QueryExecutor(this, query); + } + + /** + * Use this method if the document is checked out. If not {@link CmisVersioningException} will be thrown. + * + * @return + */ + public CmisWrapper usingPWCDocument() + { + STEP(String.format("%s Navigate to private working copy of content '%s'", STEP_PREFIX, withCMISUtil().getPWCFileModel().getName())); + setCurrentSpace(withCMISUtil().getPWCFileModel().getCmisLocation()); + return this; + } + + /** + * @param baseType + * @return the DSL of asserting BaseObject type children for example. + */ + public BaseObjectType usingObjectType(String baseType) + { + return new BaseObjectType(this, baseType); + } + + /** + * Create a new data list type + * + * @param dataListModel {@link DataListModel} + * @return + */ + public CmisWrapper createDataList(DataListModel dataListModel) + { + Map properties = withCMISUtil().getProperties(dataListModel, "F:dl:dataList"); + properties.put("dl:dataListItemType", dataListModel.getDataListItemType()); + Folder folder = withCMISUtil().getCmisFolder(getCurrentSpace()).createFolder(properties); + String location = buildPath(getCurrentSpace(), dataListModel.getName()); + setLastResource(location); + dataListModel.setProtocolLocation(location); + dataListModel.setCmisLocation(location); + dataListModel.setNodeRef(folder.getId()); + return this; + } + + /** + * Create new data list item + * + * @param itemModel {@link DataListItemModel} + * @return + */ + public CmisWrapper createDataListItem(DataListItemModel itemModel) + { + Map propertyMap = itemModel.getItemProperties(); + String name = (String) propertyMap.get(PropertyIds.NAME); + STEP(String.format("%s Create new data list item %s (type: %s)", STEP_PREFIX, name, propertyMap.get(PropertyIds.OBJECT_TYPE_ID))); + ObjectId itemId = getSession().createDocument(propertyMap, withCMISUtil().getCmisObject(getLastResource()), null, null); + String path = buildPath(getCurrentSpace(), name); + itemModel.setName(name); + itemModel.setCmisLocation(path); + itemModel.setProtocolLocation(path); + itemModel.setNodeRef(itemId.getId()); + setLastResource(path); + return this; + } + + /** + * Attach documents to existent item set in last resource + * + * @param documents {@link ContentModel} list of content to attach + * @return + */ + public CmisWrapper attachDocument(ContentModel... contents) + { + String itemId = withCMISUtil().getCmisObject(getLastResource()).getId(); + for (ContentModel content : contents) + { + STEP(String.format("Attach document %s to item %s", content.getName(), itemId)); + Map relProps = new HashMap(); + relProps.put(PropertyIds.OBJECT_TYPE_ID, "R:cm:attachments"); + relProps.put(PropertyIds.SOURCE_ID, itemId); + relProps.put(PropertyIds.TARGET_ID, content.getNodeRef()); + getSession().createRelationship(relProps); + } + return this; + } + + /** + * Assign user to existent item set in last resource + * + * @param user {@link UserModel} + * @param relationType e.g. R:dl:issueAssignedTo, R:dl:assignee, R:dl:taskAssignee + * @return + */ + public CmisWrapper assignToUser(UserModel user, String relationType) + { + Map relProps = new HashMap(); + relProps.put(PropertyIds.OBJECT_TYPE_ID, relationType); + relProps.put(PropertyIds.SOURCE_ID, withCMISUtil().getCmisObject(getLastResource()).getId()); + relProps.put(PropertyIds.TARGET_ID, withCMISUtil().getUserNodeRef(user)); + getSession().createRelationship(relProps); + return this; + } + + /** + * Add new secondary types + * + * @param secondaryTypes e.g. P:cm:effectivity, P:audio:audio, P:cm:dublincore + * @return + */ + public CmisWrapper addSecondaryTypes(String...secondaryTypes) + { + CmisObject object = withCMISUtil().getCmisObject(getLastResource()); + List secondaryTypesNew = new ArrayList(); + for(SecondaryType oldType : object.getSecondaryTypes()) + { + secondaryTypesNew.add(oldType.getId()); + } + for(String newType : secondaryTypes) + { + secondaryTypesNew.add(newType); + } + Map properties = new HashMap(); + properties.put(PropertyIds.SECONDARY_OBJECT_TYPE_IDS, secondaryTypesNew); + object.updateProperties(properties); + return this; + } +} diff --git a/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/BaseObjectType.java b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/BaseObjectType.java new file mode 100644 index 0000000000..b5f1787f09 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/BaseObjectType.java @@ -0,0 +1,185 @@ +package org.alfresco.cmis.dsl; + +import static org.alfresco.utility.report.log.Step.STEP; + +import java.util.Iterator; +import java.util.List; + +import org.alfresco.cmis.CmisWrapper; +import org.alfresco.utility.LogFactory; +import org.apache.chemistry.opencmis.client.api.ItemIterable; +import org.apache.chemistry.opencmis.client.api.ObjectType; +import org.apache.chemistry.opencmis.client.api.Tree; +import org.apache.chemistry.opencmis.client.runtime.objecttype.ObjectTypeHelper; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.slf4j.Logger; +import org.testng.Assert; + +/** + * DSL for preparing calls on getting the type children of a type. + */ +public class BaseObjectType +{ + private CmisWrapper cmisAPI; + private String baseTypeID; + private boolean includePropertyDefinition = false; + private Logger LOG = LogFactory.getLogger(); + + public BaseObjectType(CmisWrapper cmisAPI, String baseTypeID) + { + this.cmisAPI = cmisAPI; + this.baseTypeID = baseTypeID; + } + + public BaseObjectType withPropertyDefinitions() + { + this.includePropertyDefinition = true; + return this; + } + + public BaseObjectType withoutPropertyDefinitions() + { + this.includePropertyDefinition = false; + return this; + } + + /** + * Example of objectTypeID: + * "D:trx:transferReport" - see {@link ObjectTypeHelper} "D:trx:tempTransferStore" + * "D:imap:imapAttach" + * + * @param objectTypeID + */ + public PropertyDefinitionObject hasChildren(String objectTypeID) + { + return checkChildren(objectTypeID, true); + } + + /** + * Example of objectTypeID: + * "D:trx:transferReport" - see {@link ObjectTypeHelper} "D:trx:tempTransferStore" + * "D:imap:imapAttach" + * + * @param objectTypeID + */ + public CmisWrapper doesNotHaveChildren(String objectTypeID) + { + checkChildren(objectTypeID, false); + return cmisAPI; + } + + /** + * Example of objectTypeID: + * "D:trx:transferReport" - see {@link ObjectTypeHelper} "D:trx:tempTransferStore" + * "D:imap:imapAttach" + * + * @param objectTypeID + */ + private PropertyDefinitionObject checkChildren(String objectTypeID, boolean exist) + { + ItemIterable values = cmisAPI.withCMISUtil().getTypeChildren(this.baseTypeID, includePropertyDefinition); + boolean foundChild = false; + PropertyDefinitionObject propDefinition = null; + for (Iterator iterator = values.iterator(); iterator.hasNext();) + { + ObjectType type = (ObjectType) iterator.next(); + LOG.info("Found child Object Type: {}", ToStringBuilder.reflectionToString(type, ToStringStyle.MULTI_LINE_STYLE)); + if (type.getId().equals(objectTypeID)) + { + foundChild = true; + propDefinition = new PropertyDefinitionObject(type); + break; + } + } + Assert.assertEquals(foundChild, exist, + String.format("Object Type with ID[%s] is found as children for Parent Type: [%s]", objectTypeID, this.baseTypeID)); + return propDefinition; + } + + public class PropertyDefinitionObject + { + ObjectType type; + + public PropertyDefinitionObject(ObjectType type) + { + this.type = type; + } + + public PropertyDefinitionObject propertyDefinitionIsEmpty() + { + STEP(String.format("%s Verify that property definitions map is empty.", CmisWrapper.STEP_PREFIX)); + Assert.assertTrue(type.getPropertyDefinitions().isEmpty(), "Property definitions is empty."); + return this; + } + + public PropertyDefinitionObject propertyDefinitionIsNotEmpty() + { + STEP(String.format("%s Verify that property definitions map is not empty.", CmisWrapper.STEP_PREFIX)); + Assert.assertFalse(type.getPropertyDefinitions().isEmpty(), "Property definitions is not empty."); + return this; + } + } + + private CmisWrapper checkDescendents(int depth, boolean exist, String... objectTypeIDs) + { + List> values = cmisAPI.withCMISUtil().getTypeDescendants(this.baseTypeID, depth, includePropertyDefinition); + for (String objectTypeID : objectTypeIDs) + { + boolean foundChild = false; + for (Tree tree : values) + { + if (tree.getItem().getId().equals(objectTypeID)) + { + foundChild = true; + break; + } + } + Assert.assertEquals(foundChild, exist, + String.format("Assert %b: Descendant [%s] is found as descendant for Type: [%s]", exist, objectTypeID, this.baseTypeID)); + + if (foundChild) + { + STEP(String.format("%s Cmis object '%s' is found as descendant.", CmisWrapper.STEP_PREFIX, objectTypeID)); + } + else + { + STEP(String.format("%s Cmis object '%s' is NOT found as descendant.", CmisWrapper.STEP_PREFIX, objectTypeID)); + } + } + return cmisAPI; + } + + /** + * Assert that specified descendantType is present in the depth of tree + * Depth can be -1 or >= 1 + * Example of objectTypeID: + * "D:trx:transferReport" - see {@link ObjectTypeHelper} "D:trx:tempTransferStore" + * "D:imap:imapAttach" + * + * @param objectTypeID + * @param depth + * @return + */ + public CmisWrapper hasDescendantType(int depth, String... objectTypeIDs) + { + return checkDescendents(depth, true, objectTypeIDs); + } + + /** + * Assert that specified descendantType is NOT present in the depth of tree + * Depth can be -1 or >= 1 + * Example of objectTypeID: + * "D:trx:transferReport" - see {@link ObjectTypeHelper} "D:trx:tempTransferStore" + * "D:imap:imapAttach" + * + * @param objectTypeID + * @param depth + * @return + */ + public CmisWrapper doesNotHaveDescendantType(int depth, String... objectTypeIDs) + { + checkDescendents(depth, false, objectTypeIDs); + return cmisAPI; + } +} diff --git a/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/CheckIn.java b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/CheckIn.java new file mode 100644 index 0000000000..1dbcc3f3dc --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/CheckIn.java @@ -0,0 +1,78 @@ +package org.alfresco.cmis.dsl; + +import org.alfresco.cmis.CmisWrapper; +import org.alfresco.utility.Utility; +import org.apache.chemistry.opencmis.client.api.Document; +import org.apache.chemistry.opencmis.commons.data.ContentStream; +import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException; + +import java.util.Map; + +/** + * DSL pertaining only to check in a {@link Document} + */ +public class CheckIn +{ + private CmisWrapper cmisWrapper; + private boolean version; + private Map properties; + private String content; + private String comment; + + public CheckIn(CmisWrapper cmisWrapper) + { + this.cmisWrapper = cmisWrapper; + } + + public CheckIn withMajorVersion() + { + this.version = true; + return this; + } + + public CheckIn withMinorVersion() + { + this.version = false; + return this; + } + + public CheckIn withContent(String content) + { + this.content = content; + return this; + } + + public CheckIn withoutComment() + { + this.comment = null; + return this; + } + + public CheckIn withComment(String comment) + { + this.comment = comment; + return this; + } + + public CmisWrapper checkIn() throws Exception + { + return checkIn(properties); + } + + public CmisWrapper checkIn(Map properties) throws Exception + { + ContentStream contentStream = cmisWrapper.withCMISUtil().getContentStream(content); + try + { + Document pwc = cmisWrapper.withCMISUtil().getPWCDocument(); + pwc.refresh(); + Utility.waitToLoopTime(2); + pwc.checkIn(version, properties, contentStream, comment); + } + catch(CmisStorageException st) + { + cmisWrapper.withCMISUtil().getPWCDocument().checkIn(version, properties, contentStream, comment); + } + return cmisWrapper; + } +} diff --git a/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/CmisAssertion.java b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/CmisAssertion.java new file mode 100644 index 0000000000..8fd4267fd5 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/CmisAssertion.java @@ -0,0 +1,1153 @@ +package org.alfresco.cmis.dsl; + +import static org.alfresco.utility.report.log.Step.STEP; + +import java.io.File; +import java.util.ArrayList; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.cmis.CmisWrapper; +import org.alfresco.utility.constants.UserRole; +import org.alfresco.utility.dsl.DSLAssertion; +import org.alfresco.utility.exception.TestConfigurationException; +import org.alfresco.utility.model.ContentModel; +import org.alfresco.utility.model.FileModel; +import org.alfresco.utility.model.FolderModel; +import org.alfresco.utility.model.GroupModel; +import org.alfresco.utility.model.UserModel; +import org.apache.chemistry.opencmis.client.api.ChangeEvent; +import org.apache.chemistry.opencmis.client.api.CmisObject; +import org.apache.chemistry.opencmis.client.api.Document; +import org.apache.chemistry.opencmis.client.api.Folder; +import org.apache.chemistry.opencmis.client.api.ItemIterable; +import org.apache.chemistry.opencmis.client.api.ObjectType; +import org.apache.chemistry.opencmis.client.api.OperationContext; +import org.apache.chemistry.opencmis.client.api.Property; +import org.apache.chemistry.opencmis.client.api.Relationship; +import org.apache.chemistry.opencmis.client.api.Rendition; +import org.apache.chemistry.opencmis.client.api.SecondaryType; +import org.apache.chemistry.opencmis.client.api.Session; +import org.apache.chemistry.opencmis.client.runtime.OperationContextImpl; +import org.apache.chemistry.opencmis.commons.data.Ace; +import org.apache.chemistry.opencmis.commons.data.Acl; +import org.apache.chemistry.opencmis.commons.data.CmisExtensionElement; +import org.apache.chemistry.opencmis.commons.enums.Action; +import org.apache.chemistry.opencmis.commons.enums.ChangeType; +import org.apache.chemistry.opencmis.commons.enums.ExtensionLevel; +import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships; +import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException; +import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException; +import org.apache.commons.lang3.StringUtils; +import org.testng.Assert; + +/** + * DSL with all assertion available for {@link CmisWrapper} + */ +public class CmisAssertion extends DSLAssertion +{ + public static String STEP_PREFIX = "CMIS:"; + + public CmisAssertion(CmisWrapper cmisAPI) + { + super(cmisAPI); + } + + public CmisWrapper cmisAPI() + { + return getProtocol(); + } + + @Override + public CmisWrapper existsInRepo() + { + STEP(String.format("CMIS: Assert that content '%s' exists in repository", cmisAPI().getLastResource())); + Assert.assertTrue(!cmisAPI().withCMISUtil().getCmisObject(cmisAPI().getLastResource()).getId().isEmpty(), + String.format("Content {%s} was found in repository", cmisAPI().getLastResource())); + return cmisAPI(); + } + + @Override + public CmisWrapper doesNotExistInRepo() + { + STEP(String.format("CMIS: Assert that content '%s' does not exist in repository", cmisAPI().getLastResource())); + boolean notFound = false; + try + { + cmisAPI().getSession().clear(); + cmisAPI().withCMISUtil().getCmisObject(cmisAPI().getLastResource()); + } + catch (CmisObjectNotFoundException | CmisRuntimeException e) + { + notFound = true; + } + Assert.assertTrue(notFound, String.format("Content {%s} was NOT found in repository", cmisAPI().getLastResource())); + return cmisAPI(); + } + + /** + * Verify changes for a specific object from cmis log + * + * @param model {@link ContentModel} + * @param changeTypes {@link ChangeType} + * @return + * @throws Exception + */ + public CmisWrapper contentModelHasChanges(ContentModel model, ChangeType... changeTypes) throws Exception + { + String token = cmisAPI().getRepositoryInfo().getLatestChangeLogToken(); + if (StringUtils.isEmpty(token)) + { + throw new TestConfigurationException("Please enable CMIS audit"); + } + ItemIterable events = cmisAPI().getSession().getContentChanges(token, true); + String lastObjectId = model.getNodeRef(); + boolean isChange = false; + for (ChangeType changeType : changeTypes) + { + STEP(String.format("%s Verify action %s for content: %s", CmisWrapper.STEP_PREFIX, changeType, model.getName())); + isChange = false; + for (ChangeEvent event : events) + { + if (event.getObjectId().equals(lastObjectId)) + { + if (changeType == event.getChangeType()) + { + isChange = true; + break; + } + } + } + Assert.assertTrue(isChange, String.format("Action %s for content: '%s' was found", changeType, model.getName())); + } + return cmisAPI(); + } + + /** + * Verify that a specific object does not have changes from cmis log + * + * @param model {@link ContentModel} + * @param changeTypes {@link ChangeType} + * @return + * @throws Exception + */ + public CmisWrapper contentModelDoesnotHaveChangesWithWrongToken(ContentModel model, ChangeType... changeTypes) throws Exception + { + String token = cmisAPI().getRepositoryInfo().getLatestChangeLogToken(); + if (StringUtils.isEmpty(token)) + { + throw new TestConfigurationException("Please enable CMIS audit"); + } + ItemIterable events = cmisAPI().getSession().getContentChanges(token + 1, true); + String lastObjectId = model.getNodeRef(); + boolean isChange = false; + for (ChangeType changeType : changeTypes) + { + STEP(String.format("%s Verify action %s for content: %s", CmisWrapper.STEP_PREFIX, changeType, model.getName())); + isChange = false; + for (ChangeEvent event : events) + { + if (event.getObjectId().equals(lastObjectId)) + { + if (changeType == event.getChangeType()) + { + isChange = true; + break; + } + } + } + Assert.assertFalse(isChange, String.format("Action %s for content: '%s' was found", changeType, model.getName())); + } + return cmisAPI(); + } + + /** + * Check if the {@link #getLastResource()} has the list of {@link Action} Example: + * {code} + * .hasAllowableActions(Action.CAN_CREATE_FOLDER); + * {code} + * + * @param actions + * @return + */ + public CmisWrapper hasAllowableActions(Action... actions) + { + CmisObject cmisObject = cmisAPI().withCMISUtil().getCmisObject(cmisAPI().getLastResource()); + for (Action action : actions) + { + STEP(String.format("%s Verify if object %s has allowable action %s", CmisWrapper.STEP_PREFIX, cmisObject.getName(), action.name())); + Assert.assertTrue(cmisObject.hasAllowableAction(action), String.format("Object %s does not have action %s", cmisObject.getName(), action.name())); + } + return cmisAPI(); + } + + /** + * Check if {@link #getLastResource()} object has actions returned + * from {@link org.apache.chemistry.opencmis.client.api.CmisObject#getAllowableActions()} + * + * @param actions + * @return + */ + public CmisWrapper isAllowableActionInList(Action... actions) + { + List currentActions = cmisAPI().withCMISUtil().getAllowableActions(); + for (Action action : actions) + { + STEP(String.format("%s Verify that action '%s' exists", CmisWrapper.STEP_PREFIX, action.name())); + Assert.assertTrue(currentActions.contains(action), String.format("Action %s was found", action.name())); + } + return cmisAPI(); + } + + /** + * Check if the {@link #getLastResource()) does not have the list of {@link Action} Example: + * {code} + * .doesNotHaveAllowableActions(Action.CAN_CREATE_FOLDER); + * {code} + * + * @param actions + * @return + */ + public CmisWrapper doesNotHaveAllowableActions(Action... actions) + { + CmisObject cmisObject = cmisAPI().withCMISUtil().getCmisObject(cmisAPI().getLastResource()); + for (Action action : actions) + { + STEP(String.format("%s Verify if object %s does not have allowable action %s", CmisWrapper.STEP_PREFIX, cmisObject.getName(), action.name())); + Assert.assertFalse(cmisObject.hasAllowableAction(action), String.format("Object %s does not have action %s", cmisObject.getName(), action.name())); + } + return cmisAPI(); + } + + /** + * Verify document content + * + * @param content String expected content + * @return + * @throws Exception + */ + public CmisWrapper contentIs(String content) throws Exception + { + STEP(String.format("%s Verify if content '%s' is the expected one", CmisWrapper.STEP_PREFIX, content)); + Assert.assertEquals(cmisAPI().withCMISUtil().getDocumentContent(), content, + String.format("The content of file %s - is the expected one", cmisAPI().getLastResource())); + return cmisAPI(); + } + + /** + * Verify document content contains specific details + * + * @param content String expected content + * @return + * @throws Exception + */ + public CmisWrapper contentContains(String content) throws Exception + { + STEP(String.format("%s Verify if content '%s' is the expected one", CmisWrapper.STEP_PREFIX, content)); + Assert.assertTrue(cmisAPI().withCMISUtil().getDocumentContent().contains(content), + String.format("The content of file %s - is the expected one", cmisAPI().getLastResource())); + return cmisAPI(); + } + + /** + * Verify if current resource has the id given + * + * @param id - expected object id + * @return + */ + public CmisWrapper objectIdIs(String id) + { + CmisObject objSource = cmisAPI().withCMISUtil().getCmisObject(cmisAPI().getLastResource()); + STEP(String.format("%s Verify if '%s' object has '%s' id", CmisWrapper.STEP_PREFIX, objSource.getName(), id)); + Assert.assertEquals(objSource.getId(), id, "Object has id."); + return cmisAPI(); + } + + /** + * Verify the value of the given property + * + * @param property - the property id + * @param value - expected property value + * @return + */ + public CmisWrapper contentPropertyHasValue(String property, String value) + { + CmisObject objSource = cmisAPI().withCMISUtil().getCmisObject(cmisAPI().getLastResource()); + STEP(String.format("%s Verify if '%s' property for '%s' content has '%s' value", CmisWrapper.STEP_PREFIX, property, objSource.getName(), value)); + Object propertyValue = objSource.getPropertyValue(property); + if (propertyValue instanceof ArrayList) + { + @SuppressWarnings({ "unchecked", "rawtypes" }) + ArrayList values = (ArrayList) propertyValue; + Assert.assertEquals(values.get(0).toString(), value, "Property has value."); + } + else + { + Assert.assertEquals(propertyValue.toString(), value, "Property has value."); + } + return cmisAPI(); + } + + /** + * Verify if {@link Document} is checked out + * + * @return + */ + public CmisWrapper documentIsCheckedOut() + { + Document document = cmisAPI().withCMISUtil().getCmisDocument(cmisAPI().getLastResource()); + STEP(String.format("%s Verify if document '%s' is checked out", CmisWrapper.STEP_PREFIX, document.getName())); + Assert.assertTrue(document.isVersionSeriesCheckedOut(), "Document is checkedout"); + return cmisAPI(); + } + + /** + * Verify if {@link Document} is private working copy (pwc) + * + * @return + */ + public CmisWrapper isPrivateWorkingCopy() + { + Document document = cmisAPI().withCMISUtil().getCmisDocument(cmisAPI().getLastResource()); + STEP(String.format("%s Verify if document '%s' is private working copy", CmisWrapper.STEP_PREFIX, document.getName())); + Assert.assertTrue(cmisAPI().withCMISUtil().isPrivateWorkingCopy()); + return cmisAPI(); + } + + /** + * Verify that {@link Document} is not private working copy (pwc) + * + * @return + */ + public CmisWrapper isNotPrivateWorkingCopy() + { + Document document = cmisAPI().withCMISUtil().getCmisDocument(cmisAPI().getLastResource()); + STEP(String.format("%s Verify if document '%s' PWC is not private working copy", CmisWrapper.STEP_PREFIX, document.getName())); + Assert.assertFalse(cmisAPI().withCMISUtil().isPrivateWorkingCopy()); + return cmisAPI(); + } + + /** + * Verify that {@link Document} is not checked out + * + * @return + */ + public CmisWrapper documentIsNotCheckedOut() + { + Document document = cmisAPI().withCMISUtil().getCmisDocument(cmisAPI().getLastResource()); + STEP(String.format("%s Verify if document '%s' is not checked out", CmisWrapper.STEP_PREFIX, document.getName())); + Assert.assertFalse(document.isVersionSeriesCheckedOut(), "Document is not checked out"); + return cmisAPI(); + } + + /** + * Verify if there is a relationship between current resource and the given target + * + * @param targetContent + * @return + */ + public CmisWrapper objectHasRelationshipWith(ContentModel targetContent) + { + OperationContext oc = new OperationContextImpl(); + oc.setIncludeRelationships(IncludeRelationships.SOURCE); + + CmisObject source = cmisAPI().withCMISUtil().getCmisObject(cmisAPI().getLastResource(), oc); + CmisObject target = cmisAPI().withCMISUtil().getCmisObject(targetContent.getCmisLocation()); + + STEP(String.format("%s Verify if source '%s' has relationship with '%s'", CmisWrapper.STEP_PREFIX, source.getName(), target.getName())); + List relTargetIds = new ArrayList<>(); + for (Relationship rel : source.getRelationships()) + { + relTargetIds.add(rel.getTarget().getId()); + } + Assert.assertTrue(relTargetIds.contains(target.getId()), + String.format("Relationship is created between source '%s' and target '%s'.", source.getName(), target.getName())); + return cmisAPI(); + } + + /** + * Verify document has version + * + * @param version String expected version + * @return + * @throws Exception + */ + public CmisWrapper documentHasVersion(double version) throws Exception + { + Document document = cmisAPI().withCMISUtil().getCmisDocument(cmisAPI().getLastResource()); + document.refresh(); + STEP(String.format("%s Verify if document '%s' has version '%s'", CmisWrapper.STEP_PREFIX, document.getName(), version)); + Assert.assertEquals(Double.parseDouble(document.getVersionLabel()), version, "File has version"); + return cmisAPI(); + } + + /** + * Verify parent from the {@link Folder} set as last resource + * + * @param contentModel + * @return + */ + public CmisWrapper folderHasParent(ContentModel contentModel) + { + STEP(String.format("%s Verify folder %s has parent %s", CmisWrapper.STEP_PREFIX, cmisAPI().getLastResource(), contentModel.getName())); + Assert.assertEquals(cmisAPI().withCMISUtil().getFolderParent().getName(), contentModel.getName(), "Folder name is not the expected one"); + return cmisAPI(); + } + + /** + * Verify base type id + * + * @param baseTypeId String expected object type value + * @return + * @throws Exception + */ + public CmisWrapper baseTypeIdIs(String baseTypeId) throws Exception + { + STEP(String.format("%s Verify if base object type '%s' is the expected one", CmisWrapper.STEP_PREFIX, baseTypeId)); + String actualBaseTypeIdValue = cmisAPI().withCMISUtil().getCmisObject(cmisAPI().getLastResource()).getType().getBaseTypeId().value(); + Assert.assertEquals(actualBaseTypeIdValue, baseTypeId, "Object type is the expected one"); + return cmisAPI(); + } + + /** + * Verify object type id + * + * @param objectTypeId String expected object type value + * @return + * @throws Exception + */ + public CmisWrapper objectTypeIdIs(String objectTypeId) throws Exception + { + STEP(String.format("%s Verify if object type id '%s' is the expected one", CmisWrapper.STEP_PREFIX, objectTypeId)); + String typeId = cmisAPI().withCMISUtil().getCmisObject(cmisAPI().getLastResource()).getType().getId(); + if (StringUtils.isEmpty(typeId)) + { + typeId = ""; + } + Assert.assertEquals(cmisAPI().withCMISUtil().getCmisObject(cmisAPI().getLastResource()).getType().getId(), objectTypeId, + "Object type id is the expected one"); + return cmisAPI(); + } + + /** + * Verify a specific object property + * + * @param propertyId + * @param value + * @return + */ + public CmisWrapper objectHasProperty(String propertyId, Object value) + { + CmisObject cmisObject = cmisAPI().withCMISUtil().getCmisObject(cmisAPI().getLastResource()); + STEP(String.format("%s Verify if object %s has property %s ", CmisWrapper.STEP_PREFIX, cmisObject.getName(), propertyId)); + Property property = cmisAPI().withCMISUtil().getProperty(propertyId); + Object propValue = property.getValue(); + if(propValue instanceof GregorianCalendar) + { + Date date = (Date) value; + long longDate = date.getTime(); + long actualDate = ((GregorianCalendar) propValue).getTimeInMillis(); + Assert.assertEquals(actualDate, longDate); + } + else + { + if (propValue == null) + { + propValue = ""; + } + Assert.assertEquals(property.getValue().toString(), value.toString(), String.format("Found property value %s", value)); + } + return cmisAPI(); + } + + /** + * Check if CMIS object contains a property. + * Example: + * ...assertObjectHasProperty("cmis:secondaryObjectTypeIds", "Secondary Object Type Ids","secondaryObjectTypeIds", "cmis:secondaryObjectTypeIds", + * "P:cm:titled", "P:sys:localized"); + * + * @param propertyId + * @param displayName + * @param localName + * @param queryName + * @param values + * @return + */ + public CmisWrapper objectHasProperty(String propertyId, String displayName, String localName, String queryName, String... values) + { + CmisObject cmisObject = cmisAPI().withCMISUtil().getCmisObject(cmisAPI().getLastResource()); + STEP(String.format("%s Verify if object %s has property %s ", CmisWrapper.STEP_PREFIX, cmisObject.getName(), propertyId)); + Property property = cmisAPI().withCMISUtil().getProperty(propertyId); + if (property != null) + { + Assert.assertEquals(property.getDisplayName(), displayName, "Property displayName"); + Assert.assertEquals(property.getLocalName(), localName, "Property localName"); + Assert.assertEquals(property.getQueryName(), queryName, "Property queryName"); + for (String value : values) + { + Assert.assertTrue(property.getValues().contains(value), "Property value"); + } + } + else + { + Assert.assertFalse(false, String.format("Object %s does not have property %s", cmisObject.getName(), propertyId)); + } + return cmisAPI(); + } + + /** + * Assert if the {@link #getLastResource()) has the latest major version set + */ + public CmisWrapper isLatestMajorVersion() + { + String path = cmisAPI().getLastResource(); + STEP(String.format("%s Verify that document from '%s' is latest major version", CmisWrapper.STEP_PREFIX, path)); + Assert.assertTrue(cmisAPI().withCMISUtil().getCmisDocument(path).isLatestMajorVersion(), String.format("Document from %s is last major version", path)); + return cmisAPI(); + } + + /** + * Verify that {@link Document} is not latest major version. + * + * @return + */ + public CmisWrapper isNotLatestMajorVersion() + { + String path = cmisAPI().getLastResource(); + STEP(String.format("%s Verify that document from '%s' is not latest major version", CmisWrapper.STEP_PREFIX, path)); + Assert.assertFalse(cmisAPI().withCMISUtil().getCmisDocument(path).isLatestMajorVersion(), + String.format("Document from %s is last major version", path)); + return cmisAPI(); + } + + /** + * Verify that renditions are available + */ + public CmisWrapper renditionIsAvailable() + { + STEP(String.format("%s Verify if renditions are available for %s", CmisWrapper.STEP_PREFIX, cmisAPI().getLastResource())); + List renditions = cmisAPI().withCMISUtil().getRenditions(); + Assert.assertTrue(renditions != null && !renditions.isEmpty()); + return cmisAPI(); + } + + /** + * Verify that thumbnail rendition is available + * + * @return + */ + public CmisWrapper thumbnailRenditionIsAvailable() + { + boolean found = false; + STEP(String.format("%s Verify if thumbnail rendition is available for %s", CmisWrapper.STEP_PREFIX, cmisAPI().getLastResource())); + List renditions = cmisAPI().withCMISUtil().getRenditions(); + for (Rendition rendition : renditions) + { + if (rendition.getKind().equals("cmis:thumbnail")) + { + found = true; + } + } + Assert.assertTrue(found, String.format("Thumbnail rendition found for", cmisAPI().getLastResource())); + return cmisAPI(); + } + + private boolean isSecondaryTypeAvailable(String secondaryTypeId) + { + boolean found = false; + List secondaryTypes = cmisAPI().withCMISUtil().getSecondaryTypes(); + for (SecondaryType type : secondaryTypes) + { + if (type.getId().equals(secondaryTypeId)) + { + found = true; + break; + } + } + + return found; + } + + /** + * Verify secondary type for specific {@link CmisObject} + * + * @param secondaryTypeId + * @return + */ + public CmisWrapper secondaryTypeIsAvailable(String secondaryTypeId) + { + STEP(String.format("%s Verify if '%s' secondary type is available for '%s'", CmisWrapper.STEP_PREFIX, secondaryTypeId, + new File(cmisAPI().getLastResource()).getName())); + Assert.assertTrue(isSecondaryTypeAvailable(secondaryTypeId), String.format("%s is available for %s", secondaryTypeId, cmisAPI().getLastResource())); + return cmisAPI(); + } + + /** + * Verify secondary type is not available for specific {@link CmisObject} + * + * @param secondaryTypeId + * @return + */ + public CmisWrapper secondaryTypeIsNotAvailable(String secondaryTypeId) + { + STEP(String.format("%s Verify if '%s' aspect is NOT available for %s", CmisWrapper.STEP_PREFIX, secondaryTypeId, cmisAPI().getLastResource())); + Assert.assertFalse(isSecondaryTypeAvailable(secondaryTypeId), + String.format("%s is NOT available for %s", secondaryTypeId, cmisAPI().getLastResource())); + return cmisAPI(); + } + + /** + * Verify document content length + * + * @param contentLength String expected content length + * @return + * @throws Exception + */ + public CmisWrapper contentLengthIs(long contentLength) throws Exception + { + STEP(String.format("%s Verify if content length '%s' is the expected one", CmisWrapper.STEP_PREFIX, contentLength)); + Document lastVersion = cmisAPI().withCMISUtil().getCmisDocument(cmisAPI().getLastResource()); + lastVersion.refresh(); + Assert.assertEquals(lastVersion.getContentStreamLength(), contentLength, "File content is the expected one"); + return cmisAPI(); + } + + /** + * e.g. assertFolderHasDescendant(1, file1) will verify if file1 is a direct descendant of {@link #getLastResource()} + * + * @param depth {@link #getFolderDescendants(int)} + * @param contentModels {@link #getCmisObjectsFromContentModels(ContentModel...)} + */ + public void hasDescendants(int depth, ContentModel... contentModels) + { + STEP(String.format("%s Assert that folder %s has descendants in depth %d:", STEP_PREFIX, getProtocol().getLastResource(), depth)); + CmisObject currentCmisObject = getProtocol().withCMISUtil().getCmisObject(getProtocol().getLastResource()); + List cmisObjects = getProtocol().withCMISUtil().getCmisObjectsFromContentModels(contentModels); + List folderDescendants = getProtocol().withCMISUtil().getFolderDescendants(depth); + for (CmisObject cmisObject : cmisObjects) + { + boolean found = false; + STEP(String.format("%s Verify that folder '%s' has descendant %s", CmisWrapper.STEP_PREFIX, currentCmisObject.getName(), cmisObject.getName())); + for (CmisObject folderDescendant : folderDescendants) + if (folderDescendant.getId().equals(cmisObject.getId())) + { + found = true; + break; + } + Assert.assertTrue(found, String.format("Folder %s does not have descendant %s", currentCmisObject.getName(), cmisObject)); + } + } + + public void doesNotHaveDescendants(int depth) + { + STEP(String.format("%s Assert that folder %s does not have descendants in depth %d:", STEP_PREFIX, getProtocol().getLastResource(), depth)); + CmisObject currentCmisObject = getProtocol().withCMISUtil().getCmisObject(getProtocol().getLastResource()); + List folderDescendants = getProtocol().withCMISUtil().getFolderDescendants(depth); + Assert.assertTrue(folderDescendants.isEmpty(), String.format("Folder %s should not have descendants", currentCmisObject.getName())); + } + + /** + * Verify that {@link CmisObject} has ACLs (Access Control Lists) + * + * @return + */ + public CmisWrapper hasAcls() + { + STEP(String.format("%s Verify that %s has acls", CmisWrapper.STEP_PREFIX, cmisAPI().getLastResource())); + String path = cmisAPI().getLastResource(); + STEP(String.format("%s Get Acls for %s", CmisWrapper.STEP_PREFIX, path)); + Assert.assertNotNull(cmisAPI().withCMISUtil().getAcls(), String.format("Acls found for %s", path)); + return cmisAPI(); + } + + /** + * Depending on the specified depth, checks that all the contents from contentModels list are present in the current folder tree structure + * + * @param depth the depth of the tree to check, must be -1 or >= 1 + * @param contentModels expected list of contents to be found in the tree + * @return + */ + public CmisWrapper hasFolderTree(int depth, ContentModel... contentModels) + { + CmisObject currentCmisObject = getProtocol().withCMISUtil().getCmisObject(getProtocol().getLastResource()); + List cmisObjects = getProtocol().withCMISUtil().getCmisObjectsFromContentModels(contentModels); + List folderDescendants = getProtocol().withCMISUtil().getFolderTree(depth); + for (CmisObject cmisObject : cmisObjects) + { + boolean found = false; + STEP(String.format("%s Verify that folder '%s' has folder tree %s", CmisWrapper.STEP_PREFIX, currentCmisObject.getName(), cmisObject.getName())); + for (CmisObject folderDescendant : folderDescendants) + if (folderDescendant.getId().equals(cmisObject.getId())) + found = true; + Assert.assertTrue(found, String.format("Folder %s does not have folder tree %s", currentCmisObject.getName(), cmisObject)); + } + return cmisAPI(); + } + + /** + * Verify the permission for a specific user from the last resource object + * + * @param userModel {@link UserModel} user to verify + * @param role {@link UserRole} user role to verify + * @return + */ + public CmisWrapper permissionIsSetForUser(UserModel userModel, UserRole role) + { + STEP(String.format("%s Verify that user %s has role %s set to content %s", CmisWrapper.STEP_PREFIX, userModel.getUsername(), role.name(), + cmisAPI().getLastResource())); + Assert.assertTrue(checkPermission(userModel.getUsername(), role.getRoleId()), + String.format("User %s has permission %s", userModel.getUsername(), role.name())); + return cmisAPI(); + } + + /** + * Verify the permission for a specific group of users from the last resource object + * + * @param groupModel {@link GroupModel} group to verify + * @param role {@link UserRole} user role to verify + * @return + */ + public CmisWrapper permissionIsSetForGrup(GroupModel groupModel, UserRole role) + { + STEP(String.format("%s Verify that user %s has role %s set to content %s", CmisWrapper.STEP_PREFIX, groupModel.getDisplayName(), role.name(), + cmisAPI().getLastResource())); + Assert.assertTrue(checkPermission(groupModel.getDisplayName(), role.getRoleId()), + String.format("User %s has permission %s", groupModel.getDisplayName(), role.name())); + return cmisAPI(); + } + + private boolean checkPermission(String user, String permission) + { + Acl acl = cmisAPI().withCMISUtil().getAcls(); + if (acl == null) + { + throw new CmisRuntimeException(String.format("No acls returned for '%s'", cmisAPI().getLastResource())); + } + List aces = acl.getAces(); + boolean found = false; + for (Ace ace : aces) + { + if (ace.getPrincipalId().equals(user) && ace.getPermissions().get(0).equals(permission)) + { + found = true; + break; + } + } + return found; + } + + /** + * Verify the permission for a specific user from the last resource object + * + * @param userModel {@link UserModel} + * @param permission to verify + * @return + */ + public CmisWrapper permissionIsSetForUser(UserModel userModel, String permission) + { + STEP(String.format("%s Verify that user %s has role %s set to content %s", CmisWrapper.STEP_PREFIX, userModel.getUsername(), permission, + cmisAPI().getLastResource())); + Assert.assertTrue(checkPermission(userModel.getUsername(), permission), + String.format("User %s has permission %s", userModel.getUsername(), permission)); + return cmisAPI(); + } + + /** + * Verify that permission is not set for a specific user from the last resource object + * + * @param userModel {@link UserModel} user to verify + * @param role {@link UserRole} user role to verify + * @return + */ + public CmisWrapper permissionIsNotSetForUser(UserModel userModel, UserRole role) + { + STEP(String.format("%s Verify that user %s doesn't have role %s set to content %s", CmisWrapper.STEP_PREFIX, userModel.getUsername(), role.name(), + cmisAPI().getLastResource())); + Assert.assertFalse(checkPermission(userModel.getUsername(), role.getRoleId()), + String.format("User %s has permission %s", userModel.getUsername(), role.name())); + return cmisAPI(); + } + + /** + * Verify that permission is not set for a specific user from the last resource object + * + * @param userModel {@link UserModel} user to verify + * @param permission to verify + * @return + */ + public CmisWrapper permissionIsNotSetForUser(UserModel userModel, String permission) + { + STEP(String.format("%s Verify that user %s doesn't have permission %s set to content %s", CmisWrapper.STEP_PREFIX, userModel.getUsername(), permission, + cmisAPI().getLastResource())); + Assert.assertFalse(checkPermission(userModel.getUsername(), permission), + String.format("User %s has permission %s", userModel.getUsername(), permission)); + return cmisAPI(); + } + + public CmisWrapper typeDefinitionIs(ContentModel contentModel) + { + CmisObject cmisObject = cmisAPI().withCMISUtil().getCmisObject(contentModel.getCmisLocation()); + STEP(String.format("%s Verify that object '%s' type definition matches '%s' type definition", CmisWrapper.STEP_PREFIX, cmisObject.getName(), + cmisAPI().withCMISUtil().getTypeDefinition().getId())); + Assert.assertTrue(cmisAPI().withCMISUtil().getTypeDefinition().equals(cmisObject.getType()), String.format( + "Object '%s' type definition does not match '%s' type definition", cmisObject.getName(), cmisAPI().withCMISUtil().getTypeDefinition().getId())); + return cmisAPI(); + } + + /** + * Verify that a specific folder(set by calling {@link org.alfresco.cmis.CmisWrapper#usingResource(ContentModel)}) + * contains checked out documents + * + * @param contentModels checked out documents to verify + * @return + */ + public CmisWrapper folderHasCheckedOutDocument(ContentModel... contentModels) + { + List cmisObjectList = cmisAPI().withCMISUtil().getCmisObjectsFromContentModels(contentModels); + List cmisCheckedOutDocuments = cmisAPI().withCMISUtil().getCheckedOutDocumentsFromFolder(); + for (CmisObject cmisObject : cmisObjectList) + { + Assert.assertTrue(cmisAPI().withCMISUtil().isCmisObjectContainedInCmisCheckedOutDocumentsList(cmisObject, cmisCheckedOutDocuments), + String.format("Folder %s does not contain checked out document %s", cmisAPI().getLastResource(), cmisObject)); + } + return cmisAPI(); + } + + /** + * Verify that a specific folder(set by calling {@link org.alfresco.cmis.CmisWrapper#usingResource(ContentModel)}) + * contains checked out documents in a specific order. + * + * @param context {@link OperationContext} + * @param contentModels documents to verify in the order returned by the {@link OperationContext} + * @return + */ + public CmisWrapper folderHasCheckedOutDocument(OperationContext context, ContentModel... contentModels) + { + List cmisObjectList = cmisAPI().withCMISUtil().getCmisObjectsFromContentModels(contentModels); + List cmisCheckedOutDocuments = cmisAPI().withCMISUtil().getCheckedOutDocumentsFromFolder(context); + for (int i = 0; i < cmisObjectList.size(); i++) + { + Assert.assertEquals(cmisObjectList.get(i).getId().split(";")[0], cmisCheckedOutDocuments.get(i).getId().split(";")[0], + String.format("Folder %s does not contain checked out document %s", cmisAPI().getLastResource(), cmisObjectList.get(i).getName())); + } + return cmisAPI(); + } + + /** + * Verify checked out documents from {@link Session} + * + * @param contentModels documents to verify + * @return + */ + public CmisWrapper sessionHasCheckedOutDocument(ContentModel... contentModels) + { + List cmisObjectList = cmisAPI().withCMISUtil().getCmisObjectsFromContentModels(contentModels); + List cmisCheckedOutDocuments = cmisAPI().withCMISUtil().getCheckedOutDocumentsFromSession(); + for (CmisObject cmisObject : cmisObjectList) + { + Assert.assertTrue(cmisAPI().withCMISUtil().isCmisObjectContainedInCmisCheckedOutDocumentsList(cmisObject, cmisCheckedOutDocuments), + String.format("Session does not contain checked out document %s", cmisObject)); + } + return cmisAPI(); + } + + /** + * Verify checked out documents from {@link Session} in a specific order set in {@link OperationContext} + * + * @param context {@link OperationContext} + * @param contentModels documents to verify + * @return + */ + public CmisWrapper sessionHasCheckedOutDocument(OperationContext context, ContentModel... contentModels) + { + List cmisObjectList = cmisAPI().withCMISUtil().getCmisObjectsFromContentModels(contentModels); + List cmisCheckedOutDocuments = cmisAPI().withCMISUtil().getCheckedOutDocumentsFromSession(context); + for (int i = 0; i < cmisObjectList.size(); i++) + { + Assert.assertEquals(cmisObjectList.get(i).getId().split(";")[0], cmisCheckedOutDocuments.get(i).getId().split(";")[0], + String.format("Session does not contain checked out document %s", cmisObjectList.get(i).getName())); + } + return cmisAPI(); + } + + /** + * Verify that checked out documents are not found in {@link Session} + * + * @param contentModels documents to verify + * @return + */ + public CmisWrapper sessioDoesNotHaveCheckedOutDocument(ContentModel... contentModels) + { + List cmisObjectList = cmisAPI().withCMISUtil().getCmisObjectsFromContentModels(contentModels); + List cmisCheckedOutDocuments = cmisAPI().withCMISUtil().getCheckedOutDocumentsFromSession(); + for (CmisObject cmisObject : cmisObjectList) + { + Assert.assertFalse(cmisAPI().withCMISUtil().isCmisObjectContainedInCmisCheckedOutDocumentsList(cmisObject, cmisCheckedOutDocuments), + String.format("Session does contain checked out document %s", cmisObject)); + } + return cmisAPI(); + } + + /** + * Verify that {@link CmisObject} has a specific aspect extension + * + * @param aspectId + * @return + */ + public CmisWrapper hasAspectExtension(String aspectId) + { + STEP(String.format("Verify that aspect %s is applied to %s", aspectId, cmisAPI().getLastResource())); + boolean found = false; + List extensions = cmisAPI().withCMISUtil().getCmisObject(cmisAPI().getLastResource()).getExtensions(ExtensionLevel.PROPERTIES); + for (CmisExtensionElement extElement : extensions) + { + if (extElement.getName().equals("aspects")) + { + List aspects = extElement.getChildren(); + for (CmisExtensionElement aspect : aspects) + { + if (aspect.getValue() != null) + { + if (aspect.getValue().equals(aspectId)) + { + found = true; + } + } + } + } + } + Assert.assertTrue(found, String.format("Aspect extension %s for %s was found", aspectId, cmisAPI().getLastResource())); + return cmisAPI(); + } + + /** + * Verify the parents for a {@link CmisObject} + * + * @param parentsList + * @return + */ + public CmisWrapper hasParents(String... parentsList) + { + List folderNames = new ArrayList<>(); + List parents = new ArrayList<>(); + String source = cmisAPI().getLastResource(); + CmisObject objSource = cmisAPI().withCMISUtil().getCmisObject(source); + STEP(String.format("%s Verify the parents for '%s'.", CmisWrapper.STEP_PREFIX, objSource.getName())); + if (objSource instanceof Document) + { + Document d = (Document) objSource; + parents = d.getParents(); + } + else if (objSource instanceof Folder) + { + Folder f = (Folder) objSource; + parents = f.getParents(); + } + for (Folder folder : parents) + { + folderNames.add(folder.getName()); + } + Assert.assertEqualsNoOrder(folderNames.toArray(), parentsList, "Parents list is the expected one."); + return cmisAPI(); + } + + public CmisWrapper descriptionIs(String description) + { + String source = cmisAPI().getLastResource(); + STEP(String.format("%s Verify object '%s' description is '%s'", CmisWrapper.STEP_PREFIX, source, description)); + CmisObject cmisObject = cmisAPI().withCMISUtil().getCmisObject(source); + Assert.assertEquals(description, cmisObject.getDescription()); + return cmisAPI(); + } + + /** + * Verify if folder children exist in parent folder + * + * @param fileModel children files + * @return + * @throws Exception + */ + public CmisWrapper hasFolders(FolderModel... folderModel) throws Exception + { + String currentSpace = cmisAPI().getCurrentSpace(); + List folders = cmisAPI().getFolders(); + for (FolderModel folder : folderModel) + { + STEP(String.format("%s Verify that folder %s is in %s", CmisWrapper.STEP_PREFIX, folder.getName(), currentSpace)); + Assert.assertTrue(cmisAPI().withCMISUtil().isFolderInList(folder, folders), String.format("Folder %s is in %s", folder.getName(), currentSpace)); + } + return cmisAPI(); + } + + /** + * Verify if file children exist in parent folder + * + * @param fileModel children files + * @return + * @throws Exception + */ + public CmisWrapper hasFiles(FileModel... fileModel) throws Exception + { + String currentSpace = cmisAPI().getLastResource(); + List files = cmisAPI().getFiles(); + for (FileModel file : fileModel) + { + STEP(String.format("%s Verify that file '%s' is in '%s'", CmisWrapper.STEP_PREFIX, file.getName(), currentSpace)); + Assert.assertTrue(cmisAPI().withCMISUtil().isFileInList(file, files), String.format("File %s is in %s", file.getName(), currentSpace)); + } + return cmisAPI(); + } + + /** + * Verify if file(s) children exist in parent folder + * + * @param fileModels children files + * @return + * @throws Exception + */ + public CmisWrapper doesNotHaveFile(FileModel... fileModels) throws Exception + { + String currentSpace = cmisAPI().getLastResource(); + List files = cmisAPI().getFiles(); + for (FileModel fileModel : fileModels) + { + STEP(String.format("%s Verify that file '%s' is not in '%s'", CmisWrapper.STEP_PREFIX, fileModel.getName(), currentSpace)); + Assert.assertFalse(cmisAPI().withCMISUtil().isFileInList(fileModel, files), String.format("File %s is in %s", fileModel.getName(), currentSpace)); + } + return cmisAPI(); + } + + /** + * Verify if folder(s) children exist in parent folder + * + * @param folderModels children files + * @return + * @throws Exception + */ + public CmisWrapper doesNotHaveFolder(FolderModel... folderModels) throws Exception + { + String currentSpace = cmisAPI().getLastResource(); + List folders = cmisAPI().getFolders(); + for (FolderModel folderModel : folderModels) + { + STEP(String.format("%s Verify that folder '%s' is not in '%s'", CmisWrapper.STEP_PREFIX, folderModel.getName(), currentSpace)); + Assert.assertFalse(cmisAPI().withCMISUtil().isFolderInList(folderModel, folders), + String.format("File %s is in %s", folderModel.getName(), currentSpace)); + } + return cmisAPI(); + } + + /** + * Verify the children(files and folders) from a parent folder + * + * @param contentModel children + * @return + * @throws Exception + */ + public CmisWrapper hasChildren(ContentModel... contentModel) throws Exception + { + String currentSpace = cmisAPI().getCurrentSpace(); + Map mapContents = cmisAPI().withCMISUtil().getChildren(); + List contents = new ArrayList(); + for (Map.Entry entry : mapContents.entrySet()) + { + contents.add(entry.getKey()); + } + for (ContentModel content : contentModel) + { + STEP(String.format("%s Verify that file %s is in %s", CmisWrapper.STEP_PREFIX, content.getName(), currentSpace)); + Assert.assertTrue(cmisAPI().withCMISUtil().isContentInList(content, contents), + String.format("Content %s is in %s", content.getName(), currentSpace)); + } + return cmisAPI(); + } + + + public CmisWrapper hasUniqueChildren(int numberOfChildren) throws Exception + { + STEP(String.format("%s Verify that current folder has %d unique children", CmisWrapper.STEP_PREFIX, numberOfChildren)); + Map mapContents = cmisAPI().withCMISUtil().getChildren(); + + Set documentIds = new HashSet(); + for (ContentModel key : mapContents.keySet()) + { + documentIds.add(key.getName()); + } + Assert.assertTrue(numberOfChildren==documentIds.size(), String.format("Current folder contains %d unique children, but expected is %d", documentIds.size(), numberOfChildren)); + return cmisAPI(); + } + + /** + * Get check in comment for last document version + * + * @param comment to verify + * @return + */ + public CmisWrapper hasCheckInCommentLastVersion(String comment) + { + String source = cmisAPI().getLastResource(); + STEP(String.format("%s Verify check in comment for last version of %s", CmisWrapper.STEP_PREFIX, source)); + Document document = cmisAPI().withCMISUtil().getCmisDocument(source); + Assert.assertEquals(comment, document.getCheckinComment(), String.format("Document %s has check in comment %s", document.getName(), comment)); + return cmisAPI(); + } + + /** + * Get check in comment for a specific document version + * + * @param documentVersion version of document + * @param comment to verify + * @return + */ + public CmisWrapper hasCheckInCommentForVersion(double documentVersion, String comment) + { + String source = cmisAPI().getLastResource(); + STEP(String.format("%s Verify check in comment for version %s of %s", CmisWrapper.STEP_PREFIX, documentVersion, source)); + String documentId = cmisAPI().withCMISUtil().getObjectId(source).split(";")[0]; + documentId = documentId + ";" + documentVersion; + Document document = (Document) cmisAPI().withCMISUtil().getCmisObjectById(documentId); + Assert.assertEquals(comment, document.getCheckinComment(), String.format("Document %s has check in comment %s", document.getName(), comment)); + return cmisAPI(); + } + + /** + * Verify failed deleted objects after delete tree action + * + * @param nodeRef objects to verify + * @return + */ + public CmisWrapper hasFailedDeletedObject(String nodeRef) + { + STEP(String.format("%s Verify failed deleted object from %s", CmisWrapper.STEP_PREFIX, cmisAPI().getLastResource())); + Assert.assertTrue(cmisAPI().deleteTreeFailedObjects.contains(nodeRef), String.format("Object %s found after delete", nodeRef)); + return cmisAPI(); + } + + /** + * Verify if there is a relationship between current resource and the given target + * + * @param user + * @return + */ + public CmisWrapper userIsAssigned(UserModel user) + { + OperationContext oc = new OperationContextImpl(); + oc.setIncludeRelationships(IncludeRelationships.SOURCE); + CmisObject source = cmisAPI().withCMISUtil().getCmisObject(cmisAPI().getLastResource(), oc); + String userNodeRef = cmisAPI().withCMISUtil().getUserNodeRef(user); + + STEP(String.format("%s Verify if user '%s' has relationship with '%s'", CmisWrapper.STEP_PREFIX, user.getUsername(), source.getName())); + List relTargetIds = new ArrayList<>(); + for (Relationship rel : source.getRelationships()) + { + relTargetIds.add(rel.getTarget().getId()); + } + Assert.assertTrue(relTargetIds.contains(userNodeRef), + String.format("Relationship is created between source '%s' and target '%s'.", source.getName(), user.getUsername())); + return cmisAPI(); + } +} diff --git a/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/CmisUtil.java b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/CmisUtil.java new file mode 100644 index 0000000000..92a724c1df --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/CmisUtil.java @@ -0,0 +1,751 @@ +package org.alfresco.cmis.dsl; + +import static org.alfresco.utility.report.log.Step.STEP; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.alfresco.cmis.CmisWrapper; +import org.alfresco.cmis.exception.InvalidCmisObjectException; +import org.alfresco.utility.LogFactory; +import org.alfresco.utility.Utility; +import org.alfresco.utility.constants.UserRole; +import org.alfresco.utility.exception.IORuntimeException; +import org.alfresco.utility.model.ContentModel; +import org.alfresco.utility.model.FileModel; +import org.alfresco.utility.model.FolderModel; +import org.alfresco.utility.model.GroupModel; +import org.alfresco.utility.model.UserModel; +import org.apache.chemistry.opencmis.client.api.CmisObject; +import org.apache.chemistry.opencmis.client.api.Document; +import org.apache.chemistry.opencmis.client.api.FileableCmisObject; +import org.apache.chemistry.opencmis.client.api.Folder; +import org.apache.chemistry.opencmis.client.api.ItemIterable; +import org.apache.chemistry.opencmis.client.api.ObjectType; +import org.apache.chemistry.opencmis.client.api.OperationContext; +import org.apache.chemistry.opencmis.client.api.Property; +import org.apache.chemistry.opencmis.client.api.QueryResult; +import org.apache.chemistry.opencmis.client.api.Rendition; +import org.apache.chemistry.opencmis.client.api.SecondaryType; +import org.apache.chemistry.opencmis.client.api.Tree; +import org.apache.chemistry.opencmis.commons.PropertyIds; +import org.apache.chemistry.opencmis.commons.data.Ace; +import org.apache.chemistry.opencmis.commons.data.Acl; +import org.apache.chemistry.opencmis.commons.data.AclCapabilities; +import org.apache.chemistry.opencmis.commons.data.ContentStream; +import org.apache.chemistry.opencmis.commons.data.PermissionMapping; +import org.apache.chemistry.opencmis.commons.data.PropertyData; +import org.apache.chemistry.opencmis.commons.data.RepositoryInfo; +import org.apache.chemistry.opencmis.commons.enums.Action; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException; +import org.apache.chemistry.opencmis.commons.exceptions.CmisVersioningException; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.testng.collections.Lists; + +/** + * DSL utility for managing CMIS objects + */ +public class CmisUtil +{ + private CmisWrapper cmisAPI; + private Logger LOG = LogFactory.getLogger(); + + public CmisUtil(CmisWrapper cmisAPI) + { + this.cmisAPI = cmisAPI; + } + + /** + * Get cmis object by object id + * + * @param objectId cmis object id + * @return CmisObject cmis object + */ + public CmisObject getCmisObjectById(String objectId) + { + LOG.debug("Get CMIS object by ID {}", objectId); + if (cmisAPI.getSession() == null) + { + throw new CmisRuntimeException("Please authenticate user, call: cmisAPI.authenticate(..)!"); + } + if (objectId == null) + { + throw new InvalidCmisObjectException("Invalid content id"); + } + return cmisAPI.getSession().getObject(objectId); + } + + /** + * Get cmis object by object id with OperationContext + * + * @param objectId cmis object id + * @param context OperationContext + * @return CmisObject cmis object + */ + public CmisObject getCmisObjectById(String objectId, OperationContext context) + { + if (cmisAPI.getSession() == null) + { + throw new CmisRuntimeException("Please authenticate user, call: cmisAPI.authenticate(..)!"); + } + if (objectId == null) + { + throw new InvalidCmisObjectException("Invalid content id"); + } + return cmisAPI.getSession().getObject(objectId, context); + } + + /** + * Get cmis object by path + * + * @param pathToItem String path to item + * @return CmisObject cmis object + */ + public CmisObject getCmisObject(String pathToItem) + { + if (cmisAPI.getSession() == null) + { + throw new CmisRuntimeException("Please authenticate user, call: cmisAPI.authenticate(..)!"); + } + if (pathToItem == null) + { + throw new InvalidCmisObjectException("Invalid path set for content"); + } + CmisObject cmisObject = cmisAPI.getSession().getObjectByPath(Utility.removeLastSlash(pathToItem)); + if (cmisObject instanceof Document) + { + if (!((Document) cmisObject).getVersionLabel().contentEquals("pwc")) + { + // get last version of document + cmisObject = ((Document) cmisObject).getObjectOfLatestVersion(false); + } + else + { + // get pwc document + cmisObject = cmisAPI.getSession().getObject(((Document) cmisObject).getObjectOfLatestVersion(false).getVersionSeriesCheckedOutId()); + } + } + return cmisObject; + } + + /** + * Get cmis object by path with context + * + * @param pathToItem String path to item + * @param context OperationContext + * @return CmisObject cmis object + */ + public CmisObject getCmisObject(String pathToItem, OperationContext context) + { + if (cmisAPI.getSession() == null) + { + throw new CmisRuntimeException("Please authenticate user!"); + } + if (pathToItem == null) + { + throw new InvalidCmisObjectException("Invalid path set for content"); + } + CmisObject cmisObject = cmisAPI.getSession().getObjectByPath(Utility.removeLastSlash(pathToItem), context); + if (cmisObject instanceof Document) + { + if (!((Document) cmisObject).getVersionLabel().contentEquals("pwc")) + { + // get last version of document + cmisObject = ((Document) cmisObject).getObjectOfLatestVersion(false, context); + } + else + { + // get pwc document + cmisObject = cmisAPI.getSession().getObject(((Document) cmisObject).getObjectOfLatestVersion(false, context).getVersionSeriesCheckedOutId()); + } + } + return cmisObject; + } + + /** + * Get Document object for a file + * + * @param path String path to document + * @return {@link Document} object + */ + public Document getCmisDocument(final String path) + { + LOG.debug("Get CMIS Document by path {}", path); + Document d = null; + CmisObject docObj = getCmisObject(path); + if (docObj instanceof Document) + { + d = (Document) docObj; + } + else if (docObj instanceof Folder) + { + throw new InvalidCmisObjectException("Content at " + path + " is not a file"); + } + return d; + } + + /** + * Get Folder object for a folder + * + * @param path String path to folder + * @return {@link Folder} object + */ + public Folder getCmisFolder(final String path) + { + Folder f = null; + CmisObject folderObj = getCmisObject(path); + if (folderObj instanceof Folder) + { + f = (Folder) folderObj; + } + else if (folderObj instanceof Document) + { + throw new InvalidCmisObjectException("Content at " + path + " is not a folder"); + } + return f; + } + + /** + * Helper method to get the contents of a stream + * + * @param stream + * @return + * @throws IORuntimeException + */ + protected String getContentAsString(ContentStream stream) + { + LOG.debug("Get Content as String {}", stream); + InputStream inputStream = stream.getStream(); + String result; + try + { + result = IOUtils.toString(inputStream, StandardCharsets.UTF_8); + } + catch (IOException e) + { + throw new IORuntimeException(e); + } + IOUtils.closeQuietly(inputStream); + return result; + } + + /** + * Copy all the children of the source folder to the target folder + * + * @param sourceFolder + * @param targetFolder + */ + protected void copyChildrenFromFolder(Folder sourceFolder, Folder targetFolder) + { + for (Tree t : sourceFolder.getDescendants(-1)) + { + CmisObject obj = t.getItem(); + if (obj instanceof Document) + { + Document d = (Document) obj; + d.copy(targetFolder); + } + else if (obj instanceof Folder) + { + copyFolder((Folder) obj, targetFolder); + } + } + } + + /** + * Copy folder with all children + * + * @param sourceFolder source folder + * @param targetFolder target folder + * @return CmisObject of new created folder + */ + public CmisObject copyFolder(Folder sourceFolder, Folder targetFolder) + { + Map folderProperties = new HashMap(2); + folderProperties.put(PropertyIds.NAME, sourceFolder.getName()); + folderProperties.put(PropertyIds.OBJECT_TYPE_ID, sourceFolder.getBaseTypeId().value()); + Folder newFolder = targetFolder.createFolder(folderProperties); + copyChildrenFromFolder(sourceFolder, newFolder); + return newFolder; + } + + protected boolean isPrivateWorkingCopy() + { + boolean result = false; + try + { + result = getPWCDocument().isPrivateWorkingCopy(); + } + catch (CmisVersioningException cmisVersioningException) + { + result = false; + } + return result; + } + + /** + * Returns the PWC (private working copy) ID of the document version series + */ + public Document getPWCDocument() + { + Document document = getCmisDocument(cmisAPI.getLastResource()); + String pwcId = document.getVersionSeriesCheckedOutId(); + if (pwcId != null) + { + return (Document) cmisAPI.getSession().getObject(pwcId); + } + else + { + throw new CmisVersioningException(String.format("Document %s is not checked out", document.getName())); + } + } + + public FileModel getPWCFileModel() + { + Document document = getPWCDocument(); + String[] pathTokens = cmisAPI.getLastResource().split("/"); + String path = ""; + for (int i = 0; i < pathTokens.length - 1; i++) + path = Utility.buildPath(path, pathTokens[i]); + path = Utility.buildPath(path, document.getName()); + + FileModel fileModel = new FileModel(); + fileModel.setName(document.getName()); + fileModel.setCmisLocation(path); + return fileModel; + } + + protected Folder getFolderParent() + { + return getCmisFolder(cmisAPI.getLastResource()).getFolderParent(); + } + + /** + * @return List of allowable actions for the current object + */ + protected List getAllowableActions() + { + return Lists.newArrayList(getCmisObject(cmisAPI.getLastResource()).getAllowableActions().getAllowableActions()); + } + + /** + * Returns the requested property. If the property is not available, null is returned + * + * @param propertyId + * @return CMIS Property + */ + protected Property getProperty(String propertyId) + { + CmisObject cmisObject = getCmisObject(cmisAPI.getLastResource()); + return cmisObject.getProperty(propertyId); + } + + protected List getRenditions() + { + OperationContext operationContext = cmisAPI.getSession().createOperationContext(); + operationContext.setRenditionFilterString("*"); + CmisObject obj = cmisAPI.getSession().getObjectByPath(cmisAPI.getLastResource(), operationContext); + obj.refresh(); + List renditions = obj.getRenditions(); + int retry = 0; + while ((renditions == null || renditions.isEmpty()) && retry < Utility.retryCountSeconds) + { + Utility.waitToLoopTime(1); + obj.refresh(); + renditions = obj.getRenditions(); + retry++; + } + return obj.getRenditions(); + } + + protected List getSecondaryTypes() + { + CmisObject obj = getCmisObject(cmisAPI.getLastResource()); + obj.refresh(); + return obj.getSecondaryTypes(); + } + + /** + * Get the children from a parent folder + * + * @return Map + */ + public Map getChildren() + { + String folderParent = cmisAPI.getLastResource(); + ItemIterable children = cmisAPI.withCMISUtil().getCmisFolder(folderParent).getChildren(); + Map contents = new HashMap(); + for (CmisObject o : children) + { + ContentModel content = new ContentModel(o.getName()); + content.setNodeRef(o.getId()); + content.setDescription(o.getDescription()); + content.setCmisLocation(Utility.buildPath(folderParent, o.getName())); + contents.put(content, o.getType()); + } + return contents; + } + + /** + * Gets the folder descendants starting with the current folder + * + * @param depth level of the tree that you want to go to + * - currentFolder + * -- file1.txt + * -- file2.txt + * -- folderB + * --- file3.txt + * --- file4.txt + * e.g. A depth of 1 will give you just the current folder descendants (file1.txt, file2.txt, folder1) + * e.g. A depth of -1 will return all the descendants (file1.txt, file2.txt, folder1, file3.txt and file4.txt) + */ + public List getFolderDescendants(int depth) + { + return getFolderTreeCmisObjects(getCmisFolder(cmisAPI.getLastResource()).getDescendants(depth)); + } + + /** + * Returns a list of Cmis objects for the provided Content Models + * + * @param contentModels + */ + public List getCmisObjectsFromContentModels(ContentModel... contentModels) + { + List cmisObjects = new ArrayList<>(); + for (ContentModel contentModel : contentModels) + cmisObjects.add(getCmisObject(contentModel.getCmisLocation())); + return cmisObjects; + } + + public ContentStream getContentStream(String content) + { + String fileName = getCmisDocument(cmisAPI.getLastResource()).getName(); + + return cmisAPI.getDataContentService().getContentStream(fileName, content); + } + + public Acl getAcls() + { + OperationContext context = cmisAPI.getSession().createOperationContext(); + context.setIncludeAcls(true); + return getCmisObject(cmisAPI.getLastResource(), context).getAcl(); + } + + /** + * Gets only the folder descendants for the {@link #getLastResource()} folder + * + * @param depth level of the tree that you want to go to + * - currentFolder + * -- folderB + * -- folderC + * --- folderD + * e.g. A depth of 1 will give you just the current folder descendants (folderB, folderC) + * e.g. A depth of -1 will return all the descendants (folderB, folderC, folderD) + */ + public List getFolderTree(int depth) + { + return getFolderTreeCmisObjects(getCmisFolder(cmisAPI.getLastResource()).getFolderTree(depth)); + } + + /** + * Helper method for getFolderTree and getFolderDescendants that cycles threw the folder descendants and returns List + */ + private List getFolderTreeCmisObjects(List> descendants) + { + List cmisObjectList = new ArrayList<>(); + for (Tree descendant : descendants) + { + cmisObjectList.add(descendant.getItem()); + cmisObjectList.addAll(descendant.getChildren().stream().map(Tree::getItem).collect(Collectors.toList())); + } + return cmisObjectList; + } + + protected List getAllDocumentVersions() + { + return getCmisDocument(cmisAPI.getLastResource()).getAllVersions(); + } + + public List getAllDocumentVersionsBy(OperationContext context) + { + return getCmisDocument(cmisAPI.getLastResource()).getAllVersions(context); + } + + public List getCheckedOutDocumentsFromSession() + { + return com.google.common.collect.Lists.newArrayList(cmisAPI.getSession().getCheckedOutDocs()); + } + + public List getCheckedOutDocumentsFromSession(OperationContext context) + { + return com.google.common.collect.Lists.newArrayList(cmisAPI.getSession().getCheckedOutDocs(context)); + } + + public List getCheckedOutDocumentsFromFolder() + { + Folder folder = cmisAPI.withCMISUtil().getCmisFolder(cmisAPI.getLastResource()); + return com.google.common.collect.Lists.newArrayList(folder.getCheckedOutDocs()); + } + + public List getCheckedOutDocumentsFromFolder(OperationContext context) + { + Folder folder = cmisAPI.withCMISUtil().getCmisFolder(cmisAPI.getLastResource()); + return com.google.common.collect.Lists.newArrayList(folder.getCheckedOutDocs(context)); + } + + protected boolean isCmisObjectContainedInCmisCheckedOutDocumentsList(CmisObject cmisObject, List cmisCheckedOutDocuments) + { + for (Document cmisCheckedOutDocument : cmisCheckedOutDocuments) + if (cmisObject.getId().split(";")[0].equals(cmisCheckedOutDocument.getId().split(";")[0])) + return true; + return false; + } + + public Map getProperties(ContentModel contentModel, String baseTypeId) + { + List aspects = new ArrayList(); + aspects.add("P:cm:titled"); + Map properties = new HashMap(); + properties.put(PropertyIds.OBJECT_TYPE_ID, baseTypeId); + properties.put(PropertyIds.NAME, contentModel.getName()); + properties.put(PropertyIds.SECONDARY_OBJECT_TYPE_IDS, aspects); + properties.put("cm:title", contentModel.getTitle()); + properties.put("cm:description", contentModel.getDescription()); + return properties; + } + + public OperationContext setIncludeAclContext() + { + OperationContext context = cmisAPI.getSession().createOperationContext(); + context.setIncludeAcls(true); + return context; + } + + public List createAce(UserModel user, UserRole role) + { + List addPermission = new ArrayList(); + addPermission.add(role.getRoleId()); + Ace addAce = cmisAPI.getSession().getObjectFactory().createAce(user.getUsername(), addPermission); + List addAces = new ArrayList(); + addAces.add(addAce); + return addAces; + } + + public List createAce(GroupModel group, UserRole role) + { + List addPermission = new ArrayList(); + addPermission.add(role.getRoleId()); + Ace addAce = cmisAPI.getSession().getObjectFactory().createAce(group.getDisplayName(), addPermission); + List addAces = new ArrayList(); + addAces.add(addAce); + return addAces; + } + + public List createAce(UserModel user, String... permissions) + { + List addAces = new ArrayList(); + RepositoryInfo repositoryInfo = cmisAPI.getSession().getRepositoryInfo(); + AclCapabilities aclCapabilities = repositoryInfo.getAclCapabilities(); + Map permissionMappings = aclCapabilities.getPermissionMapping(); + for (String perm : permissions) + { + STEP(String.format("%s Add permission '%s' for user %s ", CmisWrapper.STEP_PREFIX, perm, user.getUsername())); + PermissionMapping permissionMapping = permissionMappings.get(perm); + List permissionsList = permissionMapping.getPermissions(); + Ace addAce = cmisAPI.getSession().getObjectFactory().createAce(user.getUsername(), permissionsList); + addAces.add(addAce); + } + return addAces; + } + + public ObjectType getTypeDefinition() + { + CmisObject cmisObject = cmisAPI.withCMISUtil().getCmisObject(cmisAPI.getLastResource()); + return cmisAPI.getSession().getTypeDefinition(cmisObject.getBaseTypeId().value()); + } + + public ItemIterable getTypeChildren(String baseType, boolean includePropertyDefinitions) + { + STEP(String.format("%s Get type children for '%s' and includePropertyDefinitions set to '%s'", CmisWrapper.STEP_PREFIX, baseType, + includePropertyDefinitions)); + return cmisAPI.getSession().getTypeChildren(baseType, includePropertyDefinitions); + } + + public List> getTypeDescendants(String baseTypeId, int depth, boolean includePropertyDefinitions) + { + STEP(String.format("%s Get type descendants for '%s' with depth set to %d and includePropertyDefinitions set to '%s'", CmisWrapper.STEP_PREFIX, + baseTypeId, depth, includePropertyDefinitions)); + return cmisAPI.getSession().getTypeDescendants(baseTypeId, depth, includePropertyDefinitions); + } + + public String getObjectId(String pathToObject) + { + return getCmisObject(pathToObject).getId(); + } + + /** + * Update property for last resource cmis object + * + * @param propertyName String property name (e.g. cmis:name) + * @param propertyValue Object property value + */ + public void updateProperties(String propertyName, Object propertyValue) + { + Map props = new HashMap(); + props.put(propertyName, propertyValue); + getCmisObject(cmisAPI.getLastResource()).updateProperties(props); + } + + protected boolean isFolderInList(FolderModel folderModel, List folders) + { + for (FolderModel folder : folders) + { + if (folderModel.getName().equals(folder.getName())) + { + return true; + } + } + return false; + } + + protected boolean isFileInList(FileModel fileModel, List files) + { + for (FileModel file : files) + { + if (fileModel.getName().equals(file.getName())) + { + return true; + } + } + return false; + } + + protected boolean isContentInList(ContentModel contentModel, List contents) + { + for (ContentModel content : contents) + { + if (content.getName().equals(content.getName())) + { + return true; + } + } + return false; + } + + /** + * Get children folders from a parent folder + * + * @return List + */ + public List getFolders() + { + STEP(String.format("%s Get the folder children from '%s'", CmisWrapper.STEP_PREFIX, cmisAPI.getLastResource())); + Map children = getChildren(); + List folders = new ArrayList(); + for (Map.Entry entry : children.entrySet()) + { + if (entry.getValue().getId().equals(BaseTypeId.CMIS_FOLDER.value())) + { + FolderModel folder = new FolderModel(entry.getKey().getName()); + folder.setNodeRef(entry.getKey().getNodeRef()); + folder.setDescription(entry.getKey().getDescription()); + folder.setCmisLocation(entry.getKey().getCmisLocation()); + folder.setProtocolLocation(entry.getKey().getCmisLocation()); + folders.add(folder); + } + } + return folders; + } + + /** + * Get children documents from a parent folder + * + * @return List + */ + public List getFiles() + { + STEP(String.format("%s Get the file children from '%s'", CmisWrapper.STEP_PREFIX, cmisAPI.getLastResource())); + Map children = getChildren(); + List files = new ArrayList(); + for (Map.Entry entry : children.entrySet()) + { + if (entry.getValue().getId().equals(BaseTypeId.CMIS_DOCUMENT.value())) + { + FileModel file = new FileModel(entry.getKey().getName()); + file.setNodeRef(entry.getKey().getNodeRef()); + file.setDescription(entry.getKey().getDescription()); + file.setCmisLocation(entry.getKey().getCmisLocation()); + file.setProtocolLocation(entry.getKey().getCmisLocation()); + files.add(file); + } + } + return files; + } + + /* + * Get document(set as last resource) content + */ + public String getDocumentContent() + { + Utility.waitToLoopTime(2); + Document lastVersion = getCmisDocument(cmisAPI.getLastResource()); + lastVersion.refresh(); + LOG.info(String.format("Get the content from %s - node: %s", lastVersion.getName(), lastVersion.getId())); + ContentStream contentStream = lastVersion.getContentStream(); + String actualContent = ""; + if (contentStream != null) + { + actualContent = getContentAsString(contentStream); + } + else + { + lastVersion = getCmisDocument(cmisAPI.getLastResource()); + lastVersion.refresh(); + LOG.info(String.format("Retry get content stream for %s node: %s", lastVersion.getName(), lastVersion.getId())); + contentStream = lastVersion.getContentStream(); + if (contentStream != null) + { + actualContent = getContentAsString(contentStream); + } + } + if(actualContent.isEmpty()) + { + Utility.waitToLoopTime(2); + Document retryDoc = getCmisDocument(cmisAPI.getLastResource()); + retryDoc.refresh(); + LOG.info(String.format("Retry get content stream for %s node: %s", retryDoc.getName(), retryDoc.getId())); + contentStream = retryDoc.getContentStream(); + if (contentStream != null) + { + actualContent = getContentAsString(contentStream); + } + } + return actualContent; + } + + /** + * Get user noderef + * + * @param user {@link UserModel} + */ + public String getUserNodeRef(UserModel user) + { + String objectId = ""; + ItemIterable results = cmisAPI.getSession().query("select cmis:objectId from cm:person where cm:userName = '" + user.getUsername() + "'", + false); + for (QueryResult qResult : results) + { + PropertyData propData = qResult.getPropertyById("cmis:objectId"); + objectId = (String) propData.getFirstValue(); + } + return objectId; + } +} diff --git a/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/DocumentVersioning.java b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/DocumentVersioning.java new file mode 100644 index 0000000000..e772aec0a9 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/DocumentVersioning.java @@ -0,0 +1,137 @@ +package org.alfresco.cmis.dsl; + +import static org.alfresco.utility.report.log.Step.STEP; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.alfresco.cmis.CmisWrapper; +import org.apache.chemistry.opencmis.client.api.CmisObject; +import org.apache.chemistry.opencmis.client.api.Document; +import org.apache.chemistry.opencmis.client.api.OperationContext; +import org.testng.Assert; + +/** + * DSL utility for verifying a document version {@link Document} + */ +public class DocumentVersioning +{ + private CmisWrapper cmisWrapper; + private CmisObject cmisObject; + private boolean majorVersion; + private Object versionLabel; + private List versions; + + public DocumentVersioning(CmisWrapper cmisWrapper, CmisObject cmisObject) + { + this.cmisWrapper = cmisWrapper; + this.cmisObject = cmisObject; + } + + private DocumentVersioning withLatestMajorVersion() + { + this.majorVersion = true; + return this; + } + + private DocumentVersioning withLatestMinorVersion() + { + this.majorVersion = false; + return this; + } + + private Document getVersionOfDocument() + { + Document document = (Document) cmisObject; + if (versionLabel != null) + { + List documents = document.getAllVersions(); + for (Document documentVersion : documents) + if (documentVersion.getVersionLabel().equals(versionLabel.toString())) + return documentVersion; + } + else + { + return document.getObjectOfLatestVersion(majorVersion); + } + return document; + } + + private List getDocumentVersions(List documentList) + { + List versions = new ArrayList(); + for (Document document : documentList) + { + versions.add(document.getVersionLabel()); + } + return versions; + } + + public CmisWrapper assertVersionIs(Double expectedVersion) + { + STEP(String.format("%s Verify if document '%s' has version '%s'", cmisWrapper.getProtocolName(), cmisObject.getName(), expectedVersion)); + Assert.assertEquals(getVersionOfDocument().getVersionLabel(), expectedVersion.toString(), "File has version"); + return cmisWrapper; + } + + public CmisWrapper assertLatestMajorVersionIs(Double expectedVersion) + { + STEP(String.format("%s Verify if latest major version of document '%s' is '%s'", cmisWrapper.getProtocolName(), cmisObject.getName(), expectedVersion)); + Assert.assertEquals(withLatestMajorVersion().getVersionOfDocument().getVersionLabel(), expectedVersion.toString(), "File has version"); + return cmisWrapper; + } + + public CmisWrapper assertLatestMinorVersionIs(Double expectedVersion) + { + STEP(String.format("%s Verify if latest minor version of document '%s' is '%s'", cmisWrapper.getProtocolName(), cmisObject.getName(), expectedVersion)); + Assert.assertEquals(withLatestMinorVersion().getVersionOfDocument().getVersionLabel(), expectedVersion.toString(), "File has version"); + return cmisWrapper; + } + + public DocumentVersioning getAllDocumentVersions() + { + setVersions(getDocumentVersions(cmisWrapper.withCMISUtil().getAllDocumentVersions())); + return this; + } + + public CmisWrapper assertHasVersions(Object... versions) + { + setVersions(getDocumentVersions(cmisWrapper.withCMISUtil().getAllDocumentVersions())); + List documentVersions = getVersions(); + for (Object version : versions) + { + STEP(String.format("%s Verify if document '%s' has version '%s'", cmisWrapper.getProtocolName(), cmisObject.getName(), version)); + Assert.assertTrue(documentVersions.contains(version.toString()), + String.format("Document %s does not have version %s", cmisObject.getName(), version)); + } + return cmisWrapper; + } + + public DocumentVersioning getAllDocumentVersionsBy(OperationContext context) + { + setVersions(getDocumentVersions(cmisWrapper.withCMISUtil().getAllDocumentVersionsBy(context))); + return this; + } + + public CmisWrapper assertHasVersionsInOrder(Object... versions) + { + List documentVersions = getVersions(); + List expectedVersions = Arrays.asList(versions); + STEP(String.format("%s Verify if document '%s' has versions in this order '%s'", cmisWrapper.getProtocolName(), cmisObject.getName(), + Arrays.toString(expectedVersions.toArray()))); + Assert.assertTrue(documentVersions.toString().equals(expectedVersions.toString()), + String.format("Document %s does not have versions in this order %s", cmisObject.getName(), Arrays.toString(expectedVersions.toArray()))); + return cmisWrapper; + } + + public List getVersions() + { + return versions; + } + + public void setVersions(List versions) + { + this.versions = versions; + } +} diff --git a/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/JmxUtil.java b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/JmxUtil.java new file mode 100644 index 0000000000..40666d1cb1 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/JmxUtil.java @@ -0,0 +1,39 @@ +package org.alfresco.cmis.dsl; + +import org.alfresco.cmis.CmisWrapper; +import org.alfresco.utility.network.Jmx; +import org.alfresco.utility.network.JmxClient; +import org.alfresco.utility.network.JmxJolokiaProxyClient; + +/** + * DSL for interacting with JMX (using direct JMX call see {@link JmxClient} or {@link JmxJolokiaProxyClient} + */ +public class JmxUtil +{ + @SuppressWarnings("unused") + private CmisWrapper cmisWrapper; + private Jmx jmx; + private final String jmxAuditObjectName = "Alfresco:Type=Configuration,Category=Audit,id1=default"; + + public JmxUtil(CmisWrapper cmisWrapper, Jmx jmx) + { + this.cmisWrapper = cmisWrapper; + this.jmx = jmx; + } + + public void enableCMISAudit() throws Exception + { + if(jmx.readProperty(jmxAuditObjectName, "audit.enabled").equals(String.valueOf(false))) + { + jmx.writeProperty(jmxAuditObjectName, "audit.enabled", String.valueOf(true)); + } + jmx.writeProperty(jmxAuditObjectName, "audit.cmischangelog.enabled", String.valueOf(true)); + jmx.writeProperty(jmxAuditObjectName, "audit.alfresco-access.enabled", String.valueOf(true)); + } + + public void disableCMISAudit() throws Exception + { + jmx.writeProperty(jmxAuditObjectName, "audit.cmischangelog.enabled", String.valueOf(false)); + jmx.writeProperty(jmxAuditObjectName, "audit.alfresco-access.enabled", String.valueOf(false)); + } +} diff --git a/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/QueryExecutor.java b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/QueryExecutor.java new file mode 100644 index 0000000000..55954e4eb0 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/dsl/QueryExecutor.java @@ -0,0 +1,226 @@ +package org.alfresco.cmis.dsl; + +import static org.alfresco.utility.Utility.checkObjectIsInitialized; +import static org.alfresco.utility.report.log.Step.STEP; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.alfresco.cmis.CmisWrapper; +import org.alfresco.utility.LogFactory; +import org.alfresco.utility.data.provider.XMLTestData; +import org.alfresco.utility.exception.TestConfigurationException; +import org.alfresco.utility.model.FileModel; +import org.alfresco.utility.model.FolderModel; +import org.alfresco.utility.model.SiteModel; +import org.alfresco.utility.model.TestModel; +import org.apache.chemistry.opencmis.client.api.ItemIterable; +import org.apache.chemistry.opencmis.client.api.QueryResult; +import org.apache.chemistry.opencmis.client.api.Session; +import org.slf4j.Logger; +import org.testng.Assert; + +/** + * DSL for CMIS Queries + * This will also handle execution of CMIS queries + */ +public class QueryExecutor +{ + static Logger LOG = LogFactory.getLogger(); + + CmisWrapper cmisWrapper; + private long returnedResults = -1; + private String currentQuery = ""; + private ItemIterable results; + + public QueryExecutor(CmisWrapper cmisWrapper, String query) + { + this.cmisWrapper = cmisWrapper; + currentQuery = query; + } + + public QueryResultAssertion assertResultsCount() + { + returnedResults = executeQuery(currentQuery).getPageNumItems(); + return new QueryResultAssertion(); + } + + public QueryResultAssertion assertColumnIsOrdered() + { + results = executeQuery(currentQuery); + return new QueryResultAssertion(); + } + + public QueryResultAssertion assertColumnValuesRange() + { + results = executeQuery(currentQuery); + return new QueryResultAssertion(); + } + + private ItemIterable executeQuery(String query) + { + Session session = cmisWrapper.getSession(); + checkObjectIsInitialized(session, "You need to authenticate first using "); + + return session.query(query, false); + } + + /** + * Call getNodeRef on each test data item used in test and replace that with NODE_REF keywords in your Query + * + * @param testData + * @return + */ + public QueryExecutor applyNodeRefsFrom(XMLTestData testData) + { + List dataItems = extractKeywords("NODE_REF"); + if (dataItems.isEmpty()) + return this; + + List nodeRefs = new ArrayList(); + for (String dataItemName : dataItems) + { + currentQuery = currentQuery.replace(String.format("NODE_REF[%s]", dataItemName), "%s"); + TestModel model = testData.getTestDataItemWithId(dataItemName).getModel(); + if (model == null) + throw new TestConfigurationException("No TestData with ID: " + dataItemName + " found in your XML file."); + + if (model instanceof SiteModel) + { + nodeRefs.add(cmisWrapper.getDataContentService().usingAdmin().usingSite((SiteModel) model).getNodeRef()); + } + else if (model instanceof FolderModel) + { + nodeRefs.add(((FolderModel) model).getNodeRef()); + } + else if (model instanceof FileModel) + { + nodeRefs.add(((FileModel) model).getNodeRef()); + } + } + + try + { + currentQuery = String.format(currentQuery, nodeRefs.toArray()); + LOG.info("Injecting nodeRef IDs \n\tQuery: [{}]", currentQuery); + } + catch (Exception e) + { + throw new TestConfigurationException( + "You passed multiple keywords to your search query, please re-analyze your query search format: " + e.getMessage()); + } + return this; + } + + /** + * if you have in your search 'SELECT * from cmis:document where workspace://SpacesStore/NODE_REF[site1] or workspace://SpacesStore/NODE_REF[site2]' + * and pass key="NODE_REF" this method will get "site1" and "site2" as values + * + * @param key + * @return + * @throws TestConfigurationException + */ + private List extractKeywords(String key) throws TestConfigurationException + { + String[] lines = currentQuery.split(key); + List keywords = new ArrayList(); + + for (int i = 0; i < lines.length; i++) + { + if (lines[i].startsWith("[")) + { + String keyValue = ""; + for (int j = 1; j < lines[i].length() - 1; j++) + { + String tmp = Character.toString(lines[i].charAt(j)); + if (tmp.equals("]")) + break; + keyValue += tmp; + } + keywords.add(keyValue); + } + } + return keywords; + } + + public class QueryResultAssertion + { + public QueryResultAssertion equals(long expectedValue) + { + STEP(String.format("Verify that query: '%s' has %d results count returned", currentQuery, expectedValue)); + Assert.assertEquals(returnedResults, expectedValue, showErrorMessage()); + return this; + } + + public QueryResultAssertion isGreaterThan(long expectedValue) + { + STEP(String.format("Verify that query: '%s' has more than %d results count returned", currentQuery, expectedValue)); + if (expectedValue <= returnedResults) + Assert.fail(String.format("%s expected to have more than %d results, but found %d", showErrorMessage(), expectedValue, returnedResults)); + + return this; + } + + public QueryResultAssertion isLowerThan(long expectedValue) + { + STEP(String.format("Verify that query: '%s' has more than %d results count returned", currentQuery, expectedValue)); + if (returnedResults >= expectedValue) + Assert.fail(String.format("%s expected to have less than %d results, but found %d", showErrorMessage(), expectedValue, returnedResults)); + + return this; + } + + public QueryResultAssertion isOrderedAsc(String queryName) + { + STEP(String.format("Verify that query: '%s' is returning ascending ordered values for column %s", currentQuery, queryName)); + List columnValues = new ArrayList<>(); + results.forEach((r)->{ + columnValues.add(r.getPropertyValueByQueryName(queryName)); + }); + List orderedColumnValues = columnValues.stream().sorted().collect(Collectors.toList()); + Assert.assertEquals(columnValues, orderedColumnValues, + String.format("%s column values expected to be in ascendent order, but found %s", queryName, columnValues.toString())); + + return this; + + } + + public QueryResultAssertion isOrderedDesc(String queryName) + { + STEP(String.format("Verify that query: '%s' is returning descending ordered values for column %s", currentQuery, queryName)); + List columnValues = new ArrayList<>(); + results.forEach((r)->{ + columnValues.add(r.getPropertyValueByQueryName(queryName)); + }); + List reverseOrderedColumnValues = columnValues.stream().sorted(Collections.reverseOrder()).collect(Collectors.toList()); + Assert.assertEquals(columnValues, reverseOrderedColumnValues, + String.format("%s column values expected to be in descendent order, but found %s", queryName, columnValues.toString())); + + return this; + + } + + public QueryResultAssertion isReturningValuesInRange(String queryName, BigDecimal minValue, BigDecimal maxValue) + { + STEP(String.format("Verify that query: '%s' is returning values for column %s in range from %.4f to %.4f", currentQuery, queryName, minValue, maxValue)); + results.forEach((r)->{ + BigDecimal value = (BigDecimal) r.getPropertyValueByQueryName(queryName); + if (value.compareTo(minValue) < 0 || value.compareTo(maxValue) > 0) + { + Assert.fail(String.format("%s column values expected to be in range from %.4f to %.4f, but found %.4f", queryName, minValue, maxValue, value)); + } + }); + + return this; + + } + + private String showErrorMessage() + { + return String.format("Returned results count of Query [%s] is not the expected one:", currentQuery); + } + } +} diff --git a/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/exception/InvalidCmisObjectException.java b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/exception/InvalidCmisObjectException.java new file mode 100644 index 0000000000..9bc532652d --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/exception/InvalidCmisObjectException.java @@ -0,0 +1,10 @@ +package org.alfresco.cmis.exception; + +public class InvalidCmisObjectException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + public InvalidCmisObjectException(String reason) + { + super(reason); + } +} diff --git a/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/exception/UnrecognizedBinding.java b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/exception/UnrecognizedBinding.java new file mode 100644 index 0000000000..cf1dcaf250 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/java/org/alfresco/cmis/exception/UnrecognizedBinding.java @@ -0,0 +1,12 @@ +package org.alfresco.cmis.exception; + +public class UnrecognizedBinding extends Exception +{ + private static final long serialVersionUID = 1L; + private static final String DEFAULT_MESSAGE = "Unrecognized CMIS Binding [%s]. Available binding options: BROWSER or ATOM"; + + public UnrecognizedBinding(String binding) + { + super(String.format(DEFAULT_MESSAGE, binding)); + } +} diff --git a/packaging/tests/tas-cmis/src/main/resources/alfresco-cmis-context.xml b/packaging/tests/tas-cmis/src/main/resources/alfresco-cmis-context.xml new file mode 100644 index 0000000000..ecf3d2db4b --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/resources/alfresco-cmis-context.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/packaging/tests/tas-cmis/src/main/resources/default.properties b/packaging/tests/tas-cmis/src/main/resources/default.properties new file mode 100644 index 0000000000..1f69d79d08 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/resources/default.properties @@ -0,0 +1,76 @@ +# dataprep related +alfresco.scheme=http +alfresco.server=localhost +alfresco.port=8081 + +# credentials +admin.user=admin +admin.password=admin + +solrWaitTimeInSeconds=30 + +# in containers we cannot access directly JMX, so we will use http://jolokia.org agent +# disabling this we will use direct JMX calls to server +jmx.useJolokiaAgent=false + +# Server Health section +# in ServerHealth#isServerReachable() - could also be shown. +# enable this option to view if on server there are tenants or not +serverHealth.showTenants=true + +# set CMIS binding to 'browser' or 'atom' +cmis.binding=browser +cmis.basePath=/alfresco/api/-default-/public/cmis/versions/1.1/${cmis.binding} + +# TEST MANAGEMENT SECTION - Test Rail +# +# (currently supporting Test Rail v5.2.1.3472 integration) +# +# Example of configuration: +# ------------------------------------------------------ +# if testManagement.enabled=true we enabled TestRailExecutorListener (if used in your suite xml file) +# testManagement.updateTestExecutionResultsOnly=true (this will just update the results of a test: no step will be updated - good for performance) +# testManagement.endPoint=https://alfresco.testrail.com/ +# testManagement.username= +# testManagement.apiKey= +# testManagement.project= +# testManagement.includeOnlyTestCasesExecuted=true #if you want to include in your run ONLY the test cases that you run, then set this value to false +# testManagement.rateLimitInSeconds=1 #is the default rate limit after what minimum time, should we upload the next request. http://docs.gurock.com/testrail-api2/introduction #Rate Limit +# testManagement.suiteId=23 (the id of the Master suite) +# ------------------------------------------------------ +testManagement.enabled=false +testManagement.endPoint= +testManagement.username= +testManagement.apiKey= +testManagement.project=7 +testManagement.includeOnlyTestCasesExecuted=true +testManagement.rateLimitInSeconds=1 +testManagement.testRun=MyTestRunInTestRail +testManagement.suiteId=12 + +# The location of the reports path +reports.path=./target/reports + +# +# Database Section +# You should provide here the database URL, that can be a differed server as alfresco. +# https://docs.oracle.com/javase/tutorial/jdbc/basics/connecting.html +# +# Current supported db.url: +# +# MySQL: +# db.url = jdbc:mysql://${alfresco.server}:3306/alfresco +# +# PostgreSQL: +# db.url = jdbc:postgresql://:3306/alfresco +# +# Oracle: +# db.url = jdbc:oracle://:3306/alfresco +# +# MariaDB: +# db.url = jdbc:mariadb://:3306/alfresco +# +db.url = jdbc:mysql://${alfresco.server}:3306/alfresco +db.username = alfresco +db.password = alfresco diff --git a/packaging/tests/tas-cmis/src/main/resources/log4j.properties b/packaging/tests/tas-cmis/src/main/resources/log4j.properties new file mode 100644 index 0000000000..00e9b5a114 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/resources/log4j.properties @@ -0,0 +1,26 @@ +# 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=./target/reports/alfresco-tas.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 + +# TestRail particular log file +# Direct log messages to a log file +log4j.appender.testrailLog=org.apache.log4j.RollingFileAppender +log4j.appender.testrailLog.File=./target/reports/alfresco-testrail.log +log4j.appender.testrailLog.MaxBackupIndex=10 +log4j.appender.testrailLog.layout=org.apache.log4j.PatternLayout +log4j.appender.testrailLog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n + +log4j.category.testrail=INFO, testrailLog +log4j.additivity.testrail=false \ No newline at end of file diff --git a/packaging/tests/tas-cmis/src/main/resources/shared-resources/cmis-runner-suite.xml b/packaging/tests/tas-cmis/src/main/resources/shared-resources/cmis-runner-suite.xml new file mode 100644 index 0000000000..08d75ef621 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/resources/shared-resources/cmis-runner-suite.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packaging/tests/tas-cmis/src/main/resources/shared-resources/cmis-sanity-suite.xml b/packaging/tests/tas-cmis/src/main/resources/shared-resources/cmis-sanity-suite.xml new file mode 100644 index 0000000000..38fdd63e74 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/resources/shared-resources/cmis-sanity-suite.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packaging/tests/tas-cmis/src/main/resources/shared-resources/cmis-suites.xml b/packaging/tests/tas-cmis/src/main/resources/shared-resources/cmis-suites.xml new file mode 100644 index 0000000000..9963b115e8 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/resources/shared-resources/cmis-suites.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packaging/tests/tas-cmis/src/main/resources/shared-resources/model/tas-model.xml b/packaging/tests/tas-cmis/src/main/resources/shared-resources/model/tas-model.xml new file mode 100644 index 0000000000..892027f9c1 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/resources/shared-resources/model/tas-model.xml @@ -0,0 +1,151 @@ + + + + + Alfresco TAS custom model + Bogdan + 1.0 + + + + + + + + + + + + + CMIS TAS Content + cm:content + + + Text + d:text + + + Datetime + d:datetime + + + Integer + d:int + + + Long + d:long + + + Multiply String + d:text + true + true + + + + + + + false + false + + + cm:content + false + false + + + + + + + CMIS TAS Folder + cm:folder + + + Text + d:text + + + Datetime + d:datetime + + + Integer + d:int + + + Multiply String + d:text + true + true + + + + + + + false + false + + + cm:folder + false + false + + + + + + + + + TAS Content Aspect + + + Aspect Text + d:text + + + Aspect Datetime + d:datetime + + + Aspect Integer + d:int + + + Aspect Multiply String + d:text + true + true + + + + + + TAS Folder Aspect + + + Aspect Text + d:text + + + Aspect Datetime + d:datetime + + + Aspect Integer + d:int + + + Aspect Multiply String + d:text + true + true + + + + + \ No newline at end of file diff --git a/packaging/tests/tas-cmis/src/main/resources/shared-resources/testCount.xml b/packaging/tests/tas-cmis/src/main/resources/shared-resources/testCount.xml new file mode 100644 index 0000000000..249e06ac86 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/resources/shared-resources/testCount.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packaging/tests/tas-cmis/src/main/resources/shared-resources/testdata/cmis-checkIn.txt b/packaging/tests/tas-cmis/src/main/resources/shared-resources/testdata/cmis-checkIn.txt new file mode 100644 index 0000000000..863a223b69 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/resources/shared-resources/testdata/cmis-checkIn.txt @@ -0,0 +1 @@ +Sp23xfcYhUBYpsXuPFzn8nVQ \ No newline at end of file diff --git a/packaging/tests/tas-cmis/src/main/resources/shared-resources/testdata/cmis-resource b/packaging/tests/tas-cmis/src/main/resources/shared-resources/testdata/cmis-resource new file mode 100644 index 0000000000..e01d809153 --- /dev/null +++ b/packaging/tests/tas-cmis/src/main/resources/shared-resources/testdata/cmis-resource @@ -0,0 +1 @@ +tas \ No newline at end of file