Skip to content

Commit

Permalink
feat(server): execute campaign in parallel on different environments (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
KarimGl authored Apr 8, 2024
1 parent c28cbe5 commit 78692e6
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,14 @@

import com.chutneytesting.server.core.domain.scenario.campaign.CampaignExecution;
import java.util.List;
import java.util.Optional;
import java.util.Set;

public interface CampaignExecutionRepository {
Optional<CampaignExecution> currentExecution(Long campaignId);

List<CampaignExecution> currentExecutions();
List<CampaignExecution> currentExecutions(Long campaignId);

void startExecution(Long campaignId, CampaignExecution campaignExecution);

void stopExecution(Long campaignId);
void stopExecution(Long campaignId, String environment);

CampaignExecution getLastExecution(Long campaignId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.chutneytesting.campaign.infra;

import static java.util.Collections.emptyList;
import static org.apache.commons.lang3.Validate.notBlank;
import static org.apache.commons.lang3.Validate.notNull;

Expand All @@ -33,7 +34,6 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
Expand All @@ -50,7 +50,7 @@ public class CampaignExecutionDBRepository implements CampaignExecutionRepositor
private final CampaignJpaRepository campaignJpaRepository;
private final DatabaseExecutionJpaRepository scenarioExecutionJpaRepository;
private final TestCaseRepository testCaseRepository;
private final Map<Long, CampaignExecution> currentCampaignExecutions = new ConcurrentHashMap<>();
private final Map<Long, List<CampaignExecution>> currentCampaignExecutions = new ConcurrentHashMap<>();

public CampaignExecutionDBRepository(
CampaignExecutionJpaRepository campaignExecutionJpaRepository,
Expand All @@ -64,24 +64,27 @@ public CampaignExecutionDBRepository(
}

@Override
public Optional<CampaignExecution> currentExecution(Long campaignId) {
return Optional.ofNullable(campaignId)
.map(id -> currentCampaignExecutions.get(campaignId));
}

@Override
public List<CampaignExecution> currentExecutions() {
return new ArrayList<>(currentCampaignExecutions.values());
public List<CampaignExecution> currentExecutions(Long campaignId) {
return currentCampaignExecutions.getOrDefault(campaignId, emptyList());
}

@Override
public void startExecution(Long campaignId, CampaignExecution campaignExecution) {
currentCampaignExecutions.put(campaignId, campaignExecution);
List<CampaignExecution> campaignExecutions = new ArrayList<>();
if (currentCampaignExecutions.containsKey(campaignId)) {
campaignExecutions = currentCampaignExecutions.get(campaignId);
}
campaignExecutions.add(campaignExecution);
currentCampaignExecutions.put(campaignId, campaignExecutions);
}

@Override
public void stopExecution(Long campaignId) {
currentCampaignExecutions.remove(campaignId);
public void stopExecution(Long campaignId, String environment) {
currentCampaignExecutions.get(campaignId)
.removeIf(exec -> exec.executionEnvironment.equals(environment));
if (currentCampaignExecutions.get(campaignId).isEmpty()) {
currentCampaignExecutions.remove(campaignId);
}
}

@Override
Expand Down Expand Up @@ -176,8 +179,8 @@ private String title(String scenarioId) {
}

private boolean isCampaignExecutionRunning(CampaignExecutionEntity campaignExecutionEntity) {
return currentExecution(campaignExecutionEntity.campaignId())
.map(cer -> cer.executionId.equals(campaignExecutionEntity.id()))
.orElse(false);
return currentExecutions(campaignExecutionEntity.campaignId())
.stream()
.anyMatch(exec -> exec.executionId.equals(campaignExecutionEntity.id()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ public List<ScenarioExecutionEntity> scenarioExecutions() {
return scenarioExecutions;
}

public String environment() { return environment;}

public void updateFromDomain(CampaignExecution report, Iterable<ScenarioExecutionEntity> scenarioExecutions) {
//id = report.executionId;
//campaignId = report.campaignId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ public class CampaignAlreadyRunningException extends RuntimeException {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("YYYYMMdd HH:mm:ss");

public CampaignAlreadyRunningException(CampaignExecution currentReport) {
super("Campaign [" + currentReport.campaignName + "] is already running since " + currentReport.startDate.format(DATE_TIME_FORMATTER));
super(String.format("Campaign [%s] is already running on [%s] since [%s]",
currentReport.campaignName,
currentReport.executionEnvironment,
currentReport.startDate.format(DATE_TIME_FORMATTER)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,13 @@ public CampaignExecution executeById(Long campaignId, String environment, String
.orElseThrow(() -> new CampaignNotFoundException(campaignId));
}

public Optional<CampaignExecution> currentExecution(Long campaignId) {
return campaignExecutionRepository.currentExecution(campaignId);
public Optional<CampaignExecution> currentExecution(Long campaignId, String environment) {
return campaignExecutionRepository.currentExecutions(campaignId)
.stream()
.filter(exec -> exec.executionEnvironment.equals(environment))
.findAny();
}

public List<CampaignExecution> currentExecutions() {
return campaignExecutionRepository.currentExecutions();
}

public void stopExecution(Long executionId) {
LOGGER.trace("Stop requested for " + executionId);
Expand Down Expand Up @@ -177,7 +177,7 @@ public CampaignExecution executeScenarioInCampaign(List<String> failedIds, Campa
campaignExecution.endCampaignExecution();
LOGGER.info("Save campaign {} execution {} with status {}", campaign.id, campaignExecution.executionId, campaignExecution.status());
currentCampaignExecutionsStopRequests.remove(executionId);
campaignExecutionRepository.stopExecution(campaign.id);
campaignExecutionRepository.stopExecution(campaign.id, campaign.executionEnvironment());

Try.exec(() -> {
campaignExecutionRepository.saveCampaignExecution(campaign.id, campaignExecution);
Expand Down Expand Up @@ -303,7 +303,7 @@ private CampaignExecution executeCampaign(Campaign campaign, String userId) {
}

private void verifyNotAlreadyRunning(Campaign campaign) {
Optional<CampaignExecution> currentReport = currentExecution(campaign.id);
Optional<CampaignExecution> currentReport = currentExecution(campaign.id, campaign.executionEnvironment());
if (currentReport.isPresent() && !currentReport.get().status().isFinal()) {
throw new CampaignAlreadyRunningException(currentReport.get());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.chutneytesting.campaign.infra;

import static java.util.Collections.*;
import static java.util.Optional.ofNullable;
import static org.assertj.core.util.Lists.newArrayList;

Expand All @@ -30,7 +31,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
Expand Down Expand Up @@ -139,15 +139,9 @@ public List<Campaign> findCampaignsByScenarioId(String scenarioId) {
}

@Override
public Optional<CampaignExecution> currentExecution(Long campaignId) {
public List<CampaignExecution> currentExecutions(Long campaignId) {
// not needed in tests
return Optional.empty();
}

@Override
public List<CampaignExecution> currentExecutions() {
// not needed in tests
return null;
return emptyList();
}

@Override
Expand All @@ -156,7 +150,7 @@ public void startExecution(Long campaignId, CampaignExecution campaignExecution)
}

@Override
public void stopExecution(Long campaignId) {
public void stopExecution(Long campaignId, String environment) {
// not needed in tests
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.mockito.AdditionalMatchers.or;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
Expand Down Expand Up @@ -60,6 +61,7 @@
import com.chutneytesting.server.core.domain.scenario.campaign.CampaignExecution;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.ExecutorService;
Expand Down Expand Up @@ -266,17 +268,31 @@ public void should_throw_when_no_campaign_found_on_execute_by_id() {
}

@Test
public void should_throw_when_campaign_already_running() {
Campaign campaign = createCampaign(1L);
public void should_throw_when_campaign_already_running_on_the_same_env() {
String env = "env";
Campaign campaign = createCampaign(1L, env);

CampaignExecution mockReport = new CampaignExecution(1L, "", false, "", null, null, "");
when(campaignExecutionRepository.currentExecution(1L)).thenReturn(Optional.of(mockReport));
CampaignExecution mockReport = new CampaignExecution(1L,"", false, env, null, null, "");
when(campaignExecutionRepository.currentExecutions(1L)).thenReturn(List.of(mockReport));

// When
assertThatThrownBy(() -> sut.executeScenarioInCampaign(null, campaign, "user"))
assertThatThrownBy(() -> sut.executeScenarioInCampaign(emptyList(), campaign, "user"))
.isInstanceOf(CampaignAlreadyRunningException.class);
}

@Test
public void should_execute_campaign_in_parallel_on_two_different_envs() {
String env = "env";
String otherEnv = "otherEnv";
Campaign campaign = createCampaign(1L, env);

CampaignExecution mockReport = new CampaignExecution(1L,"", false, otherEnv, null, null, "");
when(campaignExecutionRepository.currentExecutions(anyLong())).thenReturn(List.of(mockReport));

// When
assertDoesNotThrow(()-> sut.executeScenarioInCampaign(emptyList(), campaign, "user"));
}

@Test
public void should_generate_campaign_execution_id_when_executed() {
// Given
Expand Down Expand Up @@ -364,16 +380,20 @@ public void should_execute_campaign_with_given_environment_when_executed_by_name
}

@Test
public void should_retrieve_current_campaign_executions() {
CampaignExecution report = new CampaignExecution(1L, 33L, emptyList(), "", false, "", null, null, "");
CampaignExecution report2 = new CampaignExecution(2L, 42L, emptyList(), "", false, "", null, null, "");
when(campaignExecutionRepository.currentExecution(1L)).thenReturn(Optional.of(report));
when(campaignExecutionRepository.currentExecution(2L)).thenReturn(Optional.of(report2));
public void should_retrieve_current_campaign_execution_on_a_given_env() {
String env = "env";
CampaignExecution report = new CampaignExecution(1L, 33L, emptyList(), "", false, env, null, null, "");
String otherEnv = "otherEnv";
CampaignExecution report2 = new CampaignExecution(2L, 33L, emptyList(), "", false, otherEnv, null, null, "");
CampaignExecution report3 = new CampaignExecution(3L, 42L, emptyList(), "", false, otherEnv, null, null, "");
when(campaignExecutionRepository.currentExecutions(33L)).thenReturn(List.of(report, report2));
when(campaignExecutionRepository.currentExecutions(42L)).thenReturn(List.of(report3));

Optional<CampaignExecution> campaignExecutionReport = sut.currentExecution(1L);
Optional<CampaignExecution> campaignExecutionReport = sut.currentExecution(33L, env);

assertThat(campaignExecutionReport).isNotEmpty();
assertThat(campaignExecutionReport.get().campaignId).isEqualTo(33L);
assertThat(campaignExecutionReport.get().executionId).isEqualTo(1L);
assertThat(campaignExecutionReport.get().executionEnvironment).isEqualTo(env);
}

@Test
Expand Down Expand Up @@ -463,8 +483,8 @@ private Campaign createCampaign() {
return new Campaign(generateId(), "...", null, null, "campaignEnv", false, false, null, null);
}

private Campaign createCampaign(Long idCampaign) {
return new Campaign(idCampaign, "campaign1", null, emptyList(), "env", false, false, null, null);
private Campaign createCampaign(Long idCampaign, String env) {
return new Campaign(idCampaign, "campaign1", null, emptyList(), env, false, false, null, null);
}

private Campaign createCampaign(TestCase firstTestCase, TestCase secondtTestCase) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,9 @@ export class CampaignExecutionsHistoryComponent implements OnInit, OnDestroy {
}

private refreshCampaign() {
if (!this.isRefreshActive()) {
this.campaign$().subscribe(c => {
this.openReport({ execution: this.campaignReports[0], focus: true });
});
}
this.campaign$().subscribe(c => {
this.openReport({ execution: this.campaignReports[0], focus: true });
});
}

private onMenuError(menuErrorEvent: any) {
Expand Down

0 comments on commit 78692e6

Please sign in to comment.