From 2ed6784ffa785b2f864d8fc3334bac1aa711da20 Mon Sep 17 00:00:00 2001 From: Sabah u Din Irfan Date: Thu, 9 May 2024 11:46:36 +0100 Subject: [PATCH] PO-282: Automatically create required locations on Azure (#348) --- build.gradle | 2 +- .../values.stg.template.yaml | 2 + .../opal/scheduler/job/FileHandlerJob.java | 8 +- .../uk/gov/hmcts/opal/sftp/SftpDirection.java | 7 ++ .../hmcts/opal/sftp/SftpInboundService.java | 45 ++++++++ .../uk/gov/hmcts/opal/sftp/SftpLocation.java | 48 +++++++++ .../hmcts/opal/sftp/SftpOutboundService.java | 45 ++++++++ .../uk/gov/hmcts/opal/sftp/SftpService.java | 54 +++++----- .../opal/sftp/config/SftpConfiguration.java | 26 +++-- .../opal/sftp/config/SftpConnection.java | 1 + src/main/resources/application.yaml | 2 + .../scheduler/job/FileHandlerJobTest.java | 8 +- .../opal/sftp/SftpInboundServiceTest.java | 101 ++++++++++++++++++ .../gov/hmcts/opal/sftp/SftpLocationTest.java | 32 ++++++ .../opal/sftp/SftpOutboundServiceTest.java | 101 ++++++++++++++++++ .../gov/hmcts/opal/sftp/SftpServiceTest.java | 96 +++++++++++++++++ 16 files changed, 537 insertions(+), 41 deletions(-) create mode 100644 src/main/java/uk/gov/hmcts/opal/sftp/SftpDirection.java create mode 100644 src/main/java/uk/gov/hmcts/opal/sftp/SftpInboundService.java create mode 100644 src/main/java/uk/gov/hmcts/opal/sftp/SftpLocation.java create mode 100644 src/main/java/uk/gov/hmcts/opal/sftp/SftpOutboundService.java create mode 100644 src/test/java/uk/gov/hmcts/opal/sftp/SftpInboundServiceTest.java create mode 100644 src/test/java/uk/gov/hmcts/opal/sftp/SftpLocationTest.java create mode 100644 src/test/java/uk/gov/hmcts/opal/sftp/SftpOutboundServiceTest.java create mode 100644 src/test/java/uk/gov/hmcts/opal/sftp/SftpServiceTest.java diff --git a/build.gradle b/build.gradle index 1a94cb7cf..b8ba325a9 100644 --- a/build.gradle +++ b/build.gradle @@ -190,7 +190,7 @@ sonarqube { property "sonar.projectKey", "uk.gov.hmcts:opal-fines-service" property "sonar.gradle.skipCompile", "true" property "sonar.exclusions", coverageExclusions.join(', ') - property 'sonar.coverage.exclusions', "**/entity/*,**/model/*,**/exception/*,**/sftp/*,**/repository/jpa/*,**/opal/dto/*" + property 'sonar.coverage.exclusions', "**/entity/*,**/model/*,**/exception/*,**/config/**,**/repository/jpa/*,**/opal/dto/*" } } diff --git a/charts/opal-fines-service/values.stg.template.yaml b/charts/opal-fines-service/values.stg.template.yaml index a8c397392..4373a1ff8 100644 --- a/charts/opal-fines-service/values.stg.template.yaml +++ b/charts/opal-fines-service/values.stg.template.yaml @@ -6,4 +6,6 @@ java: OPAL_FRONTEND_URL: https://opal-frontend-staging.staging.platform.hmcts.net OPAL_LEGACY_GATEWAY_URL: https://opal-legacy-db-stub.staging.platform.hmcts.net TESTING_SUPPORT_ENDPOINTS_ENABLED: true + OPAL_SFTP_INBOUND_CREATE_SUB_LOCATIONS: true + OPAL_SFTP_OUTBOUND_CREATE_SUB_LOCATIONS: true diff --git a/src/main/java/uk/gov/hmcts/opal/scheduler/job/FileHandlerJob.java b/src/main/java/uk/gov/hmcts/opal/scheduler/job/FileHandlerJob.java index d7e67d547..c1c595714 100644 --- a/src/main/java/uk/gov/hmcts/opal/scheduler/job/FileHandlerJob.java +++ b/src/main/java/uk/gov/hmcts/opal/scheduler/job/FileHandlerJob.java @@ -10,7 +10,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import uk.gov.hmcts.opal.scheduler.model.CronJob; -import uk.gov.hmcts.opal.sftp.SftpService; +import uk.gov.hmcts.opal.sftp.SftpOutboundService; import java.io.IOException; import java.io.InputStream; @@ -28,7 +28,7 @@ public class FileHandlerJob implements CronJob { private String cronExpression; @Autowired - private SftpService sftpService; + private SftpOutboundService sftpOutboundService; @SneakyThrows @@ -40,7 +40,7 @@ public void execute(JobExecutionContext context) throws JobExecutionException { String fileName = format("test-file-%s.txt", now()); this.uploadFile("My file contents here...", fileName); - sftpService.downloadOutboundFile("", fileName, this::logInputStream); + sftpOutboundService.downloadFile("", fileName, this::logInputStream); log.info( "Job ** {} ** completed. Next job scheduled @ {}", @@ -51,7 +51,7 @@ public void execute(JobExecutionContext context) throws JobExecutionException { } public void uploadFile(String contents, String fileName) { - sftpService.uploadOutboundFile(format("%s %s", contents, now()).getBytes(), "", fileName); + sftpOutboundService.uploadFile(format("%s %s", contents, now()).getBytes(), "", fileName); } diff --git a/src/main/java/uk/gov/hmcts/opal/sftp/SftpDirection.java b/src/main/java/uk/gov/hmcts/opal/sftp/SftpDirection.java new file mode 100644 index 000000000..1bc3ef3a0 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/opal/sftp/SftpDirection.java @@ -0,0 +1,7 @@ +package uk.gov.hmcts.opal.sftp; + +public enum SftpDirection { + INBOUND, + OUTBOUND +} + diff --git a/src/main/java/uk/gov/hmcts/opal/sftp/SftpInboundService.java b/src/main/java/uk/gov/hmcts/opal/sftp/SftpInboundService.java new file mode 100644 index 000000000..2fb4aa205 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/opal/sftp/SftpInboundService.java @@ -0,0 +1,45 @@ +package uk.gov.hmcts.opal.sftp; + +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.integration.sftp.session.DefaultSftpSessionFactory; +import org.springframework.stereotype.Service; +import uk.gov.hmcts.opal.sftp.config.SftpConnection; + +import java.io.InputStream; +import java.util.function.Consumer; + +import static uk.gov.hmcts.opal.sftp.SftpLocation.getInboundLocations; + +@Slf4j +@Service +@RequiredArgsConstructor +public class SftpInboundService { + + private final DefaultSftpSessionFactory inboundSessionFactory; + private final SftpService sftpService; + private final SftpConnection inboundConnection; + + public void uploadFile(byte[] fileBytes, String path, String fileName) { + sftpService.uploadFile(inboundSessionFactory, fileBytes, path, fileName); + } + + public boolean downloadFile(String path, String fileName, Consumer fileProcessor) { + return sftpService.downloadFile(inboundSessionFactory, path, fileName, fileProcessor); + } + + public boolean deleteFile(String path, String fileName) { + return sftpService.deleteFile(inboundSessionFactory, path, fileName); + } + + @PostConstruct + public void createSftpLocations() { + if (inboundConnection.isCreateSubLocations()) { + getInboundLocations() + .forEach(sftpLocation -> + this.sftpService.createDirectoryIfNotExists(inboundSessionFactory, sftpLocation) + ); + } + } +} diff --git a/src/main/java/uk/gov/hmcts/opal/sftp/SftpLocation.java b/src/main/java/uk/gov/hmcts/opal/sftp/SftpLocation.java new file mode 100644 index 000000000..8e1190a18 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/opal/sftp/SftpLocation.java @@ -0,0 +1,48 @@ +package uk.gov.hmcts.opal.sftp; + +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static uk.gov.hmcts.opal.sftp.SftpDirection.INBOUND; +import static uk.gov.hmcts.opal.sftp.SftpDirection.OUTBOUND; + +@Getter +public enum SftpLocation { + + AUTO_CHEQUES(INBOUND, "auto-cheque", "Comes from OAGS (pushed)"), + AUTO_CASH(INBOUND, "auto-cash", "Comes from OAGS (pushed)"), + NATWEST(INBOUND, "natwest", "Comes from BAIS (pulled)"), + ALL_PAY_BT_BARCLAY_CARD(INBOUND, "allpay", "Comes from BAIS (pulled)"), + DWP_BAILIFFS(INBOUND, "dwp-bailiffs", "Comes from BAIS (pulled)"), + + ALL_PAY(OUTBOUND, "allpay", "Goes to BAIS (pushed)"), + ARCHIVE(OUTBOUND, "allpay-archive", "Goes to OAGS (pushed)"); + + private final SftpDirection direction; + private final String path; + private final String description; + + SftpLocation(SftpDirection direction, String path, String description) { + this.direction = direction; + this.path = path; + this.description = description; + } + + public static List getLocations(SftpDirection direction) { + return Arrays.stream(SftpLocation.values()) + .filter(location -> location.getDirection() == direction) + .collect(Collectors.toList()); + } + + public static List getInboundLocations() { + return getLocations(INBOUND); + } + + public static List getOutboundLocations() { + return getLocations(OUTBOUND); + } + +} diff --git a/src/main/java/uk/gov/hmcts/opal/sftp/SftpOutboundService.java b/src/main/java/uk/gov/hmcts/opal/sftp/SftpOutboundService.java new file mode 100644 index 000000000..f09936708 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/opal/sftp/SftpOutboundService.java @@ -0,0 +1,45 @@ +package uk.gov.hmcts.opal.sftp; + +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.integration.sftp.session.DefaultSftpSessionFactory; +import org.springframework.stereotype.Service; +import uk.gov.hmcts.opal.sftp.config.SftpConnection; + +import java.io.InputStream; +import java.util.function.Consumer; + +import static uk.gov.hmcts.opal.sftp.SftpLocation.getOutboundLocations; + +@Slf4j +@Service +@RequiredArgsConstructor +public class SftpOutboundService { + + private final DefaultSftpSessionFactory outboundSessionFactory; + private final SftpService sftpService; + private final SftpConnection outboundConnection; + + public void uploadFile(byte[] fileBytes, String path, String fileName) { + sftpService.uploadFile(outboundSessionFactory, fileBytes, path, fileName); + } + + public boolean downloadFile(String path, String fileName, Consumer fileProcessor) { + return sftpService.downloadFile(outboundSessionFactory, path, fileName, fileProcessor); + } + + public boolean deleteFile(String path, String fileName) { + return sftpService.deleteFile(outboundSessionFactory, path, fileName); + } + + @PostConstruct + public void createSftpLocations() { + if (outboundConnection.isCreateSubLocations()) { + getOutboundLocations() + .forEach(sftpLocation -> + this.sftpService.createDirectoryIfNotExists(outboundSessionFactory, sftpLocation) + ); + } + } +} diff --git a/src/main/java/uk/gov/hmcts/opal/sftp/SftpService.java b/src/main/java/uk/gov/hmcts/opal/sftp/SftpService.java index 0103beb67..beb6bc3af 100644 --- a/src/main/java/uk/gov/hmcts/opal/sftp/SftpService.java +++ b/src/main/java/uk/gov/hmcts/opal/sftp/SftpService.java @@ -1,12 +1,13 @@ package uk.gov.hmcts.opal.sftp; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.integration.file.remote.RemoteFileTemplate; import org.springframework.integration.sftp.session.DefaultSftpSessionFactory; +import org.springframework.integration.sftp.session.SftpSession; import org.springframework.stereotype.Service; import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.InputStream; import java.util.function.Consumer; @@ -14,16 +15,8 @@ @Slf4j @Service -@RequiredArgsConstructor public class SftpService { - private final DefaultSftpSessionFactory inboundSessionFactory; - private final DefaultSftpSessionFactory outboundSessionFactory; - - public void uploadOutboundFile(byte[] fileBytes, String path, String fileName) { - uploadFile(outboundSessionFactory, fileBytes, path, fileName); - } - public void uploadFile(DefaultSftpSessionFactory sessionFactory, byte[] fileBytes, String path, String fileName) { var template = new RemoteFileTemplate<>(sessionFactory); template.execute(session -> { @@ -33,14 +26,6 @@ public void uploadFile(DefaultSftpSessionFactory sessionFactory, byte[] fileByte }); } - public boolean downloadInboundFile(String path, String fileName, Consumer fileProcessor) { - return downloadFile(inboundSessionFactory, path, fileName, fileProcessor); - } - - public boolean downloadOutboundFile(String path, String fileName, Consumer fileProcessor) { - return downloadFile(outboundSessionFactory, path, fileName, fileProcessor); - } - public boolean downloadFile(DefaultSftpSessionFactory sessionFactory, String path, String fileName, @@ -49,17 +34,38 @@ public boolean downloadFile(DefaultSftpSessionFactory sessionFactory, return template.get(path + "/" + fileName, fileProcessor::accept); } - public boolean deleteOutboundFile(String path, String fileName) { - return deleteFile(outboundSessionFactory, path, fileName); + public boolean deleteFile(DefaultSftpSessionFactory sessionFactory, String path, String fileName) { + var template = new RemoteFileTemplate<>(sessionFactory); + return template.execute(session -> session.remove(path + "/" + fileName)); } - public boolean deleteInboundFile(String path, String fileName) { - return deleteFile(inboundSessionFactory, path, fileName); + public void createDirectoryIfNotExists(DefaultSftpSessionFactory sessionFactory, SftpLocation location) { + try { + String remoteDirectory = location.getPath(); + if (!directoryExists(sessionFactory, remoteDirectory)) { + sessionFactory.getSession().mkdir(remoteDirectory); + log.info(format( + "%s SFTP directory %s created for %s", + location.getDirection(), + remoteDirectory, + location.getDescription() + )); + } + } catch (Exception exception) { + log.error(exception.getMessage(), exception); + } } - public boolean deleteFile(DefaultSftpSessionFactory sessionFactory, String path, String fileName) { - var template = new RemoteFileTemplate<>(sessionFactory); - return template.execute(session -> session.remove(path + "/" + fileName)); + public boolean directoryExists(DefaultSftpSessionFactory sessionFactory, String path) throws IOException { + + try (SftpSession session = sessionFactory.getSession()) { + for (var file : session.list("/")) { + if (path.equalsIgnoreCase(file.getFilename())) { + return true; + } + } + return false; + } } } diff --git a/src/main/java/uk/gov/hmcts/opal/sftp/config/SftpConfiguration.java b/src/main/java/uk/gov/hmcts/opal/sftp/config/SftpConfiguration.java index b16a90cc5..f42717ee1 100644 --- a/src/main/java/uk/gov/hmcts/opal/sftp/config/SftpConfiguration.java +++ b/src/main/java/uk/gov/hmcts/opal/sftp/config/SftpConfiguration.java @@ -11,25 +11,35 @@ public class SftpConfiguration { private final SftpProperties sftpProperties; + @Bean + public SftpConnection inboundConnection() { + return sftpProperties.getInbound(); + } + @Bean public DefaultSftpSessionFactory inboundSessionFactory() { DefaultSftpSessionFactory sessionFactory = new DefaultSftpSessionFactory(); - sessionFactory.setHost(sftpProperties.getInbound().getHost()); - sessionFactory.setPort(sftpProperties.getInbound().getPort()); - sessionFactory.setUser(sftpProperties.getInbound().getUser()); - sessionFactory.setPassword(sftpProperties.getInbound().getPassword()); + sessionFactory.setHost(inboundConnection().getHost()); + sessionFactory.setPort(inboundConnection().getPort()); + sessionFactory.setUser(inboundConnection().getUser()); + sessionFactory.setPassword(inboundConnection().getPassword()); sessionFactory.setAllowUnknownKeys(true); return sessionFactory; } + @Bean + public SftpConnection outboundConnection() { + return sftpProperties.getOutbound(); + } + @Bean public DefaultSftpSessionFactory outboundSessionFactory() { DefaultSftpSessionFactory sessionFactory = new DefaultSftpSessionFactory(); - sessionFactory.setHost(sftpProperties.getOutbound().getHost()); - sessionFactory.setPort(sftpProperties.getOutbound().getPort()); - sessionFactory.setUser(sftpProperties.getOutbound().getUser()); - sessionFactory.setPassword(sftpProperties.getOutbound().getPassword()); + sessionFactory.setHost(outboundConnection().getHost()); + sessionFactory.setPort(outboundConnection().getPort()); + sessionFactory.setUser(outboundConnection().getUser()); + sessionFactory.setPassword(outboundConnection().getPassword()); sessionFactory.setAllowUnknownKeys(true); return sessionFactory; diff --git a/src/main/java/uk/gov/hmcts/opal/sftp/config/SftpConnection.java b/src/main/java/uk/gov/hmcts/opal/sftp/config/SftpConnection.java index 49ff97d67..439fbfa07 100644 --- a/src/main/java/uk/gov/hmcts/opal/sftp/config/SftpConnection.java +++ b/src/main/java/uk/gov/hmcts/opal/sftp/config/SftpConnection.java @@ -12,4 +12,5 @@ public class SftpConnection { private String user; private String password; private String location; + private boolean createSubLocations; } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 3b5666487..bfe125c0c 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -117,12 +117,14 @@ opal: user: ${OPAL_SFTP_INBOUND_USER:-} password: ${OPAL_SFTP_INBOUND_PASSWORD:-} location: ${OPAL_SFTP_INBOUND_LOCATION:inbound} + create-sub-locations: ${OPAL_SFTP_INBOUND_CREATE_SUB_LOCATIONS:false} outbound: host: ${OPAL_SFTP_OUTBOUND_HOST:opalsftpstg.blob.core.windows.net} port: ${OPAL_SFTP_OUTBOUND_PORT:22} user: ${OPAL_SFTP_OUTBOUND_USER:-} password: ${OPAL_SFTP_OUTBOUND_PASSWORD:-} location: ${OPAL_SFTP_OUTBOUND_LOCATION:outbound} + create-sub-locations: ${OPAL_SFTP_OUTBOUND_CREATE_SUB_LOCATIONS:false} schedule: log-retention-job: cron: ${OPAL_LOG_RETENTION_JOB_CRON:0 0 * * * ?} diff --git a/src/test/java/uk/gov/hmcts/opal/scheduler/job/FileHandlerJobTest.java b/src/test/java/uk/gov/hmcts/opal/scheduler/job/FileHandlerJobTest.java index 608138d0e..7f2b2880f 100644 --- a/src/test/java/uk/gov/hmcts/opal/scheduler/job/FileHandlerJobTest.java +++ b/src/test/java/uk/gov/hmcts/opal/scheduler/job/FileHandlerJobTest.java @@ -11,7 +11,7 @@ import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; -import uk.gov.hmcts.opal.sftp.SftpService; +import uk.gov.hmcts.opal.sftp.SftpOutboundService; import java.io.ByteArrayInputStream; import java.io.InputStream; @@ -33,7 +33,7 @@ class FileHandlerJobTest { JobExecutionContext jobExecutionContext; @Mock - SftpService sftpService; + SftpOutboundService sftpOutboundService; @Mock Logger logger; @@ -57,8 +57,8 @@ void testExecute() throws JobExecutionException { fileHandlerJob.execute(jobExecutionContext); - verify(sftpService, times(1)).uploadOutboundFile(any(), anyString(), anyString()); - verify(sftpService, times(1)).downloadOutboundFile(anyString(), anyString(), any()); + verify(sftpOutboundService, times(1)).uploadFile(any(), anyString(), anyString()); + verify(sftpOutboundService, times(1)).downloadFile(anyString(), anyString(), any()); } @Test diff --git a/src/test/java/uk/gov/hmcts/opal/sftp/SftpInboundServiceTest.java b/src/test/java/uk/gov/hmcts/opal/sftp/SftpInboundServiceTest.java new file mode 100644 index 000000000..5701be9ef --- /dev/null +++ b/src/test/java/uk/gov/hmcts/opal/sftp/SftpInboundServiceTest.java @@ -0,0 +1,101 @@ +package uk.gov.hmcts.opal.sftp; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.integration.sftp.session.DefaultSftpSessionFactory; +import uk.gov.hmcts.opal.sftp.config.SftpConnection; + +import java.io.InputStream; +import java.util.List; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@SpringBootTest(classes = {SftpInboundService.class}) +class SftpInboundServiceTest { + + // Mocks + @MockBean + DefaultSftpSessionFactory inboundSessionFactory; + + @MockBean + SftpService sftpService; + + @MockBean + SftpConnection inboundConnection; + + @Autowired + SftpInboundService sftpInboundService; + + + @Test + void testUploadFile() { + byte[] fileBytes = {/* Some byte array */}; + String path = "test/path"; + String fileName = "test.txt"; + + sftpInboundService.uploadFile(fileBytes, path, fileName); + + verify(sftpService).uploadFile(inboundSessionFactory, fileBytes, path, fileName); + } + + @Test + void testDownloadFile() { + String path = "test/path"; + String fileName = "test.txt"; + + Consumer fileProcessor = inputStream -> { + }; + sftpInboundService.downloadFile(path, fileName, fileProcessor); + + verify(sftpService).downloadFile(inboundSessionFactory, path, fileName, fileProcessor); + } + + @Test + void testDeleteFile() { + String path = "test/path"; + String fileName = "test.txt"; + + when(sftpService.deleteFile(inboundSessionFactory, path, fileName)).thenReturn(true); + + boolean result = sftpInboundService.deleteFile(path, fileName); + + verify(sftpService).deleteFile(inboundSessionFactory, path, fileName); + + assertTrue(result); + } + + @Test + void testCreateSftpLocationsWhenCreateSubLocationsIsTrue() { + + when(inboundConnection.isCreateSubLocations()).thenReturn(true); + + List inboundLocations = SftpLocation.getInboundLocations(); + + doNothing().when(sftpService).createDirectoryIfNotExists(any(), any()); + + sftpInboundService.createSftpLocations(); + + for (SftpLocation inboundLocation : inboundLocations) { + verify(sftpService).createDirectoryIfNotExists(inboundSessionFactory, inboundLocation); + } + } + + @Test + void testCreateSftpLocationsWhenCreateSubLocationsIsFalse() { + when(inboundConnection.isCreateSubLocations()).thenReturn(false); + + doNothing().when(sftpService).createDirectoryIfNotExists(any(), any()); + + sftpInboundService.createSftpLocations(); + + verify(sftpService, never()).createDirectoryIfNotExists(any(), any()); + } +} diff --git a/src/test/java/uk/gov/hmcts/opal/sftp/SftpLocationTest.java b/src/test/java/uk/gov/hmcts/opal/sftp/SftpLocationTest.java new file mode 100644 index 000000000..fd2f36182 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/opal/sftp/SftpLocationTest.java @@ -0,0 +1,32 @@ +package uk.gov.hmcts.opal.sftp; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static uk.gov.hmcts.opal.sftp.SftpDirection.INBOUND; + +@ExtendWith(MockitoExtension.class) +class SftpLocationTest { + + @Test + void testGetInboundLocations() { + List inboundLocations = SftpLocation.getInboundLocations(); + assertEquals(5, inboundLocations.size()); + } + + @Test + void testGetOutboundLocations() { + List outboundLocations = SftpLocation.getOutboundLocations(); + assertEquals(2, outboundLocations.size()); + } + + @Test + void testGetLocations() { + List locations = SftpLocation.getLocations(INBOUND); + assertEquals(5, locations.size()); + } +} diff --git a/src/test/java/uk/gov/hmcts/opal/sftp/SftpOutboundServiceTest.java b/src/test/java/uk/gov/hmcts/opal/sftp/SftpOutboundServiceTest.java new file mode 100644 index 000000000..cc3c66421 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/opal/sftp/SftpOutboundServiceTest.java @@ -0,0 +1,101 @@ +package uk.gov.hmcts.opal.sftp; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.integration.sftp.session.DefaultSftpSessionFactory; +import uk.gov.hmcts.opal.sftp.config.SftpConnection; + +import java.io.InputStream; +import java.util.List; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@SpringBootTest(classes = {SftpOutboundService.class}) +class SftpOutboundServiceTest { + + // Mocks + @MockBean + DefaultSftpSessionFactory outboundSessionFactory; + + @MockBean + SftpService sftpService; + + @MockBean + SftpConnection outboundConnection; + + @Autowired + SftpOutboundService sftpOutboundService; + + + @Test + void testUploadFile() { + byte[] fileBytes = {/* Some byte array */}; + String path = "test/path"; + String fileName = "test.txt"; + + sftpOutboundService.uploadFile(fileBytes, path, fileName); + + verify(sftpService).uploadFile(outboundSessionFactory, fileBytes, path, fileName); + } + + @Test + void testDownloadFile() { + String path = "test/path"; + String fileName = "test.txt"; + + Consumer fileProcessor = inputStream -> { + }; + sftpOutboundService.downloadFile(path, fileName, fileProcessor); + + verify(sftpService).downloadFile(outboundSessionFactory, path, fileName, fileProcessor); + } + + @Test + void testDeleteFile() { + String path = "test/path"; + String fileName = "test.txt"; + + when(sftpService.deleteFile(outboundSessionFactory, path, fileName)).thenReturn(true); + + boolean result = sftpOutboundService.deleteFile(path, fileName); + + verify(sftpService).deleteFile(outboundSessionFactory, path, fileName); + + assertTrue(result); + } + + @Test + void testCreateSftpLocationsWhenCreateSubLocationsIsTrue() { + + when(outboundConnection.isCreateSubLocations()).thenReturn(true); + + List outboundLocations = SftpLocation.getOutboundLocations(); + + doNothing().when(sftpService).createDirectoryIfNotExists(any(), any()); + + sftpOutboundService.createSftpLocations(); + + for (SftpLocation location : outboundLocations) { + verify(sftpService).createDirectoryIfNotExists(outboundSessionFactory, location); + } + } + + @Test + void testCreateSftpLocationsWhenCreateSubLocationsIsFalse() { + when(outboundConnection.isCreateSubLocations()).thenReturn(false); + + doNothing().when(sftpService).createDirectoryIfNotExists(any(), any()); + + sftpOutboundService.createSftpLocations(); + + verify(sftpService, never()).createDirectoryIfNotExists(any(), any()); + } +} diff --git a/src/test/java/uk/gov/hmcts/opal/sftp/SftpServiceTest.java b/src/test/java/uk/gov/hmcts/opal/sftp/SftpServiceTest.java new file mode 100644 index 000000000..43b42a78e --- /dev/null +++ b/src/test/java/uk/gov/hmcts/opal/sftp/SftpServiceTest.java @@ -0,0 +1,96 @@ +package uk.gov.hmcts.opal.sftp; + +import lombok.SneakyThrows; +import org.apache.sshd.sftp.client.SftpClient; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.integration.sftp.session.DefaultSftpSessionFactory; +import org.springframework.integration.sftp.session.SftpSession; + +import java.io.IOException; +import java.io.InputStream; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static uk.gov.hmcts.opal.sftp.SftpLocation.AUTO_CHEQUES; + +@ExtendWith(MockitoExtension.class) +class SftpServiceTest { + + @InjectMocks + private SftpService sftpService; + + @Mock + DefaultSftpSessionFactory sessionFactory; + + @Mock + SftpSession sftpSession; + + @Test + @SneakyThrows + void testUploadFile() { + byte[] fileBytes = "Test file content".getBytes(); + String path = "/test"; + String fileName = "test.txt"; + + when(sessionFactory.getSession()).thenReturn(sftpSession); + + sftpService.uploadFile(sessionFactory, fileBytes, path, fileName); + verify(sftpSession).write(any(InputStream.class), eq(path + "/" + fileName)); + } + + @Test + void testDownloadFile() { + String path = "/test"; + String fileName = "test.txt"; + + when(sessionFactory.getSession()).thenReturn(sftpSession); + when(sftpSession.finalizeRaw()).thenReturn(true); + + boolean result = sftpService.downloadFile(sessionFactory, path, fileName, inputStream -> { + }); + + assertTrue(result); + } + + @Test + @SneakyThrows + void testDeleteFile() { + String path = "/test"; + String fileName = "test.txt"; + + when(sessionFactory.getSession()).thenReturn(sftpSession); + when(sftpSession.remove(anyString())).thenReturn(true); + assertTrue(sftpService.deleteFile(sessionFactory, path, fileName)); + } + + @Test + void testCreateDirectoryIfNotExists() throws IOException { + + when(sessionFactory.getSession()).thenReturn(sftpSession); + when(sftpSession.list(any())).thenReturn(new SftpClient.DirEntry[]{}); + when(sftpSession.mkdir(anyString())).thenReturn(true); + + sftpService.createDirectoryIfNotExists(sessionFactory, AUTO_CHEQUES); + + verify(sftpSession).mkdir(AUTO_CHEQUES.getPath()); + } + + @Test + void testDirectoryExists() throws IOException { + String path = "/non-existent"; + + when(sessionFactory.getSession()).thenReturn(sftpSession); + when(sftpSession.list(any())).thenReturn(new SftpClient.DirEntry[]{}); + + assertFalse(sftpService.directoryExists(sessionFactory, path)); + } +}