Skip to content

Commit f349e1a

Browse files
oxcafedeadartembilan
authored andcommitted
GH-3804: Migrate SMB unit tests to Testcontainers
Fixes #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
1 parent b074f19 commit f349e1a

File tree

3 files changed

+92
-69
lines changed

3 files changed

+92
-69
lines changed

spring-integration-smb/src/test/java/org/springframework/integration/smb/SmbTestSupport.java

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,49 +16,99 @@
1616

1717
package org.springframework.integration.smb;
1818

19+
import java.io.IOException;
20+
import java.nio.charset.Charset;
21+
import java.nio.charset.StandardCharsets;
22+
import java.util.Map;
23+
24+
import org.apache.commons.io.IOUtils;
1925
import org.junit.jupiter.api.BeforeAll;
20-
import org.junit.jupiter.api.Disabled;
26+
import org.testcontainers.containers.GenericContainer;
27+
import org.testcontainers.junit.jupiter.Testcontainers;
2128

2229
import org.springframework.integration.file.remote.RemoteFileTestSupport;
2330
import org.springframework.integration.file.remote.session.CachingSessionFactory;
31+
import org.springframework.integration.file.remote.session.Session;
2432
import org.springframework.integration.file.remote.session.SessionFactory;
2533
import org.springframework.integration.smb.session.SmbSessionFactory;
34+
import org.springframework.integration.test.util.TestUtils;
2635

2736
import jcifs.DialectVersion;
2837
import jcifs.smb.SmbFile;
2938

3039
/**
31-
* Provides a connection to an external SMB Server for test cases.
40+
* Provides a connection to a Testcontainers-driven SMB Server for test cases.
41+
*
42+
* The following folder structures in the SMB share is expected
43+
* for a successful completion of these unit tests:
3244
*
33-
* The constants need to be updated with the 'real' server settings for testing.
45+
* <pre class="code">
46+
* smbSource/
47+
* |-- smbSource1.txt - contains 'source1'
48+
* |-- smbSource2.txt - contains 'source2'
49+
* |-- SMBSOURCE1.TXT.a
50+
* |-- SMBSOURCE2.TXT.a
51+
* |-- subSmbSource/
52+
* |-- subSmbSource1.txt - contains 'subSource1'
53+
* |-- subSmbSource2.txt - contains 'subSource2'
54+
* |-- subSmbSource2/ - directory will be created in testSmbPutFlow
55+
* |-- subSmbSource2-1.txt - file will be created in testSmbPutFlow and deleted in testSmbRmFlow
56+
* |-- subSmbSource2-2.txt - file will be created in testSmbMputFlow
57+
* |-- subSmbSource2-3.txt - file will be created in testSmbMputFlow and renamed in testSmbMvFlow to subSmbSource-MV-Flow-Renamed.txt
58+
* smbTarget/
59+
* </pre>
3460
*
3561
* @author Gregory Bragg
62+
* @author Artem Vozhdayenko
63+
* @author Artem Bilan
3664
*
3765
* @since 6.0
3866
*/
3967

40-
@Disabled("Actual SMB share must be configured in class [SmbTestSupport].")
68+
@Testcontainers(disabledWithoutDocker = true)
4169
public class SmbTestSupport extends RemoteFileTestSupport {
4270

43-
public static final String HOST = "localhost";
71+
public static final String HOST = "127.0.0.1";
4472

45-
public static final String SHARE_AND_DIR = "smb-share/";
73+
public static final String SHARE_AND_DIR = "smb-share";
4674

4775
public static final String USERNAME = "sambaguest";
4876

4977
public static final String PASSWORD = "sambaguest";
5078

79+
private static final String INNER_SHARE_DIR = "/tmp";
80+
81+
private static final GenericContainer<?> SMB_CONTAINER = new GenericContainer<>("elswork/samba:4.15.5")
82+
.withTmpFs(Map.of(INNER_SHARE_DIR, "rw"))
83+
.withCommand("-u", "1000:1000:" + USERNAME + ":" + USERNAME + ":" + PASSWORD, "-s", SHARE_AND_DIR + ":" + INNER_SHARE_DIR + ":rw:" + USERNAME)
84+
.withExposedPorts(445);
85+
5186
private static SmbSessionFactory smbSessionFactory;
5287

5388
@BeforeAll
54-
public static void connectToSMBServer() {
89+
public static void connectToSMBServer() throws IOException {
90+
SMB_CONTAINER.start();
91+
5592
smbSessionFactory = new SmbSessionFactory();
5693
smbSessionFactory.setHost(HOST);
94+
smbSessionFactory.setPort(SMB_CONTAINER.getFirstMappedPort());
5795
smbSessionFactory.setUsername(USERNAME);
5896
smbSessionFactory.setPassword(PASSWORD);
59-
smbSessionFactory.setShareAndDir(SHARE_AND_DIR);
97+
smbSessionFactory.setShareAndDir(SHARE_AND_DIR + "/");
6098
smbSessionFactory.setSmbMinVersion(DialectVersion.SMB210);
6199
smbSessionFactory.setSmbMaxVersion(DialectVersion.SMB311);
100+
101+
try (Session<SmbFile> smbFileSession = smbSessionFactory.getSession()) {
102+
smbFileSession.mkdir("smbTarget");
103+
Charset charset = StandardCharsets.UTF_8;
104+
smbFileSession.write(IOUtils.toInputStream("source1", charset), TestUtils.applySystemFileSeparator("smbSource/smbSource1.txt"));
105+
smbFileSession.write(IOUtils.toInputStream("source2", charset), TestUtils.applySystemFileSeparator("smbSource/smbSource2.txt"));
106+
smbFileSession.write(IOUtils.toInputStream("", charset), "SMBSOURCE1.TXT.a");
107+
smbFileSession.write(IOUtils.toInputStream("", charset), "SMBSOURCE2.TXT.a");
108+
109+
smbFileSession.write(IOUtils.toInputStream("subSource1", charset), TestUtils.applySystemFileSeparator("smbSource/subSmbSource/subSmbSource1.txt"));
110+
smbFileSession.write(IOUtils.toInputStream("subSource2", charset), TestUtils.applySystemFileSeparator("smbSource/subSmbSource/subSmbSource2.txt"));
111+
}
62112
}
63113

64114
public static SessionFactory<SmbFile> sessionFactory() {

spring-integration-smb/src/test/java/org/springframework/integration/smb/dsl/SmbTests.java

Lines changed: 33 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import java.util.Map;
3030
import java.util.regex.Matcher;
3131

32-
import org.junit.jupiter.api.Disabled;
3332
import org.junit.jupiter.api.Test;
3433

3534
import org.springframework.beans.factory.annotation.Autowired;
@@ -38,8 +37,6 @@
3837
import org.springframework.integration.IntegrationMessageHeaderAccessor;
3938
import org.springframework.integration.channel.QueueChannel;
4039
import org.springframework.integration.config.EnableIntegration;
41-
import org.springframework.integration.config.EnableIntegrationManagement;
42-
import org.springframework.integration.config.IntegrationManagementConfigurer;
4340
import org.springframework.integration.core.MessageSource;
4441
import org.springframework.integration.dsl.IntegrationFlow;
4542
import org.springframework.integration.dsl.Pollers;
@@ -69,39 +66,19 @@
6966
import jcifs.smb.SmbFileInputStream;
7067

7168
/**
72-
* The actual SMB share must be configured in class 'SmbTestSupport'
73-
* with the 'real' server settings for testing.
74-
*
75-
* You must create the following folder structures in your SMB share
76-
* for a successful completion of these unit tests:
77-
*
78-
* <pre class="code">
79-
* smbSource/
80-
* |-- smbSource1.txt - contains 'source1'
81-
* |-- smbSource2.txt - contains 'source2'
82-
* |-- SMBSOURCE1.TXT.a
83-
* |-- SMBSOURCE2.TXT.a
84-
* |-- subSmbSource/
85-
* |-- subSmbSource1.txt - contains 'subSource1'
86-
* |-- subSmbSource2.txt - contains 'subSource2'
87-
* |-- subSmbSource2/ - directory will be created in testSmbPutFlow
88-
* |-- subSmbSource2-1.txt - file will be created in testSmbPutFlow and deleted in testSmbRmFlow
89-
* |-- subSmbSource2-2.txt - file will be created in testSmbMputFlow
90-
* |-- subSmbSource2-3.txt - file will be created in testSmbMputFlow and renamed in testSmbMvFlow to subSmbSource-MV-Flow-Renamed.txt
91-
* smbTarget/
92-
* </pre>
9369
*
9470
* The intent is tests retrieve from smbSource and verify arrival in localTarget or
9571
* send from localSource and verify arrival in remoteTarget.
9672
*
9773
* @author Gregory Bragg
74+
* @author Artem Vozhdayenko
75+
* @author Artem Bilan
9876
*
9977
* @since 6.0
10078
*/
10179

10280
@SpringJUnitConfig
10381
@DirtiesContext
104-
@Disabled("Actual SMB share must be configured in class [SmbTestSupport].")
10582
public class SmbTests extends SmbTestSupport {
10683

10784
@Autowired
@@ -110,22 +87,19 @@ public class SmbTests extends SmbTestSupport {
11087
@Autowired
11188
private ApplicationContext context;
11289

113-
@Autowired
114-
private IntegrationManagementConfigurer integrationManagementConfigurer;
115-
11690
@Test
117-
public void testSmbInboundFlow() throws IOException {
91+
public void testSmbInboundFlow() {
11892
QueueChannel out = new QueueChannel();
11993
DirectoryScanner scanner = new DefaultDirectoryScanner();
12094
IntegrationFlow flow = IntegrationFlow.from(Smb.inboundAdapter(sessionFactory())
121-
.preserveTimestamp(true)
122-
.remoteDirectory("smbSource")
123-
.maxFetchSize(10)
124-
.scanner(scanner)
125-
.regexFilter(".*\\.txt$")
126-
.localFilename(f -> f.toUpperCase() + ".a")
127-
.localDirectory(getTargetLocalDirectory()),
128-
e -> e.id("smbInboundAdapter").poller(Pollers.fixedDelay(100)))
95+
.preserveTimestamp(true)
96+
.remoteDirectory("smbSource")
97+
.maxFetchSize(10)
98+
.scanner(scanner)
99+
.regexFilter(".*\\.txt$")
100+
.localFilename(f -> f.toUpperCase() + ".a")
101+
.localDirectory(getTargetLocalDirectory()),
102+
e -> e.id("smbInboundAdapter").poller(Pollers.fixedDelay(100)))
129103
.channel(out)
130104
.get();
131105
IntegrationFlowRegistration registration = this.flowContext.registration(flow).register();
@@ -163,11 +137,11 @@ public void testSmbInboundFlow() throws IOException {
163137
public void testSmbInboundStreamFlow() throws Exception {
164138
QueueChannel out = new QueueChannel();
165139
StandardIntegrationFlow flow = IntegrationFlow.from(
166-
Smb.inboundStreamingAdapter(new SmbRemoteFileTemplate(sessionFactory()))
167-
.remoteDirectory("smbSource")
168-
.maxFetchSize(11)
169-
.regexFilter(".*\\.txt$"),
170-
e -> e.id("smbInboundAdapter").poller(Pollers.fixedDelay(100)))
140+
Smb.inboundStreamingAdapter(new SmbRemoteFileTemplate(sessionFactory()))
141+
.remoteDirectory("smbSource")
142+
.maxFetchSize(11)
143+
.regexFilter(".*\\.txt$"),
144+
e -> e.id("smbInboundAdapter").poller(Pollers.fixedDelay(100)))
171145
.channel(out)
172146
.get();
173147
IntegrationFlowRegistration registration = this.flowContext.registration(flow).register();
@@ -207,7 +181,7 @@ public void testSmbOutboundFlow() {
207181
RemoteFileTemplate<SmbFile> template = new RemoteFileTemplate<>(sessionFactory());
208182
SmbFile[] files = template.execute(session ->
209183
session.list(getTargetRemoteDirectory().getName()));
210-
assertThat(files.length).isEqualTo(1);
184+
assertThat(files).hasSize(1);
211185
try {
212186
assertThat(files[0].length()).isEqualTo(3);
213187
}
@@ -235,7 +209,7 @@ public void testSmbOutboundFlowWithSmbRemoteTemplate() {
235209
registration.getInputChannel().send(message);
236210
SmbFile[] files = smbTemplate.execute(session ->
237211
session.list(getTargetRemoteDirectory().getName()));
238-
assertThat(files.length).isEqualTo(1);
212+
assertThat(files).hasSize(1);
239213
try {
240214
assertThat(files[0].length()).isEqualTo(3);
241215
}
@@ -268,7 +242,7 @@ public void testSmbOutboundFlowWithSmbRemoteTemplateAndMode() {
268242
registration.getInputChannel().send(message2);
269243
SmbFile[] files = smbTemplate.execute(session ->
270244
session.list(getTargetRemoteDirectory().getName()));
271-
assertThat(files.length).isEqualTo(1);
245+
assertThat(files).hasSize(1);
272246
try {
273247
assertThat(files[0].length()).isEqualTo(9);
274248
}
@@ -331,7 +305,7 @@ public void testSmbMgetFlow() {
331305
assertThat(result).isNotNull();
332306

333307
List<File> localFiles = (List<File>) result.getPayload();
334-
assertThat(localFiles.size()).as("unexpected local files " + localFiles).isEqualTo(2);
308+
assertThat(localFiles).as("unexpected local files " + localFiles).hasSize(2);
335309

336310
for (File file : localFiles) {
337311
assertThat(file.getPath().replaceAll(Matcher.quoteReplacement(File.separator), "/")).contains(dir);
@@ -365,7 +339,7 @@ public void testSmbLsFlow() {
365339
assertThat(result).isNotNull();
366340

367341
List<SmbFileInfo> localFiles = (List<SmbFileInfo>) result.getPayload();
368-
assertThat(localFiles.size()).as("unexpected local files " + localFiles).isEqualTo(2);
342+
assertThat(localFiles).as("unexpected local files " + localFiles).hasSize(2);
369343

370344
for (SmbFileInfo fileInfo : localFiles) {
371345
SmbFile file = fileInfo.getFileInfo();
@@ -400,10 +374,10 @@ public void testSmbNlstFlow() {
400374
assertThat(result).isNotNull();
401375

402376
List<String> localFilenames = (List<String>) result.getPayload();
403-
assertThat(localFilenames.size()).as("unexpected local filenames " + localFilenames).isEqualTo(2);
377+
assertThat(localFilenames).as("unexpected local filenames " + localFilenames).hasSize(2);
404378

405379
for (String filename : localFilenames) {
406-
assertThat(filename.contains("subSmbSource"));
380+
assertThat(filename).contains("subSmbSource");
407381
}
408382

409383
registration.destroy();
@@ -431,8 +405,9 @@ public void testSmbPutFlow() {
431405
assertThat(result).isNotNull();
432406

433407
String path = (String) result.getPayload();
434-
assertThat(path).isNotNull();
435-
assertThat(path.contains("subSmbSource2"));
408+
assertThat(path)
409+
.isNotNull()
410+
.contains("subSmbSource2");
436411

437412
registration.destroy();
438413
}
@@ -450,8 +425,7 @@ public void testSmbRmFlow() {
450425
Message<?> result = out.receive(10_000);
451426
assertThat(result).isNotNull();
452427

453-
Boolean success = (Boolean) result.getPayload();
454-
assertThat(success).isTrue();
428+
assertThat(result.getPayload()).isEqualTo(Boolean.TRUE);
455429

456430
registration.destroy();
457431
}
@@ -488,18 +462,19 @@ public void testSmbMputFlow() throws IOException {
488462
assertThat(result).isNotNull();
489463

490464
List<String> remoteFilenames = (List<String>) result.getPayload();
491-
assertThat(remoteFilenames).isNotNull();
492-
assertThat(remoteFilenames.size()).as("unexpected remote filenames " + remoteFilenames).isEqualTo(2);
465+
assertThat(remoteFilenames)
466+
.isNotNull()
467+
.as("unexpected remote filenames " + remoteFilenames).hasSize(2);
493468

494469
for (String filename : remoteFilenames) {
495-
assertThat(filename.contains("subSmbSource2"));
470+
assertThat(filename).contains("subSmbSource2");
496471
}
497472

498473
registration.destroy();
499474
}
500475

501476
@Test
502-
public void testSmbMvFlow() throws IOException {
477+
public void testSmbMvFlow() {
503478
QueueChannel out = new QueueChannel();
504479
IntegrationFlow flow = f -> f
505480
.handle(
@@ -512,15 +487,13 @@ public void testSmbMvFlow() throws IOException {
512487
Message<?> result = out.receive(10_000);
513488
assertThat(result).isNotNull();
514489

515-
Boolean success = (Boolean) result.getPayload();
516-
assertThat(success).isTrue();
490+
assertThat(result.getPayload()).isEqualTo(Boolean.TRUE);
517491

518492
registration.destroy();
519493
}
520494

521495
@Configuration
522496
@EnableIntegration
523-
@EnableIntegrationManagement
524497
public static class ContextConfiguration {
525498

526499
}

spring-integration-smb/src/test/resources/log4j2-test.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</Appenders>
88
<Loggers>
99
<Logger name="org.springframework.integration" level="warn"/>
10-
<Logger name="org.springframework.integration.smb" level="info"/>
10+
<Logger name="org.springframework.integration.smb" level="warn"/>
1111
<Root level="warn">
1212
<AppenderRef ref="STDOUT" />
1313
</Root>

0 commit comments

Comments
 (0)