This is an automated email from the ASF dual-hosted git repository.

zhangliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new 8d0d5761d83 Add StatisticsCollectJobWorkerTest (#37088)
8d0d5761d83 is described below

commit 8d0d5761d836ab1489f09aa8ccf7efb35fcaf842
Author: Liang Zhang <[email protected]>
AuthorDate: Thu Nov 13 19:48:21 2025 +0800

    Add StatisticsCollectJobWorkerTest (#37088)
    
    * Add StatisticsCollectJobWorkerTest
    
    * Add StatisticsCollectJobWorkerTest
    
    * Add StatisticsCollectJobWorkerTest
---
 AGENTS.md                                          |   4 +-
 .../collect/StatisticsCollectJobWorkerTest.java    | 142 ++++++++++++++++++++-
 2 files changed, 141 insertions(+), 5 deletions(-)

diff --git a/AGENTS.md b/AGENTS.md
index 5cb50e0028e..3022ccd1d88 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -40,7 +40,7 @@ Mention which topology you target, the registry used, and any 
compatibility cons
 - **Styles to favor:** elegant, minimal solutions—express intent with the 
smallest construct that works, keep methods/tests lean, and avoid incidental 
complexity.
 - **Patterns to lean on:** builder/factory helpers in `infra`, SPI-based 
extension points, immutable DTOs for plan descriptions, explicit strategy enums 
for behavior toggles.
 - **Anti-patterns:** duplicating SQL parsing logic, bypassing metadata caches, 
silent fallbacks when configuration is invalid, adding static singletons in 
shared modules, over-engineering simple flows.
-- **Known pitfalls:** routing regressions when skipping shadow rules, timezone 
drift when mocking time poorly, forgetting to validate both standalone and 
cluster (`mode`) settings, missing ASF headers in new files.
+- **Known pitfalls:** routing regressions when skipping shadow rules, timezone 
drift when mocking time poorly, forgetting to validate both standalone and 
cluster (`mode`) settings, missing ASF headers in new files, Mockito inline 
mocks failing on GraalVM or other JDKs that block self-attach (run 
inline-mocking suites on HotSpot/Temurin or document the limitation), 
reflexively poking private methods instead of driving logic through public APIs.
 - **Success recipe:** describe why a change is needed, point to affected data 
flow step, keep public APIs backwards compatible, and document defaults in 
`docs`.
 - **Case in point:** a prior shadow-rule regression was fixed by (1) 
reproducing via `proxy` + sample config, (2) adding a `kernel` unit test 
covering the skipped branch, (3) updating docs with the exact YAML flag—mirror 
that discipline for new features.
 
@@ -67,7 +67,7 @@ Mention which topology you target, the registry used, and any 
compatibility cons
 ## Testing Expectations
 - Use JUnit 5 + Mockito; tests mirror package paths and follow the 
`ClassNameTest` convention.
 - Method names read `assertXxxCondition`; structure tests as 
Arrange–Act–Assert sections with explicit separators/comments when clarity 
drops.
-- Mock databases, time, and network boundaries; build POJOs directly.
+- Mock databases, time, and network boundaries; build POJOs directly. When 
production code keeps static guards or caches, add shared setup/teardown 
helpers that reset them between tests so cases stay isolated.
 - When Jacoco fails, open `{module}/target/site/jacoco/index.html`, note 
uncovered branches, and explain how new tests address them.
 - Need a quick coverage view? Run `./mvnw -pl {module} -am -Djacoco.skip=false 
test jacoco:report` and open `{module}/target/site/jacoco/index.html`.
 - Jacoco is disabled by default (the top-level POM sets `jacoco.skip=true`), 
so explicitly pass `-Djacoco.skip=false` when you need coverage data, then run 
`jacoco:report` for the same module scope.
diff --git 
a/kernel/schedule/core/src/test/java/org/apache/shardingsphere/schedule/core/job/statistics/collect/StatisticsCollectJobWorkerTest.java
 
b/kernel/schedule/core/src/test/java/org/apache/shardingsphere/schedule/core/job/statistics/collect/StatisticsCollectJobWorkerTest.java
index 103154035cf..1b7c4d83284 100644
--- 
a/kernel/schedule/core/src/test/java/org/apache/shardingsphere/schedule/core/job/statistics/collect/StatisticsCollectJobWorkerTest.java
+++ 
b/kernel/schedule/core/src/test/java/org/apache/shardingsphere/schedule/core/job/statistics/collect/StatisticsCollectJobWorkerTest.java
@@ -18,33 +18,76 @@
 package org.apache.shardingsphere.schedule.core.job.statistics.collect;
 
 import lombok.SneakyThrows;
+import org.apache.shardingsphere.elasticjob.api.JobConfiguration;
+import org.apache.shardingsphere.elasticjob.infra.pojo.JobConfigurationPOJO;
 import 
org.apache.shardingsphere.elasticjob.lite.api.bootstrap.impl.ScheduleJobBootstrap;
+import 
org.apache.shardingsphere.elasticjob.lite.lifecycle.internal.operate.JobOperateAPIImpl;
+import 
org.apache.shardingsphere.elasticjob.lite.lifecycle.internal.settings.JobConfigurationAPIImpl;
+import org.apache.shardingsphere.elasticjob.reg.base.CoordinatorRegistryCenter;
+import 
org.apache.shardingsphere.elasticjob.reg.zookeeper.ZookeeperRegistryCenter;
+import org.apache.shardingsphere.infra.config.mode.ModeConfiguration;
+import 
org.apache.shardingsphere.infra.config.props.temporary.TemporaryConfigurationProperties;
+import 
org.apache.shardingsphere.infra.config.props.temporary.TemporaryConfigurationPropertyKey;
+import org.apache.shardingsphere.infra.util.props.PropertiesBuilder;
+import org.apache.shardingsphere.infra.util.props.PropertiesBuilder.Property;
 import org.apache.shardingsphere.mode.manager.ContextManager;
+import 
org.apache.shardingsphere.mode.repository.cluster.ClusterPersistRepositoryConfiguration;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.MockedConstruction;
 import org.mockito.internal.configuration.plugins.Plugins;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockConstruction;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 @ExtendWith(MockitoExtension.class)
 class StatisticsCollectJobWorkerTest {
     
+    private static AtomicBoolean workerInitialized = new AtomicBoolean(false);
+    
     private StatisticsCollectJobWorker jobWorker;
     
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private ContextManager contextManager;
     
     @BeforeEach
+    @SneakyThrows(ReflectiveOperationException.class)
     void setUp() {
+        workerInitialized = (AtomicBoolean) 
Plugins.getMemberAccessor().get(StatisticsCollectJobWorker.class.getDeclaredField("WORKER_INITIALIZED"),
 StatisticsCollectJobWorker.class);
         jobWorker = new StatisticsCollectJobWorker();
     }
     
+    @AfterEach
+    void tearDown() {
+        workerInitialized.set(false);
+        setStaticField("scheduleJobBootstrap", null);
+        setStaticField("contextManager", null);
+        setStaticField("registryCenter", null);
+    }
+    
+    @SneakyThrows(ReflectiveOperationException.class)
+    private void setStaticField(final String fieldName, final Object value) {
+        
Plugins.getMemberAccessor().set(StatisticsCollectJobWorker.class.getDeclaredField(fieldName),
 StatisticsCollectJobWorker.class, value);
+    }
+    
     @Test
     void assertInitializeTwice() {
         jobWorker.initialize(contextManager);
@@ -53,9 +96,58 @@ class StatisticsCollectJobWorkerTest {
     }
     
     @Test
-    void assertInitializeWithNotZooKeeperRepository() {
-        jobWorker.initialize(contextManager);
-        assertNull(getScheduleJobBootstrap());
+    void assertInitializeWithZooKeeperRepository() {
+        Properties repositoryProps = PropertiesBuilder.build(new 
Property("retryIntervalMilliseconds", 200),
+                new Property("maxRetries", 4), new 
Property("timeToLiveSeconds", 5), new Property("operationTimeoutMilliseconds", 
800), new Property("digest", "digest"));
+        
when(contextManager.getComputeNodeInstanceContext().getModeConfiguration()).thenReturn(
+                new ModeConfiguration("Cluster", new 
ClusterPersistRepositoryConfiguration("ZooKeeper", "namespace", 
"127.0.0.1:2181", repositoryProps)));
+        
when(contextManager.getMetaDataContexts().getMetaData().getTemporaryProps()).thenReturn(
+                new 
TemporaryConfigurationProperties(PropertiesBuilder.build(new 
Property(TemporaryConfigurationPropertyKey.PROXY_META_DATA_COLLECTOR_CRON.getKey(),
 "0 0/5 * * * ?"))));
+        AtomicReference<JobConfiguration> jobConfigRef = new 
AtomicReference<>();
+        try (
+                MockedConstruction<ZookeeperRegistryCenter> 
registryCenterConstruction = mockConstruction(ZookeeperRegistryCenter.class);
+                MockedConstruction<ScheduleJobBootstrap> 
scheduleJobBootstrapConstruction = mockConstruction(ScheduleJobBootstrap.class,
+                        (mock, context) -> jobConfigRef.set((JobConfiguration) 
context.arguments().get(2)));
+                MockedConstruction<JobOperateAPIImpl> 
jobOperateAPIConstruction = mockConstruction(JobOperateAPIImpl.class)) {
+            jobWorker.initialize(contextManager);
+            verify(registryCenterConstruction.constructed().get(0)).init();
+            
verify(scheduleJobBootstrapConstruction.constructed().get(0)).schedule();
+            assertThat(jobConfigRef.get().getCron(), is("0 0/5 * * * ?"));
+            
verify(jobOperateAPIConstruction.constructed().get(0)).trigger("statistics-collect");
+        }
+    }
+    
+    @Test
+    void assertInitializeWithZooKeeperRepositoryUsingDefaultConfiguration() {
+        Properties repositoryProps = PropertiesBuilder.build(new 
Property("timeToLiveSeconds", 0), new Property("operationTimeoutMilliseconds", 
0));
+        
when(contextManager.getComputeNodeInstanceContext().getModeConfiguration()).thenReturn(
+                new ModeConfiguration("Cluster", new 
ClusterPersistRepositoryConfiguration("ZooKeeper", "namespace", 
"127.0.0.1:2181", repositoryProps)));
+        
when(contextManager.getMetaDataContexts().getMetaData().getTemporaryProps()).thenReturn(new
 TemporaryConfigurationProperties(new Properties()));
+        try (
+                MockedConstruction<ZookeeperRegistryCenter> 
registryCenterConstruction = mockConstruction(ZookeeperRegistryCenter.class);
+                MockedConstruction<ScheduleJobBootstrap> 
scheduleJobBootstrapConstruction = mockConstruction(ScheduleJobBootstrap.class);
+                MockedConstruction<JobOperateAPIImpl> 
jobOperateAPIConstruction = mockConstruction(JobOperateAPIImpl.class)) {
+            jobWorker.initialize(contextManager);
+            verify(registryCenterConstruction.constructed().get(0)).init();
+            
verify(scheduleJobBootstrapConstruction.constructed().get(0)).schedule();
+            
verify(jobOperateAPIConstruction.constructed().get(0)).trigger("statistics-collect");
+        }
+    }
+    
+    @Test
+    void assertInitializeWithZooKeeperRepositoryUsingDefaultValues() {
+        
when(contextManager.getComputeNodeInstanceContext().getModeConfiguration()).thenReturn(
+                new ModeConfiguration("Cluster", new 
ClusterPersistRepositoryConfiguration("ZooKeeper", "namespace", 
"127.0.0.1:2181", new Properties())));
+        
when(contextManager.getMetaDataContexts().getMetaData().getTemporaryProps()).thenReturn(new
 TemporaryConfigurationProperties(new Properties()));
+        try (
+                MockedConstruction<ZookeeperRegistryCenter> 
registryCenterConstruction = mockConstruction(ZookeeperRegistryCenter.class);
+                MockedConstruction<ScheduleJobBootstrap> 
scheduleJobBootstrapConstruction = mockConstruction(ScheduleJobBootstrap.class);
+                MockedConstruction<JobOperateAPIImpl> 
jobOperateAPIConstruction = mockConstruction(JobOperateAPIImpl.class)) {
+            jobWorker.initialize(contextManager);
+            verify(registryCenterConstruction.constructed().get(0)).init();
+            
verify(scheduleJobBootstrapConstruction.constructed().get(0)).schedule();
+            
verify(jobOperateAPIConstruction.constructed().get(0)).trigger("statistics-collect");
+        }
     }
     
     @Test
@@ -63,6 +155,40 @@ class StatisticsCollectJobWorkerTest {
         assertDoesNotThrow(() -> jobWorker.updateJobConfiguration());
     }
     
+    @Test
+    void assertUpdateJobConfiguration() {
+        setStaticField("contextManager", contextManager);
+        CoordinatorRegistryCenter registryCenter = 
mock(CoordinatorRegistryCenter.class);
+        setStaticField("registryCenter", registryCenter);
+        
when(contextManager.getMetaDataContexts().getMetaData().getTemporaryProps()).thenReturn(
+                new 
TemporaryConfigurationProperties(PropertiesBuilder.build(new 
Property(TemporaryConfigurationPropertyKey.PROXY_META_DATA_COLLECTOR_CRON.getKey(),
 "invalid"))));
+        AtomicReference<Object> constructorRegistryCenter = new 
AtomicReference<>();
+        try (
+                MockedConstruction<JobConfigurationAPIImpl> 
jobConfigurationAPIConstruction = 
mockConstruction(JobConfigurationAPIImpl.class,
+                        (mock, context) -> 
constructorRegistryCenter.set(context.arguments().get(0)))) {
+            jobWorker.updateJobConfiguration();
+            assertThat(constructorRegistryCenter.get(), is(registryCenter));
+            ArgumentCaptor<JobConfigurationPOJO> argumentCaptor = 
ArgumentCaptor.forClass(JobConfigurationPOJO.class);
+            
verify(jobConfigurationAPIConstruction.constructed().get(0)).updateJobConfiguration(argumentCaptor.capture());
+            JobConfiguration jobConfiguration = 
argumentCaptor.getValue().toJobConfiguration();
+            assertThat(jobConfiguration.getCron(), 
is(TemporaryConfigurationPropertyKey.PROXY_META_DATA_COLLECTOR_CRON.getDefaultValue()));
+        }
+    }
+    
+    @Test
+    void assertUpdateJobConfigurationWithException() {
+        setStaticField("contextManager", contextManager);
+        setStaticField("registryCenter", 
mock(CoordinatorRegistryCenter.class));
+        
when(contextManager.getMetaDataContexts().getMetaData().getTemporaryProps()).thenReturn(
+                new 
TemporaryConfigurationProperties(PropertiesBuilder.build(new 
Property(TemporaryConfigurationPropertyKey.PROXY_META_DATA_COLLECTOR_CRON.getKey(),
 "0 0/2 * * * ?"))));
+        try (
+                MockedConstruction<JobConfigurationAPIImpl> 
jobConfigurationAPIConstruction = 
mockConstruction(JobConfigurationAPIImpl.class,
+                        (mock, context) -> 
doThrow(RuntimeException.class).when(mock).updateJobConfiguration(any(JobConfigurationPOJO.class))))
 {
+            assertDoesNotThrow(() -> jobWorker.updateJobConfiguration());
+            
verify(jobConfigurationAPIConstruction.constructed().get(0)).updateJobConfiguration(any(JobConfigurationPOJO.class));
+        }
+    }
+    
     @Test
     void assertDestroy() {
         jobWorker.destroy();
@@ -70,6 +196,16 @@ class StatisticsCollectJobWorkerTest {
         assertNull(getScheduleJobBootstrap());
     }
     
+    @Test
+    void assertDestroyWhenInitialized() {
+        ScheduleJobBootstrap scheduleJobBootstrap = 
mock(ScheduleJobBootstrap.class);
+        setStaticField("scheduleJobBootstrap", scheduleJobBootstrap);
+        workerInitialized.set(true);
+        jobWorker.destroy();
+        verify(scheduleJobBootstrap).shutdown();
+        assertNull(getScheduleJobBootstrap());
+    }
+    
     @SneakyThrows(ReflectiveOperationException.class)
     private ScheduleJobBootstrap getScheduleJobBootstrap() {
         return (ScheduleJobBootstrap) 
Plugins.getMemberAccessor().get(StatisticsCollectJobWorker.class.getDeclaredField("scheduleJobBootstrap"),
 StatisticsCollectJobWorker.class);

Reply via email to