content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  package org.apache.chemistry.opencmis.inmemory;
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertNotNull;
  import static org.junit.Assert.assertTrue;
  import static org.junit.Assert.fail;
  
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import org.apache.chemistry.opencmis.commons.data.ContentStream;
  import org.apache.chemistry.opencmis.commons.data.ExtensionsData;
  import org.apache.chemistry.opencmis.commons.data.Properties;
  import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
  import org.apache.chemistry.opencmis.commons.definitions.DocumentTypeDefinition;
  import org.apache.chemistry.opencmis.commons.definitions.MutableDocumentTypeDefinition;
  import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
  import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
  import org.apache.chemistry.opencmis.commons.enums.Updatability;
  import org.apache.chemistry.opencmis.commons.enums.VersioningState;
  import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
  import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException;
  import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerDefinitionImpl;
  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringDefinitionImpl;
  import org.apache.chemistry.opencmis.inmemory.server.InMemoryServiceFactoryImpl;
  import org.apache.chemistry.opencmis.inmemory.types.DocumentTypeCreationHelper;
  import org.apache.chemistry.opencmis.inmemory.types.PropertyCreationHelper;
  import org.junit.After;
  import org.junit.Before;
  import org.junit.Test;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * Temporary test class until CMIS 1.1 bindings are completed. Until then we use
   * a special setup procedure to directly connect to the repository service
   * implementation of InMemory.
   */
  public class RepositoryServiceMutabilityTest extends AbstractServiceTest {
  
      private static final Logger log = LoggerFactory.getLogger(RepositoryServiceTest.class);
      private static final String REPOSITORY_ID = "UnitTestRepository";
      private static final String TYPE_ID_MUTABILITY = "BookTypeAddedLater";
      private static final String PROPERTY_ID_TITLE = "Title";
      private static final String PROPERTY_ID_NUMBER = "Number";
  
      @Override
      @Before
      public void setUp() {
          super.setTypeCreatorClass(UnitTestTypeSystemCreator.class.getName());
          super.setUp();
  
          Map<String, String> parameters = new HashMap<String, String>();
  
          // attach repository info to the session:
          parameters.put(ConfigConstants.TYPE_CREATOR_CLASS, getTypeCreatorClass());
          parameters.put(ConfigConstants.REPOSITORY_ID, REPOSITORY_ID);
  
          InMemoryServiceFactoryImpl factory = new InMemoryServiceFactoryImpl();
          factory.init(parameters);
      }
  
      @Override
      @After
      public void tearDown() {
          super.tearDown();
      }
  
      // This test is just added because this test class uses a different setup to
      // connect to the
      // server as long as the server bindings do not support the type mutability
      // extension of
      // CMIS 1.1. If this test fails then the setUp() fails!
      @Test
      public void testRepositoryInfo() {
          log.info("starting testRepositoryInfo() ...");
          List<RepositoryInfo> repositories = fRepSvc.getRepositoryInfos(null);
          assertNotNull(repositories);
          assertFalse(repositories.isEmpty());
  
          log.info("geRepositoryInfo(), found " + repositories.size() + " repository/repositories).");
  
          for (RepositoryInfo repository : repositories) {
              RepositoryInfo repository2 = fRepSvc.getRepositoryInfo(repository.getId(), null);
              assertNotNull(repository2);
              assertEquals(repository.getId(), repository2.getId());
              log.info("found repository" + repository2.getId());
          }
  
          log.info("... testRepositoryInfo() finished.");
      }
  
      @Test
      public void testTypeMutabilityCreation() throws Exception {
          log.info("");
          log.info("starting testTypeMutabilityCreation() ...");
          TypeDefinition typeDefRef = createTypeForAddingAtRuntime();
          String repositoryId = getRepositoryId();
          // add type.
          fRepSvc.createType(repositoryId, typeDefRef, null);
          TypeDefinition type = fRepSvc.getTypeDefinition(repositoryId, typeDefRef.getId(), null);
          assertEquals(typeDefRef.getId(), type.getId());
          assertEquals(typeDefRef.getDescription(), type.getDescription());
          assertEquals(typeDefRef.getDisplayName(), type.getDisplayName());
          assertEquals(typeDefRef.getLocalName(), type.getLocalName());
          assertEquals(typeDefRef.getLocalNamespace(), type.getLocalNamespace());
          RepositoryServiceTest.containsAllBasePropertyDefinitions(type);
          log.info("... testTypeMutabilityCreation() finished.");
      }
  
      @Test
      public void testTypeMutabilityCreateDuplicate() throws Exception {
          log.info("");
          log.info("starting testTypeMutabilityCreateDuplicate() ...");
          TypeDefinition typeDefRef = createTypeForAddingAtRuntime();
          String repositoryId = getRepositoryId();
          // add type.
          fRepSvc.createType(repositoryId, typeDefRef, null);
          // add type again should fail
          checkAddingType(repositoryId, typeDefRef, CmisInvalidArgumentException.class);
          // type should still exist then
          TypeDefinition type = fRepSvc.getTypeDefinition(repositoryId, typeDefRef.getId(), null);
          assertEquals(typeDefRef.getId(), type.getId());
          log.info("... testTypeMutabilityCreateDuplicate() finished.");
      }
  
      @Test
      public void testTypeMutabilityTypeNameConstraints() throws Exception {
          log.info("starting testTypeMutabilityTypeNameConstraints() ...");
  
          String repositoryId = getRepositoryId();
  
          // test illegal type id
          MutableDocumentTypeDefinition typeDefRef = createTypeForAddingAtRuntime();
          typeDefRef.setId(typeDefRef.getId() + "!!!");
          TypeDefinition typeDefNew = fRepSvc.createType(repositoryId, typeDefRef, null);
          assertEquals(TYPE_ID_MUTABILITY + "___", typeDefNew.getId());
          fRepSvc.deleteType(repositoryId, TYPE_ID_MUTABILITY + "___", null);
  
          // test illegal parent type id
          typeDefRef = createTypeForAddingAtRuntime();
          typeDefRef.setParentTypeId("NonExistingParentType");
          checkAddingType(repositoryId, typeDefRef, CmisInvalidArgumentException.class);
  
          // test null type id
          typeDefRef = createTypeForAddingAtRuntime();
          typeDefRef.setId(null);
          typeDefNew = fRepSvc.createType(repositoryId, typeDefRef, null);
          assertTrue(null != typeDefNew.getId() && typeDefNew.getId().length() > 0);
          fRepSvc.deleteType(repositoryId, typeDefNew.getId(), null);
  
          // test null query name
          typeDefRef = createTypeForAddingAtRuntime();
          typeDefRef.setQueryName(null);
          typeDefNew = fRepSvc.createType(repositoryId, typeDefRef, null);
          assertEquals(TYPE_ID_MUTABILITY, typeDefNew.getQueryName());
          fRepSvc.deleteType(repositoryId, TYPE_ID_MUTABILITY, null);
  
          // test illegal query name
          typeDefRef = createTypeForAddingAtRuntime();
          typeDefRef.setQueryName(typeDefRef.getQueryName() + "!!!");
          typeDefNew = fRepSvc.createType(repositoryId, typeDefRef, null);
          assertEquals(TYPE_ID_MUTABILITY, typeDefNew.getQueryName());
          fRepSvc.deleteType(repositoryId, TYPE_ID_MUTABILITY, null);
  
          // test null local name
          typeDefRef = createTypeForAddingAtRuntime();
          typeDefRef.setLocalName(null);
          assertEquals(TYPE_ID_MUTABILITY, typeDefNew.getLocalName());
          typeDefNew = fRepSvc.createType(repositoryId, typeDefRef, null);
          fRepSvc.deleteType(repositoryId, TYPE_ID_MUTABILITY, null);
  
          // test illegal local name
          typeDefRef = createTypeForAddingAtRuntime();
          typeDefRef.setLocalName(typeDefRef.getLocalName() + "!!!");
          assertEquals(TYPE_ID_MUTABILITY, typeDefNew.getLocalName());
          typeDefNew = fRepSvc.createType(repositoryId, typeDefRef, null);
          fRepSvc.deleteType(repositoryId, TYPE_ID_MUTABILITY, null);
  
          log.info("... testTypeMutabilityTypeNameConstraints() finished.");
      }
  
      @Test
      public void testTypeMutabilityPropertyNameConstraints() throws Exception {
          log.info("starting testTypeMutabilityPropertyNameConstraints() ...");
  
          String repositoryId = getRepositoryId();
  
          // test null property id
          DocumentTypeDefinition typeDef = createTypeForAddingAtRuntime();
          PropertyStringDefinitionImpl pd = getPropertyDefinitionImpl(typeDef);
          pd.setId(null);
          checkAddingType(repositoryId, typeDef, CmisInvalidArgumentException.class);
  
          // test illegal property id
          typeDef = createTypeForAddingAtRuntime();
          pd = getPropertyDefinitionImpl(typeDef);
          pd.setQueryName(pd.getQueryName() + "!*!");
          checkAddingType(repositoryId, typeDef, CmisInvalidArgumentException.class);
  
          // test null property query name
          typeDef = createTypeForAddingAtRuntime();
          pd = getPropertyDefinitionImpl(typeDef);
          pd.setQueryName(null);
          checkAddingType(repositoryId, typeDef, CmisInvalidArgumentException.class);
  
          // test illegal property query name
          typeDef = createTypeForAddingAtRuntime();
          pd = getPropertyDefinitionImpl(typeDef);
          pd.setQueryName(pd.getQueryName() + "!!!");
          checkAddingType(repositoryId, typeDef, CmisInvalidArgumentException.class);
  
          // test null property local name
          typeDef = createTypeForAddingAtRuntime();
          pd = getPropertyDefinitionImpl(typeDef);
          pd.setLocalName(null);
          checkAddingType(repositoryId, typeDef, CmisInvalidArgumentException.class);
  
          // test illegal property local name
          typeDef = createTypeForAddingAtRuntime();
          pd = getPropertyDefinitionImpl(typeDef);
          pd.setLocalName(typeDef.getLocalName() + "!!!");
          checkAddingType(repositoryId, typeDef, CmisInvalidArgumentException.class);
  
          log.info("... testTypeMutabilityPropertyNameConstraints() finished.");
      }
  
      private void checkAddingType(String repositoryId, TypeDefinition typeDef, Class<? extends Exception> clazz) {
          try {
              fRepSvc.createType(repositoryId, typeDef, null);
              fail("Illegal type should throw a " + clazz.getName());
          } catch (RuntimeException e) {
              assertTrue("Illegal type name threw wrong exception type (should be a " + clazz.getName() + ")",
                      clazz.isInstance(e));
          }
      }
  
      @Test
      public void testTypeMutabilityUpdate() throws Exception {
          log.info("");
          log.info("starting testTypeMutabilityUpdate() ...");
          TypeDefinition typeDefRef = createTypeForAddingAtRuntime();
          String repositoryId = getRepositoryId();
          fRepSvc.createType(repositoryId, typeDefRef, null);
          // update type.
          try {
              fRepSvc.updateType(repositoryId, typeDefRef, null);
              fail("updating a type should throw exception.");
          } catch (Exception e) {
              assertTrue(e instanceof CmisNotSupportedException);
          }
          log.info("... testTypeMutabilityUpdate() finished.");
      }
  
      @Test
      public void testTypeMutabilityDeletion() throws Exception {
          log.info("");
          log.info("starting testTypeMutabilityDeletion() ...");
          TypeDefinition typeDefRef = createTypeForAddingAtRuntime();
          String repositoryId = getRepositoryId();
          fRepSvc.createType(repositoryId, typeDefRef, null);
  
          String docId = createDoc("Book1", getRootFolderId(REPOSITORY_ID), TYPE_ID_MUTABILITY);
  
          // try deleting type, should fail, because in use.
          try {
              fRepSvc.deleteType(repositoryId, TYPE_ID_MUTABILITY, null);
              fail("deleting a type which is in use should throw exception.");
          } catch (Exception e) {
              assertTrue(e instanceof CmisInvalidArgumentException);
          }
  
          fObjSvc.deleteObject(fRepositoryId, docId, true, null);
  
          try {
              fRepSvc.deleteType(repositoryId, TYPE_ID_MUTABILITY, null);
          } catch (Exception e) {
              fail("deleting a type which is in not in use should not throw exception! Exception is: " + e);
          }
  
          try {
              fRepSvc.getTypeDefinition(repositoryId, TYPE_ID_MUTABILITY, null);
              fail("getting a type after it was deleted should fail.");
          } catch (Exception e) {
          }
  
          try {
              fRepSvc.deleteType(repositoryId, BaseTypeId.CMIS_DOCUMENT.value(), null);
              fail("deleting a CMIS base type throw exception.");
          } catch (Exception e) {
              assertTrue (e instanceof CmisInvalidArgumentException);
          }
          try {
              fRepSvc.deleteType(repositoryId, BaseTypeId.CMIS_FOLDER.value(), null);
              fail("deleting a CMIS base type throw exception.");
          } catch (Exception e) {
              assertTrue (e instanceof CmisInvalidArgumentException);
          }
  
          log.info("... testTypeMutabilityDeletion() finished.");
      }
  
      private String getRepositoryId() {
          List<RepositoryInfo> repositories = fRepSvc.getRepositoryInfos(null);
          RepositoryInfo repository = repositories.get(0);
          assertNotNull(repository);
          return repository.getId();
      }
  
      private String getRootFolderId(String repositoryId) {
          RepositoryInfo repository = fRepSvc.getRepositoryInfo(repositoryId, null);
          assertNotNull(repository);
          return repository.getRootFolderId();
      }
  
      private PropertyStringDefinitionImpl getPropertyDefinitionImpl(TypeDefinition typeDef) {
          PropertyStringDefinitionImpl pd = (PropertyStringDefinitionImpl) typeDef.getPropertyDefinitions().get(
                  PROPERTY_ID_TITLE);
          return pd;
      }
  
      private MutableDocumentTypeDefinition createTypeForAddingAtRuntime() {
          try {
              MutableDocumentTypeDefinition cmisLaterType;
              cmisLaterType = DocumentTypeCreationHelper
                      .createDocumentTypeDefinitionWithoutBaseProperties(DocumentTypeCreationHelper.getCmisDocumentType());
              cmisLaterType.setId(TYPE_ID_MUTABILITY);
              cmisLaterType.setDisplayName("Type with two properties");
              cmisLaterType.setDescription("Builtin InMemory type definition " + TYPE_ID_MUTABILITY);
              cmisLaterType.setIsIncludedInSupertypeQuery(false);
  
              PropertyIntegerDefinitionImpl prop1 = PropertyCreationHelper.createIntegerDefinition(PROPERTY_ID_NUMBER,
                      "Sample Int Property", Updatability.READWRITE);
              cmisLaterType.addPropertyDefinition(prop1);
  
              PropertyStringDefinitionImpl prop2 = PropertyCreationHelper.createStringDefinition(PROPERTY_ID_TITLE,
                      "Sample String Property", Updatability.READWRITE);
              cmisLaterType.addPropertyDefinition(prop2);
  
              return cmisLaterType;
          } catch (Exception e) {
              throw new CmisRuntimeException("Error when creating built-in InMemory types.", e);
          }
      }
  
      String createDoc(String name, String folderId, String typeId) {
          ContentStream contentStream = null;
          List<String> policies = null;
          ExtensionsData extension = null;
  
          Properties props = createDocumentProperties(name, typeId);
  
          String id = fObjSvc.createDocument(fRepositoryId, props, folderId, contentStream, VersioningState.NONE,
                  policies, null, null, extension);
          return id;
      }
  }
  