Skip to content

Commit

Permalink
Add base functionality to run Cucumber features by REST API.
Browse files Browse the repository at this point in the history
  • Loading branch information
SergeyAkkuratov committed Mar 9, 2024
1 parent b136f67 commit a45163d
Show file tree
Hide file tree
Showing 13 changed files with 309 additions and 19 deletions.
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ repositories {
}

dependencies {
compileOnly("ru.alfabank.tests:akita:4.0.0")
// This dependency is used by the application.
implementation(libs.guava)
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("io.cucumber:cucumber-java:7.15.0")
implementation("io.qameta.allure:allure-cucumber7-jvm:2.25.0")

compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,37 @@
package ru.sakkuratov.autotests.configuration;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import ru.sakkuratov.autotests.exception.CustomAsyncExceptionHandler;

@Configuration
public class TestLauncherConfiguration {
@Value("${")
public class TestLauncherConfiguration implements AsyncConfigurer {
@Value("${spring.async.core-pool-size:1}")
private Integer corePoolSize;
@Value("${spring.async.max-pool-size:3}")
private Integer maxCorePoolSize;
@Value("${ spring.async.queue-capacity:100}")
private Integer queueCapacity;

@Bean("taskExecutor")
@Bean(name = "TaskExecutor")
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(3);
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxCorePoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("TaskExecutor::");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}

@Bean
public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
Expand All @@ -32,4 +40,9 @@ public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
eventMulticaster.setTaskExecutor(asyncTaskExecutor);
return eventMulticaster;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
Original file line number Diff line number Diff line change
@@ -1,44 +1,52 @@
package ru.sakkuratov.autotests.controllers;

import lombok.AllArgsConstructor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import ru.sakkuratov.autotests.models.TestParameters;
import ru.sakkuratov.autotests.services.StabSystemTaskService;
import ru.sakkuratov.autotests.services.StabTestWatcherService;
import ru.sakkuratov.autotests.services.TestWatcher;

import java.util.Map;
import static ru.sakkuratov.autotests.helpers.CommonHelpers.getStackTrace;

@RestController
public class TaskController {
@Autowired
private StabTestWatcherService testWatcher;
@Autowired
private StabSystemTaskService systemTaskService;

private static final HttpHeaders headers = new HttpHeaders();

static {
headers.setContentType(MediaType.APPLICATION_JSON);
}

@Autowired
private TestWatcher testWatcher;
@Autowired
private StabTestWatcherService stabTestWatcher;
@Autowired
private StabSystemTaskService systemTaskService;

@PostMapping("task/add")
public ResponseEntity<Object> taskAdd(@RequestBody String body) {
Map<String, String> responseBody = testWatcher.addTask(body);
return ResponseEntity.accepted().headers(headers).body(responseBody);
public ResponseEntity taskAdd(@RequestBody String body) {
try {
TestParameters parameters = new ObjectMapper().readValue(body, TestParameters.class);
return testWatcher.addTask(parameters);
} catch (Exception ex) {
return ResponseEntity.internalServerError().body("Test didn't launch: " + getStackTrace(ex));
}
}

@GetMapping("tasks")
public ResponseEntity<Object> getStatus() {
Object result = testWatcher.getStatus(null);
Object result = stabTestWatcher.getStatus(null);
return ResponseEntity.ok().headers(headers).body(result);
}

@GetMapping("tasks/{id}")
public ResponseEntity<Object> getStatus(@PathVariable String id) {
Object result = testWatcher.getStatus(id);
Object result = stabTestWatcher.getStatus(id);
return ResponseEntity.ok().headers(headers).body(result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ru.sakkuratov.autotests.cucumber.steps;

import io.cucumber.java.bg.И;

public class CommonSteps {

@И("^вывести на экран сообщение \"(.+)\"$")
public void showMessage(String message) {
System.out.println(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ru.sakkuratov.autotests.events;

import org.springframework.context.ApplicationEvent;

public class StartCleanEvent extends ApplicationEvent {
public StartCleanEvent(Object source) {
super(source);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ru.sakkuratov.autotests.events;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent;
import ru.sakkuratov.autotests.models.CucumberTask;

public class TestFinishedEvent extends ApplicationEvent {

private static final Logger logger = LoggerFactory.getLogger(TestFinishedEvent.class);
private final String resultMessage;

public TestFinishedEvent(CucumberTask source, String resultMessage) {
super(source);
this.resultMessage = resultMessage;
logger.debug("Event created: Test finished event");
}

public String getResultMessage() {
return this.resultMessage;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ru.sakkuratov.autotests.exception;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;

import java.lang.reflect.Method;

public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(CustomAsyncExceptionHandler.class);

@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {

logger.error("Exception message - " + throwable.getMessage());
logger.error("Method name - " + method.getName());
for (Object param : obj) {
logger.error("Parameter value - " + param);
}
}

}
14 changes: 14 additions & 0 deletions src/main/java/ru/sakkuratov/autotests/helpers/CommonHelpers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ru.sakkuratov.autotests.helpers;

import java.io.PrintWriter;
import java.io.StringWriter;

public class CommonHelpers {

public static String getStackTrace(Throwable exception) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
exception.printStackTrace(pw);
return sw.toString();
}
}
75 changes: 75 additions & 0 deletions src/main/java/ru/sakkuratov/autotests/models/CucumberTask.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package ru.sakkuratov.autotests.models;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.cucumber.core.cli.Main;
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import ru.sakkuratov.autotests.events.TestFinishedEvent;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import static ru.sakkuratov.autotests.helpers.CommonHelpers.getStackTrace;

@Getter
@Setter
public class CucumberTask implements Runnable {

private static final Logger logger = LoggerFactory.getLogger(CucumberTask.class);
private final String id = UUID.randomUUID().toString();
private final TestParameters parameters = TestParameters.defaultParameters();

@JsonIgnore
private final ApplicationEventPublisher publisher;

public CucumberTask(TestParameters parameters, ApplicationEventPublisher publisher) {
this.publisher = publisher;
this.parameters.setParameters(parameters);
}

@Override
public void run() {
logger.info("Test has started.");
String resultMessage = "Tests finished successful.";
int exitStatus = Main.run(getCucumberArgs());
logger.info("Test has finished.");
if (exitStatus != 0) resultMessage = "Test finished with errors.";
publisher.publishEvent(new TestFinishedEvent(this, resultMessage));
}

private String[] getCucumberArgs() {
List<String> args = new ArrayList<>();
args.add("--threads");
args.add(parameters.getThreads());
args.add("--glue");
args.add(parameters.getGlue());
parameters.getPlugin().forEach(plugin -> {
args.add("--plugin");
args.add(plugin);
});
args.add("--tags");
args.add(parameters.getTags());
args.add(parameters.getFeaturesPath());
return args.toArray(new String[0]);
}

@Override
public String toString() {
try {
return new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
} catch (JsonProcessingException e) {
logger.error("Couldn't parse CucumberTask to JSON. Error: " + getStackTrace(e));
return id;
}
}

public Integer getPriority() {
return Integer.valueOf(this.parameters.getPriority());
}
}
72 changes: 72 additions & 0 deletions src/main/java/ru/sakkuratov/autotests/models/TestParameters.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package ru.sakkuratov.autotests.models;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.Duration;
import java.util.HashSet;
import java.util.Set;

@Setter
@Getter
@NoArgsConstructor
public class TestParameters {

private String priority;
private String glue;
private String threads;
private Set<String> plugin = new HashSet<>();
private String featuresPath;
private String owner;
private String tags;
private String timeout;

public static TestParameters defaultParameters() {
TestParameters testParameters = new TestParameters();
testParameters.setPriority("0");
testParameters.setThreads("1");
testParameters.setGlue("ru.sakkuratov.autotests.cucumber");
testParameters.addPlugin("pretty");
testParameters.addPlugin("io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm");
testParameters.setFeaturesPath("");
testParameters.setOwner("DEFAULT");
testParameters.setTags("");
testParameters.setTimeout("10S");
return testParameters;
}

@JsonIgnore
public long getCucumberRunTimeout() {
return Duration.parse("PT" + timeout).getSeconds();
}

public void addPlugin(String plugin) {
this.plugin.add(plugin);
}

public void setParameters(TestParameters testParameters) {
if (testParameters.getGlue() != null) {
this.glue = testParameters.getGlue();
}
if (testParameters.getThreads() != null) {
this.threads = testParameters.getThreads();
}
if (testParameters.getPlugin() != null) {
this.plugin.addAll(testParameters.getPlugin());
}
if (testParameters.getFeaturesPath() != null) {
this.featuresPath = testParameters.getFeaturesPath();
}
if (testParameters.getOwner() != null) {
this.owner = testParameters.getOwner();
}
if (testParameters.getTags() != null) {
this.tags = testParameters.getTags();
}
if (testParameters.getGlue() != null) {
this.timeout = testParameters.getTimeout();
}
}
}
34 changes: 34 additions & 0 deletions src/main/java/ru/sakkuratov/autotests/services/TestWatcher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package ru.sakkuratov.autotests.services;

import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.task.TaskExecutor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import ru.sakkuratov.autotests.models.CucumberTask;
import ru.sakkuratov.autotests.models.TestParameters;

@Service
public class TestWatcher {

@Autowired
private TaskExecutor taskExecutor;
@Autowired
private ApplicationEventPublisher publisher;

private HttpHeaders headers = new HttpHeaders();

@PostConstruct
public void init() {
headers.setContentType(MediaType.APPLICATION_JSON);
}

public ResponseEntity<CucumberTask> addTask(TestParameters testParameters) {
CucumberTask task = new CucumberTask(testParameters, publisher);
taskExecutor.execute(task);
return ResponseEntity.ok().headers(headers).body(task);
}
}
Loading

0 comments on commit a45163d

Please sign in to comment.