GH-3804: Migrate SMB unit tests to Testcontainers

Fixes https://github.com/spring-projects/spring-integration/issues/3804

Instead of manual SMB server setup and preparation, make it all
automated using convenient Testcontainers library and a bit of extra
code executed before tests.

* Change `localhost` to `127.0.0.1` for a proper integration with docker container
* A few new config values to support container environment, like tmpFs,
share permissions and a dynamic port value
* Introduce Testcontainers API for container management
* Few configuration changes to support docker container usage
* Automatic dirs and files layout creation in `@BeforeAll`
* A few bugfixes where the assertion has not been done actually
* Couple of small changes related to assertions readability as well
* Use `warn` as default logging level for the SMB tests
* Refactor an `SmbTests` a bit: moving its `createFilesInSmbShare()` to
the `SmbTestSupport` since that files tree can be useful for any other
similar integration tests in the future
* Use `TestUtils.applySystemFileSeparator()` for cross-platform file paths
This commit is contained in:
Artem Vozhdayenko
2022-07-11 07:21:10 -04:00
committed by Artem Bilan
parent b074f1961f
commit f349e1a114
3 changed files with 92 additions and 69 deletions

View File

@@ -16,49 +16,99 @@
package org.springframework.integration.smb;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.integration.file.remote.RemoteFileTestSupport;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.file.remote.session.Session;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.smb.session.SmbSessionFactory;
import org.springframework.integration.test.util.TestUtils;
import jcifs.DialectVersion;
import jcifs.smb.SmbFile;
/**
* Provides a connection to an external SMB Server for test cases.
* Provides a connection to a Testcontainers-driven SMB Server for test cases.
*
* The constants need to be updated with the 'real' server settings for testing.
* The following folder structures in the SMB share is expected
* for a successful completion of these unit tests:
*
* <pre class="code">
* smbSource/
* |-- smbSource1.txt - contains 'source1'
* |-- smbSource2.txt - contains 'source2'
* |-- SMBSOURCE1.TXT.a
* |-- SMBSOURCE2.TXT.a
* |-- subSmbSource/
* |-- subSmbSource1.txt - contains 'subSource1'
* |-- subSmbSource2.txt - contains 'subSource2'
* |-- subSmbSource2/ - directory will be created in testSmbPutFlow
* |-- subSmbSource2-1.txt - file will be created in testSmbPutFlow and deleted in testSmbRmFlow
* |-- subSmbSource2-2.txt - file will be created in testSmbMputFlow
* |-- subSmbSource2-3.txt - file will be created in testSmbMputFlow and renamed in testSmbMvFlow to subSmbSource-MV-Flow-Renamed.txt
* smbTarget/
* </pre>
*
* @author Gregory Bragg
* @author Artem Vozhdayenko
* @author Artem Bilan
*
* @since 6.0
*/
@Disabled("Actual SMB share must be configured in class [SmbTestSupport].")
@Testcontainers(disabledWithoutDocker = true)
public class SmbTestSupport extends RemoteFileTestSupport {
public static final String HOST = "localhost";
public static final String HOST = "127.0.0.1";
public static final String SHARE_AND_DIR = "smb-share/";
public static final String SHARE_AND_DIR = "smb-share";
public static final String USERNAME = "sambaguest";
public static final String PASSWORD = "sambaguest";
private static final String INNER_SHARE_DIR = "/tmp";
private static final GenericContainer<?> SMB_CONTAINER = new GenericContainer<>("elswork/samba:4.15.5")
.withTmpFs(Map.of(INNER_SHARE_DIR, "rw"))
.withCommand("-u", "1000:1000:" + USERNAME + ":" + USERNAME + ":" + PASSWORD, "-s", SHARE_AND_DIR + ":" + INNER_SHARE_DIR + ":rw:" + USERNAME)
.withExposedPorts(445);
private static SmbSessionFactory smbSessionFactory;
@BeforeAll
public static void connectToSMBServer() {
public static void connectToSMBServer() throws IOException {
SMB_CONTAINER.start();
smbSessionFactory = new SmbSessionFactory();
smbSessionFactory.setHost(HOST);
smbSessionFactory.setPort(SMB_CONTAINER.getFirstMappedPort());
smbSessionFactory.setUsername(USERNAME);
smbSessionFactory.setPassword(PASSWORD);
smbSessionFactory.setShareAndDir(SHARE_AND_DIR);
smbSessionFactory.setShareAndDir(SHARE_AND_DIR + "/");
smbSessionFactory.setSmbMinVersion(DialectVersion.SMB210);
smbSessionFactory.setSmbMaxVersion(DialectVersion.SMB311);
try (Session<SmbFile> smbFileSession = smbSessionFactory.getSession()) {
smbFileSession.mkdir("smbTarget");
Charset charset = StandardCharsets.UTF_8;
smbFileSession.write(IOUtils.toInputStream("source1", charset), TestUtils.applySystemFileSeparator("smbSource/smbSource1.txt"));
smbFileSession.write(IOUtils.toInputStream("source2", charset), TestUtils.applySystemFileSeparator("smbSource/smbSource2.txt"));
smbFileSession.write(IOUtils.toInputStream("", charset), "SMBSOURCE1.TXT.a");
smbFileSession.write(IOUtils.toInputStream("", charset), "SMBSOURCE2.TXT.a");
smbFileSession.write(IOUtils.toInputStream("subSource1", charset), TestUtils.applySystemFileSeparator("smbSource/subSmbSource/subSmbSource1.txt"));
smbFileSession.write(IOUtils.toInputStream("subSource2", charset), TestUtils.applySystemFileSeparator("smbSource/subSmbSource/subSmbSource2.txt"));
}
}
public static SessionFactory<SmbFile> sessionFactory() {

View File

@@ -29,7 +29,6 @@ import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -38,8 +37,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.integration.IntegrationMessageHeaderAccessor;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.config.EnableIntegrationManagement;
import org.springframework.integration.config.IntegrationManagementConfigurer;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.Pollers;
@@ -69,39 +66,19 @@ import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileInputStream;
/**
* The actual SMB share must be configured in class 'SmbTestSupport'
* with the 'real' server settings for testing.
*
* You must create the following folder structures in your SMB share
* for a successful completion of these unit tests:
*
* <pre class="code">
* smbSource/
* |-- smbSource1.txt - contains 'source1'
* |-- smbSource2.txt - contains 'source2'
* |-- SMBSOURCE1.TXT.a
* |-- SMBSOURCE2.TXT.a
* |-- subSmbSource/
* |-- subSmbSource1.txt - contains 'subSource1'
* |-- subSmbSource2.txt - contains 'subSource2'
* |-- subSmbSource2/ - directory will be created in testSmbPutFlow
* |-- subSmbSource2-1.txt - file will be created in testSmbPutFlow and deleted in testSmbRmFlow
* |-- subSmbSource2-2.txt - file will be created in testSmbMputFlow
* |-- subSmbSource2-3.txt - file will be created in testSmbMputFlow and renamed in testSmbMvFlow to subSmbSource-MV-Flow-Renamed.txt
* smbTarget/
* </pre>
*
* The intent is tests retrieve from smbSource and verify arrival in localTarget or
* send from localSource and verify arrival in remoteTarget.
*
* @author Gregory Bragg
* @author Artem Vozhdayenko
* @author Artem Bilan
*
* @since 6.0
*/
@SpringJUnitConfig
@DirtiesContext
@Disabled("Actual SMB share must be configured in class [SmbTestSupport].")
public class SmbTests extends SmbTestSupport {
@Autowired
@@ -110,22 +87,19 @@ public class SmbTests extends SmbTestSupport {
@Autowired
private ApplicationContext context;
@Autowired
private IntegrationManagementConfigurer integrationManagementConfigurer;
@Test
public void testSmbInboundFlow() throws IOException {
public void testSmbInboundFlow() {
QueueChannel out = new QueueChannel();
DirectoryScanner scanner = new DefaultDirectoryScanner();
IntegrationFlow flow = IntegrationFlow.from(Smb.inboundAdapter(sessionFactory())
.preserveTimestamp(true)
.remoteDirectory("smbSource")
.maxFetchSize(10)
.scanner(scanner)
.regexFilter(".*\\.txt$")
.localFilename(f -> f.toUpperCase() + ".a")
.localDirectory(getTargetLocalDirectory()),
e -> e.id("smbInboundAdapter").poller(Pollers.fixedDelay(100)))
.preserveTimestamp(true)
.remoteDirectory("smbSource")
.maxFetchSize(10)
.scanner(scanner)
.regexFilter(".*\\.txt$")
.localFilename(f -> f.toUpperCase() + ".a")
.localDirectory(getTargetLocalDirectory()),
e -> e.id("smbInboundAdapter").poller(Pollers.fixedDelay(100)))
.channel(out)
.get();
IntegrationFlowRegistration registration = this.flowContext.registration(flow).register();
@@ -163,11 +137,11 @@ public class SmbTests extends SmbTestSupport {
public void testSmbInboundStreamFlow() throws Exception {
QueueChannel out = new QueueChannel();
StandardIntegrationFlow flow = IntegrationFlow.from(
Smb.inboundStreamingAdapter(new SmbRemoteFileTemplate(sessionFactory()))
.remoteDirectory("smbSource")
.maxFetchSize(11)
.regexFilter(".*\\.txt$"),
e -> e.id("smbInboundAdapter").poller(Pollers.fixedDelay(100)))
Smb.inboundStreamingAdapter(new SmbRemoteFileTemplate(sessionFactory()))
.remoteDirectory("smbSource")
.maxFetchSize(11)
.regexFilter(".*\\.txt$"),
e -> e.id("smbInboundAdapter").poller(Pollers.fixedDelay(100)))
.channel(out)
.get();
IntegrationFlowRegistration registration = this.flowContext.registration(flow).register();
@@ -207,7 +181,7 @@ public class SmbTests extends SmbTestSupport {
RemoteFileTemplate<SmbFile> template = new RemoteFileTemplate<>(sessionFactory());
SmbFile[] files = template.execute(session ->
session.list(getTargetRemoteDirectory().getName()));
assertThat(files.length).isEqualTo(1);
assertThat(files).hasSize(1);
try {
assertThat(files[0].length()).isEqualTo(3);
}
@@ -235,7 +209,7 @@ public class SmbTests extends SmbTestSupport {
registration.getInputChannel().send(message);
SmbFile[] files = smbTemplate.execute(session ->
session.list(getTargetRemoteDirectory().getName()));
assertThat(files.length).isEqualTo(1);
assertThat(files).hasSize(1);
try {
assertThat(files[0].length()).isEqualTo(3);
}
@@ -268,7 +242,7 @@ public class SmbTests extends SmbTestSupport {
registration.getInputChannel().send(message2);
SmbFile[] files = smbTemplate.execute(session ->
session.list(getTargetRemoteDirectory().getName()));
assertThat(files.length).isEqualTo(1);
assertThat(files).hasSize(1);
try {
assertThat(files[0].length()).isEqualTo(9);
}
@@ -331,7 +305,7 @@ public class SmbTests extends SmbTestSupport {
assertThat(result).isNotNull();
List<File> localFiles = (List<File>) result.getPayload();
assertThat(localFiles.size()).as("unexpected local files " + localFiles).isEqualTo(2);
assertThat(localFiles).as("unexpected local files " + localFiles).hasSize(2);
for (File file : localFiles) {
assertThat(file.getPath().replaceAll(Matcher.quoteReplacement(File.separator), "/")).contains(dir);
@@ -365,7 +339,7 @@ public class SmbTests extends SmbTestSupport {
assertThat(result).isNotNull();
List<SmbFileInfo> localFiles = (List<SmbFileInfo>) result.getPayload();
assertThat(localFiles.size()).as("unexpected local files " + localFiles).isEqualTo(2);
assertThat(localFiles).as("unexpected local files " + localFiles).hasSize(2);
for (SmbFileInfo fileInfo : localFiles) {
SmbFile file = fileInfo.getFileInfo();
@@ -400,10 +374,10 @@ public class SmbTests extends SmbTestSupport {
assertThat(result).isNotNull();
List<String> localFilenames = (List<String>) result.getPayload();
assertThat(localFilenames.size()).as("unexpected local filenames " + localFilenames).isEqualTo(2);
assertThat(localFilenames).as("unexpected local filenames " + localFilenames).hasSize(2);
for (String filename : localFilenames) {
assertThat(filename.contains("subSmbSource"));
assertThat(filename).contains("subSmbSource");
}
registration.destroy();
@@ -431,8 +405,9 @@ public class SmbTests extends SmbTestSupport {
assertThat(result).isNotNull();
String path = (String) result.getPayload();
assertThat(path).isNotNull();
assertThat(path.contains("subSmbSource2"));
assertThat(path)
.isNotNull()
.contains("subSmbSource2");
registration.destroy();
}
@@ -450,8 +425,7 @@ public class SmbTests extends SmbTestSupport {
Message<?> result = out.receive(10_000);
assertThat(result).isNotNull();
Boolean success = (Boolean) result.getPayload();
assertThat(success).isTrue();
assertThat(result.getPayload()).isEqualTo(Boolean.TRUE);
registration.destroy();
}
@@ -488,18 +462,19 @@ public class SmbTests extends SmbTestSupport {
assertThat(result).isNotNull();
List<String> remoteFilenames = (List<String>) result.getPayload();
assertThat(remoteFilenames).isNotNull();
assertThat(remoteFilenames.size()).as("unexpected remote filenames " + remoteFilenames).isEqualTo(2);
assertThat(remoteFilenames)
.isNotNull()
.as("unexpected remote filenames " + remoteFilenames).hasSize(2);
for (String filename : remoteFilenames) {
assertThat(filename.contains("subSmbSource2"));
assertThat(filename).contains("subSmbSource2");
}
registration.destroy();
}
@Test
public void testSmbMvFlow() throws IOException {
public void testSmbMvFlow() {
QueueChannel out = new QueueChannel();
IntegrationFlow flow = f -> f
.handle(
@@ -512,15 +487,13 @@ public class SmbTests extends SmbTestSupport {
Message<?> result = out.receive(10_000);
assertThat(result).isNotNull();
Boolean success = (Boolean) result.getPayload();
assertThat(success).isTrue();
assertThat(result.getPayload()).isEqualTo(Boolean.TRUE);
registration.destroy();
}
@Configuration
@EnableIntegration
@EnableIntegrationManagement
public static class ContextConfiguration {
}

View File

@@ -7,7 +7,7 @@
</Appenders>
<Loggers>
<Logger name="org.springframework.integration" level="warn"/>
<Logger name="org.springframework.integration.smb" level="info"/>
<Logger name="org.springframework.integration.smb" level="warn"/>
<Root level="warn">
<AppenderRef ref="STDOUT" />
</Root>