Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions step-automation-packages/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
<module>step-automation-packages-manager</module>
<module>step-automation-packages-client</module>
<module>step-automation-packages-controller</module>
<module>step-automation-packages-ide</module>
</modules>

<properties>
Expand Down
65 changes: 65 additions & 0 deletions step-automation-packages/step-automation-packages-ide/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ch.exense.step</groupId>
<artifactId>step-automation-packages</artifactId>
<version>0.0.0-SNAPSHOT</version>
</parent>

<artifactId>step-automation-packages-ide</artifactId>

<properties>
<groupId>ch.exense.step</groupId>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>ch.exense.step</groupId>
<artifactId>step-plans-base-artefacts</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ch.exense.step</groupId>
<artifactId>step-automation-packages-yaml</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.exense.step</groupId>
<artifactId>step-plans-core</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.exense.step</groupId>
<artifactId>step-functions-plugins-jmeter-def</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.exense.step</groupId>
<artifactId>step-functions-plugins-node-def</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.exense.step</groupId>
<artifactId>step-automation-packages-controller</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*******************************************************************************
* Copyright (C) 2026, 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
package step.core.collections;

import java.io.IOException;
import java.util.Properties;

import step.automation.packages.yaml.AutomationPackageYamlFragmentManager;
import step.core.collections.inmemory.InMemoryCollectionFactory;
import step.core.plans.Plan;

public class AutomationPackageCollectionFactory implements CollectionFactory {

private final InMemoryCollectionFactory baseFactory;
private final AutomationPackageYamlFragmentManager fragmentManager;

public AutomationPackageCollectionFactory(Properties properties, AutomationPackageYamlFragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
this.baseFactory = new InMemoryCollectionFactory(properties);
}

@Override
public <T> Collection<T> getCollection(String name, Class<T> entityClass) {

if (entityClass == Plan.class) {
return (Collection<T>) new AutomationPackagePlanCollection(fragmentManager);
}

return baseFactory.getCollection(name, entityClass);
}

@Override
public Collection<EntityVersion> getVersionedCollection(String name) {
Collection<EntityVersion> baseCollection = baseFactory.getCollection(name, EntityVersion.class);
return baseCollection;
}

@Override
public void close() throws IOException {
baseFactory.close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*******************************************************************************
* Copyright (C) 2026, 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
package step.core.collections;

import step.automation.packages.yaml.AutomationPackageYamlFragmentManager;
import step.core.collections.inmemory.InMemoryCollection;
import step.core.plans.Plan;

public class AutomationPackagePlanCollection extends InMemoryCollection<Plan> implements Collection<Plan> {


private final AutomationPackageYamlFragmentManager fragmentManager;

public AutomationPackagePlanCollection(AutomationPackageYamlFragmentManager fragmentManager) {
super(true, "plan");
this.fragmentManager = fragmentManager;
super.save(fragmentManager.getPlans());
}

@Override
public Plan save(Plan p){
return super.save(fragmentManager.savePlan(p));
}

@Override
public void save(Iterable<Plan> iterable) {
for (Plan p : iterable) {
save(p);
}
}

@Override
public void remove(Filter filter) {
find(filter, null, null, null, Integer.MAX_VALUE).forEach(fragmentManager::removePlan);
super.remove(filter);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*******************************************************************************
* Copyright (C) 2026, 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 <http://www.gnu.org/licenses/>.
******************************************************************************/
package step.core.collections;

import ch.exense.commons.app.Configuration;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import step.artefacts.Echo;
import step.automation.packages.AutomationPackageHookRegistry;
import step.automation.packages.AutomationPackageReadingException;
import step.automation.packages.JavaAutomationPackageReader;
import step.automation.packages.deserialization.AutomationPackageSerializationRegistry;
import step.automation.packages.yaml.AutomationPackageYamlFragmentManager;
import step.automation.packages.yaml.YamlAutomationPackageVersions;
import step.core.dynamicbeans.DynamicValue;
import step.core.plans.Plan;
import step.parameter.ParameterManager;
import step.parameter.automation.AutomationPackageParametersRegistration;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

import static org.junit.Assert.*;

public class AutomationPackageCollectionTest {

private static final Logger log = LoggerFactory.getLogger(AutomationPackageCollectionTest.class);

private final JavaAutomationPackageReader reader;

private final File sourceDirectory = new File("src/test/resources/samples/step-automation-packages-sample1");;
private File destinationDirectory;
private Collection<Plan> planCollection;
private final Path expectedFilesPath = sourceDirectory.toPath().resolve("expected");

public AutomationPackageCollectionTest() throws AutomationPackageReadingException {
AutomationPackageSerializationRegistry serializationRegistry = new AutomationPackageSerializationRegistry();
AutomationPackageHookRegistry hookRegistry = new AutomationPackageHookRegistry();

// accessor is not required in this test - we only read the yaml and don't store the result anywhere
AutomationPackageParametersRegistration.registerParametersHooks(hookRegistry, serializationRegistry, Mockito.mock(ParameterManager.class));

this.reader = new JavaAutomationPackageReader(YamlAutomationPackageVersions.ACTUAL_JSON_SCHEMA_PATH, hookRegistry, serializationRegistry, new Configuration());
}

@Before
public void setUp() throws IOException, AutomationPackageReadingException {
Properties properties = new Properties();
destinationDirectory = Files.createTempDirectory("automationPackageCollectionTest").toFile();
FileUtils.copyDirectory(sourceDirectory, destinationDirectory);

AutomationPackageYamlFragmentManager fragmentManager = reader.provideAutomationPackageYamlFragmentManager(destinationDirectory);
AutomationPackageCollectionFactory collectionFactory = new AutomationPackageCollectionFactory(properties, fragmentManager);
planCollection = collectionFactory.getCollection("plan", Plan.class);
}

@After
public void tearDown() throws IOException {
FileUtils.deleteDirectory(destinationDirectory);
}

@Test
public void testReadAllPlans() {
long count = planCollection.count(Filters.empty(), 100);
List<Plan> plans = planCollection.find(Filters.empty(), null, null, null, 100).collect(Collectors.toList());

assertEquals(2, count);
Set<String> names = plans.stream().map(p -> p.getAttributes().get("name")).collect(Collectors.toUnmodifiableSet());

assertEquals(2, names.size());

assertTrue(names.contains("Test Plan"));
assertTrue(names.contains("Test Plan with Composite"));
}

@Test
public void testPlanModify() throws IOException {
Optional<Plan> optionalPlan = planCollection.find(Filters.equals("attributes.name", "Test Plan"), null, null, null, 100).findFirst();

assertTrue(optionalPlan.isPresent());

Plan plan = optionalPlan.get();

Echo firstEcho = (Echo) plan.getRoot().getChildren().get(0);
DynamicValue<Object> text = firstEcho.getText();
text.setDynamic(true);
text.setExpression("new Date().toString();");

planCollection.save(plan);

assertFilesEqual(expectedFilesPath.resolve("plan1AfterModification.yml"), destinationDirectory.toPath().resolve("plans").resolve("plan1.yml"));
}


@Test
public void testPlanRenameExisting() throws IOException {
Optional<Plan> optionalPlan = planCollection.find(Filters.equals("attributes.name", "Test Plan"), null, null, null, 100).findFirst();

assertTrue(optionalPlan.isPresent());

Plan plan = optionalPlan.get();

plan.getAttributes().put("name", "New Plan Name");

planCollection.save(plan);

assertFilesEqual(expectedFilesPath.resolve("plan1AfterRename.yml"), destinationDirectory.toPath().resolve("plans").resolve("plan1.yml"));
}


@Test
public void testPlanRemoveExisting() throws IOException {
planCollection.remove(Filters.equals("attributes.name", "Test Plan"));

assertFilesEqual(expectedFilesPath.resolve("plan1AfterRemove.yml"), destinationDirectory.toPath().resolve("plans").resolve("plan1.yml"));
}

private void assertFilesEqual(Path expected, Path actual) throws IOException {
List<String> expectedLines = Files.readAllLines(expected);
List<String> actualLines = Files.readAllLines(actual);

assertEquals(expectedLines, actualLines);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/ignored
/ignoredFile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
schemaVersion: 1.0.0
name: "My package"
fragments:
- "keywords.yml"
- "plans/*.yml"
- "schedules.yml"
- "parameters.yml"
- "parameters2.yml"
- "unknown.yml"
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
fragments: []
keywords: []
plans:
- version: "1.2.0"
name: "Test Plan"
root:
testCase:
nodeName: "Test Plan"
children:
- echo:
text:
expression: "new Date().toString();"
- echo:
text:
expression: "mySimpleKey"
- callKeyword:
nodeName: "CallMyKeyword2"
inputs:
- myInput: "myValue"
keyword: "MyKeyword2"
agents: null
categories:
- "Yaml Plan"
plansPlainText:
- name: "Plain text plan"
rootType: "Sequence"
categories:
- "PlainTextPlan"
file: "plans/plan2.plan"
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
fragments: []
keywords: []
plans: []
plansPlainText:
- name: "Plain text plan"
rootType: "Sequence"
categories:
- "PlainTextPlan"
file: "plans/plan2.plan"
Loading