From ca8b7e2e9d35976c8bc51f6b40252357b1633739 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Wed, 5 Nov 2025 19:04:51 +0300 Subject: [PATCH 01/16] SED-4305: unit tests for AutomationPackageManager --- .../AutomationPackageManagerEETest.java | 233 ++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageManagerEETest.java 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 new file mode 100644 index 000000000..135058999 --- /dev/null +++ b/step-automation-packages/step-automation-packages-controller/src/test/java/step/automation/packages/AutomationPackageManagerEETest.java @@ -0,0 +1,233 @@ +/* + * ****************************************************************************** + * * Copyright (C) 2020, exense GmbH + * * + * * This file is part of STEP + * * + * * STEP is free software: you can redistribute it and/or modify + * * it under the terms of the GNU Affero General Public License as published by + * * the Free Software Foundation, either version 3 of the License, or + * * (at your option) any later version. + * * + * * STEP is distributed in the hope that it will be useful, + * * but WITHOUT ANY WARRANTY; without even the implied warranty of + * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * * GNU Affero General Public License for more details. + * * + * * You should have received a copy of the GNU Affero General Public License + * * along with STEP. If not, see . + * ***************************************************************************** + */ +package step.automation.packages; + +import jakarta.validation.constraints.AssertTrue; +import org.junit.Assert; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import step.attachments.FileResolver; +import step.core.maven.MavenArtifactIdentifier; +import step.core.objectenricher.*; +import step.repositories.artifact.ResolvedMavenArtifact; +import step.repositories.artifact.SnapshotMetadata; +import step.resources.Resource; +import step.resources.ResourceMissingException; + +import java.io.*; +import java.util.*; + +import static step.automation.packages.AutomationPackageUpdateStatus.CREATED; +import static step.automation.packages.AutomationPackageUpdateStatus.UPDATED; + +public class AutomationPackageManagerEETest extends AbstractAutomationPackageManagerTest { + + private static final Logger log = LoggerFactory.getLogger(AutomationPackageManagerEETest.class); + + private static final String ATTRIBUTE_PROJECT_NAME = "project"; + private static final String GLOBAL_PROJECT = "globalProject"; + private static final String PROJECT_1 = "project1"; + private static final String PROJECT_2 = "project2"; + + @Test + public void testCrudWithPermissions() { + File automationPackageJar = new File("src/test/resources/samples/" + SAMPLE1_FILE_NAME); + File extendedAutomationPackageJar = new File("src/test/resources/samples/" + SAMPLE1_EXTENDED_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))); + + try (InputStream is = new FileInputStream(automationPackageJar); + InputStream isExt = new FileInputStream(extendedAutomationPackageJar); + InputStream isAnother = new FileInputStream(anotherAutomationPackageJar); + ) { + // 1. Save new automation package for tenant1 + AutomationPackageFileSource sample1ApSource = AutomationPackageFileSource.withInputStream(is, SAMPLE1_FILE_NAME); + AutomationPackageFileSource extendedApSource = AutomationPackageFileSource.withInputStream(isExt, SAMPLE1_EXTENDED_FILE_NAME); + AutomationPackageFileSource anotherApSource = AutomationPackageFileSource.withInputStream(isAnother, SAMPLE1_EXTENDED_FILE_NAME); + AutomationPackageFileSource libSource = AutomationPackageFileSource.withMavenIdentifier(libVersion1); + + AutomationPackageUpdateParameter createParametersGlobal = new AutomationPackageUpdateParameterBuilder().forJunit() + .withApSource(sample1ApSource) + .withApLibrarySource(libSource) + .withAllowUpdate(false) + .withAsync(false) + .withCheckForSameOrigin(true) + .withEnricher(createTenantEnricher(GLOBAL_PROJECT)) + .withWriteAccessValidator(createWriteAccessValidator(GLOBAL_PROJECT)) + .withObjectPredicate(createAccessPredicate(GLOBAL_PROJECT, PROJECT_1)) + .build(); + AutomationPackageUpdateResult resultGlobal = manager.createOrUpdateAutomationPackage(createParametersGlobal); + Assert.assertEquals(CREATED, resultGlobal.getStatus()); + + // all common checks for CRUD operations are covered in step.automation.packages.AutomationPackageManagerOSTest + // here we only check restriction via tenants (enricher and object predicate) + AutomationPackage apGlobal = automationPackageAccessor.get(resultGlobal.getId()); + + // check if enricher fills the project attribute + Assert.assertEquals(GLOBAL_PROJECT, apGlobal.getAttribute(ATTRIBUTE_PROJECT_NAME)); + + // check if the project is also propagated to the resources + Resource apResourceGlobal = resourceManager.getResource(FileResolver.resolveResourceId(apGlobal.getAutomationPackageResource())); + Resource libResourceGlobal = resourceManager.getResource(FileResolver.resolveResourceId(apGlobal.getAutomationPackageLibraryResource())); + + Assert.assertEquals(GLOBAL_PROJECT, apResourceGlobal.getAttribute(ATTRIBUTE_PROJECT_NAME)); + Assert.assertEquals(GLOBAL_PROJECT, libResourceGlobal.getAttribute(ATTRIBUTE_PROJECT_NAME)); + + // upload another ap in another project, but with the same (unmodified lib) + AutomationPackageUpdateParameter createParameters1 = new AutomationPackageUpdateParameterBuilder().forJunit() + .withApSource(anotherApSource) + .withApLibrarySource(libSource) + .withAllowUpdate(false) + .withCheckForSameOrigin(true) + .withAsync(false) + .withEnricher(createTenantEnricher(PROJECT_1)) + .withWriteAccessValidator(createWriteAccessValidator(PROJECT_1)) + .withObjectPredicate(createAccessPredicate(GLOBAL_PROJECT, PROJECT_1)) + .build(); + + AutomationPackageUpdateResult result1 = manager.createOrUpdateAutomationPackage(createParameters1); + Assert.assertEquals(CREATED, result1.getStatus()); + + // check that ap library is reused + AutomationPackage ap1 = automationPackageAccessor.get(result1.getId()); + Resource libResource1 = resourceManager.getResource(FileResolver.resolveResourceId(ap1.getAutomationPackageLibraryResource())); + Assert.assertEquals(libResource1.getId(), libResourceGlobal.getId()); + + // lib resource is still linked with global project + Assert.assertEquals(GLOBAL_PROJECT, libResource1.getAttribute(ATTRIBUTE_PROJECT_NAME)); + + // try to reload AP library (newSnapshotVersion=true) without permissions on project1 + providersResolver.getMavenArtifactMocks().put(libVersion1, new ResolvedMavenArtifact( + libJarUpdated, + new SnapshotMetadata("some timestamp", System.currentTimeMillis(), 1, true)) + ); + + AutomationPackageUpdateParameter updateParameter = new AutomationPackageUpdateParameterBuilder().forJunit() + .withApSource(extendedApSource) + .withApLibrarySource(libSource) + .withAllowUpdate(true) + .withForceRefreshOfSnapshots(true) + .withAsync(false) + .withCheckForSameOrigin(true) + .withEnricher(createTenantEnricher(GLOBAL_PROJECT)) + .withWriteAccessValidator(createWriteAccessValidator(GLOBAL_PROJECT)) + .withObjectPredicate(createAccessPredicate(GLOBAL_PROJECT, PROJECT_1)) + .build(); + + // TODO: is it OK that the AP linked with project1 is automatically refreshed without write access to project1? + AutomationPackageUpdateResult result3 = manager.createOrUpdateAutomationPackage(updateParameter); + Assert.assertEquals(UPDATED, result3.getStatus()); + + // Try to delete global AP without permissions - it is restricted + try { + manager.removeAutomationPackage(apGlobal.getId(), "userProject1", + createAccessPredicate(GLOBAL_PROJECT), + createWriteAccessValidator(PROJECT_1) + ); + Assert.fail("Exception should be thrown"); + } catch (AutomationPackageAccessException ex){ + log.info("Exception caught: {}", ex.getMessage()); + } + + // Delete global AP with enough permissions + manager.removeAutomationPackage(apGlobal.getId(), "globalAdmin", + createAccessPredicate(GLOBAL_PROJECT), + createWriteAccessValidator(GLOBAL_PROJECT) + ); + + // check AP and resource is deleted, but AP lib still exists and used in Project1 + AutomationPackage automationPackage = automationPackageAccessor.get(apGlobal.getId()); + Assert.assertNull(automationPackage); + + try { + resourceManager.getResource(apResourceGlobal.getId().toHexString()); + Assert.fail("Resource is not removed"); + } catch (ResourceMissingException ex){ + log.info("Resource is removed: {}", ex.getMessage()); + } + + Assert.assertNotNull(resourceManager.getResource(libResourceGlobal.getId().toHexString())); + Assert.assertTrue(resourceManager.getResourceFile(libResourceGlobal.getId().toHexString()).getResourceFile().exists()); + + } catch (IOException e) { + throw new RuntimeException("IO Exception", e); + } + } + + protected WriteAccessValidator createWriteAccessValidator(String ... projectNames){ + return new WriteAccessValidator() { + @Override + public void validate(EnricheableObject entity) throws ObjectAccessException { + if(!createAccessPredicate(projectNames).test(entity)){ + throw new ObjectAccessException(List.of(new ObjectAccessViolation("testHookId", "testErrorCode", "User has no access to " + entity.toString()))); + } + } + }; + } + + protected ObjectPredicate createAccessPredicate(String ... projectNames) { + return object -> { + String projectNameAttr = object.getAttribute(ATTRIBUTE_PROJECT_NAME); + return projectNameAttr != null && Arrays.asList(projectNames).contains(projectNameAttr); + }; + } + + protected ObjectEnricher createTenantEnricher(String projectName) { + return new ObjectEnricher() { + + @Override + public void accept(EnricheableObject t) { + enrichObject(t); + } + + @Override + public TreeMap getAdditionalAttributes() { + TreeMap additionalAttributes = new TreeMap<>(); + if (projectName != null) { + additionalAttributes.put(ATTRIBUTE_PROJECT_NAME, projectName); + } + return additionalAttributes; + } + + private void enrichObject(EnricheableObject object) { + if (object != null) { + if (projectName != null) { + Map attributes = object.getAttributes(); + if (attributes == null) { + attributes = new HashMap<>(); + object.setAttributes(attributes); + } + attributes.put(ATTRIBUTE_PROJECT_NAME, projectName); + } + } + + } + }; + } + +} From 26b3d75bbad19eed4e56daceb2f561bbba1ad9f6 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Wed, 5 Nov 2025 19:12:10 +0300 Subject: [PATCH 02/16] SED-4305: forbid libraries in local execution (CLI) --- .../step-cli-launcher/src/main/java/step/cli/ApCommand.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/step-cli/step-cli-launcher/src/main/java/step/cli/ApCommand.java b/step-cli/step-cli-launcher/src/main/java/step/cli/ApCommand.java index 0705fcfdd..ba09f912c 100644 --- a/step-cli/step-cli-launcher/src/main/java/step/cli/ApCommand.java +++ b/step-cli/step-cli-launcher/src/main/java/step/cli/ApCommand.java @@ -265,7 +265,9 @@ private void handleApLocalExecuteCommand() { File packageLibraryFile = null; if (library != null && !library.isEmpty()) { - packageLibraryFile = preparePackageLibraryFile(library); + // TODO: SED-4035 - classloader issue for local execution with library - should be fixed later + throw new StepCliExecutionException("Libraries are not supported for local execution"); +// packageLibraryFile = preparePackageLibraryFile(library); } executeLocally(file, packageLibraryFile, includePlans, excludePlans, includeCategories, excludeCategories, executionParameters); From 15330a1b66b71553ce6a2e9b9d4555c0c64efbcb Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Thu, 6 Nov 2025 09:17:19 +0300 Subject: [PATCH 03/16] SED-4305: fix test --- .../step-automation-packages-controller/pom.xml | 7 +++++++ .../packages/AutomationPackageManagerOSTest.java | 8 ++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/step-automation-packages/step-automation-packages-controller/pom.xml b/step-automation-packages/step-automation-packages-controller/pom.xml index aaa100147..9ef57eade 100644 --- a/step-automation-packages/step-automation-packages-controller/pom.xml +++ b/step-automation-packages/step-automation-packages-controller/pom.xml @@ -47,6 +47,13 @@ mockito-core test + + org.awaitility + awaitility + 4.3.0 + test + jar + 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 33668364a..578f50303 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 @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.awaitility.Awaitility; import org.bson.types.ObjectId; import org.junit.*; import org.slf4j.Logger; @@ -35,7 +36,9 @@ import java.io.*; 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; @@ -329,7 +332,6 @@ public void testUpdateAsync() throws IOException, InterruptedException { }); //Give some time to let the execution start - Thread.sleep(500); uploadSample1WithAsserts(AutomationPackageFileSource.withInputStream(is3, SAMPLE1_FILE_NAME), false, true, true, "v1", "env == TEST", Map.of("planAttr", "planAttrValue"), Map.of("functionAttr", "functionAttrValue") , Map.of("OS", "WINDOWS", "TYPE", "PLAYWRIGHT")); @@ -1309,7 +1311,9 @@ private SampleUploadingResult uploadSample1WithAsserts(ObjectId explicitOldId, A .build(); AutomationPackageUpdateResult updateResult = manager.createOrUpdateAutomationPackage(updateParameters); if (async && expectedDelay) { - Assert.assertEquals(AutomationPackageUpdateStatus.UPDATE_DELAYED, updateResult.getStatus()); + Awaitility.await().atMost(Duration.ofSeconds(10)).pollDelay(Duration.ofMillis(50)).until(() -> { + log.info("Current status: {}", updateResult.getStatus()); + return updateResult.getStatus().equals(AutomationPackageUpdateStatus.UPDATE_DELAYED);}); } else { Assert.assertEquals(AutomationPackageUpdateStatus.UPDATED, updateResult.getStatus()); } From 6dd34f990b8bf1401279d51a01c875122ea93007 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Thu, 6 Nov 2025 10:33:30 +0300 Subject: [PATCH 04/16] SED-4305: new unit test --- .../AutomationPackageManagerEETest.java | 102 ++++++++++++++++++ .../packages/AutomationPackageManager.java | 2 +- 2 files changed, 103 insertions(+), 1 deletion(-) 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 135058999..0097ad201 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 @@ -31,9 +31,13 @@ import step.repositories.artifact.ResolvedMavenArtifact; import step.repositories.artifact.SnapshotMetadata; import step.resources.Resource; +import step.resources.ResourceManager; import step.resources.ResourceMissingException; +import step.resources.ResourceRevisionFileHandle; import java.io.*; +import java.nio.file.Files; +import java.time.Instant; import java.util.*; import static step.automation.packages.AutomationPackageUpdateStatus.CREATED; @@ -174,6 +178,104 @@ public void testCrudWithPermissions() { Assert.assertNotNull(resourceManager.getResource(libResourceGlobal.getId().toHexString())); Assert.assertTrue(resourceManager.getResourceFile(libResourceGlobal.getId().toHexString()).getResourceFile().exists()); + + + } catch (IOException e) { + throw new RuntimeException("IO Exception", e); + } + } + + @Test + public void testManagedLibrary(){ + 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))); + + try (InputStream is = new FileInputStream(automationPackageJar); + InputStream isAnother = new FileInputStream(anotherAutomationPackageJar); + ) { + 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 + AutomationPackageUpdateParameter globalAdminParams = new AutomationPackageUpdateParameterBuilder() + .forJunit() + .withActorUser("globalAdmin") + .withEnricher(createTenantEnricher(GLOBAL_PROJECT)) + .withObjectPredicate(createAccessPredicate(GLOBAL_PROJECT)) + .withWriteAccessValidator(createWriteAccessValidator(GLOBAL_PROJECT)) + .build(); + + Resource globalLibResource = manager.createAutomationPackageResource(ResourceManager.RESOURCE_TYPE_AP_MANAGED_LIBRARY, libSource, "testManagedLibrary", globalAdminParams); + Assert.assertNotNull(globalLibResource); + Assert.assertEquals(GLOBAL_PROJECT, globalLibResource.getAttribute(ATTRIBUTE_PROJECT_NAME)); + + // 2. User in Project1 uses the managed library + AutomationPackageUpdateParameter user1CreateApParams = new AutomationPackageUpdateParameterBuilder() + .forJunit() + .withApSource(sample1ApSource) + .withApLibrarySource(AutomationPackageFileSource.withResourceId(globalLibResource.getId().toHexString())) + .withAllowUpdate(false) + .withAsync(false) + .withCheckForSameOrigin(true) + .withEnricher(createTenantEnricher(PROJECT_1)) + .withObjectPredicate(createAccessPredicate(GLOBAL_PROJECT, PROJECT_1)) + .withWriteAccessValidator(createWriteAccessValidator(PROJECT_1)) + .build(); + + Instant timeBeforeUpdate = Instant.now(); + AutomationPackageUpdateResult resultAp1 = manager.createOrUpdateAutomationPackage(user1CreateApParams); + Assert.assertEquals(CREATED, resultAp1.getStatus()); + + // new library snapshot is uploaded in nexus + providersResolver.getMavenArtifactMocks().put(libVersion1, new ResolvedMavenArtifact( + libJarUpdated, + new SnapshotMetadata("some timestamp", System.currentTimeMillis(), 1, true)) + ); + + // 3.1. The managed library cannot be created with the same name + try { + manager.createAutomationPackageResource(ResourceManager.RESOURCE_TYPE_AP_MANAGED_LIBRARY, libSource, "testManagedLibrary", globalAdminParams); + Assert.fail("Exception is not thrown"); + } catch (AutomationPackageManagerException ex){ + log.info("Caught: {}", ex.getMessage()); + } + + // 3.2 Global admin updates the library + Resource updatedGlobalLibResource = manager.updateAutomationPackageManagedLibrary( + globalLibResource.getId().toString(), libSource, "testManagedLibraryUpdated", globalAdminParams + ); + + // updated library has the same id as the original one + Assert.assertEquals(globalLibResource.getId(), updatedGlobalLibResource.getId()); + Assert.assertEquals("globalAdmin", globalLibResource.getLastModificationUser()); + Assert.assertTrue(globalLibResource.getLastModificationDate().toInstant().isAfter(timeBeforeUpdate)); + ResourceRevisionFileHandle actualLibRevision = resourceManager.getResourceFile(updatedGlobalLibResource.getId().toHexString()); + + // actual resource revision contains updated lib + Assert.assertArrayEquals(Files.readAllBytes(libJarUpdated.toPath()), Files.readAllBytes(actualLibRevision.getResourceFile().toPath())); + + // linked automation package is also updated (refreshed) + AutomationPackage apAfterLibUpdate = automationPackageAccessor.get(resultAp1.getId()); + Assert.assertEquals(FileResolver.resolveResourceId(apAfterLibUpdate.getAutomationPackageLibraryResource()), updatedGlobalLibResource.getId().toHexString()); + Assert.assertTrue(apAfterLibUpdate.getLastModificationDate().toInstant().isAfter(timeBeforeUpdate)); + + // 4. user without write permission cannot update the managed library + try { + manager.updateAutomationPackageManagedLibrary( + globalLibResource.getId().toString(), libSource, "testManagedLibraryUpdated", user1CreateApParams + ); + Assert.fail("Exception is not thrown"); + } catch (AutomationPackageManagerException ex){ + log.info("Exception caught: {}", ex.getMessage()); + } } catch (IOException e) { throw new RuntimeException("IO Exception", e); } diff --git a/step-automation-packages/step-automation-packages-manager/src/main/java/step/automation/packages/AutomationPackageManager.java b/step-automation-packages/step-automation-packages-manager/src/main/java/step/automation/packages/AutomationPackageManager.java index b3c5a07ca..df818650e 100644 --- a/step-automation-packages/step-automation-packages-manager/src/main/java/step/automation/packages/AutomationPackageManager.java +++ b/step-automation-packages/step-automation-packages-manager/src/main/java/step/automation/packages/AutomationPackageManager.java @@ -644,7 +644,7 @@ public void reloadRelatedAutomationPackages(Set automationPackagesForR public Resource createAutomationPackageResource(String resourceType, AutomationPackageFileSource fileSource, - String managedLibraryName, AutomationPackageUpdateParameter parameters) throws AutomationPackageManagerException { + String managedLibraryName, AutomationPackageAccessParameters parameters) throws AutomationPackageManagerException { try { validateSourceMode(fileSource); switch (resourceType) { From 1d9f938f3b6669bb6c2c067f3df1581b9d588a5b Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Thu, 6 Nov 2025 18:14:13 +0300 Subject: [PATCH 05/16] SED-4305: remove redundant todo --- .../automation/packages/AutomationPackageManagerEETest.java | 4 ---- 1 file changed, 4 deletions(-) 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 0097ad201..3b69b78bf 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 @@ -20,7 +20,6 @@ */ package step.automation.packages; -import jakarta.validation.constraints.AssertTrue; import org.junit.Assert; import org.junit.Test; import org.slf4j.Logger; @@ -143,7 +142,6 @@ public void testCrudWithPermissions() { .withObjectPredicate(createAccessPredicate(GLOBAL_PROJECT, PROJECT_1)) .build(); - // TODO: is it OK that the AP linked with project1 is automatically refreshed without write access to project1? AutomationPackageUpdateResult result3 = manager.createOrUpdateAutomationPackage(updateParameter); Assert.assertEquals(UPDATED, result3.getStatus()); @@ -178,8 +176,6 @@ public void testCrudWithPermissions() { Assert.assertNotNull(resourceManager.getResource(libResourceGlobal.getId().toHexString())); Assert.assertTrue(resourceManager.getResourceFile(libResourceGlobal.getId().toHexString()).getResourceFile().exists()); - - } catch (IOException e) { throw new RuntimeException("IO Exception", e); } From c5e9c1aed7a68dcad9caf93da477b63ba4ceae71 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Thu, 13 Nov 2025 08:18:07 +0300 Subject: [PATCH 06/16] SED-4350: new unit test --- .../AutomationPackageManagerEETest.java | 171 +++++++++++++++++- 1 file changed, 166 insertions(+), 5 deletions(-) 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 3b69b78bf..88a3fcacb 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 @@ -184,7 +184,6 @@ public void testCrudWithPermissions() { @Test public void testManagedLibrary(){ 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); @@ -193,11 +192,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 @@ -277,6 +273,171 @@ public void testManagedLibrary(){ } } + @Test + public void testManagedLibraryInIsolatedProjects() throws IOException { + 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()); + + // 5. 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(); + Resource updatedLib1Resource = manager.updateAutomationPackageManagedLibrary( + projectLibResource1.getId().toString(), libSource, "testManagedLibrary", user1Params + ); + + // the same id has been used + Assert.assertEquals(projectLibResource1.getId(), updatedLib1Resource.getId()); + // lib1 has been updated + Assert.assertFalse(projectLibResource1.getLastModificationDate().toInstant().isBefore(nowBeforeLib1Update)); + // lib2 is not updated + Assert.assertFalse(projectLibResource2.getLastModificationDate().toInstant().isAfter(nowBeforeLib1Update)); + Assert.assertArrayEquals(Files.readAllBytes(resourceManager.getResourceFile(projectLibResource1.getId().toHexString()).getResourceFile().toPath()), Files.readAllBytes(libJarUpdated.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)); + + // 5. User1 deletes the AP1 + manager.removeAutomationPackage(ap1.getId(), "user1", createAccessPredicate(PROJECT_1), createWriteAccessValidator(PROJECT_1)); + + // ap1 doesn't exist anymore + try { + Assert.assertNull(automationPackageAccessor.get(ap1.getId())); + Assert.fail("Exception should be thrown"); + } catch (AutomationPackageManagerException ex){ + log.info("Exception: {}", ex.getMessage()); + } + ResourceRevisionFileHandle removedResourceFile = resourceManager.getResourceFile(updatedLib1Resource.getId().toString()); + Assert.assertFalse(removedResourceFile.getResourceFile().exists()); + } + protected WriteAccessValidator createWriteAccessValidator(String ... projectNames){ return new WriteAccessValidator() { @Override From 10ee3ef9250b7e8eb78742f5cb985d8b2f1ec567 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Thu, 13 Nov 2025 08:21:35 +0300 Subject: [PATCH 07/16] SED-4350: merge master into SED-4350 --- .../step/automation/packages/AutomationPackageManagerEETest.java | 1 - 1 file changed, 1 deletion(-) 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 597bd54a5..ee5ebae6d 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 @@ -195,7 +195,6 @@ public void testManagedLibrary(){ 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 From 009ba3529415ae1019d12b85dbba472ee73c1301 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Thu, 13 Nov 2025 08:27:31 +0300 Subject: [PATCH 08/16] SED-4350: fix test --- .../automation/packages/AutomationPackageManagerEETest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ee5ebae6d..145807bea 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 @@ -406,7 +406,7 @@ public void testManagedLibraryInIsolatedProjects() throws IOException { // the same id has been used Assert.assertEquals(projectLibResource1.getId(), updatedLib1Resource.getId()); // lib1 has been updated - Assert.assertFalse(projectLibResource1.getLastModificationDate().toInstant().isBefore(nowBeforeLib1Update)); + Assert.assertFalse(updatedLib1Resource.getLastModificationDate().toInstant().isBefore(nowBeforeLib1Update)); // lib2 is not updated Assert.assertFalse(projectLibResource2.getLastModificationDate().toInstant().isAfter(nowBeforeLib1Update)); Assert.assertArrayEquals(Files.readAllBytes(resourceManager.getResourceFile(projectLibResource1.getId().toHexString()).getResourceFile().toPath()), Files.readAllBytes(libJarUpdated.toPath())); From f9b6ac8c514662dac90a5cc4d84238a66a43dc7d Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Thu, 13 Nov 2025 08:38:12 +0300 Subject: [PATCH 09/16] SED-4350: unit test --- .../packages/AutomationPackageManagerEETest.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 145807bea..3c795ee31 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 @@ -425,9 +425,14 @@ public void testManagedLibraryInIsolatedProjects() throws IOException { Assert.assertEquals(PROJECT_1, ap1.getAttribute(ATTRIBUTE_PROJECT_NAME)); Assert.assertEquals(PROJECT_2, ap2.getAttribute(ATTRIBUTE_PROJECT_NAME)); - // 5. User1 deletes the AP1 + // 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)); + // TODO: finish test after fixing p.5 + /* // ap1 doesn't exist anymore try { Assert.assertNull(automationPackageAccessor.get(ap1.getId())); @@ -437,6 +442,7 @@ public void testManagedLibraryInIsolatedProjects() throws IOException { } ResourceRevisionFileHandle removedResourceFile = resourceManager.getResourceFile(updatedLib1Resource.getId().toString()); Assert.assertFalse(removedResourceFile.getResourceFile().exists()); + */ } protected WriteAccessValidator createWriteAccessValidator(String ... projectNames){ From ae44821f8e24ac3f2bf55a33f875dfb889e30f0b Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Tue, 18 Nov 2025 09:52:50 +0300 Subject: [PATCH 10/16] SED-4350: unit test --- .../AbstractAutomationPackageManagerTest.java | 12 ++-- .../AutomationPackageManagerEETest.java | 61 +++++++++++++++---- 2 files changed, 56 insertions(+), 17 deletions(-) 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 c37f1cbfd..afd447ffa 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 3c795ee31..dad12c819 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,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import step.attachments.FileResolver; +import step.core.AbstractContext; import step.core.maven.MavenArtifactIdentifier; import step.core.objectenricher.*; import step.repositories.artifact.ResolvedMavenArtifact; @@ -34,6 +35,7 @@ import step.resources.ResourceMissingException; import step.resources.ResourceRevisionFileHandle; +import javax.xml.transform.Result; import java.io.*; import java.nio.file.Files; import java.time.Instant; @@ -399,17 +401,19 @@ public void testManagedLibraryInIsolatedProjects() throws IOException { ); Instant nowBeforeLib1Update = Instant.now(); - Resource updatedLib1Resource = manager.updateAutomationPackageManagedLibrary( - projectLibResource1.getId().toString(), libSource, "testManagedLibrary", user1Params + RefreshResourceResult refreshResourceResult = manager.getAutomationPackageResourceManager().refreshResourceAndLinkedPackages( + projectLibResource1.getId().toHexString(), user1Params, manager ); + Assert.assertEquals(RefreshResourceResult.ResultStatus.REFRESHED, refreshResourceResult.getResultStatus()); - // the same id has been used - Assert.assertEquals(projectLibResource1.getId(), updatedLib1Resource.getId()); // 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(projectLibResource1.getId().toHexString()).getResourceFile().toPath()), Files.readAllBytes(libJarUpdated.toPath())); + 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()); @@ -431,18 +435,49 @@ public void testManagedLibraryInIsolatedProjects() throws IOException { manager.removeAutomationPackage(ap1.getId(), "user1", createAccessPredicate(PROJECT_1), createWriteAccessValidator(PROJECT_1)); - // TODO: finish test after fixing p.5 - /* // ap1 doesn't exist anymore + Assert.assertNull(automationPackageAccessor.get(ap1.getId())); + try { - Assert.assertNull(automationPackageAccessor.get(ap1.getId())); + resourceManager.getResourceFile(updatedLib1Resource.getId().toString()); Assert.fail("Exception should be thrown"); - } catch (AutomationPackageManagerException ex){ - log.info("Exception: {}", ex.getMessage()); + } catch (ResourceMissingException ex){ + log.info("Resource deleted: {}", ex.getMessage()); } - ResourceRevisionFileHandle removedResourceFile = resourceManager.getResourceFile(updatedLib1Resource.getId().toString()); - Assert.assertFalse(removedResourceFile.getResourceFile().exists()); - */ + } + + 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){ From 368ff0aa26b50f59e7be390e492ed105d6c26f91 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Sun, 23 Nov 2025 18:36:55 +0300 Subject: [PATCH 11/16] SED-4357: unit tests --- .../AutomationPackageManagerEETest.java | 32 +++++++++++++++++-- .../packages/AutomationPackageReaderTest.java | 20 ++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) 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 dad12c819..113d7003b 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,7 @@ 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.*; @@ -35,7 +36,6 @@ import step.resources.ResourceMissingException; import step.resources.ResourceRevisionFileHandle; -import javax.xml.transform.Result; import java.io.*; import java.nio.file.Files; import java.time.Instant; @@ -277,7 +277,7 @@ public void testManagedLibrary(){ } @Test - public void testManagedLibraryInIsolatedProjects() throws IOException { + 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); @@ -394,7 +394,7 @@ public void testManagedLibraryInIsolatedProjects() throws IOException { Assert.assertEquals(FileResolver.resolveResourceId(ap1.getAutomationPackageLibraryResource()), projectLibResource1.getId().toHexString()); Assert.assertEquals(FileResolver.resolveResourceId(ap2.getAutomationPackageLibraryResource()), projectLibResource2.getId().toHexString()); - // 5. User1 updates the lib in project1 - AP from project2 should be untouched + // 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)) @@ -429,6 +429,32 @@ public void testManagedLibraryInIsolatedProjects() throws IOException { 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); 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..d340e050b 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,23 @@ 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()); + + } + } \ No newline at end of file From 99bb4116ea62e67a74cc142ef15797d13826dd1f Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Thu, 27 Nov 2025 10:43:39 +0300 Subject: [PATCH 12/16] SED-4357: config for aggregated jacoco report --- pom.xml | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/pom.xml b/pom.xml index 635b3c66d..74a13c710 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,98 @@ + + + + AggregatedJacocoReportEnabled + + + AggregatedJacocoReportEnabled + true + + + + + + + org.jacoco + jacoco-maven-plugin + ${dep.mvn.jacoco.version} + + + ${jacoco.overall.exec} + ${jacoco.overall.exec} + + *.jar + + + + + + + + + + + + + AggregatedJacocoReportRunTests + + + AggregatedJacocoReportRunTests + true + + + + + + + org.jacoco + jacoco-maven-plugin + + + default-prepare-agent + + prepare-agent + + + true + + + + + + + + + + + + AggregatedJacocoReportCreateSingleReports + + + AggregatedJacocoReportCreateSingleReports + true + + + + + + org.jacoco + jacoco-maven-plugin + + + default-report + validate + + report + + + + + + + From 3b4f524942bfbc6cd0d0ae74a397a8c67726e84a Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Thu, 4 Dec 2025 15:22:29 +0300 Subject: [PATCH 13/16] SED-4357: remove redundant jacoco profile --- pom.xml | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/pom.xml b/pom.xml index 74a13c710..1375f26c2 100644 --- a/pom.xml +++ b/pom.xml @@ -814,29 +814,10 @@ ${jacoco.overall.exec} ${jacoco.overall.exec} - - *.jar - - - - - - - - - AggregatedJacocoReportRunTests - - - AggregatedJacocoReportRunTests - true - - - - org.jacoco @@ -857,8 +838,8 @@ - - + + AggregatedJacocoReportCreateSingleReports From 266d59f8e5b99d21a9c80b750d1a7ef56ea36e5a Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Thu, 4 Dec 2025 17:06:09 +0300 Subject: [PATCH 14/16] SED-4357: remove redundant jacoco profile --- pom.xml | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/pom.xml b/pom.xml index 1375f26c2..6a93f02e9 100644 --- a/pom.xml +++ b/pom.xml @@ -832,28 +832,6 @@ true - - - - - - - - - - AggregatedJacocoReportCreateSingleReports - - - AggregatedJacocoReportCreateSingleReports - true - - - - - - org.jacoco - jacoco-maven-plugin - default-report validate From 32871c47956fd1e310710a3b15df747cc32ed7b8 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Thu, 4 Dec 2025 17:43:44 +0300 Subject: [PATCH 15/16] SED-4357: fix test --- .../step/automation/packages/AutomationPackageReaderTest.java | 2 ++ 1 file changed, 2 insertions(+) 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 d340e050b..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 @@ -272,6 +272,8 @@ public void testMissingDescriptor() throws IOException, AutomationPackageReading 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 From 4beb36963f738b7b3ebbe8a864e4755f0fe20468 Mon Sep 17 00:00:00 2001 From: Igor Egorov Date: Sun, 7 Dec 2025 17:16:56 +0300 Subject: [PATCH 16/16] SED-4357: fix test --- .../packages/AutomationPackageManagerEETest.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) 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 8e10a316f..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 @@ -466,12 +466,10 @@ public void testManagedLibraryInIsolatedProjects() throws IOException, ManagedLi // ap1 doesn't exist anymore Assert.assertNull(automationPackageAccessor.get(ap1.getId())); - try { - resourceManager.getResourceFile(updatedLib1Resource.getId().toString()); - Assert.fail("Exception should be thrown"); - } catch (ResourceMissingException ex){ - log.info("Resource deleted: {}", ex.getMessage()); - } + // 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) {