diff --git a/pom.xml b/pom.xml
index 0269fb995..8624aa3ee 100644
--- a/pom.xml
+++ b/pom.xml
@@ -74,6 +74,8 @@
2.22.1
step.junit.categories.LocalJMeter
+
+ ${maven.multiModuleProjectDirectory}/target/jacoco_analysis/jacoco.exec
@@ -791,6 +793,57 @@
+
+
+
+ AggregatedJacocoReportEnabled
+
+
+ AggregatedJacocoReportEnabled
+ true
+
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${dep.mvn.jacoco.version}
+
+
+ ${jacoco.overall.exec}
+ ${jacoco.overall.exec}
+
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ default-prepare-agent
+
+ prepare-agent
+
+
+ true
+
+
+
+ default-report
+ validate
+
+ report
+
+
+
+
+
+
+
diff --git a/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AbstractAutomationPackageManagerTest.java b/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AbstractAutomationPackageManagerTest.java
index 0da7edcca..8d260ffa0 100644
--- a/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AbstractAutomationPackageManagerTest.java
+++ b/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AbstractAutomationPackageManagerTest.java
@@ -38,7 +38,7 @@
import step.core.controller.ControllerSettingAccessorImpl;
import step.core.dynamicbeans.DynamicBeanResolver;
import step.core.dynamicbeans.DynamicValueResolver;
-import step.core.objectenricher.ObjectHookRegistry;
+import step.core.objectenricher.*;
import step.core.plans.Plan;
import step.core.plans.PlanAccessorImpl;
import step.core.scheduler.ExecutionScheduler;
@@ -132,7 +132,13 @@ public void before() {
AutomationPackageReaderRegistry automationPackageReaderRegistry = new AutomationPackageReaderRegistry(YamlAutomationPackageVersions.ACTUAL_JSON_SCHEMA_PATH, automationPackageHookRegistry, serializationRegistry);
automationPackageReaderRegistry.register(apReader);
- this.manager = AutomationPackageManager.createMainAutomationPackageManager(
+ this.manager = createManager(automationPackageHookRegistry, automationPackageReaderRegistry);
+
+ this.manager.setProvidersResolver(new MockedAutomationPackageProvidersResolver(new HashMap<>(), resourceManager, automationPackageReaderRegistry));
+ }
+
+ protected AutomationPackageManager createManager(AutomationPackageHookRegistry automationPackageHookRegistry, AutomationPackageReaderRegistry automationPackageReaderRegistry) {
+ return AutomationPackageManager.createMainAutomationPackageManager(
automationPackageAccessor,
functionManager,
functionAccessor,
@@ -144,8 +150,6 @@ public void before() {
null, -1,
new ObjectHookRegistry()
);
-
- this.manager.setProvidersResolver(new MockedAutomationPackageProvidersResolver(new HashMap<>(), resourceManager, automationPackageReaderRegistry));
}
@After
diff --git a/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageManagerEETest.java b/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageManagerEETest.java
index 421f80957..738680f7a 100644
--- a/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageManagerEETest.java
+++ b/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageManagerEETest.java
@@ -25,6 +25,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import step.attachments.FileResolver;
+import step.automation.packages.library.ManagedLibraryProvider;
+import step.core.AbstractContext;
import step.core.maven.MavenArtifactIdentifier;
import step.core.objectenricher.*;
import step.repositories.artifact.ResolvedMavenArtifact;
@@ -193,11 +195,8 @@ public void testManagedLibrary(){
MockedAutomationPackageProvidersResolver providersResolver = (MockedAutomationPackageProvidersResolver) manager.getProvidersResolver();
providersResolver.getMavenArtifactMocks().put(libVersion1, new ResolvedMavenArtifact(libJar, new SnapshotMetadata("some timestamp", System.currentTimeMillis(), 1, false)));
- try (InputStream is = new FileInputStream(automationPackageJar);
- InputStream isAnother = new FileInputStream(anotherAutomationPackageJar);
- ) {
+ try (InputStream is = new FileInputStream(automationPackageJar)) {
AutomationPackageFileSource sample1ApSource = AutomationPackageFileSource.withInputStream(is, SAMPLE1_FILE_NAME);
- AutomationPackageFileSource anotherApSource = AutomationPackageFileSource.withInputStream(isAnother, SAMPLE1_EXTENDED_FILE_NAME);
AutomationPackageFileSource libSource = AutomationPackageFileSource.withMavenIdentifier(libVersion1);
// 1. Create managed library by Global Admin
@@ -279,6 +278,234 @@ public void testManagedLibrary(){
}
}
+ @Test
+ public void testManagedLibraryInIsolatedProjects() throws IOException, ManagedLibraryMissingException, AutomationPackageReadingException {
+ File automationPackageJar = new File("src/test/resources/samples/" + SAMPLE1_FILE_NAME);
+ File anotherAutomationPackageJar = new File("src/test/resources/samples/" + SAMPLE_ECHO_FILE_NAME);
+
+ File libJar = new File("src/test/resources/samples/" + KW_LIB_FILE_NAME);
+ File libJarUpdated = new File("src/test/resources/samples/" + KW_LIB_FILE_UPDATED_NAME);
+
+ MavenArtifactIdentifier libVersion1 = new MavenArtifactIdentifier("test-group", "test-lib", "1.0.0-SNAPSHOT", null, null);
+ MockedAutomationPackageProvidersResolver providersResolver = (MockedAutomationPackageProvidersResolver) manager.getProvidersResolver();
+ providersResolver.getMavenArtifactMocks().put(libVersion1, new ResolvedMavenArtifact(libJar, new SnapshotMetadata("some timestamp", System.currentTimeMillis(), 1, false)));
+
+ AutomationPackageFileSource libSource = AutomationPackageFileSource.withMavenIdentifier(libVersion1);
+
+ // 1. Create managed library in project1
+ AutomationPackageUpdateParameter user1Params = new AutomationPackageUpdateParameterBuilder()
+ .forJunit()
+ .withActorUser("user1")
+ .withEnricher(createTenantEnricher(PROJECT_1))
+ .withObjectPredicate(createAccessPredicate(PROJECT_1))
+ .withWriteAccessValidator(createWriteAccessValidator(PROJECT_1))
+ .build();
+
+ Resource projectLibResource1 = manager.createAutomationPackageResource(ResourceManager.RESOURCE_TYPE_AP_MANAGED_LIBRARY, libSource, "testManagedLibrary", user1Params);
+ Assert.assertNotNull(projectLibResource1);
+ Assert.assertEquals(PROJECT_1, projectLibResource1.getAttribute(ATTRIBUTE_PROJECT_NAME));
+
+ // 2. Create managed library in project2 with the same name - it is allowed, because we use separate tenants
+ AutomationPackageUpdateParameter user2Params = new AutomationPackageUpdateParameterBuilder()
+ .forJunit()
+ .withActorUser("user2")
+ .withEnricher(createTenantEnricher(PROJECT_2))
+ .withObjectPredicate(createAccessPredicate(PROJECT_2))
+ .withWriteAccessValidator(createWriteAccessValidator(PROJECT_2))
+ .build();
+
+ Resource projectLibResource2 = manager.createAutomationPackageResource(ResourceManager.RESOURCE_TYPE_AP_MANAGED_LIBRARY, libSource, "testManagedLibrary", user2Params);
+ Assert.assertNotNull(projectLibResource2);
+ Assert.assertEquals(PROJECT_2, projectLibResource2.getAttribute(ATTRIBUTE_PROJECT_NAME));
+
+ // 3. User1 cannot use the library from project2
+ try (InputStream is = new FileInputStream(automationPackageJar)) {
+ AutomationPackageFileSource sample1ApSource = AutomationPackageFileSource.withInputStream(is, SAMPLE1_FILE_NAME);
+ try {
+ AutomationPackageUpdateParameter user1CreateApParams = new AutomationPackageUpdateParameterBuilder()
+ .forJunit()
+ .withApSource(sample1ApSource)
+ .withApLibrarySource(AutomationPackageFileSource.withResourceId(projectLibResource2.getId().toHexString()))
+ .withAllowUpdate(false)
+ .withAsync(false)
+ .withCheckForSameOrigin(true)
+ .withEnricher(createTenantEnricher(PROJECT_1))
+ .withObjectPredicate(createAccessPredicate(PROJECT_1))
+ .withWriteAccessValidator(createWriteAccessValidator(PROJECT_1))
+ .build();
+
+ manager.createOrUpdateAutomationPackage(user1CreateApParams);
+ Assert.fail("Exception should be thrown");
+ } catch (AutomationPackageManagerException ex) {
+ log.info("Exception: {}", ex.getMessage());
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("IO Exception", e);
+ }
+
+ AutomationPackageUpdateResult resultAp1;
+ AutomationPackageUpdateResult resultAp2;
+ try (InputStream is = new FileInputStream(automationPackageJar);
+ InputStream isAnother = new FileInputStream(anotherAutomationPackageJar)) {
+
+ // 4. User1 and User2 can create automation packages referencing managed library within their own projects
+ AutomationPackageFileSource sample1ApSource = AutomationPackageFileSource.withInputStream(is, SAMPLE1_FILE_NAME);
+
+ AutomationPackageUpdateParameter user1CreateApParams = new AutomationPackageUpdateParameterBuilder()
+ .forJunit()
+ .withApSource(sample1ApSource)
+ .withApLibrarySource(AutomationPackageFileSource.withResourceId(projectLibResource1.getId().toHexString()))
+ .withAllowUpdate(false)
+ .withAsync(false)
+ .withCheckForSameOrigin(true)
+ .withEnricher(createTenantEnricher(PROJECT_1))
+ .withObjectPredicate(createAccessPredicate(PROJECT_1))
+ .withWriteAccessValidator(createWriteAccessValidator(PROJECT_1))
+ .build();
+
+ resultAp1 = manager.createOrUpdateAutomationPackage(user1CreateApParams);
+ Assert.assertEquals(CREATED, resultAp1.getStatus());
+ AutomationPackageFileSource sample2ApSource = AutomationPackageFileSource.withInputStream(isAnother, SAMPLE1_FILE_NAME);
+
+ AutomationPackageUpdateParameter user2CreateApParams = new AutomationPackageUpdateParameterBuilder()
+ .forJunit()
+ .withApSource(sample2ApSource)
+ .withApLibrarySource(AutomationPackageFileSource.withResourceId(projectLibResource2.getId().toHexString()))
+ .withAllowUpdate(false)
+ .withAsync(false)
+ .withCheckForSameOrigin(true)
+ .withEnricher(createTenantEnricher(PROJECT_2))
+ .withObjectPredicate(createAccessPredicate(PROJECT_2))
+ .withWriteAccessValidator(createWriteAccessValidator(PROJECT_2))
+ .build();
+
+ resultAp2 = manager.createOrUpdateAutomationPackage(user2CreateApParams);
+ Assert.assertEquals(CREATED, resultAp2.getStatus());
+ } catch (IOException e) {
+ throw new RuntimeException("IO Exception", e);
+ }
+
+ AutomationPackage ap1 = automationPackageAccessor.get(resultAp1.getId());
+ AutomationPackage ap2 = automationPackageAccessor.get(resultAp2.getId());
+
+ // check tenants linked with both APs
+ Assert.assertEquals(PROJECT_1, ap1.getAttribute(ATTRIBUTE_PROJECT_NAME));
+ Assert.assertEquals(PROJECT_2, ap2.getAttribute(ATTRIBUTE_PROJECT_NAME));
+
+ // check references to libs
+ Assert.assertEquals(FileResolver.resolveResourceId(ap1.getAutomationPackageLibraryResource()), projectLibResource1.getId().toHexString());
+ Assert.assertEquals(FileResolver.resolveResourceId(ap2.getAutomationPackageLibraryResource()), projectLibResource2.getId().toHexString());
+
+ // 4.1 User1 updates the lib in project1 - AP from project2 should be untouched
+ providersResolver.getMavenArtifactMocks().put(libVersion1, new ResolvedMavenArtifact(
+ libJarUpdated,
+ new SnapshotMetadata("some timestamp", System.currentTimeMillis(), 1, true))
+ );
+
+ Instant nowBeforeLib1Update = Instant.now();
+ RefreshResourceResult refreshResourceResult = manager.getAutomationPackageResourceManager().refreshResourceAndLinkedPackages(
+ projectLibResource1.getId().toHexString(), user1Params, manager
+ );
+ Assert.assertEquals(RefreshResourceResult.ResultStatus.REFRESHED, refreshResourceResult.getResultStatus());
+
+ // lib1 has been updated
+ Resource updatedLib1Resource = resourceManager.getResource(projectLibResource1.getId().toHexString());
+ Assert.assertFalse(updatedLib1Resource.getLastModificationDate().toInstant().isBefore(nowBeforeLib1Update));
+ Assert.assertArrayEquals(Files.readAllBytes(resourceManager.getResourceFile(projectLibResource1.getId().toHexString()).getResourceFile().toPath()), Files.readAllBytes(libJarUpdated.toPath()));
+
+ // lib2 is not updated
+ Assert.assertFalse(projectLibResource2.getLastModificationDate().toInstant().isAfter(nowBeforeLib1Update));
+ Assert.assertArrayEquals(Files.readAllBytes(resourceManager.getResourceFile(projectLibResource2.getId().toHexString()).getResourceFile().toPath()), Files.readAllBytes(libJar.toPath()));
+
+ // take the actual state from db
+ ap1 = automationPackageAccessor.get(ap1.getId());
+ ap2 = automationPackageAccessor.get(ap2.getId());
+
+ // ap1 has been reuploaded
+ Assert.assertFalse(ap1.getLastModificationDate().toInstant().isBefore(nowBeforeLib1Update));
+
+ // ap2 has not been reuploaded
+ Assert.assertFalse(ap2.getLastModificationDate().toInstant().isAfter(nowBeforeLib1Update));
+
+ // original tenants for automation packages should not be changed after reupload
+ Assert.assertEquals(PROJECT_1, ap1.getAttribute(ATTRIBUTE_PROJECT_NAME));
+ Assert.assertEquals(PROJECT_2, ap2.getAttribute(ATTRIBUTE_PROJECT_NAME));
+
+ // 4.2 User1 re-uploads the same managed library again - the behavior should be the same as in 4.1
+ providersResolver.getMavenArtifactMocks().put(libVersion1, new ResolvedMavenArtifact(
+ libJarUpdated,
+ new SnapshotMetadata("some timestamp 2", System.currentTimeMillis(), 1, true))
+ );
+
+ nowBeforeLib1Update = Instant.now();
+ updatedLib1Resource = manager.getAutomationPackageResourceManager().uploadOrReuseAutomationPackageLibrary(
+ new ManagedLibraryProvider(manager.getAutomationPackageLibraryProvider(libSource, createAccessPredicate(PROJECT_1)), projectLibResource1, "testManagedLibrary"), ap1, user1Params, true, true
+ );
+
+ Assert.assertFalse(updatedLib1Resource.getLastModificationDate().toInstant().isBefore(nowBeforeLib1Update));
+ Assert.assertArrayEquals(Files.readAllBytes(resourceManager.getResourceFile(projectLibResource1.getId().toHexString()).getResourceFile().toPath()), Files.readAllBytes(libJarUpdated.toPath()));
+
+ // lib2 is not updated
+ Assert.assertFalse(projectLibResource2.getLastModificationDate().toInstant().isAfter(nowBeforeLib1Update));
+ Assert.assertArrayEquals(Files.readAllBytes(resourceManager.getResourceFile(projectLibResource2.getId().toHexString()).getResourceFile().toPath()), Files.readAllBytes(libJar.toPath()));
+
+ // take the actual state from db
+ ap1 = automationPackageAccessor.get(ap1.getId());
+ ap2 = automationPackageAccessor.get(ap2.getId());
+
+ // original tenants for automation packages should not be changed after reupload
+ Assert.assertEquals(PROJECT_1, ap1.getAttribute(ATTRIBUTE_PROJECT_NAME));
+ Assert.assertEquals(PROJECT_2, ap2.getAttribute(ATTRIBUTE_PROJECT_NAME));
+
+ // 5. User1 still has the access to AP to read and delete it
+ AutomationPackage apTakenFromManager = manager.getAutomationPackageById(ap1.getId(), createAccessPredicate(PROJECT_1));
+ Assert.assertNotNull(apTakenFromManager);
+
+ manager.removeAutomationPackage(ap1.getId(), "user1", createAccessPredicate(PROJECT_1), createWriteAccessValidator(PROJECT_1));
+
+ // ap1 doesn't exist anymore
+ Assert.assertNull(automationPackageAccessor.get(ap1.getId()));
+
+ // resource file and resource for lib are not automatically deleted
+ ResourceRevisionFileHandle resourceFile = resourceManager.getResourceFile(updatedLib1Resource.getId().toString());
+ Assert.assertNotNull(resourceManager.getResource(updatedLib1Resource.getId().toString()));
+ Assert.assertNotNull(resourceFile);
+ }
+
+ protected AutomationPackageManager createManager(AutomationPackageHookRegistry automationPackageHookRegistry, AutomationPackageReaderRegistry automationPackageReaderRegistry) {
+ ObjectHookRegistry objectHookRegistry = new ObjectHookRegistry();
+ objectHookRegistry.add(new ObjectHook() {
+ @Override
+ public ObjectFilter getObjectFilter(AbstractContext context) {
+ // TODO: maybe we need to mock the object filter also
+ return null;
+ }
+
+ @Override
+ public ObjectEnricher getObjectEnricher(AbstractContext context) {
+ return createTenantEnricher(context.get("project") == null ? null : (String) context.get("project"));
+ }
+
+ @Override
+ public void rebuildContext(AbstractContext context, EnricheableObject object) throws Exception {
+ context.put("project", object.getAttribute(ATTRIBUTE_PROJECT_NAME));
+ }
+ });
+
+ return AutomationPackageManager.createMainAutomationPackageManager(
+ automationPackageAccessor,
+ functionManager,
+ functionAccessor,
+ planAccessor,
+ resourceManager,
+ automationPackageHookRegistry,
+ automationPackageReaderRegistry,
+ automationPackageLocks,
+ null, -1,
+ objectHookRegistry
+ );
+ }
+
protected WriteAccessValidator createWriteAccessValidator(String ... projectNames){
return new WriteAccessValidator() {
@Override
diff --git a/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageManagerOSTest.java b/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageManagerOSTest.java
index 8b1545efd..aa18098e4 100644
--- a/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageManagerOSTest.java
+++ b/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageManagerOSTest.java
@@ -47,6 +47,7 @@
import java.nio.file.Files;
import java.time.Duration;
import java.util.*;
+import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
diff --git a/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageReaderTest.java b/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageReaderTest.java
index 7ef07307d..046266aa8 100644
--- a/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageReaderTest.java
+++ b/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageReaderTest.java
@@ -4,6 +4,7 @@
import ch.exense.commons.io.FileHelper;
import jakarta.json.spi.JsonProvider;
import org.apache.commons.collections.CollectionUtils;
+import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import org.slf4j.Logger;
@@ -254,4 +255,25 @@ public void testInvalidAPNames() {
}
}
+ @Test
+ public void testMissingDescriptor() throws IOException, AutomationPackageReadingException {
+ File tempFolder = FileHelper.createTempFolder();
+ FileHelper.unzip(this.getClass().getClassLoader().getResourceAsStream("step/automation/packages/step-automation-packages.zip"), tempFolder);
+
+ // remove descriptor - reader will use the folder name as AP name and autoscan to lookup existing plans in this folder
+ File descriptor = new File(tempFolder, "automation-package.yaml");
+ boolean deleteOk = descriptor.delete();
+ Assert.assertTrue(deleteOk);
+
+ AutomationPackageContent automationPackageContent = reader.readAutomationPackageFromJarFile(tempFolder, null, null);
+ assertNotNull(automationPackageContent);
+ assertEquals(tempFolder.getName(), automationPackageContent.getName());
+
+ List plans = automationPackageContent.getPlans();
+ assertEquals(0, plans.size());
+
+ // cleanup temp folder
+ Assert.assertTrue("Temp folder cannot be removed", FileHelper.deleteFolder(tempFolder));
+ }
+
}
\ No newline at end of file