From f237a1e508d60983e7089087b02fe87cdcb5ea25 Mon Sep 17 00:00:00 2001 From: Kai Zimmermann Date: Fri, 25 Mar 2016 16:24:42 +0100 Subject: [PATCH] Added tests for software management - while at it I renamed also a few methods to fit into the overall pattern --- .../repository/SoftwareManagement.java | 53 ++- .../repository/SoftwareModuleRepository.java | 2 + .../hawkbit/repository/SystemManagement.java | 2 - .../model/CustomSoftwareModule.java | 40 ++ .../repository/model/SoftwareModule.java | 24 +- .../model/SoftwareModuleMetadata.java | 87 ++-- .../repository/SoftwareManagementTest.java | 447 ++++++++++++++++-- .../rest/resource/SoftwareModuleResource.java | 2 +- .../resource/SoftwareModuleTypeResource.java | 2 +- .../resource/SoftwareModuleResourceTest.java | 8 +- .../smtable/SwModuleBeanQuery.java | 7 +- 11 files changed, 564 insertions(+), 110 deletions(-) diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareManagement.java index 1f610d29c..b51019338 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareManagement.java @@ -59,11 +59,7 @@ import com.google.common.base.Strings; import com.google.common.collect.Sets; /** - * Business facade for managing the deployable {@link SoftwareModule}s. - * - * - * - * + * Business facade for managing {@link SoftwareModule}s. * */ @Transactional(readOnly = true) @@ -99,7 +95,7 @@ public class SoftwareManagement { private ArtifactManagement artifactManagement; /** - * Updates existing {@link SoftwareModule}. Updateable values are + * Updates existing {@link SoftwareModule}. Update-able values are * {@link SoftwareModule#getDescription()} * {@link SoftwareModule#getVendor()}. * @@ -119,17 +115,21 @@ public class SoftwareManagement { final SoftwareModule module = softwareModuleRepository.findOne(sm.getId()); + boolean updated = false; if (null == sm.getDescription() || !sm.getDescription().equals(module.getDescription())) { module.setDescription(sm.getDescription()); + updated = true; } if (null == sm.getVendor() || !sm.getVendor().equals(module.getVendor())) { module.setVendor(sm.getVendor()); + updated = true; } - return softwareModuleRepository.save(module); + + return updated ? softwareModuleRepository.save(module) : module; } /** - * Updates existing {@link SoftwareModuleType}. Updatable value is + * Updates existing {@link SoftwareModuleType}. Update-able value is * {@link SoftwareModuleType#getDescription()} and * {@link SoftwareModuleType#getColour()}. * @@ -145,13 +145,16 @@ public class SoftwareManagement { final SoftwareModuleType type = softwareModuleTypeRepository.findOne(sm.getId()); + boolean updated = false; if (sm.getDescription() != null && !sm.getDescription().equals(type.getDescription())) { type.setDescription(sm.getDescription()); + updated = true; } if (sm.getColour() != null && !sm.getColour().equals(type.getColour())) { type.setColour(sm.getColour()); + updated = true; } - return softwareModuleTypeRepository.save(type); + return updated ? softwareModuleTypeRepository.save(type) : type; } /** @@ -255,6 +258,10 @@ public class SoftwareManagement { return artifactManagement.findSoftwareModuleById(id); } + // TODO: discuss this method, does not seem to make sense to search by name + // and version without type. It also makes no sense to return collection + // here. It should be a single result based on ytpe,name,version (like the + // constraint). /** * retrieves {@link SoftwareModule}s by their name AND version. * @@ -491,12 +498,17 @@ public class SoftwareManagement { /** * Filter {@link SoftwareModule}s with given * {@link SoftwareModule#getName()} or {@link SoftwareModule#getVersion()} - * and {@link SoftwareModule#getType()} that are not marked as deleted. + * search text and {@link SoftwareModule#getType()} that are not marked as + * deleted and sort them by means of given distribution set related modules + * on top of the list. + * + * After that the modules are sorted by {@link SoftwareModule#getName()} and + * {@link SoftwareModule#getVersion()} in ascending order. * * @param pageable * page parameter * @param orderByDistributionId - * the ID of distribution set to be order by + * the ID of distribution set to be ordered on top * @param searchText * to be filtered as "like" on {@link SoftwareModule#getName()} * @param type @@ -504,8 +516,9 @@ public class SoftwareManagement { * @return the page of found {@link SoftwareModule} */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public Slice findSoftwareModuleOrderByDistribution(@NotNull final Pageable pageable, - @NotNull final Long orderByDistributionId, final String searchText, final SoftwareModuleType type) { + public Slice findSoftwareModuleOrderByDistributionModuleNameAscModuleVersionAsc( + @NotNull final Pageable pageable, @NotNull final Long orderByDistributionId, final String searchText, + final SoftwareModuleType type) { final List resultList = new ArrayList<>(); final int pageSize = pageable.getPageSize(); @@ -522,7 +535,7 @@ public class SoftwareManagement { assignedRoot, assignedQuery, cb, cb.equal(assignedDsJoin.get(DistributionSet_.id), orderByDistributionId)); // if we have some predicates then add it to the where clause of the - // multiselect + // multi select assignedQuery.where(specPredicate); assignedQuery.orderBy(cb.asc(assignedRoot.get(SoftwareModule_.name)), cb.asc(assignedRoot.get(SoftwareModule_.version))); @@ -546,7 +559,7 @@ public class SoftwareManagement { unassignedQuery.distinct(true); final Root unassignedRoot = unassignedQuery.from(SoftwareModule.class); - Predicate[] unassignedSpec = null; + Predicate[] unassignedSpec; if (!assignedSoftwareModules.isEmpty()) { unassignedSpec = specificationsToPredicate(buildSpecificationList(searchText, type), unassignedRoot, unassignedQuery, cb, cb.not(unassignedRoot.get(SoftwareModule_.id) @@ -709,8 +722,8 @@ public class SoftwareManagement { @Modifying @Transactional @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY) - public List createSoftwareModuleTypes(@NotNull final Collection types) { - return types.stream().map(type -> createSoftwareModuleType(type)).collect(Collectors.toList()); + public List createSoftwareModuleType(@NotNull final Collection types) { + return types.stream().map(this::createSoftwareModuleType).collect(Collectors.toList()); } /** @@ -826,7 +839,7 @@ public class SoftwareManagement { @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) public SoftwareModuleMetadata updateSoftwareModuleMetadata(@NotNull final SoftwareModuleMetadata metadata) { // check if exists otherwise throw entity not found exception - findOne(metadata.getId()); + findSoftwareModuleMetadata(metadata.getId()); // touch it to update the lock revision because we are modifying the // software module // indirectly @@ -884,7 +897,7 @@ public class SoftwareManagement { cb) -> cb.and( cb.equal(root.get(SoftwareModuleMetadata_.softwareModule) .get(SoftwareModule_.id), softwareModuleId), - spec.toPredicate(root, query, cb)), + spec.toPredicate(root, query, cb)), pageable); } @@ -899,7 +912,7 @@ public class SoftwareManagement { * in case the meta data does not exists for the given key */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) - public SoftwareModuleMetadata findOne(@NotNull final SwMetadataCompositeKey id) { + public SoftwareModuleMetadata findSoftwareModuleMetadata(@NotNull final SwMetadataCompositeKey id) { final SoftwareModuleMetadata findOne = softwareModuleMetadataRepository.findOne(id); if (findOne == null) { throw new EntityNotFoundException("Metadata with key '" + id.getKey() + "' does not exist"); diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleRepository.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleRepository.java index ccadb028a..44682da64 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleRepository.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleRepository.java @@ -81,6 +81,8 @@ public interface SoftwareModuleRepository Page findByAssignedTo(Pageable pageable, DistributionSet set); /** + * + * * @param set * to search for * @return all {@link SoftwareModule}s that are assigned to given diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SystemManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SystemManagement.java index 559b71498..31f5f1c18 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SystemManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/SystemManagement.java @@ -310,8 +310,6 @@ public class SystemManagement { } private DistributionSetType createStandardSoftwareDataSetup() { - - // Edge Controller Linux standard setup final SoftwareModuleType eclApp = softwareModuleTypeRepository.save(new SoftwareModuleType("application", "ECL Application", "Edge Controller Linux base application type", 1)); final SoftwareModuleType eclOs = softwareModuleTypeRepository diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/CustomSoftwareModule.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/CustomSoftwareModule.java index 96c7cdd62..b68f9df49 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/CustomSoftwareModule.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/CustomSoftwareModule.java @@ -52,4 +52,44 @@ public class CustomSoftwareModule implements Serializable { public boolean isAssigned() { return assigned; } + + @Override + public String toString() { + return "CustomSoftwareModule [softwareModule=" + softwareModule + ", assigned=" + assigned + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (assigned ? 1231 : 1237); + result = prime * result + ((softwareModule == null) ? 0 : softwareModule.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final CustomSoftwareModule other = (CustomSoftwareModule) obj; + if (assigned != other.assigned) { + return false; + } + if (softwareModule == null) { + if (other.softwareModule != null) { + return false; + } + } else if (!softwareModule.equals(other.softwareModule)) { + return false; + } + return true; + } + } diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java index 49e73f749..7b0c03c0e 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModule.java @@ -43,7 +43,7 @@ import org.eclipse.persistence.annotations.CascadeOnDelete; */ @Entity @Table(name = "sp_base_software_module", uniqueConstraints = @UniqueConstraint(columnNames = { "module_type", "name", - "version", "tenant" }, name = "uk_base_sw_mod") , indexes = { + "version", "tenant" }, name = "uk_base_sw_mod"), indexes = { @Index(name = "sp_idx_base_sw_module_01", columnList = "tenant,deleted,name,version"), @Index(name = "sp_idx_base_sw_module_02", columnList = "tenant,deleted,module_type"), @Index(name = "sp_idx_base_sw_module_prim", columnList = "tenant,id") }) @@ -52,7 +52,7 @@ public class SoftwareModule extends NamedVersionedEntity { private static final long serialVersionUID = 1L; @ManyToOne - @JoinColumn(name = "module_type", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_module_type") ) + @JoinColumn(name = "module_type", nullable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_module_type")) private SoftwareModuleType type; @ManyToMany(mappedBy = "modules", targetEntity = DistributionSet.class, fetch = FetchType.LAZY) @@ -239,23 +239,12 @@ public class SoftwareModule extends NamedVersionedEntity { return metadata; } - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ @Override public String toString() { - return "SoftwareModule [type=" + type + ", deleted=" + deleted + ", getVersion()=" + getVersion() - + ", getOptLockRevision()=" + getOptLockRevision() + ", getId()=" + getId() + ", getType()=" - + getType().getName() + "]"; + return "SoftwareModule [deleted=" + deleted + ", getVersion()=" + getVersion() + ", getOptLockRevision()=" + + getOptLockRevision() + ", getId()=" + getId() + ", getType()=" + getType().getName() + "]"; } - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { // NOSONAR - as this is generated final int prime = 31; @@ -264,11 +253,6 @@ public class SoftwareModule extends NamedVersionedEntity { return result; } - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(final Object obj) { // NOSONAR - as this is generated if (this == obj) { diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModuleMetadata.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModuleMetadata.java index dfc1ecfa7..4ffbd2c10 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModuleMetadata.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModuleMetadata.java @@ -24,9 +24,6 @@ import javax.persistence.Table; /** * Metadata for {@link SoftwareModule}. * - * - * - * */ @IdClass(SwMetadataCompositeKey.class) @Entity @@ -50,18 +47,21 @@ public class SoftwareModuleMetadata implements Serializable { @JoinColumn(name = "sw_id", foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_metadata_sw")) private SoftwareModule softwareModule; + /** + * Default constructor for JPA. + */ public SoftwareModuleMetadata() { - + // default constructor for JPA. } /** * Standard constructor. * * @param key - * of the metadata element + * of the meta data element * @param softwareModule * @param value - * of the metadata element + * of the meta data element */ public SoftwareModuleMetadata(final String key, final SoftwareModule softwareModule, final String value) { this.key = key; @@ -69,56 +69,83 @@ public class SoftwareModuleMetadata implements Serializable { this.value = value; } - /** - * @return the id - */ public SwMetadataCompositeKey getId() { return new SwMetadataCompositeKey(softwareModule, key); } - /** - * @return the value - */ public String getValue() { return value; } - /** - * @param value - * the value to set - */ public void setValue(final String value) { this.value = value; } - /** - * @return the softwareModule - */ public SoftwareModule getSoftwareModule() { return softwareModule; } - /** - * @param softwareModule - * the softwareModule to set - */ public void setSoftwareModule(final SoftwareModule softwareModule) { this.softwareModule = softwareModule; } - /** - * @return the key - */ public String getKey() { return key; } - /** - * @param key - * the key to set - */ public void setKey(final String key) { this.key = key; } + @Override + public String toString() { + return "SoftwareModuleMetadata [key=" + key + ", value=" + value + ", softwareModule=" + softwareModule + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((softwareModule == null) ? 0 : softwareModule.hashCode()); + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final SoftwareModuleMetadata other = (SoftwareModuleMetadata) obj; + if (key == null) { + if (other.key != null) { + return false; + } + } else if (!key.equals(other.key)) { + return false; + } + if (softwareModule == null) { + if (other.softwareModule != null) { + return false; + } + } else if (!softwareModule.equals(other.softwareModule)) { + return false; + } + if (value == null) { + if (other.value != null) { + return false; + } + } else if (!value.equals(other.value)) { + return false; + } + return true; + } + } diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/SoftwareManagementTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/SoftwareManagementTest.java index 49776e392..c295c57ce 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/SoftwareManagementTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/SoftwareManagementTest.java @@ -14,7 +14,6 @@ import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -24,14 +23,18 @@ import org.eclipse.hawkbit.AbstractIntegrationTestWithMongoDB; import org.eclipse.hawkbit.RandomGeneratedInputStream; import org.eclipse.hawkbit.TestDataUtil; import org.eclipse.hawkbit.WithUser; +import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; +import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Artifact; +import org.eclipse.hawkbit.repository.model.CustomSoftwareModule; import org.eclipse.hawkbit.repository.model.DistributionSet; -import org.eclipse.hawkbit.repository.model.DistributionSetTag; +import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.LocalArtifact; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; +import org.eclipse.hawkbit.repository.model.SwMetadataCompositeKey; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.junit.Test; @@ -51,6 +54,143 @@ import ru.yandex.qatools.allure.annotations.Stories; @Stories("Software Management") public class SoftwareManagementTest extends AbstractIntegrationTestWithMongoDB { + @Test + @Description("Try to update non updatable fields results in repository doing nothing.") + public void updateTypeNonUpdateableFieldsFails() { + final SoftwareModuleType created = softwareManagement + .createSoftwareModuleType(new SoftwareModuleType("test-key", "test-name", "test-desc", 1)); + + created.setName("a new name"); + final SoftwareModuleType updated = softwareManagement.updateSoftwareModuleType(created); + + assertThat(updated.getOptLockRevision()) + .as("Expected version number of updated entitity to be equal to created version") + .isEqualTo(created.getOptLockRevision()); + } + + @Test + @Description("Calling update without changing fields results in no recorded change in the repository including unchanged audit fields.") + public void updateNothingResultsInUnchangedRepositoryForType() { + final SoftwareModuleType created = softwareManagement + .createSoftwareModuleType(new SoftwareModuleType("test-key", "test-name", "test-desc", 1)); + + final SoftwareModuleType updated = softwareManagement.updateSoftwareModuleType(created); + + assertThat(updated.getOptLockRevision()) + .as("Expected version number of updated entitity to be equal to created version") + .isEqualTo(created.getOptLockRevision()); + } + + @Test + @Description("Calling update for changed fields results in change in the repository.") + public void updateSoftareModuleTypeFieldsToNewValue() { + final SoftwareModuleType created = softwareManagement + .createSoftwareModuleType(new SoftwareModuleType("test-key", "test-name", "test-desc", 1)); + + created.setDescription("changed"); + created.setColour("changed"); + + final SoftwareModuleType updated = softwareManagement.updateSoftwareModuleType(created); + + assertThat(updated.getOptLockRevision()).as("Expected version number of updated entitity is") + .isEqualTo(created.getOptLockRevision() + 1); + assertThat(updated.getDescription()).as("Updated description is").isEqualTo("changed"); + assertThat(updated.getColour()).as("Updated vendor is").isEqualTo("changed"); + } + + @Test + @Description("Try to update non updatable fields results in repository doing nothing.") + public void updateNonUpdateableFieldsFails() { + final SoftwareModule ah = softwareManagement + .createSoftwareModule(new SoftwareModule(appType, "agent-hub", "1.0.1", null, "")); + + ah.setName("a new name"); + final SoftwareModule updated = softwareManagement.updateSoftwareModule(ah); + + assertThat(updated.getOptLockRevision()) + .as("Expected version number of updated entitity to be equal to created version") + .isEqualTo(ah.getOptLockRevision()); + } + + @Test + @Description("Calling update without changing fields results in no recorded change in the repository including unchanged audit fields.") + public void updateNothingResultsInUnchangedRepository() { + final SoftwareModule ah = softwareManagement + .createSoftwareModule(new SoftwareModule(appType, "agent-hub", "1.0.1", null, "")); + + final SoftwareModule updated = softwareManagement.updateSoftwareModule(ah); + + assertThat(updated.getOptLockRevision()) + .as("Expected version number of updated entitity to be equal to created version") + .isEqualTo(ah.getOptLockRevision()); + } + + @Test + @Description("Calling update for changed fields results in change in the repository.") + public void updateSoftareModuleFieldsToNewValue() { + final SoftwareModule ah = softwareManagement + .createSoftwareModule(new SoftwareModule(appType, "agent-hub", "1.0.1", "test desc", "test vendor")); + + ah.setDescription("changed"); + ah.setVendor("changed"); + final SoftwareModule updated = softwareManagement.updateSoftwareModule(ah); + + assertThat(updated.getOptLockRevision()).as("Expected version number of updated entitity is") + .isEqualTo(ah.getOptLockRevision() + 1); + assertThat(updated.getDescription()).as("Updated description is").isEqualTo("changed"); + assertThat(updated.getVendor()).as("Updated vendor is").isEqualTo("changed"); + } + + @Test(expected = EntityAlreadyExistsException.class) + @Description("Create Software Module call fails when called for existing entity.") + public void createModuleCallFailsForExistingModule() { + final SoftwareModule ah = softwareManagement + .createSoftwareModule(new SoftwareModule(appType, "agent-hub", "1.0.1", "test desc", "test vendor")); + softwareManagement.createSoftwareModule(ah); + } + + @Test(expected = EntityAlreadyExistsException.class) + @Description("Create Software Modules call fails when called for existing entities.") + public void createModulesCallFailsForExistingModule() { + final List modules = softwareManagement.createSoftwareModule( + Lists.newArrayList(new SoftwareModule(appType, "agent-hub", "1.0.1", "test desc", "test vendor"), + new SoftwareModule(appType, "agent-hub", "1.0.2", "test desc", "test vendor"))); + softwareManagement.createSoftwareModule(modules); + } + + @Test(expected = EntityAlreadyExistsException.class) + @Description("Create Software Module Type call fails when called for existing entity.") + public void createModuleTypeCallFailsForExistingType() { + final SoftwareModuleType created = softwareManagement + .createSoftwareModuleType(new SoftwareModuleType("test-key", "test-name", "test-desc", 1)); + softwareManagement.createSoftwareModuleType(created); + } + + @Test(expected = EntityAlreadyExistsException.class) + @Description("Create Software Module Types call fails when called for existing entities.") + public void createModuleTypesCallFailsForExistingTypes() { + final List created = softwareManagement.createSoftwareModuleType( + Lists.newArrayList(new SoftwareModuleType("test-key", "test-name", "test-desc", 1), + new SoftwareModuleType("test-key2", "test-name", "test-desc", 1))); + softwareManagement.createSoftwareModuleType(created); + } + + @Test + @Description("Calling update for changing fields to null results in change in the repository.") + public void eraseSoftareModuleFields() { + final SoftwareModule ah = softwareManagement + .createSoftwareModule(new SoftwareModule(appType, "agent-hub", "1.0.1", "test desc", "test vendor")); + + ah.setDescription(null); + ah.setVendor(null); + final SoftwareModule updated = softwareManagement.updateSoftwareModule(ah); + + assertThat(updated.getOptLockRevision()).as("Expected version number of updated entitity is") + .isEqualTo(ah.getOptLockRevision() + 1); + assertThat(updated.getDescription()).as("Updated description is").isNull(); + assertThat(updated.getVendor()).as("Updated vendor is").isNull(); + } + @Test @Description("searched for software modules based on the various filter options, e.g. name,desc,type, version.") public void findSoftwareModuleByFilters() { @@ -105,7 +245,7 @@ public class SoftwareManagementTest extends AbstractIntegrationTestWithMongoDB { @Test @Description("Searches for software modules based on a list of IDs.") - public void findSoftwareModulesByIdAndType() { + public void findSoftwareModulesById() { final List modules = new ArrayList(); @@ -118,6 +258,54 @@ public class SoftwareManagementTest extends AbstractIntegrationTestWithMongoDB { assertThat(softwareManagement.findSoftwareModulesById(modules)).hasSize(2); } + @Test + @Description("Searches for software modules by type.") + public void findSoftwareModulesByType() { + // found in test + final SoftwareModule one = softwareManagement + .createSoftwareModule(new SoftwareModule(osType, "one", "one", null, "")); + final SoftwareModule two = softwareManagement + .createSoftwareModule(new SoftwareModule(osType, "two", "two", null, "")); + // ignored + softwareManagement.deleteSoftwareModule( + softwareManagement.createSoftwareModule(new SoftwareModule(osType, "deleted", "deleted", null, ""))); + softwareManagement.createSoftwareModule(new SoftwareModule(appType, "three", "3.0.2", null, "")); + + assertThat(softwareManagement.findSoftwareModulesByType(pageReq, osType).getContent()) + .as("Expected to find the following number of modules:").hasSize(2).as("with the following elements") + .contains(two, one); + } + + @Test + @Description("Counts all software modules in the repsitory that are not marked as deleted.") + public void countSoftwareModulesAll() { + // found in test + softwareManagement.createSoftwareModule(new SoftwareModule(osType, "one", "one", null, "")); + softwareManagement.createSoftwareModule(new SoftwareModule(appType, "two", "two", null, "")); + // ignored + softwareManagement.deleteSoftwareModule( + softwareManagement.createSoftwareModule(new SoftwareModule(osType, "deleted", "deleted", null, ""))); + + assertThat(softwareManagement.countSoftwareModulesAll()).as("Expected to find the following number of modules:") + .isEqualTo(2); + } + + @Test + @Description("Counts for software modules by type.") + public void countSoftwareModulesByType() { + // found in test + softwareManagement.createSoftwareModule(new SoftwareModule(osType, "one", "one", null, "")); + softwareManagement.createSoftwareModule(new SoftwareModule(osType, "two", "two", null, "")); + + // ignored + softwareManagement.deleteSoftwareModule( + softwareManagement.createSoftwareModule(new SoftwareModule(osType, "deleted", "deleted", null, ""))); + softwareManagement.createSoftwareModule(new SoftwareModule(appType, "three", "3.0.2", null, "")); + + assertThat(softwareManagement.countSoftwareModulesByType(osType)) + .as("Expected to find the following number of modules:").isEqualTo(2); + } + @Test @Description("Tests the successfull deletion of software module types. Both unused (hard delete) and used ones (soft delete).") public void deleteAssignedAndUnassignedSoftwareModuleTypes() { @@ -419,32 +607,184 @@ public class SoftwareManagementTest extends AbstractIntegrationTestWithMongoDB { } } - /** - * - * @param findAll - * @return - */ - @SuppressWarnings("rawtypes") - private Collection iterable2Collection(final Iterable iterable) { - final Collection col = new ArrayList(); - for (final Object o : iterable) { - col.add(o); - } - return col; + @Test + @Description("Test verfies that results are returned based on given filter parameters and in the specified order.") + public void findSoftwareModuleOrderByDistributionModuleNameAscModuleVersionAsc() { + // test meta data + final SoftwareModuleType testType = softwareManagement + .createSoftwareModuleType(new SoftwareModuleType("thetype", "thename", "desc", 100)); + final DistributionSetType testDsType = distributionSetManagement + .createDistributionSetType(new DistributionSetType("key", "name", "desc").addMandatoryModuleType(osType) + .addOptionalModuleType(testType)); + + // found in test + final SoftwareModule unassigned = softwareManagement + .createSoftwareModule(new SoftwareModule(testType, "asis", "found", null, "")); + final SoftwareModule one = softwareManagement + .createSoftwareModule(new SoftwareModule(testType, "found", "b", null, "")); + final SoftwareModule two = softwareManagement + .createSoftwareModule(new SoftwareModule(testType, "found", "c", null, "")); + final SoftwareModule differentName = softwareManagement + .createSoftwareModule(new SoftwareModule(testType, "differentname", "d", null, "")); + + // ignored + final SoftwareModule deleted = softwareManagement + .createSoftwareModule(new SoftwareModule(testType, "deleted", "deleted", null, "")); + final SoftwareModule four = softwareManagement + .createSoftwareModule(new SoftwareModule(osType, "sdfjhsdj", "e", null, "")); + + final DistributionSet set = distributionSetManagement.createDistributionSet(new DistributionSet("set", "1", + "desc", testDsType, Lists.newArrayList(one, two, deleted, four, differentName))); + softwareManagement.deleteSoftwareModule(deleted); + + // with filter on name, version and module type + assertThat(softwareManagement.findSoftwareModuleOrderByDistributionModuleNameAscModuleVersionAsc(pageReq, + set.getId(), "found", testType).getContent()) + .as("Found modules with given name, given module type and the assigned ones first") + .containsExactly(new CustomSoftwareModule(one, true), new CustomSoftwareModule(two, true), + new CustomSoftwareModule(unassigned, false)); + + // with filter on module type only + assertThat(softwareManagement.findSoftwareModuleOrderByDistributionModuleNameAscModuleVersionAsc(pageReq, + set.getId(), null, testType).getContent()) + .as("Found modules with given module type and the assigned ones first").containsExactly( + new CustomSoftwareModule(differentName, true), new CustomSoftwareModule(one, true), + new CustomSoftwareModule(two, true), new CustomSoftwareModule(unassigned, false)); + + // without any filter + assertThat(softwareManagement + .findSoftwareModuleOrderByDistributionModuleNameAscModuleVersionAsc(pageReq, set.getId(), null, null) + .getContent()).as("Found modules with the assigned ones first").containsExactly( + new CustomSoftwareModule(differentName, true), new CustomSoftwareModule(one, true), + new CustomSoftwareModule(two, true), new CustomSoftwareModule(four, true), + new CustomSoftwareModule(unassigned, false)); } - /** - * - */ - private void printDSTags() { - System.out.println("=============================================================================="); - for (final DistributionSet d : distributionSetRepository.findAll()) { - System.out.printf("%s\t[", d.getName()); - for (final DistributionSetTag t : d.getTags()) { - System.out.printf("%s ", t.getName()); - } - System.out.println("]"); - } + @Test + @Description("Checks that number of modules is returned as expected based on given filters.") + public void countSoftwareModuleByFilters() { + + // test meta data + final SoftwareModuleType testType = softwareManagement + .createSoftwareModuleType(new SoftwareModuleType("thetype", "thename", "desc", 100)); + final DistributionSetType testDsType = distributionSetManagement + .createDistributionSetType(new DistributionSetType("key", "name", "desc").addMandatoryModuleType(osType) + .addOptionalModuleType(testType)); + + // test modules + softwareManagement.createSoftwareModule(new SoftwareModule(testType, "asis", "found", null, "")); + final SoftwareModule one = softwareManagement + .createSoftwareModule(new SoftwareModule(testType, "found", "b", null, "")); + final SoftwareModule two = softwareManagement + .createSoftwareModule(new SoftwareModule(testType, "found", "c", null, "")); + final SoftwareModule differentName = softwareManagement + .createSoftwareModule(new SoftwareModule(testType, "differentname", "d", null, "")); + final SoftwareModule four = softwareManagement + .createSoftwareModule(new SoftwareModule(osType, "found", "3.0.2", null, "")); + + // one soft deleted + final SoftwareModule deleted = softwareManagement + .createSoftwareModule(new SoftwareModule(testType, "deleted", "deleted", null, "")); + distributionSetManagement.createDistributionSet(new DistributionSet("set", "1", "desc", testDsType, + Lists.newArrayList(one, two, deleted, four, differentName))); + softwareManagement.deleteSoftwareModule(deleted); + + // test + assertThat(softwareManagement.countSoftwareModuleByFilters("found", testType)) + .as("Number of modules with given name or version and type").isEqualTo(3); + assertThat(softwareManagement.countSoftwareModuleByFilters(null, testType)) + .as("Number of modules with given type").isEqualTo(4); + assertThat(softwareManagement.countSoftwareModuleByFilters(null, null)).as("Number of modules overall") + .isEqualTo(5); + } + + @Test + @Description("Verfies that all undeleted software modules are found in the repository.") + public void countSoftwareModuleTypesAll() { + final SoftwareModuleType testType = softwareManagement + .createSoftwareModuleType(new SoftwareModuleType("thetype", "thename", "desc", 100)); + final DistributionSetType testDsType = distributionSetManagement + .createDistributionSetType(new DistributionSetType("key", "name", "desc").addMandatoryModuleType(osType) + .addOptionalModuleType(testType)); + final SoftwareModule four = softwareManagement + .createSoftwareModule(new SoftwareModule(osType, "found", "3.0.2", null, "")); + + // one soft deleted + final SoftwareModule deleted = softwareManagement + .createSoftwareModule(new SoftwareModule(testType, "deleted", "deleted", null, "")); + distributionSetManagement.createDistributionSet( + new DistributionSet("set", "1", "desc", testDsType, Lists.newArrayList(deleted, four))); + softwareManagement.deleteSoftwareModule(deleted); + + assertThat(softwareManagement.countSoftwareModulesAll()).as("Number of undeleted modules").isEqualTo(1); + assertThat(softwareModuleRepository.count()).as("Number of all modules").isEqualTo(2); + } + + @Test + @Description("Checks that software module typeis found based on given name.") + public void findSoftwareModuleTypeByName() { + final SoftwareModuleType found = softwareManagement + .createSoftwareModuleType(new SoftwareModuleType("thetype", "thename", "desc", 100)); + softwareManagement.createSoftwareModuleType(new SoftwareModuleType("thetype2", "anothername", "desc", 100)); + + assertThat(softwareManagement.findSoftwareModuleTypeByName("thename")).as("Type with given name") + .isEqualTo(found); + } + + @Test(expected = EntityAlreadyExistsException.class) + @Description("Verfies that it is not possible to create a type that alrady exists.") + public void createSoftwareModuleTypeFailsWithExistingEntity() { + final SoftwareModuleType created = softwareManagement + .createSoftwareModuleType(new SoftwareModuleType("thetype", "thename", "desc", 100)); + softwareManagement.createSoftwareModuleType(created); + } + + @Test(expected = EntityAlreadyExistsException.class) + @Description("Verfies that it is not possible to create a list of types where one already exists.") + public void createSoftwareModuleTypesFailsWithExistingEntity() { + final SoftwareModuleType created = softwareManagement + .createSoftwareModuleType(new SoftwareModuleType("thetype", "thename", "desc", 100)); + softwareManagement.createSoftwareModuleType( + Lists.newArrayList(created, new SoftwareModuleType("anothertype", "anothername", "desc", 100))); + } + + @Test + @Description("Verfies that multiple types are created as requested.") + public void createMultipleoftwareModuleTypes() { + final List created = softwareManagement + .createSoftwareModuleType(Lists.newArrayList(new SoftwareModuleType("thetype", "thename", "desc", 100), + new SoftwareModuleType("thetype2", "thename2", "desc2", 100))); + + assertThat(created.size()).as("Number of created types").isEqualTo(2); + assertThat(softwareManagement.countSoftwareModuleTypesAll()).as("Number of types in repository").isEqualTo(5); + } + + @Test + @Description("Verfies that sofwtare modules are resturned that are assigned to given DS.") + public void findSoftwareModuleByAssignedTo() { + // test meta data + final SoftwareModuleType testType = softwareManagement + .createSoftwareModuleType(new SoftwareModuleType("thetype", "thename", "desc", 100)); + final DistributionSetType testDsType = distributionSetManagement + .createDistributionSetType(new DistributionSetType("key", "name", "desc").addMandatoryModuleType(osType) + .addOptionalModuleType(testType)); + + // test modules + softwareManagement.createSoftwareModule(new SoftwareModule(testType, "asis", "found", null, "")); + final SoftwareModule one = softwareManagement + .createSoftwareModule(new SoftwareModule(testType, "found", "b", null, "")); + final SoftwareModule two = softwareManagement + .createSoftwareModule(new SoftwareModule(testType, "found", "c", null, "")); + + // one soft deleted + final SoftwareModule deleted = softwareManagement + .createSoftwareModule(new SoftwareModule(testType, "deleted", "deleted", null, "")); + final DistributionSet set = distributionSetManagement.createDistributionSet( + new DistributionSet("set", "1", "desc", testDsType, Lists.newArrayList(one, deleted))); + softwareManagement.deleteSoftwareModule(deleted); + + assertThat(softwareManagement.findSoftwareModuleByAssignedTo(pageReq, set).getContent()) + .as("Found this number of modules").hasSize(2); } @Test @@ -479,6 +819,22 @@ public class SoftwareManagementTest extends AbstractIntegrationTestWithMongoDB { assertThat(softwareModuleMetadata.get(0).getSoftwareModule().getId()).isEqualTo(ah.getId()); } + @Test(expected = EntityAlreadyExistsException.class) + @Description("Checks that metadata for a software module cannot be created for an existing key.") + public void createSoftwareModuleMetadataFailsIfKeyExists() { + + final String knownKey1 = "myKnownKey1"; + final String knownValue1 = "myKnownValue1"; + final String knownValue2 = "myKnownValue2"; + + final SoftwareModule ah = softwareManagement + .createSoftwareModule(new SoftwareModule(appType, "agent-hub", "1.0.1", null, "")); + + softwareManagement.createSoftwareModuleMetadata(new SoftwareModuleMetadata(knownKey1, ah, knownValue1)); + + softwareManagement.createSoftwareModuleMetadata(new SoftwareModuleMetadata(knownKey1, ah, knownValue2)); + } + @Test @WithUser(allSpPermissions = true) @Description("Checks that metadata for a software module can be updated.") @@ -525,6 +881,43 @@ public class SoftwareManagementTest extends AbstractIntegrationTestWithMongoDB { assertThat(updated.getSoftwareModule().getId()).isEqualTo(ah.getId()); } + @Test + @Description("Verfies that existing metadata can be deleted.") + public void deleteSoftwareModuleMetadata() { + final String knownKey1 = "myKnownKey1"; + final String knownValue1 = "myKnownValue1"; + + SoftwareModule ah = softwareManagement + .createSoftwareModule(new SoftwareModule(appType, "agent-hub", "1.0.1", null, "")); + + ah = softwareManagement.createSoftwareModuleMetadata(new SoftwareModuleMetadata(knownKey1, ah, knownValue1)) + .getSoftwareModule(); + + assertThat(softwareManagement.findSoftwareModuleById(ah.getId()).getMetadata()) + .as("Contains the created metadata element") + .containsExactly(new SoftwareModuleMetadata(knownKey1, ah, knownValue1)); + + softwareManagement.deleteSoftwareModuleMetadata(new SwMetadataCompositeKey(ah, knownKey1)); + assertThat(softwareManagement.findSoftwareModuleById(ah.getId()).getMetadata()).as("Metadata elemenets are") + .isEmpty(); + } + + @Test(expected = EntityNotFoundException.class) + @Description("Verfies that non existing metadata find results in exception.") + public void findSoftwareModuleMetadataFailsIfEntryDoesNotExist() { + final String knownKey1 = "myKnownKey1"; + final String knownValue1 = "myKnownValue1"; + + SoftwareModule ah = softwareManagement + .createSoftwareModule(new SoftwareModule(appType, "agent-hub", "1.0.1", null, "")); + + ah = softwareManagement.createSoftwareModuleMetadata(new SoftwareModuleMetadata(knownKey1, ah, knownValue1)) + .getSoftwareModule(); + + softwareManagement.findSoftwareModuleMetadata(new SwMetadataCompositeKey(ah, "doesnotexist")); + + } + @Test @Description("Queries and loads the metadata related to a given software module.") public void findAllSoftwareModuleMetadataBySwId() { diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResource.java index e165fca20..cfddf9d40 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResource.java @@ -226,7 +226,7 @@ public class SoftwareModuleResource implements SoftwareModuleRestAPI { // check if distribution set exists otherwise throw exception // immediately final SoftwareModule sw = findSoftwareModuleWithExceptionIfNotFound(softwareModuleId, null); - final SoftwareModuleMetadata findOne = softwareManagement.findOne(new SwMetadataCompositeKey(sw, metadataKey)); + final SoftwareModuleMetadata findOne = softwareManagement.findSoftwareModuleMetadata(new SwMetadataCompositeKey(sw, metadataKey)); return ResponseEntity. ok(SoftwareModuleMapper.toResponseSwMetadata(findOne)); } diff --git a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleTypeResource.java b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleTypeResource.java index 7dda79690..e3498f141 100644 --- a/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleTypeResource.java +++ b/hawkbit-rest-resource/src/main/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleTypeResource.java @@ -102,7 +102,7 @@ public class SoftwareModuleTypeResource implements SoftwareModuleTypeRestApi { final List softwareModuleTypes) { final List createdSoftwareModules = this.softwareManagement - .createSoftwareModuleTypes(SoftwareModuleTypeMapper.smFromRequest(softwareModuleTypes)); + .createSoftwareModuleType(SoftwareModuleTypeMapper.smFromRequest(softwareModuleTypes)); return new ResponseEntity<>(SoftwareModuleTypeMapper.toTypesResponse(createdSoftwareModules), HttpStatus.CREATED); diff --git a/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResourceTest.java b/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResourceTest.java index 0b8503d6b..09ae7978f 100644 --- a/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResourceTest.java +++ b/hawkbit-rest-resource/src/test/java/org/eclipse/hawkbit/rest/resource/SoftwareModuleResourceTest.java @@ -930,8 +930,8 @@ public class SoftwareModuleResourceTest extends AbstractIntegrationTestWithMongo .andExpect(jsonPath("[1]key", equalTo(knownKey2))) .andExpect(jsonPath("[1]value", equalTo(knownValue2))); - final SoftwareModuleMetadata metaKey1 = softwareManagement.findOne(new SwMetadataCompositeKey(sm, knownKey1)); - final SoftwareModuleMetadata metaKey2 = softwareManagement.findOne(new SwMetadataCompositeKey(sm, knownKey2)); + final SoftwareModuleMetadata metaKey1 = softwareManagement.findSoftwareModuleMetadata(new SwMetadataCompositeKey(sm, knownKey1)); + final SoftwareModuleMetadata metaKey2 = softwareManagement.findSoftwareModuleMetadata(new SwMetadataCompositeKey(sm, knownKey2)); assertThat(metaKey1.getValue()).as("Metadata key is wrong").isEqualTo(knownValue1); assertThat(metaKey2.getValue()).as("Metadata key is wrong").isEqualTo(knownValue2); @@ -957,7 +957,7 @@ public class SoftwareModuleResourceTest extends AbstractIntegrationTestWithMongo .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("key", equalTo(knownKey))).andExpect(jsonPath("value", equalTo(updateValue))); - final SoftwareModuleMetadata assertDS = softwareManagement.findOne(new SwMetadataCompositeKey(sm, knownKey)); + final SoftwareModuleMetadata assertDS = softwareManagement.findSoftwareModuleMetadata(new SwMetadataCompositeKey(sm, knownKey)); assertThat(assertDS.getValue()).as("Metadata is wrong").isEqualTo(updateValue); } @@ -976,7 +976,7 @@ public class SoftwareModuleResourceTest extends AbstractIntegrationTestWithMongo .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); try { - softwareManagement.findOne(new SwMetadataCompositeKey(sm, knownKey)); + softwareManagement.findSoftwareModuleMetadata(new SwMetadataCompositeKey(sm, knownKey)); fail("expected EntityNotFoundException but didn't throw"); } catch (final EntityNotFoundException e) { // ok as expected diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtable/SwModuleBeanQuery.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtable/SwModuleBeanQuery.java index 859febbce..9aae5ca5c 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtable/SwModuleBeanQuery.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/smtable/SwModuleBeanQuery.java @@ -22,8 +22,6 @@ import org.eclipse.hawkbit.ui.utils.SPDateTimeUtil; import org.eclipse.hawkbit.ui.utils.SPUIDefinitions; import org.eclipse.hawkbit.ui.utils.SpringContextHelper; import org.springframework.data.domain.Slice; -import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Direction; import org.vaadin.addons.lazyquerycontainer.AbstractBeanQuery; import org.vaadin.addons.lazyquerycontainer.QueryDefinition; @@ -79,9 +77,8 @@ public class SwModuleBeanQuery extends AbstractBeanQuery final Slice swModuleBeans; final List proxyBeans = new ArrayList<>(); - swModuleBeans = getSoftwareManagement().findSoftwareModuleOrderByDistribution( - new OffsetBasedPageRequest(startIndex, count, new Sort(Direction.ASC, "name", "version")), - orderByDistId, searchText, type); + swModuleBeans = getSoftwareManagement().findSoftwareModuleOrderByDistributionModuleNameAscModuleVersionAsc( + new OffsetBasedPageRequest(startIndex, count), orderByDistId, searchText, type); for (final CustomSoftwareModule swModule : swModuleBeans) { proxyBeans.add(getProxyBean(swModule));