1
0
mirror of https://github.com/mc1arke/sonarqube-community-branch-plugin.git synced 2024-11-28 08:58:55 +02:00

#674: Add support for Sonarqube 9.7

Sonarqube has removed the use of the same external component key and
differing database IDs for different branches on the same projects and
now use different IDs in all cases. The pull request web service
endpoints have also been removed from community edition. To allow the
plugin to work with the new version of Sonarqube, the component key
generation for branches has been modified to save branch DTOs whenever a
new branch is created, and to remove the conditions around re-using the
same branch if the target branch details matched an existing branch. The
Pull Request endpoint actions have been copied from the old community
edition sources, and tidied up to use a cleaner abstraction model. As
the front-end only shows branch features if the implementation of
`BranchFeatureExtension` returns the name 'branch-support', the
`CommunityBranchFeatureExtension` has been altered to follow this
requirement, and an additional `MonoRepoFeature` has been implemented to
allow the mono-repo switches to be shown against the front-end.

Includes the migration of any altered unit tests to JUnit 5.
This commit is contained in:
Michael Clarke 2022-12-11 09:15:17 +00:00 committed by Michael Clarke
parent 874b9f23d9
commit a1f28e5df2
43 changed files with 1396 additions and 578 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -40,7 +40,7 @@ repositories {
}
}
def sonarqubeVersion = '9.1.0.47736'
def sonarqubeVersion = '9.7.0.61563'
def sonarqubeLibDir = "${projectDir}/sonarqube-lib"
def sonarLibraries = "${sonarqubeLibDir}/sonarqube-${sonarqubeVersion}/lib"

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 Michael Clarke
* Copyright (C) 2021-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -52,8 +52,9 @@ public final class CommunityBranchAgent {
if (component == Component.CE) {
redefineEdition(instrumentation, "org.sonar.core.platform.PlatformEditionProvider", redefineOptionalEditionGetMethod());
redefineEdition(instrumentation, "org.sonar.server.almsettings.MultipleAlmFeature", redefineIsEnabledFlag());
} else if (component == Component.WEB) {
redefineEdition(instrumentation, "org.sonar.server.almsettings.MultipleAlmFeatureProvider", redefineConstructorEditionProviderField(EditionProvider.Edition.ENTERPRISE));
redefineEdition(instrumentation, "org.sonar.server.almsettings.MultipleAlmFeature", redefineIsEnabledFlag());
redefineEdition(instrumentation, "org.sonar.server.newcodeperiod.ws.SetAction", redefineConstructorEditionProviderField(EditionProvider.Edition.DEVELOPER));
redefineEdition(instrumentation, "org.sonar.server.newcodeperiod.ws.UnsetAction", redefineConstructorEditionProviderField(EditionProvider.Edition.DEVELOPER));
}
@ -101,6 +102,13 @@ public final class CommunityBranchAgent {
};
}
private static Redefiner redefineIsEnabledFlag() {
return ctClass -> {
CtMethod ctMethod = ctClass.getDeclaredMethod("isEnabled");
ctMethod.setBody("return true;");
};
}
private static Redefiner redefineConstructorEditionProviderField(EditionProvider.Edition edition) {
return ctClass -> {
CtConstructor ctConstructor = ctClass.getDeclaredConstructors()[0];

View File

@ -43,17 +43,22 @@ import com.github.mc1arke.sonarqube.plugin.scanner.autoconfiguration.GitlabCiAut
import com.github.mc1arke.sonarqube.plugin.scanner.autoconfiguration.JenkinsAutoConfigurer;
import com.github.mc1arke.sonarqube.plugin.server.CommunityBranchFeatureExtension;
import com.github.mc1arke.sonarqube.plugin.server.CommunityBranchSupportDelegate;
import com.github.mc1arke.sonarqube.plugin.server.MonoRepoFeature;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.validator.AzureDevopsValidator;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.validator.BitbucketValidator;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.validator.GithubValidator;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.validator.GitlabValidator;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action.DeleteBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action.SetAzureBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action.SetBitbucketBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action.SetBitbucketCloudBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action.SetGithubBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action.SetGitlabBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action.ValidateBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action.DeleteBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action.SetAzureBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action.SetBitbucketBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action.SetBitbucketCloudBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action.SetGithubBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action.SetGitlabBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action.ValidateBindingAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.PullRequestWs;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action.DeleteAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action.ListAction;
import org.sonar.api.CoreProperties;
import org.sonar.api.Plugin;
import org.sonar.api.PropertyType;
@ -88,6 +93,9 @@ public class CommunityBranchPlugin implements Plugin, CoreExtension {
SetBitbucketCloudBindingAction.class,
SetGitlabBindingAction.class,
ValidateBindingAction.class,
DeleteAction.class,
ListAction.class,
PullRequestWs.class,
GithubValidator.class,
DefaultGraphqlProvider.class,
@ -147,7 +155,8 @@ public class CommunityBranchPlugin implements Plugin, CoreExtension {
.name("Images base URL")
.description("Base URL used to load the images for the PR comments (please use this only if images are not displayed properly).")
.type(PropertyType.STRING)
.build());
.build(),
MonoRepoFeature.class);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -18,11 +18,8 @@
*/
package com.github.mc1arke.sonarqube.plugin.ce;
import org.apache.commons.lang.StringUtils;
import org.sonar.ce.task.projectanalysis.analysis.Branch;
import org.sonar.core.component.ComponentKeys;
import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
/**
* @author Michael Clarke
@ -80,24 +77,6 @@ public class CommunityBranch implements Branch {
return pullRequestKey;
}
@Override
public String generateKey(String projectKey, String fileOrDirPath) {
String effectiveKey;
if (null == fileOrDirPath) {
effectiveKey = projectKey;
} else {
effectiveKey = ComponentKeys.createEffectiveKey(projectKey, StringUtils.trimToNull(fileOrDirPath));
}
if (main) {
return effectiveKey;
} else if (BranchType.PULL_REQUEST == branchType) {
return ComponentDto.generatePullRequestKey(effectiveKey, pullRequestKey);
} else {
return ComponentDto.generateBranchKey(effectiveKey, name);
}
}
@Override
public String getTargetBranchName() {
return targetBranchName;

View File

@ -71,16 +71,16 @@ public abstract class DiscussionAwarePullRequestDecorator<C, P, U, D, N> impleme
U user = getCurrentUser(client);
List<PostAnalysisIssueVisitor.ComponentIssue> openSonarqubeIssues = analysis.getScmReportableIssues();
List<Triple<D, N, Optional<ProjectIssueIdentifier>>> currentProjectSonarqueComments = findOpenSonarqubeComments(client,
List<Triple<D, N, Optional<ProjectIssueIdentifier>>> currentProjectSonarqubeComments = findOpenSonarqubeComments(client,
pullRequest,
user)
.stream()
.filter(comment -> isCommentFromCurrentProject(comment, analysis.getAnalysisProjectKey()))
.filter(comment -> !projectAlmSettingDto.getMonorepo() || isCommentFromCurrentProject(comment, analysis.getAnalysisProjectKey()))
.collect(Collectors.toList());
List<String> commentKeysForOpenComments = closeOldDiscussionsAndExtractRemainingKeys(client,
user,
currentProjectSonarqueComments,
currentProjectSonarqubeComments,
openSonarqubeIssues,
pullRequest);

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 Michael Clarke
* Copyright (C) 2019-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -27,6 +27,11 @@ import org.sonar.server.branch.BranchFeatureExtension;
*/
public class CommunityBranchFeatureExtension implements BranchFeatureExtension {
@Override
public String getName() {
return "branch-support";
}
@Override
public boolean isEnabled() {
return true;

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -18,7 +18,15 @@
*/
package com.github.mc1arke.sonarqube.plugin.server;
import java.time.Clock;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.config.Configuration;
import org.sonar.core.config.PurgeConstants;
import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
@ -28,11 +36,7 @@ import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.ce.queue.BranchSupport;
import org.sonar.server.ce.queue.BranchSupportDelegate;
import java.time.Clock;
import java.util.Date;
import java.util.Map;
import java.util.Optional;
import org.sonar.server.setting.ProjectConfigurationLoader;
/**
* @author Michael Clarke
@ -42,12 +46,15 @@ public class CommunityBranchSupportDelegate implements BranchSupportDelegate {
private final UuidFactory uuidFactory;
private final DbClient dbClient;
private final Clock clock;
private final ProjectConfigurationLoader projectConfigurationLoader;
public CommunityBranchSupportDelegate(UuidFactory uuidFactory, DbClient dbClient, Clock clock) {
public CommunityBranchSupportDelegate(UuidFactory uuidFactory, DbClient dbClient, Clock clock,
ProjectConfigurationLoader projectConfigurationLoader) {
super();
this.uuidFactory = uuidFactory;
this.dbClient = dbClient;
this.clock = clock;
this.projectConfigurationLoader = projectConfigurationLoader;
}
@Override
@ -61,9 +68,7 @@ public class CommunityBranchSupportDelegate implements BranchSupportDelegate {
CeTaskCharacteristicDto.BRANCH_TYPE_KEY,
CeTaskCharacteristicDto.PULL_REQUEST));
} else {
return new CommunityComponentKey(projectKey,
ComponentDto.generatePullRequestKey(projectKey, pullRequest), null,
pullRequest);
return new CommunityComponentKey(projectKey, null, pullRequest);
}
}
@ -71,7 +76,7 @@ public class CommunityBranchSupportDelegate implements BranchSupportDelegate {
try {
BranchType.valueOf(branchTypeParam);
return new CommunityComponentKey(projectKey, ComponentDto.generateBranchKey(projectKey, branch), branch, null);
return new CommunityComponentKey(projectKey, branch, null);
} catch (IllegalArgumentException ex) {
throw new IllegalArgumentException(String.format("Unsupported branch type '%s'", branchTypeParam), ex);
}
@ -85,25 +90,37 @@ public class CommunityBranchSupportDelegate implements BranchSupportDelegate {
throw new IllegalStateException("Component Key and Main Component Key do not match");
}
Optional<String> branchOptional = componentKey.getBranchName();
if (branchOptional.isPresent() && branchOptional.get().equals(mainComponentBranchDto.getKey())) {
return mainComponentDto;
}
String branchUuid = uuidFactory.create();
// borrowed from https://github.com/SonarSource/sonarqube/blob/e80c0f3d1e5cd459f88b7e0c41a2d9a7519e260f/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImpl.java
ComponentDto branchDto = mainComponentDto.copy();
branchDto.setUuid(branchUuid);
branchDto.setProjectUuid(branchUuid);
branchDto.setRootUuid(branchUuid);
branchDto.setUuidPath(ComponentDto.UUID_PATH_OF_ROOT);
branchDto.setModuleUuidPath(ComponentDto.UUID_PATH_SEPARATOR + branchUuid + ComponentDto.UUID_PATH_SEPARATOR);
branchDto.setMainBranchProjectUuid(mainComponentDto.uuid());
branchDto.setDbKey(componentKey.getDbKey());
branchDto.setCreatedAt(new Date(clock.millis()));
dbClient.componentDao().insert(dbSession, branchDto);
return branchDto;
ComponentDto componentDto = mainComponentDto.copy()
.setUuid(branchUuid)
.setRootUuid(branchUuid)
.setBranchUuid(branchUuid)
.setUuidPath(ComponentDto.UUID_PATH_OF_ROOT)
.setModuleUuidPath(ComponentDto.UUID_PATH_SEPARATOR + branchUuid + ComponentDto.UUID_PATH_SEPARATOR)
.setMainBranchProjectUuid(mainComponentDto.uuid())
.setCreatedAt(new Date(clock.millis()));
dbClient.componentDao().insert(dbSession, componentDto);
BranchDto branchDto = new BranchDto()
.setProjectUuid(mainComponentDto.uuid())
.setUuid(branchUuid);
componentKey.getPullRequestKey().ifPresent(pullRequestKey -> branchDto.setBranchType(BranchType.PULL_REQUEST)
.setExcludeFromPurge(false)
.setKey(pullRequestKey));
componentKey.getBranchName().ifPresent(branchName -> branchDto.setBranchType(BranchType.BRANCH)
.setExcludeFromPurge(isBranchExcludedFromPurge(projectConfigurationLoader.loadProjectConfiguration(dbSession, mainComponentDto), branchName))
.setKey(branchName));
dbClient.branchDao().insert(dbSession, branchDto);
return componentDto;
}
private static boolean isBranchExcludedFromPurge(Configuration projectConfiguration, String branchName) {
return Arrays.stream(projectConfiguration.getStringArray(PurgeConstants.BRANCHES_TO_KEEP_WHEN_INACTIVE))
.map(Pattern::compile)
.map(Pattern::asMatchPredicate)
.anyMatch(p -> p.test(branchName));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -28,13 +28,11 @@ import java.util.Optional;
/*package*/ class CommunityComponentKey extends BranchSupport.ComponentKey {
private final String key;
private final String dbKey;
private final String branchName;
private final String pullRequestKey;
/*package*/ CommunityComponentKey(String key, String dbKey, String branchName, String pullRequestKey) {
/*package*/ CommunityComponentKey(String key, String branchName, String pullRequestKey) {
this.key = key;
this.dbKey = dbKey;
this.branchName = branchName;
this.pullRequestKey = pullRequestKey;
}
@ -44,25 +42,14 @@ import java.util.Optional;
return key;
}
@Override
public String getDbKey() {
return dbKey;
}
@Override
public Optional<String> getBranchName() {
return Optional.ofNullable(branchName);
}
@Override
public Optional<String> getPullRequestKey() {
return Optional.ofNullable(pullRequestKey);
}
@Override
public CommunityComponentKey getMainBranchComponentKey() {
if (key.equals(dbKey)) {
return this;
}
return new CommunityComponentKey(key, key, null, null);
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.server.ServerSide;
import org.sonar.server.feature.SonarQubeFeature;
@ServerSide
@ComputeEngineSide
public class MonoRepoFeature implements SonarQubeFeature {
@Override
public String getName() {
return "monorepo";
}
@Override
public boolean isEnabled() {
return true;
}
}

View File

@ -1,13 +0,0 @@
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import com.google.protobuf.Message;
@FunctionalInterface
interface ProtoBufWriter {
void write(Message message, Request request, Response response);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,7 +16,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
@ -32,14 +32,14 @@ public class DeleteBindingAction extends ProjectWsAction {
private final DbClient dbClient;
public DeleteBindingAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder) {
super("delete_binding", dbClient, componentFinder, userSession, true);
super("delete_binding", dbClient, componentFinder, userSession);
this.dbClient = dbClient;
}
@Override
protected void configureAction(WebService.NewAction action) {
//no-op
action.setPost(true);
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,7 +16,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
@ -29,8 +29,6 @@ import org.sonar.server.almsettings.ws.AlmSettingsWsAction;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.user.UserSession;
import java.util.Optional;
public abstract class ProjectWsAction implements AlmSettingsWsAction {
private static final String PROJECT_PARAMETER = "project";
@ -39,27 +37,25 @@ public abstract class ProjectWsAction implements AlmSettingsWsAction {
private final DbClient dbClient;
private final ComponentFinder componentFinder;
private final UserSession userSession;
private final boolean projectParameterRequired;
private final String permission;
protected ProjectWsAction(String actionName, DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, boolean projectParameterRequired) {
this(actionName, dbClient, componentFinder, userSession, projectParameterRequired, UserRole.ADMIN);
protected ProjectWsAction(String actionName, DbClient dbClient, ComponentFinder componentFinder, UserSession userSession) {
this(actionName, dbClient, componentFinder, userSession, UserRole.ADMIN);
}
protected ProjectWsAction(String actionName, DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, boolean projectParameterRequired, String permission) {
protected ProjectWsAction(String actionName, DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, String permission) {
super();
this.actionName = actionName;
this.dbClient = dbClient;
this.componentFinder = componentFinder;
this.userSession = userSession;
this.projectParameterRequired = projectParameterRequired;
this.permission = permission;
}
@Override
public void define(WebService.NewController context) {
WebService.NewAction action = context.createAction(actionName).setHandler(this);
action.createParam(PROJECT_PARAMETER).setRequired(projectParameterRequired);
action.createParam(PROJECT_PARAMETER).setRequired(true);
configureAction(action);
}
@ -69,20 +65,11 @@ public abstract class ProjectWsAction implements AlmSettingsWsAction {
@Override
public void handle(Request request, Response response) {
Optional<String> projectKey = Optional.ofNullable(request.param(PROJECT_PARAMETER));
String projectKey = request.mandatoryParam(PROJECT_PARAMETER);
try (DbSession dbSession = dbClient.openSession(false)) {
ProjectDto project;
if (projectKey.isPresent()) {
project = componentFinder.getProjectByKey(dbSession, projectKey.get());
userSession.checkProjectPermission(permission, project);
} else {
if (projectParameterRequired) {
throw new IllegalArgumentException("The 'project' parameter is missing");
} else {
project = null;
}
}
ProjectDto project = componentFinder.getProjectByKey(dbSession, projectKey);
userSession.checkProjectPermission(permission, project);
handleProjectRequest(project, request, response, dbSession);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,7 +16,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.WebService;
@ -44,13 +44,13 @@ public class SetAzureBindingAction extends SetBindingAction {
@Override
protected ProjectAlmSettingDto createProjectAlmSettingDto(String projectUuid, String settingsUuid,
Request request) {
boolean monoRepo, Request request) {
return new ProjectAlmSettingDto()
.setProjectUuid(projectUuid)
.setAlmSettingUuid(settingsUuid)
.setAlmRepo(request.mandatoryParam(REPOSITORY_NAME_PARAMETER))
.setAlmSlug(request.mandatoryParam(PROJECT_NAME_PARAMETER))
.setMonorepo(false);
.setMonorepo(monoRepo);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,7 +16,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
@ -35,23 +35,27 @@ import static java.lang.String.format;
public abstract class SetBindingAction extends ProjectWsAction {
private static final String ALM_SETTING_PARAMETER = "almSetting";
private static final String MONOREPO_PARAMETER = "monorepo";
protected SetBindingAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, String actionName) {
super(actionName, dbClient, componentFinder, userSession, true);
super(actionName, dbClient, componentFinder, userSession);
}
@Override
protected void configureAction(WebService.NewAction action) {
action.createParam(ALM_SETTING_PARAMETER).setRequired(true);
action.setPost(true)
.createParam(ALM_SETTING_PARAMETER).setRequired(true);
action.createParam(MONOREPO_PARAMETER).setRequired(true).setBooleanPossibleValues();
}
@Override
protected void handleProjectRequest(ProjectDto project, Request request, Response response, DbSession dbSession) {
String almSetting = request.mandatoryParam(ALM_SETTING_PARAMETER);
boolean monoRepo = request.mandatoryParamAsBoolean(MONOREPO_PARAMETER);
DbClient dbClient = getDbClient();
AlmSettingDto almSettingDto = getAlmSetting(dbClient, dbSession, almSetting);
dbClient.projectAlmSettingDao().insertOrUpdate(dbSession, createProjectAlmSettingDto(project.getUuid(), almSettingDto.getUuid(), request), almSettingDto.getUuid(), project.getName(), project.getKey());
dbClient.projectAlmSettingDao().insertOrUpdate(dbSession, createProjectAlmSettingDto(project.getUuid(), almSettingDto.getUuid(), monoRepo, request), almSettingDto.getUuid(), project.getName(), project.getKey());
dbSession.commit();
response.noContent();
@ -63,6 +67,6 @@ public abstract class SetBindingAction extends ProjectWsAction {
}
protected abstract ProjectAlmSettingDto createProjectAlmSettingDto(String projectUuid, String settingsUuid, Request request);
protected abstract ProjectAlmSettingDto createProjectAlmSettingDto(String projectUuid, String settingsUuid, boolean monoRepo, Request request);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,7 +16,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.WebService;
@ -43,13 +43,13 @@ public class SetBitbucketBindingAction extends SetBindingAction {
@Override
protected ProjectAlmSettingDto createProjectAlmSettingDto(String projectUuid, String settingsUuid,
Request request) {
boolean monoRepo, Request request) {
return new ProjectAlmSettingDto()
.setProjectUuid(projectUuid)
.setAlmSettingUuid(settingsUuid)
.setAlmRepo(request.mandatoryParam(REPOSITORY_PARAMETER))
.setAlmSlug(request.mandatoryParam(SLUG_PARAMETER))
.setMonorepo(false);
.setMonorepo(monoRepo);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 Michael Clarke
* Copyright (C) 2021-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,7 +16,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.WebService;
@ -37,16 +37,17 @@ public class SetBitbucketCloudBindingAction extends SetBindingAction {
protected void configureAction(WebService.NewAction action) {
super.configureAction(action);
action.createParam(REPOSITORY_PARAMETER).setRequired(true);
}
@Override
protected ProjectAlmSettingDto createProjectAlmSettingDto(String projectUuid, String settingsUuid,
Request request) {
boolean monoRepo, Request request) {
return new ProjectAlmSettingDto()
.setProjectUuid(projectUuid)
.setAlmSettingUuid(settingsUuid)
.setAlmRepo(request.mandatoryParam(REPOSITORY_PARAMETER))
.setMonorepo(false);
.setMonorepo(monoRepo);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,7 +16,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.WebService;
@ -42,13 +42,13 @@ public class SetGithubBindingAction extends SetBindingAction {
}
@Override
protected ProjectAlmSettingDto createProjectAlmSettingDto(String projectUuid, String settingsUuid, Request request) {
protected ProjectAlmSettingDto createProjectAlmSettingDto(String projectUuid, String settingsUuid, boolean monoRepo, Request request) {
return new ProjectAlmSettingDto()
.setProjectUuid(projectUuid)
.setAlmSettingUuid(settingsUuid)
.setAlmRepo(request.mandatoryParam(REPOSITORY_PARAMETER))
.setSummaryCommentEnabled(request.paramAsBoolean(SUMMARY_COMMENT_PARAMETER))
.setMonorepo(false);
.setMonorepo(monoRepo);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,7 +16,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.WebService;
@ -40,12 +40,12 @@ public class SetGitlabBindingAction extends SetBindingAction {
@Override
protected ProjectAlmSettingDto createProjectAlmSettingDto(String projectUuid, String settingsUuid,
Request request) {
boolean monoRepo, Request request) {
return new ProjectAlmSettingDto()
.setProjectUuid(projectUuid)
.setAlmSettingUuid(settingsUuid)
.setAlmRepo(request.param(REPOSITORY_PARAMETER))
.setMonorepo(false);
.setMonorepo(monoRepo);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 Michael Clarke
* Copyright (C) 2021-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,7 +16,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import com.github.mc1arke.sonarqube.plugin.InvalidConfigurationException;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.validator.Validator;
@ -41,7 +41,7 @@ public class ValidateBindingAction extends ProjectWsAction {
private final List<Validator> validators;
public ValidateBindingAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, List<Validator> validators) {
super("validate_binding", dbClient, componentFinder, userSession, true, UserRole.USER);
super("validate_binding", dbClient, componentFinder, userSession, UserRole.USER);
this.validators = validators;
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest;
import org.sonar.api.server.ws.WebService;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action.PullRequestWsAction;
public class PullRequestWs implements WebService {
private final PullRequestWsAction[] actions;
public PullRequestWs(PullRequestWsAction... actions) {
this.actions = actions;
}
@Override
public void define(Context context) {
NewController controller = context.createController("api/project_pull_requests");
for (PullRequestWsAction action : actions) {
action.define(controller);
}
controller.done();
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2009-2022 SonarSource SA (mailto:info AT sonarsource DOT com), Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.BranchType;
import org.sonar.db.project.ProjectDto;
import org.sonar.server.component.ComponentCleanerService;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.user.UserSession;
public class DeleteAction extends ProjectWsAction {
private static final String PULL_REQUEST_PARAMETER = "pullRequest";
private final UserSession userSession;
private final ComponentCleanerService componentCleanerService;
public DeleteAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, ComponentCleanerService componentCleanerService) {
super("delete", dbClient, componentFinder);
this.userSession = userSession;
this.componentCleanerService = componentCleanerService;
}
@Override
protected void configureAction(WebService.NewAction action) {
action.setPost(true)
.createParam(PULL_REQUEST_PARAMETER)
.setRequired(true);
}
@Override
public void handleProjectRequest(ProjectDto project, Request request, Response response, DbSession dbSession) {
userSession.checkLoggedIn()
.checkProjectPermission(UserRole.ADMIN, project);
String pullRequestId = request.mandatoryParam(PULL_REQUEST_PARAMETER);
BranchDto pullRequest = getDbClient().branchDao().selectByPullRequestKey(dbSession, project.getUuid(), pullRequestId)
.filter(branch -> branch.getBranchType() == BranchType.PULL_REQUEST)
.orElseThrow(() -> new NotFoundException(String.format("Pull request '%s' is not found for project '%s'", pullRequestId, project.getKey())));
componentCleanerService.deleteBranch(dbSession, pullRequest);
response.noContent();
}
}

View File

@ -0,0 +1,163 @@
/*
* Copyright (C) 2009-2022 SonarSource SA (mailto:info AT sonarsource DOT com), Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action;
import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.web.UserRole;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.BranchDao;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.BranchType;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.permission.GlobalPermission;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.protobuf.DbProjectBranches;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.issue.index.IssueIndex;
import org.sonar.server.issue.index.PrStatistics;
import org.sonar.server.user.UserSession;
import org.sonar.server.ws.WsUtils;
import org.sonarqube.ws.ProjectPullRequests;
import org.springframework.beans.factory.annotation.Autowired;
import com.google.common.base.Strings;
public class ListAction extends ProjectWsAction {
private final UserSession userSession;
private final IssueIndex issueIndex;
private final ProtoBufWriter protoBufWriter;
@Autowired
public ListAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, IssueIndex issueIndex) {
this(dbClient, componentFinder, userSession, issueIndex, WsUtils::writeProtobuf);
}
ListAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, IssueIndex issueIndex, ProtoBufWriter protoBufWriter) {
super("list", dbClient, componentFinder);
this.userSession = userSession;
this.issueIndex = issueIndex;
this.protoBufWriter = protoBufWriter;
}
@Override
protected void configureAction(WebService.NewAction action) {
//no-op
}
@Override
public void handleProjectRequest(ProjectDto project, Request request, Response response, DbSession dbSession) {
checkPermission(project, userSession);
BranchDao branchDao = getDbClient().branchDao();
List<BranchDto> pullRequests = branchDao.selectByProject(dbSession, project).stream()
.filter(b -> b.getBranchType() == BranchType.PULL_REQUEST)
.collect(Collectors.toList());
List<String> pullRequestUuids = pullRequests.stream().map(BranchDto::getUuid).collect(Collectors.toList());
Map<String, BranchDto> mergeBranchesByUuid = branchDao
.selectByUuids(dbSession, pullRequests.stream()
.map(BranchDto::getMergeBranchUuid)
.filter(Objects::nonNull)
.collect(Collectors.toList()))
.stream().collect(MoreCollectors.uniqueIndex(BranchDto::getUuid));
Map<String, PrStatistics> branchStatisticsByBranchUuid = issueIndex.searchBranchStatistics(project.getUuid(), pullRequestUuids).stream()
.collect(MoreCollectors.uniqueIndex(PrStatistics::getBranchUuid, Function.identity()));
Map<String, LiveMeasureDto> qualityGateMeasuresByComponentUuids = getDbClient().liveMeasureDao()
.selectByComponentUuidsAndMetricKeys(dbSession, pullRequestUuids, List.of(CoreMetrics.ALERT_STATUS_KEY)).stream()
.collect(MoreCollectors.uniqueIndex(LiveMeasureDto::getComponentUuid));
Map<String, String> analysisDateByBranchUuid = getDbClient().snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, pullRequestUuids).stream()
.collect(MoreCollectors.uniqueIndex(SnapshotDto::getComponentUuid, s -> DateUtils.formatDateTime(s.getCreatedAt())));
ProjectPullRequests.ListWsResponse.Builder protobufResponse = ProjectPullRequests.ListWsResponse.newBuilder();
pullRequests
.forEach(b -> addPullRequest(protobufResponse, b, mergeBranchesByUuid, qualityGateMeasuresByComponentUuids.get(b.getUuid()), branchStatisticsByBranchUuid.get(b.getUuid()),
analysisDateByBranchUuid.get(b.getUuid())));
protoBufWriter.write(protobufResponse.build(), request, response);
}
private static void checkPermission(ProjectDto project, UserSession userSession) {
if (userSession.hasProjectPermission(UserRole.USER, project) ||
userSession.hasProjectPermission(UserRole.SCAN, project) ||
userSession.hasPermission(GlobalPermission.SCAN)) {
return;
}
throw insufficientPrivilegesException();
}
private static void addPullRequest(ProjectPullRequests.ListWsResponse.Builder response, BranchDto branch, Map<String, BranchDto> mergeBranchesByUuid,
@Nullable LiveMeasureDto qualityGateMeasure, PrStatistics prStatistics, @Nullable String analysisDate) {
Optional<BranchDto> mergeBranch = Optional.ofNullable(mergeBranchesByUuid.get(branch.getMergeBranchUuid()));
ProjectPullRequests.PullRequest.Builder builder = ProjectPullRequests.PullRequest.newBuilder();
builder.setKey(branch.getKey());
DbProjectBranches.PullRequestData pullRequestData = Objects.requireNonNull(branch.getPullRequestData(), "Pull request data should be available for branch type PULL_REQUEST");
builder.setBranch(pullRequestData.getBranch());
Optional.ofNullable(Strings.emptyToNull(pullRequestData.getUrl())).ifPresent(builder::setUrl);
Optional.ofNullable(Strings.emptyToNull(pullRequestData.getTitle())).ifPresent(builder::setTitle);
if (mergeBranch.isPresent()) {
String mergeBranchKey = mergeBranch.get().getKey();
builder.setBase(mergeBranchKey);
} else {
builder.setIsOrphan(true);
}
if (StringUtils.isNotEmpty(pullRequestData.getTarget())) {
builder.setTarget(pullRequestData.getTarget());
} else {
mergeBranch.ifPresent(branchDto -> builder.setTarget(branchDto.getKey()));
}
Optional.ofNullable(analysisDate).ifPresent(builder::setAnalysisDate);
setQualityGate(builder, qualityGateMeasure, prStatistics);
response.addPullRequests(builder);
}
private static void setQualityGate(ProjectPullRequests.PullRequest.Builder builder, @Nullable LiveMeasureDto qualityGateMeasure, @Nullable PrStatistics prStatistics) {
ProjectPullRequests.Status.Builder statusBuilder = ProjectPullRequests.Status.newBuilder();
if (qualityGateMeasure != null) {
Optional.ofNullable(qualityGateMeasure.getDataAsString()).ifPresent(statusBuilder::setQualityGateStatus);
}
statusBuilder.setBugs(prStatistics == null ? 0L : prStatistics.getBugs());
statusBuilder.setVulnerabilities(prStatistics == null ? 0L : prStatistics.getVulnerabilities());
statusBuilder.setCodeSmells(prStatistics == null ? 0L : prStatistics.getCodeSmells());
builder.setStatus(statusBuilder);
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.project.ProjectDto;
import org.sonar.server.component.ComponentFinder;
public abstract class ProjectWsAction implements PullRequestWsAction {
private static final String PROJECT_PARAMETER = "project";
private final String actionName;
private final DbClient dbClient;
private final ComponentFinder componentFinder;
protected ProjectWsAction(String actionName, DbClient dbClient, ComponentFinder componentFinder) {
super();
this.actionName = actionName;
this.dbClient = dbClient;
this.componentFinder = componentFinder;
}
@Override
public void define(WebService.NewController context) {
WebService.NewAction action = context.createAction(actionName).setHandler(this);
action.createParam(PROJECT_PARAMETER).setRequired(true);
configureAction(action);
}
protected abstract void configureAction(WebService.NewAction action);
@Override
public void handle(Request request, Response response) {
String projectKey = request.mandatoryParam(PROJECT_PARAMETER);
try (DbSession dbSession = dbClient.openSession(false)) {
ProjectDto project = componentFinder.getProjectByKey(dbSession, projectKey);
handleProjectRequest(project, request, response, dbSession);
}
}
protected abstract void handleProjectRequest(ProjectDto project, Request request, Response response, DbSession dbSession);
protected DbClient getDbClient() {
return dbClient;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import com.google.protobuf.Message;
@FunctionalInterface
interface ProtoBufWriter {
void write(Message message, Request request, Response response);
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action;
import org.sonar.server.ws.WsAction;
public interface PullRequestWsAction extends WsAction {
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 Michael Clarke
* Copyright (C) 2021-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -18,18 +18,11 @@
*/
package com.github.mc1arke.sonarqube.plugin;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.sonar.core.platform.EditionProvider;
import org.sonar.core.platform.PlatformEditionProvider;
import org.sonar.db.DbClient;
import org.sonar.db.newcodeperiod.NewCodePeriodDao;
import org.sonar.server.almsettings.MultipleAlmFeatureProvider;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.newcodeperiod.ws.SetAction;
import org.sonar.server.newcodeperiod.ws.UnsetAction;
import org.sonar.server.user.UserSession;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.io.IOException;
import java.io.InputStream;
@ -40,49 +33,56 @@ import java.lang.instrument.UnmodifiableClassException;
import java.lang.reflect.Field;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.sonar.api.SonarRuntime;
import org.sonar.core.platform.EditionProvider;
import org.sonar.core.platform.PlatformEditionProvider;
import org.sonar.db.DbClient;
import org.sonar.db.newcodeperiod.NewCodePeriodDao;
import org.sonar.server.almsettings.MultipleAlmFeature;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.feature.SonarQubeFeature;
import org.sonar.server.newcodeperiod.ws.SetAction;
import org.sonar.server.newcodeperiod.ws.UnsetAction;
import org.sonar.server.user.UserSession;
public class CommunityBranchAgentTest {
class CommunityBranchAgentTest {
@Test
public void checkErrorThrownIfAgentArgsNotValid() {
void shouldThrowErrorIfAgentArgsNotValid() {
assertThatThrownBy(() -> CommunityBranchAgent.premain("badarg", mock(Instrumentation.class)))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Invalid/missing agent argument");
}
@Test
public void checkRedefineForWebLaunchRedefinesMultipleAlmFeatureClass() throws ReflectiveOperationException, IOException, UnmodifiableClassException, IllegalClassFormatException {
void shouldRedefineMultipleAlmFeatureClassForWebLaunch() throws ReflectiveOperationException, IOException, UnmodifiableClassException, IllegalClassFormatException {
CustomClassloader classLoader = new CustomClassloader();
Instrumentation instrumentation = mock(Instrumentation.class);
CommunityBranchAgent.premain("web", instrumentation);
ArgumentCaptor<ClassFileTransformer> classFileTransformerArgumentCaptor = ArgumentCaptor.forClass(ClassFileTransformer.class);
verify(instrumentation).retransformClasses(MultipleAlmFeatureProvider.class);
verify(instrumentation).retransformClasses(MultipleAlmFeature.class);
verify(instrumentation, times(3)).addTransformer(classFileTransformerArgumentCaptor.capture());
try (InputStream inputStream = MultipleAlmFeatureProvider.class.getResourceAsStream(MultipleAlmFeatureProvider.class.getSimpleName())) {
try (InputStream inputStream = MultipleAlmFeature.class.getResourceAsStream(MultipleAlmFeature.class.getSimpleName() + ".class")) {
byte[] input = IOUtils.toByteArray(inputStream);
byte[] result = classFileTransformerArgumentCaptor.getAllValues().get(0).transform(classLoader, MultipleAlmFeatureProvider.class.getName().replaceAll("\\.", "/"), getClass(), getClass().getProtectionDomain(), input);
Class<?> redefined = classLoader.loadClass(MultipleAlmFeatureProvider.class.getName(), result);
byte[] result = classFileTransformerArgumentCaptor.getAllValues().get(0).transform(classLoader, MultipleAlmFeature.class.getName().replaceAll("\\.", "/"), getClass(), getClass().getProtectionDomain(), input);
Class<SonarQubeFeature> redefined = (Class<SonarQubeFeature>) classLoader.loadClass(MultipleAlmFeature.class.getName(), result);
PlatformEditionProvider platformEditionProvider = mock(PlatformEditionProvider.class);
SonarRuntime sonarRuntime = mock(SonarRuntime.class);
Object multipleAlmFeatureProvider = redefined.getConstructor(PlatformEditionProvider.class).newInstance(platformEditionProvider);
Field editionProviderField = redefined.getDeclaredField("editionProvider");
editionProviderField.setAccessible(true);
assertThat(((EditionProvider) editionProviderField.get(multipleAlmFeatureProvider)).get()).isEqualTo(Optional.of(EditionProvider.Edition.ENTERPRISE));
SonarQubeFeature multipleAlmFeatureProvider = redefined.getConstructor(SonarRuntime.class).newInstance(sonarRuntime);
assertThat(multipleAlmFeatureProvider.isEnabled()).isTrue();
}
}
@Test
public void checkRedefineForWebLaunchRedefinesSetActionClass() throws ReflectiveOperationException, IOException, UnmodifiableClassException, IllegalClassFormatException {
void shouldRedefineSetActionClassForWebLaunch() throws ReflectiveOperationException, IOException, UnmodifiableClassException, IllegalClassFormatException {
CustomClassloader classLoader = new CustomClassloader();
Instrumentation instrumentation = mock(Instrumentation.class);
@ -92,7 +92,7 @@ public class CommunityBranchAgentTest {
verify(instrumentation).retransformClasses(SetAction.class);
verify(instrumentation, times(3)).addTransformer(classFileTransformerArgumentCaptor.capture());
try (InputStream inputStream = SetAction.class.getResourceAsStream(SetAction.class.getSimpleName())) {
try (InputStream inputStream = SetAction.class.getResourceAsStream(SetAction.class.getSimpleName() + ".class")) {
byte[] input = IOUtils.toByteArray(inputStream);
byte[] result = classFileTransformerArgumentCaptor.getAllValues().get(1).transform(classLoader, SetAction.class.getName().replaceAll("\\.", "/"), getClass(), getClass().getProtectionDomain(), input);
@ -114,7 +114,7 @@ public class CommunityBranchAgentTest {
}
@Test
public void checkRedefineForWebLaunchRedefinesUnsetActionClass() throws IOException, UnmodifiableClassException, IllegalClassFormatException, ReflectiveOperationException {
void shouldRedefinesUnsetActionClassForWebLaunch() throws IOException, UnmodifiableClassException, IllegalClassFormatException, ReflectiveOperationException {
CustomClassloader classLoader = new CustomClassloader();
Instrumentation instrumentation = mock(Instrumentation.class);
@ -124,7 +124,7 @@ public class CommunityBranchAgentTest {
verify(instrumentation).retransformClasses(UnsetAction.class);
verify(instrumentation, times(3)).addTransformer(classFileTransformerArgumentCaptor.capture());
try (InputStream inputStream = SetAction.class.getResourceAsStream(SetAction.class.getSimpleName())) {
try (InputStream inputStream = SetAction.class.getResourceAsStream(SetAction.class.getSimpleName() + ".class")) {
byte[] input = IOUtils.toByteArray(inputStream);
byte[] result = classFileTransformerArgumentCaptor.getAllValues().get(2).transform(classLoader, UnsetAction.class.getName().replaceAll("\\.", "/"), getClass(), getClass().getProtectionDomain(), input);
@ -145,13 +145,13 @@ public class CommunityBranchAgentTest {
}
@Test
public void checkRedefineForWebLaunchSkipsNonTargetClass() throws UnmodifiableClassException, ClassNotFoundException, IllegalClassFormatException {
void shouldSkipNonTargetClasForWebLaunch() throws UnmodifiableClassException, ClassNotFoundException, IllegalClassFormatException {
Instrumentation instrumentation = mock(Instrumentation.class);
CommunityBranchAgent.premain("web", instrumentation);
ArgumentCaptor<ClassFileTransformer> classFileTransformerArgumentCaptor = ArgumentCaptor.forClass(ClassFileTransformer.class);
verify(instrumentation).retransformClasses(MultipleAlmFeatureProvider.class);
verify(instrumentation).retransformClasses(MultipleAlmFeature.class);
verify(instrumentation, times(3)).addTransformer(classFileTransformerArgumentCaptor.capture());
byte[] input = new byte[]{1, 2, 3, 4, 5, 6};
@ -161,14 +161,14 @@ public class CommunityBranchAgentTest {
}
@Test
public void checkRedefineForCeLaunchSkipsNonTargetClass() throws UnmodifiableClassException, ClassNotFoundException, IllegalClassFormatException {
void shouldSkipNonTargetClassForCeLunch() throws UnmodifiableClassException, ClassNotFoundException, IllegalClassFormatException {
Instrumentation instrumentation = mock(Instrumentation.class);
CommunityBranchAgent.premain("ce", instrumentation);
ArgumentCaptor<ClassFileTransformer> classFileTransformerArgumentCaptor = ArgumentCaptor.forClass(ClassFileTransformer.class);
verify(instrumentation).retransformClasses(PlatformEditionProvider.class);
verify(instrumentation).addTransformer(classFileTransformerArgumentCaptor.capture());
verify(instrumentation, times(2)).addTransformer(classFileTransformerArgumentCaptor.capture());
byte[] input = new byte[]{1, 2, 3, 4, 5, 6};
byte[] result = classFileTransformerArgumentCaptor.getValue().transform(getClass().getClassLoader(), "com/github/mc1arke/Dummy", getClass(), getClass().getProtectionDomain(), input);
@ -176,25 +176,38 @@ public class CommunityBranchAgentTest {
assertThat(result).isEqualTo(input);
}
@Test
public void checkRedefineForCeLaunchRedefinesTargetClass() throws ReflectiveOperationException, IOException, UnmodifiableClassException, IllegalClassFormatException {
void shouldRedefineTargetClassesForCeLaunch() throws ReflectiveOperationException, IOException, UnmodifiableClassException, IllegalClassFormatException {
Instrumentation instrumentation = mock(Instrumentation.class);
CommunityBranchAgent.premain("ce", instrumentation);
ArgumentCaptor<ClassFileTransformer> classFileTransformerArgumentCaptor = ArgumentCaptor.forClass(ClassFileTransformer.class);
verify(instrumentation).retransformClasses(MultipleAlmFeature.class);
verify(instrumentation).retransformClasses(PlatformEditionProvider.class);
verify(instrumentation).addTransformer(classFileTransformerArgumentCaptor.capture());
verify(instrumentation, times(2)).addTransformer(classFileTransformerArgumentCaptor.capture());
try (InputStream inputStream = PlatformEditionProvider.class.getResourceAsStream(PlatformEditionProvider.class.getSimpleName() + ".class")) {
byte[] input = IOUtils.toByteArray(inputStream);
byte[] result = classFileTransformerArgumentCaptor.getValue().transform(getClass().getClassLoader(), PlatformEditionProvider.class.getName().replaceAll("\\.", "/"), getClass(), getClass().getProtectionDomain(), input);
byte[] result = classFileTransformerArgumentCaptor.getAllValues().get(0).transform(getClass().getClassLoader(), PlatformEditionProvider.class.getName().replaceAll("\\.", "/"), getClass(), getClass().getProtectionDomain(), input);
CustomClassloader classLoader = new CustomClassloader();
Class<EditionProvider> redefined = (Class<EditionProvider>) classLoader.loadClass(PlatformEditionProvider.class.getName(), result);
assertThat(redefined.getConstructor().newInstance().get()).isEqualTo(Optional.of(EditionProvider.Edition.DEVELOPER));
assertThat(redefined.getConstructor().newInstance().get()).contains(EditionProvider.Edition.DEVELOPER);
}
try (InputStream inputStream = MultipleAlmFeature.class.getResourceAsStream(MultipleAlmFeature.class.getSimpleName() + ".class")) {
byte[] input = IOUtils.toByteArray(inputStream);
byte[] result = classFileTransformerArgumentCaptor.getAllValues().get(1).transform(getClass().getClassLoader(), MultipleAlmFeature.class.getName().replaceAll("\\.", "/"), getClass(), getClass().getProtectionDomain(), input);
CustomClassloader classLoader = new CustomClassloader();
SonarRuntime sonarRuntime = mock(SonarRuntime.class);
Class<MultipleAlmFeature> redefined = (Class<MultipleAlmFeature>) classLoader.loadClass(MultipleAlmFeature.class.getName(), result);
SonarQubeFeature multipleAlmFeatureProvider = redefined.getConstructor(SonarRuntime.class).newInstance(sonarRuntime);
assertThat(multipleAlmFeatureProvider.isEnabled()).isTrue();
}
}
@ -204,6 +217,6 @@ public class CommunityBranchAgentTest {
return defineClass(name, value, 0, value.length);
}
};
}
}

View File

@ -18,26 +18,7 @@
*/
package com.github.mc1arke.sonarqube.plugin;
import com.github.mc1arke.sonarqube.plugin.ce.CommunityReportAnalysisComponentProvider;
import com.github.mc1arke.sonarqube.plugin.scanner.CommunityBranchConfigurationLoader;
import com.github.mc1arke.sonarqube.plugin.scanner.CommunityBranchParamsValidator;
import com.github.mc1arke.sonarqube.plugin.scanner.CommunityProjectBranchesLoader;
import com.github.mc1arke.sonarqube.plugin.scanner.CommunityProjectPullRequestsLoader;
import com.github.mc1arke.sonarqube.plugin.server.CommunityBranchFeatureExtension;
import com.github.mc1arke.sonarqube.plugin.server.CommunityBranchSupportDelegate;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.sonar.api.Plugin;
import org.sonar.api.SonarQubeSide;
import org.sonar.core.extension.CoreExtension;
import java.util.Arrays;
import java.util.Collections;
import static org.junit.Assert.assertEquals;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@ -45,20 +26,31 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.sonar.api.Plugin;
import org.sonar.api.SonarQubeSide;
import org.sonar.core.extension.CoreExtension;
import com.github.mc1arke.sonarqube.plugin.ce.CommunityReportAnalysisComponentProvider;
import com.github.mc1arke.sonarqube.plugin.scanner.CommunityBranchConfigurationLoader;
import com.github.mc1arke.sonarqube.plugin.scanner.CommunityBranchParamsValidator;
import com.github.mc1arke.sonarqube.plugin.scanner.CommunityProjectBranchesLoader;
import com.github.mc1arke.sonarqube.plugin.scanner.CommunityProjectPullRequestsLoader;
import com.github.mc1arke.sonarqube.plugin.server.CommunityBranchFeatureExtension;
import com.github.mc1arke.sonarqube.plugin.server.CommunityBranchSupportDelegate;
/**
* @author Michael Clarke
*/
public class CommunityBranchPluginTest {
private final ExpectedException expectedException = ExpectedException.none();
@Rule
public ExpectedException expectedException() {
return expectedException;
}
class CommunityBranchPluginTest {
@Test
public void testScannerSideDefine() {
void shouldDefineClassesForScannerSide() {
final CommunityBranchPlugin testCase = new CommunityBranchPlugin();
final Plugin.Context context = mock(Plugin.Context.class, Mockito.RETURNS_DEEP_STUBS);
@ -71,13 +63,12 @@ public class CommunityBranchPluginTest {
.addExtensions(argumentCaptor.capture(), argumentCaptor.capture(), argumentCaptor.capture());
assertEquals(Arrays.asList(CommunityProjectBranchesLoader.class, CommunityProjectPullRequestsLoader.class,
CommunityBranchConfigurationLoader.class, CommunityBranchParamsValidator.class),
argumentCaptor.getAllValues().subList(0, 4));
assertThat(argumentCaptor.getAllValues().subList(0, 4)).isEqualTo(Arrays.asList(CommunityProjectBranchesLoader.class, CommunityProjectPullRequestsLoader.class,
CommunityBranchConfigurationLoader.class, CommunityBranchParamsValidator.class));
}
@Test
public void testNonScannerSideDefine() {
void shouldDefineClassesForServerSide() {
final CommunityBranchPlugin testCase = new CommunityBranchPlugin();
final Plugin.Context context = mock(Plugin.Context.class, Mockito.RETURNS_DEEP_STUBS);
@ -89,7 +80,7 @@ public class CommunityBranchPluginTest {
}
@Test
public void testComputeEngineSideLoad() {
void shouldDefineClassesForComputeEngineSide() {
final CommunityBranchPlugin testCase = new CommunityBranchPlugin();
final CoreExtension.Context context = mock(CoreExtension.Context.class, Mockito.RETURNS_DEEP_STUBS);
@ -97,17 +88,16 @@ public class CommunityBranchPluginTest {
testCase.load(context);
final ArgumentCaptor<Class> argumentCaptor = ArgumentCaptor.forClass(Class.class);
final ArgumentCaptor<Class<?>> argumentCaptor = ArgumentCaptor.forClass(Class.class);
verify(context, times(2)).addExtensions(argumentCaptor.capture(), argumentCaptor.capture());
assertEquals(Collections.singletonList(CommunityReportAnalysisComponentProvider.class),
argumentCaptor.getAllValues().subList(0, 1));
assertThat(argumentCaptor.getAllValues().subList(0, 1)).isEqualTo(List.of(CommunityReportAnalysisComponentProvider.class));
}
@Test
public void testServerSideLoad() {
void shouldAddExtensionsForServerSideLoad() {
final CommunityBranchPlugin testCase = new CommunityBranchPlugin();
final CoreExtension.Context context = mock(CoreExtension.Context.class, Mockito.RETURNS_DEEP_STUBS);
@ -118,14 +108,13 @@ public class CommunityBranchPluginTest {
final ArgumentCaptor<Object> argumentCaptor = ArgumentCaptor.forClass(Object.class);
verify(context, times(2)).addExtensions(argumentCaptor.capture(), argumentCaptor.capture());
assertEquals(25, argumentCaptor.getAllValues().size());
assertThat(argumentCaptor.getAllValues()).hasSize(29);
assertEquals(Arrays.asList(CommunityBranchFeatureExtension.class, CommunityBranchSupportDelegate.class),
argumentCaptor.getAllValues().subList(0, 2));
assertThat(argumentCaptor.getAllValues().subList(0, 2)).isEqualTo(List.of(CommunityBranchFeatureExtension.class, CommunityBranchSupportDelegate.class));
}
@Test
public void testLoad() {
void shouldNotAddAnyExtensionsForScannerSideLoad() {
final CommunityBranchPlugin testCase = new CommunityBranchPlugin();
final CoreExtension.Context context = mock(CoreExtension.Context.class, Mockito.RETURNS_DEEP_STUBS);
@ -137,7 +126,7 @@ public class CommunityBranchPluginTest {
}
@Test
public void testGetName() {
assertEquals("Community Branch Plugin", new CommunityBranchPlugin().getName());
void shouldReturnPluginNameForGetName() {
assertThat(new CommunityBranchPlugin().getName()).isEqualTo("Community Branch Plugin");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -18,76 +18,50 @@
*/
package com.github.mc1arke.sonarqube.plugin.ce;
import org.hamcrest.core.IsEqual;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.db.component.BranchType;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
import org.junit.jupiter.api.Test;
import org.sonar.db.component.BranchType;
/**
* @author Michael Clarke
*/
public class CommunityBranchTest {
private final ExpectedException expectedException = ExpectedException.none();
@Rule
public ExpectedException expectedException() {
return expectedException;
}
class CommunityBranchTest {
@Test
public void testGenerateKeyMainBranchNullFileOfPath() {
void testGenerateKeyMainBranchNullFileOfPath() {
CommunityBranch testCase = new CommunityBranch("name", BranchType.PULL_REQUEST, true, null, null, null);
assertEquals("projectKey", testCase.generateKey("projectKey", null));
assertThat(testCase.generateKey("projectKey", null)).isEqualTo("projectKey");
}
@Test
public void testGenerateKeyMainBranchNonNullFileOfPathHolder() {
void testGenerateKeyMainBranchNonNullFileOfPathHolder() {
CommunityBranch testCase = new CommunityBranch("name", BranchType.PULL_REQUEST, true, null, null, null);
assertEquals("projectKey", testCase.generateKey("projectKey", ""));
assertThat(testCase.generateKey("projectKey", "")).isEqualTo("projectKey");
}
@Test
public void testGenerateKeyMainBranchNonNullFileOfPathContent() {
void testGenerateKeyMainBranchNonNullFileOfPathContent() {
CommunityBranch testCase = new CommunityBranch("name", BranchType.PULL_REQUEST, true, null, null, null);
assertEquals("projectKey:path", testCase.generateKey("projectKey", "path"));
assertThat(testCase.generateKey("projectKey", "path")).isEqualTo("projectKey:path");
}
@Test
public void testGenerateKeyNonMainBranchNonNullFileOfPathContentPullRequest() {
CommunityBranch testCase =
new CommunityBranch("name", BranchType.PULL_REQUEST, false, null, "pullRequestKey", null);
assertEquals("projectKey:path:PULL_REQUEST:pullRequestKey", testCase.generateKey("projectKey", "path"));
void testGetPulRequestKey() {
assertThat(new CommunityBranch("name", BranchType.PULL_REQUEST, false, null, "prKey", null)
.getPullRequestKey()).isEqualTo("prKey");
}
@Test
public void testGenerateKeyNonMainBranchNonNullFileOfPathContentBranch() {
CommunityBranch testCase = new CommunityBranch("name", BranchType.BRANCH, false, null, null, null);
assertEquals("projectKey:path:BRANCH:name", testCase.generateKey("projectKey", "path"));
}
@Test
public void testGetPulRequestKey() {
assertEquals("prKey", new CommunityBranch("name", BranchType.PULL_REQUEST, false, null, "prKey", null)
.getPullRequestKey());
}
@Test
public void testGetPulRequestKeyNonPullRequest() {
expectedException
.expectMessage(IsEqual.equalTo("Only a branch of type PULL_REQUEST can have a pull request ID"));
expectedException.expect(IllegalStateException.class);
new CommunityBranch("name", BranchType.BRANCH, false, null, "prKey", null).getPullRequestKey();
void testGetPulRequestKeyNonPullRequest() {
CommunityBranch underTest = new CommunityBranch("name", BranchType.BRANCH, false, null, "prKey", null);
assertThatThrownBy(underTest::getPullRequestKey)
.isInstanceOf(IllegalStateException.class)
.hasMessage("Only a branch of type PULL_REQUEST can have a pull request ID");
}
}

View File

@ -95,11 +95,11 @@ public class GitlabMergeRequestDecoratorIntegrationTest {
AlmSettingDto almSettingDto = mock(AlmSettingDto.class);
when(almSettingDto.getDecryptedPersonalAccessToken(any())).thenReturn("token");
when(almSettingDto.getUrl()).thenReturn(wireMockRule.baseUrl()+"/api/v4");
when(projectAlmSettingDto.getAlmRepo()).thenReturn(repositorySlug);
AnalysisDetails analysisDetails = mock(AnalysisDetails.class);
when(almSettingDto.getUrl()).thenReturn(wireMockRule.baseUrl()+"/api/v4");
when(projectAlmSettingDto.getAlmRepo()).thenReturn(repositorySlug);
when(projectAlmSettingDto.getMonorepo()).thenReturn(true);
when(analysisDetails.getQualityGateStatus()).thenReturn(status);
when(analysisDetails.getAnalysisProjectKey()).thenReturn(projectKey);
when(analysisDetails.getPullRequestId()).thenReturn(Long.toString(mergeRequestIid));

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 Michael Clarke
* Copyright (C) 2019-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -18,17 +18,24 @@
*/
package com.github.mc1arke.sonarqube.plugin.server;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertTrue;
import org.junit.jupiter.api.Test;
/**
* @author Michael Clarke
*/
public class CommunityBranchFeatureExtensionTest {
class CommunityBranchFeatureExtensionTest {
private final CommunityBranchFeatureExtension underTest = new CommunityBranchFeatureExtension();
@Test
public void testEnabled() {
assertTrue(new CommunityBranchFeatureExtension().isEnabled());
void shouldReturnNameThatFrontEndLooksFor() {
assertThat(underTest.getName()).isEqualTo("branch-support");
}
@Test
void shouldReturnEnabledForFeature() {
assertThat(underTest.isEnabled()).isTrue();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -18,114 +18,91 @@
*/
package com.github.mc1arke.sonarqube.plugin.server;
import org.hamcrest.core.IsEqual;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.sonar.core.util.SequenceUuidFactory;
import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDao;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.ce.queue.BranchSupport;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.time.Clock;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.sonar.api.config.Configuration;
import org.sonar.core.util.SequenceUuidFactory;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.BranchDao;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDao;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.ce.queue.BranchSupport;
import org.sonar.server.setting.ProjectConfigurationLoader;
/**
* @author Michael Clarke
*/
public class CommunityBranchSupportDelegateTest {
class CommunityBranchSupportDelegateTest {
private final ExpectedException expectedException = ExpectedException.none();
@Rule
public ExpectedException expectedException() {
return expectedException;
}
private final Clock clock = mock(Clock.class);
private final SequenceUuidFactory sequenceUuidFactory = mock(SequenceUuidFactory.class);
private final DbClient dbClient = mock(DbClient.class);
private final ProjectConfigurationLoader projectConfigurationLoader = mock(ProjectConfigurationLoader.class);
private final CommunityBranchSupportDelegate underTest = new CommunityBranchSupportDelegate(sequenceUuidFactory, dbClient, clock, projectConfigurationLoader);
@Test
public void testCreateComponentKeyBranchType() {
void shouldReturnValidComponentKeyForBranchParameters() {
Map<String, String> params = new HashMap<>();
params.put("branch", "release-1.1");
params.put("branchType", "BRANCH");
BranchSupport.ComponentKey componentKey =
new CommunityBranchSupportDelegate(new SequenceUuidFactory(), mock(DbClient.class), mock(Clock.class))
.createComponentKey("yyy", params);
BranchSupport.ComponentKey componentKey = underTest.createComponentKey("yyy", params);
assertEquals("yyy:BRANCH:release-1.1", componentKey.getDbKey());
assertEquals("yyy", componentKey.getKey());
assertFalse(componentKey.getPullRequestKey().isPresent());
assertFalse(componentKey.isMainBranch());
assertTrue(componentKey.getBranchName().isPresent());
assertEquals("release-1.1", componentKey.getBranchName().get());
assertTrue(componentKey.getMainBranchComponentKey().isMainBranch());
assertThat(componentKey).usingRecursiveComparison().isEqualTo(new CommunityComponentKey("yyy", "release-1.1", null));
}
@Test
public void testCreateComponentKeyPullRequest() {
void shouldReturnValidComponentKeyForPullRequestParameters() {
Map<String, String> params = new HashMap<>();
params.put("pullRequest", "pullrequestkey");
CommunityComponentKey componentKey =
new CommunityBranchSupportDelegate(new SequenceUuidFactory(), mock(DbClient.class), mock(Clock.class))
.createComponentKey("yyy", params);
assertEquals("yyy:PULL_REQUEST:pullrequestkey", componentKey.getDbKey());
assertEquals("yyy", componentKey.getKey());
assertTrue(componentKey.getPullRequestKey().isPresent());
assertEquals("pullrequestkey", componentKey.getPullRequestKey().get());
assertFalse(componentKey.isMainBranch());
assertFalse(componentKey.getBranchName().isPresent());
assertTrue(componentKey.getMainBranchComponentKey().isMainBranch());
CommunityComponentKey mainBranchComponentKey = componentKey.getMainBranchComponentKey();
assertSame(mainBranchComponentKey, mainBranchComponentKey.getMainBranchComponentKey());
CommunityComponentKey componentKey = underTest.createComponentKey("aaa", params);
assertThat(componentKey).usingRecursiveComparison().isEqualTo(new CommunityComponentKey("aaa", null, "pullrequestkey"));
}
@Test
public void testCreateComponentKeyMissingBranchTypeAndPullParameters() {
void shouldThrowExceptionOnCreateComponentKeyMissingBranchTypeAndPullParameters() {
Map<String, String> params = new HashMap<>();
expectedException.expect(IllegalArgumentException.class);
expectedException
.expectMessage(IsEqual.equalTo("One of 'branchType' or 'pullRequest' parameters must be specified"));
new CommunityBranchSupportDelegate(new SequenceUuidFactory(), mock(DbClient.class), mock(Clock.class))
.createComponentKey("xxx", params);
assertThatThrownBy(() -> underTest.createComponentKey("xxx", params)).isInstanceOf(IllegalArgumentException.class)
.hasMessage("One of 'branchType' or 'pullRequest' parameters must be specified");
}
@Test
public void testCreateComponentKeyInvalidBranchTypeParameter() {
void shouldThrowExceptoinOnCreateComponentKeyInvalidBranchTypeParameter() {
Map<String, String> params = new HashMap<>();
params.put("branchType", "abc");
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage(IsEqual.equalTo("Unsupported branch type 'abc'"));
new CommunityBranchSupportDelegate(new SequenceUuidFactory(), mock(DbClient.class), mock(Clock.class))
.createComponentKey("xxx", params);
assertThatThrownBy(() -> underTest.createComponentKey("xxx", params)).isInstanceOf(IllegalArgumentException.class)
.hasMessage("Unsupported branch type 'abc'");
}
@Test
public void testCreateBranchComponentComponentKeyComponentDtoKeyMismatch() {
void shouldThrowExceptionIfBranchAndComponentKeysMismatch() {
DbSession dbSession = mock(DbSession.class);
ComponentDto componentDto = mock(ComponentDto.class);
@ -139,128 +116,34 @@ public class CommunityBranchSupportDelegateTest {
when(branchDto.getUuid()).thenReturn("componentUuid");
when(branchDto.getBranchType()).thenReturn(BranchType.BRANCH);
Clock clock = mock(Clock.class);
when(clock.millis()).thenReturn(12345678901234L);
BranchSupport.ComponentKey componentKey = mock(BranchSupport.ComponentKey.class);
when(componentKey.getKey()).thenReturn("componentKey");
when(componentKey.getDbKey()).thenReturn("dbKey");
when(componentKey.getBranchName()).thenReturn(Optional.of("dummy"));
when(componentKey.getPullRequestKey()).thenReturn(Optional.empty());
ComponentDao componentDao = spy(mock(ComponentDao.class));
DbClient dbClient = mock(DbClient.class);
when(dbClient.componentDao()).thenReturn(componentDao);
UuidFactory uuidFactory = new SequenceUuidFactory();
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(IsEqual.equalTo("Component Key and Main Component Key do not match"));
new CommunityBranchSupportDelegate(uuidFactory, dbClient, clock)
.createBranchComponent(dbSession, componentKey, componentDto, branchDto);
}
@Test
public void testCreateBranchComponent() {
DbSession dbSession = mock(DbSession.class);
ComponentDto componentDto = mock(ComponentDto.class);
when(componentDto.getKey()).thenReturn("componentKey");
when(componentDto.uuid()).thenReturn("componentUuid");
ComponentDto copyComponentDto = spy(ComponentDto.class);
when(componentDto.copy()).thenReturn(copyComponentDto);
BranchDto branchDto = mock(BranchDto.class);
when(branchDto.getUuid()).thenReturn("componentUuid");
when(branchDto.getKey()).thenReturn("nonDummy");
Clock clock = mock(Clock.class);
when(clock.millis()).thenReturn(12345678901234L);
BranchSupport.ComponentKey componentKey = mock(BranchSupport.ComponentKey.class);
when(componentKey.getKey()).thenReturn("componentKey");
when(componentKey.getDbKey()).thenReturn("dbKey");
when(componentKey.getBranchName()).thenReturn(Optional.of("dummy"));
when(componentKey.getPullRequestKey()).thenReturn(Optional.empty());
ComponentDao componentDao = mock(ComponentDao.class);
DbClient dbClient = mock(DbClient.class);
when(dbClient.componentDao()).thenReturn(componentDao);
UuidFactory uuidFactory = mock(UuidFactory.class);
when(uuidFactory.create()).then(new Answer<String>() {
private int i = 0;
@Override
public String answer(InvocationOnMock invocationOnMock) {
return "uuid" + (i++);
}
});
ComponentDto result = new CommunityBranchSupportDelegate(uuidFactory, dbClient, clock)
.createBranchComponent(dbSession, componentKey, componentDto, branchDto);
verify(componentDao).insert(dbSession, copyComponentDto);
verify(copyComponentDto).setUuid("uuid0");
verify(copyComponentDto).setProjectUuid("uuid0");
verify(copyComponentDto).setRootUuid("uuid0");
verify(copyComponentDto).setUuidPath(".");
verify(copyComponentDto).setModuleUuidPath(".uuid0.");
verify(copyComponentDto).setMainBranchProjectUuid("componentUuid");
verify(copyComponentDto).setDbKey(componentKey.getDbKey());
verify(copyComponentDto).setCreatedAt(new Date(12345678901234L));
assertSame(copyComponentDto, result);
assertThatThrownBy(() -> underTest.createBranchComponent(dbSession, componentKey, componentDto, branchDto)).isInstanceOf(IllegalStateException.class)
.hasMessage("Component Key and Main Component Key do not match");
}
@Test
public void testCreateBranchComponentUseExistingDto() {
DbSession dbSession = mock(DbSession.class);
ComponentDto componentDto = mock(ComponentDto.class);
when(componentDto.getKey()).thenReturn("componentKey");
when(componentDto.uuid()).thenReturn("componentUuid");
ComponentDto copyComponentDto = spy(ComponentDto.class);
when(componentDto.copy()).thenReturn(copyComponentDto);
BranchDto branchDto = mock(BranchDto.class);
when(branchDto.getUuid()).thenReturn("componentUuid");
when(branchDto.getKey()).thenReturn("dummy");
when(branchDto.getBranchType()).thenReturn(BranchType.BRANCH);
Clock clock = mock(Clock.class);
when(clock.millis()).thenReturn(1234567890123L);
BranchSupport.ComponentKey componentKey = mock(BranchSupport.ComponentKey.class);
when(componentKey.getKey()).thenReturn("componentKey");
when(componentKey.getDbKey()).thenReturn("dbKey");
when(componentKey.getBranchName()).thenReturn(Optional.of("dummy"));
when(componentKey.getPullRequestKey()).thenReturn(Optional.empty());
ComponentDao componentDao = spy(mock(ComponentDao.class));
DbClient dbClient = mock(DbClient.class);
when(dbClient.componentDao()).thenReturn(componentDao);
UuidFactory uuidFactory = new SequenceUuidFactory();
ComponentDto result = new CommunityBranchSupportDelegate(uuidFactory, dbClient, clock)
.createBranchComponent(dbSession, componentKey, componentDto, branchDto);
assertSame(componentDto, result);
static Stream<Arguments> shouldCreateComponentAndBranchDtoIfValidationPassesData() {
return Stream.of(
Arguments.of("branchName", null, BranchType.BRANCH, new String[0], false),
Arguments.of(null, "pullRequestKey", BranchType.PULL_REQUEST, new String[0], false),
Arguments.of("complex-name", null, BranchType.BRANCH, new String[]{"abc", "def", "comp.*"}, true)
);
}
@Test
public void testCreateBranchComponentUseExistingDto2() {
@MethodSource("shouldCreateComponentAndBranchDtoIfValidationPassesData")
@ParameterizedTest
void shouldCreateComponentAndBranchDtoIfValidationPasses(String branchName, String pullRequestKey, BranchType branchType,
String[] retainBranchesConfiguration, boolean excludedFromPurge) {
DbSession dbSession = mock(DbSession.class);
ComponentDto componentDto = mock(ComponentDto.class);
@ -269,44 +152,58 @@ public class CommunityBranchSupportDelegateTest {
ComponentDto copyComponentDto = mock(ComponentDto.class);
when(componentDto.copy()).thenReturn(copyComponentDto);
when(copyComponentDto.setBranchUuid(any())).thenReturn(copyComponentDto);
when(copyComponentDto.setKey(any())).thenReturn(copyComponentDto);
when(copyComponentDto.setRootUuid(any())).thenReturn(copyComponentDto);
when(copyComponentDto.setUuidPath(any())).thenReturn(copyComponentDto);
when(copyComponentDto.setModuleUuidPath(any())).thenReturn(copyComponentDto);
when(copyComponentDto.setUuid(any())).thenReturn(copyComponentDto);
when(copyComponentDto.setMainBranchProjectUuid(any())).thenReturn(copyComponentDto);
when(copyComponentDto.setCreatedAt(any())).thenReturn(copyComponentDto);
BranchDto branchDto = mock(BranchDto.class);
when(branchDto.getUuid()).thenReturn("componentUuid");
when(branchDto.getKey()).thenReturn("dummy");
when(branchDto.getBranchType()).thenReturn(BranchType.BRANCH);
when(branchDto.getKey()).thenReturn("nonDummy");
Clock clock = mock(Clock.class);
when(clock.millis()).thenReturn(1234567890123L);
when(clock.millis()).thenReturn(12345678901234L);
BranchSupport.ComponentKey componentKey = mock(BranchSupport.ComponentKey.class);
when(componentKey.getKey()).thenReturn("componentKey");
when(componentKey.getDbKey()).thenReturn("dbKey");
when(componentKey.getBranchName()).thenReturn(Optional.empty());
when(componentKey.getPullRequestKey()).thenReturn(Optional.empty());
when(componentKey.getBranchName()).thenReturn(Optional.ofNullable(branchName));
when(componentKey.getPullRequestKey()).thenReturn(Optional.ofNullable(pullRequestKey));
BranchDao branchDao = mock(BranchDao.class);
ComponentDao componentDao = mock(ComponentDao.class);
DbClient dbClient = mock(DbClient.class);
when(dbClient.componentDao()).thenReturn(componentDao);
when(dbClient.branchDao()).thenReturn(branchDao);
UuidFactory uuidFactory = new SequenceUuidFactory();
when(sequenceUuidFactory.create()).thenReturn("uuid0");
ComponentDto result = new CommunityBranchSupportDelegate(uuidFactory, dbClient, clock)
.createBranchComponent(dbSession, componentKey, componentDto, branchDto);
Configuration configuration = mock(Configuration.class);
when(configuration.getStringArray(any())).thenReturn(retainBranchesConfiguration);
when(projectConfigurationLoader.loadProjectConfiguration(any(), any())).thenReturn(configuration);
ComponentDto result = underTest.createBranchComponent(dbSession, componentKey, componentDto, branchDto);
verify(componentDao).insert(dbSession, copyComponentDto);
verify(copyComponentDto).setUuid("1");
verify(copyComponentDto).setProjectUuid("1");
verify(copyComponentDto).setRootUuid("1");
verify(copyComponentDto).setUuid("uuid0");
verify(copyComponentDto).setRootUuid("uuid0");
verify(copyComponentDto).setUuidPath(".");
verify(copyComponentDto).setModuleUuidPath(".1.");
verify(copyComponentDto).setModuleUuidPath(".uuid0.");
verify(copyComponentDto).setMainBranchProjectUuid("componentUuid");
verify(copyComponentDto).setDbKey(componentKey.getDbKey());
verify(copyComponentDto).setCreatedAt(new Date(1234567890123L));
verify(copyComponentDto).setCreatedAt(new Date(12345678901234L));
assertSame(copyComponentDto, result);
assertThat(result).isSameAs(copyComponentDto);
ArgumentCaptor<BranchDto> branchDtoArgumentCaptor = ArgumentCaptor.forClass(BranchDto.class);
verify(branchDao).insert(eq(dbSession), branchDtoArgumentCaptor.capture());
assertThat(branchDtoArgumentCaptor.getValue()).usingRecursiveComparison().isEqualTo(new BranchDto()
.setBranchType(branchType)
.setExcludeFromPurge(excludedFromPurge)
.setProjectUuid("componentUuid")
.setKey(branchType == BranchType.BRANCH ? branchName : pullRequestKey)
.setUuid("uuid0"));
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
class MonoRepoFeatureTest {
private final MonoRepoFeature underTest = new MonoRepoFeature();
@Test
void shouldMatchNameRequiredByFrontEnd() {
assertThat(underTest.getName()).isEqualTo("monorepo");
}
@Test
void shouldSetFeatureAsEnabled() {
assertThat(underTest.isEnabled()).isTrue();
}
}

View File

@ -1,6 +1,27 @@
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
/*
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import org.junit.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
@ -13,43 +34,38 @@ import org.sonar.db.project.ProjectDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.user.UserSession;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class DeleteBindingActionTest {
class DeleteBindingActionTest {
@Test
public void testDefine() {
void shouldDefineEndpointWithParameters() {
DbClient dbClient = mock(DbClient.class);
UserSession userSession = mock(UserSession.class);
ComponentFinder componentFinder = mock(ComponentFinder.class);
DeleteBindingAction testCase = new DeleteBindingAction(dbClient, userSession, componentFinder);
WebService.NewParam keyParam = mock(WebService.NewParam.class);
when(keyParam.setMaximumLength(eq(200))).thenReturn(keyParam);
when(keyParam.setMaximumLength(200)).thenReturn(keyParam);
WebService.NewParam newKeyParam = mock(WebService.NewParam.class);
when(newKeyParam.setMaximumLength(eq(200))).thenReturn(newKeyParam);
when(newKeyParam.setMaximumLength(200)).thenReturn(newKeyParam);
WebService.NewController newController = mock(WebService.NewController.class);
WebService.NewAction newAction = mock(WebService.NewAction.class);
when(newController.createAction(eq("delete_binding"))).thenReturn(newAction);
when(newAction.setPost(eq(true))).thenReturn(newAction);
when(newAction.setHandler(eq(testCase))).thenReturn(newAction);
when(newAction.createParam(eq("project"))).thenReturn(keyParam);
when(newController.createAction("delete_binding")).thenReturn(newAction);
when(newAction.setPost(true)).thenReturn(newAction);
when(newAction.setHandler(testCase)).thenReturn(newAction);
when(newAction.createParam("project")).thenReturn(keyParam);
testCase.define(newController);
verify(newAction).setHandler(eq(testCase));
verify(newAction).setHandler(testCase);
verify(keyParam).setRequired(true);
}
@Test
public void testHandle() {
void shouldHandleEndpointWithValidRequest() {
DbClient dbClient = mock(DbClient.class);
DbSession dbSession = mock(DbSession.class);
when(dbClient.openSession(eq(false))).thenReturn(dbSession);
when(dbClient.openSession(false)).thenReturn(dbSession);
AlmSettingDao almSettingDao = mock(AlmSettingDao.class);
when(dbClient.almSettingDao()).thenReturn(almSettingDao);
@ -60,18 +76,18 @@ public class DeleteBindingActionTest {
ProjectDto componentDto = mock(ProjectDto.class);
ComponentFinder componentFinder = mock(ComponentFinder.class);
when(componentFinder.getProjectByKey(eq(dbSession), eq("projectKey"))).thenReturn(componentDto);
when(componentFinder.getProjectByKey(dbSession, "projectKey")).thenReturn(componentDto);
DeleteBindingAction testCase = new DeleteBindingAction(dbClient, userSession, componentFinder);
Request request = mock(Request.class, Mockito.RETURNS_DEEP_STUBS);
when(request.param("project")).thenReturn("projectKey");
when(request.mandatoryParam("project")).thenReturn("projectKey");
Response response = mock(Response.class, Mockito.RETURNS_DEEP_STUBS);
testCase.handle(request, response);
verify(dbSession).commit();
verify(projectAlmSettingDao).deleteByProject(eq(dbSession), eq(componentDto));
verify(projectAlmSettingDao).deleteByProject(dbSession, componentDto);
verify(response).noContent();
verify(userSession).checkProjectPermission("admin", componentDto);

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,9 +16,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
@ -33,15 +33,16 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class SetAzureBindingActionTest {
class SetAzureBindingActionTest {
@Test
public void testConfigureAction() {
void shouldDefineActionWithRequiredParameters() {
DbClient dbClient = mock(DbClient.class);
UserSession userSession = mock(UserSession.class);
ComponentFinder componentFinder = mock(ComponentFinder.class);
WebService.NewAction newAction = mock(WebService.NewAction.class);
when(newAction.setPost(anyBoolean())).thenReturn(newAction);
WebService.NewParam repositoryNameParameter = mock(WebService.NewParam.class);
when(repositoryNameParameter.setMaximumLength(any(Integer.class))).thenReturn(repositoryNameParameter);
@ -58,16 +59,23 @@ public class SetAzureBindingActionTest {
when(almSettingParameter.setRequired(anyBoolean())).thenReturn(almSettingParameter);
when(newAction.createParam("almSetting")).thenReturn(almSettingParameter);
WebService.NewParam monoRepoParameter = mock(WebService.NewParam.class);
when(monoRepoParameter.setRequired(anyBoolean())).thenReturn(monoRepoParameter);
when(newAction.createParam("monorepo")).thenReturn(monoRepoParameter);
SetAzureBindingAction testCase = new SetAzureBindingAction(dbClient, componentFinder, userSession);
testCase.configureAction(newAction);
verify(newAction).setPost(true);
verify(repositoryNameParameter).setRequired(true);
verify(projectNameParameter).setRequired(true);
verify(almSettingParameter).setRequired(true);
verify(monoRepoParameter).setRequired(true);
verify(monoRepoParameter).setBooleanPossibleValues();
}
@Test
public void testCreateProjectAlmSettingDto() {
void shouldHandleRequestWithValidParameters() {
DbClient dbClient = mock(DbClient.class);
UserSession userSession = mock(UserSession.class);
ComponentFinder componentFinder = mock(ComponentFinder.class);
@ -77,9 +85,9 @@ public class SetAzureBindingActionTest {
when(request.mandatoryParam("projectName")).thenReturn("project");
SetAzureBindingAction testCase = new SetAzureBindingAction(dbClient, componentFinder, userSession);
ProjectAlmSettingDto result = testCase.createProjectAlmSettingDto("projectUuid", "settingsUuid", request);
ProjectAlmSettingDto result = testCase.createProjectAlmSettingDto("projectUuid", "settingsUuid", true, request);
assertThat(result).isEqualToComparingFieldByField(new ProjectAlmSettingDto().setProjectUuid("projectUuid").setAlmSettingUuid("settingsUuid").setAlmRepo("repository").setAlmSlug("project").setMonorepo(false));
assertThat(result).usingRecursiveComparison().isEqualTo(new ProjectAlmSettingDto().setProjectUuid("projectUuid").setAlmSettingUuid("settingsUuid").setAlmRepo("repository").setAlmSlug("project").setMonorepo(true));
}
}

View File

@ -1,6 +1,35 @@
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
/*
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
@ -14,21 +43,10 @@ import org.sonar.db.project.ProjectDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.user.UserSession;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class SetBindingActionTest {
class SetBindingActionTest {
@Test
public void testDefine() {
void shouldDefineActionWithRequiredParameters() {
DbClient dbClient = mock(DbClient.class);
ComponentFinder componentFinder = mock(ComponentFinder.class);
UserSession userSession = mock(UserSession.class);
@ -36,7 +54,7 @@ public class SetBindingActionTest {
SetBindingAction testCase = new SetBindingAction(dbClient, componentFinder, userSession, "dummy") {
@Override
protected ProjectAlmSettingDto createProjectAlmSettingDto(String projectUuid, String settingsUuid, Request request) {
protected ProjectAlmSettingDto createProjectAlmSettingDto(String projectUuid, String settingsUuid, boolean monoRepo, Request request) {
return projectAlmSettingDto;
}
};
@ -46,37 +64,40 @@ public class SetBindingActionTest {
WebService.NewController newController = mock(WebService.NewController.class);
WebService.NewAction newAction = mock(WebService.NewAction.class);
when(newController.createAction(any())).thenReturn(newAction);
when(newAction.setPost(eq(true))).thenReturn(newAction);
when(newAction.setHandler(eq(testCase))).thenReturn(newAction);
when(newAction.setPost(true)).thenReturn(newAction);
when(newAction.setHandler(testCase)).thenReturn(newAction);
when(newAction.createParam(any())).then(i -> {
WebService.NewParam newParam = mock(WebService.NewParam.class);
paramMap.put(i.getArgument(0), newParam);
when(newParam.setRequired(anyBoolean())).thenReturn(newParam);
return newParam;
});
testCase.define(newController);
verify(newAction).createParam(eq("project"));
verify(newAction).createParam(eq("almSetting"));
verify(newAction).createParam("project");
verify(newAction).createParam("almSetting");
verify(paramMap.get("project")).setRequired(true);
verify(paramMap.get("almSetting")).setRequired(true);
verify(paramMap.get("monorepo")).setRequired(true);
verify(paramMap.get("monorepo")).setBooleanPossibleValues();
}
@Test
public void testHandle() {
void shouldHandleRequestWithRequiredParameters() {
DbClient dbClient = mock(DbClient.class);
DbSession dbSession = mock(DbSession.class);
when(dbClient.openSession(eq(false))).thenReturn(dbSession);
when(dbClient.openSession(false)).thenReturn(dbSession);
AlmSettingDao almSettingDao = mock(AlmSettingDao.class);
AlmSettingDto almSettingDto = mock(AlmSettingDto.class);
when(almSettingDto.getUuid()).thenReturn("almSettingsUuid");
when(almSettingDao.selectByKey(eq(dbSession), eq("almSetting"))).thenReturn(Optional.of(almSettingDto));
when(almSettingDao.selectByKey(dbSession, "almSetting")).thenReturn(Optional.of(almSettingDto));
when(dbClient.almSettingDao()).thenReturn(almSettingDao);
ProjectAlmSettingDao projectAlmSettingDao = mock(ProjectAlmSettingDao.class);
when(dbClient.projectAlmSettingDao()).thenReturn(projectAlmSettingDao);
ComponentFinder componentFinder = mock(ComponentFinder.class);
ProjectDto componentDto = mock(ProjectDto.class);
when(componentDto.getUuid()).thenReturn("projectUuid");
when(componentFinder.getProjectByKey(eq(dbSession), eq("project"))).thenReturn(componentDto);
when(componentFinder.getProjectByKey(dbSession, "project")).thenReturn(componentDto);
UserSession userSession = mock(UserSession.class);
ThreadLocal<WebService.NewAction> capturedAction = new ThreadLocal<>();
ProjectAlmSettingDto projectAlmSettingDto = mock(ProjectAlmSettingDto.class);
@ -88,7 +109,7 @@ public class SetBindingActionTest {
}
@Override
protected ProjectAlmSettingDto createProjectAlmSettingDto(String projectUuid, String settingsUuid, Request request) {
protected ProjectAlmSettingDto createProjectAlmSettingDto(String projectUuid, String settingsUuid, boolean monoRepo, Request request) {
assertThat(projectUuid).isEqualTo("projectUuid");
assertThat(settingsUuid).isEqualTo("almSettingsUuid");
return projectAlmSettingDto;
@ -99,7 +120,7 @@ public class SetBindingActionTest {
Response response = mock(Response.class);
when(request.mandatoryParam("almSetting")).thenReturn("almSetting");
when(request.param("project")).thenReturn("project");
when(request.mandatoryParam("project")).thenReturn("project");
testCase.handle(request, response);

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,9 +16,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
@ -33,15 +33,16 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class SetBitBucketBindingActionTest {
class SetBitBucketBindingActionTest {
@Test
public void testConfigureAction() {
void shouldDefineActionWithRequiredParameters() {
DbClient dbClient = mock(DbClient.class);
UserSession userSession = mock(UserSession.class);
ComponentFinder componentFinder = mock(ComponentFinder.class);
WebService.NewAction newAction = mock(WebService.NewAction.class);
when(newAction.setPost(anyBoolean())).thenReturn(newAction);
WebService.NewParam slugParameter = mock(WebService.NewParam.class);
when(slugParameter.setMaximumLength(any(Integer.class))).thenReturn(slugParameter);
@ -58,18 +59,26 @@ public class SetBitBucketBindingActionTest {
when(almSettingParameter.setRequired(anyBoolean())).thenReturn(almSettingParameter);
when(newAction.createParam("almSetting")).thenReturn(almSettingParameter);
WebService.NewParam monoRepoParameter = mock(WebService.NewParam.class);
when(monoRepoParameter.setRequired(anyBoolean())).thenReturn(monoRepoParameter);
when(newAction.createParam("monorepo")).thenReturn(monoRepoParameter);
SetBitbucketBindingAction testCase = new SetBitbucketBindingAction(dbClient, componentFinder, userSession);
testCase.configureAction(newAction);
verify(newAction).setPost(true);
verify(slugParameter).setRequired(true);
verify(repositoryParameter).setRequired(true);
verify(almSettingParameter).setRequired(true);
verify(monoRepoParameter).setRequired(true);
verify(monoRepoParameter).setBooleanPossibleValues();
}
@Test
public void testCreateProjectAlmSettingDto() {
void shouldHandleRequestWithRequiredParameters() {
DbClient dbClient = mock(DbClient.class);
UserSession userSession = mock(UserSession.class);
ComponentFinder componentFinder = mock(ComponentFinder.class);
@ -79,9 +88,9 @@ public class SetBitBucketBindingActionTest {
when(request.mandatoryParam("repository")).thenReturn("repository");
SetBitbucketBindingAction testCase = new SetBitbucketBindingAction(dbClient, componentFinder, userSession);
ProjectAlmSettingDto result = testCase.createProjectAlmSettingDto("projectUuid", "settingsUuid", request);
ProjectAlmSettingDto result = testCase.createProjectAlmSettingDto("projectUuid", "settingsUuid", true, request);
assertThat(result).isEqualToComparingFieldByField(new ProjectAlmSettingDto().setProjectUuid("projectUuid").setAlmSettingUuid("settingsUuid").setAlmRepo("repository").setAlmSlug("slug").setMonorepo(false));
assertThat(result).usingRecursiveComparison().isEqualTo(new ProjectAlmSettingDto().setProjectUuid("projectUuid").setAlmSettingUuid("settingsUuid").setAlmRepo("repository").setAlmSlug("slug").setMonorepo(true));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 Michael Clarke
* Copyright (C) 2021-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,9 +16,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
@ -33,15 +33,16 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class SetBitBucketCloudBindingActionTest {
class SetBitBucketCloudBindingActionTest {
@Test
public void testConfigureAction() {
void shouldDefineActionWithValidParameters() {
DbClient dbClient = mock(DbClient.class);
UserSession userSession = mock(UserSession.class);
ComponentFinder componentFinder = mock(ComponentFinder.class);
WebService.NewAction newAction = mock(WebService.NewAction.class);
when(newAction.setPost(anyBoolean())).thenReturn(newAction);
WebService.NewParam repositoryParameter = mock(WebService.NewParam.class);
when(repositoryParameter.setMaximumLength(any(Integer.class))).thenReturn(repositoryParameter);
@ -53,16 +54,24 @@ public class SetBitBucketCloudBindingActionTest {
when(almSettingParameter.setRequired(anyBoolean())).thenReturn(almSettingParameter);
when(newAction.createParam("almSetting")).thenReturn(almSettingParameter);
WebService.NewParam monoRepoParameter = mock(WebService.NewParam.class);
when(monoRepoParameter.setRequired(anyBoolean())).thenReturn(monoRepoParameter);
when(newAction.createParam("monorepo")).thenReturn(monoRepoParameter);
SetBitbucketCloudBindingAction testCase = new SetBitbucketCloudBindingAction(dbClient, componentFinder, userSession);
testCase.configureAction(newAction);
verify(newAction).setPost(true);
verify(repositoryParameter).setRequired(true);
verify(almSettingParameter).setRequired(true);
verify(monoRepoParameter).setRequired(true);
verify(monoRepoParameter).setBooleanPossibleValues();
}
@Test
public void testCreateProjectAlmSettingDto() {
void shouldHandleRequestWithValidParameters() {
DbClient dbClient = mock(DbClient.class);
UserSession userSession = mock(UserSession.class);
ComponentFinder componentFinder = mock(ComponentFinder.class);
@ -71,9 +80,9 @@ public class SetBitBucketCloudBindingActionTest {
when(request.mandatoryParam("repository")).thenReturn("repository");
SetBitbucketCloudBindingAction testCase = new SetBitbucketCloudBindingAction(dbClient, componentFinder, userSession);
ProjectAlmSettingDto result = testCase.createProjectAlmSettingDto("projectUuid", "settingsUuid", request);
ProjectAlmSettingDto result = testCase.createProjectAlmSettingDto("projectUuid", "settingsUuid", true, request);
assertThat(result).isEqualToComparingFieldByField(new ProjectAlmSettingDto().setProjectUuid("projectUuid").setAlmSettingUuid("settingsUuid").setAlmRepo("repository").setMonorepo(false));
assertThat(result).usingRecursiveComparison().isEqualTo(new ProjectAlmSettingDto().setProjectUuid("projectUuid").setAlmSettingUuid("settingsUuid").setAlmRepo("repository").setMonorepo(true));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,9 +16,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
@ -33,15 +33,16 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class SetGithubBindingActionTest {
class SetGithubBindingActionTest {
@Test
public void testConfigureAction() {
void shouldDefineActionWithRequiredParameters() {
DbClient dbClient = mock(DbClient.class);
UserSession userSession = mock(UserSession.class);
ComponentFinder componentFinder = mock(ComponentFinder.class);
WebService.NewAction newAction = mock(WebService.NewAction.class);
when(newAction.setPost(anyBoolean())).thenReturn(newAction);
WebService.NewParam repositoryParameter = mock(WebService.NewParam.class);
when(repositoryParameter.setMaximumLength(any(Integer.class))).thenReturn(repositoryParameter);
@ -58,15 +59,22 @@ public class SetGithubBindingActionTest {
when(almSettingParameter.setRequired(anyBoolean())).thenReturn(almSettingParameter);
when(newAction.createParam("almSetting")).thenReturn(almSettingParameter);
WebService.NewParam monoRepoParameter = mock(WebService.NewParam.class);
when(monoRepoParameter.setRequired(anyBoolean())).thenReturn(monoRepoParameter);
when(newAction.createParam("monorepo")).thenReturn(monoRepoParameter);
SetGithubBindingAction testCase = new SetGithubBindingAction(dbClient, componentFinder, userSession);
testCase.configureAction(newAction);
verify(newAction).setPost(true);
verify(repositoryParameter).setRequired(true);
verify(almSettingParameter).setRequired(true);
verify(monoRepoParameter).setRequired(true);
verify(monoRepoParameter).setBooleanPossibleValues();
}
@Test
public void testCreateProjectAlmSettingDto() {
void shouldHandleRequestWithRequiredParmaeters() {
DbClient dbClient = mock(DbClient.class);
UserSession userSession = mock(UserSession.class);
ComponentFinder componentFinder = mock(ComponentFinder.class);
@ -76,9 +84,9 @@ public class SetGithubBindingActionTest {
when(request.paramAsBoolean("summaryCommentEnabled")).thenReturn(true);
SetGithubBindingAction testCase = new SetGithubBindingAction(dbClient, componentFinder, userSession);
ProjectAlmSettingDto result = testCase.createProjectAlmSettingDto("projectUuid", "settingsUuid", request);
ProjectAlmSettingDto result = testCase.createProjectAlmSettingDto("projectUuid", "settingsUuid", true, request);
assertThat(result).isEqualToComparingFieldByField(new ProjectAlmSettingDto().setProjectUuid("projectUuid").setAlmSettingUuid("settingsUuid").setAlmRepo("repository").setSummaryCommentEnabled(true).setMonorepo(false));
assertThat(result).usingRecursiveComparison().isEqualTo(new ProjectAlmSettingDto().setProjectUuid("projectUuid").setAlmSettingUuid("settingsUuid").setAlmRepo("repository").setSummaryCommentEnabled(true).setMonorepo(true));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 Michael Clarke
* Copyright (C) 2020-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,9 +16,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
@ -33,15 +33,16 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
public class SetGitlabBindingActionTest {
class SetGitlabBindingActionTest {
@Test
public void testConfigureAction() {
void shouldDefineActionWithRequiredParameters() {
DbClient dbClient = mock(DbClient.class);
UserSession userSession = mock(UserSession.class);
ComponentFinder componentFinder = mock(ComponentFinder.class);
WebService.NewAction newAction = mock(WebService.NewAction.class);
when(newAction.setPost(anyBoolean())).thenReturn(newAction);
WebService.NewParam repositoryParameter = mock(WebService.NewParam.class);
when(newAction.createParam("repository")).thenReturn(repositoryParameter);
@ -50,16 +51,23 @@ public class SetGitlabBindingActionTest {
when(almSettingParameter.setRequired(anyBoolean())).thenReturn(almSettingParameter);
when(newAction.createParam("almSetting")).thenReturn(almSettingParameter);
WebService.NewParam monoRepoParameter = mock(WebService.NewParam.class);
when(monoRepoParameter.setRequired(anyBoolean())).thenReturn(monoRepoParameter);
when(newAction.createParam("monorepo")).thenReturn(monoRepoParameter);
SetGitlabBindingAction testCase = new SetGitlabBindingAction(dbClient, componentFinder, userSession);
testCase.configureAction(newAction);
verify(newAction).createParam("repository");
verify(newAction).setPost(true);
verify(almSettingParameter).setRequired(true);
verify(monoRepoParameter).setRequired(true);
verify(monoRepoParameter).setBooleanPossibleValues();
}
@Test
public void testCreateProjectAlmSettingDto() {
void shouldHandleRequestWithValidParameters() {
DbClient dbClient = mock(DbClient.class);
UserSession userSession = mock(UserSession.class);
ComponentFinder componentFinder = mock(ComponentFinder.class);
@ -68,9 +76,9 @@ public class SetGitlabBindingActionTest {
when(request.param("repository")).thenReturn("repositoryId");
SetGitlabBindingAction testCase = new SetGitlabBindingAction(dbClient, componentFinder, userSession);
ProjectAlmSettingDto result = testCase.createProjectAlmSettingDto("projectUuid", "settingsUuid", request);
ProjectAlmSettingDto result = testCase.createProjectAlmSettingDto("projectUuid", "settingsUuid", true, request);
assertThat(result).isEqualToComparingFieldByField(new ProjectAlmSettingDto().setProjectUuid("projectUuid").setAlmSettingUuid("settingsUuid").setAlmRepo("repositoryId").setMonorepo(false));
assertThat(result).usingRecursiveComparison().isEqualTo(new ProjectAlmSettingDto().setProjectUuid("projectUuid").setAlmSettingUuid("settingsUuid").setAlmRepo("repositoryId").setMonorepo(true));
verify(request).param("repository");
verifyNoMoreInteractions(request);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 Michael Clarke
* Copyright (C) 2021-2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -16,7 +16,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.action;
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.binding.action;
import com.github.mc1arke.sonarqube.plugin.InvalidConfigurationException;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.validator.Validator;
@ -198,7 +198,7 @@ class ValidateBindingActionTest {
when(almSettingDao.selectByUuid(dbSession, almUuid)).thenReturn(Optional.of(almSettingDto));
when(dbClient.almSettingDao()).thenReturn(almSettingDao);
when(request.param("project")).thenReturn("project");
when(request.mandatoryParam("project")).thenReturn("project");
underTest.handle(request, response);

View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.sonar.api.server.ws.WebService;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action.PullRequestWsAction;
class PullRequestWsTest {
@Test
void shouldCallDefineOnEachAction() {
PullRequestWsAction[] pullRequestWsActions = new PullRequestWsAction[]{mock(PullRequestWsAction.class), mock(PullRequestWsAction.class), mock(PullRequestWsAction.class)};
WebService.Context context = mock(WebService.Context.class);
WebService.NewController controller = mock(WebService.NewController.class);
when(context.createController(any())).thenReturn(controller);
new PullRequestWs(pullRequestWsActions).define(context);
for (PullRequestWsAction pullRequestWsAction : pullRequestWsActions) {
verify(pullRequestWsAction).define(controller);
}
verify(context).createController("api/project_pull_requests");
}
}

View File

@ -0,0 +1,142 @@
/*
* Copyright (C) 2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
import org.sonar.db.component.BranchDao;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.BranchType;
import org.sonar.db.project.ProjectDto;
import org.sonar.server.component.ComponentCleanerService;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.user.UserSession;
class DeleteActionTest {
private final DbClient dbClient = mock(DbClient.class);
private final UserSession userSession = mock(UserSession.class);
private final ComponentFinder componentFinder = mock(ComponentFinder.class);
private final ComponentCleanerService componentCleanerService = mock(ComponentCleanerService.class);
private final DeleteAction underTest = new DeleteAction(dbClient, componentFinder, userSession, componentCleanerService);
@Test
void shouldDefineEndpointWithAllParameters() {
WebService.NewController newController = mock(WebService.NewController.class);
WebService.NewAction newAction = mock(WebService.NewAction.class);
when(newAction.setHandler(any())).thenReturn(newAction);
when(newController.createAction(any())).thenReturn(newAction);
WebService.NewParam projectParam = mock(WebService.NewParam.class);
WebService.NewParam pullRequestParam = mock(WebService.NewParam.class);
when(newAction.createParam(any())).thenReturn(projectParam, pullRequestParam);
when(newAction.setPost(anyBoolean())).thenReturn(newAction);
underTest.define(newController);
verify(newController).createAction("delete");
verify(newAction).setHandler(underTest);
verify(newAction).createParam("project");
verify(newAction).createParam("pullRequest");
verify(newAction).setPost(true);
verifyNoMoreInteractions(newAction);
verify(projectParam).setRequired(true);
verify(pullRequestParam).setRequired(true);
verifyNoMoreInteractions(projectParam);
verifyNoMoreInteractions(pullRequestParam);
verifyNoMoreInteractions(newController);
}
@Test
void shouldExecuteRequestWithValidParameters() {
Request request = mock(Request.class);
when(request.mandatoryParam("project")).thenReturn("project");
when(request.mandatoryParam("pullRequest")).thenReturn("pullRequestId");
when(componentFinder.getProjectByKey(any(), any())).thenReturn(new ProjectDto().setKey("projectKey").setUuid("uuid0"));
when(userSession.checkLoggedIn()).thenReturn(userSession);
BranchDto pullRequest = new BranchDto().setBranchType(BranchType.PULL_REQUEST);
BranchDao branchDao = mock(BranchDao.class);
when(dbClient.branchDao()).thenReturn(branchDao);
when(branchDao.selectByPullRequestKey(any(), any(), any())).thenReturn(Optional.of(pullRequest));
Response response = mock(Response.class);
underTest.handle(request, response);
verify(componentCleanerService).deleteBranch(any(), eq(pullRequest));
verify(response).noContent();
}
@Test
void shouldNotPerformDeleteIfUserNotLoggedIn() {
Request request = mock(Request.class);
when(request.mandatoryParam("project")).thenReturn("project");
when(request.mandatoryParam("pullRequest")).thenReturn("pullRequestId");
when(componentFinder.getProjectByKey(any(), any())).thenReturn(new ProjectDto().setKey("projectKey").setUuid("uuid0"));
when(userSession.checkLoggedIn()).thenThrow(new UnauthorizedException("Dummy"));
Response response = mock(Response.class);
assertThatThrownBy(() -> underTest.handle(request, response)).isInstanceOf(UnauthorizedException.class).hasMessage("Dummy");
verify(componentCleanerService, never()).deleteBranch(any(), any());
verify(response, never()).noContent();
}
@Test
void shouldNotPerformDeleteIfUserNotProjectAdmin() {
Request request = mock(Request.class);
when(request.mandatoryParam("project")).thenReturn("project");
when(request.mandatoryParam("pullRequest")).thenReturn("pullRequestId");
when(componentFinder.getProjectByKey(any(), any())).thenReturn(new ProjectDto().setKey("projectKey").setUuid("uuid0"));
when(userSession.checkLoggedIn()).thenReturn(userSession);
when(userSession.checkProjectPermission(any(), any())).thenThrow(new UnauthorizedException("Dummy"));
Response response = mock(Response.class);
assertThatThrownBy(() -> underTest.handle(request, response)).isInstanceOf(UnauthorizedException.class).hasMessage("Dummy");
verify(componentCleanerService, never()).deleteBranch(any(), any());
verify(response, never()).noContent();
}
}

View File

@ -0,0 +1,209 @@
/*
* Copyright (C) 2022 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
import org.sonar.db.component.BranchDao;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.BranchType;
import org.sonar.db.component.SnapshotDao;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.LiveMeasureDao;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.protobuf.DbProjectBranches;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.issue.index.IssueIndex;
import org.sonar.server.issue.index.PrStatistics;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.ProjectPullRequests;
class ListActionTest {
private final DbClient dbClient = mock(DbClient.class);
private final UserSession userSession = mock(UserSession.class);
private final ComponentFinder componentFinder = mock(ComponentFinder.class);
private final IssueIndex issueIndex = mock(IssueIndex.class);
private final ProtoBufWriter protoBufWriter = mock(ProtoBufWriter.class);
private final ListAction underTest = new ListAction(dbClient, componentFinder, userSession, issueIndex, protoBufWriter);
@Test
void shouldDefineEndpointWithProjectParameter() {
WebService.NewController newController = mock(WebService.NewController.class);
WebService.NewAction newAction = mock(WebService.NewAction.class);
when(newAction.setHandler(any())).thenReturn(newAction);
when(newController.createAction(any())).thenReturn(newAction);
WebService.NewParam projectParam = mock(WebService.NewParam.class);
when(newAction.createParam(any())).thenReturn(projectParam);
underTest.define(newController);
verify(newController).createAction("list");
verify(newAction).setHandler(underTest);
verify(newAction).createParam("project");
verifyNoMoreInteractions(newAction);
verify(projectParam).setRequired(true);
verifyNoMoreInteractions(projectParam);
verifyNoMoreInteractions(newController);
}
@Test
void shouldExecuteRequestWithValidParameter() {
Request request = mock(Request.class);
when(request.mandatoryParam("project")).thenReturn("project");
when(componentFinder.getProjectByKey(any(), any())).thenReturn(new ProjectDto().setKey("projectKey").setUuid("uuid0"));
when(userSession.hasPermission(any())).thenReturn(true);
BranchDao branchDao = mock(BranchDao.class);
when(dbClient.branchDao()).thenReturn(branchDao);
when(branchDao.selectByProject(any(), any())).thenReturn(List.of(new BranchDto()
.setBranchType(BranchType.PULL_REQUEST)
.setKey("prKey")
.setUuid("uuid1")
.setMergeBranchUuid("uuid2")
.setPullRequestData(DbProjectBranches.PullRequestData.newBuilder()
.setBranch("prBranch")
.setTitle("title")
.setTarget("target")
.setUrl("url")
.build()),
new BranchDto()
.setBranchType(BranchType.PULL_REQUEST)
.setKey("prKey2")
.setUuid("uuid3")
.setMergeBranchUuid("orphan")
.setPullRequestData(DbProjectBranches.PullRequestData.newBuilder()
.setBranch("prBranch2")
.setTitle("title2")
.setUrl("url2")
.build()),
new BranchDto()
.setBranchType(BranchType.PULL_REQUEST)
.setKey("prKey3")
.setUuid("uuid4")
.setMergeBranchUuid("uuid2")
.setPullRequestData(DbProjectBranches.PullRequestData.newBuilder()
.setBranch("prBranch2")
.setTitle("title3")
.setUrl("url3")
.build())));
when(branchDao.selectByUuids(any(), any())).thenReturn(List.of(new BranchDto()
.setUuid("uuid2")
.setKey("branch2Key")));
LiveMeasureDao liveMeasureDao = mock(LiveMeasureDao.class);
when(dbClient.liveMeasureDao()).thenReturn(liveMeasureDao);
when(liveMeasureDao.selectByComponentUuidsAndMetricKeys(any(), any(), any())).thenReturn(List.of(new LiveMeasureDto()
.setComponentUuid("uuid1")
.setData("live measure")));
SnapshotDao snapshotDao = mock(SnapshotDao.class);
when(dbClient.snapshotDao()).thenReturn(snapshotDao);
when(snapshotDao.selectLastAnalysesByRootComponentUuids(any(), any())).thenReturn(List.of(new SnapshotDto().setComponentUuid("componentUuid").setCreatedAt(1234L)));
when(issueIndex.searchBranchStatistics(any(), any())).thenReturn(List.of(new PrStatistics("uuid1", Map.of(ScannerReport.IssueType.BUG.name(), 11L, ScannerReport.IssueType.CODE_SMELL.name(), 22L, ScannerReport.IssueType.VULNERABILITY.name(), 33L)),
new PrStatistics("uuid4", Map.of(ScannerReport.IssueType.BUG.name(), 1L, ScannerReport.IssueType.CODE_SMELL.name(), 2L, ScannerReport.IssueType.VULNERABILITY.name(), 3L))));
Response response = mock(Response.class);
ProjectPullRequests.ListWsResponse expected = ProjectPullRequests.ListWsResponse.newBuilder()
.addPullRequests(ProjectPullRequests.PullRequest.newBuilder()
.setKey("prKey")
.setTitle("title")
.setBranch("prBranch")
.setBase("branch2Key")
.setStatus(ProjectPullRequests.Status.newBuilder()
.setQualityGateStatus("live measure")
.setBugs(11)
.setVulnerabilities(33)
.setCodeSmells(22)
.build())
.setUrl("url")
.setTarget("target")
.build())
.addPullRequests(ProjectPullRequests.PullRequest.newBuilder()
.setKey("prKey2")
.setTitle("title2")
.setBranch("prBranch2")
.setStatus(ProjectPullRequests.Status.newBuilder()
.setBugs(0)
.setCodeSmells(0)
.setVulnerabilities(0)
.build())
.setIsOrphan(true)
.setUrl("url2"))
.addPullRequests(ProjectPullRequests.PullRequest.newBuilder()
.setKey("prKey3")
.setTitle("title3")
.setBranch("prBranch2")
.setBase("branch2Key")
.setStatus(ProjectPullRequests.Status.newBuilder()
.setBugs(1)
.setVulnerabilities(3)
.setCodeSmells(2)
.build())
.setUrl("url3")
.setTarget("branch2Key")
.build())
.build();
underTest.handle(request, response);
ArgumentCaptor<ProjectPullRequests.ListWsResponse> messageArgumentCaptor = ArgumentCaptor.forClass(ProjectPullRequests.ListWsResponse.class);
verify(protoBufWriter).write(messageArgumentCaptor.capture(), eq(request), eq(response));
assertThat(messageArgumentCaptor.getValue()).usingRecursiveComparison().isEqualTo(expected);
}
@Test
void shouldNotExecuteRequestIfUserDoesNotHaveAnyPermissions() {
Request request = mock(Request.class);
when(request.mandatoryParam("project")).thenReturn("project");
Response response = mock(Response.class);
assertThatThrownBy(() -> underTest.handle(request, response)).isInstanceOf(ForbiddenException.class);
verifyNoMoreInteractions(protoBufWriter);
}
}