Skip to content

Commit 689f6ea

Browse files
authored
Merge pull request #46 from dalelane/builder-tests
test: add tests for MessageBuilder and sink task classes
2 parents 16263a9 + 93700b9 commit 689f6ea

File tree

9 files changed

+1269
-1
lines changed

9 files changed

+1269
-1
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ Change directory into the `kafka-connect-mq-sink` directory:
3737
cd kafka-connect-mq-sink
3838
```
3939

40+
Run the unit tests:
41+
```shell
42+
mvn test
43+
```
44+
45+
Run the integration tests (requires Docker):
46+
```shell
47+
mvn integration-test
48+
```
49+
4050
Build the connector using Maven:
4151
```shell
4252
mvn clean package

pom.xml

Lines changed: 140 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,15 @@
7878
<version>1.7.25</version>
7979
<scope>test</scope>
8080
</dependency>
81+
82+
<!-- tests in src/integration depend on a running MQ queue manager -->
83+
<!-- in a container, configured using org.testcontainers -->
84+
<dependency>
85+
<groupId>org.testcontainers</groupId>
86+
<artifactId>testcontainers</artifactId>
87+
<version>1.17.2</version>
88+
<scope>test</scope>
89+
</dependency>
8190
</dependencies>
8291

8392
<build>
@@ -90,15 +99,41 @@
9099
<target>1.8</target>
91100
</configuration>
92101
</plugin>
102+
103+
<!-- run unit tests -->
93104
<plugin>
94105
<artifactId>maven-surefire-plugin</artifactId>
95-
<version>3.0.0-M1</version>
106+
<version>3.0.0-M7</version>
96107
<configuration>
108+
<argLine>${surefire.jacoco.args}</argLine>
97109
<systemPropertyVariables>
98110
<connectorVersion>${project.version}</connectorVersion>
99111
</systemPropertyVariables>
100112
</configuration>
101113
</plugin>
114+
115+
<!-- run integration tests -->
116+
<plugin>
117+
<artifactId>maven-failsafe-plugin</artifactId>
118+
<version>3.0.0-M7</version>
119+
<configuration>
120+
<argLine>${failsafe.jacoco.args}</argLine>
121+
<systemPropertyVariables>
122+
<connectorVersion>${project.version}</connectorVersion>
123+
</systemPropertyVariables>
124+
</configuration>
125+
<executions>
126+
<execution>
127+
<id>integration-tests</id>
128+
<goals>
129+
<goal>integration-test</goal>
130+
<goal>verify</goal>
131+
</goals>
132+
</execution>
133+
</executions>
134+
</plugin>
135+
136+
<!-- build the release jar -->
102137
<plugin>
103138
<artifactId>maven-assembly-plugin</artifactId>
104139
<version>3.1.1</version>
@@ -116,6 +151,110 @@
116151
</descriptors>
117152
</configuration>
118153
</plugin>
154+
155+
<!-- add the src/integration folder as a test folder, which lets us keep -->
156+
<!-- tests that have a dependency on testcontainers separate from pure -->
157+
<!-- unit tests with no external dependency -->
158+
<plugin>
159+
<groupId>org.codehaus.mojo</groupId>
160+
<artifactId>build-helper-maven-plugin</artifactId>
161+
<version>3.3.0</version>
162+
<executions>
163+
<execution>
164+
<id>add-test-source</id>
165+
<phase>process-test-sources</phase>
166+
<goals>
167+
<goal>add-test-source</goal>
168+
</goals>
169+
<configuration>
170+
<sources>
171+
<source>src/integration/java</source>
172+
</sources>
173+
</configuration>
174+
</execution>
175+
</executions>
176+
</plugin>
177+
178+
<!-- generate test code coverage report -->
179+
<plugin>
180+
<groupId>org.jacoco</groupId>
181+
<artifactId>jacoco-maven-plugin</artifactId>
182+
<version>0.8.8</version>
183+
<executions>
184+
<execution>
185+
<id>before-unit-test-execution</id>
186+
<goals>
187+
<goal>prepare-agent</goal>
188+
</goals>
189+
<configuration>
190+
<destFile>${project.build.directory}/jacoco-output/jacoco-unit-tests.exec</destFile>
191+
<propertyName>surefire.jacoco.args</propertyName>
192+
</configuration>
193+
</execution>
194+
<execution>
195+
<id>after-unit-test-execution</id>
196+
<phase>test</phase>
197+
<goals>
198+
<goal>report</goal>
199+
</goals>
200+
<configuration>
201+
<dataFile>${project.build.directory}/jacoco-output/jacoco-unit-tests.exec</dataFile>
202+
<outputDirectory>${project.reporting.outputDirectory}/jacoco-unit-test-coverage-report</outputDirectory>
203+
</configuration>
204+
</execution>
205+
<execution>
206+
<id>before-integration-test-execution</id>
207+
<phase>pre-integration-test</phase>
208+
<goals>
209+
<goal>prepare-agent</goal>
210+
</goals>
211+
<configuration>
212+
<destFile>${project.build.directory}/jacoco-output/jacoco-integration-tests.exec</destFile>
213+
<propertyName>failsafe.jacoco.args</propertyName>
214+
</configuration>
215+
</execution>
216+
<execution>
217+
<id>after-integration-test-execution</id>
218+
<phase>post-integration-test</phase>
219+
<goals>
220+
<goal>report</goal>
221+
</goals>
222+
<configuration>
223+
<dataFile>${project.build.directory}/jacoco-output/jacoco-integration-tests.exec</dataFile>
224+
<outputDirectory>${project.reporting.outputDirectory}/jacoco-integration-test-coverage-report</outputDirectory>
225+
</configuration>
226+
</execution>
227+
<execution>
228+
<id>merge-unit-and-integration</id>
229+
<phase>post-integration-test</phase>
230+
<goals>
231+
<goal>merge</goal>
232+
</goals>
233+
<configuration>
234+
<fileSets>
235+
<fileSet>
236+
<directory>${project.build.directory}/jacoco-output/</directory>
237+
<includes>
238+
<include>*.exec</include>
239+
</includes>
240+
</fileSet>
241+
</fileSets>
242+
<destFile>${project.build.directory}/jacoco-output/merged.exec</destFile>
243+
</configuration>
244+
</execution>
245+
<execution>
246+
<id>create-merged-report</id>
247+
<phase>post-integration-test</phase>
248+
<goals>
249+
<goal>report</goal>
250+
</goals>
251+
<configuration>
252+
<dataFile>${project.build.directory}/jacoco-output/merged.exec</dataFile>
253+
<outputDirectory>${project.reporting.outputDirectory}/jacoco-merged-test-coverage-report</outputDirectory>
254+
</configuration>
255+
</execution>
256+
</executions>
257+
</plugin>
119258
</plugins>
120259
</build>
121260
</project>
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/**
2+
* Copyright 2022 IBM Corporation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.ibm.eventstreams.connect.mqsink;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
import java.util.concurrent.TimeoutException;
21+
22+
import javax.jms.Connection;
23+
import javax.jms.Destination;
24+
import javax.jms.JMSContext;
25+
import javax.jms.JMSException;
26+
import javax.jms.Message;
27+
import javax.jms.MessageConsumer;
28+
import javax.jms.Session;
29+
30+
import org.junit.ClassRule;
31+
import org.testcontainers.containers.GenericContainer;
32+
import org.testcontainers.containers.output.WaitingConsumer;
33+
34+
import com.ibm.mq.jms.MQConnectionFactory;
35+
import com.ibm.msg.client.jms.JmsConnectionFactory;
36+
import com.ibm.msg.client.jms.JmsFactoryFactory;
37+
import com.ibm.msg.client.wmq.WMQConstants;
38+
39+
40+
/**
41+
* Helper class for integration tests that have a dependency on JMSContext.
42+
*
43+
* It starts a queue manager in a test container, and uses it to create
44+
* a JMSContext instance, that can be used in tests.
45+
*/
46+
public abstract class AbstractJMSContextIT {
47+
48+
private static final String QMGR_NAME = "MYQMGR";
49+
private static final String CHANNEL_NAME = "DEV.APP.SVRCONN";
50+
51+
@ClassRule
52+
public static GenericContainer<?> MQ_CONTAINER = new GenericContainer<>("icr.io/ibm-messaging/mq:latest")
53+
.withEnv("LICENSE", "accept")
54+
.withEnv("MQ_QMGR_NAME", QMGR_NAME)
55+
.withEnv("MQ_ENABLE_EMBEDDED_WEB_SERVER", "false")
56+
.withExposedPorts(1414);
57+
58+
private JMSContext jmsContext;
59+
60+
61+
/**
62+
* Returns a JMS context pointing at a developer queue manager running in a
63+
* test container.
64+
*/
65+
public JMSContext getJmsContext() throws Exception {
66+
if (jmsContext == null) {
67+
waitForQueueManagerStartup();
68+
69+
MQConnectionFactory mqcf = new MQConnectionFactory();
70+
mqcf.setTransportType(WMQConstants.WMQ_CM_CLIENT);
71+
mqcf.setChannel(CHANNEL_NAME);
72+
mqcf.setQueueManager(QMGR_NAME);
73+
mqcf.setConnectionNameList(getConnectionName());
74+
75+
jmsContext = mqcf.createContext();
76+
}
77+
78+
return jmsContext;
79+
}
80+
81+
82+
/**
83+
* Gets the host port that has been mapped to the default MQ 1414 port in the test container.
84+
*/
85+
public Integer getMQPort() {
86+
return MQ_CONTAINER.getMappedPort(1414);
87+
}
88+
89+
public String getQmgrName() {
90+
return QMGR_NAME;
91+
}
92+
public String getChannelName() {
93+
return CHANNEL_NAME;
94+
}
95+
public String getConnectionName() {
96+
return "localhost(" + getMQPort().toString() + ")";
97+
}
98+
99+
100+
/**
101+
* Waits until we see a log line in the queue manager test container that indicates
102+
* the queue manager is ready.
103+
*/
104+
private void waitForQueueManagerStartup() throws TimeoutException {
105+
WaitingConsumer logConsumer = new WaitingConsumer();
106+
MQ_CONTAINER.followOutput(logConsumer);
107+
logConsumer.waitUntil(logline -> logline.getUtf8String().contains("AMQ5975I"));
108+
}
109+
110+
111+
/**
112+
* Retrieves all messages from the specified MQ queue (destructively). Used in
113+
* tests to verify that the expected messages were put to the test queue.
114+
*/
115+
public List<Message> getAllMessagesFromQueue(String queueName) throws JMSException {
116+
Connection connection = null;
117+
Session session = null;
118+
Destination destination = null;
119+
MessageConsumer consumer = null;
120+
121+
JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
122+
123+
JmsConnectionFactory cf = ff.createConnectionFactory();
124+
cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, "localhost");
125+
cf.setIntProperty(WMQConstants.WMQ_PORT, getMQPort());
126+
cf.setStringProperty(WMQConstants.WMQ_CHANNEL, getChannelName());
127+
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
128+
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, getQmgrName());
129+
cf.setBooleanProperty(WMQConstants.USER_AUTHENTICATION_MQCSP, false);
130+
131+
connection = cf.createConnection();
132+
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
133+
134+
destination = session.createQueue(queueName);
135+
consumer = session.createConsumer(destination);
136+
137+
connection.start();
138+
139+
List<Message> messages = new ArrayList<>();
140+
Message message;
141+
do {
142+
message = consumer.receiveNoWait();
143+
if (message != null) {
144+
messages.add(message);
145+
}
146+
}
147+
while (message != null);
148+
149+
connection.close();
150+
151+
return messages;
152+
}
153+
}

0 commit comments

Comments
 (0)