diff --git a/.github/workflows/kapua-ci.yaml b/.github/workflows/kapua-ci.yaml index e17f3c0fb84..7fb3b56c726 100755 --- a/.github/workflows/kapua-ci.yaml +++ b/.github/workflows/kapua-ci.yaml @@ -162,7 +162,7 @@ jobs: with: tag: '@jobs or @scheduler' needs-docker-images: 'false' - test-jobsIntegrationBase: + test-jobService: needs: build runs-on: ubuntu-latest timeout-minutes: 45 @@ -171,7 +171,18 @@ jobs: uses: actions/checkout@v4 - uses: ./.github/actions/runTestsTaggedAs with: - tag: '@jobsIntegrationBase' + tag: '@job and @it' + needs-docker-images: 'true' + test-jobEngineOperations: + needs: build + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - name: Clones Kapua repo inside the runner + uses: actions/checkout@v4 + - uses: ./.github/actions/runTestsTaggedAs + with: + tag: '@jobEngineOperations' needs-docker-images: 'true' test-jobsIntegration: needs: build diff --git a/qa/RunKapuaTests_asGitAction.sh b/qa/RunKapuaTests_asGitAction.sh index be4b9d8f8e5..34c015cbfb3 100755 --- a/qa/RunKapuaTests_asGitAction.sh +++ b/qa/RunKapuaTests_asGitAction.sh @@ -5,7 +5,7 @@ #RUNS BOTH UNIT AND INTEGRATION TESTS AND FINALLY BUILDS THE PROJECT #FIRT, JUNIT TESTS ARE LAUNCHED, IF THEY PASS CUCUMBER TESTS ARE THEN EXECUTED -cucumberTags=('@brokerAcl' '@tag' '@broker' '@device' '@deviceManagement' '@connection' '@datastore' '@user' '@userIntegrationBase' '@userIntegration' '@security' '@jobs' '@scheduler' '@jobsIntegrationBase' '@triggerService' '@account' '@translator' '@jobEngineStepDefinitions' '@jobEngineStartOfflineDevice' '@jobEngineRestartOnlineDevice' '@jobEngineStartOnlineDevice' '@jobEngineRestartOfflineDevice' '@jobEngineRestartOnlineDeviceSecondPart' '@jobsIntegration' '@jobEngineServiceStop' '@endpoint' '@deviceRegistry' '@role' '@group') +cucumberTags=('@brokerAcl' '@tag' '@broker' '@device' '@deviceManagement' '@connection' '@datastore' '@user' '@userIntegrationBase' '@userIntegration' '@security' '@jobs' '@scheduler' '@jobService' '@triggerService' '@account' '@translator' '@jobEngineStepDefinitions' '@jobEngineStartOfflineDevice' '@jobEngineRestartOnlineDevice' '@jobEngineStartOnlineDevice' '@jobEngineRestartOfflineDevice' '@jobEngineRestartOnlineDeviceSecondPart' '@jobsIntegration' '@jobEngineServiceStop' '@endpoint' '@deviceRegistry' '@role' '@group') exitCodesTests=() #will contain exit code for each batch of tests #checks if the last build command exited normally and exits if necessary diff --git a/qa/common/src/main/java/org/eclipse/kapua/qa/common/BasicSteps.java b/qa/common/src/main/java/org/eclipse/kapua/qa/common/BasicSteps.java index 2b8399c9b13..e80d8b57b2a 100644 --- a/qa/common/src/main/java/org/eclipse/kapua/qa/common/BasicSteps.java +++ b/qa/common/src/main/java/org/eclipse/kapua/qa/common/BasicSteps.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2022 Red Hat Inc and others. + * Copyright (c) 2017, 2024 Red Hat Inc and others. * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -76,7 +76,6 @@ public class BasicSteps extends TestBase { public static final String LIFECYCLE_CONSUMER_CONTAINER_NAME = "lifecycle-consumer"; public static final String AUTH_SERVICE_CONTAINER_NAME = "auth-service"; public static final String API_CONTAINER_NAME = "rest-api"; - public static final int JOB_ENGINE_CONTAINER_PORT = 8080; public static final String LAST_CREDENTIAL_ID = "LastCredentialId"; @@ -93,30 +92,114 @@ public class BasicSteps extends TestBase { private static final String ASSERT_ERROR_NAME = "AssertErrorName"; private static final String ASSERT_ERROR_CAUGHT = "AssertErrorCaught"; - private DBHelper database; + private final DBHelper database; @Inject public BasicSteps(StepData stepData, DBHelper database) { super(stepData); + this.database = database; } @Before(value = "@setup and (@env_docker or @env_docker_base)", order = 0) public void initParametersDocker(Scenario scenario) { logger.info("=====> Init parameters for docker environment..."); - setProperties(scenario, "kapuadb", "true", "localhost", "3306", "DEFAULT", "org.h2.Driver", - "jdbc:h2:tcp", "certificates/jwt/test.key", "certificates/jwt/test.cert", "localhost", "http://job-engine:8080/v1", "trusted", "MODE=MySQL"); + setProperties(scenario, + "kapuadb", + "true", + "localhost", + "3306", + "DEFAULT", + "org.h2.Driver", + "jdbc:h2:tcp", + "certificates/jwt/test.key", + "certificates/jwt/test.cert", + "localhost", + "http://job-engine:8080/v1", + "trusted", + "MODE=MySQL"); logger.info("=====> Init parameters for docker environment... DONE"); } @Before(value = "@setup and @env_none", order = 0) public void initParametersEmbedded(Scenario scenario) { logger.info("=====> Init parameters for embedded environment..."); - setProperties(scenario, "kapuadb", "true", "", "", "H2", "org.h2.Driver", "jdbc:h2:mem:", - "certificates/jwt/test.key", "certificates/jwt/test.cert", "localhost", "http://localhost:8080/v1", "trusted", null); + setProperties(scenario, + "kapuadb", + "true", + "", + "", + "H2", + "org.h2.Driver", + "jdbc:h2:mem:", + "certificates/jwt/test.key", + "certificates/jwt/test.cert", + "localhost", + "http://localhost:8080/v1", + "trusted", + null); logger.info("=====> Init parameters for embedded environment... DONE"); } + @Before(value = "(@env_docker or @env_docker_base) and not (@setup or @teardown)", order = 0) + public void beforeScenarioDockerFull(Scenario scenario) { + beforeCommon(scenario); + } + + @Before(value = "@env_none and not (@setup or @teardown)", order = 0) + public void beforeScenarioDockerBase(Scenario scenario) { + beforeCommon(scenario); + + databaseInit(); + } + + @After(value = "@env_docker_base and @setup", order = 0) + public void afterScenarioDockerBaseSetup(Scenario scenario) { + databaseInit(); + } + + @After(value = "(@env_docker or @env_docker_base) and not (@setup or @teardown)", order = 0) + public void afterScenarioDockerFull(Scenario scenario) { + // afterScenarioDocker(scenario); + logger.info("Database cleanup..."); + database.deleteAll(); + logger.info("Database cleanup... DONE"); + SecurityUtils.getSubject().logout(); + KapuaSecurityUtils.clearSession(); + } + + @After(value = "@env_none and not (@setup or @teardown)", order = 0) + public void afterScenarioNone(Scenario scenario) { + logger.info("Database drop..."); + try { + database.dropAll(); + database.close(); + } catch (Exception e) { + logger.error("Failed execute @After", e); + } + logger.info("Database drop... DONE"); + KapuaSecurityUtils.clearSession(); + } + + protected void beforeCommon(Scenario scenario) { + this.scenario = scenario; + stepData.clear(); + } + + protected void databaseInit() { + database.init(); + // Create KapuaSession using KapuaSecurityUtils and kapua-sys user as logged in user. + // All operations on database are performed using system user. + // Only for unit tests. Integration tests assume that a real login is performed. + KapuaSession kapuaSession = new KapuaSession(null, SYS_SCOPE_ID, SYS_USER_ID); + KapuaSecurityUtils.setSession(kapuaSession); + } + + + // + // Data Table Types + // + @DataTableType public CucAccount cucAccount(Map entry) { return new CucAccount( @@ -330,6 +413,9 @@ public CucUserProfile cucUserProfile(Map entry) { entry.get("email")); } + // + // Parameter Types + // @ParameterType(".*") public org.eclipse.kapua.transport.message.jms.JmsTopic topic(String topic) { return new JmsTopic(topic); @@ -337,13 +423,26 @@ public org.eclipse.kapua.transport.message.jms.JmsTopic topic(String topic) { @ParameterType(value = "true|True|TRUE|false|False|FALSE") public boolean booleanValue(String value) { - return Boolean.valueOf(value); - } + return Boolean.parseBoolean(value); + } + + private void setProperties(Scenario scenario, + String schema, + String updateSchema, + String dbHost, + String dbPort, + String dbConnResolver, + String dbDriver, + String jdbcConnection, + String jwtKey, + String jwtCertificate, + String brokerHost, + String jobEngineUrl, + String jobEngineAuthMode, + String additionalOptions) { - private void setProperties(Scenario scenario, String schema, String updateSchema, - String dbHost, String dbPort, String dbConnResolver, String dbDriver, String jdbcConnection, - String jwtKey, String jwtCertificate, String brokerHost, String jobEngineUrl, String jobEngineAuthMode, String additionalOptions) { SystemSetting.resetInstance(); + System.setProperty(SystemSettingKey.DB_SCHEMA.key(), schema); System.setProperty(SystemSettingKey.DB_SCHEMA_UPDATE.key(), updateSchema); System.setProperty(SystemSettingKey.DB_CONNECTION_HOST.key(), dbHost); @@ -392,80 +491,6 @@ private void setSpecificProperties(Scenario scenario) { } } - @Before - public void checkWaitMultipier() { - if (WAIT_MULTIPLIER != 1.0d) { - logger.info("Wait multiplier active: {}", WAIT_MULTIPLIER); - } - } - - @Before(value = "(@env_docker or @env_docker_base) and not (@setup or @teardown)", order = 0) - public void beforeScenarioDockerFull(Scenario scenario) { - beforeCommon(scenario); - } - - @Before(value = "@env_none and not (@setup or @teardown)", order = 0) - public void beforeScenarioDockerBase(Scenario scenario) { - beforeCommon(scenario); - databaseInit(); - } - - @After(value = "(@env_docker or @env_docker_base) and not (@setup or @teardown)", order = 0) - public void afterScenarioDockerFull(Scenario scenario) { - afterScenarioDocker(scenario); - } - - @After(value = "@env_docker_base and @setup", order = 0) - public void afterScenarioDockerBaseSetup(Scenario scenario) { - databaseInit(); - } - - @After(value = "@env_none and not (@setup or @teardown)", order = 0) - public void afterScenarioNone(Scenario scenario) { - afterScenarioNoDocker(scenario); - } - - protected void beforeCommon(Scenario scenario) { - this.scenario = scenario; - stepData.clear(); - } - - protected void databaseInit() { - database.init(); - // Create KapuaSession using KapuaSecurtiyUtils and kapua-sys user as logged in user. - // All operations on database are performed using system user. - // Only for unit tests. Integration tests assume that a real login is performed. - KapuaSession kapuaSession = new KapuaSession(null, SYS_SCOPE_ID, SYS_USER_ID); - KapuaSecurityUtils.setSession(kapuaSession); - } - - protected void afterScenarioDocker(Scenario scenario) { - logger.info("Database cleanup..."); - database.deleteAll(); - logger.info("Database cleanup... DONE"); - SecurityUtils.getSubject().logout(); - KapuaSecurityUtils.clearSession(); - } - - protected void afterScenarioNoDocker(Scenario scenario) { - logger.info("Database drop..."); - try { - database.dropAll(); - database.close(); - } catch (Exception e) { - logger.error("Failed execute @After", e); - } - logger.info("Database drop... DONE"); - KapuaSecurityUtils.clearSession(); - } - - @Given("A placeholder step") - public void doNothing() { - // An empty placeholder step. Just a breakpoint anchor point. Used to pause - // test execution by placing a breakpoint into. - Integer a = 10; - } - @Given("Scope with ID {int}") public void setSpecificScopeId(Integer id) { stepData.put(LAST_ACCOUNT_ID, getKapuaId(id)); diff --git a/qa/common/src/main/java/org/eclipse/kapua/qa/common/DBHelper.java b/qa/common/src/main/java/org/eclipse/kapua/qa/common/DBHelper.java index b080b346828..3ba27c97195 100644 --- a/qa/common/src/main/java/org/eclipse/kapua/qa/common/DBHelper.java +++ b/qa/common/src/main/java/org/eclipse/kapua/qa/common/DBHelper.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2022 Eurotech and/or its affiliates and others + * Copyright (c) 2017, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -100,7 +100,7 @@ public void deleteAllAndClose() { */ public void deleteAll() { KapuaConfigurableServiceSchemaUtilsWithResources.scriptSession(FULL_SCHEMA_PATH, DELETE_SCRIPT); - Optional.ofNullable(cacheManager).ifPresent(cm -> cm.invalidateAll()); + Optional.ofNullable(cacheManager).ifPresent(KapuaCacheManager::invalidateAll); } public void dropAll() throws SQLException { @@ -118,7 +118,7 @@ public void dropAll() throws SQLException { } else { logger.warn("================================> invoked drop all on closed connection!"); } - Optional.ofNullable(cacheManager).ifPresent(cm -> cm.invalidateAll()); + Optional.ofNullable(cacheManager).ifPresent(KapuaCacheManager::invalidateAll); } } diff --git a/qa/common/src/main/java/org/eclipse/kapua/qa/common/TestJAXBContextProvider.java b/qa/common/src/main/java/org/eclipse/kapua/qa/common/TestJAXBContextProvider.java index 7d176045afa..3b239bf124b 100644 --- a/qa/common/src/main/java/org/eclipse/kapua/qa/common/TestJAXBContextProvider.java +++ b/qa/common/src/main/java/org/eclipse/kapua/qa/common/TestJAXBContextProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2022 Eurotech and/or its affiliates and others + * Copyright (c) 2016, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -14,7 +14,17 @@ import org.eclipse.kapua.KapuaException; import org.eclipse.kapua.commons.configuration.metatype.TscalarImpl; +import org.eclipse.kapua.commons.rest.model.IsJobRunningResponse; import org.eclipse.kapua.commons.rest.model.errors.ExceptionInfo; +import org.eclipse.kapua.commons.rest.model.errors.JobAlreadyRunningExceptionInfo; +import org.eclipse.kapua.commons.rest.model.errors.JobInvalidTargetExceptionInfo; +import org.eclipse.kapua.commons.rest.model.errors.JobMissingStepExceptionInfo; +import org.eclipse.kapua.commons.rest.model.errors.JobMissingTargetExceptionInfo; +import org.eclipse.kapua.commons.rest.model.errors.JobNotRunningExceptionInfo; +import org.eclipse.kapua.commons.rest.model.errors.JobResumingExceptionInfo; +import org.eclipse.kapua.commons.rest.model.errors.JobRunningExceptionInfo; +import org.eclipse.kapua.commons.rest.model.errors.JobStartingExceptionInfo; +import org.eclipse.kapua.commons.rest.model.errors.JobStoppingExceptionInfo; import org.eclipse.kapua.commons.service.event.store.api.EventStoreRecordCreator; import org.eclipse.kapua.commons.service.event.store.api.EventStoreRecordListResult; import org.eclipse.kapua.commons.service.event.store.api.EventStoreRecordQuery; @@ -133,13 +143,28 @@ public JAXBContext getJAXBContext() throws KapuaException { // Jobs Job.class, - JobStartOptionsClient.class, - JobStartOptions.class, JobListResult.class, JobXmlRegistry.class, + + // Job Engine + JobStartOptionsClient.class, + JobStartOptions.class, JobTargetSublist.class, + IsJobRunningResponse.class, JobStepPropertiesOverrides.class, + // Job Engine Client + JobAlreadyRunningExceptionInfo.class, + JobInvalidTargetExceptionInfo.class, + JobMissingStepExceptionInfo.class, + JobMissingTargetExceptionInfo.class, + JobNotRunningExceptionInfo.class, + JobResumingExceptionInfo.class, + JobRunningExceptionInfo.class, + JobStartingExceptionInfo.class, + JobStoppingExceptionInfo.class, + + // Device Management Command DeviceCommandInput.class, DeviceCommandOutput.class, diff --git a/qa/integration-steps/src/main/java/org/eclipse/kapua/qa/integration/steps/DockerSteps.java b/qa/integration-steps/src/main/java/org/eclipse/kapua/qa/integration/steps/DockerSteps.java index daada3dbb2e..ae6079dfebf 100644 --- a/qa/integration-steps/src/main/java/org/eclipse/kapua/qa/integration/steps/DockerSteps.java +++ b/qa/integration-steps/src/main/java/org/eclipse/kapua/qa/integration/steps/DockerSteps.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2022 Eurotech and/or its affiliates and others + * Copyright (c) 2019, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -305,9 +305,20 @@ public void startDockerEnvironmentWithResources(List dockerContainers) t } } - private void cleanDockerEnvironmentInternal() throws DockerException, InterruptedException { + /** + * Stops all running Docker containers and removes the Docker network + * + * @throws Exception + * @since 2.1.0 + */ + @Given("Stop Docker environment") + public void cleanDockerEnvironmentInternal() throws Exception { removeContainers( Arrays.asList( + BasicSteps.TELEMETRY_CONSUMER_CONTAINER_NAME, + BasicSteps.LIFECYCLE_CONSUMER_CONTAINER_NAME, + BasicSteps.AUTH_SERVICE_CONTAINER_NAME, + BasicSteps.MESSAGE_BROKER_CONTAINER_NAME, BasicSteps.JOB_ENGINE_CONTAINER_NAME, BasicSteps.EVENTS_BROKER_CONTAINER_NAME, BasicSteps.ES_CONTAINER_NAME, diff --git a/qa/integration/src/test/java/org/eclipse/kapua/integration/service/job/RunJobServiceI9nTest.java b/qa/integration/src/test/java/org/eclipse/kapua/integration/service/job/RunJobServiceI9nTest.java index b0f0b5cb430..9b80438a033 100644 --- a/qa/integration/src/test/java/org/eclipse/kapua/integration/service/job/RunJobServiceI9nTest.java +++ b/qa/integration/src/test/java/org/eclipse/kapua/integration/service/job/RunJobServiceI9nTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2022 Eurotech and/or its affiliates and others + * Copyright (c) 2019, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -18,18 +18,22 @@ @RunWith(Cucumber.class) @CucumberOptions( - features = {"classpath:features/job/JobServiceI9n.feature", + features = { + "classpath:features/job/JobServiceI9n.feature", "classpath:features/job/JobTargetsServiceI9n.feature", "classpath:features/job/JobExecutionServiceI9n.feature" }, - glue = {"org.eclipse.kapua.service.job.steps", + glue = { + "org.eclipse.kapua.service.job.steps", "org.eclipse.kapua.service.user.steps", "org.eclipse.kapua.qa.common", "org.eclipse.kapua.qa.integration.steps" }, - plugin = {"pretty", + plugin = { + "pretty", "html:target/cucumber/JobServiceI9n", - "json:target/JobServiceI9n_cucumber.json"}, + "json:target/JobServiceI9n_cucumber.json" + }, monochrome = true) public class RunJobServiceI9nTest { } diff --git a/qa/integration/src/test/java/org/eclipse/kapua/integration/service/jobEngine/RunJobEngineService9nTest.java b/qa/integration/src/test/java/org/eclipse/kapua/integration/service/jobEngine/RunJobEngineService9nTest.java new file mode 100644 index 00000000000..4f277d8da84 --- /dev/null +++ b/qa/integration/src/test/java/org/eclipse/kapua/integration/service/jobEngine/RunJobEngineService9nTest.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2024, 2024 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech - initial API and implementation + *******************************************************************************/ +package org.eclipse.kapua.integration.service.jobEngine; + +import io.cucumber.junit.Cucumber; +import io.cucumber.junit.CucumberOptions; +import org.junit.runner.RunWith; + +@RunWith(Cucumber.class) +@CucumberOptions( + features = { + "classpath:features/jobEngine/JobEngineServiceOperations.feature", + }, + glue = { + "org.eclipse.kapua.service.job.steps", + "org.eclipse.kapua.service.user.steps", + "org.eclipse.kapua.qa.common", + "org.eclipse.kapua.qa.integration.steps", + "org.eclipse.kapua.service.account.steps", + "org.eclipse.kapua.service.device.registry.steps", + }, + plugin = { + "pretty", + "html:target/cucumber/JobEngineServiceOfflineDeviceI9n", + "json:target/JobEngineServiceOfflineDeviceI9n_cucumber.json" + }, + monochrome = true) +public class RunJobEngineService9nTest { +} diff --git a/qa/integration/src/test/resources/features/job/JobExecutionServiceI9n.feature b/qa/integration/src/test/resources/features/job/JobExecutionServiceI9n.feature index d4620d791b9..fb9c22b6d07 100644 --- a/qa/integration/src/test/resources/features/job/JobExecutionServiceI9n.feature +++ b/qa/integration/src/test/resources/features/job/JobExecutionServiceI9n.feature @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2017, 2022 Eurotech and/or its affiliates and others +# Copyright (c) 2017, 2024 Eurotech and/or its affiliates and others # # This program and the accompanying materials are made # available under the terms of the Eclipse Public License 2.0 @@ -10,9 +10,10 @@ # Contributors: # Eurotech - initial API and implementation ############################################################################### -@jobsIntegrationBase -@jobExecutionService @env_docker_base +@it # This is an IT because Job.*Service operations may require a Job Engine instance running +@job +@jobExecutionService Feature: Job Execution service CRUD tests The Job service is responsible for maintaining the status of the target step executions. @@ -155,7 +156,7 @@ Feature: Job Execution service CRUD tests And I test the sanity of the job execution factory @teardown - Scenario: Stop test environment - Given Stop full docker environment + Scenario: Tear down test resources + Given Stop Docker environment And Clean Locator Instance And Reset Security Context diff --git a/qa/integration/src/test/resources/features/job/JobServiceI9n.feature b/qa/integration/src/test/resources/features/job/JobServiceI9n.feature index da603e9e072..f36a777accd 100644 --- a/qa/integration/src/test/resources/features/job/JobServiceI9n.feature +++ b/qa/integration/src/test/resources/features/job/JobServiceI9n.feature @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2017, 2022 Eurotech and/or its affiliates and others +# Copyright (c) 2017, 2024 Eurotech and/or its affiliates and others # # This program and the accompanying materials are made # available under the terms of the Eclipse Public License 2.0 @@ -10,9 +10,10 @@ # Contributors: # Eurotech - initial API and implementation ############################################################################### -@jobsIntegrationBase -@jobService @env_docker_base +@it # This is an IT because Job.*Service operations may require a Job Engine instance running +@job +@jobService Feature: Job service CRUD tests The Job service is responsible for executing scheduled actions on various targets. @@ -255,7 +256,7 @@ Feature: Job service CRUD tests Then No exception was thrown @teardown - Scenario: Stop test environment - Given Stop full docker environment + Scenario: Tear down test resources + Given Stop Docker environment And Clean Locator Instance And Reset Security Context \ No newline at end of file diff --git a/qa/integration/src/test/resources/features/job/JobTargetsServiceI9n.feature b/qa/integration/src/test/resources/features/job/JobTargetsServiceI9n.feature index 78b67856d27..6ed256a1feb 100644 --- a/qa/integration/src/test/resources/features/job/JobTargetsServiceI9n.feature +++ b/qa/integration/src/test/resources/features/job/JobTargetsServiceI9n.feature @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2017, 2022 Eurotech and/or its affiliates and others +# Copyright (c) 2017, 2024 Eurotech and/or its affiliates and others # # This program and the accompanying materials are made # available under the terms of the Eclipse Public License 2.0 @@ -10,9 +10,10 @@ # Contributors: # Eurotech - initial API and implementation ############################################################################### -@jobsIntegrationBase -@jobTargetService @env_docker_base +@it # This is an IT because Job.*Service operations may require a Job Engine instance running +@job +@jobTargetService Feature: Job Target service CRUD tests The Job service is responsible for maintaining a list of job targets. @@ -156,7 +157,7 @@ Feature: Job Target service CRUD tests When I test the sanity of the job target factory @teardown - Scenario: Stop test environment - Given Stop full docker environment + Scenario: Tear down test resources + Given Stop Docker environment And Clean Locator Instance And Reset Security Context diff --git a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceKeystoreStepDefinitionsI9n.feature b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceKeystoreStepDefinitionsI9n.feature index deea0527ae4..f276d267cba 100644 --- a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceKeystoreStepDefinitionsI9n.feature +++ b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceKeystoreStepDefinitionsI9n.feature @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2021, 2022 Eurotech and/or its affiliates and others +# Copyright (c) 2021, 2024 Eurotech and/or its affiliates and others # # This program and the accompanying materials are made # available under the terms of the Eclipse Public License 2.0 @@ -10,9 +10,11 @@ # Contributors: # Eurotech - initial API and implementation ############################################################################### +@env_docker +@it +@jobEngine @jobEngineStepDefinitions @deviceManagementKeystore -@env_docker Feature: Job Engine Service - Keystore Step Definitions @@ -149,6 +151,6 @@ Feature: Job Engine Service - Keystore Step Definitions And I logout @teardown - Scenario: Stop full docker environment - Given Stop full docker environment + Scenario: Tear down test resources + Given Stop Docker environment And Clean Locator Instance \ No newline at end of file diff --git a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceOperations.feature b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceOperations.feature new file mode 100644 index 00000000000..a896428cadf --- /dev/null +++ b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceOperations.feature @@ -0,0 +1,262 @@ +############################################################################### +# Copyright (c) 2024, 2024 Eurotech and/or its affiliates and others +# +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Eurotech - initial API and implementation +############################################################################### +@env_docker +@it +@jobEngine +@jobEngineOperations + +Feature: JobEngineService stop job tests with online device + Job Engine Service test scenarios for stopping job. This feature file contains scenarios for stopping job with one target and one step, + one target and multiple steps, multiple targets and one step and multiple targets and multiple steps. + + @setup + Scenario: Setup test resources + Given Init Security Context + And Start Docker environment with resources + | db | + | events-broker | + | job-engine | + | message-broker | + | broker-auth-service | + | consumer-lifecycle | + + Scenario: Start a Job - Without JobTarget + + Given I login as user with name "kapua-sys" and password "kapua-password" + And I create a job with the name "Test Job - Empty Targets" + Then I expect the exception "JobMissingTargetException" with the text "does not have targets configured" + And I start a job + And An exception was thrown + + Scenario: Start a Job - Without JobStep + + Given I login as user with name "kapua-sys" and password "kapua-password" + And I create a device with name "Test Target" + And I create a job with the name "TestJob - Empty Steps" + And I add device targets to job + | Test Target | + Then I expect the exception "JobMissingStepException" with the text "does not have steps configured" + And I start a job + And An exception was thrown + + Scenario: Start a Job - JobTarget ok + + Given I login as user with name "kapua-sys" and password "kapua-password" + And I start the Kura Mock + And Device birth message is sent + And Device "rpione3" is connected within 10s + And I create a job with the name "TestJob" + And I add device targets to job + | rpione3 | + And I search for step definition with the name + | Bundle Start | + And I add job step to job with name "Test Step - Bundle Start" and with selected job step definition and properties + | name | type | value | + | bundleId | java.lang.String | 34 | + | timeout | java.lang.Long | 5000 | + When I start a job + And I wait job to finish its execution up to 10s + Then I confirm that job has 1 job execution + And I confirm that job target in job has step index 0 and status "PROCESS_OK" + + Scenario: Start a Job - JobTarget failed + + Given I login as user with name "kapua-sys" and password "kapua-password" + And I create a device with name "Test Target" + And I create a job with the name "TestJob" + And I add device targets to job + | Test Target | + And I search for step definition with the name + | Bundle Start | + And I add job step to job with name "Test Step - Bundle Start" and with selected job step definition and properties + | name | type | value | + | bundleId | java.lang.String | 34 | + | timeout | java.lang.Long | 5000 | + When I start a job + And I wait job to finish its execution up to 10s + Then I confirm that job has 1 job execution + And I confirm that job target in job has step index 0 and status "PROCESS_FAILED" + + Scenario: Start a Job - JobTarget ok then ok + + Given I login as user with name "kapua-sys" and password "kapua-password" + And I start the Kura Mock + And Device birth message is sent + And Device "rpione3" is connected within 10s + And I create a job with the name "TestJob" + And I add device targets to job + | rpione3 | + And I search for step definition with the name + | Bundle Start | + And I add job step to job with name "Test Step - Bundle Start" and with selected job step definition and properties + | name | type | value | + | bundleId | java.lang.String | 34 | + | timeout | java.lang.Long | 5000 | + When I start a job + And I wait job to finish its execution up to 10s + Then I confirm that job has 1 job execution + And I confirm that job target in job has step index 0 and status "PROCESS_OK" + When I start a job + And I wait job to finish its execution up to 10s + Then I confirm that job has 2 job execution + And I confirm that job target in job has step index 0 and status "PROCESS_OK" + + Scenario: Start a Job - JobTarget failed then ok + + Given I login as user with name "kapua-sys" and password "kapua-password" + And I create a device with name "rpione3" + And I create a job with the name "TestJob" + And I add device targets to job + | rpione3 | + And I search for step definition with the name + | Bundle Start | + And I add job step to job with name "Test Step - Bundle Start" and with selected job step definition and properties + | name | type | value | + | bundleId | java.lang.String | 34 | + | timeout | java.lang.Long | 5000 | + When I start a job + And I wait job to finish its execution up to 10s + Then I confirm that job has 1 job execution + And I confirm that job target in job has step index 0 and status "PROCESS_FAILED" + Then I start the Kura Mock + And Device birth message is sent + And Device "rpione3" is connected within 10s + When I start a job + And I wait job to finish its execution up to 10s + Then I confirm that job has 2 job execution + And I confirm that job target in job has step index 0 and status "PROCESS_OK" + + Scenario: Start a Job - Two JobSteps + + Given I login as user with name "kapua-sys" and password "kapua-password" + And I start the Kura Mock + And Device birth message is sent + And Device "rpione3" is connected within 10s + And I create a job with the name "TestJob" + And I add device targets to job + | rpione3 | + And I search for step definition with the name + | Bundle Start | + And I add job step to job with name "Test Step - Bundle Start" and with selected job step definition and properties + | name | type | value | + | bundleId | java.lang.String | 34 | + | timeout | java.lang.Long | 5000 | + And I search for step definition with the name + | Bundle Stop | + And I add job step to job with name "Test Step - Bundle Stop" and with selected job step definition and properties + | name | type | value | + | bundleId | java.lang.String | 34 | + | timeout | java.lang.Long | 5000 | + When I start a job + And I wait job to finish its execution up to 10s + Then I confirm that job has 1 job execution + And I confirm that job target in job has step index 1 and status "PROCESS_OK" + + Scenario: Check a Job - Running status + + Given I login as user with name "kapua-sys" and password "kapua-password" + And I start the Kura Mock + And Device birth message is sent + And Device "rpione3" is connected within 10s + And I create a job with the name "TestJob" + And I add device targets to job + | rpione3 | + And I search for step definition with the name + | Command Execution | + And I add job step to job with name "Test Step - Command Exec" and with selected job step definition and properties + | name | type | value | + | commandInput | org.eclipse.kapua.service.device.management.command.DeviceCommandInput | ping-c108.8.8.830000false | + | timeout | java.lang.Long | 15000 | + Then I confirm job is not running + When I start a job + And I wait for 5 seconds + Then I confirm job is running + When I wait job to finish its execution up to 30s + Then I confirm job is not running + Then I confirm that job has 1 job execution + And I confirm that job target in job has step index 0 and status "PROCESS_OK" + + Scenario: Stop a Job - Not running, never run + Given I login as user with name "kapua-sys" and password "kapua-password" + And I create a device with name "Test Target" + And I create a job with the name "TestJob" + And I add device targets to job + | Test Target | + And I search for step definition with the name + | Bundle Start | + And I add job step to job with name "Test Step - Bundle Start" and with selected job step definition and properties + | name | type | value | + | bundleId | java.lang.String | 34 | + | timeout | java.lang.Long | 5000 | + Then I expect the exception "JobNotRunningException" with the text "cannot be stopped because it is not running" + And I stop the job + And An exception was thrown + + Scenario: Stop a Job - Not running, but was + + Given I login as user with name "kapua-sys" and password "kapua-password" + And I start the Kura Mock + And Device birth message is sent + And Device "rpione3" is connected within 10s + And I create a job with the name "TestJob" + And I add device targets to job + | rpione3 | + And I search for step definition with the name + | Bundle Start | + And I add job step to job with name "Test Step - Bundle Start" and with selected job step definition and properties + | name | type | value | + | bundleId | java.lang.String | 34 | + | timeout | java.lang.Long | 5000 | + When I start a job + And I wait job to finish its execution up to 10s + Then I expect the exception "JobNotRunningException" with the text "cannot be stopped because it is not running" + And I stop the job + And An exception was thrown + + Scenario: Stop a Job - Running + Given I login as user with name "kapua-sys" and password "kapua-password" + And I start the Kura Mock + And Device birth message is sent + And Device "rpione3" is connected within 10s + And I create a job with the name "TestJob" + And I add device targets to job + | rpione3 | + And I search for step definition with the name + | Command Execution | + And I add job step to job with name "Test Step - Command Exec" and with selected job step definition and properties + | name | type | value | + | commandInput | org.eclipse.kapua.service.device.management.command.DeviceCommandInput | ping-c108.8.8.830000false | + | timeout | java.lang.Long | 15000 | + And I search for step definition with the name + | Bundle Start | + And I add job step to job with name "Test Step - Bundle Start" and with selected job step definition and properties + | name | type | value | + | bundleId | java.lang.String | 34 | + | timeout | java.lang.Long | 5000 | + And I start a job + And I wait for 5 seconds + When I stop the job + Then I wait job to finish its execution up to 10s + And I confirm that job has 1 job execution + And I confirm that job target in job has step index 1 and status "PROCESS_AWAITING" + When I start a job + Then I wait job to finish its execution up to 10s + And I confirm that job has 2 job execution + And I confirm that job target in job has step index 1 and status "PROCESS_OK" + + @teardown + Scenario: Tear down test resources + Given I logout + And KuraMock is disconnected + And Stop Docker environment + And Clean Locator Instance \ No newline at end of file diff --git a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceRestartOfflineDeviceI9n.feature b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceRestartOfflineDeviceI9n.feature index dfa1a121ad1..91343912621 100644 --- a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceRestartOfflineDeviceI9n.feature +++ b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceRestartOfflineDeviceI9n.feature @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2019, 2022 Eurotech and/or its affiliates and others +# Copyright (c) 2019, 2024 Eurotech and/or its affiliates and others # # This program and the accompanying materials are made # available under the terms of the Eclipse Public License 2.0 @@ -10,8 +10,10 @@ # Contributors: # Eurotech - initial API and implementation ############################################################################### -@jobEngineRestartOfflineDevice @env_docker +@it +@jobEngine +@jobEngineRestartOfflineDevice Feature: JobEngineService tests for restarting job with offline device @@ -1018,6 +1020,6 @@ Feature: JobEngineService tests for restarting job with offline device And I logout @teardown - Scenario: Stop full docker environment - Given Stop full docker environment + Scenario: Tear down test resources + Given Stop Docker environment And Clean Locator Instance diff --git a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceRestartOnlineDeviceI9n.feature b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceRestartOnlineDeviceI9n.feature index 8a754d3eacf..4c238143fb0 100644 --- a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceRestartOnlineDeviceI9n.feature +++ b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceRestartOnlineDeviceI9n.feature @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2019, 2022 Eurotech and/or its affiliates and others +# Copyright (c) 2019, 2024 Eurotech and/or its affiliates and others # # This program and the accompanying materials are made # available under the terms of the Eclipse Public License 2.0 @@ -10,8 +10,10 @@ # Contributors: # Eurotech - initial API and implementation ############################################################################### -@jobEngineRestartOnlineDevice @env_docker +@it +@jobEngine +@jobEngineRestartOnlineDevice Feature: JobEngineService restart job tests with online device @@ -501,6 +503,6 @@ Feature: JobEngineService restart job tests with online device And I logout @teardown - Scenario: Stop full docker environment - Given Stop full docker environment + Scenario: Tear down test resources + Given Stop Docker environment And Clean Locator Instance diff --git a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceRestartOnlineDeviceSecondPartI9n.feature b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceRestartOnlineDeviceSecondPartI9n.feature index 2f1e9e59f13..bd517a6c55a 100644 --- a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceRestartOnlineDeviceSecondPartI9n.feature +++ b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceRestartOnlineDeviceSecondPartI9n.feature @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2019, 2022 Eurotech and/or its affiliates and others +# Copyright (c) 2019, 2024 Eurotech and/or its affiliates and others # # This program and the accompanying materials are made # available under the terms of the Eclipse Public License 2.0 @@ -10,8 +10,10 @@ # Contributors: # Eurotech - initial API and implementation ############################################################################### -@jobEngineRestartOnlineDeviceSecondPart @env_docker +@it +@jobEngine +@jobEngineRestartOnlineDeviceSecondPart Feature: JobEngineService restart job tests with online device - second part @@ -428,6 +430,6 @@ Feature: JobEngineService restart job tests with online device - second part And I logout @teardown - Scenario: Stop full docker environment - Given Stop full docker environment + Scenario: Tear down test resources + Given Stop Docker environment And Clean Locator Instance diff --git a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceStartOfflineDeviceI9n.feature b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceStartOfflineDeviceI9n.feature index 3be6ff7b970..228678f872d 100644 --- a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceStartOfflineDeviceI9n.feature +++ b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceStartOfflineDeviceI9n.feature @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2019, 2022 Eurotech and/or its affiliates and others +# Copyright (c) 2019, 2024 Eurotech and/or its affiliates and others # # This program and the accompanying materials are made # available under the terms of the Eclipse Public License 2.0 @@ -10,8 +10,10 @@ # Contributors: # Eurotech - initial API and implementation ############################################################################### -@jobEngineStartOfflineDevice @env_docker +@it +@jobEngine +@jobEngineStartOfflineDevice Feature: JobEngineService tests for starting job with offline device @@ -1132,6 +1134,6 @@ Feature: JobEngineService tests for starting job with offline device And I logout @teardown - Scenario: Stop full docker environment - Given Stop full docker environment + Scenario: Tear down test resources + Given Stop Docker environment And Clean Locator Instance diff --git a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceStartOnlineDeviceI9n.feature b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceStartOnlineDeviceI9n.feature index 89c58b3ab4d..fc70d79fc55 100644 --- a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceStartOnlineDeviceI9n.feature +++ b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceStartOnlineDeviceI9n.feature @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2019, 2022 Eurotech and/or its affiliates and others +# Copyright (c) 2019, 2024 Eurotech and/or its affiliates and others # # This program and the accompanying materials are made # available under the terms of the Eclipse Public License 2.0 @@ -10,8 +10,10 @@ # Contributors: # Eurotech - initial API and implementation ############################################################################### -@jobEngineStartOnlineDevice @env_docker +@it +@jobEngine +@jobEngineStartOnlineDevice Feature: JobEngineService start job tests with online device @@ -861,6 +863,6 @@ Feature: JobEngineService start job tests with online device And I logout @teardown - Scenario: Stop full docker environment - Given Stop full docker environment + Scenario: Tear down test resources + Given Stop Docker environment And Clean Locator Instance diff --git a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceStopOnlineDeviceI9n.feature b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceStopOnlineDeviceI9n.feature index 6a68c9ab737..a142ee947dc 100644 --- a/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceStopOnlineDeviceI9n.feature +++ b/qa/integration/src/test/resources/features/jobEngine/JobEngineServiceStopOnlineDeviceI9n.feature @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2019, 2022 Eurotech and/or its affiliates and others +# Copyright (c) 2019, 2024 Eurotech and/or its affiliates and others # # This program and the accompanying materials are made # available under the terms of the Eclipse Public License 2.0 @@ -10,8 +10,10 @@ # Contributors: # Eurotech - initial API and implementation ############################################################################### -@jobEngineServiceStop @env_docker +@it +@jobEngine +@jobEngineServiceStop Feature: JobEngineService stop job tests with online device Job Engine Service test scenarios for stopping job. This feature file contains scenarios for stopping job with one target and one step, @@ -512,6 +514,6 @@ Feature: JobEngineService stop job tests with online device # And I logout @teardown - Scenario: Stop full docker environment - Given Stop full docker environment + Scenario: Tear down test resources + Given Stop Docker environment And Clean Locator Instance diff --git a/qa/integration/src/test/resources/features/jobScheduling/ExecuteOnDeviceConnectI9n.feature b/qa/integration/src/test/resources/features/jobScheduling/ExecuteOnDeviceConnectI9n.feature index 7b848b659b3..6b47d31f3ea 100644 --- a/qa/integration/src/test/resources/features/jobScheduling/ExecuteOnDeviceConnectI9n.feature +++ b/qa/integration/src/test/resources/features/jobScheduling/ExecuteOnDeviceConnectI9n.feature @@ -10,8 +10,8 @@ # Contributors: # Eurotech - initial API and implementation ############################################################################### -@jobsIntegration @env_docker +@jobsIntegration Feature: JobEngineService execute job on device connect diff --git a/qa/integration/src/test/resources/features/jobScheduling/TriggerServiceI9n.feature b/qa/integration/src/test/resources/features/jobScheduling/TriggerServiceI9n.feature index 42b49e9ff4f..826e80e5099 100644 --- a/qa/integration/src/test/resources/features/jobScheduling/TriggerServiceI9n.feature +++ b/qa/integration/src/test/resources/features/jobScheduling/TriggerServiceI9n.feature @@ -10,8 +10,8 @@ # Contributors: # Eurotech - initial API and implementation ############################################################################### -@jobsIntegrationBase @env_docker_base +@job Feature: Trigger service tests diff --git a/service/device/registry/test-steps/src/main/java/org/eclipse/kapua/service/device/registry/steps/BrokerSteps.java b/service/device/registry/test-steps/src/main/java/org/eclipse/kapua/service/device/registry/steps/BrokerSteps.java index 011d74d5b99..c0e71b9b03b 100644 --- a/service/device/registry/test-steps/src/main/java/org/eclipse/kapua/service/device/registry/steps/BrokerSteps.java +++ b/service/device/registry/test-steps/src/main/java/org/eclipse/kapua/service/device/registry/steps/BrokerSteps.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2022 Eurotech and/or its affiliates and others + * Copyright (c) 2017, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -17,6 +17,7 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import javax.inject.Inject; @@ -188,6 +189,10 @@ public void afterScenario() { @When("I start the Kura Mock") public void startKuraMock() { if (!kuraDevices.isEmpty()) { + // Cleanup of any leftover KuraMock, if any + for (KuraDevice kuraDevice : kuraDevices) { + kuraDevice.mqttClientDisconnect(); + } kuraDevices.clear(); } @@ -244,6 +249,33 @@ public void deviceBirthMessage() throws Exception { } } + /** + * Checks that the {@link Device} with the given {@link Device#getClientId()} has {@link DeviceConnection#getStatus()} {@link DeviceConnectionStatus#CONNECTED} within the given time. + * + * @param clientId The {@link Device#getClientId()} to check + * @param waitSeconds The max time of wait in seconds + * @throws Exception + * @since 1.2.0 + */ + @When("Device {string} is connected within {int}s") + public void deviceConnected(String clientId, int waitSeconds) throws Exception { + + long now = System.currentTimeMillis(); + while ((System.currentTimeMillis() - now) < (waitSeconds * 1000L)){ + Device kuraDevice = deviceRegistryService.findByClientId(getCurrentScopeId(), clientId); + + if (kuraDevice != null && + kuraDevice.getConnection() != null && + DeviceConnectionStatus.CONNECTED.equals(kuraDevice.getConnection().getStatus())) { + return; + } + + TimeUnit.MILLISECONDS.sleep(100); + } + + Assert.fail("Device " + clientId + "not connected within " + waitSeconds + "s"); + } + @When("Device(s) (is/are) connected within {int} second(s)") public void deviceConnected(int timeout) throws Exception { try { diff --git a/service/device/registry/test-steps/src/main/java/org/eclipse/kapua/service/device/registry/steps/KuraDevice.java b/service/device/registry/test-steps/src/main/java/org/eclipse/kapua/service/device/registry/steps/KuraDevice.java index 996705c79d4..9c30988b186 100644 --- a/service/device/registry/test-steps/src/main/java/org/eclipse/kapua/service/device/registry/steps/KuraDevice.java +++ b/service/device/registry/test-steps/src/main/java/org/eclipse/kapua/service/device/registry/steps/KuraDevice.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2022 Eurotech and/or its affiliates and others + * Copyright (c) 2017, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -34,6 +34,7 @@ import java.nio.file.Paths; import java.util.Date; import java.util.List; +import java.util.concurrent.TimeUnit; public class KuraDevice implements MqttCallback { @@ -62,6 +63,7 @@ public class KuraDevice implements MqttCallback { private String deployGetBundles; private String deployExecStart34; private String deployExecStart95; + private String deployExecStop34; private String deployExecStop77; // INVENTORY-V1 @@ -175,6 +177,7 @@ public KuraDevice() { deployGetBundles = "$EDC/kapua-sys/rpione3/DEPLOY-V2/GET/bundles"; deployExecStart34 = "$EDC/kapua-sys/rpione3/DEPLOY-V2/EXEC/start/34"; deployExecStart95 = "$EDC/kapua-sys/rpione3/DEPLOY-V2/EXEC/start/95"; + deployExecStop34 = "$EDC/kapua-sys/rpione3/DEPLOY-V2/EXEC/stop/34"; deployExecStop77 = "$EDC/kapua-sys/rpione3/DEPLOY-V2/EXEC/stop/77"; // INVENTORY-V1 @@ -292,6 +295,7 @@ private void mqttClientSetupForMoreDevices() { cmdExecCommand = $EDC_KAPUA_SYS + clientId + "/CMD-V1/EXEC/command"; deployExecStart34 = $EDC_KAPUA_SYS + clientId + "/DEPLOY-V2/EXEC/start/34"; deployExecStart95 = $EDC_KAPUA_SYS + clientId + "/DEPLOY-V2/EXEC/start/95"; + deployExecStop34 = $EDC_KAPUA_SYS + clientId + "/DEPLOY-V2/EXEC/stop/34"; deployExecStop77 = $EDC_KAPUA_SYS + clientId + "/DEPLOY-V2/EXEC/stop/77"; assetExecRead = $EDC_KAPUA_SYS + clientId + "/ASSET-V1/EXEC/read"; @@ -405,6 +409,16 @@ public void messageArrived(String topic, MqttMessage mqttMessage) throws Excepti } // CMD-V1 else if (topic.equals(cmdExecCommand)) { + KuraPayload kuraRequestPayload = new KuraPayload(); + kuraRequestPayload.readFromByteArray(requestPayload); + + String command = (String) kuraRequestPayload.getMetrics().get("command.command"); + + if ("ping".equals(command)) { + // Mimic command request `ping -c 10 8.8.8.8 + TimeUnit.SECONDS.sleep(10); + } + callbackParam = extractCallback(requestPayload); responseTopic = $EDC + CLIENT_ACCOUNT + "/" + callbackParam.getClientId() + "/CMD-V1/REPLY/" + callbackParam.getRequestId(); @@ -530,7 +544,7 @@ else if (topic.equals(deployGetPackages)) { bundleStateChanged = true; mqttClient.publish(responseTopic, responsePayload, 0, false); - } else if (topic.equals(deployExecStop77)) { + } else if (topic.equals(deployExecStop34) || topic.equals(deployExecStop77)) { callbackParam = extractCallback(requestPayload); responseTopic = $EDC + CLIENT_ACCOUNT + "/" + callbackParam.getClientId() + DEPLOY_V2_REPLY + callbackParam.getRequestId(); diff --git a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobEngineSteps.java b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobEngineSteps.java index c01f3c2ac46..17d735bfce0 100644 --- a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobEngineSteps.java +++ b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobEngineSteps.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021, 2022 Eurotech and/or its affiliates and others + * Copyright (c) 2021, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -26,8 +26,10 @@ import org.eclipse.kapua.model.id.KapuaId; import org.eclipse.kapua.qa.common.StepData; import org.eclipse.kapua.service.job.Job; +import org.junit.Assert; import javax.inject.Inject; +import java.util.concurrent.TimeUnit; @Singleton public class JobEngineSteps extends JobServiceTestBase { @@ -66,6 +68,126 @@ public void startJob() throws Exception { } } + // Wait Job Running + + /** + * Waits the last {@link Job} in context to finish it execution up the given wait time + * + * @param waitSeconds The max time to wait + * @throws Exception + * @since 2.1.0 + */ + @And("I wait job to finish its execution up to {int}s") + public void waitJobInContextUpTo(int waitSeconds) throws Exception { + Job job = (Job) stepData.get(JOB); + + waitJobUpTo(job, waitSeconds); + } + + /** + * Looks for a {@link Job} by its {@link Job#getName()} and waits to finish it execution up the given wait time + * + * @param jobName The {@link Job#getName()} to look for + * @param waitSeconds The max time to wait + * @throws Exception + * @since 2.1.0 + */ + @And("I wait job {string} to finish its execution up to {int}s") + public void waitJobByNameUpTo(String jobName, int waitSeconds) throws Exception { + Job job = findJob(jobName); + + waitJobUpTo(job, waitSeconds); + } + + /** + * Wait the given {@link Job} to finish its execution up the given wait time + * + * @param job The {@link Job} to monitor + * @param waitSeconds The max time to wait + * @throws Exception + * @since 2.1.0 + */ + private void waitJobUpTo(Job job, int waitSeconds) throws Exception { + long now = System.currentTimeMillis(); + while ((System.currentTimeMillis() - now) < (waitSeconds * 1000L)) { + if (!jobEngineService.isRunning(job.getScopeId(), job.getId())) { + return; + } + + TimeUnit.MILLISECONDS.sleep(100); + } + + Assert.fail("Job " + job.getName() + "did not completed its execution within " + waitSeconds + "s"); + } + + // Check Job Running + + /** + * Checks that the last {@link Job} in context is running + * + * @throws Exception + * @since 2.1.0 + */ + @And("I confirm job is running") + public void checkJobInContextIsRunning() throws Exception { + Job job = (Job) stepData.get(JOB); + + checkJobIsRunning(job, true); + } + + /** + * Looks for a {@link Job} by its {@link Job#getName()} and checks that is running + * + * @param jobName The {@link Job#getName()} to look for + * @throws Exception + * @since 2.1.0 + */ + @And("I confirm job {string} is running") + public void checkJobByNameIsRunning(String jobName) throws Exception { + Job job = findJob(jobName); + + checkJobIsRunning(job, true); + } + + /** + * Checks that the last {@link Job} in context is not running + * + * @throws Exception + * @since 2.1.0 + */ + @And("I confirm job is not running") + public void checkJobInContextIsNotRunning() throws Exception { + Job job = (Job) stepData.get(JOB); + + checkJobIsRunning(job, false); + } + + /** + * Looks for a {@link Job} by its {@link Job#getName()} and checks that is not running + * + * @param jobName The {@link Job#getName()} to look for + * @throws Exception + * @since 2.1.0 + */ + @And("I confirm job {string} is not running") + public void checkJobByNameIsNotRunning(String jobName) throws Exception { + Job job = findJob(jobName); + + checkJobIsRunning(job, false); + } + + /** + * Checks the running status of the given {@link Job} + * + * @param job The {@link Job} to check + * @param expectedRunning Whether expecting running or not + * @throws Exception + * @since 2.1.0 + */ + private void checkJobIsRunning(Job job, boolean expectedRunning) throws Exception { + Assert.assertEquals(expectedRunning, jobEngineService.isRunning(job.getScopeId(), job.getId())); + } + @When("I restart a job") public void restartJob() throws Exception { primeException(); @@ -83,7 +205,7 @@ public void restartJob() throws Exception { @And("I stop the job") public void iStopTheJob() throws Exception { - Job job = (Job) stepData.get("Job"); + Job job = (Job) stepData.get(JOB); try { primeException(); jobEngineService.stopJob(getCurrentScopeId(), job.getId()); diff --git a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobExecutionServiceSteps.java b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobExecutionServiceSteps.java index 38536c18f22..acb57217905 100644 --- a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobExecutionServiceSteps.java +++ b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobExecutionServiceSteps.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021, 2022 Eurotech and/or its affiliates and others + * Copyright (c) 2021, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -144,6 +144,51 @@ public void deleteLastJobExecution() throws Exception { } } + // Check Job Execution + + /** + * Checks that the last {@link Job} in context has the given number of {@link JobExecution}s + * + * @param expectedNumberOfExecution Expected number of {@link JobExecution}s + * @throws Exception + * @since 2.1.0 + */ + @When("I confirm that job has {int} job execution") + public void checkJobInContextHasExecution(int expectedNumberOfExecution) throws Exception { + Job job = (Job) stepData.get(JOB); + + checkJobHasExecution(job, expectedNumberOfExecution); + } + + /** + * Looks for a {@link Job} by its {@link Job#getName()} and checks that has the given number of {@link JobExecution}s + * + * @param jobName The {@link Job#getName()} to look for + * @param expectedNumberOfExecution Expected number of {@link JobExecution}s + * @throws Exception + * @since 2.1.0 + */ + @When("I confirm that job {string} has {int} job execution") + public void checkJobByNameHasExecution(String jobName, int expectedNumberOfExecution) throws Exception { + Job job = findJob(jobName); + + checkJobHasExecution(job, expectedNumberOfExecution); + } + + /** + * Checks that the given {@link Job} has the given number of {@link JobExecution}s + * + * @param job The {@link Job} to check + * @param expectedNumberOfExecution Expected number of {@link JobExecution}s + * @throws Exception + * @since 2.1.0 + */ + private void checkJobHasExecution(Job job, int expectedNumberOfExecution) throws Exception { + long actualNumberOfExecution = jobExecutionService.countByJobId(job.getScopeId(), job.getId()); + + Assert.assertEquals(expectedNumberOfExecution, actualNumberOfExecution); + } + @When("I count the execution items for the current job") public void countExecutionsForJob() throws Exception { Job job = (Job) stepData.get("Job"); diff --git a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobServiceSteps.java b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobServiceSteps.java index 5d9720d836d..c46794eefcd 100644 --- a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobServiceSteps.java +++ b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobServiceSteps.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2022 Eurotech and/or its affiliates and others + * Copyright (c) 2017, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -120,10 +120,10 @@ public void createANamedJob(String name) throws Exception { JobCreator jobCreator = (JobCreator) stepData.get(JOB_CREATOR); primeException(); try { - stepData.remove("Job"); + stepData.remove(JOB); stepData.remove(CURRENT_JOB_ID); Job job = jobService.create(jobCreator); - stepData.put("Job", job); + stepData.put(JOB, job); stepData.put(CURRENT_JOB_ID, job.getId()); } catch (KapuaException ex) { verifyException(ex); @@ -165,10 +165,10 @@ public void createJobFromCreator() throws Exception { JobCreator jobCreator = (JobCreator) stepData.get(JOB_CREATOR); primeException(); try { - stepData.remove("Job"); + stepData.remove(JOB); stepData.remove(CURRENT_JOB_ID); Job job = jobService.create(jobCreator); - stepData.put("Job", job); + stepData.put(JOB, job); stepData.put(CURRENT_JOB_ID, job.getId()); } catch (KapuaException ex) { verifyException(ex); @@ -177,13 +177,13 @@ public void createJobFromCreator() throws Exception { @When("I change the job name to {string}") public void updateExistingJobName(String newName) throws Exception { - Job oldJob = (Job) stepData.get("Job"); + Job oldJob = (Job) stepData.get(JOB); oldJob.setName(newName); primeException(); try { - stepData.remove("Job"); + stepData.remove(JOB); Job newJob = jobService.update(oldJob); - stepData.put("Job", newJob); + stepData.put(JOB, newJob); } catch (KapuaException ex) { verifyException(ex); } @@ -191,13 +191,13 @@ public void updateExistingJobName(String newName) throws Exception { @When("I change the job description to {string}") public void updateExistingJobDescription(String newDescription) throws Exception { - Job oldJob = (Job) stepData.get("Job"); + Job oldJob = (Job) stepData.get(JOB); oldJob.setDescription(newDescription); primeException(); try { - stepData.remove("Job"); + stepData.remove(JOB); Job newJob = jobService.update(oldJob); - stepData.put("Job", newJob); + stepData.put(JOB, newJob); } catch (KapuaException ex) { verifyException(ex); } @@ -205,13 +205,13 @@ public void updateExistingJobDescription(String newDescription) throws Exception @When("I change the job XML definition to {string}") public void updateExistingJobXMLDefinition(String newDefinition) throws Exception { - Job oldJob = (Job) stepData.get("Job"); + Job oldJob = (Job) stepData.get(JOB); oldJob.setJobXmlDefinition(newDefinition); primeException(); try { - stepData.remove("Job"); + stepData.remove(JOB); Job newJob = jobService.update(oldJob); - stepData.put("Job", newJob); + stepData.put(JOB, newJob); } catch (KapuaException ex) { verifyException(ex); } @@ -219,16 +219,16 @@ public void updateExistingJobXMLDefinition(String newDefinition) throws Exceptio @When("I add the current step to the last job") public void updateJobWithSteps() throws Exception { - Job oldJob = (Job) stepData.get("Job"); + Job oldJob = (Job) stepData.get(JOB); List tmpStepList = oldJob.getJobSteps(); JobStep step = (JobStep) stepData.get("Step"); tmpStepList.add(step); oldJob.setJobSteps(tmpStepList); primeException(); try { - stepData.remove("Job"); + stepData.remove(JOB); Job newJob = jobService.update(oldJob); - stepData.put("Job", newJob); + stepData.put(JOB, newJob); } catch (KapuaException ex) { verifyException(ex); } @@ -236,7 +236,7 @@ public void updateJobWithSteps() throws Exception { @When("I delete the job") public void deleteJobFromDatabase() throws Exception { - Job job = (Job) stepData.get("Job"); + Job job = (Job) stepData.get(JOB); primeException(); try { jobService.delete(job.getScopeId(), job.getId()); @@ -250,9 +250,9 @@ public void findJobInDatabase() throws Exception { KapuaId currentJobId = (KapuaId) stepData.get(CURRENT_JOB_ID); primeException(); try { - stepData.remove("Job"); + stepData.remove(JOB); Job job = jobService.find(getCurrentScopeId(), currentJobId); - stepData.put("Job", job); + stepData.put(JOB, job); } catch (KapuaException ex) { verifyException(ex); } @@ -281,9 +281,9 @@ public void queryForJobWithName(String name) throws Exception { tmpQuery.setPredicate(tmpQuery.attributePredicate(JobAttributes.NAME, name)); primeException(); try { - stepData.remove("Job"); + stepData.remove(JOB); Job job = jobService.query(tmpQuery).getFirstItem(); - stepData.put("Job", job); + stepData.put(JOB, job); Assert.assertEquals(name, job.getName()); } catch (KapuaException ex) { verifyException(ex); @@ -292,7 +292,7 @@ public void queryForJobWithName(String name) throws Exception { @Then("The job entity matches the creator") public void checkJobAgainstCreator() { - Job job = (Job) stepData.get("Job"); + Job job = (Job) stepData.get(JOB); JobCreator jobCreator = (JobCreator) stepData.get(JOB_CREATOR); Assert.assertEquals("The job scope does not match the creator.", jobCreator.getScopeId(), job.getScopeId()); Assert.assertEquals("The job name does not match the creator.", jobCreator.getName(), job.getName()); @@ -301,35 +301,35 @@ public void checkJobAgainstCreator() { @Then("The job has int step(s)") public void checkNumberOfJobSteps(int num) { - Job job = (Job) stepData.get("Job"); + Job job = (Job) stepData.get(JOB); Assert.assertEquals("The job item has the wrong number of steps", num, job.getJobSteps().size()); } @Then("I find a job item in the database") public void checkThatAJobWasFound() { - Assert.assertNotNull("Unexpected null value for the job.", stepData.get("Job")); + Assert.assertNotNull("Unexpected null value for the job.", stepData.get(JOB)); } @Then("There is no such job item in the database") public void checkThatNoJobWasFound() { - Assert.assertNull("Unexpected job item was found!", stepData.get("Job")); + Assert.assertNull("Unexpected job item was found!", stepData.get(JOB)); } @Then("The job name is {string}") public void checkJobItemName(String name) { - Job job = (Job) stepData.get("Job"); + Job job = (Job) stepData.get(JOB); Assert.assertEquals("The job name does not match!", name, job.getName()); } @Then("The job description is {string}") public void checkJobItemDescription(String description) { - Job job = (Job) stepData.get("Job"); + Job job = (Job) stepData.get(JOB); Assert.assertEquals("The job description does not match!", description, job.getDescription()); } @Then("The job XML definition is {string}") public void checkJobItemXMLDefinition(String definition) { - Job job = (Job) stepData.get("Job"); + Job job = (Job) stepData.get(JOB); Assert.assertEquals("The job XML definition does not match!", definition, job.getJobXmlDefinition()); } @@ -344,13 +344,13 @@ public void testJobFactorySanity() { @Then("I find a job with name {string}") public void iFindAJobWithName(String jobName) { - Job job = (Job) stepData.get("Job"); + Job job = (Job) stepData.get(JOB); Assert.assertEquals(job.getName(), jobName); } @Then("I try to delete the job with name {string}") public void iDeleteTheJobWithName(String jobName) throws Exception { - Job job = (Job) stepData.get("Job"); + Job job = (Job) stepData.get(JOB); try { primeException(); if (job.getName().equals(jobName)) { @@ -363,12 +363,12 @@ public void iDeleteTheJobWithName(String jobName) throws Exception { @Then("I try to edit job to name {string}") public void iTryToEditJobToName(String jobName) throws Throwable { - Job job = (Job) stepData.get("Job"); + Job job = (Job) stepData.get(JOB); job.setName(jobName); try { primeException(); Job newJob = jobService.update(job); - stepData.put("Job", newJob); + stepData.put(JOB, newJob); } catch (KapuaException ex) { verifyException(ex); } @@ -381,9 +381,9 @@ public void iQueryForTheJobWithTheNameAndIFoundIt(String jobName) throws Excepti tmpQuery.setPredicate(tmpQuery.attributePredicate(JobAttributes.NAME, jobName)); primeException(); try { - stepData.remove("Job"); + stepData.remove(JOB); Job job = jobService.query(tmpQuery).getFirstItem(); - stepData.put("Job", job); + stepData.put(JOB, job); Assert.assertEquals(jobName, job.getName()); } catch (KapuaException ex) { verifyException(ex); @@ -419,7 +419,7 @@ public void iTryToCreateJobWithInvalidSymbolsInName(String invalidCharacters) th @Then("I find a job with description {string}") public void iFindAJobWithDescription(String jobDescription) throws Throwable { - Job job = (Job) stepData.get("Job"); + Job job = (Job) stepData.get(JOB); Assert.assertEquals(job.getDescription(), jobDescription); } @@ -442,7 +442,7 @@ public void iChangeNameOfJobFromTo(String oldName, String newName) throws Throwa Job job = queryResult.getFirstItem(); job.setName(newName); jobService.update(job); - stepData.put("Job", job); + stepData.put(JOB, job); } catch (Exception e) { verifyException(e); } @@ -450,7 +450,7 @@ public void iChangeNameOfJobFromTo(String oldName, String newName) throws Throwa @And("There is no job with name {string} in database") public void thereIsNoJobWithNameInDatabase(String jobName) { - Job job = (Job) stepData.get("Job"); + Job job = (Job) stepData.get(JOB); Assert.assertNotEquals(job.getName(), jobName); } @@ -463,7 +463,7 @@ public void iChangeTheJobDescriptionFromTo(String oldDescription, String newDesc Job job = queryResult.getFirstItem(); job.setDescription(newDescription); jobService.update(job); - stepData.put("Job", job); + stepData.put(JOB, job); } catch (Exception e) { verifyException(e); } @@ -478,7 +478,7 @@ private void tryToCreateJob(String characters) throws Exception { try { primeException(); Job job = jobService.create(jobCreator); - stepData.put("Job", job); + stepData.put(JOB, job); stepData.put(CURRENT_JOB_ID, job.getId()); } catch (KapuaException ex) { verifyException(ex); @@ -494,12 +494,12 @@ private void tryToUpdateJobName(String characters) throws Exception { jobCreator.setName(JOB_NAME + i); try { primeException(); - stepData.remove("Job"); + stepData.remove(JOB); Job job = jobService.create(jobCreator); job.setName(jobName); jobService.update(job); stepData.put(CURRENT_JOB_ID, job.getId()); - stepData.put("Job", job); + stepData.put(JOB, job); } catch (KapuaException ex) { verifyException(ex); } diff --git a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobServiceTestBase.java b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobServiceTestBase.java index 94b9e20fde9..c6c75a0cce8 100644 --- a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobServiceTestBase.java +++ b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobServiceTestBase.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021, 2022 Eurotech and/or its affiliates and others + * Copyright (c) 2021, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -12,8 +12,15 @@ *******************************************************************************/ package org.eclipse.kapua.service.job.steps; +import org.eclipse.kapua.KapuaEntityNotFoundException; +import org.eclipse.kapua.locator.KapuaLocator; import org.eclipse.kapua.qa.common.StepData; import org.eclipse.kapua.qa.common.TestBase; +import org.eclipse.kapua.service.job.Job; +import org.eclipse.kapua.service.job.JobAttributes; +import org.eclipse.kapua.service.job.JobFactory; +import org.eclipse.kapua.service.job.JobQuery; +import org.eclipse.kapua.service.job.JobService; public class JobServiceTestBase extends TestBase { @@ -21,6 +28,7 @@ public class JobServiceTestBase extends TestBase { protected static final String JOB_NAME = "jobName"; protected static final String TEST_JOB = "Test job"; protected static final String CURRENT_JOB_ID = "CurrentJobId"; + protected static final String JOB = "Job"; protected static final String JOB_CREATOR = "JobCreator"; // Job Execution @@ -45,7 +53,31 @@ public class JobServiceTestBase extends TestBase { protected static final String JOB_TARGET = "JobTarget"; protected static final String JOB_TARGET_LIST = "JobTargetList"; + private JobService jobService; + + private JobFactory jobFactory; + protected JobServiceTestBase(StepData stepData) { super(stepData); + + KapuaLocator locator = KapuaLocator.getInstance(); + + jobService = locator.getService(JobService.class); + jobFactory = locator.getFactory(JobFactory.class); + } + + public Job findJob(String jobName) throws Exception { + JobQuery jobQuery = jobFactory.newQuery(getCurrentScopeId()); + jobQuery.setPredicate( + jobQuery.attributePredicate(JobAttributes.NAME, jobName) + ); + + Job job = jobService.query(jobQuery).getFirstItem(); + + if (job == null) { + throw new KapuaEntityNotFoundException(Job.TYPE, jobName); + } + + return job; } } diff --git a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobStepServiceSteps.java b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobStepServiceSteps.java index 58085120722..b2f2f218968 100644 --- a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobStepServiceSteps.java +++ b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobStepServiceSteps.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021, 2022 Eurotech and/or its affiliates and others + * Copyright (c) 2021, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -26,6 +26,7 @@ import org.eclipse.kapua.model.query.predicate.AttributePredicate; import org.eclipse.kapua.qa.common.StepData; import org.eclipse.kapua.qa.common.cucumber.CucJobStepProperty; +import org.eclipse.kapua.service.job.Job; import org.eclipse.kapua.service.job.step.JobStep; import org.eclipse.kapua.service.job.step.JobStepAttributes; import org.eclipse.kapua.service.job.step.JobStepCreator; @@ -44,6 +45,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; @Singleton public class JobStepServiceSteps extends JobServiceTestBase { @@ -101,6 +103,39 @@ public void iPrepareAJobStepCreatorWithPropertyList(String name, List cucJobStepProperties) throws Exception{ + Job job = (Job) stepData.get(JOB); + JobStepDefinition jobStepDefinition = (JobStepDefinition) stepData.get(JOB_STEP_DEFINITION); + + JobStepCreator jobStepCreator = jobStepFactory.newCreator(job.getScopeId()); + jobStepCreator.setJobId(job.getId()); + jobStepCreator.setName(jobStepName); + jobStepCreator.setJobStepDefinitionId(jobStepDefinition.getId()); + jobStepCreator.setStepProperties( + cucJobStepProperties.stream() + .map( + (cucJobStepProperty) -> jobStepFactory.newStepProperty( + cucJobStepProperty.getName(), + cucJobStepProperty.getType(), + cucJobStepProperty.getValue() + ) + ) + .collect(Collectors.toList()) + ); + + JobStep jobStep = jobStepService.create(jobStepCreator); + stepData.put(JOB_STEP, jobStep); + } + @And("I prepare a JobStepCreator with the name {string} and properties") // TODO Review implementation. These should be tested specifically for each JobStepDefinition public void iPrepareAJobStepCreatorWithPropertyList1(String name, List tmpProperty) { diff --git a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobTargetServiceSteps.java b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobTargetServiceSteps.java index 228de86d467..ea7e630c583 100644 --- a/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobTargetServiceSteps.java +++ b/service/job/test-steps/src/main/java/org/eclipse/kapua/service/job/steps/JobTargetServiceSteps.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021, 2022 Eurotech and/or its affiliates and others + * Copyright (c) 2021, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -20,12 +20,19 @@ import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; +import org.eclipse.kapua.KapuaEntityNotFoundException; import org.eclipse.kapua.KapuaException; import org.eclipse.kapua.locator.KapuaLocator; +import org.eclipse.kapua.model.KapuaEntity; import org.eclipse.kapua.model.id.KapuaId; import org.eclipse.kapua.model.query.predicate.AttributePredicate; import org.eclipse.kapua.qa.common.StepData; import org.eclipse.kapua.service.device.registry.Device; +import org.eclipse.kapua.service.device.registry.DeviceAttributes; +import org.eclipse.kapua.service.device.registry.DeviceFactory; +import org.eclipse.kapua.service.device.registry.DeviceListResult; +import org.eclipse.kapua.service.device.registry.DeviceQuery; +import org.eclipse.kapua.service.device.registry.DeviceRegistryService; import org.eclipse.kapua.service.job.Job; import org.eclipse.kapua.service.job.targets.JobTarget; import org.eclipse.kapua.service.job.targets.JobTargetAttributes; @@ -41,12 +48,17 @@ import javax.inject.Inject; import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; @Singleton public class JobTargetServiceSteps extends JobServiceTestBase { private static final String DEVICE = "Device"; + private DeviceFactory deviceFactory; + private DeviceRegistryService deviceRegistryService; + private JobTargetService jobTargetService; private JobTargetFactory jobTargetFactory; final Logger logger = LoggerFactory.getLogger(this.getClass()); @@ -65,6 +77,9 @@ public void beforeScenarioNone(Scenario scenario) { public void setServices() { KapuaLocator locator = KapuaLocator.getInstance(); + deviceFactory= locator.getFactory(DeviceFactory.class); + deviceRegistryService = locator.getService(DeviceRegistryService.class); + jobTargetService = locator.getService(JobTargetService.class); jobTargetFactory = locator.getFactory(JobTargetFactory.class); } @@ -126,6 +141,40 @@ public void iSearchForTheJobTargetsInDatabase() { stepData.updateCount(jobTargets.size()); } + /** + * Adds {@link Device}s with the given {@link Device#getClientId()}s as a {@link JobTarget} of the {@link Job} in context. + * + * @param clientIds The {@link Device#getClientId()}s to add + * @throws Exception + * @since 2.1.0 + */ + @And("I add device target(s) to job") + public void addDeviceTargetsToJob(List clientIds) throws Exception { + DeviceQuery deviceQuery = deviceFactory.newQuery(getCurrentScopeId()); + deviceQuery.setPredicate( + deviceQuery.attributePredicate(DeviceAttributes.CLIENT_ID, clientIds) + ); + + DeviceListResult devices = deviceRegistryService.query(deviceQuery); + + List deviceIds = devices.getItems() + .stream() + .map(KapuaEntity::getId) + .collect(Collectors.toList()); + + Job job = (Job) stepData.get("Job"); + + JobTargetCreator jobTargetCreator = jobTargetFactory.newCreator(getCurrentScopeId()); + jobTargetCreator.setJobId(job.getId()); + + List jobTargets = new ArrayList<>(); + for (KapuaId deviceId : deviceIds) { + jobTargetCreator.setJobTargetId(deviceId); + JobTarget jobTarget = jobTargetService.create(jobTargetCreator); + stepData.put(JOB_TARGET, jobTarget); + } + stepData.put(JOB_TARGET_LIST, jobTargets); + } @And("I add target(s) to job") public void addTargetsToJob() throws Exception { @@ -327,7 +376,81 @@ public void testTheJobTargetFactory() { Assert.assertNotNull(jobTargetFactory.newQuery(SYS_SCOPE_ID)); } + + // Check Job Targets + + /** + * Checks that the {@link JobTarget} in context for the {@link Job} in context has the expected {@link JobTarget#getStepIndex()} and {@link JobTarget#getStatus()} + * + * @param expectedStepIndex The expected {@link JobTarget#getStepIndex()} + * @param expectedJobTargetStatus The expected {@link JobTarget#getStatus()} + * @throws Exception + * @since 2.1.0 + */ + @When("I confirm that job target in job has step index {int} and status {string}") + public void checkJobTargetForJobInContextHas(int expectedStepIndex, String expectedJobTargetStatus) throws Exception { + + JobTarget jobTarget = (JobTarget) stepData.get(JOB_TARGET); + + checkJobTargetForJobHas(jobTarget, expectedStepIndex, expectedJobTargetStatus); + } + + /** + * Checks that the {@link JobTarget} that matches the {@link Device} with the given {@link Device#getClientId()} for the {@link Job} in context has the expected {@link JobTarget#getStepIndex()} and {@link JobTarget#getStatus()} + * + * @param clientId The {@link Device#getClientId()} to look for + * @param expectedStepIndex The expected {@link JobTarget#getStepIndex()} + * @param expectedJobTargetStatus The expected {@link JobTarget#getStatus()} + * @throws Exception + * @since 2.1.0 + */ + @And("I confirm that device job target {string} in job has step index {int} and status {string}") + public void checkJobTargetByNameForJobHas(String clientId, int expectedStepIndex, String expectedJobTargetStatus) throws Exception { + Device device = deviceRegistryService.findByClientId(getCurrentScopeId(), clientId); + + if (device == null) { + throw new KapuaEntityNotFoundException(Device.TYPE, clientId); + } + + Job job = (Job) stepData.get(JOB); + + JobTargetQuery jobTargetQuery = jobTargetFactory.newQuery(job.getScopeId()); + jobTargetQuery.setPredicate( + jobTargetQuery.andPredicate( + jobTargetQuery.attributePredicate(JobTargetAttributes.JOB_ID, job.getId()), + jobTargetQuery.attributePredicate(JobTargetAttributes.JOB_TARGET_ID, device.getId()) + ) + ); + + JobTarget jobTarget = jobTargetService.query(jobTargetQuery).getFirstItem(); + + if (jobTarget == null) { + throw new KapuaEntityNotFoundException(JobTarget.TYPE, device.getId()); + } + + checkJobTargetForJobHas(jobTarget, expectedStepIndex, expectedJobTargetStatus); + } + + /** + * Checks that given {@link JobTarget} has the expected {@link JobTarget#getStepIndex()} and {@link JobTarget#getStatus()} + * + * @param expectedStepIndex The expected {@link JobTarget#getStepIndex()} + * @param expectedJobTargetStatus The expected {@link JobTarget#getStatus()} + * @throws Exception + * @since 2.1.0 + */ + private void checkJobTargetForJobHas(JobTarget jobTarget, int expectedStepIndex, String expectedJobTargetStatus) throws Exception { + JobTarget updatedJobTarget = jobTargetService.find(jobTarget.getScopeId(), jobTarget.getId()); + + Assert.assertEquals(expectedStepIndex, updatedJobTarget.getStepIndex()); + Assert.assertEquals(JobTargetStatus.valueOf(expectedJobTargetStatus), updatedJobTarget.getStatus()); + + stepData.put(JOB_TARGET, updatedJobTarget); + } + + // // Private methods + // private JobTargetCreator prepareJobTargetCreator() { KapuaId currentJobId = (KapuaId) stepData.get(CURRENT_JOB_ID); Device device = (Device) stepData.get(DEVICE);