Skip to content

Commit

Permalink
add letter testing setup (#4172)
Browse files Browse the repository at this point in the history
  • Loading branch information
benouaer authored Dec 10, 2024
1 parent 5644904 commit e3f68f1
Show file tree
Hide file tree
Showing 11 changed files with 997 additions and 19 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ definitions/**/*.xlsx#

# befta noise
befta_recent_executions_info_PREVIEW.json
/notification_pdfs/*
15 changes: 15 additions & 0 deletions Jenkinsfile_CNP
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ def branchesToSync = ['demo', 'ithc', 'perftest']
withPipeline(type, product, component) {
def githubApi = new GithubAPI(this)
def isHearingsPr = githubApi.getLabelsbyPattern(env.BRANCH_NAME, "pr-values:hearings").size() > 0;
def notificationTestsEnabled = githubApi.getLabelsbyPattern(env.BRANCH_NAME, "run-notification-tests").size() > 0;

enableHighLevelDataSetup()

Expand Down Expand Up @@ -178,13 +179,27 @@ withPipeline(type, product, component) {
}

afterSuccess('smoketest:preview') {
if (notificationTestsEnabled) {
runNotificationTests()
}
runE2eTests()
}

afterSuccess('smoketest:aat') {
runE2eTests()
}
}

def runNotificationTests() {
try {
stage('Notification tests') {
sh './gradlew verifyNotifications'
}
} finally {
steps.archiveArtifacts artifacts: "notification_pdfs/**"
}
}

def generateDefinitions(isHearingsPr) {
if (env.ENVIRONMENT == "pr" && !isHearingsPr) {
env.DEFINITION_STORE_URL_BASE = "https://ccd-definition-store-sscs-tribunals-api-pr-${CHANGE_ID}.preview.platform.hmcts.net"
Expand Down
13 changes: 11 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,19 @@ tasks.register('functional', Test) {
maxParallelForks = Runtime.runtime.availableProcessors()
}

tasks.register('verifyNotifications', Test) {
group = 'Notifications verification Tests'
description = 'Simulates CCD callbacks to generate notifications, then retrieves pdfs from Gov.Notify'
setTestClassesDirs(sourceSets.functionalTest.output.classesDirs)
setClasspath(sourceSets.functionalTest.runtimeClasspath)
include "uk/gov/hmcts/reform/sscs/notifications/**"
exclude "uk/gov/hmcts/reform/sscs/smoke/**"
maxParallelForks = Runtime.runtime.availableProcessors()
}

tasks.register('smoke', Test) {
group = 'verification'
description = 'Executes non-destructive smoke tests against a running Tribual Case API'
description = 'Executes non-destructive smoke tests against a running Tribunal Case API'
setTestClassesDirs(sourceSets.functionalTest.output.classesDirs)
setClasspath(sourceSets.functionalTest.runtimeClasspath)
include "uk/gov/hmcts/reform/sscs/smoke/**"
Expand Down Expand Up @@ -535,7 +545,6 @@ tasks.register('runAndPublishConsumerPactTests', Test) {
logger.lifecycle("Runs pact Tests")
testClassesDirs = sourceSets.contractTest.output.classesDirs
classpath = sourceSets.contractTest.runtimeClasspath

}

tasks.register('getSscsCommonVersion') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package uk.gov.hmcts.reform.sscs.functional.tyanotifications;

import static helper.EnvironmentProfileValueSource.getEnvOrEmpty;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toSet;
import static junit.framework.TestCase.fail;
Expand All @@ -9,7 +8,9 @@
import static org.junit.Assert.assertNotNull;
import static org.slf4j.LoggerFactory.getLogger;
import static uk.gov.hmcts.reform.sscs.ccd.domain.EventType.SYA_APPEAL_CREATED;
import static uk.gov.hmcts.reform.sscs.tyanotifications.SscsCaseDataUtils.*;
import static uk.gov.hmcts.reform.sscs.tyanotifications.SscsCaseDataUtils.buildSscsCaseData;
import static uk.gov.hmcts.reform.sscs.tyanotifications.SscsCaseDataUtils.buildSscsCaseDataWelsh;
import static uk.gov.hmcts.reform.sscs.tyanotifications.SscsCaseDataUtils.subscribeRep;

import helper.EnvironmentProfileValueSource;
import io.restassured.RestAssured;
Expand All @@ -18,7 +19,15 @@
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDate;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import junitparams.JUnitParamsRunner;
Expand Down Expand Up @@ -63,6 +72,9 @@ public abstract class AbstractFunctionalTest {
// Below rules are needed to use the junitParamsRunner together with SpringRunner
@ClassRule
public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
protected static final String BASE_URL =
System.getenv("TEST_URL") != null ? System.getenv("TEST_URL") : "http://localhost:8008";

protected static final String BASE_PATH_TYAN = "tyanotifications/";
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
Expand Down Expand Up @@ -108,7 +120,6 @@ public AbstractFunctionalTest(int maxSecondsToWaitForNotification) {

protected void createCase() {
this.createCase(false);

}

protected void createCase(boolean isWelsh) {
Expand Down Expand Up @@ -242,14 +253,14 @@ public List<Notification> tryFetchNotificationsForTestCaseWithFlag(boolean notif
}

public List<Notification> fetchLetters() throws NotificationClientException {
List<Notification> allNotifications = new ArrayList<>();
List<Notification> allNotifications;
allNotifications = client.getNotifications("", "letter", caseId.toString(), "").getNotifications();
int secondsLeft = maxSecondsToWaitForNotification;

while (allNotifications.size() == 0 && secondsLeft > 0) {
delayInSeconds(5);
secondsLeft -= 5;
allNotifications = client.getNotifications("", "letter", caseId.toString(), "").getNotifications();

}
return allNotifications;
}
Expand All @@ -265,8 +276,7 @@ protected void simulateCcdCallback(NotificationEventType eventType) throws IOExc
}

public void simulateCcdCallback(NotificationEventType eventType, String resource) throws IOException {
final String callbackUrl = getEnvOrEmpty("TEST_URL") + "/sendNotification";

final String callbackUrl = BASE_URL + "/sendNotification";
String json;
try {
log.info("Getting file {}", resource);
Expand All @@ -292,10 +302,9 @@ public void simulateCcdCallback(NotificationEventType eventType, String resource
.statusCode(HttpStatus.OK.value());
}

private String updateJson(String json, NotificationEventType eventType) {
public String updateJson(String json, NotificationEventType eventType) {
json = json.replace("12345656789", caseId.toString());
json = json.replace("SC022/14/12423", caseReference);
json = json.replace("SC022/14/12423", caseReference);
json = json.replace("EVENT_TYPE", eventType.getId());

if (eventType.equals(NotificationEventType.HEARING_BOOKED)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class DocmosisWithGovNotifyLetterFunctionalTest extends AbstractFunctiona

public static final String EXPECTED_LETTER_SUBJECT = "Pre-compiled PDF";
//TODO: Add callback jsons for these letters to test them functionally
private static final Set<NotificationEventType> DOCMOSIS_LETTERS_WITH_NO_TEST_CALLBACK = EnumSet.of(
static final Set<NotificationEventType> DOCMOSIS_LETTERS_WITH_NO_TEST_CALLBACK = EnumSet.of(
ACTION_HEARING_RECORDING_REQUEST,
ACTION_POSTPONEMENT_REQUEST_WELSH,
ADMIN_APPEAL_WITHDRAWN,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package uk.gov.hmcts.reform.sscs.notifications;

import static java.time.LocalTime.now;
import static java.util.Objects.isNull;
import static org.slf4j.LoggerFactory.getLogger;

import java.io.File;
import java.io.IOException;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import junitparams.JUnitParamsRunner;
import lombok.Getter;
import org.apache.commons.io.FileUtils;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import uk.gov.hmcts.reform.sscs.ccd.domain.SscsCaseData;
import uk.gov.hmcts.reform.sscs.functional.tyanotifications.AbstractFunctionalTest;
import uk.gov.hmcts.reform.sscs.tyanotifications.domain.notify.NotificationEventType;
import uk.gov.service.notify.Notification;
import uk.gov.service.notify.NotificationClient;
import uk.gov.service.notify.NotificationClientException;

@RunWith(JUnitParamsRunner.class)
public abstract class AbstractNotificationsFT extends AbstractFunctionalTest {

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

@Autowired
@Qualifier("testNotificationClient")
@Getter
private NotificationClient client;

protected SscsCaseData caseData;

public AbstractNotificationsFT(int maxSecondsToWaitForNotification) {
super(maxSecondsToWaitForNotification);
}

protected void simulateCcdCallbackToSendLetter(NotificationEventType eventType) throws IOException {
log.info("Simulating CCD callback to send notificaiton of type {}", eventType);
String callbackJsonName = BASE_PATH_TYAN + eventType.getId() + "Callback.json";
if (isNull(getClass().getClassLoader().getResource(callbackJsonName))) {
callbackJsonName = BASE_PATH_TYAN + "missingFileFallbackCallback.json";
log.info("No callback json found for {}, using fallback file", eventType);
}
simulateCcdCallback(eventType, callbackJsonName);
log.info("{} notification successfully sent", eventType);
}

public List<UUID> saveLetterPdfs(List<Notification> notifications, NotificationEventType eventType) {
log.info("Saving notification pdfs for {} notifications triggered by {}",
notifications.size(), eventType);
List<UUID> savedLetters = new ArrayList<>();
LocalTime startTime = now();
int maxMinutesToWaitForPdf = 10;

while (notifications.size() > savedLetters.size()
&& now().isBefore(startTime.plusMinutes(maxMinutesToWaitForPdf))) {
notifications.stream()
.filter(notification -> notification.getNotificationType().equals("letter"))
.filter(notification -> !savedLetters.contains(notification.getId()))
.forEach(notification -> {
try {
final byte[] pdfForLetter = client.getPdfForLetter(String.valueOf(notification.getId()));
var pdfName = "notification_pdfs/" + eventType + "_" + notification.getTemplateId() + ".pdf";
FileUtils.writeByteArrayToFile(new File(pdfName), pdfForLetter);
savedLetters.add(notification.getId());
} catch (NotificationClientException | IOException e) {
log.error("Failed to save pdf for template {}", notification.getTemplateId());
delayInSeconds(60);
}
});
}
log.info("Finished saving letter pdfs : {} out of {} successfully saved",
notifications.size() - savedLetters.size(), notifications.size());
return savedLetters;
}

public void logFailedEventNotification(NotificationEventType notificationType, Exception e) {
log.error("Failed testing notification type {} with the following", notificationType, e);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package uk.gov.hmcts.reform.sscs.notifications;

import static org.assertj.core.api.Assertions.assertThat;
import static uk.gov.hmcts.reform.sscs.tyanotifications.config.NotificationEventTypeLists.DOCMOSIS_LETTERS;
import static uk.gov.hmcts.reform.sscs.tyanotifications.domain.notify.NotificationEventType.ACTION_HEARING_RECORDING_REQUEST;

import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import junitparams.JUnitParamsRunner;
import lombok.SneakyThrows;
import org.junit.Rule;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.rules.Timeout;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.test.context.TestPropertySource;
import uk.gov.hmcts.reform.sscs.tyanotifications.domain.notify.NotificationEventType;
import uk.gov.service.notify.Notification;
import uk.gov.service.notify.NotificationClientException;

@RunWith(JUnitParamsRunner.class)
@TestPropertySource(locations = "classpath:config/application_functional.properties")
public class DocmosisGovNotifyLettersFT extends AbstractNotificationsFT {

@Rule
public Timeout globalTimeout = Timeout.seconds(30 * 60);

@Value("${generate-all-notifications}")
private boolean generateAllNotifications;

@Value("${notifications-list}")
private Set<NotificationEventType> notificationsList;

public DocmosisGovNotifyLettersFT() {
super(30);
}

@BeforeEach
public void setup() {
super.setup();
}

@SneakyThrows
@Test
public void shouldSendDocmosisLettersViaGovNotify() {
getNotificationList()
.forEach(notificationEventType -> {
try {
simulateCcdCallbackToSendLetter(notificationEventType);
List<Notification> notifications = fetchLetters();
saveLetterPdfs(notifications, notificationEventType);
assertThat(notifications)
.extracting(Notification::getSubject)
.allSatisfy(subject -> assertThat(subject).isPresent());

} catch (IOException | NotificationClientException e) {
logFailedEventNotification(notificationEventType, e);
}
});
}

private Set<NotificationEventType> getNotificationList() {
if (generateAllNotifications) {
return DOCMOSIS_LETTERS.stream()
.filter(eventType -> !eventType.equals(ACTION_HEARING_RECORDING_REQUEST))
.collect(Collectors.toSet());
}
return notificationsList;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ job.scheduler.retryPolicy.maxNumberOfJobExecutions=2
job.scheduler.delayBetweenAttemptsInMs=1000
job.scheduler.quartzProperties.org.quartz.scheduler.instanceId=AUTO
job.scheduler.quartzProperties.jobStore.class=org.quartz.simpl.RAMJobStore
spring.flyway.enabled=false
spring.flyway.enabled=false

generate-all-notifications=true
notifications-list=ISSUE_ADJOURNMENT_NOTICE_WELSH
Original file line number Diff line number Diff line change
Expand Up @@ -707,4 +707,4 @@
},
"case_details_before": null,
"event_id": "appealCreated"
}
}
Loading

0 comments on commit e3f68f1

Please sign in to comment.