Skip to content

Commit 57e969a

Browse files
committed
test(proxy): 重构并增强 TlsCertificateManager 测试用例- 重新设计测试用例,使用临时文件模拟证书和密钥
- 增加对 TlsCertificateManager 各种方法的单元测试 - 涉及到的测试场景包括: - 构造函数 - 启动和关闭 - 注册和注销监听器 - 文件变更通知(证书、密钥、未知文件等) - 多个监听器的情况 - 监听器抛出异常的情况 - 增加对内部 CertKeyFileWatchListener 的测试 Signed-off-by: Async <raisinata@foxmail.com>
1 parent 8e1f984 commit 57e969a

2 files changed

Lines changed: 277 additions & 45 deletions

File tree

proxy/src/main/java/org/apache/rocketmq/proxy/service/cert/TlsCertificateManager.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ public TlsCertificateManager() {
4747
}
4848
}
4949

50+
public FileWatchService getFileWatchService() {
51+
return this.fileWatchService;
52+
}
53+
5054
public void registerReloadListener(TlsContextReloadListener listener) {
5155
if (listener != null) {
5256
this.reloadListeners.add(listener);
@@ -59,6 +63,10 @@ public void unregisterReloadListener(TlsContextReloadListener listener) {
5963
}
6064
}
6165

66+
public List<TlsContextReloadListener> getReloadListeners() {
67+
return this.reloadListeners;
68+
}
69+
6270
@Override
6371
public void start() throws Exception {
6472
this.fileWatchService.start();

proxy/src/test/java/org/apache/rocketmq/proxy/service/cert/TlsCertificateManagerTest.java

Lines changed: 269 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,87 +16,311 @@
1616
*/
1717
package org.apache.rocketmq.proxy.service.cert;
1818

19+
import java.io.FileWriter;
20+
import org.apache.rocketmq.proxy.config.ConfigurationManager;
21+
import org.apache.rocketmq.proxy.config.ProxyConfig;
1922
import org.apache.rocketmq.remoting.netty.TlsSystemConfig;
2023
import org.apache.rocketmq.srvutil.FileWatchService;
2124
import org.junit.jupiter.api.AfterEach;
2225
import org.junit.jupiter.api.BeforeAll;
2326
import org.junit.jupiter.api.BeforeEach;
2427
import org.junit.jupiter.api.Test;
2528
import org.junit.jupiter.api.extension.ExtendWith;
29+
import org.junit.jupiter.api.io.TempDir;
2630
import org.mockito.Mock;
31+
import org.mockito.MockitoAnnotations;
32+
import org.mockito.junit.jupiter.MockitoExtension;
33+
import org.powermock.reflect.Whitebox;
2734

35+
import java.io.File;
2836
import java.lang.reflect.Constructor;
37+
import java.lang.reflect.Field;
38+
import java.lang.reflect.Method;
39+
import java.nio.file.Files;
40+
import java.nio.file.Path;
41+
import java.util.List;
2942

30-
import static org.mockito.Mockito.never;
31-
import static org.mockito.Mockito.reset;
32-
import static org.mockito.Mockito.times;
33-
import static org.mockito.Mockito.verify;
43+
import static org.junit.jupiter.api.Assertions.*;
44+
import static org.mockito.Mockito.*;
3445

35-
@ExtendWith(org.mockito.junit.jupiter.MockitoExtension.class)
36-
class TlsCertificateManagerTest {
46+
@ExtendWith(MockitoExtension.class)
47+
public class TlsCertificateManagerTest {
48+
49+
@TempDir
50+
Path tempDir;
51+
52+
private TlsCertificateManager manager;
3753

3854
@Mock
39-
private TlsCertificateManager.TlsContextReloadListener reloadListener;
55+
private ProxyConfig proxyConfig;
4056

41-
private static TlsCertificateManager manager;
57+
@Mock
58+
private TlsCertificateManager.TlsContextReloadListener listener1;
4259

43-
private FileWatchService.Listener innerListener;
60+
@Mock
61+
private TlsCertificateManager.TlsContextReloadListener listener2;
4462

45-
@BeforeAll
46-
static void initTlsConfig() {
47-
TlsSystemConfig.tlsServerCertPath = "/tmp/server.crt";
48-
TlsSystemConfig.tlsServerKeyPath = "/tmp/server.key";
49-
TlsSystemConfig.tlsServerTrustCertPath = "/tmp/ca.crt";
63+
private File certFile;
64+
private File keyFile;
65+
private FileWatchService.Listener fileWatchListener;
66+
private Field configField;
67+
private ProxyConfig originalConfig;
5068

51-
// Obtain the singleton after the paths are set
52-
manager = TlsCertificateManager.getInstance();
69+
@BeforeAll
70+
public static void setUpAll() throws Exception {
71+
ConfigurationManager.initEnv();
72+
ConfigurationManager.intConfig();
5373
}
5474

5575
@BeforeEach
56-
void setUp() throws Exception {
57-
// Register the external listener
58-
manager.registerReloadListener(reloadListener);
76+
public void setUp() throws Exception {
77+
// Create temporary certificate and key files
78+
certFile = new File(tempDir.toFile(), "server.crt");
79+
keyFile = new File(tempDir.toFile(), "server.key");
80+
try (FileWriter certWriter = new FileWriter(certFile);
81+
FileWriter keyWriter = new FileWriter(keyFile)) {
82+
certWriter.write("test certificate content");
83+
keyWriter.write("test key content");
84+
}
85+
86+
// Set TlsSystemConfig paths
87+
TlsSystemConfig.tlsServerCertPath = certFile.getAbsolutePath();
88+
TlsSystemConfig.tlsServerKeyPath = keyFile.getAbsolutePath();
5989

60-
Class<?> innerClazz = Class.forName(
61-
"org.apache.rocketmq.proxy.service.cert.TlsCertificateManager$CertKeyFileWatchListener");
62-
Constructor<?> ctor = innerClazz.getDeclaredConstructor(TlsCertificateManager.class);
63-
ctor.setAccessible(true);
64-
innerListener = (FileWatchService.Listener) ctor.newInstance(manager);
90+
// Create the TlsCertificateManager
91+
manager = new TlsCertificateManager();
92+
93+
// Extract the file watch listener using reflection
94+
fileWatchListener = extractFileWatchListener(manager);
6595
}
6696

6797
@AfterEach
68-
void tearDown() {
69-
// Unregister and reset between tests to avoid interference
70-
manager.unregisterReloadListener(reloadListener);
71-
reset(reloadListener);
98+
public void tearDown() throws Exception {
99+
// Restore the original config
100+
if (configField != null && originalConfig != null) {
101+
configField.set(null, originalConfig);
102+
}
103+
}
104+
105+
private FileWatchService.Listener extractFileWatchListener(TlsCertificateManager manager) throws Exception {
106+
Field fileWatchServiceField = TlsCertificateManager.class.getDeclaredField("fileWatchService");
107+
fileWatchServiceField.setAccessible(true);
108+
FileWatchService fileWatchService = (FileWatchService) fileWatchServiceField.get(manager);
109+
110+
Field listenerField = FileWatchService.class.getDeclaredField("listener");
111+
listenerField.setAccessible(true);
112+
return (FileWatchService.Listener) listenerField.get(fileWatchService);
113+
}
114+
115+
@Test
116+
public void testConstructor() {
117+
// The constructor should initialize the FileWatchService with the correct paths
118+
assertNotNull(manager);
119+
}
120+
121+
@Test
122+
public void testStartAndShutdown() throws Exception {
123+
// Create a spy to verify the internal fileWatchService methods are called
124+
TlsCertificateManager managerSpy = spy(manager);
125+
126+
// Get access to the internal fileWatchService
127+
Field watchServiceField = TlsCertificateManager.class.getDeclaredField("fileWatchService");
128+
watchServiceField.setAccessible(true);
129+
FileWatchService watchService = (FileWatchService) watchServiceField.get(managerSpy);
130+
FileWatchService watchServiceSpy = spy(watchService);
131+
watchServiceField.set(managerSpy, watchServiceSpy);
132+
133+
// Test start method
134+
managerSpy.start();
135+
verify(watchServiceSpy).start();
136+
137+
// Test shutdown method
138+
managerSpy.shutdown();
139+
verify(watchServiceSpy).shutdown();
140+
}
141+
142+
@Test
143+
public void testRegisterAndUnregisterListener() {
144+
// Test registering a listener
145+
manager.registerReloadListener(listener1);
146+
147+
List<TlsCertificateManager.TlsContextReloadListener> listeners = manager.getReloadListeners();
148+
assertEquals(1, listeners.size());
149+
assertTrue(listeners.contains(listener1));
150+
151+
// Test registering another listener
152+
manager.registerReloadListener(listener2);
153+
assertEquals(2, listeners.size());
154+
assertTrue(listeners.contains(listener2));
155+
156+
// Test unregistering a listener
157+
manager.unregisterReloadListener(listener1);
158+
assertEquals(1, listeners.size());
159+
assertFalse(listeners.contains(listener1));
160+
assertTrue(listeners.contains(listener2));
161+
162+
// Test handling null listeners
163+
manager.registerReloadListener(null);
164+
assertEquals(1, listeners.size()); // Should remain unchanged
165+
166+
manager.unregisterReloadListener(null);
167+
assertEquals(1, listeners.size()); // Should remain unchanged
168+
}
169+
170+
@Test
171+
public void testFileChangeNotification_CertOnly() throws Exception {
172+
// Setup test
173+
manager.registerReloadListener(listener1);
174+
175+
// Trigger cert file change only
176+
fileWatchListener.onChanged(certFile.getAbsolutePath());
177+
178+
// Verify listener not called yet
179+
verify(listener1, never()).onTlsContextReload();
180+
}
181+
182+
@Test
183+
public void testFileChangeNotification_KeyOnly() throws Exception {
184+
// Setup test
185+
manager.registerReloadListener(listener1);
186+
187+
// Trigger key file change only
188+
fileWatchListener.onChanged(keyFile.getAbsolutePath());
189+
190+
// Verify listener not called yet
191+
verify(listener1, never()).onTlsContextReload();
72192
}
73193

74-
// Trust certificate change should trigger immediate reload
75194
@Test
76-
void trustCertChanged_shouldTriggerReload() {
77-
innerListener.onChanged(TlsSystemConfig.tlsServerTrustCertPath);
78-
verify(reloadListener, times(1)).onTlsContextReload();
195+
public void testFileChangeNotification_BothFiles() throws Exception {
196+
// Setup test
197+
manager.registerReloadListener(listener1);
198+
199+
// Trigger both file changes
200+
fileWatchListener.onChanged(certFile.getAbsolutePath());
201+
fileWatchListener.onChanged(keyFile.getAbsolutePath());
202+
203+
// Verify listener is called
204+
verify(listener1, times(1)).onTlsContextReload();
205+
}
206+
207+
@Test
208+
public void testFileChangeNotification_MultipleListeners() throws Exception {
209+
// Setup test
210+
manager.registerReloadListener(listener1);
211+
manager.registerReloadListener(listener2);
212+
213+
// Trigger both file changes
214+
fileWatchListener.onChanged(certFile.getAbsolutePath());
215+
fileWatchListener.onChanged(keyFile.getAbsolutePath());
216+
217+
// Verify both listeners are called
218+
verify(listener1, times(1)).onTlsContextReload();
219+
verify(listener2, times(1)).onTlsContextReload();
79220
}
80221

81-
// Only server certificate change should NOT trigger reload
82222
@Test
83-
void certOnlyChanged_shouldNotTriggerReload() {
84-
innerListener.onChanged(TlsSystemConfig.tlsServerCertPath);
85-
verify(reloadListener, never()).onTlsContextReload();
223+
public void testFileChangeNotification_BothFilesReverseOrder() throws Exception {
224+
// Setup test
225+
manager.registerReloadListener(listener1);
226+
227+
// Trigger both file changes in reverse order
228+
fileWatchListener.onChanged(keyFile.getAbsolutePath());
229+
fileWatchListener.onChanged(certFile.getAbsolutePath());
230+
231+
// Verify listener is called
232+
verify(listener1, times(1)).onTlsContextReload();
86233
}
87234

88-
// Only private-key change should NOT trigger reload
89235
@Test
90-
void keyOnlyChanged_shouldNotTriggerReload() {
91-
innerListener.onChanged(TlsSystemConfig.tlsServerKeyPath);
92-
verify(reloadListener, never()).onTlsContextReload();
236+
public void testFileChangeNotification_RepeatedChanges() throws Exception {
237+
// Setup test
238+
manager.registerReloadListener(listener1);
239+
240+
// First batch of changes
241+
fileWatchListener.onChanged(certFile.getAbsolutePath());
242+
fileWatchListener.onChanged(keyFile.getAbsolutePath());
243+
244+
// Verify listener is called once
245+
verify(listener1, times(1)).onTlsContextReload();
246+
247+
// Second batch of changes
248+
fileWatchListener.onChanged(certFile.getAbsolutePath());
249+
fileWatchListener.onChanged(keyFile.getAbsolutePath());
250+
251+
// Verify listener is called again (total twice)
252+
verify(listener1, times(2)).onTlsContextReload();
93253
}
94254

95-
// Server certificate + key both changed -> trigger one reload
96255
@Test
97-
void certAndKeyChanged_shouldTriggerReloadOnce() {
98-
innerListener.onChanged(TlsSystemConfig.tlsServerCertPath);
99-
innerListener.onChanged(TlsSystemConfig.tlsServerKeyPath);
100-
verify(reloadListener, times(1)).onTlsContextReload();
256+
public void testFileChangeNotification_UnknownFile() throws Exception {
257+
// Setup test
258+
manager.registerReloadListener(listener1);
259+
260+
// Trigger change to an unknown file
261+
fileWatchListener.onChanged("/unknown/file/path");
262+
263+
// Verify listener is not called
264+
verify(listener1, never()).onTlsContextReload();
265+
}
266+
267+
@Test
268+
public void testFileChangeNotification_ListenerThrowsException() throws Exception {
269+
// Setup a listener that throws an exception
270+
TlsCertificateManager.TlsContextReloadListener exceptionListener = mock(TlsCertificateManager.TlsContextReloadListener.class);
271+
doThrow(new RuntimeException("Test exception")).when(exceptionListener).onTlsContextReload();
272+
273+
// Register both listeners
274+
manager.registerReloadListener(exceptionListener);
275+
manager.registerReloadListener(listener1);
276+
277+
// Trigger both file changes
278+
fileWatchListener.onChanged(certFile.getAbsolutePath());
279+
fileWatchListener.onChanged(keyFile.getAbsolutePath());
280+
281+
// Verify both listeners were called despite the exception
282+
verify(exceptionListener, times(1)).onTlsContextReload();
283+
verify(listener1, times(1)).onTlsContextReload();
284+
}
285+
286+
@Test
287+
public void testInnerCertKeyFileWatchListener() throws Exception {
288+
// Get the CertKeyFileWatchListener class
289+
Class<?> innerClass = null;
290+
for (Class<?> clazz : TlsCertificateManager.class.getDeclaredClasses()) {
291+
if (clazz.getSimpleName().equals("CertKeyFileWatchListener")) {
292+
innerClass = clazz;
293+
break;
294+
}
295+
}
296+
297+
assertNotNull(innerClass, "CertKeyFileWatchListener class not found");
298+
299+
// Create a new instance
300+
Constructor<?> constructor = innerClass.getDeclaredConstructor(TlsCertificateManager.class);
301+
constructor.setAccessible(true);
302+
Object innerListener = constructor.newInstance(manager);
303+
304+
// Register a mock listener to the manager
305+
manager.registerReloadListener(listener1);
306+
307+
// Get the onChanged method
308+
Method onChangedMethod = innerClass.getDeclaredMethod("onChanged", String.class);
309+
onChangedMethod.setAccessible(true);
310+
311+
// Test cert file change
312+
onChangedMethod.invoke(innerListener, certFile.getAbsolutePath());
313+
verify(listener1, never()).onTlsContextReload();
314+
315+
// Test key file change - should trigger notification
316+
onChangedMethod.invoke(innerListener, keyFile.getAbsolutePath());
317+
verify(listener1, times(1)).onTlsContextReload();
318+
319+
// Test reset of flags
320+
reset(listener1);
321+
322+
// Call onChanged again for cert - should not trigger notification as flags are reset
323+
onChangedMethod.invoke(innerListener, certFile.getAbsolutePath());
324+
verify(listener1, never()).onTlsContextReload();
101325
}
102326
}

0 commit comments

Comments
 (0)