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

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


The following commit(s) were added to refs/heads/master by this push:
     new cc0a7bae7f2 HDDS-12000. Add unit test for RatisContainerSafeModeRule 
and ECContainerSafeModeRule (#8801)
cc0a7bae7f2 is described below

commit cc0a7bae7f2c0fa03633687cdf651dcf3b6202c6
Author: Anastasia Kostryukova <63952085+kosta...@users.noreply.github.com>
AuthorDate: Tue Jul 22 01:48:57 2025 +0300

    HDDS-12000. Add unit test for RatisContainerSafeModeRule and 
ECContainerSafeModeRule (#8801)
    
    Generated-by: GitHub Copilot
---
 .../scm/safemode/TestECContainerSafeModeRule.java  | 219 +++++++++++++++++++++
 .../safemode/TestRatisContainerSafeModeRule.java   | 207 +++++++++++++++++++
 2 files changed, 426 insertions(+)

diff --git 
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestECContainerSafeModeRule.java
 
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestECContainerSafeModeRule.java
new file mode 100644
index 00000000000..aceff644ec3
--- /dev/null
+++ 
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestECContainerSafeModeRule.java
@@ -0,0 +1,219 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hdds.scm.safemode;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import org.apache.hadoop.hdds.conf.ConfigurationSource;
+import org.apache.hadoop.hdds.protocol.DatanodeDetails;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos.LifeCycleState;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos.ReplicationType;
+import 
org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.ContainerReplicaProto;
+import 
org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.ContainerReportsProto;
+import org.apache.hadoop.hdds.scm.container.ContainerID;
+import org.apache.hadoop.hdds.scm.container.ContainerInfo;
+import org.apache.hadoop.hdds.scm.container.ContainerManager;
+import 
org.apache.hadoop.hdds.scm.server.SCMDatanodeProtocolServer.NodeRegistrationContainerReport;
+import org.apache.hadoop.hdds.server.events.EventQueue;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+/**
+ * This class tests ECContainerSafeModeRule.
+ */
+public class TestECContainerSafeModeRule {
+  private ContainerManager containerManager;
+  private ConfigurationSource conf;
+  private EventQueue eventQueue;
+  private SCMSafeModeManager safeModeManager;
+  private SafeModeMetrics metrics;
+
+  private ECContainerSafeModeRule rule;
+
+  @BeforeEach
+  public void setup() {
+    containerManager = mock(ContainerManager.class);
+    conf = mock(ConfigurationSource.class);
+    eventQueue = mock(EventQueue.class);
+    safeModeManager = mock(SCMSafeModeManager.class);
+    metrics = mock(SafeModeMetrics.class);
+
+    when(safeModeManager.getSafeModeMetrics()).thenReturn(metrics);
+
+    rule = new ECContainerSafeModeRule(eventQueue, conf, containerManager, 
safeModeManager);
+    rule.setValidateBasedOnReportProcessing(false);
+  }
+
+  @Test
+  public void testRefreshInitializeECContainers() {
+    List<ContainerInfo> containers = Arrays.asList(
+        mockECContainer(LifeCycleState.CLOSED, 1L),
+        mockECContainer(LifeCycleState.OPEN, 2L)
+    );
+
+    
when(containerManager.getContainers(ReplicationType.EC)).thenReturn(containers);
+
+    rule.refresh(false);
+
+    assertEquals(0.0, rule.getCurrentContainerThreshold());
+  }
+
+  @ParameterizedTest
+  @EnumSource(value = LifeCycleState.class,
+      names = {"OPEN", "CLOSING", "QUASI_CLOSED", "CLOSED", "DELETING", 
"DELETED", "RECOVERING"})
+  public void testValidateReturnsTrueAndFalse(LifeCycleState state) {
+    ContainerInfo container = mockECContainer(state, 1L);
+
+    
when(containerManager.getContainers(ReplicationType.EC)).thenReturn(Collections.singletonList(container));
+
+    boolean expected = state != LifeCycleState.QUASI_CLOSED && state != 
LifeCycleState.CLOSED;
+    assertEquals(expected, rule.validate());
+  }
+
+  @Test
+  public void testProcessECContainer() {
+    long containerId = 123L;
+    ContainerInfo container = mockECContainer(LifeCycleState.CLOSED, 
containerId);
+
+    
when(containerManager.getContainers(ReplicationType.EC)).thenReturn(Collections.singletonList(container));
+    rule.refresh(true);
+
+    assertEquals(0.0, rule.getCurrentContainerThreshold());
+
+    ContainerReplicaProto replica = mock(ContainerReplicaProto.class);
+    List<ContainerReplicaProto> replicas = new ArrayList<>();
+    replicas.add(replica);
+    ContainerReportsProto containerReport = mock(ContainerReportsProto.class);
+    NodeRegistrationContainerReport report = 
mock(NodeRegistrationContainerReport.class);
+    DatanodeDetails datanodeDetails = mock(DatanodeDetails.class);
+
+    when(report.getDatanodeDetails()).thenReturn(datanodeDetails);
+    when(datanodeDetails.getUuid()).thenReturn(UUID.randomUUID());
+    when(replica.getContainerID()).thenReturn(containerId);
+    when(containerReport.getReportsList()).thenReturn(replicas);
+    when(report.getReport()).thenReturn(containerReport);
+
+    rule.process(report);
+
+    assertEquals(1.0, rule.getCurrentContainerThreshold());
+  }
+
+  @Test
+  public void testAllContainersClosed() {
+    List<ContainerInfo> closedContainers = Arrays.asList(
+        mockECContainer(LifeCycleState.CLOSED, 11L),
+        mockECContainer(LifeCycleState.CLOSED, 32L)
+    );
+
+    
when(containerManager.getContainers(ReplicationType.EC)).thenReturn(closedContainers);
+
+    rule.refresh(false);
+
+    assertEquals(0.0, rule.getCurrentContainerThreshold(), "Threshold should 
be 0.0 when all containers are closed");
+    assertFalse(rule.validate(), "Validate should return false when all 
containers are closed");
+  }
+
+  @Test
+  public void testAllContainersOpen() {
+    List<ContainerInfo> openContainers = Arrays.asList(
+        mockECContainer(LifeCycleState.OPEN, 11L),
+        mockECContainer(LifeCycleState.OPEN, 32L)
+    );
+
+    
when(containerManager.getContainers(ReplicationType.EC)).thenReturn(openContainers);
+
+    rule.refresh(false);
+
+    assertEquals(1.0, rule.getCurrentContainerThreshold(), "Threshold should 
be 1.0 when all containers are open");
+    assertTrue(rule.validate(), "Validate should return true when all 
containers are open");
+  }
+
+  @Test
+  public void testDuplicateContainerIdsInReports() {
+    long containerId = 42L;
+    ContainerInfo container = mockECContainer(LifeCycleState.OPEN, 
containerId);
+
+    
when(containerManager.getContainers(ReplicationType.EC)).thenReturn(Collections.singletonList(container));
+
+    rule.refresh(false);
+
+    ContainerReplicaProto replica = mock(ContainerReplicaProto.class);
+    ContainerReportsProto containerReport = mock(ContainerReportsProto.class);
+    NodeRegistrationContainerReport report = 
mock(NodeRegistrationContainerReport.class);
+    DatanodeDetails datanodeDetails = mock(DatanodeDetails.class);
+
+    when(replica.getContainerID()).thenReturn(containerId);
+    
when(containerReport.getReportsList()).thenReturn(Collections.singletonList(replica));
+    when(report.getReport()).thenReturn(containerReport);
+    when(report.getDatanodeDetails()).thenReturn(datanodeDetails);
+    when(datanodeDetails.getUuid()).thenReturn(UUID.randomUUID());
+
+    rule.process(report);
+    rule.process(report);
+
+    assertEquals(1.0, rule.getCurrentContainerThreshold(), "Duplicated 
containers should be counted only once");
+  }
+
+  @Test
+  public void testValidateBasedOnReportProcessingTrue() throws Exception {
+    rule.setValidateBasedOnReportProcessing(true);
+    long containerId = 1L;
+    ContainerInfo container = mockECContainer(LifeCycleState.OPEN, 
containerId);
+
+    
when(containerManager.getContainers(ReplicationType.EC)).thenReturn(Collections.singletonList(container));
+
+    rule.refresh(false);
+
+    ContainerReplicaProto replica = mock(ContainerReplicaProto.class);
+    ContainerReportsProto reportsProto = mock(ContainerReportsProto.class);
+    NodeRegistrationContainerReport report = 
mock(NodeRegistrationContainerReport.class);
+    DatanodeDetails datanodeDetails = mock(DatanodeDetails.class);
+
+    when(replica.getContainerID()).thenReturn(containerId);
+    
when(reportsProto.getReportsList()).thenReturn(Collections.singletonList(replica));
+    when(report.getReport()).thenReturn(reportsProto);
+    when(report.getDatanodeDetails()).thenReturn(datanodeDetails);
+    when(datanodeDetails.getUuid()).thenReturn(UUID.randomUUID());
+
+
+    rule.process(report);
+
+    assertTrue(rule.validate(), "Should validate based on reported 
containers");
+  }
+
+  private static ContainerInfo mockECContainer(LifeCycleState state, long 
containerID) {
+    ContainerInfo container = mock(ContainerInfo.class);
+    when(container.getReplicationType()).thenReturn(ReplicationType.EC);
+    when(container.getState()).thenReturn(state);
+    when(container.getContainerID()).thenReturn(containerID);
+    when(container.containerID()).thenReturn(ContainerID.valueOf(containerID));
+    when(container.getNumberOfKeys()).thenReturn(1L);
+    return container;
+  }
+}
diff --git 
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestRatisContainerSafeModeRule.java
 
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestRatisContainerSafeModeRule.java
new file mode 100644
index 00000000000..56a1e1e8323
--- /dev/null
+++ 
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestRatisContainerSafeModeRule.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hdds.scm.safemode;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.apache.hadoop.hdds.conf.ConfigurationSource;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos.LifeCycleState;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos.ReplicationType;
+import 
org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.ContainerReplicaProto;
+import 
org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.ContainerReportsProto;
+import org.apache.hadoop.hdds.scm.container.ContainerInfo;
+import org.apache.hadoop.hdds.scm.container.ContainerManager;
+import 
org.apache.hadoop.hdds.scm.server.SCMDatanodeProtocolServer.NodeRegistrationContainerReport;
+import org.apache.hadoop.hdds.server.events.EventQueue;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+/**
+ * This class tests RatisContainerSafeModeRule.
+ */
+public class TestRatisContainerSafeModeRule {
+
+  private ContainerManager containerManager;
+  private ConfigurationSource conf;
+  private EventQueue eventQueue;
+  private SCMSafeModeManager safeModeManager;
+  private SafeModeMetrics metrics;
+
+  private RatisContainerSafeModeRule rule;
+
+  @BeforeEach
+  public void setup() {
+    containerManager = mock(ContainerManager.class);
+    conf = mock(ConfigurationSource.class);
+    eventQueue = mock(EventQueue.class);
+    safeModeManager = mock(SCMSafeModeManager.class);
+    metrics = mock(SafeModeMetrics.class);
+
+    when(safeModeManager.getSafeModeMetrics()).thenReturn(metrics);
+
+    rule = new RatisContainerSafeModeRule(eventQueue, conf, containerManager, 
safeModeManager);
+    rule.setValidateBasedOnReportProcessing(false);
+  }
+
+  @Test
+  public void testRefreshInitializeRatisContainers() {
+    List<ContainerInfo> containers = Arrays.asList(
+        mockRatisContainer(LifeCycleState.CLOSED, 1L),
+        mockRatisContainer(LifeCycleState.OPEN, 2L)
+    );
+
+    
when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(containers);
+
+    rule.refresh(false);
+
+    assertEquals(0.0, rule.getCurrentContainerThreshold());
+  }
+
+  @ParameterizedTest
+  @EnumSource(value = LifeCycleState.class,
+      names = {"OPEN", "CLOSING", "QUASI_CLOSED", "CLOSED", "DELETING", 
"DELETED", "RECOVERING"})
+  public void testValidateReturnsTrueAndFalse(LifeCycleState state) {
+    ContainerInfo container = mockRatisContainer(state, 1L);
+
+    
when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(Collections.singletonList(container));
+
+    boolean expected = state != LifeCycleState.QUASI_CLOSED && state != 
LifeCycleState.CLOSED;
+    assertEquals(expected, rule.validate());
+  }
+
+  @Test
+  public void testProcessRatisContainer() {
+    long containerId = 123L;
+    ContainerInfo container = mockRatisContainer(LifeCycleState.CLOSED, 
containerId);
+
+    
when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(Collections.singletonList(container));
+    rule.refresh(true);
+
+    assertEquals(0.0, rule.getCurrentContainerThreshold());
+
+    ContainerReplicaProto replica = mock(ContainerReplicaProto.class);
+    List<ContainerReplicaProto> replicas = new ArrayList<>();
+    replicas.add(replica);
+    ContainerReportsProto containerReport = mock(ContainerReportsProto.class);
+    NodeRegistrationContainerReport report = 
mock(NodeRegistrationContainerReport.class);
+
+    when(replica.getContainerID()).thenReturn(containerId);
+    when(containerReport.getReportsList()).thenReturn(replicas);
+    when(report.getReport()).thenReturn(containerReport);
+
+    rule.process(report);
+
+    assertEquals(1.0, rule.getCurrentContainerThreshold());
+  }
+
+  @Test
+  public void testAllContainersClosed() {
+    List<ContainerInfo> closedContainers = Arrays.asList(
+        mockRatisContainer(LifeCycleState.CLOSED, 11L),
+        mockRatisContainer(LifeCycleState.CLOSED, 32L)
+    );
+
+    
when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(closedContainers);
+
+    rule.refresh(false);
+
+    assertEquals(0.0, rule.getCurrentContainerThreshold(), "Threshold should 
be 0.0 when all containers are closed");
+    assertFalse(rule.validate(), "Validate should return false when all 
containers are closed");
+  }
+
+  @Test
+  public void testAllContainersOpen() {
+    List<ContainerInfo> openContainers = Arrays.asList(
+        mockRatisContainer(LifeCycleState.OPEN, 11L),
+        mockRatisContainer(LifeCycleState.OPEN, 32L)
+    );
+
+    
when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(openContainers);
+
+    rule.refresh(false);
+
+    assertEquals(1.0, rule.getCurrentContainerThreshold(), "Threshold should 
be 1.0 when all containers are open");
+    assertTrue(rule.validate(), "Validate should return true when all 
containers are open");
+  }
+
+  @Test
+  public void testDuplicateContainerIdsInReports() {
+    long containerId = 42L;
+    ContainerInfo container = mockRatisContainer(LifeCycleState.OPEN, 
containerId);
+
+    
when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(Collections.singletonList(container));
+
+    rule.refresh(false);
+
+    ContainerReplicaProto replica = mock(ContainerReplicaProto.class);
+    ContainerReportsProto containerReport = mock(ContainerReportsProto.class);
+    NodeRegistrationContainerReport report = 
mock(NodeRegistrationContainerReport.class);
+
+    when(replica.getContainerID()).thenReturn(containerId);
+    
when(containerReport.getReportsList()).thenReturn(Collections.singletonList(replica));
+    when(report.getReport()).thenReturn(containerReport);
+
+    rule.process(report);
+    rule.process(report);
+
+    assertEquals(1.0, rule.getCurrentContainerThreshold(), "Duplicated 
containers should be counted only once");
+  }
+
+  @Test
+  public void testValidateBasedOnReportProcessingTrue() throws Exception {
+    rule.setValidateBasedOnReportProcessing(true);
+    long containerId = 1L;
+    ContainerInfo container = mockRatisContainer(LifeCycleState.OPEN, 
containerId);
+
+    
when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(Collections.singletonList(container));
+
+    rule.refresh(false);
+
+    ContainerReplicaProto replica = mock(ContainerReplicaProto.class);
+    ContainerReportsProto reportsProto = mock(ContainerReportsProto.class);
+    NodeRegistrationContainerReport report = 
mock(NodeRegistrationContainerReport.class);
+
+    when(replica.getContainerID()).thenReturn(containerId);
+    
when(reportsProto.getReportsList()).thenReturn(Collections.singletonList(replica));
+    when(report.getReport()).thenReturn(reportsProto);
+
+    rule.process(report);
+
+    assertTrue(rule.validate(), "Should validate based on reported 
containers");
+  }
+
+  private static ContainerInfo mockRatisContainer(LifeCycleState state, long 
containerID) {
+    ContainerInfo container = mock(ContainerInfo.class);
+    when(container.getReplicationType()).thenReturn(ReplicationType.RATIS);
+    when(container.getState()).thenReturn(state);
+    when(container.getContainerID()).thenReturn(containerID);
+    when(container.getNumberOfKeys()).thenReturn(1L);
+    return container;
+  }
+
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@ozone.apache.org
For additional commands, e-mail: commits-h...@ozone.apache.org

Reply via email to