/*
|
* The contents of this file are subject to the terms of the Common Development and
|
* Distribution License (the License). You may not use this file except in compliance with the
|
* License.
|
*
|
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
|
* specific language governing permission and limitations under the License.
|
*
|
* When distributing Covered Software, include this CDDL Header Notice in each file and include
|
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
|
* Header, with the fields enclosed by brackets [] replaced by your own identifying
|
* information: "Portions Copyright [year] [name of copyright owner]".
|
*
|
* Copyright 2015-2016 ForgeRock AS.
|
*/
|
package org.opends.server.backends.pluggable;
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.mockito.Mockito.*;
|
import static org.opends.server.ConfigurationMock.mockCfg;
|
import static org.opends.server.TestCaseUtils.makeEntry;
|
import static org.opends.server.protocols.internal.InternalClientConnection.getRootConnection;
|
import static org.opends.server.protocols.internal.Requests.newSearchRequest;
|
import static org.opends.server.util.CollectionUtils.*;
|
import static org.opends.server.util.ServerConstants.*;
|
import static org.testng.Assert.fail;
|
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.LinkedList;
|
import java.util.List;
|
|
import org.forgerock.opendj.config.server.ConfigException;
|
import org.forgerock.opendj.ldap.ByteString;
|
import org.forgerock.opendj.ldap.ByteStringBuilder;
|
import org.forgerock.opendj.ldap.DN;
|
import org.forgerock.opendj.ldap.ResultCode;
|
import org.forgerock.opendj.ldap.SearchScope;
|
import org.opends.server.DirectoryServerTestCase;
|
import org.opends.server.TestCaseUtils;
|
import org.forgerock.opendj.server.config.meta.BackendVLVIndexCfgDefn.Scope;
|
import org.forgerock.opendj.server.config.server.BackendVLVIndexCfg;
|
import org.forgerock.opendj.server.config.server.PDBBackendCfg;
|
import org.opends.server.backends.pdb.PDBBackend;
|
import org.opends.server.controls.ServerSideSortRequestControl;
|
import org.opends.server.controls.ServerSideSortResponseControl;
|
import org.opends.server.controls.VLVRequestControl;
|
import org.opends.server.controls.VLVResponseControl;
|
import org.opends.server.core.AddOperation;
|
import org.opends.server.core.DirectoryServer;
|
import org.opends.server.protocols.internal.InternalSearchOperation;
|
import org.opends.server.protocols.internal.SearchRequest;
|
import org.opends.server.protocols.ldap.LDAPControl;
|
import org.opends.server.protocols.ldap.LDAPResultCode;
|
import org.opends.server.types.Control;
|
import org.opends.server.types.DirectoryException;
|
import org.opends.server.types.Entry;
|
import org.opends.server.types.SearchResultEntry;
|
import org.testng.annotations.AfterClass;
|
import org.testng.annotations.BeforeClass;
|
import org.testng.annotations.DataProvider;
|
import org.testng.annotations.Test;
|
|
/**
|
* This class contains a number of test cases for the controls which are supported within the
|
* backend. In particular, VLV, paged results, and server side sorting controls.
|
*/
|
@SuppressWarnings("javadoc")
|
public class ControlsTestCase extends DirectoryServerTestCase
|
{
|
private static final String BACKEND_BASE_DN = "dc=pluggable-vlv,dc=com";
|
private static final String BACKEND_NAME = "pluggable-vlv";
|
private static final String VLV_FILTER = "(objectClass=person)";
|
|
// @formatter:off
|
private static final User[] USERS = {
|
user(givenNames("Alice"), surname("Zorro"), employeeNumber(0)),
|
user(givenNames("Bob", "Robert"), surname("deBuilder"), employeeNumber(1)),
|
user(givenNames("Charlie"), surname("Speed"), employeeNumber(2)),
|
user(givenNames("Mickie"), surname("Mouse"), employeeNumber(3)),
|
user(givenNames("William", "Bill"), surname("Beak"), employeeNumber(4)),
|
user(givenNames(), surname("Prince"), employeeNumber(5)),
|
user(givenNames("Charlie"), surname("Chalk"), employeeNumber(6)),
|
user(givenNames("Albert"), surname("DEinstein"), employeeNumber(7)),
|
user(givenNames("Mini"), surname("Mouse"), employeeNumber(8)),
|
};
|
// @formatter:on
|
|
private static final int CONTENT_COUNT = USERS.length;
|
|
/** Indexed: ordered by ascending givenName then entryID */
|
private static final String SORT_ORDER_1 = "givenName";
|
|
/** Indexed: ordered by descending sn and descending employeeNumber then entryID */
|
private static final String SORT_ORDER_2 = "-sn -employeeNumber";
|
|
/** Unindexed: ordered by ascending sn and ascending employee number then entryID */
|
private static final String SORT_ORDER_3 = "sn employeeNumber";
|
|
/** Indexed: ordered by ascending givenName then entryID */
|
private static final String SORT_ORDER_4 = "surname:caseExactOrderingMatch";
|
|
/** Ordered by {@link #SORT_ORDER_1} */
|
private static final List<Integer> USERS_BY_ENTRY_ID = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8);
|
|
/** Indexed: ordered by {@link #SORT_ORDER_1} */
|
private static final List<Integer> USERS_BY_SORT_ORDER_1 = Arrays.asList(7, 0, 4, 1, 2, 6, 3, 8, 5);
|
|
/** Indexed: ordered by {@link #SORT_ORDER_2} */
|
private static final List<Integer> USERS_BY_SORT_ORDER_2 = Arrays.asList(0, 2, 5, 8, 3, 7, 1, 6, 4);
|
|
/** Unindexed: ordered by {@link #SORT_ORDER_3} */
|
private static final List<Integer> USERS_BY_SORT_ORDER_3 = Arrays.asList(4, 6, 1, 7, 3, 8, 5, 2, 0);
|
|
/** Unindexed: ordered by {@link #SORT_ORDER_4} */
|
private static final List<Integer> USERS_BY_SORT_ORDER_4 = Arrays.asList(4, 6, 7, 3, 8, 5, 2, 0, 1);
|
|
private PDBBackend backend;
|
|
@BeforeClass
|
public void beforeClass() throws Exception
|
{
|
TestCaseUtils.startServer();
|
|
final DN baseDN = DN.valueOf(BACKEND_BASE_DN);
|
|
final PDBBackendCfg backendCfg = mockCfg(PDBBackendCfg.class);
|
when(backendCfg.dn()).thenReturn(baseDN);
|
when(backendCfg.getBackendId()).thenReturn(BACKEND_NAME);
|
when(backendCfg.getBaseDN()).thenReturn(newTreeSet(baseDN));
|
when(backendCfg.listBackendIndexes()).thenReturn(new String[0]);
|
when(backendCfg.listBackendVLVIndexes()).thenReturn(new String[] { SORT_ORDER_1, SORT_ORDER_2 });
|
|
when(backendCfg.getDBDirectory()).thenReturn(BACKEND_NAME);
|
when(backendCfg.getDBDirectoryPermissions()).thenReturn("755");
|
when(backendCfg.getDBCacheSize()).thenReturn(0L);
|
when(backendCfg.getDBCachePercent()).thenReturn(20);
|
|
createVlvIndex(baseDN, backendCfg, SORT_ORDER_1);
|
createVlvIndex(baseDN, backendCfg, SORT_ORDER_2);
|
|
backend = new PDBBackend();
|
backend.setBackendID(backendCfg.getBackendId());
|
backend.configureBackend(backendCfg, DirectoryServer.getInstance().getServerContext());
|
backend.openBackend();
|
|
AddOperation op = mock(AddOperation.class);
|
backend.addEntry(makeEntry("dn: " + BACKEND_BASE_DN, "objectclass: top", "objectclass: domain"), op);
|
for (final User user : USERS)
|
{
|
backend.addEntry(user.toEntry(), op);
|
}
|
}
|
|
private void createVlvIndex(final DN baseDN, final PDBBackendCfg backendCfg, final String sortOrder)
|
throws ConfigException
|
{
|
final BackendVLVIndexCfg vlvIndexCfg = mockCfg(BackendVLVIndexCfg.class);
|
when(vlvIndexCfg.getName()).thenReturn(sortOrder);
|
when(vlvIndexCfg.getBaseDN()).thenReturn(baseDN);
|
when(vlvIndexCfg.getFilter()).thenReturn(VLV_FILTER);
|
when(vlvIndexCfg.getScope()).thenReturn(Scope.WHOLE_SUBTREE);
|
when(vlvIndexCfg.getSortOrder()).thenReturn(sortOrder);
|
when(backendCfg.getBackendVLVIndex(sortOrder)).thenReturn(vlvIndexCfg);
|
}
|
|
@DataProvider
|
private Object[][] encodedKeyDataProvider()
|
{
|
// @formatter:off
|
return new Object[][] {
|
// Null keys sort after everything else.
|
{ null, null, 0 },
|
{ "", null, -1 },
|
{ null, "", 1 },
|
{ "00", null, -1 },
|
{ null, "00", 1 },
|
{ "ff", null, -1 },
|
{ null, "ff", 1 },
|
|
// Empty keys sort before everything else.
|
{ "", "", 0 },
|
{ "00", "", 1 },
|
{ "", "00", -1 },
|
{ "ff", "", 1 },
|
{ "", "ff", -1 },
|
|
// Bytes comparisons are unsigned.
|
{ "00", "00", 0 },
|
{ "00", "ff", -1 },
|
{ "ff", "00", 1 },
|
{ "ff", "ff", 0 },
|
|
// Short keys sort before long keys.
|
{ "0000", "00", 1 },
|
{ "00", "0000", -1 },
|
{ "ffff", "ff", 1 },
|
{ "ff", "ffff", -1 },
|
{ "0000", "0000", 0 },
|
{ "ffff", "ffff", 0 },
|
{ "0000", "ffff", -1 },
|
{ "ffff", "0000", 1 },
|
};
|
// @formatter:on
|
}
|
|
@Test(dataProvider = "encodedKeyDataProvider")
|
public void vlvKeyEncodingGenerateCorrectAscendingSortOrder(String key1, String key2, int expectedCompareResult)
|
{
|
vlvKeyEncodingGenerateCorrectSortOrder(key1, key2, expectedCompareResult, false);
|
}
|
|
@Test(dataProvider = "encodedKeyDataProvider")
|
public void vlvKeyEncodingGenerateCorrectDescendingSortOrder(String key1, String key2, int expectedCompareResult)
|
{
|
vlvKeyEncodingGenerateCorrectSortOrder(key1, key2, expectedCompareResult, true);
|
}
|
|
private void vlvKeyEncodingGenerateCorrectSortOrder(String key1, String key2, int expectedCompareResult,
|
boolean isReverseOrder)
|
{
|
ByteString bytes1 = key1 != null ? ByteString.valueOfHex(key1) : null;
|
ByteStringBuilder encodedBytes1 = new ByteStringBuilder();
|
VLVIndex.encodeVLVKeyValue(bytes1, encodedBytes1, isReverseOrder);
|
|
ByteString bytes2 = key2 != null ? ByteString.valueOfHex(key2) : null;
|
ByteStringBuilder encodedBytes2 = new ByteStringBuilder();
|
VLVIndex.encodeVLVKeyValue(bytes2, encodedBytes2, isReverseOrder);
|
|
int actualResult = Math.min(Math.max(encodedBytes1.compareTo(encodedBytes2), -1), 1);
|
assertThat(actualResult).isEqualTo(isReverseOrder ? -expectedCompareResult : expectedCompareResult);
|
}
|
|
@DataProvider
|
private Object[][] indexedVlvByAssertionDataProvider()
|
{
|
// @formatter:off
|
return new Object[][] {
|
{
|
SORT_ORDER_2,
|
beforeCount(0),
|
afterCount(5),
|
assertion("ZZ"), // before first
|
USERS_BY_SORT_ORDER_2.subList(0, 6),
|
expectedPosition(1)
|
},
|
{
|
SORT_ORDER_2,
|
beforeCount(0),
|
afterCount(5),
|
assertion("zorro"), // matches first
|
USERS_BY_SORT_ORDER_2.subList(0, 6),
|
expectedPosition(1)
|
},
|
{
|
SORT_ORDER_2,
|
beforeCount(0),
|
afterCount(3),
|
assertion("PRINCE"), // matches third
|
USERS_BY_SORT_ORDER_2.subList(2, 6),
|
expectedPosition(3)
|
},
|
{
|
SORT_ORDER_2,
|
beforeCount(1),
|
afterCount(3),
|
assertion("prince"), // matches third
|
USERS_BY_SORT_ORDER_2.subList(1, 6),
|
expectedPosition(3)
|
},
|
{
|
SORT_ORDER_2,
|
beforeCount(10),
|
afterCount(10),
|
assertion("prince"), // matches third
|
USERS_BY_SORT_ORDER_2,
|
expectedPosition(3)
|
},
|
{
|
SORT_ORDER_2,
|
beforeCount(0),
|
afterCount(3),
|
assertion("a"), // after all
|
USERS_BY_SORT_ORDER_2.subList(9, 9), // nothing
|
expectedPosition(10)
|
},
|
};
|
// @formatter:on
|
}
|
|
@Test(dataProvider = "indexedVlvByAssertionDataProvider")
|
public void indexedVlvByAssertionShouldReturnPageOfResultsInCorrectOrder(final String sortOrder,
|
final int beforeCount, final int afterCount, final String assertion, final List<Integer> expectedOrder,
|
final int expectedPosition) throws Exception
|
{
|
vlvByAssertion(sortOrder, beforeCount, afterCount, assertion, expectedOrder, expectedPosition);
|
}
|
|
@DataProvider
|
private Object[][] indexedVlvByOffsetDataProvider()
|
{
|
// @formatter:off
|
return new Object[][] {
|
{
|
SORT_ORDER_1,
|
beforeCount(0),
|
afterCount(3),
|
offset(1),
|
USERS_BY_SORT_ORDER_1.subList(0, 4)
|
},
|
{
|
SORT_ORDER_1,
|
beforeCount(0),
|
afterCount(3),
|
offset(3),
|
USERS_BY_SORT_ORDER_1.subList(2, 6)
|
},
|
{
|
SORT_ORDER_1,
|
beforeCount(0),
|
afterCount(0),
|
offset(5),
|
USERS_BY_SORT_ORDER_1.subList(4, 5)
|
},
|
{
|
SORT_ORDER_1,
|
beforeCount(3),
|
afterCount(3),
|
offset(5),
|
USERS_BY_SORT_ORDER_1.subList(1, 8)
|
},
|
{
|
SORT_ORDER_1,
|
beforeCount(0),
|
afterCount(3),
|
offset(0), // 0 is equivalent to 1
|
USERS_BY_SORT_ORDER_1.subList(0, 4)
|
},
|
{
|
SORT_ORDER_1,
|
beforeCount(3), // underflow
|
afterCount(3),
|
offset(1),
|
USERS_BY_SORT_ORDER_1.subList(0, 4)
|
},
|
{
|
SORT_ORDER_1,
|
beforeCount(3),
|
afterCount(3),
|
offset(30), // overflow
|
USERS_BY_SORT_ORDER_1.subList(6, 9)
|
},
|
{
|
SORT_ORDER_1,
|
beforeCount(3),
|
afterCount(3), // overflow
|
offset(7),
|
USERS_BY_SORT_ORDER_1.subList(3, 9)
|
},
|
{
|
SORT_ORDER_1,
|
beforeCount(30), // underflow
|
afterCount(30), // overflow
|
offset(7),
|
USERS_BY_SORT_ORDER_1 // everything
|
},
|
{
|
SORT_ORDER_1,
|
beforeCount(0), // underflow
|
afterCount(0), // overflow
|
offset(30),
|
USERS_BY_SORT_ORDER_1.subList(9, 9) // nothing
|
},
|
|
};
|
// @formatter:on
|
}
|
|
@Test(dataProvider = "indexedVlvByOffsetDataProvider")
|
public void indexedVlvByOffsetShouldReturnPageOfResultsInCorrectOrder(final String sortOrder, final int beforeCount,
|
final int afterCount, final int offset, final List<Integer> expectedOrder) throws Exception
|
{
|
vlvByOffset(sortOrder, beforeCount, afterCount, offset, expectedOrder);
|
}
|
|
@Test
|
public void indexedVlvByOffsetShouldReturnOffsetRangeErrorForNegativeOffsets() throws Exception
|
{
|
final List<Control> responseControls = vlvByOffset0(SORT_ORDER_2, 0, 3, -1, USERS_BY_ENTRY_ID);
|
assertThat(responseControls).isNotEmpty();
|
final VLVResponseControl vlvResponse = getVLVResponseControl(responseControls);
|
assertThat(vlvResponse.getVLVResultCode()).isEqualTo(LDAPResultCode.OFFSET_RANGE_ERROR);
|
}
|
|
@Test
|
public void serverSideSortControlShouldUseVlvIndex() throws Exception
|
{
|
final SearchRequest request =
|
newSearchRequest(BACKEND_BASE_DN, SearchScope.WHOLE_SUBTREE, VLV_FILTER)
|
.addControl(new ServerSideSortRequestControl(mangleSortOrder(SORT_ORDER_1)));
|
final InternalSearchOperation internalSearch = getRootConnection().processSearch(request);
|
|
assertThat(internalSearch.getResultCode()).isEqualTo(ResultCode.SUCCESS);
|
assertThat(getDNs(internalSearch.getSearchEntries())).isEqualTo(getDNs(USERS_BY_SORT_ORDER_1));
|
|
final List<Control> responseControls = internalSearch.getResponseControls();
|
assertThat(responseControls).hasSize(1);
|
final ServerSideSortResponseControl sortResponse = getServerSideSortResponseControl(responseControls);
|
assertThat(sortResponse.getResultCode()).isEqualTo(LDAPResultCode.SUCCESS);
|
}
|
|
@DataProvider
|
private Object[][] unindexedVlvByAssertionDataProvider()
|
{
|
// @formatter:off
|
return new Object[][] {
|
{
|
SORT_ORDER_3,
|
beforeCount(0),
|
afterCount(3),
|
assertion("a"), // before first
|
USERS_BY_SORT_ORDER_3.subList(0, 4),
|
expectedPosition(1)
|
},
|
{
|
SORT_ORDER_3,
|
beforeCount(0),
|
afterCount(3),
|
assertion("beak"), // matches first
|
USERS_BY_SORT_ORDER_3.subList(0, 4),
|
expectedPosition(1)
|
},
|
{
|
SORT_ORDER_3,
|
beforeCount(0),
|
afterCount(3),
|
assertion("debuilder"), // matches third
|
USERS_BY_SORT_ORDER_3.subList(2, 6),
|
expectedPosition(3)
|
},
|
{
|
SORT_ORDER_3,
|
beforeCount(1),
|
afterCount(3),
|
assertion("debuilder"), // matches third
|
USERS_BY_SORT_ORDER_3.subList(1, 6),
|
expectedPosition(3)
|
},
|
{
|
SORT_ORDER_3,
|
beforeCount(0),
|
afterCount(3),
|
assertion("zz"), // after all
|
USERS_BY_SORT_ORDER_3.subList(9, 9), // nothing
|
expectedPosition(10)
|
},
|
{
|
SORT_ORDER_4,
|
beforeCount(0),
|
afterCount(10),
|
assertion("A"), // before all
|
USERS_BY_SORT_ORDER_4, // everything
|
expectedPosition(1)
|
},
|
};
|
// @formatter:on
|
}
|
|
@Test(dataProvider = "unindexedVlvByAssertionDataProvider")
|
public void unindexedVlvByAssertionShouldReturnPageOfResultsInCorrectOrder(final String sortOrder,
|
final int beforeCount, final int afterCount, final String assertion, final List<Integer> expectedOrder,
|
final int expectedPosition) throws Exception
|
{
|
vlvByAssertion(sortOrder, beforeCount, afterCount, assertion, expectedOrder, expectedPosition);
|
}
|
|
@DataProvider
|
private Object[][] unindexedVlvByOffsetDataProvider()
|
{
|
// @formatter:off
|
return new Object[][] {
|
{
|
SORT_ORDER_3,
|
beforeCount(0),
|
afterCount(3),
|
offset(1),
|
USERS_BY_SORT_ORDER_3.subList(0, 4)
|
},
|
{
|
SORT_ORDER_3,
|
beforeCount(0),
|
afterCount(3),
|
offset(3),
|
USERS_BY_SORT_ORDER_3.subList(2, 6)
|
},
|
{
|
SORT_ORDER_3,
|
beforeCount(0),
|
afterCount(0),
|
offset(5),
|
USERS_BY_SORT_ORDER_3.subList(4, 5)
|
},
|
{
|
SORT_ORDER_3,
|
beforeCount(3),
|
afterCount(3),
|
offset(5),
|
USERS_BY_SORT_ORDER_3.subList(1, 8)
|
},
|
{
|
SORT_ORDER_3,
|
beforeCount(0),
|
afterCount(3),
|
offset(0), // 0 is equivalent to 1
|
USERS_BY_SORT_ORDER_3.subList(0, 4)
|
},
|
{
|
SORT_ORDER_3,
|
beforeCount(3), // underflow
|
afterCount(3),
|
offset(1),
|
USERS_BY_SORT_ORDER_3.subList(0, 4)
|
},
|
{
|
SORT_ORDER_3,
|
beforeCount(3),
|
afterCount(3),
|
offset(30), // overflow
|
USERS_BY_SORT_ORDER_3.subList(6, 9)
|
},
|
{
|
SORT_ORDER_3,
|
beforeCount(3),
|
afterCount(3), // overflow
|
offset(7),
|
USERS_BY_SORT_ORDER_3.subList(3, 9)
|
},
|
{
|
SORT_ORDER_3,
|
beforeCount(30), // underflow
|
afterCount(30), // overflow
|
offset(7),
|
USERS_BY_SORT_ORDER_3 // everything
|
},
|
{
|
SORT_ORDER_3,
|
beforeCount(0), // underflow
|
afterCount(0), // overflow
|
offset(30),
|
USERS_BY_SORT_ORDER_3.subList(9, 9) // nothing
|
},
|
|
};
|
// @formatter:on
|
}
|
|
@Test(dataProvider = "unindexedVlvByOffsetDataProvider")
|
public void unindexedVlvByOffsetShouldReturnPageOfResultsInCorrectOrder(final String sortOrder,
|
final int beforeCount, final int afterCount, final int offset, final List<Integer> expectedOrder)
|
throws Exception
|
{
|
vlvByOffset(sortOrder, beforeCount, afterCount, offset, expectedOrder);
|
}
|
|
@AfterClass
|
public void afterClass() throws Exception
|
{
|
backend.finalizeBackend();
|
backend = null;
|
}
|
|
private static int employeeNumber(final int i)
|
{
|
return i;
|
}
|
|
private static List<String> givenNames(final String... names)
|
{
|
return Arrays.asList(names);
|
}
|
|
private static String surname(final String name)
|
{
|
return name;
|
}
|
|
private static User user(final List<String> givenNames, final String sn, final int employeeNumber)
|
{
|
return new User(givenNames, sn, employeeNumber);
|
}
|
|
private int beforeCount(final int i)
|
{
|
return i;
|
}
|
|
private int afterCount(final int i)
|
{
|
return i;
|
}
|
|
private String assertion(final String s)
|
{
|
return s;
|
}
|
|
private int offset(final int i)
|
{
|
return i;
|
}
|
|
private int expectedPosition(final int i)
|
{
|
return i;
|
}
|
|
private ArrayList<DN> getDNs(final LinkedList<SearchResultEntry> entries)
|
{
|
final ArrayList<DN> results = new ArrayList<>();
|
for (final Entry e : entries)
|
{
|
results.add(e.getName());
|
}
|
return results;
|
}
|
|
private List<DN> getDNs(List<Integer> expectedOrder) throws Exception
|
{
|
List<DN> dns = new ArrayList<>(expectedOrder.size());
|
for (int i : expectedOrder)
|
{
|
dns.add(USERS[i].toDN());
|
}
|
return dns;
|
}
|
|
private ServerSideSortResponseControl getServerSideSortResponseControl(final Control c) throws DirectoryException
|
{
|
if (c instanceof LDAPControl)
|
{
|
return ServerSideSortResponseControl.DECODER.decode(c.isCritical(), ((LDAPControl) c).getValue());
|
}
|
return (ServerSideSortResponseControl) c;
|
}
|
|
private ServerSideSortResponseControl getServerSideSortResponseControl(final List<Control> responseControls)
|
throws DirectoryException
|
{
|
for (final Control c : responseControls)
|
{
|
if (c.getOID().equals(OID_SERVER_SIDE_SORT_RESPONSE_CONTROL))
|
{
|
return getServerSideSortResponseControl(c);
|
}
|
}
|
fail("Expected to find ServerSideSortResponseControl");
|
return null;
|
}
|
|
private VLVResponseControl getVLVResponseControl(final Control c) throws DirectoryException
|
{
|
if (c instanceof LDAPControl)
|
{
|
return VLVResponseControl.DECODER.decode(c.isCritical(), ((LDAPControl) c).getValue());
|
}
|
return (VLVResponseControl) c;
|
}
|
|
private VLVResponseControl getVLVResponseControl(final List<Control> responseControls) throws DirectoryException
|
{
|
for (final Control c : responseControls)
|
{
|
if (c.getOID().equals(OID_VLV_RESPONSE_CONTROL))
|
{
|
return getVLVResponseControl(c);
|
}
|
}
|
fail("Expected to find VLVResponseControl");
|
return null;
|
}
|
|
private void vlvByAssertion(final String sortOrder, final int beforeCount, final int afterCount,
|
final String assertion, final List<Integer> expectedOrder, final int expectedPosition) throws Exception
|
{
|
final SearchRequest request =
|
newSearchRequest(BACKEND_BASE_DN, SearchScope.WHOLE_SUBTREE, VLV_FILTER).addControl(
|
new ServerSideSortRequestControl(mangleSortOrder(sortOrder))).addControl(
|
new VLVRequestControl(beforeCount, afterCount, ByteString.valueOfUtf8(assertion)));
|
final InternalSearchOperation internalSearch = getRootConnection().processSearch(request);
|
assertThat(internalSearch.getResultCode()).isEqualTo(ResultCode.SUCCESS);
|
assertThat(getDNs(internalSearch.getSearchEntries())).isEqualTo(getDNs(expectedOrder));
|
|
final List<Control> responseControls = internalSearch.getResponseControls();
|
assertThat(responseControls).hasSize(2);
|
|
final ServerSideSortResponseControl sortResponse = getServerSideSortResponseControl(responseControls);
|
assertThat(sortResponse.getResultCode()).isEqualTo(LDAPResultCode.SUCCESS);
|
|
final VLVResponseControl vlvResponse = getVLVResponseControl(responseControls);
|
assertThat(vlvResponse.getVLVResultCode()).isEqualTo(LDAPResultCode.SUCCESS);
|
assertThat(vlvResponse.getTargetPosition()).isEqualTo(expectedPosition);
|
assertThat(vlvResponse.getContentCount()).isEqualTo(CONTENT_COUNT);
|
}
|
|
private String mangleSortOrder(final String sortOrder)
|
{
|
return sortOrder.replaceAll(" ", ",");
|
}
|
|
private void vlvByOffset(final String sortOrder, final int beforeCount, final int afterCount, final int offset,
|
final List<Integer> expectedOrder) throws Exception, DirectoryException
|
{
|
final List<Control> responseControls = vlvByOffset0(sortOrder, beforeCount, afterCount, offset, expectedOrder);
|
assertThat(responseControls).hasSize(2);
|
|
final ServerSideSortResponseControl sortResponse = getServerSideSortResponseControl(responseControls);
|
assertThat(sortResponse.getResultCode()).isEqualTo(LDAPResultCode.SUCCESS);
|
|
final VLVResponseControl vlvResponse = getVLVResponseControl(responseControls);
|
assertThat(vlvResponse.getVLVResultCode()).isEqualTo(LDAPResultCode.SUCCESS);
|
final int offsetInRange = Math.min(Math.max(offset, 1), CONTENT_COUNT + 1);
|
assertThat(vlvResponse.getTargetPosition()).isEqualTo(offsetInRange);
|
assertThat(vlvResponse.getContentCount()).isEqualTo(CONTENT_COUNT);
|
}
|
|
private List<Control> vlvByOffset0(final String sortOrder, final int beforeCount, final int afterCount,
|
final int offset, final List<Integer> expectedOrder) throws Exception
|
{
|
final SearchRequest request =
|
newSearchRequest(BACKEND_BASE_DN, SearchScope.WHOLE_SUBTREE, VLV_FILTER).addControl(
|
new ServerSideSortRequestControl(mangleSortOrder(sortOrder))).addControl(
|
new VLVRequestControl(beforeCount, afterCount, offset, 0));
|
final InternalSearchOperation internalSearch = getRootConnection().processSearch(request);
|
|
assertThat(internalSearch.getResultCode()).isEqualTo(ResultCode.SUCCESS);
|
assertThat(getDNs(internalSearch.getSearchEntries())).isEqualTo(getDNs(expectedOrder));
|
return internalSearch.getResponseControls();
|
}
|
|
private static class User
|
{
|
private final int employeeNumber;
|
private final List<String> givenNames;
|
private final String sn;
|
|
private User(final List<String> givenNames, final String sn, final int employeeNumber)
|
{
|
this.givenNames = givenNames;
|
this.sn = sn;
|
this.employeeNumber = employeeNumber;
|
}
|
|
private DN toDN() throws Exception
|
{
|
return DN.valueOf(String.format("employeeNumber=%d,%s", employeeNumber, BACKEND_BASE_DN));
|
}
|
|
private Entry toEntry() throws Exception
|
{
|
final List<String> ldif = new ArrayList<>();
|
ldif.add("dn: " + toDN());
|
ldif.add("objectClass: top");
|
ldif.add("objectClass: person");
|
ldif.add("objectClass: organizationalPerson");
|
ldif.add("objectClass: inetOrgPerson");
|
ldif.add(String.format("sn: %s", sn));
|
ldif.add(String.format("employeeNumber: %d", employeeNumber));
|
for (final String givenName : givenNames)
|
{
|
ldif.add(String.format("givenName: %s", givenName));
|
}
|
if (givenNames.isEmpty())
|
{
|
ldif.add(String.format("cn: %s", sn));
|
}
|
else
|
{
|
ldif.add(String.format("cn: %s %s", givenNames.get(0), sn));
|
}
|
return makeEntry(ldif.toArray(new String[0]));
|
}
|
}
|
}
|