You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by se...@apache.org on 2021/01/24 20:28:01 UTC
[directory-studio] 01/01: DIRSTUDIO-744: Fix entry modify requests
This is an automated email from the ASF dual-hosted git repository.
seelmann pushed a commit to branch DIRSTUDIO-744-fix-entry-modify-request
in repository https://gitbox.apache.org/repos/asf/directory-studio.git
commit 294c29d4c071276a9612eedaabfbba51a1111908
Author: Stefan Seelmann <ma...@stefan-seelmann.de>
AuthorDate: Sun Jan 24 21:26:35 2021 +0100
DIRSTUDIO-744: Fix entry modify requests
* When deleting all remaining values of an attribute the deleted values are now included in the modify request.
* Remove "smart" behaviour whicht used replace operation instead of delete+add when modifying a value.
* Both can cause unwanted side effect if the client doesn't see all values.
---
.../docbook/2.40_tools_newconnection_wizard.xml | 22 +-
.../docbook/2.80_tools_connection_properties.xml | 18 +-
.../common/widgets/connection/messages.properties | 6 +-
.../studio/ldapbrowser/core/utils/Utils.java | 15 +-
tests/test.integration.core/pom-first.xml | 1 +
.../test/integration/core/ComputeDiffTest.java | 309 +++++++++++++++++++++
.../test/integration/ui/EntryEditorTest.java | 70 ++++-
.../test/integration/ui/ErrorHandlingTest.java | 3 +-
.../studio/test/integration/ui/OpenLdapTest.java | 133 +++++++++
.../studio/test/integration/ui/OpenLdapTest.ldif | 1 -
10 files changed, 536 insertions(+), 42 deletions(-)
diff --git a/helps/ldapbrowser.help/src/main/docbook/2.40_tools_newconnection_wizard.xml b/helps/ldapbrowser.help/src/main/docbook/2.40_tools_newconnection_wizard.xml
index 19f1a6d..c5312b6 100644
--- a/helps/ldapbrowser.help/src/main/docbook/2.40_tools_newconnection_wizard.xml
+++ b/helps/ldapbrowser.help/src/main/docbook/2.40_tools_newconnection_wizard.xml
@@ -574,15 +574,15 @@
Specify the modify mode for attributes with an equality matching rule.
Description of options:
<itemizedlist spacing="normal" mark="bullet">
- <listitem>Optimized Modify Operations: uses add/delete by default,
- uses replace if operation count is less</listitem>
+ <listitem>Default: uses add/delete by default,
+ uses replace for X-ORDERED attributes</listitem>
<listitem>Always REPLACE: always uses replace operations to perform
entry modifications</listitem>
<listitem>Always ADD/DELETE: always uses add and/or delete operations
to perform entry modifications</listitem>
</itemizedlist>
</entry>
- <entry>Optimized Modify Operations</entry>
+ <entry>Default</entry>
</row>
<row>
<entry>Modify Mode (no equality matching rule)</entry>
@@ -590,8 +590,8 @@
Specify the modify mode for attributes with *no* equality matching rule.
Description of options:
<itemizedlist spacing="normal" mark="bullet">
- <listitem>Optimized Modify Operations: uses add/delete by default,
- uses replace if operation count is less</listitem>
+ <listitem>Default: uses add/delete by default,
+ uses replace for X-ORDERED attributes</listitem>
<listitem>Always REPLACE: always uses replace operations to perform
entry modifications</listitem>
<listitem>Always ADD/DELETE: always uses add and/or delete operations
@@ -599,18 +599,18 @@
</itemizedlist>
Recommended values for various LDAP servers:
<itemizedlist spacing="normal" mark="bullet">
- <listitem>ApacheDS: Optimized Modify Operations or REPLACE</listitem>
+ <listitem>ApacheDS: Default or REPLACE</listitem>
<listitem>OpenLDAP: REPLACE</listitem>
- <listitem>OpenDS / SunDSEE: Optimized Modify Operations or REPLACE</listitem>
- <listitem>FedoraDS / 389DS: Optimized Modify Operations
+ <listitem>OpenDS / SunDSEE: Default or REPLACE</listitem>
+ <listitem>FedoraDS / 389DS: Default
(missing equality matching rules for many standard attribute types)</listitem>
- <listitem>Active Directory: Optimized Modify Operations
+ <listitem>Active Directory: Default
(exposes no equality matching rules at all)</listitem>
- <listitem>eDirectory: Optimized Modify Operations
+ <listitem>eDirectory: Default
(exposes no equality matching rules at all)</listitem>
</itemizedlist>
</entry>
- <entry>Optimized Modify Operations</entry>
+ <entry>Default</entry>
</row>
<row>
<entry>Modify Order</entry>
diff --git a/helps/ldapbrowser.help/src/main/docbook/2.80_tools_connection_properties.xml b/helps/ldapbrowser.help/src/main/docbook/2.80_tools_connection_properties.xml
index c080548..68ed5f4 100644
--- a/helps/ldapbrowser.help/src/main/docbook/2.80_tools_connection_properties.xml
+++ b/helps/ldapbrowser.help/src/main/docbook/2.80_tools_connection_properties.xml
@@ -437,8 +437,8 @@
Specify the modify mode for attributes with an equality matching rule.
Description of options:
<itemizedlist spacing="normal" mark="bullet">
- <listitem>Optimized Modify Operations: uses add/delete by default,
- uses replace if operation count is less</listitem>
+ <listitem>Default: uses add/delete by default,
+ uses replace for X-ORDERED attributes</listitem>
<listitem>Always REPLACE: always uses replace operations to perform
entry modifications</listitem>
<listitem>Always ADD/DELETE: always uses add and/or delete operations
@@ -452,8 +452,8 @@
Specify the modify mode for attributes with *no* equality matching rule.
Description of options:
<itemizedlist spacing="normal" mark="bullet">
- <listitem>Optimized Modify Operations: uses add/delete by default,
- uses replace if operation count is less</listitem>
+ <listitem>Default: uses add/delete by default,
+ uses replace for X-ORDERED attributes</listitem>
<listitem>Always REPLACE: always uses replace operations to perform
entry modifications</listitem>
<listitem>Always ADD/DELETE: always uses add and/or delete operations
@@ -461,14 +461,14 @@
</itemizedlist>
Recommended values for various LDAP servers:
<itemizedlist spacing="normal" mark="bullet">
- <listitem>ApacheDS: Optimized Modify Operations or REPLACE</listitem>
+ <listitem>ApacheDS: Default or REPLACE</listitem>
<listitem>OpenLDAP: REPLACE</listitem>
- <listitem>OpenDS / SunDSEE: Optimized Modify Operations or REPLACE</listitem>
- <listitem>FedoraDS / 389DS: Optimized Modify Operations
+ <listitem>OpenDS / SunDSEE: Default or REPLACE</listitem>
+ <listitem>FedoraDS / 389DS: Default
(missing equality matching rules for many standard attribute types)</listitem>
- <listitem>Active Directory: Optimized Modify Operations
+ <listitem>Active Directory: Default
(exposes no equality matching rules at all)</listitem>
- <listitem>eDirectory: Optimized Modify Operations
+ <listitem>eDirectory: Default
(exposes no equality matching rules at all)</listitem>
</itemizedlist>
</entry>
diff --git a/plugins/ldapbrowser.common/src/main/java/org/apache/directory/studio/ldapbrowser/common/widgets/connection/messages.properties b/plugins/ldapbrowser.common/src/main/java/org/apache/directory/studio/ldapbrowser/common/widgets/connection/messages.properties
index fd9548f..b1d9963 100644
--- a/plugins/ldapbrowser.common/src/main/java/org/apache/directory/studio/ldapbrowser/common/widgets/connection/messages.properties
+++ b/plugins/ldapbrowser.common/src/main/java/org/apache/directory/studio/ldapbrowser/common/widgets/connection/messages.properties
@@ -38,11 +38,11 @@ BrowserParameterPage.ManageDsaItWhileBrowsingTooltip=If enabled the ManageDsaIT
EditorParameterPage.ModifyGroup=Entry Modifcation
EditorParameterPage.ModifyMode=Modify Mode:
EditorParameterPage.ModifyModeAddDel=Always use ADD and/or DELETE
-EditorParameterPage.ModifyModeDefault=Optimized Modify Operations
+EditorParameterPage.ModifyModeDefault=Default
EditorParameterPage.ModifyModeNoEMR=Modify Mode (no equality matching rule):
-EditorParameterPage.ModifyModeNoEMRTooltip=Specify the modify mode for attributes with *no* equality matching rule.\n\nDescription of options:\n* Optimized Modify Operations: uses add/delete by default, uses replace if operation count is less\n* Always use REPLACE: always uses replace operations to perform entry modifications\n* Always use ADD and/or DELETE: always uses add and/or delete operations to perform entry modifications\n\nRecommended values for various LDAP servers:\n* ApacheDS [...]
+EditorParameterPage.ModifyModeNoEMRTooltip=Specify the modify mode for attributes with *no* equality matching rule.\n\nDescription of options:\n* Default: uses add/delete by default, uses replace for X-ORDERED attributes\n* Always use REPLACE: always uses replace operations to perform entry modifications\n* Always use ADD and/or DELETE: always uses add and/or delete operations to perform entry modifications\n\nRecommended values for various LDAP servers:\n* ApacheDS: Default or REPLACE\n [...]
EditorParameterPage.ModifyModeReplace=Always use REPLACE
-EditorParameterPage.ModifyModeTooltip=Specify the modify mode for attributes with an equality matching rule.\n\nDescription of options:\n* Optimized Modify Operations: uses add/delete by default, uses replace if operation count is less\n* Always REPLACE: always uses replace operations to perform entry modifications\n* Always ADD/DELETE: always uses add and/or delete operations to perform entry modifications\n\nRecommended value: Optimized Modify Operations
+EditorParameterPage.ModifyModeTooltip=Specify the modify mode for attributes with an equality matching rule.\n\nDescription of options:\n* Default: uses add/delete by default, uses replace for X-ORDERED attributes\n* Always REPLACE: always uses replace operations to perform entry modifications\n* Always ADD/DELETE: always uses add and/or delete operations to perform entry modifications\n\nRecommended value: Default
EditorParameterPage.ModifyOrder=Modify Order:
EditorParameterPage.ModifyOrderAddFirst=ADD First
EditorParameterPage.ModifyOrderDelFirst=DELETE First
diff --git a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/utils/Utils.java b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/utils/Utils.java
index af5b4fa..2cc7bad 100644
--- a/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/utils/Utils.java
+++ b/plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/utils/Utils.java
@@ -30,6 +30,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -406,7 +407,7 @@ public class Utils
ModifyOrder modifyAddDeleteOrder = oldEntry.getBrowserConnection().getModifyAddDeleteOrder();
// get all attribute descriptions
- Set<String> attributeDescriptions = new HashSet<>();
+ Set<String> attributeDescriptions = new LinkedHashSet<>();
for ( IAttribute oldAttr : oldEntry.getAttributes() )
{
@@ -487,6 +488,10 @@ public class Utils
{
// delete all
modSpec = LdifModSpec.createDelete( attributeDescription );
+ for ( IValue value : oldAttribute.getValues() )
+ {
+ modSpec.addAttrVal( computeDiffCreateAttrValLine( value ) );
+ }
}
modSpec.finish( LdifModSpecSepLine.create() );
@@ -558,16 +563,12 @@ public class Utils
/*
* we use add/del in the following cases:
* - add/del is forced in the connection configuration
- * - only values to add
- * - only values to delete
- * - the sum of adds and deletes is smaller or equal than the number of replaces
+ * - for attributes w/o X-ORDERED 'VALUES'
*
* we use replace in the following cases:
- * - the number of replaces is smaller to the sum of adds and deletes
* - for attributes with X-ORDERED 'VALUES'
*/
- if ( isAddDelForced || ( toAdd.size() + toDel.size() <= newAttrValLines.size() && !isOrderedValue )
- || ( !toDel.isEmpty() && toAdd.isEmpty() ) || ( !toAdd.isEmpty() && toDel.isEmpty() ) )
+ if ( isAddDelForced || !isOrderedValue )
{
// add/del del/add
LdifModSpec addModSpec = LdifModSpec.createAdd( attributeDescription );
diff --git a/tests/test.integration.core/pom-first.xml b/tests/test.integration.core/pom-first.xml
index 70a38f9..9a1a9ad 100644
--- a/tests/test.integration.core/pom-first.xml
+++ b/tests/test.integration.core/pom-first.xml
@@ -48,6 +48,7 @@
<Bundle-Activator>org.apache.directory.studio.test.integration.core.Activator</Bundle-Activator>
<Require-Bundle>org.junit;bundle-version="4.11.0",
+ org.hamcrest.library;bundle-version="1.3.0",
org.apache.directory.server.apacheds-test-framework;bundle-version="${org.apache.directory.server.version}",
org.apache.directory.server.annotations;bundle-version="${org.apache.directory.server.version}",
org.apache.directory.server.xdbm.partition;bundle-version="${org.apache.directory.server.version}",
diff --git a/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/core/ComputeDiffTest.java b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/core/ComputeDiffTest.java
new file mode 100644
index 0000000..1f7cc8e
--- /dev/null
+++ b/tests/test.integration.core/src/main/java/org/apache/directory/studio/test/integration/core/ComputeDiffTest.java
@@ -0,0 +1,309 @@
+/*
+ * 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.directory.studio.test.integration.core;
+
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertNull;
+
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.studio.connection.core.event.ConnectionEventRegistry;
+import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection;
+import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection.ModifyMode;
+import org.apache.directory.studio.ldapbrowser.core.model.IEntry;
+import org.apache.directory.studio.ldapbrowser.core.model.impl.Attribute;
+import org.apache.directory.studio.ldapbrowser.core.model.impl.DummyConnection;
+import org.apache.directory.studio.ldapbrowser.core.model.impl.DummyEntry;
+import org.apache.directory.studio.ldapbrowser.core.model.impl.Value;
+import org.apache.directory.studio.ldapbrowser.core.model.schema.Schema;
+import org.apache.directory.studio.ldapbrowser.core.utils.Utils;
+import org.apache.directory.studio.ldifparser.LdifParserConstants;
+import org.apache.directory.studio.ldifparser.model.LdifFile;
+import org.apache.directory.studio.ldifparser.model.container.LdifChangeModifyRecord;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class ComputeDiffTest
+{
+ private IBrowserConnection connection;
+
+ private IEntry oldEntry;
+ private IEntry newEntry;
+
+ static class TestConnection extends DummyConnection
+ {
+ private static final long serialVersionUID = 1L;
+
+ private ModifyMode modifyMode = ModifyMode.DEFAULT;
+ private ModifyMode modifyModeNoEMR = ModifyMode.DEFAULT;
+
+ public TestConnection()
+ {
+ super( Schema.DEFAULT_SCHEMA );
+ }
+
+
+ public ModifyMode getModifyMode()
+ {
+ return modifyMode;
+ }
+
+
+ public void setModifyMode( ModifyMode mode )
+ {
+ this.modifyMode = mode;
+ }
+
+
+ public ModifyMode getModifyModeNoEMR()
+ {
+ return modifyModeNoEMR;
+ }
+
+
+ public void setModifyModeNoEMR( ModifyMode mode )
+ {
+ this.modifyModeNoEMR = mode;
+ }
+
+ }
+
+ @Before
+ public void setup() throws Exception
+ {
+ ConnectionEventRegistry.suspendEventFiringInCurrentThread();
+ connection = new TestConnection();
+ oldEntry = new DummyEntry( new Dn( "cn=foo" ), connection );
+ newEntry = new DummyEntry( new Dn( "cn=foo" ), connection );
+ }
+
+
+ @Test
+ public void shouldReturnNullForEqualEntries()
+ {
+ // entries without attribute
+ assertNull( Utils.computeDiff( oldEntry, newEntry ) );
+ assertNull( Utils.computeDiff( oldEntry, oldEntry ) );
+ assertNull( Utils.computeDiff( newEntry, newEntry ) );
+
+ // entries with attributes
+ addAttribute( oldEntry, "cn", "1" );
+ addAttribute( oldEntry, "member", "cn=1", "cn=2", "cn=3" );
+ addAttribute( newEntry, "cn", "1" );
+ addAttribute( newEntry, "member", "cn=1", "cn=2", "cn=3" );
+ assertNull( Utils.computeDiff( oldEntry, newEntry ) );
+ assertNull( Utils.computeDiff( oldEntry, oldEntry ) );
+ assertNull( Utils.computeDiff( newEntry, newEntry ) );
+ }
+
+
+ @Test
+ public void shouldAddOneAttributeWithOneValue()
+ {
+ addAttribute( newEntry, "cn", "1" );
+
+ connection.setModifyMode( ModifyMode.DEFAULT );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "add:cn", "cn:1" );
+
+ connection.setModifyMode( ModifyMode.REPLACE );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "replace:cn", "cn:1" );
+ }
+
+
+ @Test
+ public void shouldAddMultipleAttributeWithMultipleValues()
+ {
+ addAttribute( newEntry, "cn", "1", "2" );
+ addAttribute( newEntry, "member", "cn=1", "cn=2", "cn=3" );
+
+ connection.setModifyMode( ModifyMode.DEFAULT );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+ "add:cn", "cn:1", "cn:2", "-", "add:member", "member:cn=1", "member:cn=2", "member:cn=3" );
+
+ connection.setModifyMode( ModifyMode.REPLACE );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+ "replace:cn", "cn:1", "cn:2", "-", "replace:member", "member:cn=1", "member:cn=2", "member:cn=3" );
+ }
+
+
+ @Test
+ public void shouldAddOneValueToOneExistingAttribute()
+ {
+ addAttribute( oldEntry, "cn", "1" );
+ addAttribute( newEntry, "cn", "1", "2" );
+
+ connection.setModifyMode( ModifyMode.DEFAULT );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "add:cn", "cn:2" );
+
+ connection.setModifyMode( ModifyMode.REPLACE );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "replace:cn", "cn:1", "cn:2" );
+ }
+
+
+ @Test
+ public void shouldAddMultipleValuesToMultipleExistingAttributes()
+ {
+ addAttribute( oldEntry, "cn", "1" );
+ addAttribute( newEntry, "cn", "1", "2", "3" );
+ addAttribute( oldEntry, "member", "cn=1", "cn=2", "cn=3" );
+ addAttribute( newEntry, "member", "cn=1", "cn=2", "cn=3", "cn=4", "cn=5" );
+
+ connection.setModifyMode( ModifyMode.DEFAULT );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+ "add:cn", "cn:2", "cn:3", "-", "add:member", "member:cn=4", "member:cn=5" );
+
+ connection.setModifyMode( ModifyMode.REPLACE );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+ "replace:cn", "cn:1", "cn:2", "cn:3", "-",
+ "replace:member", "member:cn=1", "member:cn=2", "member:cn=3", "member:cn=4", "member:cn=5" );
+ }
+
+
+ @Test
+ public void shouldDeleteAllOneValue()
+ {
+ addAttribute( oldEntry, "cn", "1" );
+
+ connection.setModifyMode( ModifyMode.DEFAULT );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "delete:cn", "cn:1" );
+
+ connection.setModifyMode( ModifyMode.REPLACE );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "replace:cn" );
+ }
+
+
+ @Test
+ public void shouldDeleteAllMultipleValues()
+ {
+ addAttribute( oldEntry, "cn", "1", "2" );
+ addAttribute( oldEntry, "member", "cn=1", "cn=2", "cn=3" );
+
+ connection.setModifyMode( ModifyMode.DEFAULT );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+ "delete:cn", "cn:1", "cn:2", "-",
+ "delete:member", "member:cn=1", "member:cn=2", "member:cn=3" );
+
+ connection.setModifyMode( ModifyMode.REPLACE );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "replace:cn", "-", "replace:member" );
+ }
+
+
+ @Test
+ public void shouldDeleteOneValue()
+ {
+ addAttribute( oldEntry, "cn", "1", "2", "3" );
+ addAttribute( newEntry, "cn", "1", "2" );
+
+ connection.setModifyMode( ModifyMode.DEFAULT );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "delete:cn", "cn:3" );
+
+ connection.setModifyMode( ModifyMode.REPLACE );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "replace:cn", "cn:1", "cn:2" );
+ }
+
+
+ @Test
+ public void shouldDeleteMultipleValues()
+ {
+ addAttribute( oldEntry, "cn", "1", "2", "3" );
+ addAttribute( newEntry, "cn", "1" );
+ addAttribute( oldEntry, "member", "cn=1", "cn=2", "cn=3", "cn=4", "cn=5" );
+ addAttribute( newEntry, "member", "cn=1" );
+
+ connection.setModifyMode( ModifyMode.DEFAULT );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+ "delete:cn", "cn:2", "cn:3", "-",
+ "delete:member", "member:cn=2", "member:cn=3", "member:cn=4", "member:cn=5" );
+
+ connection.setModifyMode( ModifyMode.REPLACE );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+ "replace:cn", "cn:1", "-", "replace:member", "member:cn=1" );
+ }
+
+
+ @Test
+ public void shouldReplaceOneValue()
+ {
+ addAttribute( oldEntry, "cn", "1" );
+ addAttribute( newEntry, "cn", "2" );
+
+ connection.setModifyMode( ModifyMode.DEFAULT );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "delete:cn", "cn:1", "-", "add:cn", "cn:2" );
+
+ connection.setModifyMode( ModifyMode.REPLACE );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ), "replace:cn", "cn:2" );
+ }
+
+
+ @Test
+ public void shouldReplaceMultipleValues()
+ {
+ addAttribute( oldEntry, "cn", "1", "2", "3" );
+ addAttribute( newEntry, "cn", "4" );
+ addAttribute( oldEntry, "member", "cn=1", "cn=2", "cn=3" );
+ addAttribute( newEntry, "member", "cn=1", "cn=4", "cn=5" );
+
+ connection.setModifyMode( ModifyMode.DEFAULT );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+ "delete:cn", "cn:1", "cn:2", "cn:3", "-", "add:cn", "cn:4", "-",
+ "delete:member", "member:cn=2", "member:cn=3", "-", "add:member", "member:cn=4", "member:cn=5" );
+
+ connection.setModifyMode( ModifyMode.REPLACE );
+ assertChangeModify( Utils.computeDiff( oldEntry, newEntry ),
+ "replace:cn", "cn:4", "-",
+ "replace:member", "member:cn=1", "member:cn=4", "member:cn=5" );
+ }
+
+
+ private static void addAttribute( IEntry entry, String attributeName, Object... rawValues )
+ {
+ Attribute attribute = new Attribute( entry, attributeName );
+ entry.addAttribute( attribute );
+ for ( Object rawValue : rawValues )
+ {
+ Value value = new Value( attribute, rawValue );
+ attribute.addValue( value );
+ }
+ }
+
+
+ private void assertChangeModify( LdifFile diff, String... lines )
+ {
+ // System.out.println( diff.toRawString() );
+ assertThat( diff.isChangeType(), equalTo( true ) );
+ assertThat( diff.getContainers(), hasSize( 1 ) );
+ assertThat( diff.getLastContainer(), instanceOf( LdifChangeModifyRecord.class ) );
+
+ String s = "changetype:modify" + LdifParserConstants.LINE_SEPARATOR;
+ for ( String line : lines )
+ {
+ assertThat( diff.toRawString(), containsString( line ) );
+ s += line + LdifParserConstants.LINE_SEPARATOR;
+ }
+ s += "-" + LdifParserConstants.LINE_SEPARATOR;
+ assertThat( diff.toRawString(), containsString( s ) );
+ }
+
+}
diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/EntryEditorTest.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/EntryEditorTest.java
index ae33a71..04fcbec 100644
--- a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/EntryEditorTest.java
+++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/EntryEditorTest.java
@@ -125,9 +125,6 @@ public class EntryEditorTest extends AbstractLdapTestUnit
/**
* Test adding, editing and deleting of attributes in the entry editor.
- *
- * @throws Exception
- * the exception
*/
@Test
public void testAddEditDeleteAttribute() throws Exception
@@ -165,7 +162,6 @@ public class EntryEditorTest extends AbstractLdapTestUnit
entryEditorBot.editValue( "description", "This is the 2nd description." );
entryEditorBot.typeValueAndFinish( "This is the 3rd description." );
assertEquals( 10, entryEditorBot.getAttributeValues().size() );
- assertEquals( 10, entryEditorBot.getAttributeValues().size() );
assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the 2nd description." ) );
assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 3rd description." ) );
@@ -185,13 +181,14 @@ public class EntryEditorTest extends AbstractLdapTestUnit
assertEquals( 9, entryEditorBot.getAttributeValues().size() );
assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the final description." ) );
- modificationLogsViewBot.waitForText( "replace: description\ndescription: This is the final description." );
+ modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the 1st description." );
+ modificationLogsViewBot.waitForText( "add: description\ndescription: This is the final description." );
// delete 1st value/attribute
entryEditorBot.deleteValue( "description", "This is the final description." );
assertEquals( 8, entryEditorBot.getAttributeValues().size() );
assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the final description." ) );
- modificationLogsViewBot.waitForText( "delete: description\n-" );
+ modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the final description.\n-" );
assertEquals( "Expected 6 modifications.", 6,
StringUtils.countMatches( modificationLogsViewBot.getModificationLogsText(), "#!RESULT OK" ) );
@@ -199,6 +196,55 @@ public class EntryEditorTest extends AbstractLdapTestUnit
/**
+ * Test adding, editing and deleting of attributes without equality matching rule in the entry editor.
+ */
+ @Test
+ public void testAddEditDeleteAttributeWithoutEqualityMatchingRule() throws Exception
+ {
+ browserViewBot.selectEntry( "DIT", "Root DSE", "ou=system", "ou=users", "cn=Barbara Jensen" );
+
+ EntryEditorBot entryEditorBot = studioBot.getEntryEditorBot( "cn=Barbara Jensen,ou=users,ou=system" );
+ entryEditorBot.activate();
+ String dn = entryEditorBot.getDnText();
+ assertEquals( "DN: cn=Barbara Jensen,ou=users,ou=system", dn );
+ assertEquals( 8, entryEditorBot.getAttributeValues().size() );
+ assertEquals( "", modificationLogsViewBot.getModificationLogsText() );
+
+ // add facsimileTelephoneNumber attribute
+ entryEditorBot.activate();
+ NewAttributeWizardBot wizardBot = entryEditorBot.openNewAttributeWizard();
+ assertTrue( wizardBot.isVisible() );
+ wizardBot.typeAttributeType( "facsimileTelephoneNumber" );
+ wizardBot.clickFinishButton();
+ entryEditorBot.typeValueAndFinish( "+1 234 567 890" );
+ assertEquals( 9, entryEditorBot.getAttributeValues().size() );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: +1 234 567 890" ) );
+ modificationLogsViewBot
+ .waitForText( "add: facsimileTelephoneNumber\nfacsimileTelephoneNumber: +1 234 567 890" );
+
+ // edit value
+ entryEditorBot.editValue( "facsimileTelephoneNumber", "+1 234 567 890" );
+ entryEditorBot.typeValueAndFinish( "000000000000" );
+ assertEquals( 9, entryEditorBot.getAttributeValues().size() );
+ assertFalse( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: +1 234 567 890" ) );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: 000000000000" ) );
+ modificationLogsViewBot
+ .waitForText( "delete: facsimileTelephoneNumber\nfacsimileTelephoneNumber: +1 234 567 890" );
+ modificationLogsViewBot.waitForText( "add: facsimileTelephoneNumber\nfacsimileTelephoneNumber: 000000000000" );
+
+ // delete 1st value/attribute
+ entryEditorBot.deleteValue( "facsimileTelephoneNumber", "000000000000" );
+ assertEquals( 8, entryEditorBot.getAttributeValues().size() );
+ assertFalse( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: 000000000000" ) );
+ modificationLogsViewBot
+ .waitForText( "delete: facsimileTelephoneNumber\nfacsimileTelephoneNumber: 000000000000\n-" );
+
+ assertEquals( "Expected 3 modifications.", 3,
+ StringUtils.countMatches( modificationLogsViewBot.getModificationLogsText(), "#!RESULT OK" ) );
+ }
+
+
+ /**
* DIRSTUDIO-483: DN Editor escapes all non-ascii characters
*
* @throws Exception
@@ -471,7 +517,8 @@ public class EntryEditorTest extends AbstractLdapTestUnit
assertTrue( entryEditorBot.getAttributeValues().contains( "description: " + newValue ) );
String description2Ldif = LdifAttrValLine.create( "description", newValue )
.toFormattedString( LdifFormatParameters.DEFAULT ).replace( LdifParserConstants.LINE_SEPARATOR, "\n" );
- modificationLogsViewBot.waitForText( "replace: description\n" + description2Ldif );
+ modificationLogsViewBot.waitForText( "delete: description\ndescription: testTextValueEditor 1" );
+ modificationLogsViewBot.waitForText( "add: description\n" + description2Ldif );
}
@@ -639,7 +686,8 @@ public class EntryEditorTest extends AbstractLdapTestUnit
aciItemEditor.clickOkButton();
SWTUtils.sleep( 1000 );
- modificationLogsViewBot.waitForText( "replace: entryaci\n" );
+ modificationLogsViewBot.waitForText( "delete: entryaci\n" );
+ modificationLogsViewBot.waitForText( "add: entryaci\n" );
}
@@ -678,7 +726,8 @@ public class EntryEditorTest extends AbstractLdapTestUnit
aciItemEditor.clickOkButton();
SWTUtils.sleep( 1000 );
- modificationLogsViewBot.waitForText( "replace: entryaci\n" );
+ modificationLogsViewBot.waitForText( "delete: entryaci\n" );
+ modificationLogsViewBot.waitForText( "add: entryaci\n" );
}
@@ -706,7 +755,8 @@ public class EntryEditorTest extends AbstractLdapTestUnit
aciItemEditor.clickOkButton();
SWTUtils.sleep( 1000 );
- modificationLogsViewBot.waitForText( "replace: prescriptiveaci\n" );
+ modificationLogsViewBot.waitForText( "delete: prescriptiveaci\n" );
+ modificationLogsViewBot.waitForText( "add: prescriptiveaci\n" );
}
diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/ErrorHandlingTest.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/ErrorHandlingTest.java
index 0f3dd30..6b302a8 100644
--- a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/ErrorHandlingTest.java
+++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/ErrorHandlingTest.java
@@ -195,7 +195,8 @@ public class ErrorHandlingTest extends AbstractLdapTestUnit
// verify in modification logs
modificationLogsViewBot.assertContainsError( "[LDAP result code 21 - invalidAttributeSyntax]",
- "dn: uid=user.1,ou=users,ou=system", "changetype: modify", "replace: telephonenumber",
+ "dn: uid=user.1,ou=users,ou=system", "changetype: modify", "delete: telephonenumber",
+ "telephonenumber: 976-893-3312", "-", "add: telephonenumber",
"telephonenumber: Invalid phone number" );
}
diff --git a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/OpenLdapTest.java b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/OpenLdapTest.java
index 72d94f4..0eae6d1 100644
--- a/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/OpenLdapTest.java
+++ b/tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/OpenLdapTest.java
@@ -24,6 +24,7 @@ package org.apache.directory.studio.test.integration.ui;
import static org.apache.directory.studio.test.integration.ui.Constants.LOCALHOST;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -33,15 +34,21 @@ import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
+import org.apache.commons.lang3.StringUtils;
import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException;
import org.apache.directory.api.ldap.model.ldif.LdifEntry;
import org.apache.directory.api.ldap.model.ldif.LdifReader;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;
import org.apache.directory.ldap.client.api.exception.InvalidConnectionException;
import org.apache.directory.studio.connection.core.Connection;
+import org.apache.directory.studio.ldapbrowser.core.BrowserCorePlugin;
import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection;
+import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection.ModifyMode;
import org.apache.directory.studio.test.integration.ui.bots.BrowserViewBot;
import org.apache.directory.studio.test.integration.ui.bots.ConnectionsViewBot;
+import org.apache.directory.studio.test.integration.ui.bots.EntryEditorBot;
+import org.apache.directory.studio.test.integration.ui.bots.ModificationLogsViewBot;
+import org.apache.directory.studio.test.integration.ui.bots.NewAttributeWizardBot;
import org.apache.directory.studio.test.integration.ui.bots.NewConnectionWizardBot;
import org.apache.directory.studio.test.integration.ui.bots.SearchDialogBot;
import org.apache.directory.studio.test.integration.ui.bots.StudioBot;
@@ -101,6 +108,7 @@ public class OpenLdapTest
private StudioBot studioBot;
private ConnectionsViewBot connectionsViewBot;
private BrowserViewBot browserViewBot;
+ private ModificationLogsViewBot modificationLogsViewBot;
private Connection connection;
@@ -113,6 +121,7 @@ public class OpenLdapTest
connection = connectionsViewBot.createTestConnection( "OpenLdapTest", OPENLDAP_HOST, OPENLDAP_PORT,
OPENLDAP_ADMIN_DN, OPENLDAP_ADMIN_PASSWORD );
browserViewBot = studioBot.getBrowserView();
+ modificationLogsViewBot = studioBot.getModificationLogsViewBot();
try ( LdapNetworkConnection connection = new LdapNetworkConnection( OPENLDAP_HOST, OPENLDAP_PORT );
LdifReader ldifReader = new LdifReader( OpenLdapTest.class.getResourceAsStream( "OpenLdapTest.ldif" ) ) )
@@ -293,4 +302,128 @@ public class OpenLdapTest
wizardBot.clickCancelButton();
}
+
+ /**
+ * Test adding, editing and deleting of attributes in the entry editor.
+ */
+ @Test
+ public void testAddEditDeleteAttribute() throws Exception
+ {
+ browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "uid=user.1" );
+
+ EntryEditorBot entryEditorBot = studioBot.getEntryEditorBot( "uid=user.1,ou=users,dc=example,dc=org" );
+ entryEditorBot.activate();
+ String dn = entryEditorBot.getDnText();
+ assertEquals( "DN: uid=user.1,ou=users,dc=example,dc=org", dn );
+ assertEquals( 22, entryEditorBot.getAttributeValues().size() );
+ assertEquals( "", modificationLogsViewBot.getModificationLogsText() );
+
+ // add description attribute
+ entryEditorBot.activate();
+ NewAttributeWizardBot wizardBot = entryEditorBot.openNewAttributeWizard();
+ assertTrue( wizardBot.isVisible() );
+ wizardBot.typeAttributeType( "description" );
+ wizardBot.clickFinishButton();
+ entryEditorBot.typeValueAndFinish( "This is the 1st description." );
+ assertEquals( 23, entryEditorBot.getAttributeValues().size() );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
+ modificationLogsViewBot.waitForText( "add: description\ndescription: This is the 1st description." );
+
+ // add second value
+ entryEditorBot.activate();
+ entryEditorBot.addValue( "description" );
+ entryEditorBot.typeValueAndFinish( "This is the 2nd description." );
+ assertEquals( 24, entryEditorBot.getAttributeValues().size() );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 2nd description." ) );
+ modificationLogsViewBot.waitForText( "add: description\ndescription: This is the 2nd description." );
+
+ // edit second value
+ entryEditorBot.editValue( "description", "This is the 2nd description." );
+ entryEditorBot.typeValueAndFinish( "This is the 3rd description." );
+ assertEquals( 24, entryEditorBot.getAttributeValues().size() );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
+ assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the 2nd description." ) );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 3rd description." ) );
+ modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the 2nd description." );
+ modificationLogsViewBot.waitForText( "add: description\ndescription: This is the 3rd description." );
+
+ // delete second value
+ entryEditorBot.deleteValue( "description", "This is the 3rd description." );
+ assertEquals( 23, entryEditorBot.getAttributeValues().size() );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
+ assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the 3rd description." ) );
+ modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the 3rd description." );
+
+ // edit 1st value
+ entryEditorBot.editValue( "description", "This is the 1st description." );
+ entryEditorBot.typeValueAndFinish( "This is the final description." );
+ assertEquals( 23, entryEditorBot.getAttributeValues().size() );
+ assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the 1st description." ) );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "description: This is the final description." ) );
+ modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the 1st description." );
+ modificationLogsViewBot.waitForText( "add: description\ndescription: This is the final description." );
+
+ // delete 1st value/attribute
+ entryEditorBot.deleteValue( "description", "This is the final description." );
+ assertEquals( 22, entryEditorBot.getAttributeValues().size() );
+ assertFalse( entryEditorBot.getAttributeValues().contains( "description: This is the final description." ) );
+ modificationLogsViewBot.waitForText( "delete: description\ndescription: This is the final description.\n-" );
+
+ assertEquals( "Expected 6 modifications.", 6,
+ StringUtils.countMatches( modificationLogsViewBot.getModificationLogsText(), "#!RESULT OK" ) );
+ }
+
+
+ /**
+ * Test adding, editing and deleting of attributes without equality matching rule in the entry editor.
+ */
+ @Test
+ public void testAddEditDeleteAttributeWithoutEqualityMatchingRule() throws Exception
+ {
+ IBrowserConnection browserConnection = BrowserCorePlugin.getDefault().getConnectionManager()
+ .getBrowserConnection( connection );
+ browserConnection.setModifyModeNoEMR( ModifyMode.REPLACE );
+
+ browserViewBot.selectEntry( "DIT", "Root DSE", "dc=example,dc=org", "ou=users", "uid=user.1" );
+
+ EntryEditorBot entryEditorBot = studioBot.getEntryEditorBot( "uid=user.1,ou=users,dc=example,dc=org" );
+ entryEditorBot.activate();
+ String dn = entryEditorBot.getDnText();
+ assertEquals( "DN: uid=user.1,ou=users,dc=example,dc=org", dn );
+ assertEquals( 22, entryEditorBot.getAttributeValues().size() );
+ assertEquals( "", modificationLogsViewBot.getModificationLogsText() );
+
+ // add facsimileTelephoneNumber attribute
+ entryEditorBot.activate();
+ NewAttributeWizardBot wizardBot = entryEditorBot.openNewAttributeWizard();
+ assertTrue( wizardBot.isVisible() );
+ wizardBot.typeAttributeType( "facsimileTelephoneNumber" );
+ wizardBot.clickFinishButton();
+ entryEditorBot.typeValueAndFinish( "+1 234 567 890" );
+ assertEquals( 23, entryEditorBot.getAttributeValues().size() );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: +1 234 567 890" ) );
+ modificationLogsViewBot
+ .waitForText( "replace: facsimileTelephoneNumber\nfacsimileTelephoneNumber: +1 234 567 890" );
+
+ // edit value
+ entryEditorBot.editValue( "facsimileTelephoneNumber", "+1 234 567 890" );
+ entryEditorBot.typeValueAndFinish( "000000000000" );
+ assertEquals( 23, entryEditorBot.getAttributeValues().size() );
+ assertFalse( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: +1 234 567 890" ) );
+ assertTrue( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: 000000000000" ) );
+ modificationLogsViewBot
+ .waitForText( "replace: facsimileTelephoneNumber\nfacsimileTelephoneNumber: 000000000000" );
+
+ // delete 1st value/attribute
+ entryEditorBot.deleteValue( "facsimileTelephoneNumber", "000000000000" );
+ assertEquals( 22, entryEditorBot.getAttributeValues().size() );
+ assertFalse( entryEditorBot.getAttributeValues().contains( "facsimileTelephoneNumber: 000000000000" ) );
+ modificationLogsViewBot
+ .waitForText( "replace: facsimileTelephoneNumber\n-" );
+
+ assertEquals( "Expected 3 modifications.", 3,
+ StringUtils.countMatches( modificationLogsViewBot.getModificationLogsText(), "#!RESULT OK" ) );
+ }
+
}
diff --git a/tests/test.integration.ui/src/main/resources/org/apache/directory/studio/test/integration/ui/OpenLdapTest.ldif b/tests/test.integration.ui/src/main/resources/org/apache/directory/studio/test/integration/ui/OpenLdapTest.ldif
index d64ed89..4e39b2f 100644
--- a/tests/test.integration.ui/src/main/resources/org/apache/directory/studio/test/integration/ui/OpenLdapTest.ldif
+++ b/tests/test.integration.ui/src/main/resources/org/apache/directory/studio/test/integration/ui/OpenLdapTest.ldif
@@ -42,7 +42,6 @@ l: Tallahassee
st: DE
postalCode: 67698
postalAddress: Aaccf Amar$27919 Broadway Street$Tallahassee, DE 67698
-description: This is the description for Aaccf Amar.
roomNumber: 1388
dn: uid=user.2,ou=users,dc=example,dc=org