All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.identityconnectors.contract.test.SearchApiOpTests Maven / Gradle / Ivy

The newest version!
/*
 * ====================
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common Development
 * and Distribution License("CDDL") (the "License").  You may not use this file
 * except in compliance with the License.
 *
 * You can obtain a copy of the License at
 * http://opensource.org/licenses/cddl1.php
 * See the License for the specific language governing permissions and limitations
 * under the License.
 *
 * When distributing the Covered Code, include this CDDL Header Notice in each file
 * and include the License file at http://opensource.org/licenses/cddl1.php.
 * If applicable, add the following below this CDDL Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 * ====================
 * Portions Copyrighted 2010-2013 ForgeRock AS.
 * Portions Copyrighted 2018 ConnId
 */
package org.identityconnectors.contract.test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.identityconnectors.common.logging.Log;

import org.identityconnectors.contract.exceptions.ObjectNotFoundException;
import org.identityconnectors.framework.api.operations.APIOperation;
import org.identityconnectors.framework.api.operations.CreateApiOp;
import org.identityconnectors.framework.api.operations.DeleteApiOp;
import org.identityconnectors.framework.api.operations.GetApiOp;
import org.identityconnectors.framework.api.operations.SearchApiOp;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.AttributeBuilder;
import org.identityconnectors.framework.common.objects.AttributeUtil;
import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.Name;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.OperationOptionsBuilder;
import org.identityconnectors.framework.common.objects.Uid;
import org.identityconnectors.framework.common.objects.filter.Filter;
import org.identityconnectors.framework.common.objects.filter.FilterBuilder;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

/**
 * Contract test of {@link SearchApiOp}.
 */
public class SearchApiOpTests extends ObjectClassRunner {

    private static final Log LOG = Log.getLog(SearchApiOpTests.class);

    public static final String TEST_NAME = "Search";

    /**
     * Properties' prefixes to enable case insensitive search tests
     * (Connectors by default are not capable of this.)
     */
    private static final String CASE_INSENSITIVE_PREFIX = "caseinsensitive";

    private static final String DISABLE = "disable";

    private static final String COMPARE_BY_UID_ONLY = "compareExistingObjectsByUidOnly";

    /**
     * {@inheritDoc}
     */
    @Override
    public Set> getAPIOperations() {
        Set> requiredOps = new HashSet<>();
        // list of required operations by this test:
        requiredOps.add(CreateApiOp.class);
        requiredOps.add(SearchApiOp.class);
        requiredOps.add(GetApiOp.class);
        return requiredOps;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void testRun(ObjectClass objectClass) {
        Uid uid = null;
        List uids = new ArrayList<>();
        List> attrs = new ArrayList<>();
        ConnectorObject coFound = null;
        final int recordCount = 10;

        try {
            // obtain objects stored in connector resource, before test inserts
            // own test data
            // should throw if object class is not supported and test ends
            Map coBeforeTest = ConnectorHelper.search2Map(getConnectorFacade(),
                    objectClass, null, getOperationOptionsByOp(objectClass, SearchApiOp.class));

            //prepare the data
            for (int i = 0; i < recordCount; i++) {
                //create objects
                Set attr = ConnectorHelper.getCreateableAttributes(getDataProvider(),
                        getObjectClassInfo(objectClass), getTestName(), i, true, false);

                Uid luid = getConnectorFacade().create(objectClass, attr, getOperationOptionsByOp(objectClass,
                        CreateApiOp.class));
                assertNotNull(luid, "Create returned null uid.");
                attrs.add(attr);
                uids.add(luid);
            }

            // retrieve all objects including newly created
            List coAll = ConnectorHelper.search(getConnectorFacade(),
                    objectClass, null, getOperationOptionsByOp(objectClass, SearchApiOp.class));

            //search by id
            uid = uids.get(0);
            Filter fltUid = FilterBuilder.equalTo(uid);
            List coObjects = ConnectorHelper.search(getConnectorFacade(),
                    objectClass, fltUid, getOperationOptionsByOp(objectClass, SearchApiOp.class));
            assertTrue(coObjects.size() == 1,
                    "Search filter by uid failed, expected to return one object, but returned "
                    + coObjects.size());
            coFound = coObjects.get(0);

            final Set searchBy = attrs.get(0);
            ConnectorHelper.checkObject(getObjectClassInfo(objectClass), coFound, searchBy);

            //get name
            Attribute attName = coFound.getAttributeByName(Name.NAME);
            assertTrue(attName.getValue().size() == 1, "Special attribute NAME is expected to have exactly one value.");
            String attNameValue = attName.getValue().get(0).toString();

            //search by name
            coFound = ConnectorHelper.findObjectByName(getConnectorFacade(), objectClass,
                    attNameValue, getOperationOptionsByOp(objectClass, SearchApiOp.class));

            ConnectorHelper.checkObject(getObjectClassInfo(objectClass), coFound, searchBy);

            //search by all non special readable attributes
            Filter fltAllAtts = null;
            // attributes which are used in filter must be the same for all filtered objects
            Set filteredAttrs = new HashSet<>();

            for (Attribute attribute : searchBy) {
                if (!AttributeUtil.isSpecial(attribute) && ConnectorHelper.isReadable(getObjectClassInfo(objectClass),
                        attribute)) {
                    if (fltAllAtts == null) {
                        fltAllAtts = FilterBuilder.equalTo(attribute);
                    } else {
                        fltAllAtts = FilterBuilder.and(fltAllAtts, FilterBuilder.equalTo(attribute));
                    }
                    filteredAttrs.add(attribute);
                }
            }
            // skip test when there are no non-special readable attributes
            // (results in null filter - tested explicitly)
            if (fltAllAtts != null) {
                // find how many object should pass filter
                int count = 0;
                for (ConnectorObject co : coAll) {
                    if (fltAllAtts.accept(co)) {
                        count++;
                    }
                }
                coObjects = ConnectorHelper.search(getConnectorFacade(),
                        objectClass, fltAllAtts,
                        getOperationOptionsByOp(objectClass, SearchApiOp.class));

                assertEquals(count, coObjects.size(), "Search by all non-special attributes returned "
                        + coObjects.size() + " objects, but expected was "
                        + count + " .");

                for (ConnectorObject coChecked : coObjects) {
                    ConnectorHelper.checkObject(getObjectClassInfo(objectClass), coChecked,
                            filteredAttrs);
                }
            }

            //check null filter
            coObjects = ConnectorHelper.search(getConnectorFacade(), objectClass, null, getOperationOptionsByOp(
                    objectClass, SearchApiOp.class));
            assertTrue(coObjects.size() == uids.size() + coBeforeTest.size(),
                    "Null-filter search failed, wrong number of objects returned, expected: "
                    + (uids.size() + coBeforeTest.size()) + " but found: "
                    + coObjects.size());

            List tempUids = new ArrayList<>(uids);

            for (ConnectorObject cObject : coObjects) {
                // check if the uid is in list of objects created by test
                int idx = uids.indexOf(cObject.getUid());
                if (idx > -1) {
                    // is in list
                    // remove it from temp list
                    assertTrue(tempUids.remove(cObject.getUid()));
                    // compare the attributes
                    ConnectorHelper.checkObject(getObjectClassInfo(objectClass), cObject, attrs.get(idx));
                } else {
                    if (compareExistingObjectsByUidOnly()) {
                        assertTrue(coBeforeTest.containsKey(cObject.getUid()),
                                "Object returned by null-filter search is neither in list of objects created by test nor in list of objects that were in connector resource before test. Objects were compared by Uid only.");
                    } else {
                        assertTrue(coBeforeTest.containsValue(cObject),
                                "Object returned by null-filter search is neither in list of objects created by test nor in list of objects that were in connector resource before test. Objects were compared by all attributes.");
                    }
                }
            }
            assertTrue(tempUids.isEmpty(), "Null-filter search didn't return all created objects by search test.");

        } finally {
            // remove objects created by test
            uids.forEach((deluid) -> {
                try {
                    ConnectorHelper.deleteObject(getConnectorFacade(), objectClass,
                            deluid, false, getOperationOptionsByOp(objectClass, DeleteApiOp.class));
                } catch (Exception e) {
                    // ok
                    // note: this is thrown, when we delete the same object twice.
                }
            });
        }

    }

    /**
     * Test Search without specified OperationOptions attrsToGet which is the default for all other tests.
     * All the other tests contain explicit attrsToGet.
     */
    @ParameterizedTest
    @MethodSource("objectClasses")
    public void testSearchWithoutAttrsToGet(ObjectClass objectClass) {
        // run the contract test only if search is supported by tested object class
        if (ConnectorHelper.operationsSupported(getConnectorFacade(), objectClass,
                getAPIOperations())) {
            Uid uid = null;

            try {
                Set attrs = ConnectorHelper.getCreateableAttributes(getDataProvider(),
                        getObjectClassInfo(objectClass), getTestName(), 0, true, false);

                uid = getConnectorFacade().create(objectClass, attrs, null);
                assertNotNull(uid, "Create returned null uid.");

                // get the user to make sure it exists now
                Filter fltUid = FilterBuilder.equalTo(uid);
                List coObjects = ConnectorHelper.search(getConnectorFacade(),
                        objectClass, fltUid, null);
                assertTrue(coObjects.size() == 1,
                        "Search filter by uid with no OperationOptions failed, expected to return one object, but returned "
                        + coObjects.size());

                assertNotNull(coObjects.get(0), "Unable to retrieve newly created object");

                // compare requested attributes to retrieved attributes, but
                // don't compare attrs which
                // are not returned by default
                ConnectorHelper.checkObject(getObjectClassInfo(objectClass), coObjects.get(0), attrs, false);
            } finally {
                if (uid != null) {
                    // delete the object
                    getConnectorFacade().delete(objectClass, uid, null);
                }
            }
        } else {
            LOG.info("----------------------------------------------------------------------------------------");
            LOG.info("Skipping test ''testSearchWithoutAttrsToGet'' for object class ''" + objectClass + "''.");
            LOG.info("----------------------------------------------------------------------------------------");
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getTestName() {
        return TEST_NAME;
    }

    /* ***************** CASE INSENSITIVE SEARCH ********************* */
    /**
     * Test case insensitive search for UID attribute.
     *
     * There is twice Search performed, once with changed case. The results should be identical.
     */
    @ParameterizedTest
    @MethodSource("objectClasses")
    public void testCaseInsensitiveSearch(ObjectClass objectClass) {
        // run the contract test only if search is supported by tested object
        // class
        if (ConnectorHelper.operationsSupported(getConnectorFacade(), objectClass, getAPIOperations())
                && canSearchCaseInsensitive()) {
            Uid uid = null;

            try {
                // create a new dummy object
                Set attrs = ConnectorHelper.getCreateableAttributes(
                        getDataProvider(), getObjectClassInfo(objectClass), getTestName(),
                        0, true, false);

                uid = getConnectorFacade().create(objectClass,
                        attrs, null);
                assertNotNull(uid, "Create returned null uid.");

                // 1st search, contains the original object
                ConnectorObject searchResult = searchForUid(objectClass, uid.getUidValue(), "[query by original uid]");

                // get created uid and change its case, than perform 2nd search for uid with changed case
                String uidStr = uid.getUidValue();

                // change the case in UID
                String caseChngd_uidStr = changeCase(uidStr); // inverts the case of the original uid (example: BvZAO96 --> bVzao96)

                // change the case in NAME
                String name = getName(objectClass, uid);
                String caseChngd_NAME = changeCase(name);

                //perform search with changed case UID
                ConnectorObject searchWithChngdCaseResult = searchForUid(objectClass,
                        caseChngd_uidStr, "[query by changed case uid]");

                assertTrue(searchWithChngdCaseResult.equals(searchResult),
                        "The search responses differ for changed case query [UID] and simple query.");

                //perform search with changed case NAME
                searchWithChngdCaseResult = searchForName(objectClass,
                        caseChngd_NAME, "[query by changed case name]");

                assertTrue(searchWithChngdCaseResult.equals(searchResult),
                        "The search responses differ for changed case query [NAME] and simple query.");
            } finally {
                if (uid != null) {
                    // delete the dummy object
                    getConnectorFacade().delete(objectClass, uid,
                            null);
                }
            }
        } else {
            LOG.info("----------------------------------------------------------------------------------------");
            LOG.info("Skipping test ''testCaseInsensitiveSearch'' for object class ''" + objectClass + "''.");
            LOG.info("----------------------------------------------------------------------------------------");
        }
    }

    private ConnectorObject searchForName(ObjectClass objectClass, String caseChngd_NAME, String msg) {
        // get the user to make sure it exists now
        Filter fltUid = FilterBuilder.equalTo(AttributeBuilder.build(Name.NAME, caseChngd_NAME));
        List coObjects = ConnectorHelper.search(
                getConnectorFacade(), objectClass, fltUid, null);
        assertTrue(coObjects.size() == 1,
                msg
                + " Search filter by uid with no OperationOptions failed, expected to return one object, but returned "
                + coObjects.size());

        assertNotNull(coObjects.get(0), "Unable to retrieve newly created object");
        return coObjects.get(0);
    }

    /** get the name for uid */
    private String getName(ObjectClass objectClass, Uid uid) {
        OperationOptionsBuilder oob = new OperationOptionsBuilder();
        oob.setAttributesToGet(Name.NAME);
        ConnectorObject o = getConnectorFacade().getObject(objectClass, uid, oob.build()); // get the name
        Attribute attrName = o.getAttributeByName(Name.NAME);
        return attrName.getValue().get(0).toString();
    }

    /** search for given Uid and return first item in the response list */
    private ConnectorObject searchForUid(ObjectClass objectClass, String uidValue, String msg) {
        // get the user to make sure it exists now
        Filter fltUid = FilterBuilder.equalTo(AttributeBuilder.build(Uid.NAME, uidValue));
        List coObjects = ConnectorHelper.search(
                getConnectorFacade(), objectClass, fltUid, null);
        assertTrue(coObjects.size() == 1,
                msg
                + " Search filter by uid with no OperationOptions failed, expected to return one object, but returned "
                + coObjects.size());

        assertNotNull(coObjects.get(0), "Unable to retrieve newly created object");
        return coObjects.get(0);
    }

    /** replace upper and lowercase letters */
    static String changeCase(String str_uid) {
        char[] result = new char[str_uid.length()];
        for (int i = 0; i < str_uid.length(); i++) {
            if (Character.isLowerCase(str_uid.charAt(i))) {
                result[i] = Character.toUpperCase(str_uid.charAt(i));
            } else {
                result[i] = Character.toLowerCase(str_uid.charAt(i));
            }
        }

        return new String(result);
    }

    /**
     * 

* Returns true if tests are configured to enable case insensitive tests * {@link SearchApiOpTests#testCaseInsensitiveSearch(ObjectClass)}. *

* *

* Returns true if tests are configured to test connector's sync after * specified operation. Some connectors implement sync but are not capable * to sync all changes' types. *

*/ protected static boolean canSearchCaseInsensitive() { // by default it's supposed that case insensitive search is disabled. Boolean canSearchCIns = true; try { canSearchCIns = !(Boolean) getDataProvider().getTestSuiteAttribute( DISABLE + "." + CASE_INSENSITIVE_PREFIX, TEST_NAME); } catch (ObjectNotFoundException ex) { // exceptions is throw in case property definition is not found // ok -- indicates enabling the property } return canSearchCIns; } /** * Returns true if tests should compare already existing objects by uid only. */ protected static boolean compareExistingObjectsByUidOnly() { // by default it's supposed that all attributes all compared boolean compareByUidOnly = false; try { compareByUidOnly = (Boolean) getDataProvider().getTestSuiteAttribute(COMPARE_BY_UID_ONLY, TEST_NAME); } catch (ObjectNotFoundException ex) { // exceptions is throw in case property definition is not found // ok -- indicates enabling the property } return compareByUidOnly; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy