Provide API for auto assignment check for specific device (#1262)

* Extend the auto assign executor to execute a auto assignment check for a specific device.

Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>

* add tests and handle exceptions

Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io>

* add unit test

Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io>

* add Copyright header

Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io>

* fix Copyright header

Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io>

* add authorization

Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io>

* fix exception handling and authorization

Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io>

* renaming

Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io>

* Rename auto assignment functions

Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>

Co-authored-by: Stefan Klotz <stefan.klotz@bosch.io>
This commit is contained in:
Michael Herdt
2022-07-12 15:47:38 +02:00
committed by GitHub
parent 1ec5eb6ede
commit 5c95c4ce28
8 changed files with 647 additions and 334 deletions

View File

@@ -16,6 +16,7 @@ import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -53,6 +54,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -853,6 +855,25 @@ public class JpaTargetManagement implements TargetManagement {
return targetRepository.exists(TargetSpecifications.hasControllerId(controllerId));
}
@Override
public boolean isTargetMatchingQueryAndDSNotAssignedAndCompatible(final String controllerId,
final long distributionSetId, final String targetFilterQuery) {
RSQLUtility.validateRsqlFor(targetFilterQuery, TargetFields.class);
final DistributionSet ds = distributionSetManagement.get(distributionSetId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, distributionSetId));
final Long distSetTypeId = ds.getType().getId();
final List<Specification<JpaTarget>> specList = Arrays.asList(
RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer,
database),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId),
TargetSpecifications.hasControllerId(controllerId));
final Specification<JpaTarget> combinedSpecification = Objects
.requireNonNull(SpecificationsBuilder.combineWithAnd(specList));
return targetRepository.exists(combinedSpecification);
}
@Override
public Page<Target> findByControllerAttributesRequested(final Pageable pageReq) {
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageReq,

View File

@@ -8,6 +8,7 @@
*/
package org.eclipse.hawkbit.repository.jpa.autoassign;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@@ -64,12 +65,21 @@ public class AutoAssignChecker extends AbstractAutoAssignExecutor {
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void check() {
public void checkAllTargets() {
LOGGER.debug("Auto assign check call for tenant {} started", getTenantAware().getCurrentTenant());
forEachFilterWithAutoAssignDS(this::checkByTargetFilterQueryAndAssignDS);
LOGGER.debug("Auto assign check call for tenant {} finished", getTenantAware().getCurrentTenant());
}
@Override
public void checkSingleTarget(String controllerId) {
LOGGER.debug("Auto assign check call for tenant {} and device {} started", getTenantAware().getCurrentTenant(),
controllerId);
forEachFilterWithAutoAssignDS(filter -> checkForDevice(controllerId, filter));
LOGGER.debug("Auto assign check call for tenant {} and device {} finished", getTenantAware().getCurrentTenant(),
controllerId);
}
/**
* Fetches the distribution set, gets all controllerIds and assigns the DS
* to them. Catches PersistenceException and own exceptions derived from
@@ -104,4 +114,23 @@ public class AutoAssignChecker extends AbstractAutoAssignExecutor {
LOGGER.debug("Auto assign check call for tenant {} and target filter query id {} finished",
getTenantAware().getCurrentTenant(), targetFilterQuery.getId());
}
private void checkForDevice(final String controllerId, final TargetFilterQuery targetFilterQuery) {
LOGGER.debug("Auto assign check call for tenant {} and target filter query id {} for device {} started",
getTenantAware().getCurrentTenant(), targetFilterQuery.getId(), controllerId);
try {
final boolean controllerIdMatches = targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(
controllerId, targetFilterQuery.getAutoAssignDistributionSet().getId(),
targetFilterQuery.getQuery());
if (controllerIdMatches) {
runTransactionalAssignment(targetFilterQuery, Collections.singletonList(controllerId));
}
} catch (final PersistenceException | AbstractServerRtException e) {
LOGGER.error("Error during auto assign check of target filter query id {}", targetFilterQuery.getId(), e);
}
LOGGER.debug("Auto assign check call for tenant {} and target filter query id {} finished",
getTenantAware().getCurrentTenant(), targetFilterQuery.getId());
}
}

View File

@@ -83,7 +83,7 @@ public class AutoAssignScheduler {
try {
LOGGER.debug("Auto assign scheduled execution has aquired lock and started for each tenant.");
systemManagement.forEachTenant(tenant -> autoAssignExecutor.check());
systemManagement.forEachTenant(tenant -> autoAssignExecutor.checkAllTargets());
} finally {
lock.unlock();
LOGGER.debug("Auto assign scheduled execution has released lock and finished.");

View File

@@ -48,6 +48,8 @@ import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidTargetAddressException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
@@ -1256,6 +1258,83 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
validateFoundTargetsByRsql(rsqlOrControllerIdNotEqualFilter, controllerId1, controllerId2);
}
@Test
@Description("Target matches filter.")
void matchesFilter() {
final Target target = createTargetWithMetadata("target1", 2);
final DistributionSet ds = testdataFactory.createDistributionSet();
final String filter = "metadata.key1==target1-value1";
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target.getControllerId(),
ds.getId(), filter)).isTrue();
}
@Test
@Description("Target does not matches filter.")
void matchesFilterWrongFilter() {
final Target target = testdataFactory.createTarget();
final DistributionSet ds = testdataFactory.createDistributionSet();
final String filter = "metadata.key==not_existing";
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target.getControllerId(),
ds.getId(), filter)).isFalse();
}
@Test
@Description("Target matches filter but DS already assigned.")
void matchesFilterDsAssigned() {
final Target target = testdataFactory.createTarget();
final DistributionSet ds1 = testdataFactory.createDistributionSet();
final DistributionSet ds2 = testdataFactory.createDistributionSet();
assignDistributionSet(ds1, target);
assignDistributionSet(ds2, target);
final String filter = "name==*";
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target.getControllerId(),
ds1.getId(), filter)).isFalse();
}
@Test
@Description("Target matches filter for DS with wrong type.")
void matchesFilterWrongType() {
final TargetType type = testdataFactory.createTargetType("type", Collections.emptyList());
final Target target = testdataFactory.createTarget("target", "target", type.getId());
final DistributionSet ds = testdataFactory.createDistributionSet();
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target.getControllerId(),
ds.getId(), "name==*")).isFalse();
}
@Test
@Description("Target matches filter that is invalid.")
void matchesFilterInvalidFilter() {
final String target = testdataFactory.createTarget().getControllerId();
final Long ds = testdataFactory.createDistributionSet().getId();
assertThatExceptionOfType(RSQLParameterSyntaxException.class).isThrownBy(() -> targetManagement
.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target, ds, "invalid_syntax"));
assertThatExceptionOfType(RSQLParameterUnsupportedFieldException.class).isThrownBy(() -> targetManagement
.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target, ds, "invalid_field==1"));
}
@Test
@Description("Target matches filter for not existing target.")
void matchesFilterTargetNotExists() {
final DistributionSet ds = testdataFactory.createDistributionSet();
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible("notExisting", ds.getId(),
"name==*")).isFalse();
}
@Test
@Description("Target matches filter for not existing DS.")
void matchesFilterDsNotExists() {
final String target = testdataFactory.createTarget().getControllerId();
assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(
() -> targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target, 123, "name==*"));
}
private void validateFoundTargetsByRsql(final String rsqlFilter, final String... controllerIds) {
final Slice<Target> foundTargetsByMetadataAndControllerId = targetManagement.findByRsql(PAGE, rsqlFilter);
final long foundTargetsByMetadataAndControllerIdCount = targetManagement.countByRsql(rsqlFilter);

View File

@@ -0,0 +1,411 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa.autoassign;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.ActionRepository;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Step;
import io.qameta.allure.Story;
/**
* Test class for {@link AutoAssignChecker}.
*
*/
@Feature("Component Tests - Repository")
@Story("Auto assign checker")
class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest {
@Autowired
private AutoAssignChecker autoAssignChecker;
@Autowired
private ActionRepository actionRepository;
@Test
@Description("Verifies that a running action is auto canceled by a AutoAssignment which assigns another distribution-set.")
void autoAssignDistributionSetAndAutoCloseOldActions() {
tenantConfigurationManagement
.addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, true);
try {
final String knownControllerId = "controller12345";
final DistributionSet firstDistributionSet = testdataFactory.createDistributionSet();
final DistributionSet secondDistributionSet = testdataFactory.createDistributionSet("second");
testdataFactory.createTarget(knownControllerId);
final DistributionSetAssignmentResult assignmentResult = assignDistributionSet(firstDistributionSet.getId(),
knownControllerId);
final Long manuallyAssignedActionId = getFirstAssignedActionId(assignmentResult);
// target filter query that matches all targets
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*"));
targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
.updateAutoAssign(targetFilterQuery.getId()).ds(secondDistributionSet.getId()));
// Run the check
autoAssignChecker.checkAllTargets();
// verify that manually created action is canceled and action
// created from AutoAssign is running
final List<Action> actionsByKnownTarget = deploymentManagement.findActionsByTarget(knownControllerId, PAGE)
.getContent();
// should be 2 actions, one manually and one from the AutoAssign
assertThat(actionsByKnownTarget).hasSize(2);
// verify that manually assigned action is still running
assertThat(deploymentManagement.findAction(manuallyAssignedActionId).get().getStatus())
.isEqualTo(Status.CANCELED);
// verify that AutoAssign created action is running
final Action rolloutCreatedAction = actionsByKnownTarget.stream()
.filter(action -> !action.getId().equals(manuallyAssignedActionId)).findAny().get();
assertThat(rolloutCreatedAction.getStatus()).isEqualTo(Status.RUNNING);
assertThat(rolloutCreatedAction.getActionType()).isEqualTo(ActionType.FORCED);
} finally {
tenantConfigurationManagement
.addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, false);
}
}
@Test
@Description("Test auto assignment of a DS to filtered targets")
void checkAutoAssign() {
final DistributionSet setA = testdataFactory.createDistributionSet("dsA"); // will
// be
// auto
// assigned
final DistributionSet setB = testdataFactory.createDistributionSet("dsB");
// target filter query that matches all targets
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement.updateAutoAssignDS(entityFactory
.targetFilterQuery()
.updateAutoAssign(targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*")).getId())
.ds(setA.getId()));
final String targetDsAIdPref = "targ";
final List<Target> targets = testdataFactory.createTargets(25, targetDsAIdPref,
targetDsAIdPref.concat(" description"));
final int targetsCount = targets.size();
// assign set A to first 10 targets
assignDistributionSet(setA, targets.subList(0, 10));
verifyThatTargetsHaveDistributionSetAssignment(setA, targets.subList(0, 10), targetsCount);
// assign set B to first 5 targets
// they have now 2 DS in their action history and should not get updated
// with dsA
assignDistributionSet(setB, targets.subList(0, 5));
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount);
// assign set B to next 10 targets
assignDistributionSet(setB, targets.subList(10, 20));
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(10, 20), targetsCount);
// Count the number of targets that will be assigned with setA
assertThat(targetManagement.countByRsqlAndNonDSAndCompatible(setA.getId(), targetFilterQuery.getQuery()))
.isEqualTo(15);
// Run the check
autoAssignChecker.checkAllTargets();
verifyThatTargetsHaveDistributionSetAssignment(setA, targets.subList(5, 25), targetsCount);
// first 5 should keep their dsB, because they already had the dsA once
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount);
verifyThatCreatedActionsAreInitiatedByCurrentUser(targetFilterQuery, setA, targets);
}
@Test
@Description("Test auto assignment of a DS for a specific device")
void checkAutoAssignmentForDevice() {
final DistributionSet toAssignDs = testdataFactory.createDistributionSet();
// target filter query that matches all targets
targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
.updateAutoAssign(targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*")).getId())
.ds(toAssignDs.getId()));
final List<Target> targets = testdataFactory.createTargets(25);
final int targetsCount = targets.size();
// Run the check
autoAssignChecker.checkSingleTarget(targets.get(0).getControllerId());
verifyThatTargetsHaveDistributionSetAssignment(toAssignDs, targets.subList(0, 1), targetsCount);
verifyThatTargetsNotHaveDistributionSetAssignment(toAssignDs, targets.subList(1, 25));
}
@Test
@Description("Test auto assignment of an incomplete DS to filtered targets, that causes failures")
void checkAutoAssignWithFailures() {
// incomplete distribution set that will be assigned
final DistributionSet setF = distributionSetManagement.create(entityFactory.distributionSet().create()
.name("dsA").version("1").type(testdataFactory.findOrCreateDefaultTestDsType()));
final DistributionSet setA = testdataFactory.createDistributionSet("dsA");
final DistributionSet setB = testdataFactory.createDistributionSet("dsB");
final String targetDsAIdPref = "targA";
final String targetDsFIdPref = "targB";
// target filter query that matches first bunch of targets, that should
// fail
assertThatExceptionOfType(IncompleteDistributionSetException.class).isThrownBy(() -> {
final Long filterId = targetFilterQueryManagement.create(
entityFactory.targetFilterQuery().create().name("filterA").query("id==" + targetDsFIdPref + "*"))
.getId();
targetFilterQueryManagement
.updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(setF.getId()));
});
// target filter query that matches failed bunch of targets
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("filterB")
.query("id==" + targetDsAIdPref + "*").autoAssignDistributionSet(setA.getId()));
final List<Target> targetsF = testdataFactory.createTargets(10, targetDsFIdPref,
targetDsFIdPref.concat(" description"));
final List<Target> targetsA = testdataFactory.createTargets(10, targetDsAIdPref,
targetDsAIdPref.concat(" description"));
final int targetsCount = targetsA.size() + targetsF.size();
// assign set B to first 5 targets of fail group
assignDistributionSet(setB, targetsF.subList(0, 5));
verifyThatTargetsHaveDistributionSetAssignment(setB, targetsF.subList(0, 5), targetsCount);
// Run the check
autoAssignChecker.checkAllTargets();
// first 5 targets of the fail group should still have setB
verifyThatTargetsHaveDistributionSetAssignment(setB, targetsF.subList(0, 5), targetsCount);
// all targets of A group should have received setA
verifyThatTargetsHaveDistributionSetAssignment(setA, targetsA, targetsCount);
}
/**
* @param set
* the expected distribution set
* @param targets
* the targets that should have it
*/
@Step
private void verifyThatTargetsHaveDistributionSetAssignment(final DistributionSet set, final List<Target> targets,
final int count) {
final List<Long> targetIds = targets.stream().map(Target::getId).collect(Collectors.toList());
final Slice<Target> targetsAll = targetManagement.findAll(PAGE);
assertThat(targetsAll).as("Count of targets").hasSize(count);
for (final Target target : targetsAll) {
if (targetIds.contains(target.getId())) {
assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).get())
.as("assigned DS").isEqualTo(set);
}
}
}
@Step
private void verifyThatTargetsNotHaveDistributionSetAssignment(final DistributionSet set,
final List<Target> targets) {
final List<Long> targetIds = targets.stream().map(Target::getId).collect(Collectors.toList());
final Slice<Target> targetsAll = targetManagement.findAll(PAGE);
for (final Target target : targetsAll) {
if (targetIds.contains(target.getId())) {
assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId())).isEmpty();
}
}
}
@Step
private void verifyThatCreatedActionsAreInitiatedByCurrentUser(final TargetFilterQuery targetFilterQuery,
final DistributionSet distributionSet, final List<Target> targets) {
final Set<String> targetIds = targets.stream().map(Target::getControllerId).collect(Collectors.toSet());
actionRepository.findByDistributionSetId(Pageable.unpaged(), distributionSet.getId()).stream()
.filter(a -> targetIds.contains(a.getTarget().getControllerId()))
.forEach(a -> assertThat(a.getInitiatedBy())
.as("Action should be initiated by the user who initiated the auto assignment")
.isEqualTo(targetFilterQuery.getAutoAssignInitiatedBy()));
}
@Test
@Description("Test auto assignment of a distribution set with FORCED, SOFT and DOWNLOAD_ONLY action types")
void checkAutoAssignWithDifferentActionTypes() {
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
final String targetDsAIdPref = "A";
final String targetDsBIdPref = "B";
final String targetDsCIdPref = "C";
final List<Target> targetsA = createTargetsAndAutoAssignDistSet(targetDsAIdPref, 5, distributionSet,
ActionType.FORCED);
final List<Target> targetsB = createTargetsAndAutoAssignDistSet(targetDsBIdPref, 10, distributionSet,
ActionType.SOFT);
final List<Target> targetsC = createTargetsAndAutoAssignDistSet(targetDsCIdPref, 10, distributionSet,
ActionType.DOWNLOAD_ONLY);
final int targetsCount = targetsA.size() + targetsB.size() + targetsC.size();
autoAssignChecker.checkAllTargets();
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsA, targetsCount);
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsB, targetsCount);
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsC, targetsCount);
verifyThatTargetsHaveAssignmentActionType(ActionType.FORCED, targetsA);
verifyThatTargetsHaveAssignmentActionType(ActionType.SOFT, targetsB);
verifyThatTargetsHaveAssignmentActionType(ActionType.DOWNLOAD_ONLY, targetsC);
}
@Step
private List<Target> createTargetsAndAutoAssignDistSet(final String prefix, final int targetCount,
final DistributionSet distributionSet, final ActionType actionType) {
final List<Target> targets = testdataFactory.createTargets(targetCount, "target" + prefix,
prefix.concat(" description"));
targetFilterQueryManagement.create(
entityFactory.targetFilterQuery().create().name("filter" + prefix).query("id==target" + prefix + "*")
.autoAssignDistributionSet(distributionSet).autoAssignActionType(actionType));
return targets;
}
@Step
private void verifyThatTargetsHaveAssignmentActionType(final ActionType actionType, final List<Target> targets) {
final List<Action> actions = targets.stream().map(Target::getControllerId).flatMap(
controllerId -> deploymentManagement.findActionsByTarget(controllerId, PAGE).getContent().stream())
.collect(Collectors.toList());
assertThat(actions).hasSize(targets.size());
assertThat(actions).allMatch(action -> action.getActionType().equals(actionType));
}
@Test
@Description("An auto assignment target filter with weight creates actions with weights")
void actionsWithWeightAreCreated() throws Exception {
final int amountOfTargets = 5;
final DistributionSet ds = testdataFactory.createDistributionSet();
final int weight = 32;
enableMultiAssignments();
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("a").query("name==*")
.autoAssignDistributionSet(ds).autoAssignWeight(weight));
testdataFactory.createTargets(amountOfTargets);
autoAssignChecker.checkAllTargets();
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
assertThat(actions).hasSize(amountOfTargets);
assertThat(actions).allMatch(action -> action.getWeight().get() == weight);
}
@Test
@Description("An auto assignment target filter without weight still works after multi assignment is enabled")
void filterWithoutWeightWorksInMultiAssignmentMode() throws Exception {
final int amountOfTargets = 5;
final DistributionSet ds = testdataFactory.createDistributionSet();
targetFilterQueryManagement.create(
entityFactory.targetFilterQuery().create().name("a").query("name==*").autoAssignDistributionSet(ds));
enableMultiAssignments();
testdataFactory.createTargets(amountOfTargets);
autoAssignChecker.checkAllTargets();
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
assertThat(actions).hasSize(amountOfTargets);
assertThat(actions).allMatch(action -> !action.getWeight().isPresent());
}
@Test
@Description("Verifies an auto assignment only creates actions for compatible targets")
void checkAutoAssignmentWithIncompatibleTargets() {
final int TARGET_COUNT = 5;
final DistributionSet testDs = testdataFactory.createDistributionSet();
final DistributionSetType incompatibleDsType1 = testdataFactory
.findOrCreateDistributionSetType("incompatibleDsType1", "incompDsType1");
final DistributionSetType incompatibleDsType2 = testdataFactory
.findOrCreateDistributionSetType("incompatibleDsType2", "incompDsType2");
final TargetFilterQuery testFilter = targetFilterQueryManagement.create(entityFactory.targetFilterQuery()
.create().name("test-filter").query("name==*").autoAssignDistributionSet(testDs));
final TargetType incompatibleEmptyType = testdataFactory.createTargetType("incompatibleEmptyType",
Collections.emptyList());
final TargetType incompatibleSingleType = testdataFactory.createTargetType("incompatibleSingleType",
Collections.singletonList(incompatibleDsType1));
final TargetType incompatibleMultiType = testdataFactory.createTargetType("incompatibleMultiType",
Arrays.asList(incompatibleDsType1, incompatibleDsType2));
final TargetType compatibleSingleType = testdataFactory.createTargetType("compatibleSingleType",
Collections.singletonList(testDs.getType()));
final TargetType compatibleMultiType = testdataFactory.createTargetType("compatibleMultiType",
Arrays.asList(testDs.getType(), incompatibleDsType1));
testdataFactory.createTargetsWithType(TARGET_COUNT, "incompatibleEmpty", incompatibleEmptyType);
testdataFactory.createTargetsWithType(TARGET_COUNT, "incompatibleSingle", incompatibleSingleType);
testdataFactory.createTargetsWithType(TARGET_COUNT, "incompatibleMulti", incompatibleMultiType);
final List<Target> compatibleTargetsSingleType = testdataFactory.createTargetsWithType(TARGET_COUNT,
"compatibleSingle", compatibleSingleType);
final List<Target> compatibleTargetsMultiType = testdataFactory.createTargetsWithType(TARGET_COUNT,
"compatibleMulti", compatibleMultiType);
final List<Target> compatibleTargetsWithoutType = testdataFactory.createTargets(TARGET_COUNT,
"compatibleSingleWithoutType");
final List<Long> compatibleTargets = Stream
.of(compatibleTargetsSingleType, compatibleTargetsMultiType, compatibleTargetsWithoutType)
.flatMap(Collection::stream).map(Target::getId).collect(Collectors.toList());
final long compatibleCount = targetManagement.countByRsqlAndNonDSAndCompatible(testDs.getId(),
testFilter.getQuery());
assertThat(compatibleCount).isEqualTo(compatibleTargets.size());
autoAssignChecker.checkAllTargets();
final List<Action> actions = deploymentManagement.findActionsAll(Pageable.unpaged()).getContent();
assertThat(actions).hasSize(compatibleTargets.size());
final List<Long> actionTargets = actions.stream().map(a -> a.getTarget().getId()).collect(Collectors.toList());
assertThat(actionTargets).containsExactlyInAnyOrderElementsOf(compatibleTargets);
}
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
* Copyright (c) 2022 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,366 +8,113 @@
*/
package org.eclipse.hawkbit.repository.jpa.autoassign;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.ActionRepository;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.domain.SliceImpl;
import org.springframework.transaction.PlatformTransactionManager;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Step;
import io.qameta.allure.Story;
/**
* Test class for {@link AutoAssignChecker}.
*
*/
@Feature("Component Tests - Repository")
@Feature("Unit Tests - Repository")
@Story("Auto assign checker")
class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
@ExtendWith(MockitoExtension.class)
class AutoAssignCheckerTest {
@Mock
private TargetFilterQueryManagement targetFilterQueryManagement;
@Mock
private TargetManagement targetManagement;
@Mock
private DeploymentManagement deploymentManagement;
@Mock
private PlatformTransactionManager transactionManager;
@Mock
private TenantAware tenantAware;
@Autowired
private AutoAssignChecker autoAssignChecker;
private AutoAssignChecker sut;
@Autowired
private ActionRepository actionRepository;
@Test
@Description("Verifies that a running action is auto canceled by a AutoAssignment which assigns another distribution-set.")
void autoAssignDistributionSetAndAutoCloseOldActions() {
tenantConfigurationManagement
.addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, true);
try {
final String knownControllerId = "controller12345";
final DistributionSet firstDistributionSet = testdataFactory.createDistributionSet();
final DistributionSet secondDistributionSet = testdataFactory.createDistributionSet("second");
testdataFactory.createTarget(knownControllerId);
final DistributionSetAssignmentResult assignmentResult = assignDistributionSet(firstDistributionSet.getId(),
knownControllerId);
final Long manuallyAssignedActionId = getFirstAssignedActionId(assignmentResult);
// target filter query that matches all targets
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*"));
targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
.updateAutoAssign(targetFilterQuery.getId()).ds(secondDistributionSet.getId()));
// Run the check
autoAssignChecker.check();
// verify that manually created action is canceled and action
// created from AutoAssign is running
final List<Action> actionsByKnownTarget = deploymentManagement.findActionsByTarget(knownControllerId, PAGE)
.getContent();
// should be 2 actions, one manually and one from the AutoAssign
assertThat(actionsByKnownTarget).hasSize(2);
// verify that manually assigned action is still running
assertThat(deploymentManagement.findAction(manuallyAssignedActionId).get().getStatus())
.isEqualTo(Status.CANCELED);
// verify that AutoAssign created action is running
final Action rolloutCreatedAction = actionsByKnownTarget.stream()
.filter(action -> !action.getId().equals(manuallyAssignedActionId)).findAny().get();
assertThat(rolloutCreatedAction.getStatus()).isEqualTo(Status.RUNNING);
assertThat(rolloutCreatedAction.getActionType()).isEqualTo(ActionType.FORCED);
} finally {
tenantConfigurationManagement
.addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, false);
}
@BeforeEach
void before() {
sut = new AutoAssignChecker(targetFilterQueryManagement, targetManagement, deploymentManagement,
transactionManager, tenantAware);
}
@Test
@Description("Test auto assignment of a DS to filtered targets")
void checkAutoAssign() {
@Description("Single device check triggers update for matching auto assignment filter.")
void checkForDevice() {
mockRunningAsNonSystem();
final String target = getRandomString();
final long ds = getRandomLong();
final TargetFilterQuery matching = mockFilterQuery(ds);
final TargetFilterQuery notMatching = mockFilterQuery(ds);
when(targetFilterQueryManagement.findWithAutoAssignDS(any()))
.thenReturn(new SliceImpl<>(Arrays.asList(notMatching, matching)));
final DistributionSet setA = testdataFactory.createDistributionSet("dsA"); // will
// be
// auto
// assigned
final DistributionSet setB = testdataFactory.createDistributionSet("dsB");
when(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target, ds, matching.getQuery()))
.thenReturn(true);
when(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target, ds,
notMatching.getQuery())).thenReturn(false);
// target filter query that matches all targets
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement.updateAutoAssignDS(entityFactory
.targetFilterQuery()
.updateAutoAssign(targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*")).getId())
.ds(setA.getId()));
sut.checkSingleTarget(target);
final String targetDsAIdPref = "targ";
final List<Target> targets = testdataFactory.createTargets(25, targetDsAIdPref,
targetDsAIdPref.concat(" description"));
final int targetsCount = targets.size();
// assign set A to first 10 targets
assignDistributionSet(setA, targets.subList(0, 10));
verifyThatTargetsHaveDistributionSetAssignment(setA, targets.subList(0, 10), targetsCount);
// assign set B to first 5 targets
// they have now 2 DS in their action history and should not get updated
// with dsA
assignDistributionSet(setB, targets.subList(0, 5));
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount);
// assign set B to next 10 targets
assignDistributionSet(setB, targets.subList(10, 20));
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(10, 20), targetsCount);
// Count the number of targets that will be assigned with setA
assertThat(targetManagement.countByRsqlAndNonDSAndCompatible(setA.getId(), targetFilterQuery.getQuery()))
.isEqualTo(15);
// Run the check
autoAssignChecker.check();
verifyThatTargetsHaveDistributionSetAssignment(setA, targets.subList(5, 25), targetsCount);
// first 5 should keep their dsB, because they already had the dsA once
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount);
verifyThatCreatedActionsAreInitiatedByCurrentUser(targetFilterQuery, setA, targets);
verify(deploymentManagement).assignDistributionSets(eq(matching.getAutoAssignInitiatedBy()),
Mockito.argThat(deployReqMatcher(target, ds)), any());
Mockito.verifyNoMoreInteractions(deploymentManagement);
}
@Test
@Description("Test auto assignment of an incomplete DS to filtered targets, that causes failures")
void checkAutoAssignWithFailures() {
// incomplete distribution set that will be assigned
final DistributionSet setF = distributionSetManagement.create(entityFactory.distributionSet().create()
.name("dsA").version("1").type(testdataFactory.findOrCreateDefaultTestDsType()));
final DistributionSet setA = testdataFactory.createDistributionSet("dsA");
final DistributionSet setB = testdataFactory.createDistributionSet("dsB");
final String targetDsAIdPref = "targA";
final String targetDsFIdPref = "targB";
// target filter query that matches first bunch of targets, that should
// fail
assertThatExceptionOfType(IncompleteDistributionSetException.class).isThrownBy(() -> {
final Long filterId = targetFilterQueryManagement.create(
entityFactory.targetFilterQuery().create().name("filterA").query("id==" + targetDsFIdPref + "*"))
.getId();
targetFilterQueryManagement
.updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(setF.getId()));
});
// target filter query that matches failed bunch of targets
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("filterB")
.query("id==" + targetDsAIdPref + "*").autoAssignDistributionSet(setA.getId()));
final List<Target> targetsF = testdataFactory.createTargets(10, targetDsFIdPref,
targetDsFIdPref.concat(" description"));
final List<Target> targetsA = testdataFactory.createTargets(10, targetDsAIdPref,
targetDsAIdPref.concat(" description"));
final int targetsCount = targetsA.size() + targetsF.size();
// assign set B to first 5 targets of fail group
assignDistributionSet(setB, targetsF.subList(0, 5));
verifyThatTargetsHaveDistributionSetAssignment(setB, targetsF.subList(0, 5), targetsCount);
// Run the check
autoAssignChecker.check();
// first 5 targets of the fail group should still have setB
verifyThatTargetsHaveDistributionSetAssignment(setB, targetsF.subList(0, 5), targetsCount);
// all targets of A group should have received setA
verifyThatTargetsHaveDistributionSetAssignment(setA, targetsA, targetsCount);
private ArgumentMatcher<List<DeploymentRequest>> deployReqMatcher(final String target, final long ds) {
return requests -> {
final DeploymentRequest request = requests.get(0);
return requests.size() == 1 && request.getDistributionSetId() == ds && request.getControllerId() == target;
};
}
/**
* @param set
* the expected distribution set
* @param targets
* the targets that should have it
*/
@Step
private void verifyThatTargetsHaveDistributionSetAssignment(final DistributionSet set, final List<Target> targets,
final int count) {
final List<Long> targetIds = targets.stream().map(Target::getId).collect(Collectors.toList());
final Slice<Target> targetsAll = targetManagement.findAll(PAGE);
assertThat(targetsAll).as("Count of targets").hasSize(count);
for (final Target target : targetsAll) {
if (targetIds.contains(target.getId())) {
assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).get())
.as("assigned DS").isEqualTo(set);
}
}
private static TargetFilterQuery mockFilterQuery(final long dsId) {
final DistributionSet ds = mock(DistributionSet.class);
when(ds.getId()).thenReturn(dsId);
final TargetFilterQuery filter = mock(TargetFilterQuery.class);
when(filter.getId()).thenReturn(getRandomLong());
when(filter.getQuery()).thenReturn(getRandomString());
lenient().when(filter.getAutoAssignInitiatedBy()).thenReturn(getRandomString());
when(filter.getAutoAssignDistributionSet()).thenReturn(ds);
return filter;
}
@Step
private void verifyThatCreatedActionsAreInitiatedByCurrentUser(final TargetFilterQuery targetFilterQuery,
final DistributionSet distributionSet, final List<Target> targets) {
final Set<String> targetIds = targets.stream().map(Target::getControllerId).collect(Collectors.toSet());
actionRepository.findByDistributionSetId(Pageable.unpaged(), distributionSet.getId()).stream()
.filter(a -> targetIds.contains(a.getTarget().getControllerId()))
.forEach(a -> assertThat(a.getInitiatedBy())
.as("Action should be initiated by the user who initiated the auto assignment")
.isEqualTo(targetFilterQuery.getAutoAssignInitiatedBy()));
private void mockRunningAsNonSystem() {
when(tenantAware.getCurrentUsername()).thenReturn(getRandomString());
}
@Test
@Description("Test auto assignment of a distribution set with FORCED, SOFT and DOWNLOAD_ONLY action types")
void checkAutoAssignWithDifferentActionTypes() {
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
final String targetDsAIdPref = "A";
final String targetDsBIdPref = "B";
final String targetDsCIdPref = "C";
final List<Target> targetsA = createTargetsAndAutoAssignDistSet(targetDsAIdPref, 5, distributionSet,
ActionType.FORCED);
final List<Target> targetsB = createTargetsAndAutoAssignDistSet(targetDsBIdPref, 10, distributionSet,
ActionType.SOFT);
final List<Target> targetsC = createTargetsAndAutoAssignDistSet(targetDsCIdPref, 10, distributionSet,
ActionType.DOWNLOAD_ONLY);
final int targetsCount = targetsA.size() + targetsB.size() + targetsC.size();
autoAssignChecker.check();
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsA, targetsCount);
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsB, targetsCount);
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsC, targetsCount);
verifyThatTargetsHaveAssignmentActionType(ActionType.FORCED, targetsA);
verifyThatTargetsHaveAssignmentActionType(ActionType.SOFT, targetsB);
verifyThatTargetsHaveAssignmentActionType(ActionType.DOWNLOAD_ONLY, targetsC);
private static long getRandomLong() {
return ThreadLocalRandom.current().nextLong();
}
@Step
private List<Target> createTargetsAndAutoAssignDistSet(final String prefix, final int targetCount,
final DistributionSet distributionSet, final ActionType actionType) {
final List<Target> targets = testdataFactory.createTargets(targetCount, "target" + prefix,
prefix.concat(" description"));
targetFilterQueryManagement.create(
entityFactory.targetFilterQuery().create().name("filter" + prefix).query("id==target" + prefix + "*")
.autoAssignDistributionSet(distributionSet).autoAssignActionType(actionType));
return targets;
private static String getRandomString() {
return UUID.randomUUID().toString();
}
@Step
private void verifyThatTargetsHaveAssignmentActionType(final ActionType actionType, final List<Target> targets) {
final List<Action> actions = targets.stream().map(Target::getControllerId).flatMap(
controllerId -> deploymentManagement.findActionsByTarget(controllerId, PAGE).getContent().stream())
.collect(Collectors.toList());
assertThat(actions).hasSize(targets.size());
assertThat(actions).allMatch(action -> action.getActionType().equals(actionType));
}
@Test
@Description("An auto assignment target filter with weight creates actions with weights")
void actionsWithWeightAreCreated() throws Exception {
final int amountOfTargets = 5;
final DistributionSet ds = testdataFactory.createDistributionSet();
final int weight = 32;
enableMultiAssignments();
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("a").query("name==*")
.autoAssignDistributionSet(ds).autoAssignWeight(weight));
testdataFactory.createTargets(amountOfTargets);
autoAssignChecker.check();
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
assertThat(actions).hasSize(amountOfTargets);
assertThat(actions).allMatch(action -> action.getWeight().get() == weight);
}
@Test
@Description("An auto assignment target filter without weight still works after multi assignment is enabled")
void filterWithoutWeightWorksInMultiAssignmentMode() throws Exception {
final int amountOfTargets = 5;
final DistributionSet ds = testdataFactory.createDistributionSet();
targetFilterQueryManagement.create(
entityFactory.targetFilterQuery().create().name("a").query("name==*").autoAssignDistributionSet(ds));
enableMultiAssignments();
testdataFactory.createTargets(amountOfTargets);
autoAssignChecker.check();
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
assertThat(actions).hasSize(amountOfTargets);
assertThat(actions).allMatch(action -> !action.getWeight().isPresent());
}
@Test
@Description("Verifies an auto assignment only creates actions for compatible targets")
void checkAutoAssignmentWithIncompatibleTargets() {
final int TARGET_COUNT = 5;
final DistributionSet testDs = testdataFactory.createDistributionSet();
final DistributionSetType incompatibleDsType1 = testdataFactory
.findOrCreateDistributionSetType("incompatibleDsType1", "incompDsType1");
final DistributionSetType incompatibleDsType2 = testdataFactory
.findOrCreateDistributionSetType("incompatibleDsType2", "incompDsType2");
final TargetFilterQuery testFilter = targetFilterQueryManagement.create(entityFactory.targetFilterQuery()
.create().name("test-filter").query("name==*").autoAssignDistributionSet(testDs));
final TargetType incompatibleEmptyType = testdataFactory.createTargetType("incompatibleEmptyType",
Collections.emptyList());
final TargetType incompatibleSingleType = testdataFactory.createTargetType("incompatibleSingleType",
Collections.singletonList(incompatibleDsType1));
final TargetType incompatibleMultiType = testdataFactory.createTargetType("incompatibleMultiType",
Arrays.asList(incompatibleDsType1, incompatibleDsType2));
final TargetType compatibleSingleType = testdataFactory.createTargetType("compatibleSingleType",
Collections.singletonList(testDs.getType()));
final TargetType compatibleMultiType = testdataFactory.createTargetType("compatibleMultiType",
Arrays.asList(testDs.getType(), incompatibleDsType1));
testdataFactory.createTargetsWithType(TARGET_COUNT, "incompatibleEmpty", incompatibleEmptyType);
testdataFactory.createTargetsWithType(TARGET_COUNT, "incompatibleSingle", incompatibleSingleType);
testdataFactory.createTargetsWithType(TARGET_COUNT, "incompatibleMulti", incompatibleMultiType);
final List<Target> compatibleTargetsSingleType = testdataFactory.createTargetsWithType(TARGET_COUNT,
"compatibleSingle", compatibleSingleType);
final List<Target> compatibleTargetsMultiType = testdataFactory.createTargetsWithType(TARGET_COUNT,
"compatibleMulti", compatibleMultiType);
final List<Target> compatibleTargetsWithoutType = testdataFactory.createTargets(TARGET_COUNT,
"compatibleSingleWithoutType");
final List<Long> compatibleTargets = Stream
.of(compatibleTargetsSingleType, compatibleTargetsMultiType, compatibleTargetsWithoutType)
.flatMap(Collection::stream).map(Target::getId).collect(Collectors.toList());
final long compatibleCount = targetManagement.countByRsqlAndNonDSAndCompatible(testDs.getId(),
testFilter.getQuery());
assertThat(compatibleCount).isEqualTo(compatibleTargets.size());
autoAssignChecker.check();
final List<Action> actions = deploymentManagement.findActionsAll(Pageable.unpaged()).getContent();
assertThat(actions).hasSize(compatibleTargets.size());
final List<Long> actionTargets = actions.stream().map(a -> a.getTarget().getId()).collect(Collectors.toList());
assertThat(actionTargets).containsExactlyInAnyOrderElementsOf(compatibleTargets);
}
}