Introduce Stop of a rollout (#2595)
* Stop of a rollout feature Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * remove some test comments Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * make stop transactional Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * attempt to fix hibernate failed tests Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * fix some sonar issues Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * changes after review Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * fix build Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * fixes after review Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * finish all rollout groups on deletion of rollout Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * refactor finishing groups Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * fix RolloutManagementTest Signed-off-by: strailov <Stanislav.Trailov@bosch.io> --------- Signed-off-by: strailov <Stanislav.Trailov@bosch.io>
This commit is contained in:
committed by
GitHub
parent
4566702030
commit
45cd012532
@@ -25,9 +25,9 @@ import org.eclipse.hawkbit.repository.exception.StopRolloutException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.ActionCancellationType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorAction;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorCondition;
|
||||
@@ -78,7 +78,7 @@ class ConcurrentDistributionSetInvalidationTest extends AbstractJpaIntegrationTe
|
||||
() -> rolloutGroupManagement.findByRollout(rollout.getId(), PAGE).getSize() > 0)));
|
||||
|
||||
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
|
||||
Collections.singletonList(distributionSet.getId()), CancelationType.SOFT, true);
|
||||
Collections.singletonList(distributionSet.getId()), ActionCancellationType.SOFT);
|
||||
assertThatExceptionOfType(StopRolloutException.class)
|
||||
.as("Invalidation of distributionSet should throw an exception")
|
||||
.isThrownBy(() -> distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation));
|
||||
|
||||
@@ -17,8 +17,8 @@ import org.eclipse.hawkbit.im.authentication.SpRole;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.ActionCancellationType;
|
||||
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
@@ -294,7 +294,7 @@ class DeploymentManagementSecurityTest extends AbstractJpaIntegrationTest {
|
||||
void cancelActionsForDistributionSetPermissionsCheck() {
|
||||
assertPermissions(() -> {
|
||||
deploymentManagement.cancelActionsForDistributionSet(
|
||||
DistributionSetInvalidation.CancelationType.FORCE, new JpaDistributionSet());
|
||||
ActionCancellationType.FORCE, new JpaDistributionSet());
|
||||
return null;
|
||||
}, List.of(SpPermission.UPDATE_TARGET));
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||
@@ -22,13 +23,12 @@ import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionCancellationType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidationCount;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
@@ -43,6 +43,7 @@ import org.springframework.data.repository.query.Param;
|
||||
* Feature: Component Tests - Repository<br/>
|
||||
* Story: Distribution set invalidation management
|
||||
*/
|
||||
@Slf4j
|
||||
class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
/**
|
||||
@@ -53,8 +54,7 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
|
||||
final InvalidationTestData invalidationTestData = createInvalidationTestData("verifyInvalidateDistributionSetStopAutoAssignment");
|
||||
|
||||
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), CancelationType.NONE,
|
||||
false);
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.NONE);
|
||||
final DistributionSetInvalidationCount distributionSetInvalidationCount = distributionSetInvalidationManagement
|
||||
.countEntitiesForInvalidation(distributionSetInvalidation);
|
||||
assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 0, 0);
|
||||
@@ -76,19 +76,18 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify invalidation of distribution sets that removes distribution sets from auto assignments and stops rollouts
|
||||
* Verify invalidation of distribution sets that removes distribution sets from auto assignments but does not stop rollouts
|
||||
*/
|
||||
@Test
|
||||
void verifyInvalidateDistributionSetStopRollouts() {
|
||||
void verifyInvalidateDistributionSetDoesNotStopRollouts() {
|
||||
final InvalidationTestData invalidationTestData = createInvalidationTestData(
|
||||
"verifyInvalidateDistributionSetStopRollouts");
|
||||
|
||||
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), CancelationType.NONE,
|
||||
true);
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.NONE);
|
||||
final DistributionSetInvalidationCount distributionSetInvalidationCount = distributionSetInvalidationManagement
|
||||
.countEntitiesForInvalidation(distributionSetInvalidation);
|
||||
assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 0, 1);
|
||||
assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 0, 0);
|
||||
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation);
|
||||
rolloutHandler.handleAll();
|
||||
@@ -96,9 +95,9 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
|
||||
assertThat(targetFilterQueryManagement.get(invalidationTestData.getTargetFilterQuery().getId()).get()
|
||||
.getAutoAssignDistributionSet()).isNull();
|
||||
assertThat(rolloutRepository.findById(invalidationTestData.getRollout().getId()).get().getStatus())
|
||||
.isEqualTo(RolloutStatus.FINISHED);
|
||||
.isEqualTo(RolloutStatus.READY);
|
||||
|
||||
assertNoScheduledActionsExist(invalidationTestData.getRollout());
|
||||
assertRolloutGroupsAreFinished(invalidationTestData.getRollout());
|
||||
for (final Target target : invalidationTestData.getTargets()) {
|
||||
// if status is pending, the assignment has not been canceled
|
||||
assertThat(
|
||||
@@ -118,8 +117,7 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
|
||||
"verifyInvalidateDistributionSetStopAllAndForceCancel");
|
||||
|
||||
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), CancelationType.FORCE,
|
||||
true);
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.FORCE);
|
||||
final DistributionSetInvalidationCount distributionSetInvalidationCount = distributionSetInvalidationManagement
|
||||
.countEntitiesForInvalidation(distributionSetInvalidation);
|
||||
assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 5, 1);
|
||||
@@ -129,16 +127,10 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
|
||||
|
||||
assertThat(targetFilterQueryManagement.get(invalidationTestData.getTargetFilterQuery().getId()).get()
|
||||
.getAutoAssignDistributionSet()).isNull();
|
||||
assertThat(rolloutRepository.findById(invalidationTestData.getRollout().getId()).get().getStatus())
|
||||
.isEqualTo(RolloutStatus.FINISHED);
|
||||
// rollout should be deleted when force invalidation
|
||||
assertThat(rolloutRepository.findById(invalidationTestData.getRollout().getId())).isEmpty();
|
||||
assertNoScheduledActionsExist(invalidationTestData.getRollout());
|
||||
assertRolloutGroupsAreFinished(invalidationTestData.getRollout());
|
||||
for (final Target target : invalidationTestData.getTargets()) {
|
||||
assertThat(targetRepository.findById(target.getId()).get().getUpdateStatus())
|
||||
.isEqualTo(TargetUpdateStatus.IN_SYNC);
|
||||
assertThat(findActionsByTarget(target)).hasSize(1);
|
||||
assertThat(findActionsByTarget(target).get(0).getStatus()).isEqualTo(Status.CANCELED);
|
||||
}
|
||||
assertAllRolloutActionsAreCancelled(invalidationTestData.getRollout());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,7 +141,7 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
|
||||
final InvalidationTestData invalidationTestData = createInvalidationTestData("verifyInvalidateDistributionSetStopAll");
|
||||
|
||||
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), CancelationType.SOFT,true);
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.SOFT);
|
||||
final DistributionSetInvalidationCount distributionSetInvalidationCount = distributionSetInvalidationManagement
|
||||
.countEntitiesForInvalidation(distributionSetInvalidation);
|
||||
assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 5, 1);
|
||||
@@ -176,7 +168,7 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
|
||||
final DistributionSet distributionSet = testdataFactory.createIncompleteDistributionSet();
|
||||
|
||||
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
|
||||
List.of(distributionSet.getId()), CancelationType.SOFT, true);
|
||||
List.of(distributionSet.getId()), ActionCancellationType.SOFT);
|
||||
assertThatExceptionOfType(IncompleteDistributionSetException.class)
|
||||
.as("Incomplete distributionSet should throw an exception")
|
||||
.isThrownBy(() -> distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation));
|
||||
@@ -193,7 +185,7 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
|
||||
final DistributionSet distributionSet = testdataFactory.createAndInvalidateDistributionSet();
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(
|
||||
new DistributionSetInvalidation(Collections.singletonList(distributionSet.getId()),
|
||||
CancelationType.SOFT, true));
|
||||
ActionCancellationType.SOFT));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,8 +198,7 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
|
||||
.runAsSystem(() -> createInvalidationTestData("verifyInvalidateWithUpdateRepoAuthority"));
|
||||
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(new DistributionSetInvalidation(
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), CancelationType.NONE,
|
||||
false));
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.NONE));
|
||||
assertThat(
|
||||
distributionSetRepository.findById(invalidationTestData.getDistributionSet().getId()).get().isValid())
|
||||
.isFalse();
|
||||
@@ -223,14 +214,13 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
|
||||
() -> createInvalidationTestData("verifyInvalidateWithUpdateRepoAndUpdateTargetAuthority"));
|
||||
|
||||
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
|
||||
List.of(invalidationTestData.getDistributionSet().getId()), CancelationType.SOFT, true);
|
||||
List.of(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.SOFT);
|
||||
assertThatExceptionOfType(InsufficientPermissionException.class)
|
||||
.as("Insufficient permission exception expected")
|
||||
.isThrownBy(() -> distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation));
|
||||
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(new DistributionSetInvalidation(
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), CancelationType.NONE,
|
||||
false));
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.NONE));
|
||||
assertThat(
|
||||
distributionSetRepository.findById(invalidationTestData.getDistributionSet().getId()).get().isValid())
|
||||
.isFalse();
|
||||
@@ -246,8 +236,7 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
|
||||
() -> createInvalidationTestData("verifyInvalidateWithUpdateRepoAndUpdateTargetAuthority"));
|
||||
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(new DistributionSetInvalidation(
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), CancelationType.SOFT,
|
||||
true));
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), ActionCancellationType.SOFT));
|
||||
assertThat(
|
||||
distributionSetRepository.findById(invalidationTestData.getDistributionSet().getId()).get().isValid())
|
||||
.isFalse();
|
||||
@@ -259,10 +248,10 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe
|
||||
.isZero();
|
||||
}
|
||||
|
||||
private void assertRolloutGroupsAreFinished(final Rollout rollout) {
|
||||
assertThat(rolloutGroupRepository.findByRolloutId(rollout.getId(), PAGE))
|
||||
.isNotEmpty()
|
||||
.allMatch(rolloutGroup -> rolloutGroup.getStatus().equals(RolloutGroupStatus.FINISHED));
|
||||
private void assertAllRolloutActionsAreCancelled(final Rollout rollout) {
|
||||
assertThat(
|
||||
actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), Status.CANCELED).getTotalElements())
|
||||
.isZero();
|
||||
}
|
||||
|
||||
private InvalidationTestData createInvalidationTestData(final String testName) {
|
||||
|
||||
@@ -50,11 +50,11 @@ import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThis
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionCancellationType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetFilter.DistributionSetFilterBuilder;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
@@ -482,7 +482,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Long softwareModuleId = testdataFactory.createSoftwareModuleOs().getId();
|
||||
distributionSetManagement.assignSoftwareModules(distributionSetId, singletonList(softwareModuleId));
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(
|
||||
new DistributionSetInvalidation(singletonList(distributionSetId), CancelationType.NONE, false));
|
||||
new DistributionSetInvalidation(singletonList(distributionSetId), ActionCancellationType.NONE));
|
||||
|
||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||
.as("Invalid distributionSet should throw an exception").isThrownBy(() -> distributionSetManagement
|
||||
@@ -876,7 +876,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
||||
distributionSetManagement.createMetadata(dsId, Map.of(knownKey1, knownValue));
|
||||
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(
|
||||
new DistributionSetInvalidation(singletonList(dsId), CancelationType.NONE, false));
|
||||
new DistributionSetInvalidation(singletonList(dsId), ActionCancellationType.NONE));
|
||||
|
||||
// assert that no new metadata can be created
|
||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||
|
||||
@@ -20,7 +20,9 @@ import org.eclipse.hawkbit.repository.RolloutManagement.Create;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement.GroupCreate;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement.Update;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.model.ActionCancellationType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
|
||||
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
|
||||
@@ -113,7 +115,7 @@ class RolloutManagementSecurityTest extends AbstractJpaIntegrationTest {
|
||||
DistributionSetManagement.Create.builder().type(defaultDsType()).name("name").version("1.0.0").build();
|
||||
final DistributionSet ds = distributionSetManagement.create(dsCreate);
|
||||
assertPermissions(() -> {
|
||||
rolloutManagement.cancelRolloutsForDistributionSet(ds);
|
||||
rolloutManagement.cancelRolloutsForDistributionSet(ds, ActionCancellationType.SOFT);
|
||||
return null;
|
||||
}, List.of(SpPermission.UPDATE_ROLLOUT, SpPermission.READ_REPOSITORY, SpPermission.CREATE_REPOSITORY));
|
||||
}
|
||||
|
||||
@@ -1914,11 +1914,11 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 2),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = ActionCreatedEvent.class, count = 10),
|
||||
@Expect(type = ActionUpdatedEvent.class, count = 2),
|
||||
@Expect(type = ActionUpdatedEvent.class, count = 4),
|
||||
@Expect(type = RolloutCreatedEvent.class, count = 1),
|
||||
@Expect(type = RolloutUpdatedEvent.class, count = 6),
|
||||
@Expect(type = RolloutDeletedEvent.class, count = 1),
|
||||
@Expect(type = RolloutGroupUpdatedEvent.class, count = 11),
|
||||
@Expect(type = RolloutGroupUpdatedEvent.class, count = 16),
|
||||
@Expect(type = RolloutGroupCreatedEvent.class, count = 5) })
|
||||
void deleteRolloutWhichHasBeenStartedBeforeIsSoftDeleted() {
|
||||
final int amountTargetsForRollout = 10;
|
||||
@@ -1962,8 +1962,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
// verify that all scheduled actions are deleted
|
||||
assertThat(actionRepository.findByRolloutIdAndStatus(PAGE, deletedRollout.getId(), Status.SCHEDULED)
|
||||
.getNumberOfElements()).isZero();
|
||||
// verify that all running actions keep running
|
||||
assertThat(actionRepository.findByRolloutIdAndStatus(PAGE, deletedRollout.getId(), Status.RUNNING)
|
||||
// verify that all running actions are force cancelled
|
||||
assertThat(actionRepository.findByRolloutIdAndStatus(PAGE, deletedRollout.getId(), Status.CANCELED)
|
||||
.getNumberOfElements()).isEqualTo(2);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user