diff --git a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java index 20ffacc90..d293afcd6 100644 --- a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java +++ b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java @@ -41,7 +41,6 @@ import org.eclipse.hawkbit.repository.test.util.WithUser; import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB; import org.junit.Before; import org.junit.Test; -import org.slf4j.LoggerFactory; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.event.EventListener; @@ -67,10 +66,6 @@ public class DdiArtifactDownloadTest extends AbstractRestIntegrationTestWithMong private static final int ARTIFACT_SIZE = 5 * 1024 * 1024; - public DdiArtifactDownloadTest() { - LOG = LoggerFactory.getLogger(DdiArtifactDownloadTest.class); - } - private volatile static int downLoadProgress = 0; private volatile static long shippedBytes = 0; diff --git a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java index dbb84b87a..843b26479 100644 --- a/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java +++ b/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java @@ -27,10 +27,15 @@ import java.util.ArrayList; import java.util.List; import org.eclipse.hawkbit.im.authentication.SpPermission; +import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; +import org.eclipse.hawkbit.repository.test.matcher.Expect; +import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents; import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule; import org.eclipse.hawkbit.repository.test.util.WithUser; import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB; @@ -41,7 +46,6 @@ import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationKey; import org.eclipse.hawkbit.util.IpUtil; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.hateoas.MediaTypes; import org.springframework.http.MediaType; import ru.yandex.qatools.allure.annotations.Description; @@ -62,6 +66,8 @@ public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoD @Description("Ensures that targets cannot be created e.g. in plug'n play scenarios when tenant does not exists but can be created if the tenant exists.") @WithUser(tenantId = "tenantDoesNotExists", allSpPermissions = true, authorities = { CONTROLLER_ROLE, SYSTEM_ROLE }, autoCreateTenant = false) + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), + @Expect(type = TargetDeletedEvent.class, count = 1) }) public void targetCannotBeRegisteredIfTenantDoesNotExistsButWhenExists() throws Exception { mvc.perform(get("/default-tenant/", tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print()) @@ -73,7 +79,7 @@ public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoD mvc.perform(get("/{}/controller/v1/aControllerId", tenantAware.getCurrentTenant())) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); - // delete tenant again + // delete tenant again, will also deleted target aControllerId systemManagement.deleteTenant("tenantDoesNotExists"); mvc.perform(get("/{}/controller/v1/aControllerId", tenantAware.getCurrentTenant())) @@ -84,6 +90,8 @@ public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoD @Description("Ensures that target poll request does not change audit data on the entity.") @WithUser(principal = "knownPrincipal", authorities = { SpPermission.READ_TARGET, SpPermission.UPDATE_TARGET, SpPermission.CREATE_TARGET }, allSpPermissions = false) + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), + @Expect(type = TargetUpdatedEvent.class, count = 1) }) public void targetPollDoesNotModifyAuditData() throws Exception { // create target first with "knownPrincipal" user and audit data final String knownTargetControllerId = "target1"; @@ -112,12 +120,14 @@ public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoD @Test @Description("Ensures that server returns a not found response in case of empty controlloer ID.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) }) public void rootRsWithoutId() throws Exception { mvc.perform(get("/controller/v1/")).andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound()); } @Test @Description("Ensures that the system creates a new target in plug and play manner, i.e. target is authenticated but does not exist yet.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) }) public void rootRsPlugAndPlay() throws Exception { final long current = System.currentTimeMillis(); @@ -144,6 +154,7 @@ public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoD @Test @Description("Ensures that tenant specific polling time, which is saved in the db, is delivered to the controller.") @WithUser(principal = "knownpricipal", allSpPermissions = false) + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) }) public void pollWithModifiedGloablPollingTime() throws Exception { securityRule.runAs(WithSpringAuthorityRule.withUser("tenantadmin", HAS_AUTH_TENANT_CONFIGURATION), () -> { tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.POLLING_TIME_INTERVAL, @@ -226,6 +237,8 @@ public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoD @Test @Description("Ensures that the target state machine of a precomissioned target switches from " + "UNKNOWN to REGISTERED when the target polls for the first time.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), + @Expect(type = TargetUpdatedEvent.class, count = 1) }) public void rootRsPrecommissioned() throws Exception { final Target target = entityFactory.generateTarget("4711"); targetManagement.createTarget(target); @@ -250,6 +263,7 @@ public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoD @Test @Description("Ensures that the source IP address of the polling target is correctly stored in repository") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) }) public void rootRsPlugAndPlayIpAddress() throws Exception { // test final String knownControllerId1 = "0815"; @@ -264,6 +278,7 @@ public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoD @Test @Description("Ensures that the source IP address of the polling target is not stored in repository if disabled") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) }) public void rootRsIpAddressNotStoredIfDisabled() throws Exception { securityProperties.getClients().setTrackRemoteIp(false); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java index 2212fabfa..f2956a27c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java @@ -392,7 +392,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { /** * {@link EventEntityManagerHolder} bean. - * + * * @return a new {@link EventEntityManagerHolder} */ @Bean @@ -403,7 +403,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { /** * {@link EventEntityManager} bean. - * + * * @param aware * the tenant aware * @param entityManager @@ -458,5 +458,4 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { final AutoAssignChecker autoAssignChecker) { return new AutoAssignScheduler(tenantAware, systemManagement, systemSecurityContext, autoAssignChecker); } - } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java index a8bdd5fa7..63bffc0bd 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ArtifactManagementTest.java @@ -28,7 +28,6 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.test.util.HashGeneratorUtils; import org.eclipse.hawkbit.repository.test.util.WithUser; import org.junit.Test; -import org.slf4j.LoggerFactory; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; @@ -46,9 +45,6 @@ import ru.yandex.qatools.allure.annotations.Stories; @Features("Component Tests - Repository") @Stories("Artifact Management") public class ArtifactManagementTest extends AbstractJpaIntegrationTestWithMongoDB { - public ArtifactManagementTest() { - LOG = LoggerFactory.getLogger(ArtifactManagementTest.class); - } /** * Test method for diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TagManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TagManagementTest.java index 53720d378..a1511a0ae 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TagManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TagManagementTest.java @@ -32,7 +32,6 @@ import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult; import org.junit.Before; import org.junit.Test; -import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; @@ -47,9 +46,6 @@ import ru.yandex.qatools.allure.annotations.Stories; @Features("Component Tests - Repository") @Stories("Tag Management") public class TagManagementTest extends AbstractJpaIntegrationTest { - public TagManagementTest() { - LOG = LoggerFactory.getLogger(TagManagementTest.class); - } @Before public void setup() { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java index b509ab51f..4188ebfc5 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java @@ -8,6 +8,10 @@ */ package org.eclipse.hawkbit.repository.jpa; +import static com.google.common.collect.Iterables.limit; +import static com.google.common.collect.Iterables.toArray; +import static com.google.common.collect.Lists.newArrayList; +import static java.util.stream.Collectors.toList; import static org.fest.assertions.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -29,6 +33,14 @@ import javax.persistence.Query; import javax.validation.ConstraintViolationException; import org.eclipse.hawkbit.im.authentication.SpPermission; +import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; +import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.TenantNotExistException; import org.eclipse.hawkbit.repository.jpa.model.JpaAction; @@ -43,6 +55,8 @@ import org.eclipse.hawkbit.repository.model.Tag; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetIdName; import org.eclipse.hawkbit.repository.model.TargetTag; +import org.eclipse.hawkbit.repository.test.matcher.Expect; +import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents; import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule; import org.eclipse.hawkbit.repository.test.util.WithUser; import org.junit.Test; @@ -60,6 +74,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Ensures that retrieving the target security is only permitted with the necessary permissions.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) }) public void getTargetSecurityTokenOnlyWithCorrectPermission() throws Exception { final Target createdTarget = targetManagement.createTarget(new JpaTarget("targetWithSecurityToken", "token")); @@ -85,12 +100,12 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { assertThat(securityTokenAsSystemCode).isNotNull(); assertThat(securityTokenWithoutPermission).isNull(); - } @Test @Description("Ensures that targets cannot be created e.g. in plug'n play scenarios when tenant does not exists.") @WithUser(tenantId = "tenantWhichDoesNotExists", allSpPermissions = true, autoCreateTenant = false) + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) }) public void createTargetForTenantWhichDoesNotExistThrowsTenantNotExistException() { try { targetManagement.createTarget(new JpaTarget("targetId123")); @@ -102,6 +117,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that a target with empty controller id cannot be created") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) }) public void createTargetWithNoControllerId() { try { targetManagement.createTarget(new JpaTarget("")); @@ -120,6 +136,9 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Ensures that targets can assigned and unassigned to a target tag. Not exists target will be ignored for the assignment.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 4), + @Expect(type = TargetTagCreatedEvent.class, count = 1), + @Expect(type = TargetUpdatedEvent.class, count = 8) }) public void assignAndUnassignTargetsToTag() { final List assignTarget = new ArrayList(); assignTarget.add(targetManagement.createTarget(new JpaTarget("targetId123")).getControllerId()); @@ -156,6 +175,8 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Ensures that targets can deleted e.g. test all cascades") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 12), + @Expect(type = TargetDeletedEvent.class, count = 12), @Expect(type = TargetUpdatedEvent.class, count = 6) }) public void deleteAndCreateTargets() { Target target = targetManagement.createTarget(new JpaTarget("targetId123")); assertThat(targetManagement.countTargetsAll()).as("target count is wrong").isEqualTo(1); @@ -167,7 +188,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { targetManagement.deleteTargets(target.getId()); assertThat(targetManagement.countTargetsAll()).as("target count is wrong").isEqualTo(0); - final List targets = new ArrayList(); + final List targets = new ArrayList<>(); for (int i = 0; i < 5; i++) { target = targetManagement.createTarget(new JpaTarget("" + i)); targets.add(target.getId()); @@ -194,6 +215,10 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Finds a target by given ID and checks if all data is in the reponse (including the data defined as lazy).") + @ExpectEvents({ @Expect(type = DistributionSetCreatedEvent.class, count = 2), + @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 5), + @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 1), + @Expect(type = TargetAssignDistributionSetEvent.class, count = 2) }) public void findTargetByControllerIDWithDetails() { final DistributionSet set = testdataFactory.createDistributionSet("test"); final DistributionSet set2 = testdataFactory.createDistributionSet("test2"); @@ -236,7 +261,6 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { assertThat(target.getAssignedDistributionSet()).as("Assigned ds size is wrong").isEqualTo(set2); assertThat(target.getTargetInfo().getInstalledDistributionSet().getId()).as("Installed ds is wrong") .isEqualTo(set.getId()); - } @Test @@ -247,6 +271,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Checks if the EntityAlreadyExistsException is thrown if the targets with the same controller ID are created twice.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 5) }) public void createMultipleTargetsDuplicate() { final List targets = testdataFactory.generateTargets(5, "mySimpleTargs", "my simple targets"); targetManagement.createTargets(targets); @@ -260,6 +285,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Checks if the EntityAlreadyExistsException is thrown if a single target with the same controller ID are created twice.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) }) public void createTargetDuplicate() { targetManagement.createTarget(new JpaTarget("4711")); try { @@ -320,6 +346,8 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @WithUser(allSpPermissions = true) @Description("Creates and updates a target and verifies the changes in the repository.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), + @Expect(type = TargetUpdatedEvent.class, count = 1) }) public void singleTargetIsInsertedIntoRepo() throws Exception { final String myCtrlID = "myCtrlID"; @@ -358,6 +386,9 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @WithUser(allSpPermissions = true) @Description("Create multiple tragets as bulk operation and delete them in bulk.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 101), + @Expect(type = TargetUpdatedEvent.class, count = 100), + @Expect(type = TargetDeletedEvent.class, count = 51) }) public void bulkTargetCreationAndDelete() throws Exception { final String myCtrlID = "myCtrlID"; final List firstList = testdataFactory.generateTargets(100, myCtrlID, "first description"); @@ -406,26 +437,19 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { targetManagement.deleteTargets(savedExtra.getId()); - final int nr2Del = 50; - int i = nr2Del; - final Long[] deletedTargetIDs = new Long[nr2Del]; - final Target[] deletedTargets = new Target[nr2Del]; + final int numberToDelete = 50; + final Iterable targetsToDelete = limit(firstSaved, numberToDelete); + final Target[] deletedTargets = toArray(targetsToDelete, Target.class); + final List targetsIdsToDelete = newArrayList(targetsToDelete.iterator()).stream().map(Target::getId) + .collect(toList()); - final Iterator it = firstSaved.iterator(); - while (nr2Del > 0 && it.hasNext() && i > 0) { - final Target pt = it.next(); - deletedTargetIDs[i - 1] = pt.getId(); - deletedTargets[i - 1] = pt; - i--; - } + targetManagement.deleteTargets(targetsIdsToDelete); - targetManagement.deleteTargets(deletedTargetIDs); + final List targetsLeft = targetManagement.findTargetsAll(new PageRequest(0, 200)).getContent(); + assertThat(firstSaved.spliterator().getExactSizeIfKnown() - numberToDelete).as("Size of splited list") + .isEqualTo(targetsLeft.spliterator().getExactSizeIfKnown()); - final List found = targetManagement.findTargetsAll(new PageRequest(0, 200)).getContent(); - assertThat(firstSaved.spliterator().getExactSizeIfKnown() - nr2Del).as("Size of splited list") - .isEqualTo(found.spliterator().getExactSizeIfKnown()); - - assertThat(found).as("Not all undeleted found").doesNotContain(deletedTargets); + assertThat(targetsLeft).as("Not all undeleted found").doesNotContain(deletedTargets); } @Test @@ -540,6 +564,8 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Tests the assigment of tags to the a single target.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 2), + @Expect(type = TargetTagCreatedEvent.class, count = 7) }) public void targetTagAssignment() { final Target t1 = testdataFactory.generateTarget("id-1", "blablub"); final int noT2Tags = 4; @@ -570,6 +596,9 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Tests the assigment of tags to multiple targets.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 50), + @Expect(type = TargetTagCreatedEvent.class, count = 4), + @Expect(type = TargetUpdatedEvent.class, count = 80) }) public void targetTagBulkAssignments() { final List tagATargets = targetManagement .createTargets(testdataFactory.generateTargets(10, "tagATargets", "first description")); @@ -641,6 +670,9 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Tests the unassigment of tags to multiple targets.") + @ExpectEvents({ @Expect(type = TargetTagCreatedEvent.class, count = 3), + @Expect(type = TargetCreatedEvent.class, count = 109), + @Expect(type = TargetUpdatedEvent.class, count = 227) }) public void targetTagBulkUnassignments() { final TargetTag targTagA = tagManagement.createTargetTag(new JpaTargetTag("Targ-A-Tag")); final TargetTag targTagB = tagManagement.createTargetTag(new JpaTargetTag("Targ-B-Tag")); @@ -689,7 +721,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { targetManagement.toggleTagAssignment(targBCs, targTagC); targetManagement.toggleTagAssignment(targABCs, targTagC); - checkTargetHasTags(true, targAs, targTagA); + checkTargetHasTags(true, targAs, targTagA); // 0 checkTargetHasTags(true, targBs, targTagB); checkTargetHasTags(true, targABs, targTagA, targTagB); checkTargetHasTags(true, targBCs, targTagB); @@ -699,11 +731,13 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { checkTargetHasNotTags(targACs, targTagC); checkTargetHasNotTags(targBCs, targTagC); checkTargetHasNotTags(targABCs, targTagC); - } @Test @Description("Retrieves targets by ID with lazy loading of the tags. Checks the successfull load.") + @ExpectEvents({ @Expect(type = TargetTagCreatedEvent.class, count = 1), + @Expect(type = TargetCreatedEvent.class, count = 25), + @Expect(type = TargetUpdatedEvent.class, count = 25) }) public void findTargetsByControllerIDsWithTags() { final TargetTag targTagA = tagManagement.createTargetTag(new JpaTargetTag("Targ-A-Tag")); @@ -726,6 +760,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Test the optimized quere for retrieving all ID/name pairs of targets.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 25) }) public void findAllTargetIdNamePaiss() { final List targAs = targetManagement .createTargets(testdataFactory.generateTargets(25, "target-id-A", "first description")); @@ -741,6 +776,9 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Test that NO TAG functionality which gives all targets with no tag assigned.") + @ExpectEvents({ @Expect(type = TargetTagCreatedEvent.class, count = 1), + @Expect(type = TargetCreatedEvent.class, count = 50), + @Expect(type = TargetUpdatedEvent.class, count = 25) }) public void findTargetsWithNoTag() { final TargetTag targTagA = tagManagement.createTargetTag(new JpaTargetTag("Targ-A-Tag")); @@ -762,6 +800,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Tests the a target can be read with only the read target permission") + @Expect(type = TargetCreatedEvent.class, count = 0) public void targetCanBeReadWithOnlyReadTargetPermission() throws Exception { final String knownTargetControllerId = "readTarget"; controllerManagament.findOrRegisterTargetIfItDoesNotexist(knownTargetControllerId, new URI("http://127.0.0.1")); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/model/EntityInterceptorListenerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/model/EntityInterceptorListenerTest.java index c3db7ab76..946661e8c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/model/EntityInterceptorListenerTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/model/EntityInterceptorListenerTest.java @@ -15,6 +15,7 @@ import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder; import org.eclipse.hawkbit.repository.model.EntityInterceptor; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.Target; +import org.junit.After; import org.junit.Test; import ru.yandex.qatools.allure.annotations.Description; @@ -28,10 +29,9 @@ import ru.yandex.qatools.allure.annotations.Stories; @Stories("Entity Listener Interceptor") public class EntityInterceptorListenerTest extends AbstractJpaIntegrationTest { - @Override - public void after() { + @After + public void tearDown() { EntityInterceptorHolder.getInstance().getEntityInterceptors().clear(); - super.after(); } @Test diff --git a/hawkbit-repository/hawkbit-repository-test/pom.xml b/hawkbit-repository/hawkbit-repository-test/pom.xml index 09e86dff8..350896f74 100644 --- a/hawkbit-repository/hawkbit-repository-test/pom.xml +++ b/hawkbit-repository/hawkbit-repository-test/pom.xml @@ -90,5 +90,14 @@ io.protostuff protostuff-runtime + + org.easytesting + fest-assert-core + test + + + com.jayway.awaitility + awaitility + \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java index 73f425971..738513989 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/TestConfiguration.java @@ -22,6 +22,7 @@ import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver; import org.eclipse.hawkbit.repository.test.util.JpaTestRepositoryManagement; +import org.eclipse.hawkbit.repository.test.util.TestContextProvider; import org.eclipse.hawkbit.repository.test.util.TestRepositoryManagement; import org.eclipse.hawkbit.repository.test.util.TestdataFactory; import org.eclipse.hawkbit.security.DdiSecurityProperties; @@ -38,6 +39,7 @@ import org.springframework.cache.guava.GuavaCacheManager; import org.springframework.cloud.bus.ConditionalOnBusEnabled; import org.springframework.cloud.bus.ServiceMatcher; import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.AdviceMode; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -102,7 +104,7 @@ public class TestConfiguration implements AsyncConfigurer { /** * Bean for the download id cache. - * + * * @return the cache */ @Bean @@ -161,7 +163,7 @@ public class TestConfiguration implements AsyncConfigurer { } /** - * + * * @return the protostuff io message converter */ @Bean @@ -170,4 +172,13 @@ public class TestConfiguration implements AsyncConfigurer { return new BusProtoStuffMessageConverter(); } + /** + * {@link TestContextProvider} bean. + * + * @return a new {@link TestContextProvider} + */ + @Bean + public ApplicationContextAware applicationContextProvider() { + return new TestContextProvider(); + } } diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/matcher/EventVerifier.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/matcher/EventVerifier.java new file mode 100644 index 000000000..55d75cf18 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/matcher/EventVerifier.java @@ -0,0 +1,133 @@ +/** + * 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.test.matcher; + +import static java.util.Optional.ofNullable; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.hamcrest.Matchers.equalTo; + +import java.util.Iterator; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.hawkbit.repository.test.util.TestContextProvider; +import org.junit.Assert; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.springframework.cloud.bus.event.RemoteApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.event.ApplicationEventMulticaster; + +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; +import com.google.common.collect.Sets; +import com.jayway.awaitility.Awaitility; +import com.jayway.awaitility.core.ConditionTimeoutException; + +/** + * Test rule to setup and verify the event count for a method. + */ +public class EventVerifier implements TestRule { + + private EventCaptor eventCaptor; + + @Override + public Statement apply(final Statement test, final Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + + final Optional expectedEvents = getExpectationsFrom(description); + expectedEvents.ifPresent(events -> beforeTest()); + try { + test.evaluate(); + expectedEvents.ifPresent(events -> afterTest(events)); + } finally { + expectedEvents.ifPresent(listener -> removeEventListener()); + } + } + }; + } + + private Optional getExpectationsFrom(final Description description) { + return ofNullable(description.getAnnotation(ExpectEvents.class)).map(ExpectEvents::value); + } + + private void beforeTest() { + final ConfigurableApplicationContext context = TestContextProvider.getContext(); + eventCaptor = new EventCaptor(); + context.addApplicationListener(eventCaptor); + } + + private void afterTest(final Expect[] expectedEvents) { + verifyRightCountOfEvents(expectedEvents); + verifyAllEventsCounted(expectedEvents); + } + + private void verifyRightCountOfEvents(final Expect[] expectedEvents) { + + for (final Expect expectedEvent : expectedEvents) { + try { + Awaitility.await().atMost(5, SECONDS).until(() -> eventCaptor.getCountFor(expectedEvent.type()), + equalTo(expectedEvent.count())); + + } catch (final ConditionTimeoutException ex) { + Assert.fail("Did not receive the expected amount of events form " + expectedEvent.type() + " Expected: " + + expectedEvent.count() + " but was: " + eventCaptor.getCountFor(expectedEvent.type())); + } + } + } + + private void verifyAllEventsCounted(final Expect[] expectedEvents) { + + final Set> diffSet = eventCaptor.diff(expectedEvents); + if (diffSet.size() > 0) { + final StringBuilder failMessage = new StringBuilder("Missing event verification for "); + final Iterator> itr = diffSet.iterator(); + while (itr.hasNext()) { + final Class element = itr.next(); + final int count = eventCaptor.getCountFor(element); + failMessage.append(element + " with count: " + count + " "); + } + Assert.fail(failMessage.toString()); + } + + } + + private void removeEventListener() { + final ApplicationEventMulticaster multicaster = TestContextProvider.getContext() + .getBean(ApplicationEventMulticaster.class); + multicaster.removeApplicationListener(eventCaptor); + } + + private static class EventCaptor implements ApplicationListener { + + private final Multiset> capturedEvents = HashMultiset.create(); + + @Override + public synchronized void onApplicationEvent(final RemoteApplicationEvent event) { + capturedEvents.add(event.getClass()); + } + + public synchronized int getCountFor(final Class expectedEvent) { + return capturedEvents.count(expectedEvent); + } + + public synchronized Set> diff(final Expect[] allEvents) { + return Sets.difference(capturedEvents.elementSet(), + java.util.stream.Stream.of(allEvents).map((e) -> e.type()).collect(Collectors.toSet())); + } + + } + +} diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/matcher/Expect.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/matcher/Expect.java new file mode 100644 index 000000000..04f8a1ccf --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/matcher/Expect.java @@ -0,0 +1,33 @@ +/** + * 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.test.matcher; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Interface to add annotations for counting events by type and count. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD, ElementType.TYPE }) +public @interface Expect { + + /** + * @return the type of the event + */ + Class type(); + + /** + * @return the expected count of events + */ + int count() default 0; +} diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/matcher/ExpectEvents.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/matcher/ExpectEvents.java new file mode 100644 index 000000000..f40d1110e --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/matcher/ExpectEvents.java @@ -0,0 +1,29 @@ +/** + * 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.test.matcher; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Interface to annotate methods when event count is required. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD, ElementType.TYPE }) +public @interface ExpectEvents { + + /** + * @return a list of {@link Expect} + */ + Expect[] value() default {}; + +} diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java index d945e779f..caf56426c 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java @@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.test.util; import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.CONTROLLER_ROLE; import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.SYSTEM_ROLE; +import static org.junit.rules.RuleChain.outerRule; import org.eclipse.hawkbit.ExcludePathAwareShallowETagFilter; import org.eclipse.hawkbit.TestConfiguration; @@ -29,6 +30,7 @@ import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; +import org.eclipse.hawkbit.repository.test.matcher.EventVerifier; import org.eclipse.hawkbit.security.DosFilter; import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.tenancy.TenantAware; @@ -37,11 +39,12 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; -import org.junit.rules.MethodRule; -import org.junit.rules.TestWatchman; +import org.junit.rules.RuleChain; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; import org.junit.runner.RunWith; -import org.junit.runners.model.FrameworkMethod; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.cloud.bus.ServiceMatcher; @@ -79,7 +82,7 @@ import de.flapdoodle.embed.mongo.MongodExecutable; @DirtiesContext(classMode = ClassMode.AFTER_CLASS) @TestPropertySource(properties = { "spring.data.mongodb.port=0", "spring.mongodb.embedded.version=3.2.7" }) public abstract class AbstractIntegrationTest implements EnvironmentAware { - protected static Logger LOG = null; + private final static Logger LOG = LoggerFactory.getLogger(AbstractIntegrationTest.class); protected static final Pageable pageReq = new PageRequest(0, 400); @@ -151,9 +154,6 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware { @Autowired protected SystemSecurityContext systemSecurityContext; - @Autowired - protected TestRepositoryManagement testRepositoryManagement; - protected MockMvc mvc; protected SoftwareModuleType osType; @@ -174,9 +174,33 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware { @Autowired protected ServiceMatcher serviceMatcher; + @Rule + // Cleaning repository will fire "delete" events. We won't count them to the + // test execution. So there is order between both rules: + public RuleChain ruleChain = outerRule(new CleanRepositoryRule()).around(new EventVerifier()); + @Rule public final WithSpringAuthorityRule securityRule = new WithSpringAuthorityRule(); + @Rule + public TestWatcher testLifecycleLoggerRule = new TestWatcher() { + + @Override + protected void starting(final Description description) { + LOG.info("Starting Test {}...", description.getMethodName()); + }; + + @Override + protected void succeeded(final Description description) { + LOG.info("Test {} succeeded.", description.getMethodName()); + }; + + @Override + protected void failed(final Throwable e, final Description description) { + LOG.error("Test {} failed with {}.", description.getMethodName(), e); + } + }; + protected Environment environment = null; @Override @@ -207,11 +231,6 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware { standardDsType = securityRule.runAsPrivileged(() -> testdataFactory.findOrCreateDefaultTestDsType()); } - @After - public void after() { - testRepositoryManagement.clearTestRepository(); - } - @After public void cleanCurrentCollection() { operations.delete(new Query()); @@ -225,30 +244,6 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware { "/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download", "/*/controller/artifacts/**")); } - @Rule - public MethodRule watchman = new TestWatchman() { - @Override - public void starting(final FrameworkMethod method) { - if (LOG != null) { - LOG.info("Starting Test {}...", method.getName()); - } - } - - @Override - public void succeeded(final FrameworkMethod method) { - if (LOG != null) { - LOG.info("Test {} succeeded.", method.getName()); - } - } - - @Override - public void failed(final Throwable e, final FrameworkMethod method) { - if (LOG != null) { - LOG.error("Test {} failed with {}.", method.getName(), e); - } - } - }; - private static CIMySqlTestDatabase tesdatabase; @BeforeClass diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/CleanRepositoryRule.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/CleanRepositoryRule.java new file mode 100644 index 000000000..80ff8ced5 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/CleanRepositoryRule.java @@ -0,0 +1,21 @@ +/** + * 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.test.util; + +import org.junit.rules.ExternalResource; + +public class CleanRepositoryRule extends ExternalResource { + + @Override + protected void after() { + final TestRepositoryManagement repository = TestContextProvider.getContext() + .getBean(TestRepositoryManagement.class); + repository.clearTestRepository(); + } +} diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestContextProvider.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestContextProvider.java new file mode 100644 index 000000000..8bd38808d --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestContextProvider.java @@ -0,0 +1,27 @@ +/** + * 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.test.util; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; + +public class TestContextProvider implements ApplicationContextAware { + + private static ApplicationContext applicationContext; + + public static ConfigurableApplicationContext getContext() { + return (ConfigurableApplicationContext) applicationContext; + } + + @Override + public void setApplicationContext(final ApplicationContext context) { + applicationContext = context; + } +}