From 494e4755799af13b2428d5405df474d0930bdbce Mon Sep 17 00:00:00 2001 From: Anastasia Kostryukova Date: Mon, 14 Jul 2025 15:41:52 +0300 Subject: [PATCH 1/3] HDDS-12000. Add unit tests --- .../safemode/TestECContainerSafeModeRule.java | 137 ++++++++++++++++++ .../TestRatisContainerSafeModeRule.java | 131 +++++++++++++++++ 2 files changed, 268 insertions(+) create mode 100644 hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestECContainerSafeModeRule.java create mode 100644 hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestRatisContainerSafeModeRule.java 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 000000000000..8fc7dc269230 --- /dev/null +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestECContainerSafeModeRule.java @@ -0,0 +1,137 @@ +/* + * 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.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +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() { + ContainerInfo container1 = mockECContainer(LifeCycleState.CLOSED, 1L); + ContainerInfo container2 = mockECContainer(LifeCycleState.OPEN, 2L); + + List containers = new ArrayList<>(); + containers.add(container1); + containers.add(container2); + + when(containerManager.getContainers(ReplicationType.EC)).thenReturn(containers); + + rule.refresh(false); + + assertEquals(0.0, rule.getCurrentContainerThreshold()); + } + + @ParameterizedTest + @EnumSource(value = LifeCycleState.class, names = {"OPEN", "CLOSED"}) + public void testValidateReturnsTrueAndFalse(LifeCycleState state) { + ContainerInfo container = mockECContainer(state, 1L); + List containers = new ArrayList<>(); + containers.add(container); + + when(containerManager.getContainers(ReplicationType.EC)).thenReturn(containers); + + boolean expected = state != LifeCycleState.CLOSED; + assertEquals(expected, rule.validate()); + } + + @Test + public void testProcessECContainer() { + long containerId = 123L; + ContainerInfo container = mockECContainer(LifeCycleState.CLOSED, containerId); + List containers = new ArrayList<>(); + containers.add(container); + + when(containerManager.getContainers(ReplicationType.EC)).thenReturn(containers); + rule.refresh(true); + + assertEquals(0.0, rule.getCurrentContainerThreshold()); + + ContainerReplicaProto replica = mock(ContainerReplicaProto.class); + List 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()); + } + + private static ContainerInfo mockECContainer(LifeCycleState cycleState, long containerID) { + ContainerInfo container = mock(ContainerInfo.class); + when(container.getReplicationType()).thenReturn(ReplicationType.EC); + when(container.getState()).thenReturn(cycleState); + 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 000000000000..933f9d371afe --- /dev/null +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestRatisContainerSafeModeRule.java @@ -0,0 +1,131 @@ +/* + * 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.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +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() { + ContainerInfo container1 = mockRatisContainer(LifeCycleState.CLOSED, 1L); + ContainerInfo container2 = mockRatisContainer(LifeCycleState.OPEN, 2L); + List containers = new ArrayList<>(); + containers.add(container1); + containers.add(container2); + + when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(containers); + + rule.refresh(false); + + assertEquals(0.0, rule.getCurrentContainerThreshold()); + } + + @ParameterizedTest + @EnumSource(value = LifeCycleState.class, names = {"OPEN", "CLOSED"}) + public void testValidateReturnsTrueAndFalse(LifeCycleState state) { + ContainerInfo container = mockRatisContainer(state, 1L); + List containers = new ArrayList<>(); + containers.add(container); + + when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(containers); + + boolean expected = state != LifeCycleState.CLOSED; + assertEquals(expected, rule.validate()); + } + + @Test + public void testProcessRatisContainer() { + long containerId = 123L; + ContainerInfo container = mockRatisContainer(LifeCycleState.CLOSED, containerId); + List containers = new ArrayList<>(); + containers.add(container); + + when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(containers); + rule.refresh(true); + + assertEquals(0.0, rule.getCurrentContainerThreshold()); + + ContainerReplicaProto replica = mock(ContainerReplicaProto.class); + List 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()); + } + + private static ContainerInfo mockRatisContainer(LifeCycleState cycleState, long containerID) { + ContainerInfo container = mock(ContainerInfo.class); + when(container.getReplicationType()).thenReturn(ReplicationType.RATIS); + when(container.getState()).thenReturn(cycleState); + when(container.getContainerID()).thenReturn(containerID); + when(container.getNumberOfKeys()).thenReturn(1L); + return container; + } + +} From 9faab47ec101ccd8b5b0c239c0f98780e6bfcbe2 Mon Sep 17 00:00:00 2001 From: Anastasia Kostryukova Date: Mon, 21 Jul 2025 13:52:44 +0300 Subject: [PATCH 2/3] HDDS-12000. Add suggested unit tests --- .../safemode/TestECContainerSafeModeRule.java | 117 +++++++++++++++--- .../TestRatisContainerSafeModeRule.java | 110 +++++++++++++--- 2 files changed, 193 insertions(+), 34 deletions(-) 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 index 8fc7dc269230..fb0711aa0367 100644 --- 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 @@ -17,11 +17,14 @@ package org.apache.hadoop.hdds.scm.safemode; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.*; +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; @@ -68,12 +71,10 @@ public void setup() { @Test public void testRefreshInitializeECContainers() { - ContainerInfo container1 = mockECContainer(LifeCycleState.CLOSED, 1L); - ContainerInfo container2 = mockECContainer(LifeCycleState.OPEN, 2L); - - List containers = new ArrayList<>(); - containers.add(container1); - containers.add(container2); + List containers = Arrays.asList( + mockECContainer(LifeCycleState.CLOSED, 1L), + mockECContainer(LifeCycleState.OPEN, 2L) + ); when(containerManager.getContainers(ReplicationType.EC)).thenReturn(containers); @@ -83,15 +84,14 @@ public void testRefreshInitializeECContainers() { } @ParameterizedTest - @EnumSource(value = LifeCycleState.class, names = {"OPEN", "CLOSED"}) - public void testValidateReturnsTrueAndFalse(LifeCycleState state) { + @EnumSource(value = LifeCycleState.class, + names = {"OPEN", "CLOSING", "QUASI_CLOSED", "CLOSED", "DELETING", "DELETED", "RECOVERING"}) + public void testValidateReturnsTrueAndFalse(LifeCycleState state) { ContainerInfo container = mockECContainer(state, 1L); - List containers = new ArrayList<>(); - containers.add(container); - when(containerManager.getContainers(ReplicationType.EC)).thenReturn(containers); + when(containerManager.getContainers(ReplicationType.EC)).thenReturn(Collections.singletonList(container)); - boolean expected = state != LifeCycleState.CLOSED; + boolean expected = state != LifeCycleState.QUASI_CLOSED && state != LifeCycleState.CLOSED; assertEquals(expected, rule.validate()); } @@ -99,10 +99,8 @@ public void testValidateReturnsTrueAndFalse(LifeCycleState state) { public void testProcessECContainer() { long containerId = 123L; ContainerInfo container = mockECContainer(LifeCycleState.CLOSED, containerId); - List containers = new ArrayList<>(); - containers.add(container); - when(containerManager.getContainers(ReplicationType.EC)).thenReturn(containers); + when(containerManager.getContainers(ReplicationType.EC)).thenReturn(Collections.singletonList(container)); rule.refresh(true); assertEquals(0.0, rule.getCurrentContainerThreshold()); @@ -125,10 +123,93 @@ public void testProcessECContainer() { assertEquals(1.0, rule.getCurrentContainerThreshold()); } - private static ContainerInfo mockECContainer(LifeCycleState cycleState, long containerID) { + @Test + public void testAllContainersClosed() { + List 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 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(cycleState); + when(container.getState()).thenReturn(state); when(container.getContainerID()).thenReturn(containerID); when(container.containerID()).thenReturn(ContainerID.valueOf(containerID)); when(container.getNumberOfKeys()).thenReturn(1L); 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 index 933f9d371afe..3e68d3e947bc 100644 --- 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 @@ -17,11 +17,16 @@ package org.apache.hadoop.hdds.scm.safemode; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 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; @@ -32,6 +37,7 @@ 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.apache.ozone.test.tag.Unhealthy; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -66,11 +72,10 @@ public void setup() { @Test public void testRefreshInitializeRatisContainers() { - ContainerInfo container1 = mockRatisContainer(LifeCycleState.CLOSED, 1L); - ContainerInfo container2 = mockRatisContainer(LifeCycleState.OPEN, 2L); - List containers = new ArrayList<>(); - containers.add(container1); - containers.add(container2); + List containers = Arrays.asList( + mockRatisContainer(LifeCycleState.CLOSED, 1L), + mockRatisContainer(LifeCycleState.OPEN, 2L) + ); when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(containers); @@ -80,15 +85,14 @@ public void testRefreshInitializeRatisContainers() { } @ParameterizedTest - @EnumSource(value = LifeCycleState.class, names = {"OPEN", "CLOSED"}) - public void testValidateReturnsTrueAndFalse(LifeCycleState state) { + @EnumSource(value = LifeCycleState.class, + names = {"OPEN", "CLOSING", "QUASI_CLOSED", "CLOSED", "DELETING", "DELETED", "RECOVERING"}) + public void testValidateReturnsTrueAndFalse(LifeCycleState state) { ContainerInfo container = mockRatisContainer(state, 1L); - List containers = new ArrayList<>(); - containers.add(container); - when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(containers); + when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(Collections.singletonList(container)); - boolean expected = state != LifeCycleState.CLOSED; + boolean expected = state != LifeCycleState.QUASI_CLOSED && state != LifeCycleState.CLOSED; assertEquals(expected, rule.validate()); } @@ -96,10 +100,8 @@ public void testValidateReturnsTrueAndFalse(LifeCycleState state) { public void testProcessRatisContainer() { long containerId = 123L; ContainerInfo container = mockRatisContainer(LifeCycleState.CLOSED, containerId); - List containers = new ArrayList<>(); - containers.add(container); - when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(containers); + when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(Collections.singletonList(container)); rule.refresh(true); assertEquals(0.0, rule.getCurrentContainerThreshold()); @@ -119,10 +121,86 @@ public void testProcessRatisContainer() { assertEquals(1.0, rule.getCurrentContainerThreshold()); } - private static ContainerInfo mockRatisContainer(LifeCycleState cycleState, long containerID) { + @Test + public void testAllContainersClosed() { + List 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 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(cycleState); + when(container.getState()).thenReturn(state); when(container.getContainerID()).thenReturn(containerID); when(container.getNumberOfKeys()).thenReturn(1L); return container; From 5f8167b429bec4254a8121a28b05effa7f899633 Mon Sep 17 00:00:00 2001 From: Anastasia Kostryukova Date: Mon, 21 Jul 2025 16:10:34 +0300 Subject: [PATCH 3/3] HDDS-12000. Fix checkstyle error --- .../hadoop/hdds/scm/safemode/TestECContainerSafeModeRule.java | 3 ++- .../hdds/scm/safemode/TestRatisContainerSafeModeRule.java | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) 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 index fb0711aa0367..aceff644ec30 100644 --- 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 @@ -17,7 +17,8 @@ package org.apache.hadoop.hdds.scm.safemode; -import static org.junit.jupiter.api.Assertions.*; +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; 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 index 3e68d3e947bc..56a1e1e83235 100644 --- 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 @@ -17,7 +17,6 @@ package org.apache.hadoop.hdds.scm.safemode; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -37,7 +36,6 @@ 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.apache.ozone.test.tag.Unhealthy; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest;