/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2008 Sun Microsystems, Inc. */ package org.opends.server.admin.server; import static org.fest.assertions.Assertions.*; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import java.io.IOException; import java.util.List; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.LocalizableMessageBuilder; import org.forgerock.opendj.ldap.Entry; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldif.LDIF; import org.mockito.ArgumentCaptor; import org.opends.server.admin.AdminTestCase; import org.opends.server.admin.PropertyDefinitionsOptions; import org.opends.server.admin.TestCfg; import org.opends.server.admin.TestChildCfg; import org.opends.server.admin.TestChildCfgDefn; import org.opends.server.admin.TestParentCfg; import org.opends.server.config.spi.ConfigAddListener; import org.opends.server.config.spi.ConfigChangeListener; import org.opends.server.config.spi.ConfigDeleteListener; import org.opends.server.config.spi.ConfigException; import org.opends.server.config.spi.ConfigurationRepository; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; /** * Test cases for constraints on the server-side. */ @SuppressWarnings("javadoc") public final class ConstraintTest extends AdminTestCase { private static class AddListener implements ConfigurationAddListener { public ConfigChangeResult applyConfigurationAdd(TestChildCfg configuration) { return new ConfigChangeResult(ResultCode.SUCCESS, false); } public boolean isConfigurationAddAcceptable(TestChildCfg configuration, List unacceptableReasons) { return true; } } private static class DeleteListener implements ConfigurationDeleteListener { public ConfigChangeResult applyConfigurationDelete(TestChildCfg configuration) { return new ConfigChangeResult(ResultCode.SUCCESS, false); } public boolean isConfigurationDeleteAcceptable(TestChildCfg configuration, List unacceptableReasons) { return true; } } private static class ChangeListener implements ConfigurationChangeListener { public ConfigChangeResult applyConfigurationChange(TestChildCfg configuration) { return new ConfigChangeResult(ResultCode.SUCCESS, false); } public boolean isConfigurationChangeAcceptable(TestChildCfg configuration, List unacceptableReasons) { return true; } } // @Checkstyle:off private static final Entry TEST_CHILD_1 = LDIF.makeEntry( "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top", "objectclass: ds-cfg-test-child-dummy", "cn: test child 1", "ds-cfg-enabled: true", "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider", "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real"); private static final Entry TEST_BASE_CHILD = LDIF.makeEntry( "dn:cn=test children,cn=test parent 1,cn=test parents,cn=config", "objectclass: top", "objectclass: ds-cfg-branch", "cn: test children"); // Parent 1 - uses default values for // optional-multi-valued-dn-property. private static final Entry TEST_PARENT_1 = LDIF.makeEntry( "dn: cn=test parent 1,cn=test parents,cn=config", "objectclass: top", "objectclass: ds-cfg-test-parent-dummy", "cn: test parent 1", "ds-cfg-enabled: true", "ds-cfg-java-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider", "ds-cfg-attribute-type: description", "ds-cfg-conflict-behavior: virtual-overrides-real"); // @Checkstyle:on @BeforeClass public void setUp() throws Exception { TestCfg.setUp(); } @AfterClass public void tearDown() throws Exception { TestCfg.cleanup(); } /** Success just ensure there is no exception raised */ @Test public void testGetManagedObjectSuccess() throws Exception { // arrange ConfigurationRepository configRepository = createConfigRepositoryWithEntries(TEST_PARENT_1, TEST_BASE_CHILD, TEST_CHILD_1); ServerManagementContext context = new ServerManagementContext(configRepository, PropertyDefinitionsOptions.NO_VALIDATION_OPTIONS); MockConstraint constraint = new MockConstraint(true, false, configRepository); try { TestCfg.addConstraint(constraint); TestParentCfg parentCfg = getParentCfg(TEST_PARENT_1, context); // act parentCfg.getTestChild(entryName(TEST_CHILD_1)); } finally { TestCfg.removeConstraint(constraint); } } @Test public void testGetManagedObjectFail() throws Exception { // arrange ConfigurationRepository configRepository = createConfigRepositoryWithEntries(TEST_PARENT_1, TEST_BASE_CHILD, TEST_CHILD_1); ServerManagementContext context = new ServerManagementContext(configRepository, PropertyDefinitionsOptions.NO_VALIDATION_OPTIONS); MockConstraint constraint = new MockConstraint(false, true, configRepository); try { TestCfg.addConstraint(constraint); TestParentCfg parentCfg = getParentCfg(TEST_PARENT_1, context); // act parentCfg.getTestChild(entryName(TEST_CHILD_1)); failWasExpected(ConfigException.class); } catch (ConfigException e) { // assert Throwable cause = e.getCause(); assertThat(e.getCause()).isNotNull().isInstanceOf(ConstraintViolationException.class); ConstraintViolationException cve = (ConstraintViolationException) cause; assertThat(cve.getMessages().size()).isEqualTo(1); assertThat(cve.getManagedObject().getManagedObjectDefinition()).isSameAs(TestChildCfgDefn.getInstance()); } finally { TestCfg.removeConstraint(constraint); } } @DataProvider Object[][] constraintValues() { return new Object[][] { // value of constraint used { true }, // success { false } // failure }; } @Test(dataProvider = "constraintValues") public void testAddConstraint(boolean isUsableConstraint) throws Exception { // arrange ConfigurationRepository configRepository = createConfigRepositoryWithEntries(TEST_PARENT_1, TEST_BASE_CHILD, TEST_CHILD_1); ServerManagementContext context = new ServerManagementContext(configRepository, PropertyDefinitionsOptions.NO_VALIDATION_OPTIONS); TestParentCfg parentCfg = getParentCfg(TEST_PARENT_1, context); parentCfg.addTestChildAddListener(new AddListener()); MockConstraint constraint = new MockConstraint(isUsableConstraint, false, configRepository); try { TestCfg.addConstraint(constraint); // act boolean isAcceptable = simulateEntryAdd(TEST_CHILD_1, configRepository); // assert : success depends on constraint used assertThat(isAcceptable).isEqualTo(isUsableConstraint); } finally { TestCfg.removeConstraint(constraint); } } @Test(dataProvider = "constraintValues") public void testDeleteConstraint(boolean isDeleteAllowedConstraint) throws Exception { // arrange ConfigurationRepository configRepository = createConfigRepositoryWithEntries(TEST_PARENT_1, TEST_BASE_CHILD, TEST_CHILD_1); ServerManagementContext context = new ServerManagementContext(configRepository, PropertyDefinitionsOptions.NO_VALIDATION_OPTIONS); TestParentCfg parentCfg = getParentCfg(TEST_PARENT_1, context); parentCfg.addTestChildDeleteListener(new DeleteListener()); MockConstraint constraint = new MockConstraint(false, isDeleteAllowedConstraint, configRepository); try { TestCfg.addConstraint(constraint); // act boolean isAcceptable = simulateEntryDelete(TEST_CHILD_1, configRepository); // assert : success depends on constraint used assertThat(isAcceptable).isEqualTo(isDeleteAllowedConstraint); } finally { TestCfg.removeConstraint(constraint); } } @Test(dataProvider = "constraintValues") public void testChangeConstraint(boolean isUsableConstraint) throws Exception { // arrange ConfigurationRepository configRepository = createConfigRepositoryWithEntries(TEST_PARENT_1, TEST_BASE_CHILD, TEST_CHILD_1); ServerManagementContext context = new ServerManagementContext(configRepository, PropertyDefinitionsOptions.NO_VALIDATION_OPTIONS); MockConstraint constraint = new MockConstraint(isUsableConstraint, false, configRepository); TestParentCfg parentCfg = getParentCfg(TEST_PARENT_1, context); TestChildCfg childCfg = parentCfg.getTestChild(entryName(TEST_CHILD_1)); try { TestCfg.addConstraint(constraint); childCfg.addChangeListener(new ChangeListener()); // act // It is not an issue to use the same child entry here // as we're only interested in constraint checking boolean isAcceptable = simulateEntryChange(TEST_CHILD_1, configRepository); // assert : success depends on constraint used assertThat(isAcceptable).isEqualTo(isUsableConstraint); } finally { TestCfg.removeConstraint(constraint); } } /** * Simulate an entry add by triggering configAddIsAcceptable method of last * registered add listener. * * @return true if add is acceptable, false otherwise. */ private boolean simulateEntryAdd(Entry entry, ConfigurationRepository configRepository) throws IOException { // use argument capture to retrieve the actual listener ArgumentCaptor registeredListener = ArgumentCaptor.forClass(ConfigAddListener.class); verify(configRepository).registerAddListener(eq(entry.getName().parent()), registeredListener.capture()); return registeredListener.getValue().configAddIsAcceptable(entry, new LocalizableMessageBuilder()); } /** * Simulate an entry delete by triggering configDeleteIsAcceptable method of * last registered add listener. * * @return true if delete is acceptable, false otherwise. */ private boolean simulateEntryDelete(Entry entry, ConfigurationRepository configRepository) throws IOException { // use argument capture to retrieve the actual listener ArgumentCaptor registeredListener = ArgumentCaptor.forClass(ConfigDeleteListener.class); verify(configRepository).registerDeleteListener(eq(entry.getName().parent()), registeredListener.capture()); return registeredListener.getValue().configDeleteIsAcceptable(entry, new LocalizableMessageBuilder()); } /** * Simulate an entry change by triggering configChangeIsAcceptable method on * last registered change listener. * * @return true if change is acceptable, false otherwise. */ private boolean simulateEntryChange(Entry newEntry, ConfigurationRepository configRepository) { // use argument capture to retrieve the actual listener ArgumentCaptor registeredListener = ArgumentCaptor.forClass(ConfigChangeListener.class); verify(configRepository).registerChangeListener(eq(newEntry.getName()), registeredListener.capture()); return registeredListener.getValue().configChangeIsAcceptable(newEntry, new LocalizableMessageBuilder()); } }