ConsumerPomArtifactTransformerTest.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.internal.transformation.impl;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.maven.api.Constants;
import org.apache.maven.model.Model;
import org.apache.maven.model.v4.MavenStaxReader;
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.SessionData;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.deployment.DeployRequest;
import org.eclipse.aether.installation.InstallRequest;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.xmlunit.builder.DiffBuilder;
import org.xmlunit.diff.Diff;

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.when;

class ConsumerPomArtifactTransformerTest {

    @Test
    void transform() throws Exception {
        RepositorySystemSession systemSessionMock = Mockito.mock(RepositorySystemSession.class);
        SessionData sessionDataMock = Mockito.mock(SessionData.class);
        when(systemSessionMock.getData()).thenReturn(sessionDataMock);

        Path beforePomFile =
                Paths.get("src/test/resources/projects/transform/before.pom").toAbsolutePath();
        Path afterPomFile =
                Paths.get("src/test/resources/projects/transform/after.pom").toAbsolutePath();
        Path tempFile = Files.createTempFile("", ".pom");
        Files.delete(tempFile);
        try (InputStream expected = Files.newInputStream(beforePomFile)) {
            Model model = new Model(new MavenStaxReader().read(expected));
            MavenProject project = new MavenProject(model);
            project.setOriginalModel(model);
            ConsumerPomArtifactTransformer t = new ConsumerPomArtifactTransformer((s, p, f) -> {
                try (InputStream is = Files.newInputStream(f)) {
                    return DefaultConsumerPomBuilder.transformPom(new MavenStaxReader().read(is), project);
                }
            });

            t.transform(project, systemSessionMock, beforePomFile, tempFile);
        }
        Diff diff = DiffBuilder.compare(afterPomFile.toFile())
                .withTest(tempFile.toFile())
                .ignoreComments()
                .ignoreWhitespace()
                .build();
        assertFalse(diff.hasDifferences(), "XML files should be identical: " + diff.toString());
    }

    @Test
    void transformJarConsumerPom() throws Exception {
        RepositorySystemSession systemSessionMock = Mockito.mock(RepositorySystemSession.class);
        SessionData sessionDataMock = Mockito.mock(SessionData.class);
        when(systemSessionMock.getData()).thenReturn(sessionDataMock);

        Path beforePomFile = Paths.get("src/test/resources/projects/transform/jar/before.pom")
                .toAbsolutePath();
        Path afterPomFile =
                Paths.get("src/test/resources/projects/transform/jar/after.pom").toAbsolutePath();
        Path tempFile = Files.createTempFile("", ".pom");
        Files.delete(tempFile);
        try (InputStream expected = Files.newInputStream(beforePomFile)) {
            Model model = new Model(new MavenStaxReader().read(expected));
            MavenProject project = new MavenProject(model);
            project.setOriginalModel(model);
            ConsumerPomArtifactTransformer t = new ConsumerPomArtifactTransformer((s, p, f) -> {
                try (InputStream is = Files.newInputStream(f)) {
                    return DefaultConsumerPomBuilder.transformNonPom(new MavenStaxReader().read(is), project);
                }
            });

            t.transform(project, systemSessionMock, beforePomFile, tempFile);
        }
        Diff diff = DiffBuilder.compare(afterPomFile.toFile())
                .withTest(tempFile.toFile())
                .ignoreComments()
                .ignoreWhitespace()
                .build();
        assertFalse(diff.hasDifferences(), "XML files should be identical: " + diff.toString());
    }

    @Test
    void injectTransformedArtifactsWithoutPomShouldNotInjectAnyArtifacts() throws IOException {
        MavenProject emptyProject = new MavenProject();

        RepositorySystemSession systemSessionMock = Mockito.mock(RepositorySystemSession.class);
        SessionData sessionDataMock = Mockito.mock(SessionData.class);
        when(systemSessionMock.getData()).thenReturn(sessionDataMock);

        new ConsumerPomArtifactTransformer((session, project, src) -> null)
                .injectTransformedArtifacts(systemSessionMock, emptyProject);

        assertTrue(emptyProject.getAttachedArtifacts().isEmpty());
    }

    @Test
    void testDeployBuildPomEnabledByDefault() {
        // Test that build POM deployment is enabled by default
        ConsumerPomArtifactTransformer transformer = new ConsumerPomArtifactTransformer((s, p, f) -> null);

        RepositorySystemSession session = createMockSession(Map.of());
        DeployRequest request = createDeployRequestWithConsumerPom();

        DeployRequest result = transformer.remapDeployArtifacts(session, request);

        // Should have both consumer POM (no classifier) and build POM (with "build" classifier)
        Collection<Artifact> artifacts = result.getArtifacts();
        assertEquals(3, artifacts.size()); // original jar + consumer pom + build pom

        assertTrue(artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "".equals(a.getClassifier())));
        assertTrue(
                artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "build".equals(a.getClassifier())));
    }

    @Test
    void testDeployBuildPomDisabled() {
        // Test that build POM deployment can be disabled
        ConsumerPomArtifactTransformer transformer = new ConsumerPomArtifactTransformer((s, p, f) -> null);

        Map<String, Object> configProps = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "false");
        RepositorySystemSession session = createMockSession(configProps);
        DeployRequest request = createDeployRequestWithConsumerPom();

        DeployRequest result = transformer.remapDeployArtifacts(session, request);

        // Should have only consumer POM (no classifier), no build POM
        Collection<Artifact> artifacts = result.getArtifacts();
        assertEquals(2, artifacts.size()); // original jar + consumer pom (no build pom)

        assertTrue(artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "".equals(a.getClassifier())));
        assertTrue(
                artifacts.stream().noneMatch(a -> "pom".equals(a.getExtension()) && "build".equals(a.getClassifier())));
    }

    @Test
    void testDeployBuildPomExplicitlyEnabled() {
        // Test that build POM deployment can be explicitly enabled
        ConsumerPomArtifactTransformer transformer = new ConsumerPomArtifactTransformer((s, p, f) -> null);

        Map<String, Object> configProps = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "true");
        RepositorySystemSession session = createMockSession(configProps);
        DeployRequest request = createDeployRequestWithConsumerPom();

        DeployRequest result = transformer.remapDeployArtifacts(session, request);

        // Should have both consumer POM (no classifier) and build POM (with "build" classifier)
        Collection<Artifact> artifacts = result.getArtifacts();
        assertEquals(3, artifacts.size()); // original jar + consumer pom + build pom

        assertTrue(artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "".equals(a.getClassifier())));
        assertTrue(
                artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "build".equals(a.getClassifier())));
    }

    @Test
    void testDeployBuildPomWithBooleanValue() {
        // Test that build POM deployment works with Boolean values (not just strings)
        ConsumerPomArtifactTransformer transformer = new ConsumerPomArtifactTransformer((s, p, f) -> null);

        Map<String, Object> configProps = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, Boolean.FALSE);
        RepositorySystemSession session = createMockSession(configProps);
        DeployRequest request = createDeployRequestWithConsumerPom();

        DeployRequest result = transformer.remapDeployArtifacts(session, request);

        // Should have only consumer POM (no classifier), no build POM
        Collection<Artifact> artifacts = result.getArtifacts();
        assertEquals(2, artifacts.size()); // original jar + consumer pom (no build pom)

        assertTrue(artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "".equals(a.getClassifier())));
        assertTrue(
                artifacts.stream().noneMatch(a -> "pom".equals(a.getExtension()) && "build".equals(a.getClassifier())));
    }

    @Test
    void testInstallAlwaysIncludesBuildPom() {
        // Test that install always includes build POM regardless of the deployment setting
        ConsumerPomArtifactTransformer transformer = new ConsumerPomArtifactTransformer((s, p, f) -> null);

        Map<String, Object> configProps = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "false");
        RepositorySystemSession session = createMockSession(configProps);
        InstallRequest request = createInstallRequestWithConsumerPom();

        InstallRequest result = transformer.remapInstallArtifacts(session, request);

        // Should have both consumer POM and build POM even when deployment is disabled
        Collection<Artifact> artifacts = result.getArtifacts();
        assertEquals(3, artifacts.size()); // original jar + consumer pom + build pom

        assertTrue(artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "".equals(a.getClassifier())));
        assertTrue(
                artifacts.stream().anyMatch(a -> "pom".equals(a.getExtension()) && "build".equals(a.getClassifier())));
    }

    @Test
    void testDeployWithoutConsumerPomIsUnaffected() {
        // Test that requests without consumer POMs are not affected by the setting
        ConsumerPomArtifactTransformer transformer = new ConsumerPomArtifactTransformer((s, p, f) -> null);

        Map<String, Object> configProps = Map.of(Constants.MAVEN_DEPLOY_BUILD_POM, "false");
        RepositorySystemSession session = createMockSession(configProps);
        DeployRequest request = createDeployRequestWithoutConsumerPom();

        DeployRequest result = transformer.remapDeployArtifacts(session, request);

        // Should be unchanged since there's no consumer POM
        assertEquals(request.getArtifacts(), result.getArtifacts());
    }

    private RepositorySystemSession createMockSession(Map<String, Object> configProperties) {
        RepositorySystemSession session = Mockito.mock(RepositorySystemSession.class);
        when(session.getConfigProperties()).thenReturn(configProperties);
        return session;
    }

    private DeployRequest createDeployRequestWithConsumerPom() {
        DeployRequest request = new DeployRequest();
        List<Artifact> artifacts = List.of(
                new DefaultArtifact("com.example", "test", "", "jar", "1.0.0"),
                new DefaultArtifact("com.example", "test", "", "pom", "1.0.0"), // main POM
                new DefaultArtifact("com.example", "test", "consumer", "pom", "1.0.0") // consumer POM
                );
        request.setArtifacts(artifacts);
        return request;
    }

    private InstallRequest createInstallRequestWithConsumerPom() {
        InstallRequest request = new InstallRequest();
        List<Artifact> artifacts = List.of(
                new DefaultArtifact("com.example", "test", "", "jar", "1.0.0"),
                new DefaultArtifact("com.example", "test", "", "pom", "1.0.0"), // main POM
                new DefaultArtifact("com.example", "test", "consumer", "pom", "1.0.0") // consumer POM
                );
        request.setArtifacts(artifacts);
        return request;
    }

    private DeployRequest createDeployRequestWithoutConsumerPom() {
        DeployRequest request = new DeployRequest();
        List<Artifact> artifacts = List.of(
                new DefaultArtifact("com.example", "test", "", "jar", "1.0.0"),
                new DefaultArtifact("com.example", "test", "", "pom", "1.0.0") // only main POM
                );
        request.setArtifacts(artifacts);
        return request;
    }
}