DuplicateElementStrategyTest.java

/*
 * 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.maven.cling.invoker.mvnup.goals;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;

import eu.maveniverse.domtrip.Document;
import eu.maveniverse.domtrip.Element;
import org.apache.maven.cling.invoker.mvnup.UpgradeContext;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * Tests for {@link DuplicateElementStrategy}.
 */
@DisplayName("DuplicateElementStrategy")
class DuplicateElementStrategyTest {

    private DuplicateElementStrategy strategy;

    @BeforeEach
    void setUp() {
        strategy = new DuplicateElementStrategy();
    }

    private UpgradeContext createMockContext() {
        return TestUtils.createMockContext();
    }

    @Nested
    @DisplayName("Applicability")
    class ApplicabilityTests {

        @Test
        @DisplayName("should be applicable with default options")
        void shouldBeApplicableWithDefaults() {
            UpgradeContext context = createMockContext();
            assertTrue(strategy.isApplicable(context));
        }

        @Test
        @DisplayName("should be applicable when --model is true")
        void shouldBeApplicableWithModelTrue() {
            UpgradeContext context = TestUtils.createMockContext(TestUtils.createOptionsWithFixModel(true));
            assertTrue(strategy.isApplicable(context));
        }

        @Test
        @DisplayName("should not be applicable when --model is false")
        void shouldNotBeApplicableWithModelFalse() {
            UpgradeContext context = TestUtils.createMockContext(TestUtils.createOptionsWithFixModel(false));
            assertFalse(strategy.isApplicable(context));
        }

        @Test
        @DisplayName("should be applicable when --all is set")
        void shouldBeApplicableWithAll() {
            UpgradeContext context = TestUtils.createMockContext(TestUtils.createOptionsWithAll(true));
            assertTrue(strategy.isApplicable(context));
        }
    }

    @Nested
    @DisplayName("Duplicate Element Removal")
    class DuplicateElementRemovalTests {

        @Test
        @DisplayName("should remove duplicate artifactId keeping last occurrence")
        void shouldRemoveDuplicateArtifactId() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>first-name</artifactId>
                    <artifactId>second-name</artifactId>
                    <version>1.0.0</version>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(1, result.modifiedCount(), "Should have modified 1 POM");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("<artifactId>second-name</artifactId>"), "Should keep last artifactId");
            assertFalse(xml.contains("<artifactId>first-name</artifactId>"), "Should remove first artifactId");
        }

        @Test
        @DisplayName("should remove duplicate properties blocks keeping last")
        void shouldRemoveDuplicateProperties() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <properties>
                        <old.property>old-value</old.property>
                    </properties>
                    <properties>
                        <new.property>new-value</new.property>
                    </properties>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(1, result.modifiedCount(), "Should have modified 1 POM");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("<new.property>new-value</new.property>"), "Should keep last properties block");
            assertFalse(xml.contains("<old.property>old-value</old.property>"), "Should remove first properties block");
        }

        @Test
        @DisplayName("should remove duplicate version element keeping last")
        void shouldRemoveDuplicateVersion() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <version>2.0.0</version>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(1, result.modifiedCount(), "Should have modified 1 POM");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("<version>2.0.0</version>"), "Should keep last version");
            assertFalse(xml.contains("<version>1.0.0</version>"), "Should remove first version");
        }

        @Test
        @DisplayName("should remove duplicate groupId element keeping last")
        void shouldRemoveDuplicateGroupId() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>com.old</groupId>
                    <groupId>com.new</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(1, result.modifiedCount(), "Should have modified 1 POM");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("<groupId>com.new</groupId>"), "Should keep last groupId");
            assertFalse(xml.contains("<groupId>com.old</groupId>"), "Should remove first groupId");
        }

        @Test
        @DisplayName("should remove multiple different duplicate elements in same POM")
        void shouldRemoveMultipleDifferentDuplicates() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>com.old</groupId>
                    <groupId>com.new</groupId>
                    <artifactId>old-name</artifactId>
                    <artifactId>new-name</artifactId>
                    <version>1.0.0</version>
                    <version>2.0.0</version>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(1, result.modifiedCount(), "Should have modified 1 POM");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("<groupId>com.new</groupId>"), "Should keep last groupId");
            assertFalse(xml.contains("<groupId>com.old</groupId>"), "Should remove first groupId");
            assertTrue(xml.contains("<artifactId>new-name</artifactId>"), "Should keep last artifactId");
            assertFalse(xml.contains("<artifactId>old-name</artifactId>"), "Should remove first artifactId");
            assertTrue(xml.contains("<version>2.0.0</version>"), "Should keep last version");
            assertFalse(xml.contains("<version>1.0.0</version>"), "Should remove first version");
        }

        @Test
        @DisplayName("should not modify POM with no duplicates")
        void shouldNotModifyWhenNoDuplicates() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(0, result.modifiedCount(), "Should not have modified any POM");
        }
    }

    @Nested
    @DisplayName("Nested Duplicate Elements")
    class NestedDuplicateElementTests {

        @Test
        @DisplayName("should remove duplicate groupId inside a dependency element")
        void shouldRemoveDuplicateGroupIdInDependency() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <dependencies>
                        <dependency>
                            <groupId>old-group</groupId>
                            <groupId>new-group</groupId>
                            <artifactId>some-lib</artifactId>
                            <version>1.0</version>
                        </dependency>
                    </dependencies>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(1, result.modifiedCount(), "Should have modified 1 POM");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("<groupId>new-group</groupId>"), "Should keep last groupId in dependency");
            assertFalse(xml.contains("<groupId>old-group</groupId>"), "Should remove first groupId in dependency");
        }

        @Test
        @DisplayName("should remove duplicate version inside parent element")
        void shouldRemoveDuplicateVersionInParent() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <parent>
                        <groupId>com.example</groupId>
                        <artifactId>parent</artifactId>
                        <version>1.0.0</version>
                        <version>2.0.0</version>
                    </parent>
                    <artifactId>child</artifactId>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(1, result.modifiedCount(), "Should have modified 1 POM");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("<version>2.0.0</version>"), "Should keep last version in parent");
            assertFalse(xml.contains("<version>1.0.0</version>"), "Should remove first version in parent");
        }
    }

    @Nested
    @DisplayName("List Container Skipping")
    class ListContainerSkippingTests {

        @Test
        @DisplayName("should not remove multiple dependency elements in dependencies")
        void shouldNotRemoveDependenciesInList() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <dependencies>
                        <dependency>
                            <groupId>commons-io</groupId>
                            <artifactId>commons-io</artifactId>
                            <version>2.15.0</version>
                        </dependency>
                        <dependency>
                            <groupId>commons-lang</groupId>
                            <artifactId>commons-lang3</artifactId>
                            <version>3.14.0</version>
                        </dependency>
                    </dependencies>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(0, result.modifiedCount(), "Should not have modified any POM");

            Element deps = document.root().childElement("dependencies").orElse(null);
            assertNotNull(deps, "dependencies element should still exist");
            assertEquals(2, deps.childElements("dependency").count(), "Should still have 2 dependency elements");
        }

        @Test
        @DisplayName("should not remove multiple plugin elements in plugins")
        void shouldNotRemovePluginsInList() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <build>
                        <plugins>
                            <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-compiler-plugin</artifactId>
                                <version>3.13.0</version>
                            </plugin>
                            <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-surefire-plugin</artifactId>
                                <version>3.2.5</version>
                            </plugin>
                        </plugins>
                    </build>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(0, result.modifiedCount(), "Should not have modified any POM");

            Element plugins = document.root()
                    .childElement("build")
                    .orElseThrow()
                    .childElement("plugins")
                    .orElse(null);
            assertNotNull(plugins, "plugins element should still exist");
            assertEquals(2, plugins.childElements("plugin").count(), "Should still have 2 plugin elements");
        }

        @Test
        @DisplayName("should not remove multiple module elements in modules")
        void shouldNotRemoveModulesInList() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test-parent</artifactId>
                    <version>1.0.0</version>
                    <packaging>pom</packaging>
                    <modules>
                        <module>module-a</module>
                        <module>module-b</module>
                        <module>module-c</module>
                    </modules>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(0, result.modifiedCount(), "Should not have modified any POM");

            Element modules = document.root().childElement("modules").orElse(null);
            assertNotNull(modules, "modules element should still exist");
            assertEquals(3, modules.childElements("module").count(), "Should still have 3 module elements");
        }
    }

    @Nested
    @DisplayName("Plugin Configuration Skipping")
    class PluginConfigurationSkippingTests {

        @Test
        @DisplayName("should not remove multiple arg elements inside compilerArgs in configuration")
        void shouldNotRemoveCompilerArgsInConfiguration() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <build>
                        <plugins>
                            <plugin>
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-compiler-plugin</artifactId>
                                <configuration>
                                    <compilerArgs>
                                        <arg>-Xlint:all</arg>
                                        <arg>-Xlint:-processing</arg>
                                        <arg>-Xmaxwarns</arg>
                                        <arg>5</arg>
                                    </compilerArgs>
                                </configuration>
                            </plugin>
                        </plugins>
                    </build>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(0, result.modifiedCount(), "Should not have modified any POM");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("<arg>-Xlint:all</arg>"), "Should keep -Xlint:all arg");
            assertTrue(xml.contains("<arg>-Xlint:-processing</arg>"), "Should keep -Xlint:-processing arg");
            assertTrue(xml.contains("<arg>-Xmaxwarns</arg>"), "Should keep -Xmaxwarns arg");
            assertTrue(xml.contains("<arg>5</arg>"), "Should keep 5 arg");
        }

        @Test
        @DisplayName("should not remove elements inside execution configuration")
        void shouldNotRemoveElementsInsideExecutionConfiguration() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <build>
                        <plugins>
                            <plugin>
                                <groupId>org.codehaus.mojo</groupId>
                                <artifactId>exec-maven-plugin</artifactId>
                                <executions>
                                    <execution>
                                        <configuration>
                                            <arguments>
                                                <argument>-classpath</argument>
                                                <argument>${project.build.outputDirectory}</argument>
                                                <argument>com.example.Main</argument>
                                            </arguments>
                                        </configuration>
                                    </execution>
                                </executions>
                            </plugin>
                        </plugins>
                    </build>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(0, result.modifiedCount(), "Should not have modified any POM");

            String xml = DomUtils.toXml(document);
            // Count argument occurrences
            int count = xml.split("<argument>", -1).length - 1;
            assertEquals(3, count, "Should still have 3 argument elements");
        }

        @Test
        @DisplayName("should not remove checkstyle module elements inside configuration")
        void shouldNotRemoveCheckstyleModulesInConfiguration() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <build>
                        <plugins>
                            <plugin>
                                <artifactId>maven-checkstyle-plugin</artifactId>
                                <configuration>
                                    <checkstyleRules>
                                        <module name="Checker">
                                            <property name="charset" value="UTF-8" />
                                            <property name="severity" value="warning" />
                                            <module name="FileTabCharacter" />
                                            <module name="TreeWalker">
                                                <module name="AvoidStarImport" />
                                                <module name="NeedBraces" />
                                            </module>
                                        </module>
                                    </checkstyleRules>
                                </configuration>
                            </plugin>
                        </plugins>
                    </build>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(0, result.modifiedCount(), "Should not have modified any POM");

            String xml = DomUtils.toXml(document);
            // Count module occurrences (Checker, FileTabCharacter, TreeWalker, AvoidStarImport, NeedBraces = 5)
            int moduleCount = xml.split("<module ", -1).length - 1;
            assertEquals(5, moduleCount, "Should still have 5 module elements");
            // Count property occurrences (charset, severity = 2)
            int propertyCount = xml.split("<property ", -1).length - 1;
            assertEquals(2, propertyCount, "Should still have 2 property elements");
        }

        @Test
        @DisplayName("should still remove POM-level duplicates when configuration is present")
        void shouldStillRemovePomDuplicatesWithConfiguration() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>old-name</artifactId>
                    <artifactId>new-name</artifactId>
                    <version>1.0.0</version>
                    <build>
                        <plugins>
                            <plugin>
                                <artifactId>maven-compiler-plugin</artifactId>
                                <configuration>
                                    <compilerArgs>
                                        <arg>-Xlint:all</arg>
                                        <arg>-Xmaxwarns</arg>
                                        <arg>5</arg>
                                    </compilerArgs>
                                </configuration>
                            </plugin>
                        </plugins>
                    </build>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(1, result.modifiedCount(), "Should have modified 1 POM");

            String xml = DomUtils.toXml(document);
            // POM-level duplicate should be removed
            assertTrue(xml.contains("<artifactId>new-name</artifactId>"), "Should keep last artifactId");
            assertFalse(xml.contains("<artifactId>old-name</artifactId>"), "Should remove first artifactId");
            // Configuration args should be preserved
            assertTrue(xml.contains("<arg>-Xlint:all</arg>"), "Should keep -Xlint:all arg");
            assertTrue(xml.contains("<arg>-Xmaxwarns</arg>"), "Should keep -Xmaxwarns arg");
            assertTrue(xml.contains("<arg>5</arg>"), "Should keep 5 arg");
        }

        @Test
        @DisplayName("should not remove exclude elements inside inputExcludes in configuration")
        void shouldNotRemoveExcludesInConfiguration() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <build>
                        <plugins>
                            <plugin>
                                <artifactId>apache-rat-plugin</artifactId>
                                <configuration>
                                    <inputExcludes>
                                        <exclude>.github/**</exclude>
                                        <exclude>src/main/antlr4/Abnf.g4</exclude>
                                    </inputExcludes>
                                </configuration>
                            </plugin>
                        </plugins>
                    </build>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(0, result.modifiedCount(), "Should not have modified any POM");

            String xml = DomUtils.toXml(document);
            assertTrue(xml.contains("<exclude>.github/**</exclude>"), "Should keep first exclude");
            assertTrue(xml.contains("<exclude>src/main/antlr4/Abnf.g4</exclude>"), "Should keep second exclude");
        }

        @Test
        @DisplayName("should not remove pluginExecution elements inside lifecycleMappingMetadata in configuration")
        void shouldNotRemovePluginExecutionsInConfiguration() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <build>
                        <pluginManagement>
                            <plugins>
                                <plugin>
                                    <groupId>org.eclipse.m2e</groupId>
                                    <artifactId>lifecycle-mapping</artifactId>
                                    <configuration>
                                        <lifecycleMappingMetadata>
                                            <pluginExecutions>
                                                <pluginExecution>
                                                    <pluginExecutionFilter>
                                                        <groupId>org.apache.maven.plugins</groupId>
                                                        <artifactId>maven-dependency-plugin</artifactId>
                                                    </pluginExecutionFilter>
                                                </pluginExecution>
                                                <pluginExecution>
                                                    <pluginExecutionFilter>
                                                        <groupId>org.codehaus.mojo</groupId>
                                                        <artifactId>exec-maven-plugin</artifactId>
                                                    </pluginExecutionFilter>
                                                </pluginExecution>
                                            </pluginExecutions>
                                        </lifecycleMappingMetadata>
                                    </configuration>
                                </plugin>
                            </plugins>
                        </pluginManagement>
                    </build>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(0, result.modifiedCount(), "Should not have modified any POM");

            String xml = DomUtils.toXml(document);
            int count = xml.split("<pluginExecution>", -1).length - 1;
            assertEquals(2, count, "Should still have 2 pluginExecution elements");
        }
    }

    @Nested
    @DisplayName("Strategy Description")
    class StrategyDescriptionTests {

        @Test
        @DisplayName("should return non-null description")
        void shouldReturnNonNullDescription() {
            assertNotNull(strategy.getDescription(), "Description should not be null");
        }

        @Test
        @DisplayName("should return non-empty description")
        void shouldReturnNonEmptyDescription() {
            assertFalse(strategy.getDescription().isEmpty(), "Description should not be empty");
        }
    }

    @Nested
    @DisplayName("Error Handling")
    class ErrorHandlingTests {

        @Test
        @DisplayName("should return success with 0 modifications for POM with no duplicates")
        void shouldReturnSuccessWithZeroModifications() {
            String pomXml = """
                <?xml version="1.0" encoding="UTF-8"?>
                <project xmlns="http://maven.apache.org/POM/4.0.0">
                    <modelVersion>4.0.0</modelVersion>
                    <groupId>test</groupId>
                    <artifactId>test</artifactId>
                    <version>1.0.0</version>
                    <description>A simple project</description>
                    <properties>
                        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                    </properties>
                </project>
                """;

            Document document = Document.of(pomXml);
            Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

            UpgradeContext context = createMockContext();
            UpgradeResult result = strategy.doApply(context, pomMap);

            assertTrue(result.success(), "Strategy should succeed");
            assertEquals(0, result.modifiedCount(), "Should report 0 modifications");
            assertEquals(0, result.errorCount(), "Should report 0 errors");
        }
    }
}