Repository: Normation/rudder-plugins
Pull request #814: Fixes #26686: The ValidationNeeded class doesn't have a test class
Author: login: skaerg
Created: 2025-04-04T13:12:01Z | Merged: 2025-04-09T08:48:08Z

https://issues.rudder.io/issues/26686


Diff:
diff --git a/change-validation/src/main/scala/com/normation/plugins/changevalidation/ValidationNeeded.scala b/change-validation/src/main/scala/com/normation/plugins/changevalidation/ValidationNeeded.scala
index b4a725c19..3e32e9f00 100644
--- a/change-validation/src/main/scala/com/normation/plugins/changevalidation/ValidationNeeded.scala
+++ b/change-validation/src/main/scala/com/normation/plugins/changevalidation/ValidationNeeded.scala
@@ -1,3 +1,40 @@
+/*
+ *************************************************************************************
+ * Copyright 2025 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder.  If not, see <http://www.gnu.org/licenses/>.
+
+ *
+ *************************************************************************************
+ */
+
 package com.normation.plugins.changevalidation
 
 import com.normation.box.*
diff --git a/change-validation/src/test/scala/com/normation/plugins/changevalidation/ValidationNeededTest.scala b/change-validation/src/test/scala/com/normation/plugins/changevalidation/ValidationNeededTest.scala
new file mode 100644
index 000000000..0b1eb15b0
--- /dev/null
+++ b/change-validation/src/test/scala/com/normation/plugins/changevalidation/ValidationNeededTest.scala
@@ -0,0 +1,169 @@
+/*
+ *************************************************************************************
+ * Copyright 2025 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder.  If not, see <http://www.gnu.org/licenses/>.
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.plugins.changevalidation
+
+import com.normation.cfclerk.domain.SectionSpec
+import com.normation.cfclerk.domain.TechniqueName
+import com.normation.eventlog.EventActor
+import com.normation.rudder.MockDirectives
+import com.normation.rudder.MockGitConfigRepo
+import com.normation.rudder.MockGlobalParam
+import com.normation.rudder.MockNodeGroups
+import com.normation.rudder.MockNodes
+import com.normation.rudder.MockRules
+import com.normation.rudder.MockTechniques
+import com.normation.rudder.domain.policies.ActiveTechniqueId
+import com.normation.rudder.domain.policies.GroupTarget
+import com.normation.rudder.domain.policies.SimpleTarget
+import com.normation.rudder.services.workflows.DGModAction
+import com.normation.rudder.services.workflows.DirectiveChangeRequest
+import com.normation.rudder.services.workflows.GlobalParamChangeRequest
+import com.normation.rudder.services.workflows.GlobalParamModAction
+import com.normation.rudder.services.workflows.NodeGroupChangeRequest
+import com.normation.rudder.services.workflows.RuleChangeRequest
+import com.normation.rudder.services.workflows.RuleModAction
+import net.liftweb.common.Full
+import org.junit.runner.RunWith
+import org.specs2.mutable.Specification
+import org.specs2.runner.JUnitRunner
+import zio.syntax.ToZio
+
+@RunWith(classOf[JUnitRunner])
+class ValidationNeededTest extends Specification {
+
+  val mockNodes:      MockNodes      = new MockNodes()
+  val mockNodeGroups: MockNodeGroups = new MockNodeGroups(mockNodes, new MockGlobalParam())
+  val actor:          EventActor     = EventActor("myActor")
+  val mockRules:      MockRules      = new MockRules()
+
+  val nodeGrpValNdd = new NodeGroupValidationNeeded(
+    () => Set[SimpleTarget](GroupTarget(mockNodeGroups.g0.id)).succeed,
+    null,
+    mockRules.ruleRepo,
+    mockNodeGroups.groupsRepo,
+    mockNodes.nodeFactRepo
+  )
+
+  "ValidationNeeded" should {
+
+    "always validate a modification in a GlobalParam" in {
+      val globalParamChangeReq = GlobalParamChangeRequest(GlobalParamModAction.Create, None)
+      nodeGrpValNdd.forGlobalParam(actor, globalParamChangeReq) must beEqualTo(Full(true))
+    }
+
+    "validate a modification in a node group if that group is supervised" in {
+      val nodeGrp          = mockNodeGroups.g0
+      val nodeGrpChangeReq = NodeGroupChangeRequest(DGModAction.CreateSolo, nodeGrp, None, None)
+      nodeGrpValNdd.forNodeGroup(actor, nodeGrpChangeReq) must beEqualTo(Full(true))
+    }
+
+    "not validate a modification in a node group if that group is not supervised" in {
+      val nodeGrp          = mockNodeGroups.g1
+      val nodeGrpChangeReq = NodeGroupChangeRequest(DGModAction.CreateSolo, nodeGrp, None, None)
+      nodeGrpValNdd.forNodeGroup(actor, nodeGrpChangeReq) must beEqualTo(Full(false))
+    }
+
+    "if a modification concerns a rule that" in {
+
+      "targets a supervised node, the modification must be validated" in {
+        // defaultRule targets all nodes
+        val rule = mockRules.rules.defaultRule
+
+        val ruleChangeReq = RuleChangeRequest(
+          RuleModAction.Create,
+          rule,
+          None
+        )
+        nodeGrpValNdd.forRule(actor, ruleChangeReq) must beEqualTo(Full(true))
+      }
+
+      "doesn't target a supervised node, the modification mustn't be validated" in {
+        // copyGitFileRule targets the nodeGroup g1 which is empty
+        val rule = mockRules.rules.copyGitFileRule
+
+        val ruleChangeReq = RuleChangeRequest(
+          RuleModAction.Create,
+          rule,
+          None
+        )
+        nodeGrpValNdd.forRule(actor, ruleChangeReq) must beEqualTo(Full(false))
+      }
+    }
+
+    val mockGitRepo    = new MockGitConfigRepo("")
+    val mockTechniques = MockTechniques(mockGitRepo)
+    val mockDirectives = new MockDirectives(mockTechniques)
+    val sectionSpec    = SectionSpec("ROOT")
+    val technique      = mockDirectives.directives.commonTechnique
+
+    "validate a modification in a directive if any rule that uses it requires validation" in {
+
+      val dirChangeReq = DirectiveChangeRequest(
+        DGModAction.Delete,
+        TechniqueName(technique.name),
+        ActiveTechniqueId("myId"),
+        sectionSpec,
+        mockDirectives.directives.pkgDirective, // used by defaultRule
+        Some(mockDirectives.directives.commonDirective),
+        List(),
+        List(mockRules.rules.defaultRule)       // targets all nodes, including node group g0
+      )
+
+      nodeGrpValNdd.forDirective(actor, dirChangeReq) must beEqualTo(Full(true))
+    }
+
+    "not validate a modification in a directive if no rule that uses it requires validation" in {
+
+      val dirChangeReq = DirectiveChangeRequest(
+        DGModAction.Delete,
+        TechniqueName(technique.name),
+        ActiveTechniqueId("myId"),
+        sectionSpec,
+        mockDirectives.directives.gvdDirective1, // unused by copyGitFileRule
+        None,
+        List(),
+        List(mockRules.rules.copyGitFileRule)    // targets node group g1 which is empty
+
+      )
+
+      nodeGrpValNdd.forDirective(actor, dirChangeReq) must beEqualTo(Full(false))
+    }
+
+  }
+
+}


Reviews:
user: login: clarktsiory
body: Having tests is a relief !
There are some suggested changes
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T13:34:36Z
user: login: clarktsiory
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T13:46:01Z
user: login: clarktsiory
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T13:52:04Z
user: login: clarktsiory
body: Nice PR :+1:
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T15:05:44Z

Review comments:
diff_hunk: @@ -0,0 +1,162 @@
+/*
+ *************************************************************************************
+ * Copyright 2025 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder.  If not, see <http://www.gnu.org/licenses/>.
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.plugins.changevalidation
+
+import com.normation.cfclerk.domain.{SectionSpec, TechniqueName}
+import com.normation.errors.IOResult
+import com.normation.eventlog.EventActor
+import com.normation.rudder.domain.policies.{ActiveTechniqueId, GroupTarget}
+import com.normation.rudder.{MockDirectives, MockGitConfigRepo, MockGlobalParam, MockNodeGroups, MockNodes, MockRules, MockTechniques}
+import com.normation.rudder.services.workflows.{DGModAction, DirectiveChangeRequest, GlobalParamChangeRequest, GlobalParamModAction, NodeGroupChangeRequest, RuleChangeRequest, RuleModAction}
+import net.liftweb.common.Full
+import org.junit.runner.RunWith
+import org.specs2.mutable.Specification
+import org.specs2.runner.JUnitRunner
+
+
+@RunWith(classOf[JUnitRunner])
+class ValidationNeededTest extends Specification {
+
+  val mockGlobalParam: MockGlobalParam = new MockGlobalParam()
+  val mockNodes: MockNodes = new MockNodes()
+  val mockNodeGroups: MockNodeGroups = new MockNodeGroups(mockNodes, mockGlobalParam)
+  val actor: EventActor = EventActor("myActor")
+  val mockRules = new MockRules()
+
+  val nodeGrpValNdd = new NodeGroupValidationNeeded(
+    () => IOResult.attempt(Set.apply(GroupTarget(mockNodeGroups.g0.id))),
+    null,
+    null,
+    mockNodeGroups.groupsRepo,
+    mockNodes.nodeFactRepo)
+
+  "ValidationNeeded" should {
+
+    "always validate a modification in a GlobalParam" in {
+      val globalParamChangeReq = GlobalParamChangeRequest(GlobalParamModAction.Create, None)
+      nodeGrpValNdd.forGlobalParam(actor, globalParamChangeReq) must beEqualTo(Full(true))
+    }
+
+    "validate a modification in a node group if that group is supervised" in {
+      val nodeGrp = mockNodeGroups.g0
+      val nodeGrpChangeReq = NodeGroupChangeRequest(DGModAction.CreateSolo, nodeGrp, None, None)
+      nodeGrpValNdd.forNodeGroup(actor, nodeGrpChangeReq) must beEqualTo(Full(true))
+    }
+
+    "not validate a modification in a node group if that group is not supervised" in {
+      val nodeGrp = mockNodeGroups.g1
+      val nodeGrpChangeReq = NodeGroupChangeRequest(DGModAction.CreateSolo, nodeGrp, None, None)
+      nodeGrpValNdd.forNodeGroup(actor, nodeGrpChangeReq) must beEqualTo(Full(false))
+    }
+
+
+    "if a modification concerns a rule that" in {
+
+      "targets a supervised node, the modification must be validated" in {
+        // defaultRule targets all nodes
+        val rule = mockRules.rules.defaultRule
+
+        val ruleChangeReq = RuleChangeRequest(
+          RuleModAction.Create, rule, None
+        )
+        nodeGrpValNdd.forRule(actor, ruleChangeReq) must beEqualTo(Full(true))
+      }
+
+      "doesn't target a supervised node, the modification mustn't be validated" in {
+        // copyGitFileRule targets the nodeGroup g1 which is empty
+        val rule = mockRules.rules.copyGitFileRule
+
+        val ruleChangeReq = RuleChangeRequest(
+          RuleModAction.Create, rule, None
+        )
+        nodeGrpValNdd.forRule(actor, ruleChangeReq) must beEqualTo(Full(false))
+      }
+    }
+
+
+    val nodeGrpValNdd2 = new NodeGroupValidationNeeded(
+      () => IOResult.attempt(Set.apply(GroupTarget(mockNodeGroups.g0.id))),
+      null,
+      mockRules.ruleRepo,
+      mockNodeGroups.groupsRepo,
+      mockNodes.nodeFactRepo)
+
+    val mockGitRepo = new MockGitConfigRepo("")
+    val mockTechniques = MockTechniques(mockGitRepo)
+    val mockDirectives = new MockDirectives(mockTechniques)
path: change-validation/src/test/scala/com/normation/plugins/changevalidation/ValidationNeededTest.scala
user: login: clarktsiory
body: ```suggestion
    val mockDirectives = new MockDirectives(MockTechniques(new MockGitConfigRepo("")))
```
since the dependency are only for `mockDirectives`, 
the scope of exposed variables is better when inlined : we know that they are only used in one place, and they are not exposed 
created_at: 2025-04-07T13:29:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,162 @@
+/*
+ *************************************************************************************
+ * Copyright 2025 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder.  If not, see <http://www.gnu.org/licenses/>.
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.plugins.changevalidation
+
+import com.normation.cfclerk.domain.{SectionSpec, TechniqueName}
+import com.normation.errors.IOResult
+import com.normation.eventlog.EventActor
+import com.normation.rudder.domain.policies.{ActiveTechniqueId, GroupTarget}
+import com.normation.rudder.{MockDirectives, MockGitConfigRepo, MockGlobalParam, MockNodeGroups, MockNodes, MockRules, MockTechniques}
+import com.normation.rudder.services.workflows.{DGModAction, DirectiveChangeRequest, GlobalParamChangeRequest, GlobalParamModAction, NodeGroupChangeRequest, RuleChangeRequest, RuleModAction}
+import net.liftweb.common.Full
+import org.junit.runner.RunWith
+import org.specs2.mutable.Specification
+import org.specs2.runner.JUnitRunner
+
+
+@RunWith(classOf[JUnitRunner])
+class ValidationNeededTest extends Specification {
+
+  val mockGlobalParam: MockGlobalParam = new MockGlobalParam()
+  val mockNodes: MockNodes = new MockNodes()
+  val mockNodeGroups: MockNodeGroups = new MockNodeGroups(mockNodes, mockGlobalParam)
+  val actor: EventActor = EventActor("myActor")
+  val mockRules = new MockRules()
+
+  val nodeGrpValNdd = new NodeGroupValidationNeeded(
+    () => IOResult.attempt(Set.apply(GroupTarget(mockNodeGroups.g0.id))),
+    null,
+    null,
+    mockNodeGroups.groupsRepo,
+    mockNodes.nodeFactRepo)
+
+  "ValidationNeeded" should {
+
+    "always validate a modification in a GlobalParam" in {
+      val globalParamChangeReq = GlobalParamChangeRequest(GlobalParamModAction.Create, None)
+      nodeGrpValNdd.forGlobalParam(actor, globalParamChangeReq) must beEqualTo(Full(true))
+    }
+
+    "validate a modification in a node group if that group is supervised" in {
+      val nodeGrp = mockNodeGroups.g0
+      val nodeGrpChangeReq = NodeGroupChangeRequest(DGModAction.CreateSolo, nodeGrp, None, None)
+      nodeGrpValNdd.forNodeGroup(actor, nodeGrpChangeReq) must beEqualTo(Full(true))
+    }
+
+    "not validate a modification in a node group if that group is not supervised" in {
+      val nodeGrp = mockNodeGroups.g1
+      val nodeGrpChangeReq = NodeGroupChangeRequest(DGModAction.CreateSolo, nodeGrp, None, None)
+      nodeGrpValNdd.forNodeGroup(actor, nodeGrpChangeReq) must beEqualTo(Full(false))
+    }
+
+
+    "if a modification concerns a rule that" in {
+
+      "targets a supervised node, the modification must be validated" in {
+        // defaultRule targets all nodes
+        val rule = mockRules.rules.defaultRule
+
+        val ruleChangeReq = RuleChangeRequest(
+          RuleModAction.Create, rule, None
+        )
+        nodeGrpValNdd.forRule(actor, ruleChangeReq) must beEqualTo(Full(true))
+      }
+
+      "doesn't target a supervised node, the modification mustn't be validated" in {
+        // copyGitFileRule targets the nodeGroup g1 which is empty
+        val rule = mockRules.rules.copyGitFileRule
+
+        val ruleChangeReq = RuleChangeRequest(
+          RuleModAction.Create, rule, None
+        )
+        nodeGrpValNdd.forRule(actor, ruleChangeReq) must beEqualTo(Full(false))
+      }
+    }
+
+
+    val nodeGrpValNdd2 = new NodeGroupValidationNeeded(
+      () => IOResult.attempt(Set.apply(GroupTarget(mockNodeGroups.g0.id))),
+      null,
+      mockRules.ruleRepo,
+      mockNodeGroups.groupsRepo,
+      mockNodes.nodeFactRepo)
path: change-validation/src/test/scala/com/normation/plugins/changevalidation/ValidationNeededTest.scala
user: login: clarktsiory
body: Is this not a duplicate of the existing `nodeGrpValNdd` ?
created_at: 2025-04-07T13:30:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,162 @@
+/*
+ *************************************************************************************
+ * Copyright 2025 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder.  If not, see <http://www.gnu.org/licenses/>.
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.plugins.changevalidation
+
+import com.normation.cfclerk.domain.{SectionSpec, TechniqueName}
+import com.normation.errors.IOResult
+import com.normation.eventlog.EventActor
+import com.normation.rudder.domain.policies.{ActiveTechniqueId, GroupTarget}
+import com.normation.rudder.{MockDirectives, MockGitConfigRepo, MockGlobalParam, MockNodeGroups, MockNodes, MockRules, MockTechniques}
+import com.normation.rudder.services.workflows.{DGModAction, DirectiveChangeRequest, GlobalParamChangeRequest, GlobalParamModAction, NodeGroupChangeRequest, RuleChangeRequest, RuleModAction}
path: change-validation/src/test/scala/com/normation/plugins/changevalidation/ValidationNeededTest.scala
user: login: clarktsiory
body: `mvn spotless:apply` or scalafmt should be run : imports should be exploded into multiple lines, and not grouped
created_at: 2025-04-07T13:30:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,162 @@
+/*
+ *************************************************************************************
+ * Copyright 2025 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder.  If not, see <http://www.gnu.org/licenses/>.
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.plugins.changevalidation
+
+import com.normation.cfclerk.domain.{SectionSpec, TechniqueName}
+import com.normation.errors.IOResult
+import com.normation.eventlog.EventActor
+import com.normation.rudder.domain.policies.{ActiveTechniqueId, GroupTarget}
+import com.normation.rudder.{MockDirectives, MockGitConfigRepo, MockGlobalParam, MockNodeGroups, MockNodes, MockRules, MockTechniques}
+import com.normation.rudder.services.workflows.{DGModAction, DirectiveChangeRequest, GlobalParamChangeRequest, GlobalParamModAction, NodeGroupChangeRequest, RuleChangeRequest, RuleModAction}
+import net.liftweb.common.Full
+import org.junit.runner.RunWith
+import org.specs2.mutable.Specification
+import org.specs2.runner.JUnitRunner
+
+
+@RunWith(classOf[JUnitRunner])
+class ValidationNeededTest extends Specification {
+
+  val mockGlobalParam: MockGlobalParam = new MockGlobalParam()
+  val mockNodes: MockNodes = new MockNodes()
+  val mockNodeGroups: MockNodeGroups = new MockNodeGroups(mockNodes, mockGlobalParam)
path: change-validation/src/test/scala/com/normation/plugins/changevalidation/ValidationNeededTest.scala
user: login: clarktsiory
body: there is scalafmt issue,
also as mentioned in another comment, we  could inline the mocks, as we neither need to use nor expose the global parameters and node mocks 
created_at: 2025-04-07T13:32:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,3 +1,40 @@
+/*
+ *************************************************************************************
+ * Copyright 2025 Normation SAS
+ *************************************************************************************
path: change-validation/src/main/scala/com/normation/plugins/changevalidation/ValidationNeeded.scala
user: login: clarktsiory
body: :+1: 
created_at: 2025-04-07T13:32:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,162 @@
+/*
+ *************************************************************************************
+ * Copyright 2025 Normation SAS
path: change-validation/src/test/scala/com/normation/plugins/changevalidation/ValidationNeededTest.scala
user: login: clarktsiory
body: :+1: 
created_at: 2025-04-07T13:46:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,162 @@
+/*
+ *************************************************************************************
+ * Copyright 2025 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder.  If not, see <http://www.gnu.org/licenses/>.
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.plugins.changevalidation
+
+import com.normation.cfclerk.domain.{SectionSpec, TechniqueName}
+import com.normation.errors.IOResult
+import com.normation.eventlog.EventActor
+import com.normation.rudder.domain.policies.{ActiveTechniqueId, GroupTarget}
+import com.normation.rudder.{MockDirectives, MockGitConfigRepo, MockGlobalParam, MockNodeGroups, MockNodes, MockRules, MockTechniques}
+import com.normation.rudder.services.workflows.{DGModAction, DirectiveChangeRequest, GlobalParamChangeRequest, GlobalParamModAction, NodeGroupChangeRequest, RuleChangeRequest, RuleModAction}
+import net.liftweb.common.Full
+import org.junit.runner.RunWith
+import org.specs2.mutable.Specification
+import org.specs2.runner.JUnitRunner
+
+
+@RunWith(classOf[JUnitRunner])
+class ValidationNeededTest extends Specification {
+
+  val mockGlobalParam: MockGlobalParam = new MockGlobalParam()
+  val mockNodes: MockNodes = new MockNodes()
+  val mockNodeGroups: MockNodeGroups = new MockNodeGroups(mockNodes, mockGlobalParam)
+  val actor: EventActor = EventActor("myActor")
+  val mockRules = new MockRules()
+
+  val nodeGrpValNdd = new NodeGroupValidationNeeded(
+    () => IOResult.attempt(Set.apply(GroupTarget(mockNodeGroups.g0.id))),
path: change-validation/src/test/scala/com/normation/plugins/changevalidation/ValidationNeededTest.scala
user: login: clarktsiory
body: `IOResult.attempt` is the way to declare a body that can fail.
Here with a pure value, you can use `X.succeed` (by importing `zio.syntax.*`)
created_at: 2025-04-07T13:52:04Z
author_association: CONTRIBUTOR

PR comments:
user: login: skaerg
created_at: 2025-04-07T11:51:42Z
body: PR updated with a new commit
tests for NodeGroupValidationNeeded.forDirective method
author_association: CONTRIBUTOR
user: login: skaerg
created_at: 2025-04-07T14:06:04Z
body: PR updated with a new commit
author_association: CONTRIBUTOR
user: login: skaerg
created_at: 2025-04-07T14:34:24Z
body: PR updated with a new commit
author_association: CONTRIBUTOR
user: login: skaerg
created_at: 2025-04-07T14:41:48Z
body: PR updated with a new commit
author_association: CONTRIBUTOR
user: login: Normation-Quality-Assistant
created_at: 2025-04-09T06:04:28Z
body: This PR is not mergeable to upper versions.
Since it is "Ready for merge" you must merge it by yourself using the following command:
`rudder-dev merge https://github.com/Normation/rudder-plugins/pull/814`
-- Your faithful QA
Kant merge: "In law a man is guilty when he violates the rights of others. In ethics he is guilty if he only thinks of doing so."
 (https://ci.normation.com/jenkins/job/merge-accepted-pr/99506/console)
author_association: CONTRIBUTOR
user: login: skaerg
created_at: 2025-04-09T08:05:12Z
body: OK, squash merging this PR
author_association: CONTRIBUTOR
user: login: skaerg
created_at: 2025-04-09T08:23:57Z
body: Commit modified
author_association: CONTRIBUTOR
user: login: Normation-Quality-Assistant
created_at: 2025-04-09T08:24:33Z
body: This PR is not mergeable to upper versions.
Since it is "Ready for merge" you must merge it by yourself using the following command:
`rudder-dev merge https://github.com/Normation/rudder-plugins/pull/814`
-- Your faithful QA
Kant merge: "In law a man is guilty when he violates the rights of others. In ethics he is guilty if he only thinks of doing so."
 (https://ci.normation.com/jenkins/job/merge-accepted-pr/99518/console)
author_association: CONTRIBUTOR
user: login: skaerg
created_at: 2025-04-09T08:25:18Z
body: Commit modified
author_association: CONTRIBUTOR
user: login: skaerg
created_at: 2025-04-09T08:27:28Z
body: OK, merging this PR
author_association: CONTRIBUTOR
user: login: skaerg
created_at: 2025-04-09T08:29:25Z
body: OK, merging this PR
author_association: CONTRIBUTOR
user: login: clarktsiory
created_at: 2025-04-09T08:48:05Z
body: OK, merging this PR
author_association: CONTRIBUTOR