Skip to content

Commit

Permalink
Add first simulation report tests
Browse files Browse the repository at this point in the history
Other changes:

1. Added primitive CSV file asserter.
2. Added model-level bulk test objects creator.
3. Experimental support for '#' (oid, id) as a report column path.
  • Loading branch information
mederly committed Feb 16, 2023
1 parent 8036f28 commit c227a5d
Show file tree
Hide file tree
Showing 19 changed files with 456 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ public static List<ObjectOrdering> createObjectOrderings(SortParam<String> sortP
List<ObjectOrdering> rv = new ArrayList<>();
rv.add(prismContext.queryFactory().createOrdering(primaryItemPath, sortParam.isAscending() ? OrderDirection.ASCENDING : OrderDirection.DESCENDING));
// additional criteria are used to avoid random shuffling if first criteria is too vague)
rv.add(prismContext.queryFactory().createOrdering(campaignPath.append(PrismConstants.T_ID), OrderDirection.ASCENDING)); // campaign OID
rv.add(prismContext.queryFactory().createOrdering(casePath.append(PrismConstants.T_ID), OrderDirection.ASCENDING)); // case ID
rv.add(prismContext.queryFactory().createOrdering(campaignPath.append(PrismConstants.T_ID), OrderDirection.ASCENDING)); // campaign OID
rv.add(prismContext.queryFactory().createOrdering(casePath.append(PrismConstants.T_ID), OrderDirection.ASCENDING)); // case ID
if (isWorkItem) {
rv.add(prismContext.queryFactory().createOrdering(ItemName.fromQName(PrismConstants.T_ID), OrderDirection.ASCENDING)); // work item ID
rv.add(prismContext.queryFactory().createOrdering(PrismConstants.T_ID, OrderDirection.ASCENDING)); // work item ID
}
return rv;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7188,4 +7188,26 @@ public <O extends ObjectType> PrismObject<O> getObject(
protected int accessesMetadataAuditOverhead(int relevantExecutionRecords) {
return accessesMetadataEnabled ? relevantExecutionRecords : 0;
}

/**
* Provides a model-level objects creator.
*
* @see AbstractIntegrationTest#realRepoCreator()
*/
private <O extends ObjectType> ObjectCreator.RealCreator<O> realModelCreator() {
return (o, result) -> {
addObject(o.asPrismObject(), getTestTask(), result);
};
}

protected <O extends ObjectType> ObjectCreatorBuilder<O> modelObjectCreatorFor(Class<O> type) {
return ObjectCreator.forType(type)
.withRealCreator(realModelCreator());
}

protected CsvAsserter<Void> assertCsv(List<String> lines, String message) {
CsvAsserter<Void> asserter = new CsvAsserter<>(lines, null, message);
initializeAsserter(asserter);
return asserter;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,8 @@ private void applyAttributesDefinitions(List<? extends ProcessedObject<?>> objec
CloneUtil.cloneCollectionMembers(processedObjectBeans));
return simulationResult;
}

public @NotNull String getSimulationResultOid() {
return simulationResultOid;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import java.util.stream.Collectors;
import javax.xml.datatype.XMLGregorianCalendar;

import com.evolveum.midpoint.util.annotation.Experimental;

import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -42,9 +44,8 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;

/**
* Converts record ({@link Containerable}) to a semi-formatted row
* ({@link ExportedReportDataRow} - basically, a string representation)
* according to individual columns specifications.
* Converts record ({@link Containerable}, {@link Referencable} or later POJO) to a semi-formatted row
* ({@link ExportedReportDataRow} - basically, a string representation) according to individual columns specifications.
*
* Responsibilities:
*
Expand Down Expand Up @@ -141,6 +142,9 @@ List<String> convertColumn(@NotNull GuiObjectColumnType column) {
* @return List of values of the item found. (Or empty list of nothing was found.)
*/
private @NotNull List<? extends PrismValue> resolvePath(ItemPath itemPath) {
if (itemPath.equivalent(PrismConstants.T_ID)) {
return getIdentifier();
}
Item<?, ?> currentItem = null;
Iterator<?> iterator = itemPath.getSegments().iterator();
while (iterator.hasNext()) {
Expand Down Expand Up @@ -179,6 +183,26 @@ List<String> convertColumn(@NotNull GuiObjectColumnType column) {
return currentItem != null ? currentItem.getValues() : List.of();
}

@Experimental
private List<? extends PrismValue> getIdentifier() {
if (record instanceof Objectable) {
return toPropertyValues(((Objectable) record).getOid());
} else if (record instanceof Containerable) {
return toPropertyValues(((Containerable) record).asPrismContainerValue().getId());
} else {
return List.of();
}
}

private List<? extends PrismValue> toPropertyValues(Object realValue) {
if (realValue == null) {
return List.of();
} else {
return List.of(
PrismContext.get().itemFactory().createPropertyValue(realValue));
}
}

private List<String> prettyPrintValues(Collection<? extends PrismValue> values) {
return values.stream()
.map(this::prettyPrintValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
import java.text.ParseException;
import java.util.List;

import com.evolveum.midpoint.prism.MutablePrismPropertyDefinition;
import com.evolveum.midpoint.prism.PrismContainerValue;
import com.evolveum.midpoint.prism.PrismProperty;
import com.evolveum.midpoint.report.api.ReportConstants;

import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;

Expand All @@ -32,10 +37,10 @@
import com.evolveum.midpoint.util.exception.*;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;

import javax.xml.namespace.QName;

/**
* Common superclass for "empty" report integration tests.
*
* VERY EXPERIMENTAL
*/
@ContextConfiguration(locations = { "classpath:ctx-report-test-main.xml" })
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
Expand Down Expand Up @@ -174,7 +179,8 @@ private void commonInitialization(OperationResult initResult)
}
modelService.postInit(initResult);

PrismObject<UserType> userAdministrator = repoAddObjectFromFile(USER_ADMINISTRATOR_FILE, RepoAddOptions.createOverwrite(), false, initResult);
PrismObject<UserType> userAdministrator =
repoAddObjectFromFile(USER_ADMINISTRATOR_FILE, RepoAddOptions.createOverwrite(), false, initResult);
login(userAdministrator);

repoAdd(ARCHETYPE_TASK_REPORT_EXPORT_CLASSIC, initResult);
Expand Down Expand Up @@ -294,4 +300,24 @@ void assertNotificationMessage(ReportType report, String expectedContentType) {
NotificationMessageAttachmentType attachment = attachments.get(0);
assertThat(attachment.getContentType()).as("attachment content type").isEqualTo(expectedContentType);
}

@SuppressWarnings("SameParameterValue")
ReportParameterType getParameters(String name, Class<String> type, Object realValue) throws SchemaException {
ReportParameterType reportParam = new ReportParameterType();
//noinspection unchecked
PrismContainerValue<ReportParameterType> reportParamValue = reportParam.asPrismContainerValue();

QName typeName = prismContext.getSchemaRegistry().determineTypeForClass(type);
MutablePrismPropertyDefinition<Object> def = prismContext.definitionFactory().createPropertyDefinition(
new QName(ReportConstants.NS_EXTENSION, name), typeName);
def.setDynamic(true);
def.setRuntimeSchema(true);
def.toMutable().setMaxOccurs(1);

PrismProperty<Object> prop = def.instantiate();
prop.addRealValue(realValue);
reportParamValue.add(prop);

return reportParam;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,16 @@ private int getNumberOfColumns(List<String> lines) {

@Override
protected FileFormatConfigurationType getFileFormatConfiguration() {
FileFormatConfigurationType config = new FileFormatConfigurationType();
config.setType(FileFormatTypeType.CSV);
return config;
return new FileFormatConfigurationType()
.type(FileFormatTypeType.CSV);
}

void assertNotificationMessage(TestResource<ReportType> reportTestResource) {
assertNotificationMessage(reportTestResource.getObjectable(), "text/csv");
}

protected void testClassicExport(TestResource<ReportType> reportResource, int expectedRows, int expectedColumns,
protected List<String> testClassicExport(
TestResource<ReportType> reportResource, int expectedRows, int expectedColumns,
String lastLine, ReportParameterType parameters) throws Exception {
given();
Task task = getTestTask();
Expand Down Expand Up @@ -114,9 +114,11 @@ protected void testClassicExport(TestResource<ReportType> reportResource, int ex
.assertHasArchetype(SystemObjectsType.ARCHETYPE_REPORT_EXPORT_CLASSIC_TASK.value());

PrismObject<TaskType> reportTask = getObject(TaskType.class, TASK_EXPORT_CLASSIC.oid);
basicCheckOutputFile(reportTask, expectedRows, expectedColumns, lastLine);
var rows = basicCheckOutputFile(reportTask, expectedRows, expectedColumns, lastLine);

assertNotificationMessage(reportResource);

return rows;
}

protected void testClassicExport(TestResource<ReportType> reportResource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,22 +230,4 @@ public void test140ExportAuditRecords() throws Exception {

testClassicExport(REPORT_SUBREPORT_AUDIT, 5, 4, null, parameters);
}

private ReportParameterType getParameters(String name, Class<String> type, Object realValue) throws SchemaException {
ReportParameterType reportParam = new ReportParameterType();
PrismContainerValue<ReportParameterType> reportParamValue = reportParam.asPrismContainerValue();

QName typeName = prismContext.getSchemaRegistry().determineTypeForClass(type);
MutablePrismPropertyDefinition<Object> def = prismContext.definitionFactory().createPropertyDefinition(
new QName(ReportConstants.NS_EXTENSION, name), typeName);
def.setDynamic(true);
def.setRuntimeSchema(true);
def.toMutable().setMaxOccurs(1);

PrismProperty<Object> prop = def.instantiate();
prop.addRealValue(realValue);
reportParamValue.add(prop);

return reportParam;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* Copyright (C) 2010-2023 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/
package com.evolveum.midpoint.report;

import java.util.List;

import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import com.evolveum.midpoint.model.test.CommonInitialObjects;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.test.TestResource;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;

@ContextConfiguration(locations = { "classpath:ctx-report-test-main.xml" })
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class TestCsvSimulationReport extends TestCsvReport {

private static final int EXISTING_USERS = 10;

private static final int COLUMNS = 7;

private static final TestResource<ReportType> REPORT_SIMULATION_BASIC = new TestResource<>(
TEST_DIR_REPORTS, "report-simulation-basic.xml", "ee0e28ae-2827-4a69-bd5b-35b478cc2f5f");

private List<UserType> existingUsers;

@BeforeMethod
public void onNativeOnly() {
skipIfNotNativeRepository();
}

@Override
public void initSystem(Task initTask, OperationResult initResult) throws Exception {
// Only for Native repo, as Generic repo does not support simulations
if (!isNativeRepository()) {
return;
}
super.initSystem(initTask, initResult);

CommonInitialObjects.addMarks(this, initTask, initResult);

existingUsers = modelObjectCreatorFor(UserType.class)
.withObjectCount(EXISTING_USERS)
.withNamePattern("existing-%04d")
.execute(initResult);

repoAdd(TASK_EXPORT_CLASSIC, initResult);
repoAdd(REPORT_SIMULATION_BASIC, initResult);
}

@Test
public void test100CreateNewUsers() throws Exception {
int users = 10;

Task task = getTestTask();
OperationResult result = task.getResult();

when("users are created in simulation mode");
var simulationResult = executeWithSimulationResult(
task, result,
() -> modelObjectCreatorFor(UserType.class)
.withObjectCount(users)
.withNamePattern("new-%04d")
.execute(result));

then("simulation result is OK");
assertProcessedObjects(simulationResult, "after")
.assertSize(users);

ReportParameterType parameters = getParameters(
"simulationResultOid", String.class, simulationResult.getSimulationResultOid());
var rows = testClassicExport(REPORT_SIMULATION_BASIC, users + 1, COLUMNS, null, parameters);

// Assuming nice sequential ordering of processed records (may require specifying it in the report)
assertCsv(rows, "after")
.removeStandardFooter()
.parse()
.display()
.row(0)
.assertValue(2, "new-0000")
.assertValue(4, "Added")
.assertValue(5, "UserType")
.assertValues(6, "Focus activated")
.end();
}

@Test
public void test110DisableAndRenameUsers() throws Exception {
int users = 10;

Task task = getTestTask();
OperationResult result = task.getResult();

when("users are renamed and optionally disabled in simulation mode");
var simulationResult = executeWithSimulationResult(
task, result,
() -> {
for (int i = 0; i < existingUsers.size(); i++) {
UserType existingUser = existingUsers.get(i);
modelService.executeChanges(
List.of(deltaFor(UserType.class)
.item(UserType.F_NAME)
.replace(PolyString.fromOrig(existingUser.getName().getOrig() + "-renamed"))
.item(UserType.F_ACTIVATION, ActivationType.F_ADMINISTRATIVE_STATUS)
.replace(i%2 == 0 ? ActivationStatusType.DISABLED : null)
.asObjectDelta(existingUser.getOid())),
null, task, result);
}
});

then("simulation result is OK");
assertProcessedObjects(simulationResult, "after")
.assertSize(EXISTING_USERS);

ReportParameterType parameters = getParameters(
"simulationResultOid", String.class, simulationResult.getSimulationResultOid());
var rows = testClassicExport(REPORT_SIMULATION_BASIC, users + 1, COLUMNS, null, parameters);

// Assuming nice sequential ordering of processed records (may require specifying it in the report)
assertCsv(rows, "after")
.removeStandardFooter()
.parse()
.display()
.row(0)
.assertValue(2, "existing-0000-renamed")
.assertValue(3, existingUsers.get(0).getOid())
.assertValue(4, "Modified")
.assertValue(5, "UserType")
.assertValues(6, "Focus renamed", "Focus deactivated")
.end()
.row(1)
.assertValue(2, "existing-0001-renamed")
.assertValue(3, existingUsers.get(1).getOid())
.assertValue(4, "Modified")
.assertValue(5, "UserType")
.assertValues(6, "Focus renamed");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,7 @@
</collection>
<condition>
<script>
<code>
if (object.getTargetRef().getOid().equals("c0c010c0-d34d-b33f-f00d-111111111122")) {
return true;
}
return false;
</code>
<code>object.targetRef.oid == 'c0c010c0-d34d-b33f-f00d-111111111122'</code>
</script>
</condition>
</objectCollection>
Expand Down

0 comments on commit c227a5d

Please sign in to comment.