/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates.
*
* Licensed 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.kie.workbench.common.services.backend.project;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.event.Event;
import javax.enterprise.inject.spi.Bean;
import org.guvnor.common.services.shared.metadata.model.Metadata;
import org.guvnor.common.services.backend.util.CommentedOptionFactory;
import org.guvnor.common.services.project.events.NewPackageEvent;
import org.guvnor.common.services.project.events.NewProjectEvent;
import org.guvnor.common.services.project.model.GAV;
import org.guvnor.common.services.project.model.POM;
import org.guvnor.common.services.project.model.Project;
import org.guvnor.common.services.project.service.POMService;
import org.guvnor.common.services.project.service.ProjectRepositoriesService;
import org.guvnor.structure.repositories.Repository;
import org.jboss.errai.security.shared.api.identity.User;
import org.jboss.errai.security.shared.api.identity.UserImpl;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.kie.workbench.common.services.shared.kmodule.KModuleService;
import org.kie.workbench.common.services.shared.project.KieProject;
import org.kie.workbench.common.services.shared.project.ProjectImportsService;
import org.kie.workbench.common.services.shared.whitelist.PackageNameWhiteListService;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.uberfire.backend.server.util.Paths;
import org.uberfire.backend.vfs.Path;
import org.uberfire.io.IOService;
import org.uberfire.java.nio.file.FileAlreadyExistsException;
import org.uberfire.java.nio.fs.file.SimpleFileSystemProvider;
import org.uberfire.rpc.SessionInfo;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import org.mockito.ArgumentCaptor;
@RunWith(MockitoJUnitRunner.class)
public class ProjectSaverTest extends WeldProjectTestBase {
public static final String GROUP_ID = "org.kie.workbench.services";
public static final String ARTIFACT_ID = "kie-wb-common-services-test";
public static final String VERSION = "1.0.0-SNAPSHOT";
public static final String PROJECT_NAME = "p0";
@Mock
private IOService ioService;
@Mock
private POMService pomService;
@Mock
private KieResourceResolver resourceResolver;
@Mock
private Event<NewPackageEvent> newPackageEvent;
private ProjectSaver saver;
private SimpleFileSystemProvider fs;
private Paths paths;
@Before
public void setUp() throws Exception {
fs = new SimpleFileSystemProvider();
super.startWeld();
//Instantiate Paths used in tests for Path conversion
final Bean pathsBean = (Bean) beanManager.getBeans( Paths.class ).iterator().next();
final CreationalContext cc = beanManager.createCreationalContext( pathsBean );
paths = (Paths) beanManager.getReference( pathsBean,
Paths.class,
cc );
//Ensure URLs use the default:// scheme
fs.forceAsDefault();
final Event<NewProjectEvent> newProjectEvent = mock( Event.class );
when( ioService.createDirectory( any( org.uberfire.java.nio.file.Path.class ) ) ).thenAnswer( new Answer<Object>() {
@Override
public Object answer( final InvocationOnMock invocation ) throws Throwable {
return invocation.getArguments()[ 0 ];
}
} );
saver = new ProjectSaver( ioService,
pomService,
mock( KModuleService.class ),
newProjectEvent,
newPackageEvent,
resourceResolver,
mock( ProjectImportsService.class ),
mock( ProjectRepositoriesService.class ),
mock( PackageNameWhiteListService.class ),
mock( CommentedOptionFactory.class ),
new SessionInfo() {
@Override
public String getId() {
return "session";
}
@Override
public User getIdentity() {
return new UserImpl( "testuser" );
}
} );
}
@After
public void cleanUp() {
super.stopWeld();
}
@Test
public void testNewProjecCreationByGAV() throws IOException {
final POM pom = new POM( new GAV( GROUP_ID, ARTIFACT_ID, VERSION ) );
runProjecCreationTest( pom );
}
@Test
public void testNewProjectRegularCreation() throws URISyntaxException, IOException {
final POM pom = new POM();
pom.setName( PROJECT_NAME );
pom.getGav().setGroupId( GROUP_ID );
pom.getGav().setArtifactId( ARTIFACT_ID );
pom.getGav().setVersion( VERSION );
runProjecCreationTest( pom );
}
/**
* This test checks that when we have a managed repository and the user tries to create a project "project1" in the
* given repository, and a project "project1" already exists. Then the parent pom.xml for the managed repository
* must remain untouched. i.e. If the project "project1" already exists then the parent pom.xml shouldn't be modified.
* In this way the parent pom.xml remains consistent with the already existing structure.
*/
@Test
public void testDuplicatedChildInManagedRepositoryPrevention() throws URISyntaxException, IOException {
final POM parentPom = mock( POM.class );
final POM newPOM = mock( POM.class );
final Repository repository = mock( Repository.class );
final String baseURL = "/";
final File test = File.createTempFile( "test", Long.toString( System.nanoTime() ) );
final Path repositoryRootPath = paths.convert( fs.getPath( test.toURI() ) );
FileAlreadyExistsException fileExistsException = null;
when( repository.getRoot() ).thenReturn( repositoryRootPath );
//name for the project we are trying to re-create over an existing one.
when ( newPOM.getName() ).thenReturn( "existingProject" );
//path that will be calculated for looking for the parent pom.xml for the managed repository. (the parent pom.xml
//lies basically in the root of the repository by definition.)
final org.uberfire.java.nio.file.Path parentPomNioPath = paths.convert( repositoryRootPath ).resolve( "pom.xml" );
final Path parentPomVFSPath = paths.convert( parentPomNioPath );
//path that will be calculated for saving the project pom.xml for the project that are about to be created.
final org.uberfire.java.nio.file.Path projectNioPath = paths.convert( repositoryRootPath ).resolve( "existingProject" ).resolve( "pom.xml" );
//emulates that we have a parent pom.xml in current repository root.
//This is the case for the ManagedRepository.
when( pomService.load( parentPomVFSPath ) ).thenReturn( parentPom );
//emulate the project already exists
when ( ioService.exists( projectNioPath ) ).thenReturn( true );
try {
saver.save( repositoryRootPath, newPOM, baseURL );
} catch ( FileAlreadyExistsException e ) {
fileExistsException = e;
}
//The file already exists must have been thrown, since the project already exists.
assertNotNull( fileExistsException );
//And also the parent pom must have never been updated/modified.
verify( pomService, never() ).save( eq( parentPomVFSPath ) , any( POM.class ), any( Metadata.class ), any( String.class ) );
}
@Test
public void testGavBasedPackagesSanitized() throws IOException {
String unsanitizedId = "hyphs-and.int.123";
String sanitizedPkgStructure = "hyphs_and/_int/_23/hyphs_and/_int/_23";
POM pom = new POM();
pom.setName( "gavBassedPackagesTest" );
pom.getGav().setGroupId( unsanitizedId );
pom.getGav().setArtifactId( unsanitizedId );
pom.getGav().setVersion( VERSION );
ArgumentCaptor<Event> eventCaptor = ArgumentCaptor.forClass( Event.class );
doNothing().when( newPackageEvent ).fire( ( NewPackageEvent ) eventCaptor.capture() );
when( resourceResolver.newPackage( any( org.guvnor.common.services.project.model.Package.class ), anyString(), anyBoolean() ) )
.thenAnswer( new Answer<Object>() {
@Override
public Object answer( InvocationOnMock invocation ) throws Throwable {
org.guvnor.common.services.project.model.Package pkg = mock( org.guvnor.common.services.project.model.Package.class );
when( pkg.getRelativeCaption() ).thenReturn( ( String ) invocation.getArguments()[1] );
return pkg;
}
} );
runProjecCreationTest( pom );
assertEquals( sanitizedPkgStructure, ( ( NewPackageEvent ) eventCaptor.getValue() ).getPackage().getRelativeCaption() );
}
protected void runProjecCreationTest( final POM pom ) throws IOException {
final Repository repository = mock( Repository.class );
final String baseURL = "/";
final File test = File.createTempFile( "test", Long.toString( System.nanoTime() ) );
final Path repositoryRootPath = paths.convert( fs.getPath( test.toURI() ) );
when( repository.getRoot() ).thenReturn( repositoryRootPath );
when( pomService.load( any( Path.class ) ) ).thenReturn( pom );
final ArrayList<String> directories = new ArrayList<String>();
when( resourceResolver.simpleProjectInstance( any( org.uberfire.java.nio.file.Path.class ) ) ).thenReturn( mock( KieProject.class ) );
final KieProject kieProject = new KieProject();
kieProject.setPom( pom );
when( resourceResolver.resolveProject( any( Path.class ) ) ).thenReturn( kieProject );
stub( ioService.createDirectory( any( org.uberfire.java.nio.file.Path.class ) ) ).toAnswer( new Answer<org.uberfire.java.nio.file.Path>() {
@Override
public org.uberfire.java.nio.file.Path answer( final InvocationOnMock invocationOnMock ) throws Throwable {
org.uberfire.java.nio.file.Path path = (org.uberfire.java.nio.file.Path) invocationOnMock.getArguments()[ 0 ];
directories.add( path.toString() );
return null;
}
} );
Project project = saver.save( repository.getRoot(), pom, baseURL );
assertEquals( 4, directories.size() );
assertTrue( directories.add( "src/main/java" ) );
assertTrue( directories.add( "src/main/resources" ) );
assertTrue( directories.add( "src/test/resources" ) );
assertTrue( directories.add( "src/main/java" ) );
assertEquals( pom, project.getPom() );
}
}