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:
committed by
Artem Bilan
parent
b074f1961f
commit
f349e1a114
@@ -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() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user