1
2
3
4
5
6
7
8
9
10
11 package psiprobe;
12
13 import static org.junit.jupiter.api.Assertions.assertEquals;
14 import static org.junit.jupiter.api.Assertions.assertNotNull;
15 import static org.junit.jupiter.api.Assertions.assertNull;
16 import static org.junit.jupiter.api.Assertions.assertTrue;
17 import static org.mockito.ArgumentMatchers.any;
18 import static org.mockito.ArgumentMatchers.eq;
19 import static org.mockito.Mockito.mock;
20 import static org.mockito.Mockito.times;
21 import static org.mockito.Mockito.verify;
22 import static org.mockito.Mockito.when;
23
24 import jakarta.servlet.ServletContext;
25
26 import java.io.InputStream;
27 import java.net.URI;
28 import java.nio.file.Files;
29 import java.nio.file.Path;
30 import java.util.ArrayList;
31 import java.util.List;
32
33 import javax.management.MBeanServer;
34 import javax.management.ObjectName;
35
36 import org.apache.catalina.Container;
37 import org.apache.catalina.Context;
38 import org.apache.catalina.Engine;
39 import org.apache.catalina.Host;
40 import org.apache.catalina.LifecycleState;
41 import org.apache.catalina.Pipeline;
42 import org.apache.catalina.Service;
43 import org.apache.catalina.Valve;
44 import org.apache.catalina.Wrapper;
45 import org.apache.catalina.connector.Connector;
46 import org.apache.jasper.JspCompilationContext;
47 import org.apache.jasper.Options;
48 import org.apache.jasper.compiler.JspRuntimeContext;
49 import org.junit.jupiter.api.Test;
50
51 import psiprobe.model.ApplicationParam;
52 import psiprobe.model.ApplicationResource;
53 import psiprobe.model.FilterInfo;
54 import psiprobe.model.FilterMapping;
55 import psiprobe.model.jsp.Summary;
56
57
58
59
60 class AbstractTomcatContainerTest {
61
62 private static class TestTomcatContainer extends AbstractTomcatContainer {
63
64 private final Valve valve = mock(Valve.class);
65
66 @Override
67 protected Object getNamingToken(Context context) {
68 return "token";
69 }
70
71 @Override
72 protected JspCompilationContext createJspCompilationContext(String name, Options opt,
73 ServletContext sctx, JspRuntimeContext jrctx, ClassLoader classLoader) {
74 return null;
75 }
76
77 @Override
78 protected Valve createValve() {
79 return valve;
80 }
81
82 @Override
83 public boolean canBoundTo(String binding) {
84 return true;
85 }
86
87 @Override
88 public List<FilterMapping> getApplicationFilterMaps(Context context) {
89 return List.of();
90 }
91
92 @Override
93 public void addContextResource(Context context, List<ApplicationResource> resourceList) {
94
95 }
96
97 @Override
98 public void addContextResourceLink(Context context, List<ApplicationResource> resourceList) {
99
100 }
101
102 @Override
103 public List<FilterInfo> getApplicationFilters(Context context) {
104 return List.of();
105 }
106
107 @Override
108 public List<ApplicationParam> getApplicationInitParams(Context context) {
109 return List.of();
110 }
111
112 @Override
113 public boolean resourceExists(String name, Context context) {
114 return false;
115 }
116
117 @Override
118 public InputStream getResourceStream(String name, Context context) {
119 return null;
120 }
121
122 @Override
123 public Long[] getResourceAttributes(String name, Context context) {
124 return new Long[] {0L, 0L};
125 }
126 }
127
128 @Test
129 void setWrapperInitializesContainerStateAndRemovesValveOnUnset() {
130 TestTomcatContainer container = new TestTomcatContainer();
131 Wrapper wrapper = mock(Wrapper.class);
132 Container parent = mock(Container.class);
133 Host host = mock(Host.class);
134 Engine engine = mock(Engine.class);
135 Service service = mock(Service.class);
136 Pipeline pipeline = mock(Pipeline.class);
137
138 when(wrapper.getParent()).thenReturn(parent);
139 when(parent.getParent()).thenReturn(host);
140 when(host.getParent()).thenReturn(engine);
141 when(host.getPipeline()).thenReturn(pipeline);
142 when(host.getName()).thenReturn("localhost");
143 when(host.getAppBase()).thenReturn("webapps");
144 when(engine.getService()).thenReturn(service);
145 when(engine.getName()).thenReturn("Catalina");
146 when(service.findConnectors()).thenReturn(new Connector[] {mock(Connector.class)});
147
148 container.setWrapper(wrapper);
149 assertEquals(1, container.findConnectors().size());
150
151 container.setWrapper(null);
152 verify(pipeline, times(1)).addValve(any());
153 verify(pipeline, times(1)).removeValve(any());
154 }
155
156 @Test
157 void formatContextNameAndFilenameHandleRootAndNestedPaths() {
158 TestTomcatContainer container = new TestTomcatContainer();
159
160 assertNull(container.formatContextName(null));
161 assertEquals("", container.formatContextName("ROOT"));
162 assertEquals("", container.formatContextName("/ROOT"));
163 assertEquals("/app", container.formatContextName("app"));
164 assertEquals("##v1", container.formatContextName("/ROOT##v1"));
165
166 assertNull(container.formatContextFilename(null));
167 assertEquals("ROOT", container.formatContextFilename("/"));
168 assertEquals("ROOT##v2", container.formatContextFilename("/##v2"));
169 assertEquals("a#b", container.formatContextFilename("/a/b"));
170 }
171
172 @Test
173 void findContextFallsBackToSlashForRoot() {
174 TestTomcatContainer container = new TestTomcatContainer();
175 Host host = mock(Host.class);
176 Context rootContext = mock(Context.class);
177
178 container.host = host;
179 when(host.findChild("")).thenReturn(null);
180 when(host.findChild("/")).thenReturn(rootContext);
181
182 assertEquals(rootContext, container.findContext("/"));
183 }
184
185 @Test
186 void getAppBaseSupportsRelativeAndAbsolutePaths() {
187 TestTomcatContainer container = new TestTomcatContainer();
188 Host host = mock(Host.class);
189
190 container.host = host;
191
192 String oldBase = System.getProperty("catalina.base");
193 try {
194 System.setProperty("catalina.base", "/tmp/catalina-base");
195
196 when(host.getAppBase()).thenReturn("webapps");
197 assertEquals(Path.of("/tmp/catalina-base", "webapps").toString(),
198 container.getAppBase().toPath().toString());
199
200 String absoluteAppBase =
201 Path.of(System.getProperty("java.io.tmpdir"), "opt-webapps").toAbsolutePath().toString();
202 when(host.getAppBase()).thenReturn(absoluteAppBase);
203 assertEquals(Path.of(absoluteAppBase).toString(), container.getAppBase().toPath().toString());
204 } finally {
205 if (oldBase != null) {
206 System.setProperty("catalina.base", oldBase);
207 } else {
208 System.clearProperty("catalina.base");
209 }
210 }
211 }
212
213 @Test
214 void findContextsReturnsOnlyContextChildren() {
215 TestTomcatContainer container = new TestTomcatContainer();
216 Host host = mock(Host.class);
217 Context context = mock(Context.class);
218 Container other = mock(Container.class);
219
220 container.host = host;
221 when(host.findChildren()).thenReturn(new Container[] {context, other});
222
223 List<Context> contexts = container.findContexts();
224 assertEquals(1, contexts.size());
225 assertEquals(context, contexts.get(0));
226 }
227
228 @Test
229 void stopAndStartOperateWhenContextExists() throws Exception {
230 TestTomcatContainer container = new TestTomcatContainer();
231 Host host = mock(Host.class);
232 Context context = mock(Context.class);
233
234 container.host = host;
235 when(host.findChild("/app")).thenReturn(context);
236
237 container.stop("/app");
238 container.start("/app");
239
240 verify(context, times(1)).stop();
241 verify(context, times(1)).start();
242 }
243
244 @Test
245 void installWarInvokesMbeanChangesFlow() throws Exception {
246 TestTomcatContainer container = new TestTomcatContainer();
247 MBeanServer mBeanServer = mock(MBeanServer.class);
248
249 container.mbeanServer = mBeanServer;
250 container.objectNameDeployer = new ObjectName("Catalina:type=Deployer,host=localhost");
251
252 when(mBeanServer.invoke(any(), eq("tryAddServiced"), any(), any())).thenReturn(Boolean.TRUE);
253 when(mBeanServer.invoke(any(), eq("check"), any(), any())).thenReturn(null);
254 when(mBeanServer.invoke(any(), eq("removeServiced"), any(), any())).thenReturn(null);
255
256 container.installWar("/app");
257
258 verify(mBeanServer, times(1)).invoke(any(), eq("check"), any(), any());
259 verify(mBeanServer, times(1)).invoke(any(), eq("removeServiced"), any(), any());
260 }
261
262 @Test
263 void getConfigFileReturnsOnlyFileScheme() throws Exception {
264 TestTomcatContainer container = new TestTomcatContainer();
265 Context context = mock(Context.class);
266
267 Path temp = Files.createTempFile("ctx", ".xml");
268 temp.toFile().deleteOnExit();
269 when(context.getConfigFile()).thenReturn(temp.toUri().toURL());
270 assertNotNull(container.getConfigFile(context));
271
272 when(context.getConfigFile()).thenReturn(URI.create("https://example.com/context.xml").toURL());
273 assertNull(container.getConfigFile(context));
274 }
275
276 @Test
277 void addFilterMappingSupportsUrlAndServletMappings() {
278 TestTomcatContainer container = new TestTomcatContainer();
279 List<FilterMapping> mappings = new ArrayList<>();
280
281 container.addFilterMapping("f1", "REQUEST", "FilterClass", new String[] {"/a", "/b"}, mappings,
282 AbstractTomcatContainer.FilterMapType.URL);
283 container.addFilterMapping("f2", "REQUEST", "FilterClass2", new String[] {"s1"}, mappings,
284 AbstractTomcatContainer.FilterMapType.SERVLET_NAME);
285
286 assertEquals(3, mappings.size());
287 assertEquals("/a", mappings.get(0).getUrl());
288 assertEquals("s1", mappings.get(2).getServletName());
289 }
290
291 @Test
292 void getAvailableReflectsLifecycleState() {
293 TestTomcatContainer container = new TestTomcatContainer();
294 Context context = mock(Context.class);
295
296 when(context.getState()).thenReturn(LifecycleState.STARTED);
297 assertTrue(container.getAvailable(context));
298 }
299
300 @Test
301 void getNameAndHostNameUseHostHierarchy() {
302 TestTomcatContainer container = new TestTomcatContainer();
303 Host host = mock(Host.class);
304 Engine engine = mock(Engine.class);
305
306 container.host = host;
307 when(host.getName()).thenReturn("localhost");
308 when(host.getParent()).thenReturn(engine);
309 when(engine.getName()).thenReturn("Catalina");
310
311 assertEquals("localhost", container.getHostName());
312 assertEquals("Catalina", container.getName());
313 }
314
315 @Test
316 void listContextJspsHandlesMissingJspServletWithoutThrowing() {
317 TestTomcatContainer container = new TestTomcatContainer();
318 Context context = mock(Context.class);
319 Summary summary = new Summary();
320
321 when(context.findChild("jsp")).thenReturn(null);
322 when(context.getName()).thenReturn("/app");
323
324 container.listContextJsps(context, summary, false);
325 assertNotNull(summary.getItems());
326 assertTrue(summary.getItems().isEmpty());
327 }
328 }