/* * 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 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 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 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 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 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 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 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 ); } }