From c84b64a96cb09a9258d744e1d73c2255f473290c Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Thu, 8 Jun 2017 18:36:18 +0200 Subject: [PATCH 1/2] More tests for role performance, fixing possible dummy connector issue. --- .../icf/dummy/connector/DummyConnector.java | 129 ++++-- .../prism/query/QueryJaxbConvertor.java | 24 +- .../midpoint/util/FailableProducer.java | 31 ++ .../story/TestPlentyOfAssignments.java | 394 +++++++++++++++++- .../story/src/test/resources/logback-test.xml | 4 +- .../plenty-of-assignments/user-alice.xml | 37 ++ 6 files changed, 554 insertions(+), 65 deletions(-) create mode 100644 infra/util/src/main/java/com/evolveum/midpoint/util/FailableProducer.java create mode 100644 testing/story/src/test/resources/plenty-of-assignments/user-alice.xml diff --git a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java index b83ee86857f..bf72d990e4a 100644 --- a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java +++ b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; import org.identityconnectors.common.logging.Log; import org.identityconnectors.common.security.GuardedString; @@ -1185,50 +1186,28 @@ public void executeQuery(ObjectClass objectClass, Filter query, ResultsHandler h try { if (ObjectClass.ACCOUNT.is(objectClass.getObjectClassValue())) { - Collection accounts = resource.listAccounts(); - for (DummyAccount account : accounts) { - ConnectorObject co = convertToConnectorObject(account, attributesToGet); - if (matches(query, co)) { - co = filterOutAttributesToGet(co, account, attributesToGet, options.getReturnDefaultAttributes()); - handler.handle(co); - } - } + search(objectClass, query, handler, options, + resource::listAccounts, resource::getAccountByUsername, resource::getAccountById, this::convertToConnectorObject, null); } else if (ObjectClass.GROUP.is(objectClass.getObjectClassValue())) { - Collection groups = resource.listGroups(); - for (DummyGroup group : groups) { - ConnectorObject co = convertToConnectorObject(group, attributesToGet); - if (matches(query, co)) { - if (attributesToGetHasAttribute(attributesToGet, DummyGroup.ATTR_MEMBERS_NAME)) { - resource.recordGroupMembersReadCount(); - } - co = filterOutAttributesToGet(co, group, attributesToGet, options.getReturnDefaultAttributes()); - handler.handle(co); - } - } - + search(objectClass, query, handler, options, + resource::listGroups, resource::getGroupByName, resource::getGroupById, this::convertToConnectorObject, + object -> { + if (attributesToGetHasAttribute(attributesToGet, DummyGroup.ATTR_MEMBERS_NAME)) { + resource.recordGroupMembersReadCount(); + } + }); + } else if (objectClass.is(OBJECTCLASS_PRIVILEGE_NAME)) { - Collection privs = resource.listPrivileges(); - for (DummyPrivilege priv : privs) { - ConnectorObject co = convertToConnectorObject(priv, attributesToGet); - if (matches(query, co)) { - co = filterOutAttributesToGet(co, priv, attributesToGet, options.getReturnDefaultAttributes()); - handler.handle(co); - } - } - + search(objectClass, query, handler, options, + resource::listPrivileges, resource::getPrivilegeByName, resource::getPrivilegeById, this::convertToConnectorObject, null); + } else if (objectClass.is(OBJECTCLASS_ORG_NAME)) { - Collection orgs = resource.listOrgs(); - for (DummyOrg org : orgs) { - ConnectorObject co = convertToConnectorObject(org, attributesToGet); - if (matches(query, co)) { - co = filterOutAttributesToGet(co, org, attributesToGet, options.getReturnDefaultAttributes()); - handler.handle(co); - } - } + search(objectClass, query, handler, options, + resource::listOrgs, resource::getOrgByName, resource::getOrgById, this::convertToConnectorObject, null); } else { throw new ConnectorException("Unknown object class "+objectClass); @@ -1250,6 +1229,80 @@ public void executeQuery(ObjectClass objectClass, Filter query, ResultsHandler h log.info("executeQuery::end"); } + + private void search(ObjectClass objectClass, Filter query, ResultsHandler handler, OperationOptions options, + Lister lister, Getter nameGetter, Getter idGetter, Converter converter, Consumer recorder) throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException { + Collection attributesToGet = getAttrsToGet(options); + log.ok("attributesToGet={0}", attributesToGet); + + if (isEqualsFilter(query, Name.NAME)) { + Attribute nameAttribute = ((EqualsFilter)query).getAttribute(); + String name = (String)nameAttribute.getValue().get(0); + T object = nameGetter.get(name); + if (object != null) { + handleObject(object, handler, options, attributesToGet, converter, recorder); + } + return; + } + + if (isEqualsFilter(query, Uid.NAME)) { + Attribute uidAttribute = ((EqualsFilter)query).getAttribute(); + String uid = (String)uidAttribute.getValue().get(0); + T object; + if (configuration.getUidMode().equals(DummyConfiguration.UID_MODE_NAME)) { + object = nameGetter.get(uid); + } else if (configuration.getUidMode().equals(DummyConfiguration.UID_MODE_UUID)) { + object = idGetter.get(uid); + } else { + throw new IllegalStateException("Unknown UID mode "+configuration.getUidMode()); + } + if (object != null) { + handleObject(object, handler, options, attributesToGet, converter, recorder); + } + return; + } + + // Brute force: list all, filter out + Collection allObjects = lister.list(); + for (T object : allObjects) { + ConnectorObject co = converter.convert(object, attributesToGet); + if (matches(query, co)) { + handleConnectorObject(object, co, handler, options, attributesToGet, recorder); + } + } + } + + private void handleObject(T object, ResultsHandler handler, OperationOptions options, Collection attributesToGet, Converter converter, Consumer recorder) throws SchemaViolationException { + ConnectorObject co = converter.convert(object, attributesToGet); + handleConnectorObject(object, co, handler, options, attributesToGet, recorder); + } + + private void handleConnectorObject(T object, ConnectorObject co, ResultsHandler handler, OperationOptions options, Collection attributesToGet, Consumer recorder) { + if (recorder != null) { + recorder.accept(object); + } + co = filterOutAttributesToGet(co, object, attributesToGet, options.getReturnDefaultAttributes()); + handler.handle(co); + } + + private boolean isEqualsFilter(Filter icfFilter, String icfAttrname) { + return icfFilter != null && (icfFilter instanceof EqualsFilter) && icfAttrname.equals(((EqualsFilter)icfFilter).getName()); + } + + @FunctionalInterface + interface Lister { + Collection list() throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException; + } + + @FunctionalInterface + interface Getter { + T get(String id) throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException; + } + + @FunctionalInterface + interface Converter { + ConnectorObject convert(T object, Collection attributesToGet) throws SchemaViolationException; + } private boolean shouldRequireBaseContext(ObjectClass objectClass, Filter query, OperationOptions options) { diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/query/QueryJaxbConvertor.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/query/QueryJaxbConvertor.java index 2e04aa407b7..dcf8e0a4018 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/query/QueryJaxbConvertor.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/query/QueryJaxbConvertor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2014 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -136,22 +136,24 @@ public static ObjectQuery createObjectQueryInternal(Pr } - public static QueryType createQueryType(ObjectQuery query, PrismContext prismContext) throws SchemaException{ - + public static QueryType createQueryType(ObjectQuery query, PrismContext prismContext) throws SchemaException { ObjectFilter filter = query.getFilter(); QueryType queryType = new QueryType(); - if (filter != null){ - SearchFilterType filterType = new SearchFilterType(); - MapXNode filterXNode = QueryConvertor.serializeFilter(filter, prismContext); - filterType.setFilterClauseXNode(filterXNode); - queryType.setFilter(filterType); + if (filter != null) { + queryType.setFilter(createSearchFilterType(filter, prismContext)); } - queryType.setPaging(PagingConvertor.createPagingType(query.getPaging())); return queryType; - } - + public static SearchFilterType createSearchFilterType(ObjectFilter filter, PrismContext prismContext) throws SchemaException { + if (filter == null) { + return null; + } + SearchFilterType filterType = new SearchFilterType(); + MapXNode filterXNode = QueryConvertor.serializeFilter(filter, prismContext); + filterType.setFilterClauseXNode(filterXNode); + return filterType; + } } diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/FailableProducer.java b/infra/util/src/main/java/com/evolveum/midpoint/util/FailableProducer.java new file mode 100644 index 00000000000..ac80872d0ee --- /dev/null +++ b/infra/util/src/main/java/com/evolveum/midpoint/util/FailableProducer.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2016-2017 Evolveum + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.evolveum.midpoint.util; + +import java.io.Serializable; + +/** + * Almost the same as java.util.function.Supplier, but this one is Serializable. + * That is very useful especially in use in Wicket models. + * + * @author Radovan Semancik + */ +@FunctionalInterface +public interface FailableProducer extends Serializable { + + T run() throws Exception; + +} diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestPlentyOfAssignments.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestPlentyOfAssignments.java index 4f671adf6c3..3db78a34898 100644 --- a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestPlentyOfAssignments.java +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestPlentyOfAssignments.java @@ -15,7 +15,10 @@ */ package com.evolveum.midpoint.testing.story; +import static org.testng.AssertJUnit.assertEquals; import java.io.File; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -29,17 +32,26 @@ import org.testng.annotations.Test; import com.evolveum.icf.dummy.resource.DummyAccount; +import com.evolveum.icf.dummy.resource.DummyGroup; +import com.evolveum.midpoint.common.refinery.RefinedResourceSchema; import com.evolveum.midpoint.model.api.context.ModelContext; import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismObjectDefinition; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.query.EqualFilter; +import com.evolveum.midpoint.prism.query.QueryJaxbConvertor; import com.evolveum.midpoint.prism.util.PrismTestUtil; import com.evolveum.midpoint.schema.constants.MidPointConstants; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.internals.InternalMonitor; +import com.evolveum.midpoint.schema.processor.ObjectClassComplexTypeDefinition; +import com.evolveum.midpoint.schema.processor.ResourceAttribute; +import com.evolveum.midpoint.schema.processor.ResourceAttributeContainer; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.MiscSchemaUtil; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; +import com.evolveum.midpoint.schema.util.ShadowUtil; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.DummyResourceContoller; import com.evolveum.midpoint.test.IntegrationTestTools; @@ -51,11 +63,15 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectFactory; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectSearchStrategyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SearchObjectExpressionEvaluatorType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; /** @@ -72,10 +88,16 @@ public class TestPlentyOfAssignments extends AbstractStoryTest { protected static final File RESOURCE_DUMMY_FILE = new File(TEST_DIR, "resource-dummy.xml"); protected static final String RESOURCE_DUMMY_OID = "10000000-0000-0000-0000-000000000004"; private static final String RESOURCE_DUMMY_NS = MidPointConstants.NS_RI; + private static final QName RESOURCE_DUMMY_ASSOCIATION_GROUP_QNAME = new QName(RESOURCE_DUMMY_NS, "group"); public static final File USER_CHEESE_FILE = new File(TEST_DIR, "user-cheese.xml"); public static final String USER_CHEESE_OID = "9e796c76-45e0-11e7-9dfd-1792e56081d0"; + protected static final File USER_ALICE_FILE = new File(TEST_DIR, "user-alice.xml"); + protected static final String USER_ALICE_OID = "5e8fdb32-4c4c-11e7-86a8-9706c2f85f86"; + protected static final String USER_ALICE_USERNAME = "alice"; + protected static final String USER_ALICE_FULLNAME = "Alice"; + protected static final File USER_BOB_FILE = new File(TEST_DIR, "user-bob.xml"); protected static final String USER_BOB_OID = "f5ffef5e-4b96-11e7-8e4c-1b0bc353a751"; protected static final String USER_BOB_USERNAME = "bob"; @@ -87,8 +109,10 @@ public class TestPlentyOfAssignments extends AbstractStoryTest { private static final int NUMBER_OF_ORDINARY_ROLES = 2; // including superuser role private static final int NUMBER_OF_GENERATED_EMPTY_ROLES = 1000; private static final String GENERATED_EMPTY_ROLE_OID_FORMAT = "00000000-0000-ffff-2000-e0000000%04d"; - private static final int NUMBER_OF_GENERATED_DUMMY_ROLES = 10; + private static final int NUMBER_OF_GENERATED_DUMMY_ROLES = 100; private static final String GENERATED_DUMMY_ROLE_OID_FORMAT = "00000000-0000-ffff-2000-d0000000%04d"; + private static final int NUMBER_OF_GENERATED_DUMMY_GROUPS = 100; + private static final String GENERATED_DUMMY_GROUP_ROLE_OID_FORMAT = "00000000-0000-ffff-2000-g0000000%04d"; private static final int NUMBER_OF_CHEESE_ASSIGNMENTS_APPROVER = 600; private static final int NUMBER_OF_CHEESE_ASSIGNMENTS_OWNER = 400; @@ -116,7 +140,7 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti (role,i) -> { ItemPathType attrPath = new ItemPathType( new ItemPath(new QName(RESOURCE_DUMMY_NS, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_DRINK_NAME))); - JAXBElement evaluator = objectFactory.createValue(String.format("bottle of rum #%04d", i)); + JAXBElement evaluator = objectFactory.createValue(formatRum(i)); role .beginInducement() .beginConstruction() @@ -127,11 +151,11 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti .beginOutbound() .beginExpression() .expressionEvaluator(evaluator); - try { - IntegrationTestTools.displayXml("RRRRRRRRR", role.asPrismObject()); - } catch (SchemaException e) { - throw new SystemException(e); - } +// try { +// IntegrationTestTools.displayXml("RRRRRRRRR", role.asPrismObject()); +// } catch (SchemaException e) { +// throw new SystemException(e); +// } }, initResult); inspector = new RepoReadInspector(); @@ -142,6 +166,14 @@ private String generateRoleOid(String format, int num) { return String.format(format, num); } + private String formatRum(int num) { + return String.format("bottle of rum #%04d", num); + } + + private String formatGroupName(int num) { + return String.format("G#%04d", num); + } + @Test public void test000Sanity() throws Exception { final String TEST_NAME = "test000Sanity"; @@ -301,12 +333,91 @@ private void assertCheeseRoleMembershipRef(PrismObject cheese) { assertRoleMembershipRefs(cheese, NUMBER_OF_CHEESE_ASSIGNMENTS); } + /** + * Create dummy groups that can be used for associationTargetSearch later on. + * Create them from midPoint so they have shadows. + * * MID-3938 #8 */ @Test - public void test200AddBob() throws Exception { - final String TEST_NAME = "test200AddBob"; + public void test200DummyGroups() throws Exception { + final String TEST_NAME = "test200DummyGroups"; + displayTestTile(TEST_NAME); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + // WHEN + displayWhen(TEST_NAME); + + PrismObjectDefinition shadowDef = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(ShadowType.class); + PrismObjectDefinition roleDef = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(RoleType.class); + RefinedResourceSchema rSchema = RefinedResourceSchema.getRefinedSchema(getDummyResourceObject()); + ObjectClassComplexTypeDefinition rOcDef = rSchema.findObjectClassDefinition(getDummyResourceController().getGroupObjectClass()); + + ObjectFactory objectFactory = new ObjectFactory(); + ItemPath nameAttributePath = new ItemPath(ShadowType.F_ATTRIBUTES, SchemaConstants.ICFS_NAME); + for (int i=0; i shadow = shadowDef.instantiate(); + ShadowType shadowType = shadow.asObjectable(); + shadowType + .resourceRef(RESOURCE_DUMMY_OID, ResourceType.COMPLEX_TYPE) + .objectClass(rOcDef.getTypeName()); + ResourceAttributeContainer attributesContainer = ShadowUtil.getOrCreateAttributesContainer(shadow, rOcDef); + ResourceAttribute nameAttribute = attributesContainer.findOrCreateAttribute(SchemaConstants.ICFS_NAME); + String groupName = formatGroupName(i); + nameAttribute.setRealValue(groupName); + display("Group shadow "+i, shadow); + addObject(shadow, task, result); + + PrismObject role = roleDef.instantiate(); + RoleType roleType = role.asObjectable(); + ItemPathType assPath = new ItemPathType(new ItemPath(RESOURCE_DUMMY_ASSOCIATION_GROUP_QNAME)); + SearchObjectExpressionEvaluatorType associationTargetSearchType = new SearchObjectExpressionEvaluatorType(); + EqualFilter filter = EqualFilter.createEqual(nameAttributePath, null, null, prismContext, groupName); + + SearchFilterType filterType = QueryJaxbConvertor.createSearchFilterType(filter, prismContext); + associationTargetSearchType.setFilter(filterType); + associationTargetSearchType.setSearchStrategy(ObjectSearchStrategyType.IN_REPOSITORY); + JAXBElement evaluator = objectFactory.createAssociationTargetSearch(associationTargetSearchType); + roleType + .oid(generateRoleOid(GENERATED_DUMMY_GROUP_ROLE_OID_FORMAT, i)) + .name(String.format("Group role %04d", i)) + .beginInducement() + .beginConstruction() + .resourceRef(RESOURCE_DUMMY_OID, ResourceType.COMPLEX_TYPE) + .kind(ShadowKindType.ACCOUNT) + .beginAssociation() + .ref(assPath) + .beginOutbound() + .beginExpression() + .expressionEvaluator(evaluator); + try { + IntegrationTestTools.displayXml("RRRRRRRRR group", role); + } catch (SchemaException e) { + throw new SystemException(e); + } + addObject(role, task, result); + } + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + Collection dummyGroups = getDummyResource().listGroups(); + assertEquals("Wrong number of dummy groups", NUMBER_OF_GENERATED_DUMMY_GROUPS, dummyGroups.size()); + assertShadows(NUMBER_OF_GENERATED_DUMMY_GROUPS); + + assertObjects(RoleType.class, NUMBER_OF_GENERATED_EMPTY_ROLES + NUMBER_OF_GENERATED_DUMMY_ROLES + NUMBER_OF_ORDINARY_ROLES + NUMBER_OF_GENERATED_DUMMY_GROUPS); + } + + /** + * MID-3938 #8 + */ + @Test + public void test210AddBob() throws Exception { + final String TEST_NAME = "test210AddBob"; displayTestTile(TEST_NAME); Task task = createTask(TEST_NAME); @@ -331,7 +442,7 @@ public void test200AddBob() throws Exception { long endMillis = System.currentTimeMillis(); assertSuccess(result); - display("Added jack in "+(endMillis - startMillis)+"ms ("+((endMillis - startMillis)/NUMBER_OF_BOB_DUMMY_ROLE_ASSIGNMENTS)+"ms per assignment)"); + display("Added bob in "+(endMillis - startMillis)+"ms ("+((endMillis - startMillis)/NUMBER_OF_BOB_DUMMY_ROLE_ASSIGNMENTS)+"ms per assignment)"); PrismObject userAfter = getUser(USER_BOB_OID); display("User after", assignmentSummary(userAfter)); @@ -342,14 +453,103 @@ public void test200AddBob() throws Exception { display("Inspector", inspector); -// inspector.assertRead(RoleType.class, NUMBER_OF_JACK_DUMMY_ROLE_ASSIGNMENTS); + // TODO: why *3 ??? + inspector.assertRead(RoleType.class, NUMBER_OF_BOB_DUMMY_ROLE_ASSIGNMENTS * 3); // assertRepositoryReadCount(xxx); // may be influenced by tasks assertPrismObjectCompareCount(0); - DummyAccount dummyAccount = assertDummyAccount(null, USER_BOB_USERNAME, USER_BOB_FULLNAME, true); - display("Dummy account", dummyAccount); - // TODO: assert drink: bottles of rum + assertBobDummy(NUMBER_OF_BOB_DUMMY_ROLE_ASSIGNMENTS); + } + + /** + * MID-3938 #8 + */ + @Test + public void test212RecomputeBob() throws Exception { + final String TEST_NAME = "test212RecomputeBob"; + displayTestTile(TEST_NAME); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + inspector.reset(); + rememberPrismObjectCompareCount(); + rememberRepositoryReadCount(); + long startMillis = System.currentTimeMillis(); + + // WHEN + displayWhen(TEST_NAME); + + recomputeUser(USER_BOB_OID, task, result); + + // THEN + displayThen(TEST_NAME); + long endMillis = System.currentTimeMillis(); + assertSuccess(result); + + display("Recomputed bob in "+(endMillis - startMillis)+"ms ("+((endMillis - startMillis)/NUMBER_OF_BOB_DUMMY_ROLE_ASSIGNMENTS)+"ms per assignment)"); + + PrismObject userAfter = getUser(USER_BOB_OID); + display("User after", assignmentSummary(userAfter)); + assertBobRoleMembershipRef(userAfter); + + display("Repo reads", InternalMonitor.getRepositoryReadCount()); + display("Object compares", InternalMonitor.getPrismObjectCompareCount()); + + display("Inspector", inspector); + + inspector.assertRead(RoleType.class, NUMBER_OF_BOB_DUMMY_ROLE_ASSIGNMENTS); +// assertRepositoryReadCount(xxx); // may be influenced by tasks + + assertPrismObjectCompareCount(0); + + assertBobDummy(NUMBER_OF_BOB_DUMMY_ROLE_ASSIGNMENTS); + } + + /** + * MID-3938 #8 + */ + @Test + public void test2124ReconcileBob() throws Exception { + final String TEST_NAME = "test212RecomputeBob"; + displayTestTile(TEST_NAME); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + inspector.reset(); + rememberPrismObjectCompareCount(); + rememberRepositoryReadCount(); + long startMillis = System.currentTimeMillis(); + + // WHEN + displayWhen(TEST_NAME); + + reconcileUser(USER_BOB_OID, task, result); + + // THEN + displayThen(TEST_NAME); + long endMillis = System.currentTimeMillis(); + assertSuccess(result); + + display("Reconciled bob in "+(endMillis - startMillis)+"ms ("+((endMillis - startMillis)/NUMBER_OF_BOB_DUMMY_ROLE_ASSIGNMENTS)+"ms per assignment)"); + + PrismObject userAfter = getUser(USER_BOB_OID); + display("User after", assignmentSummary(userAfter)); + assertBobRoleMembershipRef(userAfter); + + display("Repo reads", InternalMonitor.getRepositoryReadCount()); + display("Object compares", InternalMonitor.getPrismObjectCompareCount()); + + display("Inspector", inspector); + + inspector.assertRead(RoleType.class, NUMBER_OF_BOB_DUMMY_ROLE_ASSIGNMENTS); +// assertRepositoryReadCount(xxx); // may be influenced by tasks + + assertPrismObjectCompareCount(0); + + assertBobDummy(NUMBER_OF_BOB_DUMMY_ROLE_ASSIGNMENTS); } private void assertBobRoleMembershipRef(PrismObject user) { @@ -358,6 +558,172 @@ private void assertBobRoleMembershipRef(PrismObject user) { assertRoleMembershipRefs(user, NUMBER_OF_BOB_DUMMY_ROLE_ASSIGNMENTS); } + private void assertBobDummy(int expectedBottlesOfRum) throws Exception { + DummyAccount dummyAccount = assertDummyAccount(null, USER_BOB_USERNAME, USER_BOB_FULLNAME, true); + display("Dummy account", dummyAccount); + List expectedDrinks = new ArrayList<>(expectedBottlesOfRum + 1); + for (int i = 0; i < expectedBottlesOfRum; i++) { + expectedDrinks.add(formatRum(i)); + } + expectedDrinks.add("barrel of rum"); + assertDummyAccountAttribute(null, USER_BOB_USERNAME, DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_DRINK_NAME, + expectedDrinks.toArray()); + } + + /** + * MID-3938 #8 + */ + @Test + public void test220AddAlice() throws Exception { + final String TEST_NAME = "test220AddAlice"; + displayTestTile(TEST_NAME); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + PrismObject userBefore = PrismTestUtil.parseObject(USER_ALICE_FILE); + addAssignments(userBefore, GENERATED_DUMMY_GROUP_ROLE_OID_FORMAT, null, 0, NUMBER_OF_GENERATED_DUMMY_GROUPS); + display("User before", assignmentSummary(userBefore)); + + inspector.reset(); + rememberPrismObjectCompareCount(); + rememberRepositoryReadCount(); + long startMillis = System.currentTimeMillis(); + + // WHEN + displayWhen(TEST_NAME); + + addObject(userBefore, task, result); + + // THEN + displayThen(TEST_NAME); + long endMillis = System.currentTimeMillis(); + assertSuccess(result); + + display("Added alice in "+(endMillis - startMillis)+"ms ("+((endMillis - startMillis)/NUMBER_OF_GENERATED_DUMMY_GROUPS)+"ms per assignment)"); + + PrismObject userAfter = getUser(USER_ALICE_OID); + display("User after", assignmentSummary(userAfter)); + assertAliceRoleMembershipRef(userAfter); + + display("Repo reads", InternalMonitor.getRepositoryReadCount()); + display("Object compares", InternalMonitor.getPrismObjectCompareCount()); + + display("Inspector", inspector); + + // TODO: why *3 ??? + inspector.assertRead(RoleType.class, NUMBER_OF_GENERATED_DUMMY_GROUPS * 3); +// assertRepositoryReadCount(xxx); // may be influenced by tasks + + assertPrismObjectCompareCount(0); + + assertAliceDummy(NUMBER_OF_GENERATED_DUMMY_GROUPS); + } + + /** + * MID-3938 #8 + */ + @Test + public void test222RecomputeAlice() throws Exception { + final String TEST_NAME = "test222RecomputeAlice"; + displayTestTile(TEST_NAME); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + inspector.reset(); + rememberPrismObjectCompareCount(); + rememberRepositoryReadCount(); + long startMillis = System.currentTimeMillis(); + + // WHEN + displayWhen(TEST_NAME); + + recomputeUser(USER_ALICE_OID, task, result); + + // THEN + displayThen(TEST_NAME); + long endMillis = System.currentTimeMillis(); + assertSuccess(result); + + display("Recomputed alice in "+(endMillis - startMillis)+"ms ("+((endMillis - startMillis)/NUMBER_OF_GENERATED_DUMMY_GROUPS)+"ms per assignment)"); + + PrismObject userAfter = getUser(USER_ALICE_OID); + display("User after", assignmentSummary(userAfter)); + assertAliceRoleMembershipRef(userAfter); + + display("Repo reads", InternalMonitor.getRepositoryReadCount()); + display("Object compares", InternalMonitor.getPrismObjectCompareCount()); + + display("Inspector", inspector); + + inspector.assertRead(RoleType.class, NUMBER_OF_GENERATED_DUMMY_GROUPS); +// assertRepositoryReadCount(xxx); // may be influenced by tasks + + assertPrismObjectCompareCount(0); + + assertAliceDummy(NUMBER_OF_GENERATED_DUMMY_GROUPS); + } + + /** + * MID-3938 #8 + */ + @Test + public void test224ReconcileAlice() throws Exception { + final String TEST_NAME = "test224ReconcileAlice"; + displayTestTile(TEST_NAME); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + inspector.reset(); + rememberPrismObjectCompareCount(); + rememberRepositoryReadCount(); + long startMillis = System.currentTimeMillis(); + + // WHEN + displayWhen(TEST_NAME); + + reconcileUser(USER_ALICE_OID, task, result); + + // THEN + displayThen(TEST_NAME); + long endMillis = System.currentTimeMillis(); + assertSuccess(result); + + display("Reconciled alice in "+(endMillis - startMillis)+"ms ("+((endMillis - startMillis)/NUMBER_OF_GENERATED_DUMMY_GROUPS)+"ms per assignment)"); + + PrismObject userAfter = getUser(USER_ALICE_OID); + display("User after", assignmentSummary(userAfter)); + assertAliceRoleMembershipRef(userAfter); + + display("Repo reads", InternalMonitor.getRepositoryReadCount()); + display("Object compares", InternalMonitor.getPrismObjectCompareCount()); + + display("Inspector", inspector); + + inspector.assertRead(RoleType.class, NUMBER_OF_GENERATED_DUMMY_GROUPS); +// assertRepositoryReadCount(xxx); // may be influenced by tasks + + assertPrismObjectCompareCount(0); + + assertAliceDummy(NUMBER_OF_GENERATED_DUMMY_GROUPS); + } + + private void assertAliceRoleMembershipRef(PrismObject user) { + + assertRoleMembershipRefs(user, GENERATED_DUMMY_GROUP_ROLE_OID_FORMAT, null, 0, NUMBER_OF_GENERATED_DUMMY_GROUPS); + assertRoleMembershipRefs(user, NUMBER_OF_GENERATED_DUMMY_GROUPS); + } + + private void assertAliceDummy(int expectedGroups) throws Exception { + DummyAccount dummyAccount = assertDummyAccount(null, USER_ALICE_USERNAME, USER_ALICE_FULLNAME, true); + display("Dummy account", dummyAccount); + for (int i = 0; i < expectedGroups; i++) { + assertDummyGroupMember(null, formatGroupName(i), USER_ALICE_USERNAME); + } + } + private void addAssignments(PrismObject user, String roleOidFormat, QName relation, int offset, int num) { UserType userType = user.asObjectable(); for (int i = 0; i < num; i++) { diff --git a/testing/story/src/test/resources/logback-test.xml b/testing/story/src/test/resources/logback-test.xml index ec37e531a0c..49ba3d07e74 100644 --- a/testing/story/src/test/resources/logback-test.xml +++ b/testing/story/src/test/resources/logback-test.xml @@ -42,7 +42,7 @@ - + @@ -56,7 +56,7 @@ - + diff --git a/testing/story/src/test/resources/plenty-of-assignments/user-alice.xml b/testing/story/src/test/resources/plenty-of-assignments/user-alice.xml new file mode 100644 index 00000000000..4d114772322 --- /dev/null +++ b/testing/story/src/test/resources/plenty-of-assignments/user-alice.xml @@ -0,0 +1,37 @@ + + + + alice + This user has many roles with construction for groups for the same resource + Alice + + + + + + + wonderland + + + + + From b2d42fd94f598b05387b122d708374b956e769e1 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Thu, 8 Jun 2017 20:10:52 +0200 Subject: [PATCH 2/2] Attempt to fix misleading 'warning-like' yellow box when reconciling user with projection on offline resource. Plus some diagnostics. --- .../com/evolveum/midpoint/provisioning/impl/ShadowCache.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java index 466cc90e20b..2591688e598 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java @@ -263,11 +263,12 @@ public PrismObject getShadow(String oid, PrismObject rep // This may be OK, e.g. for connectors that have running async add operation. if (canReturnCachedAfterNotFoundOnResource(options, repositoryShadow, resource)) { LOGGER.trace("Object not found on reading of {}, but we can return cached shadow", repositoryShadow); - parentResult.muteLastSubresultError(); + parentResult.deleteLastSubresultIfError(); // we don't want to see 'warning-like' orange boxes in GUI (TODO reconsider this) parentResult.recordSuccess(); repositoryShadow.asObjectable().setExists(false); PrismObject resultShadow = futurizeShadow(repositoryShadow, options, resource); applyAttributesDefinition(ctx, resultShadow); + LOGGER.trace("Returning futurized shadow:\n{}", DebugUtil.debugDumpLazily(resultShadow)); return resultShadow; } else { throw e;