Skip to content

Commit 37f42c5

Browse files
committed
Migrate to Testcontainers test (and support clamav 1.4 and 1.5)
1 parent 4ff23d3 commit 37f42c5

File tree

7 files changed

+240
-153
lines changed

7 files changed

+240
-153
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.idea
2+
*.iml
3+
target/

pom.xml

Lines changed: 93 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,104 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<project xmlns="http://maven.apache.org/POM/4.0.0"
3-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
55

6-
<modelVersion>4.0.0</modelVersion>
6+
<modelVersion>4.0.0</modelVersion>
77

8-
<parent>
9-
<groupId>org.sonatype.oss</groupId>
10-
<artifactId>oss-parent</artifactId>
11-
<version>7</version>
12-
</parent>
8+
<parent>
9+
<groupId>org.sonatype.oss</groupId>
10+
<artifactId>oss-parent</artifactId>
11+
<version>9</version>
12+
</parent>
1313

14-
<groupId>fi.solita.clamav</groupId>
15-
<artifactId>clamav-client</artifactId>
16-
<version>1.0.1</version>
17-
<packaging>jar</packaging>
18-
<name>Simple ClamAV client</name>
19-
<description>Simple Java client for using clamd INSTREAM scanning in your application.</description>
20-
<url>https://github.com/solita/clamav-java</url>
21-
<licenses>
22-
<license>
23-
<name>GNU LESSER GENERAL PUBLIC LICENSE, Version 2.1</name>
24-
<url>http://www.gnu.org/licenses/lgpl.txt</url>
25-
</license>
26-
</licenses>
27-
<developers>
28-
<developer>
29-
<name>Antti Virtanen</name>
30-
<email>antti.virtanen@solita.fi</email>
31-
<organization>Solita</organization>
32-
<organizationUrl>http://www.solita.fi</organizationUrl>
33-
</developer>
34-
</developers>
14+
<groupId>fi.solita.clamav</groupId>
15+
<artifactId>clamav-client</artifactId>
16+
<version>1.0.1</version>
17+
<packaging>jar</packaging>
18+
<name>Simple ClamAV client</name>
19+
<description>Simple Java client for using clamd INSTREAM scanning in your application.</description>
20+
<url>https://github.com/solita/clamav-java</url>
21+
<licenses>
22+
<license>
23+
<name>GNU LESSER GENERAL PUBLIC LICENSE, Version 2.1</name>
24+
<url>http://www.gnu.org/licenses/lgpl.txt</url>
25+
</license>
26+
</licenses>
27+
<developers>
28+
<developer>
29+
<name>Antti Virtanen</name>
30+
<email>antti.virtanen@solita.fi</email>
31+
<organization>Solita</organization>
32+
<organizationUrl>http://www.solita.fi</organizationUrl>
33+
</developer>
34+
</developers>
3535

36-
<properties>
37-
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
38-
</properties>
36+
<properties>
37+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
38+
</properties>
3939

40-
<scm>
41-
<connection>scm:git:git://github.com/solita/clamav-java.git</connection>
42-
<developerConnection>scm:git:git@github.com:solita/clamav-java.git</developerConnection>
43-
<url>https://github.com/solita/clamav-java</url>
44-
</scm>
40+
<scm>
41+
<connection>scm:git:git://github.com/solita/clamav-java.git</connection>
42+
<developerConnection>scm:git:git@github.com:solita/clamav-java.git</developerConnection>
43+
<url>https://github.com/solita/clamav-java</url>
44+
</scm>
4545

46-
<dependencies>
47-
<dependency>
48-
<groupId>junit</groupId>
49-
<artifactId>junit</artifactId>
50-
<version>4.11</version>
51-
<scope>test</scope>
52-
</dependency>
53-
</dependencies>
46+
<dependencies>
47+
<dependency>
48+
<groupId>org.junit.jupiter</groupId>
49+
<artifactId>junit-jupiter</artifactId>
50+
<version>5.14.0</version>
51+
<scope>test</scope>
52+
</dependency>
53+
<dependency>
54+
<groupId>org.testcontainers</groupId>
55+
<artifactId>testcontainers</artifactId>
56+
<version>1.21.3</version>
57+
<scope>test</scope>
58+
</dependency>
59+
<dependency>
60+
<groupId>org.testcontainers</groupId>
61+
<artifactId>junit-jupiter</artifactId>
62+
<version>1.21.3</version>
63+
<scope>test</scope>
64+
</dependency>
65+
<dependency>
66+
<groupId>org.slf4j</groupId>
67+
<artifactId>slf4j-simple</artifactId>
68+
<version>2.0.17</version>
69+
<scope>test</scope>
70+
</dependency>
71+
<dependency>
72+
<groupId>org.slf4j</groupId>
73+
<artifactId>slf4j-api</artifactId>
74+
<version>2.0.17</version>
75+
<scope>test</scope>
76+
</dependency>
77+
</dependencies>
5478

55-
<build>
56-
<plugins>
57-
<plugin>
58-
<artifactId>maven-compiler-plugin</artifactId>
59-
<version>2.3.2</version>
60-
<configuration>
61-
<source>1.7</source>
62-
<target>1.7</target>
63-
</configuration>
64-
</plugin>
79+
<build>
80+
<plugins>
81+
<plugin>
82+
<artifactId>maven-compiler-plugin</artifactId>
83+
<version>3.14.1</version>
84+
<configuration>
85+
<source>11</source>
86+
<target>11</target>
87+
</configuration>
88+
</plugin>
6589

66-
<plugin>
67-
<groupId>org.sonatype.plugins</groupId>
68-
<artifactId>nexus-staging-maven-plugin</artifactId>
69-
<version>1.6.3</version>
70-
<extensions>true</extensions>
71-
<configuration>
72-
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
73-
<serverId>ossrh-releases-fi.solita</serverId>
74-
<stagingProfileId>ff5044adfb72</stagingProfileId>
75-
</configuration>
76-
</plugin>
90+
<plugin>
91+
<groupId>org.sonatype.plugins</groupId>
92+
<artifactId>nexus-staging-maven-plugin</artifactId>
93+
<version>1.6.3</version>
94+
<extensions>true</extensions>
95+
<configuration>
96+
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
97+
<serverId>ossrh-releases-fi.solita</serverId>
98+
<stagingProfileId>ff5044adfb72</stagingProfileId>
99+
</configuration>
100+
</plugin>
77101

78-
</plugins>
79-
</build>
102+
</plugins>
103+
</build>
80104
</project>

src/main/java/fi/solita/clamav/ClamAVClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public static boolean isCleanReply(byte[] reply) {
139139
private byte[] assertSizeLimit(byte[] reply) {
140140
String r = new String(reply, StandardCharsets.US_ASCII);
141141
if (r.startsWith("INSTREAM size limit exceeded."))
142-
throw new ClamAVSizeLimitException("Clamd size limit exceeded. Full reply from server: " + r);
142+
throw new ClamAVSizeLimitException("Clamd size limit exceeded. Full reply from server: " + r.trim());
143143
return reply;
144144
}
145145

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package fi.solita.clamav;
2+
3+
import org.junit.jupiter.api.AfterAll;
4+
import org.junit.jupiter.api.BeforeAll;
5+
import org.junit.jupiter.params.provider.Arguments;
6+
import org.testcontainers.containers.GenericContainer;
7+
import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy;
8+
import org.testcontainers.utility.DockerImageName;
9+
10+
import java.util.stream.Stream;
11+
12+
public abstract class BaseTestcontainers {
13+
public static final ClamavContainer clamav14 = new ClamavContainer("clamav/clamav:1.4");
14+
public static final ClamavContainer clamav15 = new ClamavContainer("clamav/clamav:1.5");
15+
16+
@BeforeAll
17+
static void beforeAll() {
18+
clamav14.start();
19+
clamav15.start();
20+
}
21+
22+
@AfterAll
23+
static void afterAll() {
24+
clamav15.stop();
25+
clamav14.stop();
26+
}
27+
28+
private static Stream<Arguments> provideClamdContainers() {
29+
// INFO: We have to wrap GenericContainer info Supplier wrapper to force JUnit not to close AutoClosable Test method parameter
30+
return Stream.of(
31+
Arguments.of("1.4", clamav14),
32+
Arguments.of("1.5", clamav15)
33+
);
34+
}
35+
36+
protected static ClamAVClient client(GenericContainer<?> container) {
37+
return new ClamAVClient("localhost", container.getFirstMappedPort());
38+
}
39+
}
40+
41+
class ClamavContainer extends GenericContainer<ClamavContainer> {
42+
public static final int DEFAULT_PORT = 3310;
43+
44+
ClamavContainer(String image) {
45+
super(DockerImageName.parse(image));
46+
this.waitingFor(new HostPortWaitStrategy().forPorts(3310));
47+
this.withExposedPorts(3310);
48+
}
49+
}
Lines changed: 62 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,72 @@
11
package fi.solita.clamav;
22

3-
import static org.junit.Assert.assertFalse;
4-
import static org.junit.Assert.assertTrue;
3+
import org.junit.jupiter.api.Assertions;
4+
import org.junit.jupiter.api.Disabled;
5+
import org.junit.jupiter.params.ParameterizedTest;
6+
import org.junit.jupiter.params.provider.MethodSource;
7+
import org.testcontainers.containers.GenericContainer;
58

69
import java.io.IOException;
7-
import java.io.InputStream;
8-
import java.net.UnknownHostException;
10+
import java.nio.charset.StandardCharsets;
911

10-
import org.junit.Test;
12+
import static org.junit.jupiter.api.Assertions.assertEquals;
13+
import static org.junit.jupiter.api.Assertions.assertFalse;
14+
import static org.junit.jupiter.api.Assertions.assertTrue;
1115

12-
/**
13-
* These tests assume clamd is running and responding in the virtual machine.
14-
*/
15-
public class InstreamTest {
16+
public class InstreamTest extends BaseTestcontainers {
17+
@ParameterizedTest(autoCloseArguments = false)
18+
@MethodSource("provideClamdContainers")
19+
public void testRandomBytes(String clamdVersion, GenericContainer<?> container) throws IOException {
20+
byte[] r = client(container).scan("alsdklaksdla".getBytes(StandardCharsets.US_ASCII));
21+
assertTrue(ClamAVClient.isCleanReply(r));
22+
}
1623

17-
private static String CLAMAV_HOST = "localhost";
18-
19-
private byte[] scan(byte[] input) throws UnknownHostException, IOException {
20-
ClamAVClient cl = new ClamAVClient(CLAMAV_HOST, 3310);
21-
return cl.scan(input);
22-
}
23-
24-
private byte[] scan(InputStream input) throws UnknownHostException, IOException {
25-
ClamAVClient cl = new ClamAVClient(CLAMAV_HOST, 3310);
26-
return cl.scan(input);
27-
}
28-
@Test
29-
public void testRandomBytes() throws UnknownHostException, IOException {
30-
byte[] r = scan("alsdklaksdla".getBytes("ASCII"));
31-
assertTrue(ClamAVClient.isCleanReply(r));
32-
}
33-
34-
@Test
35-
public void testPositive() throws UnknownHostException, IOException {
36-
// http://www.eicar.org/86-0-Intended-use.html
37-
byte[] EICAR = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*".getBytes("ASCII");
38-
byte[] r = scan(EICAR);
39-
assertFalse(ClamAVClient.isCleanReply(r));
40-
}
24+
@ParameterizedTest(autoCloseArguments = false)
25+
@MethodSource("provideClamdContainers")
26+
public void testPositive(String clamdVersion, GenericContainer<?> container) throws IOException {
27+
// http://www.eicar.org/86-0-Intended-use.html
28+
byte[] EICAR = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*".getBytes(StandardCharsets.US_ASCII);
29+
byte[] r = client(container).scan(EICAR);
30+
assertFalse(ClamAVClient.isCleanReply(r));
31+
}
4132

42-
@Test
43-
public void testStreamChunkingWorks() throws UnknownHostException, IOException {
44-
byte[] multipleChunks = new byte[50000];
45-
byte[] r = scan(multipleChunks);
46-
assertTrue(ClamAVClient.isCleanReply(r));
47-
}
48-
49-
@Test
50-
public void testChunkLimit() throws UnknownHostException, IOException {
51-
byte[] maximumChunk = new byte[2048];
52-
byte[] r = scan(maximumChunk);
53-
assertTrue(ClamAVClient.isCleanReply(r));
54-
}
55-
56-
@Test
57-
public void testZeroBytes() throws UnknownHostException, IOException {
58-
byte[] r = scan(new byte[]{});
59-
assertTrue(ClamAVClient.isCleanReply(r));
60-
}
33+
@ParameterizedTest(autoCloseArguments = false)
34+
@MethodSource("provideClamdContainers")
35+
public void testStreamChunkingWorks(String clamdVersion, GenericContainer<?> container) throws IOException {
36+
byte[] multipleChunks = new byte[50000];
37+
byte[] r = client(container).scan(multipleChunks);
38+
assertTrue(ClamAVClient.isCleanReply(r));
39+
}
6140

62-
@Test(expected = ClamAVSizeLimitException.class)
63-
public void testSizeLimit() throws UnknownHostException, IOException {
64-
scan(new SlowInputStream());
65-
}
41+
@ParameterizedTest(autoCloseArguments = false)
42+
@MethodSource("provideClamdContainers")
43+
public void testChunkLimit(String clamdVersion, GenericContainer<?> container) throws IOException {
44+
byte[] maximumChunk = new byte[2048];
45+
byte[] r = client(container).scan(maximumChunk);
46+
assertTrue(ClamAVClient.isCleanReply(r));
47+
}
48+
49+
@ParameterizedTest(autoCloseArguments = false)
50+
@MethodSource("provideClamdContainers")
51+
public void testZeroBytes(String clamdVersion, GenericContainer<?> container) throws IOException {
52+
byte[] r = client(container).scan(new byte[]{});
53+
assertTrue(ClamAVClient.isCleanReply(r));
54+
}
55+
56+
@ParameterizedTest(autoCloseArguments = false)
57+
@MethodSource("provideClamdContainers")
58+
public void testSizeLimit(String clamdVersion, GenericContainer<?> container) {
59+
ClamAVSizeLimitException exception = Assertions.assertThrows(ClamAVSizeLimitException.class,
60+
() -> client(container).scan(new SlowInputStream(false)));
61+
assertEquals("Clamd size limit exceeded. Full reply from server: INSTREAM size limit exceeded. ERROR", exception.getMessage());
62+
}
63+
64+
@Disabled
65+
@ParameterizedTest(autoCloseArguments = false)
66+
@MethodSource("provideClamdContainers")
67+
public void testSizeLimitSuperSlow(String clamdVersion, GenericContainer<?> container) {
68+
ClamAVSizeLimitException exception = Assertions.assertThrows(ClamAVSizeLimitException.class,
69+
() -> client(container).scan(new SlowInputStream(true)));
70+
assertEquals("Clamd size limit exceeded. Full reply from server: INSTREAM size limit exceeded. ERROR", exception.getMessage());
71+
}
6672
}
Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
package fi.solita.clamav;
22

3-
import static org.junit.Assert.assertTrue;
3+
import org.junit.jupiter.params.ParameterizedTest;
4+
import org.junit.jupiter.params.provider.MethodSource;
5+
import org.testcontainers.containers.GenericContainer;
46

57
import java.io.IOException;
6-
import java.net.UnknownHostException;
78

8-
import org.junit.Test;
9+
import static org.junit.jupiter.api.Assertions.assertTrue;
910

10-
/**
11-
* These tests assume clamd is running and responding in the virtual machine.
12-
*/
13-
public class PingTest {
14-
15-
@Test
16-
public void testPingPong() throws UnknownHostException, IOException {
17-
ClamAVClient cl = new ClamAVClient("localhost", 3310);
18-
assertTrue(cl.ping());
19-
}
11+
public class PingTest extends BaseTestcontainers {
12+
@ParameterizedTest(autoCloseArguments = false)
13+
@MethodSource("provideClamdContainers")
14+
public void testPingPong(String clamdVersion, GenericContainer<?> container) throws IOException {
15+
ClamAVClient client = client(container);
16+
assertTrue(client.ping());
17+
}
2018
}

0 commit comments

Comments
 (0)