/*
* ====================
* 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 2014 ForgeRock AS.
*/
package org.identityconnectors.framework.impl.api;
import static org.identityconnectors.framework.common.objects.ObjectClass.ACCOUNT;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.identityconnectors.common.CollectionUtil;
import org.identityconnectors.common.security.GuardedString;
import org.identityconnectors.framework.api.APIConfiguration;
import org.identityconnectors.framework.api.ConnectorFacade;
import org.identityconnectors.framework.api.ConnectorFacadeFactory;
import org.identityconnectors.framework.api.operations.APIOperation;
import org.identityconnectors.framework.api.operations.GetApiOp;
import org.identityconnectors.framework.api.operations.SearchApiOp;
import org.identityconnectors.framework.api.operations.UpdateApiOp;
import org.identityconnectors.framework.common.exceptions.InvalidAttributeValueException;
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.ResultsHandler;
import org.identityconnectors.framework.common.objects.ScriptContextBuilder;
import org.identityconnectors.framework.common.objects.SyncDelta;
import org.identityconnectors.framework.common.objects.SyncResultsHandler;
import org.identityconnectors.framework.common.objects.SyncToken;
import org.identityconnectors.framework.common.objects.Uid;
import org.identityconnectors.framework.spi.Configuration;
import org.identityconnectors.framework.spi.Connector;
import org.identityconnectors.mockconnector.MockAllOpsConnector;
import org.identityconnectors.mockconnector.MockConfiguration;
import org.identityconnectors.mockconnector.MockConnector;
import org.identityconnectors.mockconnector.MockConnector.Call;
import org.identityconnectors.mockconnector.MockUpdateConnector;
import org.identityconnectors.test.common.TestHelpers;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class ConnectorFacadeTests {
// =======================================================================
// Setup/Tear down
// =======================================================================
@BeforeMethod
public void setup() {
// always reset the call patterns..
MockConnector.reset();
}
// =======================================================================
// Helper Methods
// =======================================================================
private interface TestOperationPattern {
/**
* Simple call back to make the 'facade' calls.
*/
public void makeCall(ConnectorFacade facade);
/**
* Given the list of calls determine if they match expected values based
* on the calls made in the {@link #makeCall(ConnectorFacade)} method.
*/
public void checkCalls(List<MockConnector.Call> calls);
}
/**
* Test the pattern of the common operations.
*
* @throws ClassNotFoundException
*/
@Test(enabled = false)
private void testCallPattern(TestOperationPattern pattern) {
testCallPattern(pattern, MockAllOpsConnector.class);
}
@Test(enabled = false)
private void testCallPattern(TestOperationPattern pattern, Class<? extends Connector> clazz) {
Configuration config = new MockConfiguration(false);
ConnectorFacadeFactory factory = ConnectorFacadeFactory.getInstance();
// **test only**
APIConfiguration impl = TestHelpers.createTestConfiguration(clazz, config);
ConnectorFacade facade;
facade = factory.newInstance(impl);
// make the call on the connector facade..
pattern.makeCall(facade);
// check the call structure..
List<MockConnector.Call> calls = MockConnector.getCallPattern();
// check the call pattern..
assertEquals(calls.remove(0).getMethodName(), "init");
pattern.checkCalls(calls);
assertEquals(calls.remove(0).getMethodName(), "dispose");
assertTrue(calls.isEmpty());
}
// =======================================================================
// Tests
// =======================================================================
/**
* Tests that if an SPI operation is not implemented that the API will throw
* an {@link UnsupportedOperationException}.
*/
@Test(expectedExceptions = UnsupportedOperationException.class)
public void unsupportedOperationTest() {
Configuration config = new MockConfiguration(false);
Class<? extends Connector> clazz = MockConnector.class;
ConnectorFacadeFactory factory = ConnectorFacadeFactory.getInstance();
APIConfiguration impl = TestHelpers.createTestConfiguration(clazz, config);
ConnectorFacade facade;
facade = factory.newInstance(impl);
facade.authenticate(ObjectClass.ACCOUNT, "fadf", new GuardedString("fadsf".toCharArray()),
null);
}
@Test
public void runScriptOnConnectorCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
facade.runScriptOnConnector(new ScriptContextBuilder("lang", "script").build(),
null);
}
public void checkCalls(List<Call> calls) {
assertEquals(calls.remove(0).getMethodName(), "runScriptOnConnector");
}
});
}
@Test
public void runScriptOnResourceCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
facade.runScriptOnResource(new ScriptContextBuilder("lang", "script").build(), null);
}
public void checkCalls(List<Call> calls) {
assertEquals(calls.remove(0).getMethodName(), "runScriptOnResource");
}
});
}
/**
* Test the call pattern to get the schema.
*/
@Test
public void schemaCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
facade.schema();
}
public void checkCalls(List<Call> calls) {
assertEquals(calls.remove(0).getMethodName(), "schema");
}
});
}
@Test
public void authenticateCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
facade.authenticate(ObjectClass.ACCOUNT, "dfadf", new GuardedString("fadfkj"
.toCharArray()), null);
}
public void checkCalls(List<Call> calls) {
assertEquals(calls.remove(0).getMethodName(), "authenticate");
}
});
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void authenticateAllCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
facade.authenticate(ObjectClass.ALL, "dfadf", new GuardedString("fadfkj"
.toCharArray()), null);
}
public void checkCalls(List<Call> calls) {
fail("Should not get here..");
}
});
}
@Test
public void resolveUsernameCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
facade.resolveUsername(ObjectClass.ACCOUNT, "dfadf", null);
}
public void checkCalls(List<Call> calls) {
assertEquals(calls.remove(0).getMethodName(), "resolveUsername");
}
});
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void resolveUsernameAllCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
facade.resolveUsername(ObjectClass.ALL, "dfadf", null);
}
public void checkCalls(List<Call> calls) {
fail("Should not get here..");
}
});
}
@Test
public void createCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
Set<Attribute> attrs = CollectionUtil.<Attribute> newReadOnlySet();
facade.create(ObjectClass.ACCOUNT, attrs, null);
}
public void checkCalls(List<Call> calls) {
assertEquals(calls.remove(0).getMethodName(), "create");
}
});
}
@Test(expectedExceptions = NullPointerException.class)
public void createWithOutObjectClassPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
Set<Attribute> attrs = new HashSet<Attribute>();
facade.create(null, attrs, null);
}
public void checkCalls(List<Call> calls) {
fail("Should not get here..");
}
});
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void createAllCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
Set<Attribute> attrs = CollectionUtil.<Attribute> newReadOnlySet();
facade.create(ObjectClass.ALL, attrs, null);
}
public void checkCalls(List<Call> calls) {
fail("Should not get here..");
}
});
}
@Test(expectedExceptions = InvalidAttributeValueException.class)
public void createDuplicatAttributesPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
Set<Attribute> attrs = new HashSet<Attribute>();
attrs.add(AttributeBuilder.build("abc", 1));
attrs.add(AttributeBuilder.build("abc", 2));
facade.create(ObjectClass.ACCOUNT, attrs, null);
}
public void checkCalls(List<Call> calls) {
fail("Should not get here..");
}
});
}
@Test
public void updateCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
Set<Attribute> attrs = new HashSet<Attribute>();
attrs.add(AttributeBuilder.build("accountid"));
facade.update(ObjectClass.ACCOUNT, newUid(0), attrs, null);
}
public void checkCalls(List<Call> calls) {
assertEquals(calls.remove(0).getMethodName(), "update");
}
});
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void updateAllCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
Set<Attribute> attrs = new HashSet<Attribute>();
attrs.add(AttributeBuilder.build("accountid"));
facade.update(ObjectClass.ALL, newUid(0), attrs, null);
}
public void checkCalls(List<Call> calls) {
fail("Should not get here..");
}
});
}
@Test
public void deleteCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
facade.delete(ObjectClass.ACCOUNT, newUid(0), null);
}
public void checkCalls(List<Call> calls) {
assertEquals(calls.remove(0).getMethodName(), "delete");
}
});
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void deleteAllCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
facade.delete(ObjectClass.ALL, newUid(0), null);
}
public void checkCalls(List<Call> calls) {
fail("Should not get here..");
}
});
}
@Test
public void searchCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
// create an empty results handler..
ResultsHandler rh = new ResultsHandler() {
public boolean handle(ConnectorObject obj) {
return true;
}
};
// call the search method..
facade.search(ObjectClass.ACCOUNT, null, rh, null);
}
public void checkCalls(List<Call> calls) {
assertEquals(calls.remove(0).getMethodName(), "createFilterTranslator");
assertEquals(calls.remove(0).getMethodName(), "executeQuery");
}
});
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void searchAllCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
// create an empty results handler..
ResultsHandler rh = new ResultsHandler() {
public boolean handle(ConnectorObject obj) {
return true;
}
};
// call the search method..
facade.search(ObjectClass.ALL, null, rh, null);
}
public void checkCalls(List<Call> calls) {
fail("Should not get here..");
}
});
}
@Test
public void getCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
// create an empty results handler..
// call the search method..
facade.getObject(ObjectClass.ACCOUNT, newUid(0), null);
}
public void checkCalls(List<Call> calls) {
assertEquals(calls.remove(0).getMethodName(), "createFilterTranslator");
assertEquals(calls.remove(0).getMethodName(), "executeQuery");
}
});
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void getAllCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
facade.getObject(ObjectClass.ALL, newUid(0), null);
}
public void checkCalls(List<Call> calls) {
fail("Should not get here..");
}
});
}
@Test
public void getLatestSyncTokenCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
facade.getLatestSyncToken(ObjectClass.ACCOUNT);
}
public void checkCalls(List<Call> calls) {
assertEquals(calls.remove(0).getMethodName(), "getLatestSyncToken");
}
});
}
@Test
public void getLatestSyncTokenAllCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
facade.getLatestSyncToken(ObjectClass.ALL);
}
public void checkCalls(List<Call> calls) {
assertEquals(calls.remove(0).getMethodName(), "getLatestSyncToken");
}
});
}
@Test
public void syncCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
// create an empty results handler..
// call the search method..
facade.sync(ObjectClass.ACCOUNT, new SyncToken(1), new SyncResultsHandler() {
public boolean handle(SyncDelta delta) {
return true;
}
}, null);
}
public void checkCalls(List<Call> calls) {
assertEquals(calls.remove(0).getMethodName(), "sync");
}
});
}
@Test
public void syncAllCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
// create an empty results handler..
// call the search method..
facade.sync(ObjectClass.ALL, new SyncToken(1), new SyncResultsHandler() {
public boolean handle(SyncDelta delta) {
return true;
}
}, null);
}
public void checkCalls(List<Call> calls) {
assertEquals(calls.remove(0).getMethodName(), "sync");
}
});
}
@Test
public void testOpCallPattern() {
testCallPattern(new TestOperationPattern() {
public void makeCall(ConnectorFacade facade) {
facade.test();
}
public void checkCalls(List<Call> calls) {
assertEquals(calls.remove(0).getMethodName(), "test");
}
});
}
@Test
public void updateMergeTests() {
Attribute expected, actual;
Configuration config = new MockConfiguration(false);
ConnectorFacadeFactory factory = ConnectorFacadeFactory.getInstance();
Class<? extends Connector> clazz = MockUpdateConnector.class;
// **test only**
APIConfiguration impl = TestHelpers.createTestConfiguration(clazz, config);
impl.setTimeout(GetApiOp.class, APIOperation.NO_TIMEOUT);
impl.setTimeout(UpdateApiOp.class, APIOperation.NO_TIMEOUT);
impl.setTimeout(SearchApiOp.class, APIOperation.NO_TIMEOUT);
ConnectorFacade facade = factory.newInstance(impl);
// sniff test to make sure we can get an object..
ConnectorObject obj = facade.getObject(ACCOUNT, newUid(1), null);
assertEquals(obj.getUid(), newUid(1));
// ok lets add an attribute that doesn't exist..
final String ADDED = "somthing to add to the object";
final String ATTR_NAME = "added";
Set<Attribute> addAttrSet;
addAttrSet = CollectionUtil.newSet(obj.getAttributes());
addAttrSet.add(AttributeBuilder.build(ATTR_NAME, ADDED));
Name name = obj.getName();
addAttrSet.remove(name);
Uid uid =
facade.addAttributeValues(ACCOUNT, obj.getUid(), AttributeUtil
.filterUid(addAttrSet), null);
// get back the object and see if there are the same..
addAttrSet.add(name);
ConnectorObject addO = new ConnectorObject(ACCOUNT, addAttrSet);
obj = facade.getObject(ObjectClass.ACCOUNT, newUid(1), null);
assertEquals(obj, addO);
// attempt to add on to an existing attribute..
addAttrSet.remove(name);
uid =
facade.addAttributeValues(ACCOUNT, obj.getUid(), AttributeUtil
.filterUid(addAttrSet), null);
// get the object back out and check on it..
obj = facade.getObject(ObjectClass.ACCOUNT, uid, null);
expected = AttributeBuilder.build(ATTR_NAME, ADDED, ADDED);
actual = obj.getAttributeByName(ATTR_NAME);
assertEquals(actual, expected);
// attempt to delete a value from an attribute..
Set<Attribute> deleteAttrs = CollectionUtil.newSet(addO.getAttributes());
deleteAttrs.remove(name);
uid =
facade.removeAttributeValues(ACCOUNT, addO.getUid(), AttributeUtil
.filterUid(deleteAttrs), null);
obj = facade.getObject(ObjectClass.ACCOUNT, uid, null);
expected = AttributeBuilder.build(ATTR_NAME, ADDED);
actual = obj.getAttributeByName(ATTR_NAME);
assertEquals(actual, expected);
// attempt to delete an attribute that doesn't exist..
Set<Attribute> nonExist = new HashSet<Attribute>();
nonExist.add(newUid(1));
nonExist.add(AttributeBuilder.build("does not exist", "asdfe"));
uid =
facade.removeAttributeValues(ACCOUNT, addO.getUid(), AttributeUtil
.filterUid(nonExist), null);
obj = facade.getObject(ObjectClass.ACCOUNT, newUid(1), null);
assertTrue(obj.getAttributeByName("does not exist") == null);
}
static Uid newUid(int id) {
return new Uid(Integer.toString(id));
}
}