Skip to content

Commit effe8e1

Browse files
committed
refactor: memshell integration-test
1 parent 95ec3ed commit effe8e1

72 files changed

Lines changed: 2632 additions & 4519 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
package com.reajason.javaweb.integration;
2+
3+
import com.reajason.javaweb.memshell.ShellTool;
4+
import com.reajason.javaweb.memshell.ShellType;
5+
import com.reajason.javaweb.packer.Packers;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.apache.commons.lang3.StringUtils;
8+
import org.junit.jupiter.api.AfterAll;
9+
import org.junit.jupiter.api.Assumptions;
10+
import org.junit.jupiter.api.TestInstance;
11+
import org.junit.jupiter.params.ParameterizedTest;
12+
import org.junit.jupiter.params.provider.Arguments;
13+
import org.junit.jupiter.params.provider.EnumSource;
14+
import org.junit.jupiter.params.provider.MethodSource;
15+
import org.testcontainers.containers.GenericContainer;
16+
import org.testcontainers.containers.Network;
17+
import org.testcontainers.containers.wait.strategy.Wait;
18+
import org.testcontainers.images.builder.ImageFromDockerfile;
19+
20+
import java.lang.reflect.Field;
21+
import java.lang.reflect.Modifier;
22+
import java.util.List;
23+
import java.util.Map;
24+
import java.util.stream.Stream;
25+
26+
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
27+
import static org.hamcrest.MatcherAssert.assertThat;
28+
29+
/**
30+
* @author ReaJason
31+
* @since 2025/9/19
32+
*/
33+
@Slf4j
34+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
35+
public abstract class AbstractContainerTest {
36+
private static final String NO_PROBE = "__NO_PROBE__";
37+
38+
protected static Network newNetwork() {
39+
return Network.newNetwork();
40+
}
41+
42+
protected static GenericContainer<?> buildPythonContainer(Network network) {
43+
return new GenericContainer<>(new ImageFromDockerfile()
44+
.withDockerfile(ContainerTool.neoGeorgDockerfile))
45+
.withNetwork(network);
46+
}
47+
48+
protected static GenericContainer<?> buildContainer(ContainerTestConfig config, Network network) {
49+
GenericContainer<?> container = createContainer(config);
50+
if (config.getWarFile() != null && StringUtils.isNotBlank(config.getWarDeployPath())) {
51+
container.withCopyToContainer(config.getWarFile(), config.getWarDeployPath());
52+
}
53+
if(config.getJarFile() != null && StringUtils.isNotBlank(config.getJarDeployPath())){
54+
container.withCopyToContainer(config.getJarFile(), config.getJarDeployPath());
55+
}
56+
if (config.getJattachFile() != null) {
57+
container.withCopyToContainer(config.getJattachFile(), "/jattach");
58+
}
59+
if (config.getPidScript() != null) {
60+
container.withCopyToContainer(config.getPidScript(), "/fetch_pid.sh");
61+
}
62+
Map<String, String> env = config.getEnv();
63+
if (env != null) {
64+
env.forEach(container::withEnv);
65+
}
66+
if (network != null) {
67+
container.withNetwork(network);
68+
if (StringUtils.isNotBlank(config.getNetworkAlias())) {
69+
container.withNetworkAliases(config.getNetworkAlias());
70+
}
71+
}
72+
if (config.getWaitStrategy() != null) {
73+
container.waitingFor(config.getWaitStrategy());
74+
} else if (StringUtils.isNotBlank(config.getHealthCheckPath())) {
75+
container.waitingFor(Wait.forHttp(config.getHealthCheckPath()));
76+
}
77+
container.withExposedPorts(config.getExposedPort());
78+
if (config.isPrivilegedMode()) {
79+
container.withPrivilegedMode(true);
80+
}
81+
if(config.getCommand() != null){
82+
container.withCommand(config.getCommand());
83+
}
84+
return container;
85+
}
86+
87+
protected static GenericContainer<?> buildContainer(ContainerTestConfig config) {
88+
return buildContainer(config, null);
89+
}
90+
91+
protected abstract ContainerTestConfig getConfig();
92+
93+
@AfterAll
94+
void tearDown() {
95+
GenericContainer<?> container = getContainer();
96+
if (container == null) {
97+
return;
98+
}
99+
long logDelayMillis = getConfig().getLogDelayMillis();
100+
if (logDelayMillis > 0) {
101+
try {
102+
Thread.sleep(logDelayMillis);
103+
} catch (InterruptedException ex) {
104+
Thread.currentThread().interrupt();
105+
}
106+
}
107+
String logs = container.getLogs();
108+
if (getConfig().isLogContainerOutput()) {
109+
log.info(logs);
110+
}
111+
if (getConfig().isAssertLogs()) {
112+
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
113+
}
114+
}
115+
116+
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
117+
@MethodSource("casesProvider")
118+
void test(String imageName, String shellType, String shellTool, Packers packer) {
119+
runShellInject(getConfig(), shellType, shellTool, packer);
120+
}
121+
122+
@ParameterizedTest
123+
@MethodSource("probeShellTypesProvider")
124+
void testProbeInject(String shellType) {
125+
if (NO_PROBE.equals(shellType)) {
126+
Assumptions.assumeTrue(false, "No probe shell types configured.");
127+
}
128+
runProbeInject(getConfig(), shellType);
129+
}
130+
131+
@ParameterizedTest
132+
@EnumSource(names = {"ClassLoaderJSP", "ClassLoaderJSPUnicode", "DefineClassJSP", "DefineClassJSPUnicode", "JSPX", "JSPXUnicode"})
133+
void testJspPackers(Packers packer) {
134+
ContainerTestConfig config = getConfig();
135+
if (config.isEnableJspPackerTest()) {
136+
String shellType = config.isJakarta() ? ShellType.JAKARTA_FILTER : ShellType.FILTER;
137+
String shellTool = ShellTool.Command;
138+
runShellInject(config, shellType, shellTool, packer);
139+
}
140+
}
141+
142+
protected Stream<Arguments> casesProvider() {
143+
return generateTestCases(getConfig());
144+
}
145+
146+
protected Stream<String> probeShellTypesProvider() {
147+
List<String> probeShellTypes = getConfig().getProbeShellTypes();
148+
if (probeShellTypes == null || probeShellTypes.isEmpty()) {
149+
return Stream.of(NO_PROBE);
150+
}
151+
return probeShellTypes.stream();
152+
}
153+
154+
protected static Stream<Arguments> generateTestCases(ContainerTestConfig config) {
155+
if (config.getUnSupportedCases() != null || config.getUnSupportedShellTools() != null) {
156+
return TestCasesProvider.getTestCases(
157+
config.getImageName(),
158+
config.getServer(),
159+
config.getSupportedShellTypes(),
160+
config.getTestPackers(),
161+
config.getUnSupportedCases(),
162+
config.getUnSupportedShellTools());
163+
}
164+
return TestCasesProvider.getTestCases(
165+
config.getImageName(),
166+
config.getServer(),
167+
config.getSupportedShellTypes(),
168+
config.getTestPackers());
169+
}
170+
171+
protected void runShellInject(ContainerTestConfig config, String shellType, String shellTool, Packers packer) {
172+
String url = getUrl();
173+
if (StringUtils.isNotBlank(config.getServerVersion())) {
174+
ShellAssertion.shellInjectIsOk(url, config.getServer(), config.getServerVersion(), shellType, shellTool,
175+
config.getTargetJdkVersion(), packer, getContainer(), getPythonContainer());
176+
} else {
177+
ShellAssertion.shellInjectIsOk(url, config.getServer(), shellType, shellTool,
178+
config.getTargetJdkVersion(), packer, getContainer(), getPythonContainer());
179+
}
180+
}
181+
182+
protected void runProbeInject(ContainerTestConfig config, String shellType) {
183+
String url = getUrl();
184+
int probeTargetJdkVersion = config.getProbeTargetJdkVersion() == null
185+
? config.getTargetJdkVersion()
186+
: config.getProbeTargetJdkVersion();
187+
if (StringUtils.isNotBlank(config.getServerVersion())) {
188+
ShellAssertion.testProbeInject(url, config.getServer(), config.getServerVersion(), shellType, probeTargetJdkVersion);
189+
} else {
190+
ShellAssertion.testProbeInject(url, config.getServer(), shellType, probeTargetJdkVersion);
191+
}
192+
}
193+
194+
protected String getUrl() {
195+
GenericContainer<?> container = getContainer();
196+
ContainerTestConfig config = getConfig();
197+
String host = container.getHost();
198+
int port = container.getMappedPort(config.getExposedPort());
199+
String url = "http://" + host + ":" + port;
200+
String contextPath = config.getContextPath();
201+
if (StringUtils.isNotBlank(contextPath)) {
202+
if (!contextPath.startsWith("/")) {
203+
contextPath = "/" + contextPath;
204+
}
205+
url += contextPath;
206+
}
207+
log.info("container started, app url is : {}", url);
208+
return url;
209+
}
210+
211+
protected GenericContainer<?> getContainer() {
212+
return getContainerField("container", true);
213+
}
214+
215+
protected GenericContainer<?> getPythonContainer() {
216+
return getContainerField("python", false);
217+
}
218+
219+
private static GenericContainer<?> createContainer(ContainerTestConfig config) {
220+
if (config.getDockerfilePath() != null) {
221+
return new GenericContainer<>(new ImageFromDockerfile()
222+
.withDockerfile(config.getDockerfilePath()));
223+
}
224+
if (StringUtils.isBlank(config.getImageName())) {
225+
throw new IllegalArgumentException("imageName is required when dockerfilePath is not set.");
226+
}
227+
return new GenericContainer<>(config.getImageName());
228+
}
229+
230+
private GenericContainer<?> getContainerField(String fieldName, boolean required) {
231+
Field field = findField(getClass(), fieldName);
232+
if (field == null) {
233+
if (required) {
234+
throw new IllegalStateException("Missing @" + fieldName + " container on " + getClass().getName());
235+
}
236+
return null;
237+
}
238+
try {
239+
field.setAccessible(true);
240+
Object value = field.get(Modifier.isStatic(field.getModifiers()) ? null : this);
241+
return (GenericContainer<?>) value;
242+
} catch (IllegalAccessException ex) {
243+
throw new IllegalStateException("Unable to access " + fieldName + " on " + getClass().getName(), ex);
244+
}
245+
}
246+
247+
private static Field findField(Class<?> type, String name) {
248+
Class<?> current = type;
249+
while (current != null) {
250+
try {
251+
return current.getDeclaredField(name);
252+
} catch (NoSuchFieldException ex) {
253+
current = current.getSuperclass();
254+
}
255+
}
256+
return null;
257+
}
258+
}

0 commit comments

Comments
 (0)