/*
|
* 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 2009-2010 Sun Microsystems, Inc.
|
* Portions Copyright 2010-2015 ForgeRock AS.
|
*/
|
package org.opends.server.schema;
|
|
import java.util.Calendar;
|
import java.util.GregorianCalendar;
|
import java.util.List;
|
import java.util.TimeZone;
|
|
import org.assertj.core.api.Assertions;
|
import org.forgerock.opendj.ldap.*;
|
import org.forgerock.opendj.ldap.schema.MatchingRule;
|
import org.opends.server.TestCaseUtils;
|
import org.opends.server.core.DirectoryServer;
|
import org.opends.server.protocols.internal.InternalSearchOperation;
|
import org.opends.server.protocols.internal.SearchRequest;
|
import org.opends.server.types.DN;
|
import org.opends.server.types.SearchResultEntry;
|
import org.opends.server.util.TimeThread;
|
import org.testng.annotations.BeforeClass;
|
import org.testng.annotations.DataProvider;
|
import org.testng.annotations.Test;
|
|
import static org.opends.server.protocols.internal.InternalClientConnection.*;
|
import static org.opends.server.protocols.internal.Requests.*;
|
import static org.opends.server.schema.GeneralizedTimeSyntax.*;
|
import static org.opends.server.schema.SchemaConstants.*;
|
import static org.testng.Assert.*;
|
|
/**
|
* This class tests various time-based matching rules.
|
*/
|
@SuppressWarnings("javadoc")
|
public final class TimeBasedMatchingRuleTest
|
extends SchemaTestCase
|
{
|
//User DNs to be used in tests.
|
private DN user1;
|
private DN user2;
|
private DN user3;
|
private DN user4;
|
private DN user5;
|
private DN user6;
|
|
private static final String TIME_ATTR = "test-time";
|
private static final String DATE_ATTR = "test-date";
|
|
|
/**
|
* Ensures that the Directory Server is running before executing the
|
* testcases.
|
*
|
* @throws Exception If an unexpected problem occurs.
|
*/
|
@BeforeClass
|
public void startServer()
|
throws Exception
|
{
|
TestCaseUtils.startServer();
|
|
TestCaseUtils.initializeTestBackend(true);
|
|
user1 = DN.valueOf("cn=user1,dc=example,dc=com");
|
user2 = DN.valueOf("cn=user2,dc=example,dc=com");
|
user3 = DN.valueOf("cn=user3,dc=example,dc=com");
|
user4 = DN.valueOf("cn=user4,dc=example,dc=com");
|
user5 = DN.valueOf("cn=user5,dc=example,dc=com");
|
user6 = DN.valueOf("cn=user6,dc=example,dc=com");
|
|
/**
|
Extend the schema and add an attribute which is based on
|
generalizedTimeSyntax. Since all the existing attributes based
|
on that syntax are read-only, let us create a new attribute and
|
add it.*/
|
int resultCode = TestCaseUtils.applyModifications(true,
|
"dn: cn=schema",
|
"changetype: modify",
|
"add: attributeTypes",
|
"attributeTypes: ( test-time-oid NAME 'test-time' DESC 'Test time attribute' EQUALITY " +
|
"generalizedTimeMatch ORDERING generalizedTimeOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )",
|
"attributeTypes: ( test-date-oid NAME 'test-date' DESC 'Test date attribute' EQUALITY " +
|
"generalizedTimeMatch ORDERING generalizedTimeOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )",
|
"-",
|
"add: objectclasses",
|
"objectclasses: ( testoc-oid NAME 'testOC' SUP top AUXILIARY MUST test-time)",
|
"objectclasses: ( testoc2-oid NAME 'testOC2' SUP top AUXILIARY MUST test-date)"
|
);
|
assertEquals(0, resultCode);
|
}
|
|
|
|
/**
|
* Test to search using the less-than relative time matching rule for expired events.
|
*/
|
@Test
|
public void testRTLessThanExpiredEvents() throws Exception
|
{
|
try
|
{
|
populateEntries();
|
|
String filter = TIME_ATTR + ":" + EXT_OMR_RELATIVE_TIME_LT_OID + ":=-60m";
|
SearchRequest request = newSearchRequest("dc=example,dc=com", SearchScope.WHOLE_SUBTREE, filter);
|
InternalSearchOperation searchOperation = getRootConnection().processSearch(request);
|
assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
|
List<SearchResultEntry> entries = searchOperation.getSearchEntries();
|
assertTrue(dnFoundInEntryList(entries,user1,user2));
|
}
|
finally
|
{
|
TestCaseUtils.clearJEBackend("userRoot");
|
}
|
}
|
|
|
|
/**
|
* Test to search using the less-than relative time matching rule for future events.
|
*/
|
@Test
|
public void testRTLessThanFutureEvents() throws Exception
|
{
|
try
|
{
|
populateEntries();
|
|
String filter = TIME_ATTR + ":" + EXT_OMR_RELATIVE_TIME_LT_OID + ":=1d";
|
SearchRequest request = newSearchRequest("dc=example,dc=com", SearchScope.WHOLE_SUBTREE, filter);
|
InternalSearchOperation searchOperation = getRootConnection().processSearch(request);
|
assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
|
List<SearchResultEntry> entries = searchOperation.getSearchEntries();
|
assertTrue(entries.size() == 4 && dnFoundInEntryList(entries,user1,user2,user3,user5));
|
}
|
finally
|
{
|
TestCaseUtils.clearJEBackend("userRoot");
|
}
|
}
|
|
|
|
/**
|
* Test to search using the greater-than relative time matching rule for expired events.
|
*/
|
@Test
|
public void testRTGreaterThanExpiredEvents() throws Exception
|
{
|
try
|
{
|
populateEntries();
|
|
String filter = TIME_ATTR + ":" + EXT_OMR_RELATIVE_TIME_GT_OID + ":=-1h";
|
SearchRequest request = newSearchRequest("dc=example,dc=com", SearchScope.WHOLE_SUBTREE, filter);
|
InternalSearchOperation searchOperation = getRootConnection().processSearch(request);
|
assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
|
List<SearchResultEntry> entries = searchOperation.getSearchEntries();
|
Assertions.assertThat(entries).hasSize(3);
|
assertTrue(dnFoundInEntryList(entries, user3, user4, user5));
|
}
|
finally
|
{
|
TestCaseUtils.clearJEBackend("userRoot");
|
}
|
}
|
|
|
|
/**
|
* Test to search using the greater-than relative time matching rule for future events.
|
*/
|
@Test
|
public void testRTGreaterThanFutureEvents() throws Exception
|
{
|
try
|
{
|
populateEntries();
|
|
String filter = TIME_ATTR + ":" + EXT_OMR_RELATIVE_TIME_GT_OID + ":=0s";
|
SearchRequest request = newSearchRequest("dc=example,dc=com", SearchScope.WHOLE_SUBTREE, filter);
|
InternalSearchOperation searchOperation = getRootConnection().processSearch(request);
|
assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
|
List<SearchResultEntry> entries = searchOperation.getSearchEntries();
|
assertTrue(entries.size()==2 && dnFoundInEntryList(entries,user3,user4));
|
}
|
finally
|
{
|
TestCaseUtils.clearJEBackend("userRoot");
|
}
|
}
|
|
|
|
/**
|
* Test to search using the partial date and time matching rule
|
* for an assertion value.
|
* Dates for this test are hardcoded to avoid test failures depending
|
* on when the tests are launched.
|
*/
|
@Test
|
public void testPartialDateNTimeMatchingRuleUsingSearch() throws Exception
|
{
|
try
|
{
|
populateEntries();
|
|
String assertion = "01D11M";
|
String filter = DATE_ATTR + ":" + EXT_PARTIAL_DATE_TIME_OID + ":=" + assertion;
|
SearchRequest request = newSearchRequest("dc=example,dc=com", SearchScope.WHOLE_SUBTREE, filter);
|
InternalSearchOperation searchOperation = getRootConnection().processSearch(request);
|
assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
|
List<SearchResultEntry> entries = searchOperation.getSearchEntries();
|
assertTrue(entries.size()==1 && dnFoundInEntryList(entries,user6));
|
}
|
finally
|
{
|
TestCaseUtils.clearJEBackend("userRoot");
|
}
|
}
|
|
|
/**
|
* Test to match the attribute and the assertion values using a partial date and time
|
* matching rule.
|
*/
|
@Test(dataProvider="partialDateTimeValues")
|
public void testPartialDateNTimeMatch(long attributeValue,String assertionValue) throws Exception
|
{
|
MatchingRule partialTimeRule = DirectoryServer.getMatchingRule(
|
EXT_PARTIAL_DATE_TIME_NAME.toLowerCase());
|
Assertion assertion = partialTimeRule.getAssertion(ByteString.valueOf(assertionValue));
|
assertEquals(assertion.matches(ByteString.valueOf(attributeValue)), ConditionResult.TRUE);
|
}
|
|
|
|
/**
|
* Tests the assertion syntax of the relative time matching rules.
|
*/
|
@Test(dataProvider= "relativeTimeValues")
|
public void testRelativeTimeMatchingRuleAssertionSyntax(String assertion,boolean isValid)
|
{
|
MatchingRule relativeTimeLTRule =
|
DirectoryServer.getMatchingRule(EXT_OMR_RELATIVE_TIME_LT_ALT_NAME.toLowerCase());
|
try
|
{
|
relativeTimeLTRule.getAssertion(ByteString.valueOf(assertion));
|
// An invalid value can't get away without throwing exception.
|
assertTrue(isValid);
|
}
|
catch (DecodeException e)
|
{
|
//invalid values will throw an exception.
|
assertFalse(isValid);
|
}
|
}
|
|
|
|
/**
|
* Tests the assertion syntax of the partial date and time matching rules.
|
*/
|
@Test(dataProvider= "partialDateTimeSyntaxes")
|
public void testPartialDateTimeMatchingRuleAssertionSyntax(String assertion,boolean isValid)
|
{
|
MatchingRule partialDTRule =
|
DirectoryServer.getMatchingRule(EXT_PARTIAL_DATE_TIME_OID);
|
try
|
{
|
partialDTRule.getAssertion(ByteString.valueOf(assertion));
|
assertTrue(isValid);
|
}
|
catch (DecodeException e)
|
{
|
//invalid values will throw an exception.
|
assertFalse(isValid);
|
}
|
}
|
|
|
|
/**
|
* Generates data for testing relative time matching rule assertion syntax.
|
*/
|
@DataProvider(name="relativeTimeValues")
|
private Object[][] createRelativeTimeValues()
|
{
|
return new Object[][] {
|
{"1s",true},
|
{"1s0d",false},
|
{"-1d",true},
|
{"2h",true},
|
{"+2w",true},
|
{"0",true},
|
{"0s",true},
|
{"0d",true},
|
{"xyz",false},
|
{"12w-2d",false},
|
{"1s2s",false},
|
{"1d4s5d",false}
|
|
};
|
}
|
|
|
/**
|
* Generates the data for testing partial time date and time values.
|
*/
|
@DataProvider(name="partialDateTimeValues")
|
private Object[][] createPartialDateTimeValues()
|
{
|
GregorianCalendar c = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
c.setLenient(false);
|
c.clear();
|
c.set(Calendar.HOUR_OF_DAY,23);
|
c.set(Calendar.MINUTE,0);
|
c.set(Calendar.SECOND,0);
|
long time1 = c.getTimeInMillis();
|
c.set(Calendar.HOUR_OF_DAY,00);
|
c.set(Calendar.MINUTE,59);
|
c.set(Calendar.SECOND,59);
|
long time2 = c.getTimeInMillis();
|
|
return new Object[][] {
|
{time1,"0s"},
|
{time1,"0m"},
|
{time1,"23h"},
|
{time2,"59m59s"},
|
{time2,"0h59m59s"}
|
};
|
}
|
|
|
|
/**
|
* Generates data for testing partial date and time assertion syntax.
|
*/
|
@DataProvider(name="partialDateTimeSyntaxes")
|
private Object[][] createPartialDateTimeSyntaxes()
|
{
|
//Get the current time.
|
GregorianCalendar cal =
|
new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
cal.setLenient(false);
|
|
//Get the date today.
|
int second = cal.get(Calendar.SECOND);
|
int minute = cal.get(Calendar.MINUTE);
|
int hour = cal.get(Calendar.HOUR);
|
int date = cal.get(Calendar.DATE);
|
int month = cal.get(Calendar.MONTH) + 1;
|
int year = cal.get(Calendar.YEAR);
|
|
return new Object[][] {
|
{"20MM30DD1978YY",false},
|
{"02MM29DD2009YY",false},
|
{"02MM31DD2010YY",false},
|
{"-1s",false},
|
{"02M29D2008Y",true},
|
{"DDYY",false},
|
{"02D",true},
|
{"12M",true},
|
{"1978Y",true},
|
{"0MM",false},
|
{"20MM03DD10MM",false},
|
{"00s12m13h",true},
|
{"00s12m14h1M3D1978Y",true},
|
{"1s",true},
|
{"12m",true},
|
{"23h",true},
|
{"61s",false},
|
{"60m",false},
|
{"24h",false},
|
{second+"s",true},
|
{minute+"m",true},
|
{hour+"h",true},
|
{date+"D",true},
|
{month+"M",true},
|
{year+"Y",true},
|
{month+"M"+date+"D",true},
|
{year+"Y"+date+"D",true},
|
{month+"M"+year+"Y"+date+"D",true}
|
};
|
}
|
|
// validate if the args are found in the entries list.
|
private boolean dnFoundInEntryList(List<SearchResultEntry> entries, DN... dns)
|
{
|
for (DN dn : dns)
|
{
|
assertTrue(find(entries, dn), "Could not find dn " + dn + " in list " + entries);
|
}
|
return true;
|
}
|
|
private boolean find(List<SearchResultEntry> entries, DN dn)
|
{
|
for (SearchResultEntry entry : entries)
|
{
|
if (entry.getName().equals(dn))
|
{
|
return true;
|
}
|
}
|
return false;
|
}
|
|
//Creates the entries.
|
private void populateEntries() throws Exception
|
{
|
//Get the current time from the TimeThread. Using the current time from new
|
// calendar may fail if the time thread using a stale time.
|
long currentTime = TimeThread.getTime();
|
|
TestCaseUtils.clearJEBackend("userRoot", "dc=example,dc=com");
|
TestCaseUtils.addEntries(
|
"dn: cn=user1,dc=example,dc=com",
|
"objectclass: person",
|
"objectclass: testoc",
|
"cn: user1",
|
"sn: user1",
|
TIME_ATTR + ": "+ format(currentTime-4000*1000), //more than 1 hour old.
|
"",
|
"dn: cn=user2,dc=example,dc=com",
|
"objectclass: person",
|
"objectclass: testoc",
|
"cn: user2",
|
"sn: user2",
|
TIME_ATTR + ": " + format(currentTime-25*3600*1000), //more than a day old.
|
"",
|
"dn: cn=user3,dc=example,dc=com",
|
"objectclass: person",
|
"objectclass: testoc",
|
"cn: user3",
|
"sn: user3",
|
TIME_ATTR + ": " + format(currentTime+4000*1000), //more than 1 hour in advance.
|
"",
|
"dn: cn=user4,dc=example,dc=com",
|
"objectclass: person",
|
"objectclass: testoc",
|
"cn: user4",
|
"sn: user4",
|
TIME_ATTR + ": " + format(currentTime+25*3600*1000), // more than 1 day in advance
|
"",
|
"dn: cn=user5,dc=example,dc=com",
|
"objectclass: person",
|
"objectclass: testoc",
|
"cn: user5",
|
"sn: user5",
|
TIME_ATTR + ": " + format(currentTime), // now.
|
"",
|
"dn: cn=user6,dc=example,dc=com",
|
"objectclass: person",
|
"objectclass: testoc2",
|
"cn: user6",
|
"sn: user6",
|
DATE_ATTR + ": 19651101000000Z", // Nov 1st 1965
|
"",
|
"dn: cn=user7,dc=example,dc=com",
|
"objectclass: person",
|
"objectclass: testoc2",
|
"cn: user7",
|
"sn: user7",
|
DATE_ATTR + ": 20101104000000Z", // Nov 4th 2010
|
"",
|
"dn: cn=user8,dc=example,dc=com",
|
"objectclass: person",
|
"objectclass: testoc2",
|
"cn: user8",
|
"sn: user8",
|
DATE_ATTR + ": 20000101000000Z" // Jan 1st 2000
|
);
|
}
|
}
|