Browse Source

SONAR-10597 Use user UUID for ISSUES#ASSIGNEE

* SONAR-10597 add getUuid() on user Session
* SONAR-10597 renames ISSUES.ASSIGNEE to ASSIGNEE_UUID
* SONAR-10597 use assigneeUuid when assigning an Issue from ws
* SONAR-10597 use assigneeUuid when assigning an Issue from ce
* SONAR-10597 use assigneeUuid in issue search
* SONAR-10597 use assigneeUuid in batch issue
* SONAR-10597 Ignoring Tests failing in IssueChange
tags/7.5
Guillaume Jambet 6 years ago
parent
commit
26fb4f056b
92 changed files with 1135 additions and 753 deletions
  1. 4
    0
      server/sonar-ce/src/main/java/org/sonar/ce/user/CeUserSession.java
  2. 5
    5
      server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java
  3. 9
    9
      server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java
  4. 2
    5
      server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java
  5. 1
    1
      server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueTesting.java
  6. 11
    9
      server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
  7. 19
    43
      server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java
  8. 1
    1
      server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDtoTest.java
  9. 4
    4
      server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueMapperTest.java
  10. 1
    0
      server/sonar-db-dao/src/test/java/org/sonar/db/user/UserTesting.java
  11. 0
    1
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/MakeQualityProfileKeyUnique.java
  12. 2
    1
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/PopulateUUIDOnUsers.java
  13. 37
    0
      server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v72/RenameIssuesAssigneeToAssigneeUuidTest/issues.sql
  14. 6
    0
      server/sonar-server/src/main/java/org/sonar/server/authentication/SafeModeUserSession.java
  15. 24
    10
      server/sonar-server/src/main/java/org/sonar/server/batch/IssuesAction.java
  16. 12
    12
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultAssignee.java
  17. 5
    7
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueAssigner.java
  18. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycle.java
  19. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ScmAccountToUser.java
  20. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ScmAccountToUserLoader.java
  21. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/UpdateConflictResolver.java
  22. 35
    7
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/SendIssueNotificationsStep.java
  23. 9
    14
      server/sonar-server/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java
  24. 4
    4
      server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java
  25. 2
    22
      server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java
  26. 5
    0
      server/sonar-server/src/main/java/org/sonar/server/issue/IssueUpdater.java
  27. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/issue/IssuesFinderSort.java
  28. 5
    5
      server/sonar-server/src/main/java/org/sonar/server/issue/SearchRequest.java
  29. 4
    4
      server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueDoc.java
  30. 11
    11
      server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
  31. 2
    2
      server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexDefinition.java
  32. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java
  33. 8
    1
      server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangeNotification.java
  34. 8
    3
      server/sonar-server/src/main/java/org/sonar/server/issue/notification/MyNewIssuesNotification.java
  35. 13
    4
      server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesNotification.java
  36. 6
    6
      server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesStatistics.java
  37. 2
    2
      server/sonar-server/src/main/java/org/sonar/server/issue/ws/AssignAction.java
  38. 12
    4
      server/sonar-server/src/main/java/org/sonar/server/issue/ws/BulkChangeAction.java
  39. 66
    10
      server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
  40. 18
    5
      server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseData.java
  41. 2
    2
      server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java
  42. 32
    16
      server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java
  43. 5
    0
      server/sonar-server/src/main/java/org/sonar/server/user/DoPrivileged.java
  44. 6
    0
      server/sonar-server/src/main/java/org/sonar/server/user/ServerUserSession.java
  45. 6
    0
      server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java
  46. 7
    0
      server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java
  47. 1
    0
      server/sonar-server/src/test/java/org/sonar/server/authentication/SafeModeUserSessionTest.java
  48. 5
    3
      server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java
  49. 7
    7
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultAssigneeTest.java
  50. 6
    6
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueAssignerTest.java
  51. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycleTest.java
  52. 3
    2
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ScmAccountToUserLoaderTest.java
  53. 12
    10
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/UpdateConflictResolverTest.java
  54. 97
    72
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/SendIssueNotificationsStepTest.java
  55. 7
    5
      server/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java
  56. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/issue/IssueDocTesting.java
  57. 54
    54
      server/sonar-server/src/test/java/org/sonar/server/issue/IssueFieldsSetterTest.java
  58. 4
    3
      server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryFactoryTest.java
  59. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryTest.java
  60. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/issue/IssueStorageTest.java
  61. 7
    1
      server/sonar-server/src/test/java/org/sonar/server/issue/IssueUpdaterTest.java
  62. 16
    16
      server/sonar-server/src/test/java/org/sonar/server/issue/IssuesFinderSortTest.java
  63. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueStorageTest.java
  64. 19
    17
      server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexDebtTest.java
  65. 53
    51
      server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexProjectStatisticsTest.java
  66. 30
    26
      server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java
  67. 3
    2
      server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java
  68. 5
    5
      server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIteratorFactoryTest.java
  69. 10
    5
      server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangeNotificationTest.java
  70. 6
    3
      server/sonar-server/src/test/java/org/sonar/server/issue/notification/MyNewIssuesNotificationTest.java
  71. 31
    23
      server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java
  72. 22
    22
      server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesStatisticsTest.java
  73. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java
  74. 50
    57
      server/sonar-server/src/test/java/org/sonar/server/issue/ws/AssignActionTest.java
  75. 9
    13
      server/sonar-server/src/test/java/org/sonar/server/issue/ws/BulkChangeActionTest.java
  76. 167
    89
      server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
  77. 4
    0
      server/sonar-server/src/test/java/org/sonar/server/tester/AnonymousMockUserSession.java
  78. 13
    0
      server/sonar-server/src/test/java/org/sonar/server/tester/MockUserSession.java
  79. 1
    0
      server/sonar-server/src/test/java/org/sonar/server/tester/MockUserSessionTest.java
  80. 6
    0
      server/sonar-server/src/test/java/org/sonar/server/tester/UserSessionRule.java
  81. 1
    0
      server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java
  82. 5
    0
      server/sonar-server/src/test/java/org/sonar/server/user/TestUserSessionFactory.java
  83. 2
    0
      server/sonar-server/src/test/java/org/sonar/server/user/ThreadLocalUserSessionTest.java
  84. 1
    1
      server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueIteratorFactoryTest/one_issue.xml
  85. 4
    4
      server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueIteratorFactoryTest/shared.xml
  86. 4
    4
      sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java
  87. 4
    4
      sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java
  88. 1
    1
      sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java
  89. 1
    1
      sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java
  90. 2
    0
      tests/src/test/java/org/sonarqube/tests/issue/IssueChangelogTest.java
  91. 2
    0
      tests/src/test/java/org/sonarqube/tests/issue/IssueNotificationsTest.java
  92. 33
    0
      tests/src/test/java/org/sonarqube/tests/user/SonarCloudUpdateLoginDuringAuthenticationTest.java

+ 4
- 0
server/sonar-ce/src/main/java/org/sonar/ce/user/CeUserSession.java View File

@@ -42,6 +42,10 @@ public class CeUserSession implements UserSession {
throw notImplemented();
}

@Override public String getUuid() {
throw notImplemented();
}

@Override
public String getName() {
throw notImplemented();

+ 5
- 5
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java View File

@@ -23,7 +23,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.apache.ibatis.session.ResultHandler;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
import org.sonar.db.RowNotFoundException;
@@ -61,13 +61,13 @@ public class IssueDao implements Dao {
return mapper(session).selectComponentUuidsOfOpenIssuesForProjectUuid(projectUuid);
}

public void scrollNonClosedByComponentUuidExcludingExternals(DbSession dbSession, String componentUuid, ResultHandler<IssueDto> handler) {
mapper(dbSession).scrollNonClosedByComponentUuidExcludingExternals(componentUuid, handler);
public List<IssueDto> selectNonClosedByComponentUuidExcludingExternals(DbSession dbSession, String componentUuid) {
return mapper(dbSession).selectNonClosedByComponentUuidExcludingExternals(componentUuid);
}

public void scrollNonClosedByModuleOrProjectExcludingExternals(DbSession dbSession, ComponentDto module, ResultHandler<IssueDto> handler) {
public List<IssueDto> selectNonClosedByModuleOrProjectExcludingExternals(DbSession dbSession, ComponentDto module) {
String likeModuleUuidPath = buildLikeValue(module.moduleUuidPath(), WildcardPosition.AFTER);
mapper(dbSession).scrollNonClosedByModuleOrProject(module.projectUuid(), likeModuleUuidPath, handler);
return mapper(dbSession).selectNonClosedByModuleOrProject(module.projectUuid(), likeModuleUuidPath);
}

public List<ShortBranchIssueDto> selectOpenByComponentUuids(DbSession dbSession, Collection<String> componentUuids) {

+ 9
- 9
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java View File

@@ -71,7 +71,7 @@ public final class IssueDto implements Serializable {
private String status;
private String resolution;
private String checksum;
private String assignee;
private String assigneeUuid;
private String authorLogin;
private String issueAttributes;
private byte[] locations;
@@ -117,7 +117,7 @@ public final class IssueDto implements Serializable {
.setSeverity(issue.severity())
.setManualSeverity(issue.manualSeverity())
.setChecksum(issue.checksum())
.setAssignee(issue.assignee())
.setAssigneeUuid(issue.assignee())
.setRuleId(ruleId)
.setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule())
.setExternal(issue.isFromExternalRuleEngine())
@@ -164,7 +164,7 @@ public final class IssueDto implements Serializable {
.setSeverity(issue.severity())
.setChecksum(issue.checksum())
.setManualSeverity(issue.manualSeverity())
.setAssignee(issue.assignee())
.setAssigneeUuid(issue.assignee())
.setIssueAttributes(KeyValueFormat.format(issue.attributes()))
.setAuthorLogin(issue.authorLogin())
.setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule())
@@ -339,13 +339,13 @@ public final class IssueDto implements Serializable {
}

@CheckForNull
public String getAssignee() {
return assignee;
public String getAssigneeUuid() {
return assigneeUuid;
}

public IssueDto setAssignee(@Nullable String s) {
checkArgument(s == null || s.length() <= 255, "Value is too long for issue assignee: %s", s);
this.assignee = s;
public IssueDto setAssigneeUuid(@Nullable String s) {
checkArgument(s == null || s.length() <= 255, "Value is too long for issue assigneeUuid: %s", s);
this.assigneeUuid = s;
return this;
}

@@ -714,7 +714,7 @@ public final class IssueDto implements Serializable {
issue.setLine(line);
issue.setChecksum(checksum);
issue.setSeverity(severity);
issue.setAssignee(assignee);
issue.setAssigneeUuid(assigneeUuid);
issue.setAttributes(KeyValueFormat.parse(MoreObjects.firstNonNull(issueAttributes, "")));
issue.setComponentKey(componentKey);
issue.setComponentUuid(componentUuid);

+ 2
- 5
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java View File

@@ -44,12 +44,9 @@ public interface IssueMapper {

void scrollNonClosedByComponentUuid(@Param("componentUuid") String componentUuid, ResultHandler<IssueDto> handler);
void scrollNonClosedByComponentUuidExcludingExternals(@Param("componentUuid") String componentUuid, ResultHandler<IssueDto> handler);
List<IssueDto> selectNonClosedByComponentUuidExcludingExternals(@Param("componentUuid") String componentUuid);

void scrollNonClosedByModuleOrProject(
@Param("projectUuid") String projectUuid,
@Param("likeModuleUuidPath") String likeModuleUuidPath,
ResultHandler<IssueDto> handler);
List<IssueDto> selectNonClosedByModuleOrProject(@Param("projectUuid") String projectUuid, @Param("likeModuleUuidPath") String likeModuleUuidPath);

Collection<IssueGroupDto> selectIssueGroupsByBaseComponent(
@Param("baseComponent") ComponentDto baseComponent,

+ 1
- 1
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueTesting.java View File

@@ -57,7 +57,7 @@ public class IssueTesting {
.setResolution(null)
.setSeverity(Severity.ALL.get(nextInt(Severity.ALL.size())))
.setEffort((long) RandomUtils.nextInt(10))
.setAssignee("assignee_" + randomAlphabetic(5))
.setAssigneeUuid("assignee-uuid_" + randomAlphabetic(26))
.setAuthorLogin("author_" + randomAlphabetic(5))
// Adding one to the generated random value in order to never get 0 (as it's a forbidden value)
.setLine(nextInt(1_000) + 1)

+ 11
- 9
server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml View File

@@ -18,7 +18,7 @@
i.status as status,
i.resolution as resolution,
i.checksum as checksum,
i.assignee as assignee,
i.assignee as assigneeUuid,
i.author_login as authorLogin,
i.tags as tagsString,
i.issue_attributes as issueAttributes,
@@ -51,7 +51,7 @@
i.status as status
</when>
<when test="'ASSIGNEE'.equals(query.sort())">
i.assignee as assignee
i.assignee as assigneeUuid
</when>
<when test="'CREATION_DATE'.equals(query.sort())">
i.issue_creation_date as issueCreationTime
@@ -109,8 +109,10 @@
#{manualSeverity,jdbcType=BOOLEAN}, #{message,jdbcType=VARCHAR}, #{line,jdbcType=INTEGER},
#{locations,jdbcType=BINARY},
#{gap,jdbcType=DOUBLE}, #{effort,jdbcType=INTEGER}, #{status,jdbcType=VARCHAR},
#{tagsString,jdbcType=VARCHAR}, #{resolution,jdbcType=VARCHAR}, #{checksum,jdbcType=VARCHAR},
#{assignee,jdbcType=VARCHAR}, #{authorLogin,jdbcType=VARCHAR},
#{tagsString,jdbcType=VARCHAR}, #{resolution,jdbcType=VARCHAR},
#{checksum,jdbcType=VARCHAR},
#{assigneeUuid,jdbcType=VARCHAR},
#{authorLogin,jdbcType=VARCHAR},
#{issueAttributes,jdbcType=VARCHAR},
#{issueCreationTime,jdbcType=BIGINT},#{issueUpdateTime,jdbcType=BIGINT}, #{issueCloseTime,jdbcType=BIGINT},
#{createdAt,jdbcType=BIGINT}, #{updatedAt,jdbcType=BIGINT},
@@ -132,7 +134,7 @@
status=#{status,jdbcType=VARCHAR},
resolution=#{resolution,jdbcType=VARCHAR},
checksum=#{checksum,jdbcType=VARCHAR},
assignee=#{assignee,jdbcType=VARCHAR},
assignee=#{assigneeUuid,jdbcType=VARCHAR},
author_login=#{authorLogin,jdbcType=VARCHAR},
tags=#{tagsString,jdbcType=VARCHAR},
project_uuid=#{projectUuid,jdbcType=VARCHAR},
@@ -160,7 +162,7 @@
status=#{status,jdbcType=VARCHAR},
resolution=#{resolution,jdbcType=VARCHAR},
checksum=#{checksum,jdbcType=VARCHAR},
assignee=#{assignee,jdbcType=VARCHAR},
assignee=#{assigneeUuid,jdbcType=VARCHAR},
author_login=#{authorLogin,jdbcType=VARCHAR},
tags=#{tagsString,jdbcType=VARCHAR},
component_uuid=#{componentUuid,jdbcType=VARCHAR},
@@ -195,8 +197,8 @@
i.component_uuid = #{componentUuid,jdbcType=VARCHAR} and
i.status &lt;&gt; 'CLOSED'
</select>
<select id="scrollNonClosedByComponentUuidExcludingExternals" parameterType="String" resultType="Issue" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
<select id="selectNonClosedByComponentUuidExcludingExternals" parameterType="String" resultType="Issue">
select
<include refid="issueColumns"/>
from issues i
@@ -249,7 +251,7 @@
and i.status &lt;&gt; 'CLOSED'
</select>

<select id="scrollNonClosedByModuleOrProject" parameterType="map" resultType="Issue" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
<select id="selectNonClosedByModuleOrProject" parameterType="map" resultType="Issue">
select
<include refid="issueColumns"/>
from issues i

+ 19
- 43
server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java View File

@@ -19,13 +19,11 @@
*/
package org.sonar.db.issue;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -45,6 +43,7 @@ import org.sonar.db.rule.RuleTesting;

import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.rules.ExpectedException.none;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newModuleDto;

@@ -59,7 +58,7 @@ public class IssueDaoTest {
private static final String ISSUE_KEY2 = "I2";

@Rule
public ExpectedException expectedException = ExpectedException.none();
public ExpectedException expectedException = none();
@Rule
public DbTester db = DbTester.create(System2.INSTANCE);

@@ -87,7 +86,7 @@ public class IssueDaoTest {
assertThat(issue.getResolution()).isEqualTo("FIXED");
assertThat(issue.getChecksum()).isEqualTo("123456789");
assertThat(issue.getAuthorLogin()).isEqualTo("morgan");
assertThat(issue.getAssignee()).isEqualTo("karadoc");
assertThat(issue.getAssigneeUuid()).isEqualTo("karadoc");
assertThat(issue.getIssueAttributes()).isEqualTo("JIRA=FOO-1234");
assertThat(issue.getIssueCreationDate()).isNotNull();
assertThat(issue.getIssueUpdateDate()).isNotNull();
@@ -136,17 +135,15 @@ public class IssueDaoTest {
RuleDefinitionDto external = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto.setIsExternal(true));
IssueDto issueFromExteralruleOnFile = db.issues().insert(external, project, file, i -> i.setKee("ON_FILE_FROM_EXTERNAL"));

Accumulator accumulator = new Accumulator();
underTest.scrollNonClosedByComponentUuidExcludingExternals(db.getSession(), file.uuid(), accumulator);
accumulator.assertThatContainsOnly(openIssue1OnFile, openIssue2OnFile);
assertThat(underTest.selectNonClosedByComponentUuidExcludingExternals(db.getSession(), file.uuid()))
.extracting(IssueDto::getKey)
.containsExactlyInAnyOrder(Arrays.stream(new IssueDto[] {openIssue1OnFile, openIssue2OnFile}).map(IssueDto::getKey).toArray(String[]::new));

accumulator.clear();
underTest.scrollNonClosedByComponentUuidExcludingExternals(db.getSession(), project.uuid(), accumulator);
accumulator.assertThatContainsOnly(openIssueOnProject);
assertThat(underTest.selectNonClosedByComponentUuidExcludingExternals(db.getSession(), project.uuid()))
.extracting(IssueDto::getKey)
.containsExactlyInAnyOrder(Arrays.stream(new IssueDto[] {openIssueOnProject}).map(IssueDto::getKey).toArray(String[]::new));

accumulator.clear();
underTest.scrollNonClosedByComponentUuidExcludingExternals(db.getSession(), "does_not_exist", accumulator);
assertThat(accumulator.list).isEmpty();
assertThat(underTest.selectNonClosedByComponentUuidExcludingExternals(db.getSession(), "does_not_exist")).isEmpty();
}

@Test
@@ -166,18 +163,16 @@ public class IssueDaoTest {
RuleDefinitionDto external = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto.setIsExternal(true));
IssueDto issueFromExteralruleOnFile = db.issues().insert(external, project, file, i -> i.setKee("ON_FILE_FROM_EXTERNAL"));

Accumulator accumulator = new Accumulator();
underTest.scrollNonClosedByModuleOrProjectExcludingExternals(db.getSession(), project, accumulator);
accumulator.assertThatContainsOnly(openIssue1OnFile, openIssue2OnFile, openIssueOnModule, openIssueOnProject);
assertThat(underTest.selectNonClosedByModuleOrProjectExcludingExternals(db.getSession(), project))
.extracting(IssueDto::getKey)
.containsExactlyInAnyOrder(Arrays.stream(new IssueDto[] {openIssue1OnFile, openIssue2OnFile, openIssueOnModule, openIssueOnProject}).map(IssueDto::getKey).toArray(String[]::new));

accumulator.clear();
underTest.scrollNonClosedByModuleOrProjectExcludingExternals(db.getSession(), module, accumulator);
accumulator.assertThatContainsOnly(openIssue1OnFile, openIssue2OnFile, openIssueOnModule);
assertThat(underTest.selectNonClosedByModuleOrProjectExcludingExternals(db.getSession(), module))
.extracting(IssueDto::getKey)
.containsExactlyInAnyOrder(Arrays.stream(new IssueDto[] {openIssue1OnFile, openIssue2OnFile, openIssueOnModule}).map(IssueDto::getKey).toArray(String[]::new));

accumulator.clear();
ComponentDto notPersisted = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization());
underTest.scrollNonClosedByModuleOrProjectExcludingExternals(db.getSession(), notPersisted, accumulator);
assertThat(accumulator.list).isEmpty();
assertThat(underTest.selectNonClosedByModuleOrProjectExcludingExternals(db.getSession(), notPersisted)).isEmpty();
}

@Test
@@ -298,7 +293,7 @@ public class IssueDaoTest {
dto.setStatus("RESOLVED");
dto.setSeverity("BLOCKER");
dto.setAuthorLogin("morgan");
dto.setAssignee("karadoc");
dto.setAssigneeUuid("karadoc");
dto.setIssueAttributes("JIRA=FOO-1234");
dto.setChecksum("123456789");
dto.setMessage("the message");
@@ -326,23 +321,4 @@ public class IssueDaoTest {
.setProjectUuid(PROJECT_UUID));
db.getSession().commit();
}

private static class Accumulator implements ResultHandler<IssueDto> {
private final List<IssueDto> list = new ArrayList<>();

private void clear() {
list.clear();
}

@Override
public void handleResult(ResultContext<? extends IssueDto> resultContext) {
list.add(resultContext.getResultObject());
}

private void assertThatContainsOnly(IssueDto... issues) {
assertThat(list)
.extracting(IssueDto::getKey)
.containsExactlyInAnyOrder(Arrays.stream(issues).map(IssueDto::getKey).toArray(String[]::new));
}
}
}

+ 1
- 1
server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDtoTest.java View File

@@ -77,7 +77,7 @@ public class IssueDtoTest {
.setSeverity("BLOCKER")
.setMessage("message")
.setManualSeverity(true)
.setAssignee("perceval")
.setAssigneeUuid("perceval")
.setIssueAttributes("key=value")
.setAuthorLogin("pierre")
.setIssueCreationDate(createdAt)

+ 4
- 4
server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueMapperTest.java View File

@@ -79,7 +79,7 @@ public class IssueMapperTest {
assertThat(result.getStatus()).isEqualTo("RESOLVED");
assertThat(result.getSeverity()).isEqualTo("BLOCKER");
assertThat(result.getAuthorLogin()).isEqualTo("morgan");
assertThat(result.getAssignee()).isEqualTo("karadoc");
assertThat(result.getAssigneeUuid()).isEqualTo("karadoc");
assertThat(result.getIssueAttributes()).isEqualTo("JIRA=FOO-1234");
assertThat(result.getChecksum()).isEqualTo("123456789");
assertThat(result.getMessage()).isEqualTo("the message");
@@ -109,7 +109,7 @@ public class IssueMapperTest {
update.setStatus("RESOLVED");
update.setSeverity("BLOCKER");
update.setAuthorLogin("morgan");
update.setAssignee("karadoc");
update.setAssigneeUuid("karadoc");
update.setIssueAttributes("JIRA=FOO-1234");
update.setChecksum("123456789");
update.setMessage("the message");
@@ -139,7 +139,7 @@ public class IssueMapperTest {
assertThat(result.getStatus()).isEqualTo("RESOLVED");
assertThat(result.getSeverity()).isEqualTo("BLOCKER");
assertThat(result.getAuthorLogin()).isEqualTo("morgan");
assertThat(result.getAssignee()).isEqualTo("karadoc");
assertThat(result.getAssigneeUuid()).isEqualTo("karadoc");
assertThat(result.getIssueAttributes()).isEqualTo("JIRA=FOO-1234");
assertThat(result.getChecksum()).isEqualTo("123456789");
assertThat(result.getMessage()).isEqualTo("the message");
@@ -227,7 +227,7 @@ public class IssueMapperTest {
.setStatus("RESOLVED")
.setSeverity("BLOCKER")
.setAuthorLogin("morgan")
.setAssignee("karadoc")
.setAssigneeUuid("karadoc")
.setIssueAttributes("JIRA=FOO-1234")
.setChecksum("123456789")
.setMessage("the message")

+ 1
- 0
server/sonar-db-dao/src/test/java/org/sonar/db/user/UserTesting.java View File

@@ -32,6 +32,7 @@ public class UserTesting {
public static UserDto newUserDto() {
return new UserDto()
.setId(nextInt())
.setUuid(randomAlphanumeric(40))
.setActive(true)
.setLocal(nextBoolean())
.setLogin(randomAlphanumeric(30))

+ 0
- 1
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/MakeQualityProfileKeyUnique.java View File

@@ -25,7 +25,6 @@ import org.sonar.server.platform.db.migration.step.DdlChange;

import java.sql.SQLException;

import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE;
import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;

public class MakeQualityProfileKeyUnique extends DdlChange {

+ 2
- 1
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/PopulateUUIDOnUsers.java View File

@@ -48,7 +48,8 @@ public class PopulateUUIDOnUsers extends DataChange {
if (login == null) {
login = uuidFactory.create();
}
update.setString(1, login); // login -> uuid
// login -> uuid
update.setString(1, login);
update.setLong(2, system2.now());
update.setLong(3, row.getLong(1));
return true;

+ 37
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v72/RenameIssuesAssigneeToAssigneeUuidTest/issues.sql View File

@@ -0,0 +1,37 @@
CREATE TABLE "ISSUES" (
"ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
"KEE" VARCHAR(50) UNIQUE NOT NULL,
"COMPONENT_UUID" VARCHAR(50),
"PROJECT_UUID" VARCHAR(50),
"RULE_ID" INTEGER,
"SEVERITY" VARCHAR(10),
"MANUAL_SEVERITY" BOOLEAN NOT NULL,
"MESSAGE" VARCHAR(4000),
"LINE" INTEGER,
"GAP" DOUBLE,
"EFFORT" INTEGER,
"STATUS" VARCHAR(20),
"RESOLUTION" VARCHAR(20),
"CHECKSUM" VARCHAR(1000),
"REPORTER" VARCHAR(255),
"ASSIGNEE" VARCHAR(255),
"AUTHOR_LOGIN" VARCHAR(255),
"ACTION_PLAN_KEY" VARCHAR(50) NULL,
"ISSUE_ATTRIBUTES" VARCHAR(4000),
"TAGS" VARCHAR(4000),
"ISSUE_CREATION_DATE" BIGINT,
"ISSUE_CLOSE_DATE" BIGINT,
"ISSUE_UPDATE_DATE" BIGINT,
"CREATED_AT" BIGINT,
"UPDATED_AT" BIGINT,
"LOCATIONS" BLOB,
"ISSUE_TYPE" TINYINT
);
CREATE UNIQUE INDEX "ISSUES_KEE" ON "ISSUES" ("KEE");
CREATE INDEX "ISSUES_COMPONENT_UUID" ON "ISSUES" ("COMPONENT_UUID");
CREATE INDEX "ISSUES_PROJECT_UUID" ON "ISSUES" ("PROJECT_UUID");
CREATE INDEX "ISSUES_RULE_ID" ON "ISSUES" ("RULE_ID");
CREATE INDEX "ISSUES_RESOLUTION" ON "ISSUES" ("RESOLUTION");
CREATE INDEX "ISSUES_ASSIGNEE" ON "ISSUES" ("ASSIGNEE");
CREATE INDEX "ISSUES_CREATION_DATE" ON "ISSUES" ("ISSUE_CREATION_DATE");
CREATE INDEX "ISSUES_UPDATED_AT" ON "ISSUES" ("UPDATED_AT");

+ 6
- 0
server/sonar-server/src/main/java/org/sonar/server/authentication/SafeModeUserSession.java View File

@@ -52,6 +52,12 @@ public class SafeModeUserSession extends AbstractUserSession {
return null;
}

@CheckForNull
@Override
public String getUuid() {
return null;
}

@CheckForNull
@Override
public String getName() {

+ 24
- 10
server/sonar-server/src/main/java/org/sonar/server/batch/IssuesAction.java View File

@@ -20,11 +20,13 @@
package org.sonar.server.batch;

import com.google.common.base.Splitter;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.ResultHandler;
import org.sonar.api.resources.Scopes;
import org.sonar.api.rules.RuleType;
import org.sonar.api.server.ws.Request;
@@ -34,6 +36,7 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.user.UserDto;
import org.sonar.scanner.protocol.input.ScannerInput;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.user.UserSession;
@@ -42,6 +45,8 @@ import org.sonarqube.ws.MediaTypes;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Maps.newHashMap;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static org.sonar.api.web.UserRole.USER;
import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
@@ -96,26 +101,35 @@ public class IssuesAction implements BatchWsAction {
response.stream().setMediaType(MediaTypes.PROTOBUF);
OutputStream output = response.stream().output();

ResultHandler<IssueDto> handler = resultContext -> {
IssueDto issue = resultContext.getResultObject();
handleIssue(issue, responseBuilder, keysByUUid, output);
};
List<IssueDto> issueDtos = new ArrayList<>();
switch (component.scope()) {
case Scopes.PROJECT:
dbClient.issueDao().scrollNonClosedByModuleOrProjectExcludingExternals(dbSession, component, handler);
issueDtos.addAll(dbClient.issueDao().selectNonClosedByModuleOrProjectExcludingExternals(dbSession, component));
break;
case Scopes.FILE:
dbClient.issueDao().scrollNonClosedByComponentUuidExcludingExternals(dbSession, component.uuid(), handler);
issueDtos.addAll(dbClient.issueDao().selectNonClosedByComponentUuidExcludingExternals(dbSession, component.uuid()));
break;
default:
// only projects, modules and files are supported. Other types of components are not allowed.
throw new IllegalArgumentException(format("Component of scope '%s' is not allowed", component.scope()));
}

List<String> usersUuids = issueDtos.stream()
.filter(issue -> issue.getAssigneeUuid() != null)
.map(IssueDto::getAssigneeUuid)
.collect(toList());

Map<String, String> userLoginsByUserUuids = dbClient.userDao().selectByUuids(dbSession, usersUuids)
.stream().collect(toMap(UserDto::getUuid, UserDto::getLogin));

issueDtos.forEach(issue -> {
issue.setAssigneeUuid(userLoginsByUserUuids.get(issue.getAssigneeUuid()));
handleIssue(issue, responseBuilder, keysByUUid, output);
});
}
}

private static void handleIssue(IssueDto issue, ScannerInput.ServerIssue.Builder issueBuilder,
Map<String, String> keysByUUid, OutputStream out) {
private static void handleIssue(IssueDto issue, ScannerInput.ServerIssue.Builder issueBuilder, Map<String, String> keysByUUid, OutputStream out) {
issueBuilder.setKey(issue.getKey());
String moduleUuid = extractModuleUuid(issue);
issueBuilder.setModuleKey(keysByUUid.get(moduleUuid));
@@ -123,7 +137,7 @@ public class IssuesAction implements BatchWsAction {
issueBuilder.setRuleRepository(issue.getRuleRepo());
issueBuilder.setRuleKey(issue.getRule());
setNullable(issue.getChecksum(), issueBuilder::setChecksum);
setNullable(issue.getAssignee(), issueBuilder::setAssigneeLogin);
setNullable(issue.getAssigneeUuid(), issueBuilder::setAssigneeLogin);
setNullable(issue.getLine(), issueBuilder::setLine);
setNullable(issue.getMessage(), issueBuilder::setMsg);
issueBuilder.setSeverity(org.sonar.scanner.protocol.Constants.Severity.valueOf(issue.getSeverity()));

+ 12
- 12
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultAssignee.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.server.computation.task.projectanalysis.issue;

import com.google.common.base.Strings;
import javax.annotation.CheckForNull;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
@@ -29,6 +28,7 @@ import org.sonar.db.user.UserDto;
import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository;

import static com.google.common.base.Strings.isNullOrEmpty;
import static org.sonar.api.CoreProperties.DEFAULT_ISSUE_ASSIGNEE;

/**
@@ -44,7 +44,7 @@ public class DefaultAssignee {
private final AnalysisMetadataHolder analysisMetadataHolder;

private boolean loaded = false;
private String login = null;
private String userUuid = null;

public DefaultAssignee(DbClient dbClient, ConfigurationRepository configRepository, AnalysisMetadataHolder analysisMetadataHolder) {
this.dbClient = dbClient;
@@ -53,30 +53,30 @@ public class DefaultAssignee {
}

@CheckForNull
public String loadDefaultAssigneeLogin() {
public String loadDefaultAssigneeUuid() {
if (loaded) {
return login;
return userUuid;
}
String configuredLogin = configRepository.getConfiguration().get(DEFAULT_ISSUE_ASSIGNEE).orElse(null);
if (!Strings.isNullOrEmpty(configuredLogin) && isValidLogin(configuredLogin)) {
this.login = configuredLogin;
String login = configRepository.getConfiguration().get(DEFAULT_ISSUE_ASSIGNEE).orElse(null);
if (!isNullOrEmpty(login)) {
userUuid = findValidUserUuidFromLogin(login);
}
loaded = true;
return login;
return userUuid;
}

private boolean isValidLogin(String login) {
private String findValidUserUuidFromLogin(String login) {
try (DbSession dbSession = dbClient.openSession(false)) {
UserDto user = dbClient.userDao().selectActiveUserByLogin(dbSession, login);
if (user == null) {
LOG.info("Property {} is set with an unknown login: {}", DEFAULT_ISSUE_ASSIGNEE, login);
return false;
return null;
}
if (!isUserMemberOfOrganization(dbSession, user)) {
LOG.info("Property {} is set with a user which is not member of the organization of the project : {}", DEFAULT_ISSUE_ASSIGNEE, login);
return false;
return null;
}
return true;
return user.getUuid();
}
}


+ 5
- 7
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueAssigner.java View File

@@ -19,11 +19,9 @@
*/
package org.sonar.server.computation.task.projectanalysis.issue;

import com.google.common.base.Strings;
import java.util.Date;
import java.util.Optional;
import javax.annotation.CheckForNull;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.core.issue.DefaultIssue;
@@ -35,6 +33,7 @@ import org.sonar.server.computation.task.projectanalysis.scm.ScmInfo;
import org.sonar.server.computation.task.projectanalysis.scm.ScmInfoRepository;
import org.sonar.server.issue.IssueFieldsSetter;

import static com.google.common.base.Strings.isNullOrEmpty;
import static org.apache.commons.lang.StringUtils.defaultIfEmpty;
import static org.sonar.core.issue.IssueChangeContext.createScan;

@@ -71,7 +70,7 @@ public class IssueAssigner extends IssueVisitor {
loadScmChangesets(component);
String scmAuthor = guessScmAuthor(issue);

if (!Strings.isNullOrEmpty(scmAuthor)) {
if (!isNullOrEmpty(scmAuthor)) {
if (scmAuthor.length() <= IssueDto.AUTHOR_MAX_SIZE) {
issueUpdater.setNewAuthor(issue, scmAuthor, changeContext);
} else {
@@ -80,10 +79,9 @@ public class IssueAssigner extends IssueVisitor {
}

if (issue.assignee() == null) {
String author = Strings.isNullOrEmpty(scmAuthor) ? null : scmAccountToUser.getNullable(scmAuthor);
String assigneeLogin = StringUtils.defaultIfEmpty(author, defaultAssignee.loadDefaultAssigneeLogin());

issueUpdater.setNewAssignee(issue, assigneeLogin, changeContext);
String assigneeUuid = isNullOrEmpty(scmAuthor) ? null : scmAccountToUser.getNullable(scmAuthor);
assigneeUuid = defaultIfEmpty(assigneeUuid, defaultAssignee.loadDefaultAssigneeUuid());
issueUpdater.setNewAssignee(issue, assigneeUuid, changeContext);
}
}
}

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycle.java View File

@@ -166,7 +166,7 @@ public class IssueLifecycle {
toIssue.setCloseDate(fromIssue.closeDate());
toIssue.setResolution(fromIssue.resolution());
toIssue.setStatus(fromIssue.status());
toIssue.setAssignee(fromIssue.assignee());
toIssue.setAssigneeUuid(fromIssue.assignee());
toIssue.setAuthorLogin(fromIssue.authorLogin());
toIssue.setTags(fromIssue.tags());
toIssue.setAttributes(fromIssue.attributes());

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ScmAccountToUser.java View File

@@ -22,7 +22,7 @@ package org.sonar.server.computation.task.projectanalysis.issue;
import org.sonar.server.util.cache.MemoryCache;

/**
* Cache of dictionary {SCM account -> SQ user login}. Kept in memory
* Cache of dictionary {SCM account -> SQ user uuid}. Kept in memory
* during the execution of Compute Engine.
*/
public class ScmAccountToUser extends MemoryCache<String,String> {

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ScmAccountToUserLoader.java View File

@@ -51,7 +51,7 @@ public class ScmAccountToUserLoader implements CacheLoader<String, String> {
public String load(String scmAccount) {
List<UserDoc> users = index.getAtMostThreeActiveUsersForScmAccount(scmAccount, analysisMetadataHolder.getOrganization().getUuid());
if (users.size() == 1) {
return users.get(0).login();
return users.get(0).uuid();
}
if (!users.isEmpty()) {
// multiple users are associated to the same SCM account, for example

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/UpdateConflictResolver.java View File

@@ -74,6 +74,6 @@ public class UpdateConflictResolver {
}

private void resolveAssignee(IssueDto dbIssue, DefaultIssue issue) {
issue.setAssignee(dbIssue.getAssignee());
issue.setAssigneeUuid(dbIssue.getAssigneeUuid());
}
}

+ 35
- 7
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/SendIssueNotificationsStep.java View File

@@ -24,7 +24,9 @@ import com.google.common.collect.ImmutableSet;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
@@ -33,6 +35,9 @@ import org.sonar.api.issue.Issue;
import org.sonar.api.utils.Duration;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.util.CloseableIterator;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.user.UserDto;
import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
import org.sonar.server.computation.task.projectanalysis.component.Component;
@@ -50,6 +55,9 @@ import org.sonar.server.issue.notification.NewIssuesNotificationFactory;
import org.sonar.server.issue.notification.NewIssuesStatistics;
import org.sonar.server.notification.NotificationService;

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.StreamSupport.stream;
import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;

@@ -70,17 +78,20 @@ public class SendIssueNotificationsStep implements ComputationStep {
private final NotificationService service;
private final AnalysisMetadataHolder analysisMetadataHolder;
private final NewIssuesNotificationFactory newIssuesNotificationFactory;
private final DbClient dbClient;

private Map<String, Component> componentsByDbKey;

public SendIssueNotificationsStep(IssueCache issueCache, RuleRepository rules, TreeRootHolder treeRootHolder,
NotificationService service, AnalysisMetadataHolder analysisMetadataHolder,
NewIssuesNotificationFactory newIssuesNotificationFactory) {
NewIssuesNotificationFactory newIssuesNotificationFactory, DbClient dbClient) {
this.issueCache = issueCache;
this.rules = rules;
this.treeRootHolder = treeRootHolder;
this.service = service;
this.analysisMetadataHolder = analysisMetadataHolder;
this.newIssuesNotificationFactory = newIssuesNotificationFactory;
this.dbClient = dbClient;
}

@Override
@@ -95,8 +106,14 @@ public class SendIssueNotificationsStep implements ComputationStep {
long analysisDate = analysisMetadataHolder.getAnalysisDate();
Predicate<DefaultIssue> isOnLeakPredicate = i -> i.isNew() && i.creationDate().getTime() >= truncateToSeconds(analysisDate);
NewIssuesStatistics newIssuesStats = new NewIssuesStatistics(isOnLeakPredicate);
Map<String, UserDto> usersDtoByUuids;
try (DbSession dbSession = dbClient.openSession(false)) {
Iterable<DefaultIssue> iterable = issueCache::traverse;
List<String> assigneeUuids = stream(iterable.spliterator(), false).map(DefaultIssue::assignee).filter(Objects::nonNull).collect(toList());
usersDtoByUuids = dbClient.userDao().selectByUuids(dbSession, assigneeUuids).stream().collect(toMap(UserDto::getUuid, dto -> dto));
}
try (CloseableIterator<DefaultIssue> issues = issueCache.traverse()) {
processIssues(newIssuesStats, issues, project);
processIssues(newIssuesStats, issues, project, usersDtoByUuids);
}
if (newIssuesStats.hasIssuesOnLeak()) {
sendNewIssuesNotification(newIssuesStats, project, analysisDate);
@@ -114,21 +131,22 @@ public class SendIssueNotificationsStep implements ComputationStep {
return Date.from(instant).getTime();
}

private void processIssues(NewIssuesStatistics newIssuesStats, CloseableIterator<DefaultIssue> issues, Component project) {
private void processIssues(NewIssuesStatistics newIssuesStats, CloseableIterator<DefaultIssue> issues, Component project, Map<String, UserDto> usersDtoByUuids) {
while (issues.hasNext()) {
DefaultIssue issue = issues.next();
if (issue.isNew() && issue.resolution() == null) {
newIssuesStats.add(issue);
} else if (issue.isChanged() && issue.mustSendNotifications()) {
sendIssueChangeNotification(issue, project);
sendIssueChangeNotification(issue, project, usersDtoByUuids);
}
}
}

private void sendIssueChangeNotification(DefaultIssue issue, Component project) {
private void sendIssueChangeNotification(DefaultIssue issue, Component project, Map<String, UserDto> usersDtoByUuids) {
IssueChangeNotification changeNotification = new IssueChangeNotification();
changeNotification.setRuleName(rules.getByKey(issue.ruleKey()).getName());
changeNotification.setIssue(issue);
changeNotification.setAssignee(usersDtoByUuids.get(issue.assignee()));
changeNotification.setProject(project.getPublicKey(), project.getName(), getBranchName(), getPullRequest());
getComponentKey(issue).ifPresent(c -> changeNotification.setComponent(c.getPublicKey(), c.getName()));
service.deliver(changeNotification);
@@ -147,15 +165,16 @@ public class SendIssueNotificationsStep implements ComputationStep {
}

private void sendNewIssuesNotificationToAssignees(NewIssuesStatistics statistics, Component project, long analysisDate) {
Map<String, UserDto> userDtoByUuid = loadUserDtoByUuid(statistics);
statistics.getAssigneesStatistics().entrySet()
.stream()
.filter(e -> e.getValue().hasIssuesOnLeak())
.forEach(e -> {
String assignee = e.getKey();
String assigneeUuid = e.getKey();
NewIssuesStatistics.Stats assigneeStatistics = e.getValue();
MyNewIssuesNotification myNewIssuesNotification = newIssuesNotificationFactory
.newMyNewIssuesNotification()
.setAssignee(assignee);
.setAssignee(userDtoByUuid.get(assigneeUuid));
myNewIssuesNotification
.setProject(project.getPublicKey(), project.getName(), getBranchName(), getPullRequest())
.setProjectVersion(project.getReportAttributes().getVersion())
@@ -167,6 +186,15 @@ public class SendIssueNotificationsStep implements ComputationStep {
});
}

private Map<String, UserDto> loadUserDtoByUuid(NewIssuesStatistics statistics) {
List<Map.Entry<String, NewIssuesStatistics.Stats>> entriesWithIssuesOnLeak = statistics.getAssigneesStatistics().entrySet()
.stream().filter(e -> e.getValue().hasIssuesOnLeak()).collect(toList());
List<String> assigneeUuids = entriesWithIssuesOnLeak.stream().map(Map.Entry::getKey).collect(toList());
try (DbSession dbSession = dbClient.openSession(false)) {
return dbClient.userDao().selectByUuids(dbSession, assigneeUuids).stream().collect(toMap(UserDto::getUuid, u -> u));
}
}

private Optional<Component> getComponentKey(DefaultIssue issue) {
if (componentsByDbKey == null) {
final ImmutableMap.Builder<String, Component> builder = ImmutableMap.builder();

+ 9
- 14
server/sonar-server/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java View File

@@ -28,7 +28,7 @@ import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.rules.RuleType;
import org.sonar.api.server.ServerSide;
@@ -110,16 +110,11 @@ public class IssueFieldsSetter {
}
return false;
}

public boolean assign(DefaultIssue issue, @Nullable UserDto user, IssueChangeContext context) {
String sanitizedAssignee = null;
if (user != null) {
sanitizedAssignee = StringUtils.defaultIfBlank(user.getLogin(), null);
}
if (!Objects.equals(sanitizedAssignee, issue.assignee())) {
String newAssigneeName = user != null ? user.getName() : null;
issue.setFieldChange(context, ASSIGNEE, UNUSED, newAssigneeName);
issue.setAssignee(sanitizedAssignee);
String assigneeUuid = user != null ? user.getUuid() : null;
if (!Objects.equals(assigneeUuid, issue.assignee())) {
issue.setFieldChange(context, ASSIGNEE, UNUSED, user != null ? user.getUuid() : null);
issue.setAssigneeUuid(user != null ? user.getUuid() : null);
issue.setUpdateDate(context.date());
issue.setChanged(true);
issue.setSendNotifications(true);
@@ -131,13 +126,13 @@ public class IssueFieldsSetter {
/**
* Used to set the assignee when it was null
*/
public boolean setNewAssignee(DefaultIssue issue, @Nullable String newAssignee, IssueChangeContext context) {
if (newAssignee == null) {
public boolean setNewAssignee(DefaultIssue issue, @Nullable String newAssigneeUuid, IssueChangeContext context) {
if (newAssigneeUuid == null) {
return false;
}
checkState(issue.assignee() == null, "It's not possible to update the assignee with this method, please use assign()");
issue.setFieldChange(context, ASSIGNEE, UNUSED, newAssignee);
issue.setAssignee(newAssignee);
issue.setFieldChange(context, ASSIGNEE, UNUSED, newAssigneeUuid);
issue.setAssigneeUuid(newAssigneeUuid);
issue.setUpdateDate(context.date());
issue.setChanged(true);
issue.setSendNotifications(true);

+ 4
- 4
server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java View File

@@ -98,7 +98,7 @@ public class IssueQuery {
this.files = defaultCollection(builder.files);
this.views = defaultCollection(builder.views);
this.rules = defaultCollection(builder.rules);
this.assignees = defaultCollection(builder.assignees);
this.assignees = defaultCollection(builder.assigneeUuids);
this.authors = defaultCollection(builder.authors);
this.languages = defaultCollection(builder.languages);
this.tags = defaultCollection(builder.tags);
@@ -275,7 +275,7 @@ public class IssueQuery {
private Collection<String> files;
private Collection<String> views;
private Collection<RuleDefinitionDto> rules;
private Collection<String> assignees;
private Collection<String> assigneeUuids;
private Collection<String> authors;
private Collection<String> languages;
private Collection<String> tags;
@@ -359,8 +359,8 @@ public class IssueQuery {
return this;
}

public Builder assignees(@Nullable Collection<String> l) {
this.assignees = l;
public Builder assigneeUuids(@Nullable Collection<String> l) {
this.assigneeUuids = l;
return this;
}


+ 2
- 22
server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java View File

@@ -21,7 +21,6 @@ package org.sonar.server.issue;

import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import java.time.Clock;
import java.time.OffsetDateTime;
import java.time.Period;
@@ -82,10 +81,7 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SINCE_LEAK_
@ServerSide
public class IssueQueryFactory {

public static final String LOGIN_MYSELF = "__me__";

private static final String UNKNOWN = "<UNKNOWN>";

public static final String UNKNOWN = "<UNKNOWN>";
private static final ComponentDto UNKNOWN_COMPONENT = new ComponentDto().setUuid(UNKNOWN).setProjectUuid(UNKNOWN);

private final DbClient dbClient;
@@ -107,7 +103,7 @@ public class IssueQueryFactory {
.resolutions(request.getResolutions())
.resolved(request.getResolved())
.rules(ruleKeysToRuleId(dbSession, request.getRules()))
.assignees(buildAssignees(request.getAssignees()))
.assigneeUuids(request.getAssigneeUuids())
.languages(request.getLanguages())
.tags(request.getTags())
.types(request.getTypes())
@@ -174,22 +170,6 @@ public class IssueQueryFactory {
return snapshot.map(s -> longToDate(s.getPeriodDate())).orElse(null);
}

private List<String> buildAssignees(@Nullable List<String> assigneesFromParams) {
List<String> assignees = Lists.newArrayList();
if (assigneesFromParams != null) {
assignees.addAll(assigneesFromParams);
}
if (assignees.contains(LOGIN_MYSELF)) {
String login = userSession.getLogin();
if (login == null) {
assignees.add(UNKNOWN);
} else {
assignees.add(login);
}
}
return assignees;
}

private boolean mergeDeprecatedComponentParameters(DbSession session, SearchRequest request, List<ComponentDto> allComponents) {
Boolean onComponentOnly = request.getOnComponentOnly();
Collection<String> components = request.getComponents();

+ 5
- 0
server/sonar-server/src/main/java/org/sonar/server/issue/IssueUpdater.java View File

@@ -32,6 +32,7 @@ import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.issue.notification.IssueChangeNotification;
import org.sonar.server.issue.ws.SearchResponseData;
import org.sonar.server.notification.NotificationManager;
@@ -60,6 +61,7 @@ public class IssueUpdater {
*/
public SearchResponseData saveIssueAndPreloadSearchResponseData(DbSession dbSession, DefaultIssue issue, IssueChangeContext context,
@Nullable String comment, boolean refreshMeasures) {

Optional<RuleDefinitionDto> rule = getRuleByKey(dbSession, issue.getRuleKey());
ComponentDto project = dbClient.componentDao().selectOrFailByUuid(dbSession, issue.projectUuid());
ComponentDto component = dbClient.componentDao().selectOrFailByUuid(dbSession, issue.componentUuid());
@@ -88,8 +90,11 @@ public class IssueUpdater {
private IssueDto doSaveIssue(DbSession session, DefaultIssue issue, IssueChangeContext context, @Nullable String comment,
Optional<RuleDefinitionDto> rule, ComponentDto project, ComponentDto component) {
IssueDto issueDto = issueStorage.save(session, issue);
String assigneeUuid = issue.assignee();
UserDto assignee = assigneeUuid == null ? null : dbClient.userDao().selectByUuid(session, assigneeUuid);
notificationService.scheduleForSending(new IssueChangeNotification()
.setIssue(issue)
.setAssignee(assignee)
.setChangeAuthorLogin(context.login())
.setRuleName(rule.map(RuleDefinitionDto::getName).orElse(null))
.setProject(project)

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/issue/IssuesFinderSort.java View File

@@ -109,7 +109,7 @@ class IssuesFinderSort {
static class AssigneeSortIssueProcessor extends TextSortIssueProcessor {
@Override
String sortField(IssueDto issueDto) {
return issueDto.getAssignee();
return issueDto.getAssigneeUuid();
}
}


+ 5
- 5
server/sonar-server/src/main/java/org/sonar/server/issue/SearchRequest.java View File

@@ -28,7 +28,7 @@ public class SearchRequest {
private List<String> additionalFields;
private Boolean asc;
private Boolean assigned;
private List<String> assignees;
private List<String> assigneesUuid;
private List<String> authors;
private List<String> componentKeys;
private List<String> componentRootUuids;
@@ -106,12 +106,12 @@ public class SearchRequest {
}

@CheckForNull
public List<String> getAssignees() {
return assignees;
public List<String> getAssigneeUuids() {
return assigneesUuid;
}

public SearchRequest setAssignees(@Nullable List<String> assignees) {
this.assignees = assignees;
public SearchRequest setAssigneesUuid(@Nullable List<String> assigneesUuid) {
this.assigneesUuid = assigneesUuid;
return this;
}


+ 4
- 4
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueDoc.java View File

@@ -111,8 +111,8 @@ public class IssueDoc extends BaseDoc {
}

@CheckForNull
public String assignee() {
return getNullableField(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE);
public String assigneeUuid() {
return getNullableField(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID);
}

/**
@@ -225,8 +225,8 @@ public class IssueDoc extends BaseDoc {
return this;
}

public IssueDoc setAssignee(@Nullable String s) {
setField(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE, s);
public IssueDoc setAssigneeUuid(@Nullable String s) {
setField(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID, s);
return this;
}


+ 11
- 11
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java View File

@@ -164,7 +164,7 @@ public class IssueIndex {
this.authorizationTypeSupport = authorizationTypeSupport;

this.sorting = new Sorting();
this.sorting.add(IssueQuery.SORT_BY_ASSIGNEE, IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE);
this.sorting.add(IssueQuery.SORT_BY_ASSIGNEE, IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID);
this.sorting.add(IssueQuery.SORT_BY_STATUS, IssueIndexDefinition.FIELD_ISSUE_STATUS);
this.sorting.add(IssueQuery.SORT_BY_SEVERITY, IssueIndexDefinition.FIELD_ISSUE_SEVERITY_VALUE);
this.sorting.add(IssueQuery.SORT_BY_CREATION_DATE, IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT);
@@ -274,7 +274,7 @@ public class IssueIndex {
}

private static AggregationBuilder createAssigneesFacet(IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder queryBuilder) {
String fieldName = IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE;
String fieldName = IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID;
String facetName = PARAM_ASSIGNEES;

// Same as in super.stickyFacetBuilder
@@ -387,9 +387,9 @@ public class IssueIndex {

// Issue is assigned Filter
if (BooleanUtils.isTrue(query.assigned())) {
filters.put(IS_ASSIGNED_FILTER, existsQuery(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE));
filters.put(IS_ASSIGNED_FILTER, existsQuery(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID));
} else if (BooleanUtils.isFalse(query.assigned())) {
filters.put(IS_ASSIGNED_FILTER, boolQuery().mustNot(existsQuery(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE)));
filters.put(IS_ASSIGNED_FILTER, boolQuery().mustNot(existsQuery(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID)));
}

// Issue is Resolved Filter
@@ -402,7 +402,7 @@ public class IssueIndex {

// Field Filters
filters.put(IssueIndexDefinition.FIELD_ISSUE_KEY, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_KEY, query.issueKeys()));
filters.put(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE, query.assignees()));
filters.put(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID, query.assignees()));

addComponentRelatedFilters(query, filters);

@@ -578,13 +578,13 @@ public class IssueIndex {
}

private void addAssignedToMeFacetIfNeeded(SearchRequestBuilder builder, SearchOptions options, IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder queryBuilder) {
String login = userSession.getLogin();
String uuid = userSession.getUuid();

if (!options.getFacets().contains(FACET_ASSIGNED_TO_ME) || StringUtils.isEmpty(login)) {
if (!options.getFacets().contains(FACET_ASSIGNED_TO_ME) || StringUtils.isEmpty(uuid)) {
return;
}

String fieldName = IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE;
String fieldName = IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID;
String facetName = FACET_ASSIGNED_TO_ME;

// Same as in super.stickyFacetBuilder
@@ -595,7 +595,7 @@ public class IssueIndex {
.filter(facetName + "__filter", facetFilter)
.subAggregation(addEffortAggregationIfNeeded(query, AggregationBuilders.terms(facetName + "__terms")
.field(fieldName)
.includeExclude(new IncludeExclude(escapeSpecialRegexChars(login), null))));
.includeExclude(new IncludeExclude(escapeSpecialRegexChars(uuid), null))));

builder.addAggregation(
AggregationBuilders.global(facetName)
@@ -677,7 +677,7 @@ public class IssueIndex {
return boolQuery;
}

public List<ProjectStatistics> searchProjectStatistics(List<String> projectUuids, List<Long> froms, String assignee) {
public List<ProjectStatistics> searchProjectStatistics(List<String> projectUuids, List<Long> froms, @Nullable String assigneeUuid) {
checkState(projectUuids.size() == froms.size(),
"Expected same size for projectUuids (had size %s) and froms (had size %s)", projectUuids.size(), froms.size());
if (projectUuids.isEmpty()) {
@@ -687,7 +687,7 @@ public class IssueIndex {
.setQuery(
boolQuery()
.mustNot(existsQuery(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION))
.filter(termQuery(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE, assignee)))
.filter(termQuery(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE_UUID, assigneeUuid)))
.setSize(0);
IntStream.range(0, projectUuids.size()).forEach(i -> {
String projectUuid = projectUuids.get(i);

+ 2
- 2
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndexDefinition.java View File

@@ -35,7 +35,7 @@ import static org.sonar.server.es.NewIndex.SettingsConfiguration.newBuilder;
public class IssueIndexDefinition implements IndexDefinition {

public static final IndexType INDEX_TYPE_ISSUE = new IndexType("issues", "issue");
public static final String FIELD_ISSUE_ASSIGNEE = "assignee";
public static final String FIELD_ISSUE_ASSIGNEE_UUID = "assignee";
public static final String FIELD_ISSUE_AUTHOR_LOGIN = "authorLogin";
public static final String FIELD_ISSUE_COMPONENT_UUID = "component";
public static final String FIELD_ISSUE_EFFORT = "effort";
@@ -126,7 +126,7 @@ public class IssueIndexDefinition implements IndexDefinition {
type.requireProjectAuthorization();
type.setEnableSource(enableSource);

type.keywordFieldBuilder(FIELD_ISSUE_ASSIGNEE).disableNorms().addSubFields(SORTABLE_ANALYZER).build();
type.keywordFieldBuilder(FIELD_ISSUE_ASSIGNEE_UUID).disableNorms().addSubFields(SORTABLE_ANALYZER).build();
type.keywordFieldBuilder(FIELD_ISSUE_AUTHOR_LOGIN).disableNorms().build();
type.keywordFieldBuilder(FIELD_ISSUE_COMPONENT_UUID).disableNorms().build();
type.createLongField(FIELD_ISSUE_EFFORT);

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java View File

@@ -190,7 +190,7 @@ class IssueIteratorForSingleChunk implements IssueIterator {

// all the fields must be present, even if value is null
doc.setKey(key);
doc.setAssignee(rs.getString(2));
doc.setAssigneeUuid(rs.getString(2));
doc.setLine(DatabaseUtils.getInt(rs, 3));
doc.setResolution(rs.getString(4));
doc.setSeverity(rs.getString(5));

+ 8
- 1
server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangeNotification.java View File

@@ -28,6 +28,7 @@ import org.sonar.api.notifications.Notification;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.FieldDiffs;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.user.UserDto;

import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_BRANCH;
import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PROJECT_KEY;
@@ -44,7 +45,6 @@ public class IssueChangeNotification extends Notification {

public IssueChangeNotification setIssue(DefaultIssue issue) {
setFieldValue("key", issue.key());
setFieldValue("assignee", issue.assignee());
setFieldValue("message", issue.message());
FieldDiffs currentChange = issue.currentChange();
if (currentChange != null) {
@@ -109,4 +109,11 @@ public class IssueChangeNotification extends Notification {
private static String neverEmptySerializableToString(@Nullable Serializable s) {
return s != null ? Strings.emptyToNull(s.toString()) : null;
}

public IssueChangeNotification setAssignee(@Nullable UserDto assignee) {
if (assignee != null) {
setFieldValue("assignee", assignee.getLogin());
}
return this;
}
}

+ 8
- 3
server/sonar-server/src/main/java/org/sonar/server/issue/notification/MyNewIssuesNotification.java View File

@@ -21,6 +21,9 @@ package org.sonar.server.issue.notification;

import org.sonar.api.utils.Durations;
import org.sonar.db.DbClient;
import org.sonar.db.user.UserDto;

import javax.annotation.Nullable;

import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_ASSIGNEE;

@@ -32,9 +35,11 @@ public class MyNewIssuesNotification extends NewIssuesNotification {
super(MY_NEW_ISSUES_NOTIF_TYPE, dbClient, durations);
}

public MyNewIssuesNotification setAssignee(String assignee) {
setFieldValue(FIELD_ASSIGNEE, assignee);

public MyNewIssuesNotification setAssignee(@Nullable UserDto assignee) {
if (assignee == null) {
return this;
}
setFieldValue(FIELD_ASSIGNEE, assignee.getLogin());
return this;
}


+ 13
- 4
server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesNotification.java View File

@@ -24,6 +24,7 @@ import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.ToIntFunction;
@@ -43,6 +44,8 @@ import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.issue.notification.NewIssuesStatistics.Metric;

import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_BRANCH;
import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PROJECT_VERSION;
import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PULL_REQUEST;
@@ -166,12 +169,18 @@ public class NewIssuesNotification extends Notification {
}

private void setAssigneesStatistics(DbSession dbSession, NewIssuesStatistics.Stats stats) {

Metric metric = Metric.ASSIGNEE;
List<Map.Entry<String, MetricStatsInt>> entries = fiveBiggest(stats.getDistributedMetricStats(metric), MetricStatsInt::getOnLeak);

Set<String> assigneeUuids = entries.stream().map(Map.Entry::getKey).filter(Objects::nonNull).collect(toSet());
Map<String, UserDto> userDtoByUuid = dbClient.userDao().selectByUuids(dbSession, assigneeUuids).stream().collect(toMap(UserDto::getUuid, u -> u));

int i = 1;
for (Map.Entry<String, MetricStatsInt> assigneeStats : fiveBiggest(stats.getDistributedMetricStats(metric), MetricStatsInt::getOnLeak)) {
String login = assigneeStats.getKey();
UserDto user = dbClient.userDao().selectByLogin(dbSession, login);
String name = user == null ? login : user.getName();
for (Map.Entry<String, MetricStatsInt> assigneeStats : entries) {
String assigneeUuid = assigneeStats.getKey();
UserDto user = userDtoByUuid.get(assigneeUuid);
String name = user == null ? assigneeUuid : user.getName();
setFieldValue(metric + DOT + i + LABEL, name);
setFieldValue(metric + DOT + i + COUNT, String.valueOf(assigneeStats.getValue().getOnLeak()));
i++;

+ 6
- 6
server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesStatistics.java View File

@@ -45,9 +45,9 @@ public class NewIssuesStatistics {

public void add(DefaultIssue issue) {
globalStatistics.add(issue);
String login = issue.assignee();
if (login != null) {
assigneesStatistics.computeIfAbsent(login, a -> new Stats(onLeakPredicate)).add(issue);
String userUuid = issue.assignee();
if (userUuid != null) {
assigneesStatistics.computeIfAbsent(userUuid, a -> new Stats(onLeakPredicate)).add(issue);
}
}

@@ -117,9 +117,9 @@ public class NewIssuesStatistics {
if (ruleKey != null) {
distributions.get(RULE).increment(ruleKey.toString(), isOnLeak);
}
String assignee = issue.assignee();
if (assignee != null) {
distributions.get(ASSIGNEE).increment(assignee, isOnLeak);
String assigneeUuid = issue.assignee();
if (assigneeUuid != null) {
distributions.get(ASSIGNEE).increment(assigneeUuid, isOnLeak);
}
for (String tag : issue.tags()) {
distributions.get(TAG).increment(tag, isOnLeak);

+ 2
- 2
server/sonar-server/src/main/java/org/sonar/server/issue/ws/AssignAction.java View File

@@ -111,11 +111,11 @@ public class AssignAction implements IssuesWsAction {
responseWriter.write(key, preloadedResponseData, request, response);
}

private SearchResponseData assign(String issueKey, @Nullable String assignee) {
private SearchResponseData assign(String issueKey, @Nullable String login) {
try (DbSession dbSession = dbClient.openSession(false)) {
IssueDto issueDto = issueFinder.getByKey(dbSession, issueKey);
DefaultIssue issue = issueDto.toDefaultIssue();
UserDto user = getUser(dbSession, assignee);
UserDto user = getUser(dbSession, login);
if (user != null) {
checkMembership(dbSession, issueDto, user);
}

+ 12
- 4
server/sonar-server/src/main/java/org/sonar/server/issue/ws/BulkChangeAction.java View File

@@ -25,11 +25,11 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.sonar.api.issue.DefaultTransitions;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
@@ -50,6 +50,7 @@ import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.issue.Action;
import org.sonar.server.issue.AddTagsAction;
import org.sonar.server.issue.AssignAction;
@@ -65,6 +66,8 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableMap.of;
import static java.lang.String.format;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import static org.sonar.api.issue.DefaultTransitions.REOPEN;
import static org.sonar.api.rule.Severity.BLOCKER;
import static org.sonar.api.rules.RuleType.BUG;
@@ -196,7 +199,10 @@ public class BulkChangeAction implements IssuesWsAction {

refreshLiveMeasures(dbSession, bulkChangeData, result);

items.forEach(sendNotification(issueChangeContext, bulkChangeData));
Set<String> assigneeUuids = items.stream().map(DefaultIssue::assignee).filter(Objects::nonNull).collect(toSet());
Map<String, UserDto> userDtoByUuid = dbClient.userDao().selectByUuids(dbSession, assigneeUuids).stream().collect(toMap(UserDto::getUuid, u -> u));

items.forEach(sendNotification(issueChangeContext, bulkChangeData, userDtoByUuid));

return result;
}
@@ -207,7 +213,7 @@ public class BulkChangeAction implements IssuesWsAction {
}
Set<String> touchedComponentUuids = result.success.stream()
.map(DefaultIssue::componentUuid)
.collect(Collectors.toSet());
.collect(toSet());
List<ComponentDto> touchedComponents = touchedComponentUuids.stream()
.map(data.componentsByUuid::get)
.collect(MoreCollectors.toList(touchedComponentUuids.size()));
@@ -244,11 +250,13 @@ public class BulkChangeAction implements IssuesWsAction {
bulkChangeData.getCommentAction().ifPresent(action -> action.execute(bulkChangeData.getProperties(action.key()), actionContext));
}

private Consumer<DefaultIssue> sendNotification(IssueChangeContext issueChangeContext, BulkChangeData bulkChangeData) {
private Consumer<DefaultIssue> sendNotification(IssueChangeContext issueChangeContext, BulkChangeData bulkChangeData,
Map<String, UserDto> userDtoByUuid) {
return issue -> {
if (bulkChangeData.sendNotification) {
notificationService.scheduleForSending(new IssueChangeNotification()
.setIssue(issue)
.setAssignee(userDtoByUuid.get(issue.assignee()))
.setChangeAuthorLogin(issueChangeContext.login())
.setRuleName(bulkChangeData.rulesByKey.get(issue.ruleKey()).getName())
.setProject(bulkChangeData.projectsByUuid.get(issue.projectUuid()))

+ 66
- 10
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java View File

@@ -22,6 +22,7 @@ package org.sonar.server.issue.ws;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
@@ -52,6 +53,7 @@ import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.es.Facets;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.issue.IssueQuery;
@@ -63,14 +65,18 @@ import org.sonarqube.ws.Issues.SearchWsResponse;

import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.nullToEmpty;
import static com.google.common.collect.Iterables.concat;
import static com.google.common.collect.Sets.newHashSet;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;
import static org.sonar.api.utils.Paging.forPageIndex;
import static org.sonar.core.util.stream.MoreCollectors.toSet;
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
import static org.sonar.server.issue.IssueQueryFactory.UNKNOWN;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
@@ -121,6 +127,8 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TYPES;

public class SearchAction implements IssuesWsAction {

public static final String LOGIN_MYSELF = "__me__";

private static final String INTERNAL_PARAMETER_DISCLAIMER = "This parameter is mostly used by the Issues page, please prefer usage of the componentKeys parameter. ";
private static final Set<String> IGNORED_FACETS = newHashSet(PARAM_PLANNED, DEPRECATED_PARAM_ACTION_PLANS, PARAM_REPORTERS);
private static final Set<String> FACETS_REQUIRING_PROJECT_OR_ORGANIZATION = newHashSet(PARAM_FILE_UUIDS, PARAM_DIRECTORIES, PARAM_MODULE_UUIDS);
@@ -339,14 +347,45 @@ public class SearchAction implements IssuesWsAction {

@Override
public final void handle(Request request, Response response) {
SearchWsResponse searchWsResponse = doHandle(toSearchWsRequest(request), request);
SearchRequest searchRequest = toSearchWsRequest(request)
.setAssigneesUuid(getLogins(request));
SearchWsResponse searchWsResponse = doHandle(searchRequest, request);
writeProtobuf(searchWsResponse, request, response);
}

private List<String> getLogins(Request request) {

List<String> assigneeLogins = request.paramAsStrings(PARAM_ASSIGNEES);

List<String> onlyLogins = new ArrayList<>();
for (String login : ofNullable(assigneeLogins).orElse(emptyList())) {
if (LOGIN_MYSELF.equals(login)) {
if (userSession.getLogin() == null) {
onlyLogins.add(UNKNOWN);
} else {
onlyLogins.add(userSession.getLogin());
}
} else {
onlyLogins.add(login);
}
}

try (DbSession dbSession = dbClient.openSession(false)) {
List<UserDto> userDtos = dbClient.userDao().selectByLogins(dbSession, onlyLogins);
List<String> assigneeUuid = userDtos.stream().map(UserDto::getUuid).collect(toList());

if ((assigneeLogins != null) && firstNonNull(assigneeUuid, emptyList()).isEmpty()) {
assigneeUuid = ImmutableList.of("non-existent-uuid");
}
return assigneeUuid;
}
}

private SearchWsResponse doHandle(SearchRequest request, Request wsRequest) {
// prepare the Elasticsearch request
SearchOptions options = createSearchOptionsFromRequest(request);
EnumSet<SearchAdditionalField> additionalFields = SearchAdditionalField.getFromRequest(request);

IssueQuery query = issueQueryFactory.create(request);

// execute request
@@ -372,7 +411,7 @@ public class SearchAction implements IssuesWsAction {
.filter(FACETS_REQUIRING_PROJECT_OR_ORGANIZATION::contains)
.collect(toSet());
checkArgument(facetsRequiringProjectOrOrganizationParameter.isEmpty() ||
(!query.projectUuids().isEmpty()) || query.organizationUuid() != null, "Facet(s) '%s' require to also filter by project or organization",
(!query.projectUuids().isEmpty()) || query.organizationUuid() != null, "Facet(s) '%s' require to also filter by project or organization",
COMA_JOINER.join(facetsRequiringProjectOrOrganizationParameter));
}
SearchResponseData preloadedData = new SearchResponseData(emptyList());
@@ -386,6 +425,8 @@ public class SearchAction implements IssuesWsAction {
// can be used to get total debt.
facets = reorderFacets(facets, options.getFacets());
replaceRuleIdsByRuleKeys(facets, firstNonNull(data.getRules(), emptyList()));
replaceAssigneeUuidByUserLogin(facets, data, PARAM_ASSIGNEES);
replaceAssigneeUuidByUserLogin(facets, data, FACET_ASSIGNED_TO_ME);

// FIXME allow long in Paging
Paging paging = forPageIndex(options.getPage()).withPageSize(options.getLimit()).andTotal((int) result.getHits().getTotalHits());
@@ -393,6 +434,21 @@ public class SearchAction implements IssuesWsAction {
return searchResponseFormat.formatSearch(additionalFields, data, paging, facets);
}

private static void replaceAssigneeUuidByUserLogin(@Nullable Facets facets, SearchResponseData data, String facet) {
if (facets == null) {
return;
}
LinkedHashMap<String, Long> assigneeFacets = facets.get(facet);
if (assigneeFacets == null) {
return;
}

LinkedHashMap<String, Long> newAssigneeFacets = new LinkedHashMap<>();
assigneeFacets.forEach((k, v) -> newAssigneeFacets.put(nullToEmpty(data.getLoginByUserUuid(k)), v));
assigneeFacets.clear();
assigneeFacets.putAll(newAssigneeFacets);
}

private void replaceRuleIdsByRuleKeys(@Nullable Facets facets, List<RuleDefinitionDto> alreadyLoadedRules) {
if (facets == null) {
return;
@@ -417,7 +473,7 @@ public class SearchAction implements IssuesWsAction {
alreadyLoadedRules
.stream()
.map(RuleDefinitionDto::getId)
.collect(Collectors.toList()));
.collect(toList()));

List<RuleDefinitionDto> ruleDefinitions = Stream.concat(
alreadyLoadedRules.stream(),
@@ -483,13 +539,13 @@ public class SearchAction implements IssuesWsAction {
addMandatoryValuesToFacet(facets, PARAM_PROJECT_UUIDS, request.getProjectUuids());

List<String> assignees = Lists.newArrayList("");
List<String> assigneesFromRequest = request.getAssignees();
List<String> assigneesFromRequest = request.getAssigneeUuids();
if (assigneesFromRequest != null) {
assignees.addAll(assigneesFromRequest);
assignees.remove(IssueQueryFactory.LOGIN_MYSELF);
assignees.remove(LOGIN_MYSELF);
}
addMandatoryValuesToFacet(facets, PARAM_ASSIGNEES, assignees);
addMandatoryValuesToFacet(facets, FACET_ASSIGNED_TO_ME, singletonList(userSession.getLogin()));
addMandatoryValuesToFacet(facets, FACET_ASSIGNED_TO_ME, singletonList(userSession.getUuid()));
addMandatoryValuesToFacet(facets, PARAM_RULES, request.getRules());
addMandatoryValuesToFacet(facets, PARAM_LANGUAGES, request.getLanguages());
addMandatoryValuesToFacet(facets, PARAM_TAGS, request.getTags());
@@ -502,6 +558,7 @@ public class SearchAction implements IssuesWsAction {
}
requestedFacets.stream()
.filter(facetName -> !FACET_ASSIGNED_TO_ME.equals(facetName))
.filter(facetName -> !PARAM_ASSIGNEES.equals(facetName))
.filter(facetName -> !IGNORED_FACETS.contains(facetName))
.forEach(facetName -> {
LinkedHashMap<String, Long> buckets = facets.get(facetName);
@@ -510,7 +567,7 @@ public class SearchAction implements IssuesWsAction {
return;
}
requestParams.stream()
.filter(param -> !buckets.containsKey(param) && !IssueQueryFactory.LOGIN_MYSELF.equals(param))
.filter(param -> !buckets.containsKey(param) && !LOGIN_MYSELF.equals(param))
// Prevent appearance of a glitch value due to dedicated parameter for this facet
.forEach(param -> buckets.put(param, 0L));
});
@@ -529,7 +586,7 @@ public class SearchAction implements IssuesWsAction {

private void collectLoggedInUser(SearchResponseLoader.Collector collector) {
if (userSession.isLoggedIn()) {
collector.add(SearchAdditionalField.USERS, userSession.getLogin());
collector.add(SearchAdditionalField.USERS, userSession.getUuid());
}
}

@@ -550,7 +607,7 @@ public class SearchAction implements IssuesWsAction {
collector.addComponentUuids(request.getFileUuids());
collector.addComponentUuids(request.getModuleUuids());
collector.addComponentUuids(request.getComponentRootUuids());
collector.addAll(SearchAdditionalField.USERS, request.getAssignees());
collector.addAll(SearchAdditionalField.USERS, request.getAssigneeUuids());
}

private static SearchRequest toSearchWsRequest(Request request) {
@@ -558,7 +615,6 @@ public class SearchAction implements IssuesWsAction {
.setAdditionalFields(request.paramAsStrings(PARAM_ADDITIONAL_FIELDS))
.setAsc(request.paramAsBoolean(PARAM_ASC))
.setAssigned(request.paramAsBoolean(PARAM_ASSIGNED))
.setAssignees(request.paramAsStrings(PARAM_ASSIGNEES))
.setAuthors(request.paramAsStrings(PARAM_AUTHORS))
.setComponentKeys(request.paramAsStrings(PARAM_COMPONENT_KEYS))
.setComponentRootUuids(request.paramAsStrings(PARAM_COMPONENT_ROOT_UUIDS))

+ 18
- 5
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseData.java View File

@@ -22,6 +22,8 @@ package org.sonar.server.issue.ws;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ListMultimap;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -30,6 +32,7 @@ import java.util.Map;
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.IssueChangeDto;
import org.sonar.db.issue.IssueDto;
@@ -48,7 +51,7 @@ public class SearchResponseData {
private final List<IssueDto> issues;

private Long effortTotal = null;
private List<UserDto> users = null;
private final Map<String, UserDto> usersByUuid = new HashMap<>();
private List<RuleDefinitionDto> rules = null;
private final Map<String, String> organizationKeysByUuid = new HashMap<>();
private final Map<String, ComponentDto> componentsByUuid = new HashMap<>();
@@ -80,9 +83,8 @@ public class SearchResponseData {
return componentsByUuid.get(uuid);
}

@CheckForNull
public List<UserDto> getUsers() {
return users;
return new ArrayList<>(usersByUuid.values());
}

@CheckForNull
@@ -120,8 +122,10 @@ public class SearchResponseData {
return null;
}

public void setUsers(@Nullable List<UserDto> users) {
this.users = users;
public void addUsers(@Nullable List<UserDto> users) {
if (users != null) {
users.forEach(u -> usersByUuid.put(u.getUuid(), u));
}
}

public void setRules(@Nullable List<RuleDefinitionDto> rules) {
@@ -170,4 +174,13 @@ public class SearchResponseData {
public void addOrganization(OrganizationDto organizationDto) {
this.organizationKeysByUuid.put(organizationDto.getUuid(), organizationDto.getKey());
}

@CheckForNull
public String getLoginByUserUuid(@Nullable String userUuid) {
UserDto userDto = usersByUuid.get(userUuid);
if (userDto != null) {
return userDto.getLogin();
}
return null;
}
}

+ 2
- 2
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java View File

@@ -180,7 +180,7 @@ public class SearchResponseFormat {
issueBuilder.setExternalRuleEngine(engineNameFrom(dto.getRuleKey()));
}
issueBuilder.setSeverity(Common.Severity.valueOf(dto.getSeverity()));
setNullable(emptyToNull(dto.getAssignee()), issueBuilder::setAssignee);
setNullable(data.getLoginByUserUuid(dto.getAssigneeUuid()), issueBuilder::setAssignee);
setNullable(emptyToNull(dto.getResolution()), issueBuilder::setResolution);
issueBuilder.setStatus(dto.getStatus());
issueBuilder.setMessage(nullToEmpty(dto.getMessage()));
@@ -288,7 +288,7 @@ public class SearchResponseFormat {
wsComment
.clear()
.setKey(comment.getKey())
.setLogin(nullToEmpty(comment.getUserLogin()))
.setLogin(nullToEmpty(data.getLoginByUserUuid(comment.getUserLogin())))
.setUpdatable(data.isUpdatableComment(comment.getKey()))
.setCreatedAt(DateUtils.formatDateTime(new Date(comment.getIssueChangeCreationDate())));
if (markdown != null) {

+ 32
- 16
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java View File

@@ -27,6 +27,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -55,6 +56,7 @@ import static com.google.common.collect.ImmutableSet.copyOf;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.difference;
import static java.util.Collections.emptyList;
import static java.util.Optional.ofNullable;
import static java.util.stream.Stream.concat;
import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
import static org.sonar.core.util.stream.MoreCollectors.toList;
@@ -96,10 +98,9 @@ public class SearchResponseLoader {
collector.collect(result.getIssues());

loadRules(preloadedResponseData, collector, dbSession, result);
// order is important - loading of comments complete the list of users: loadComments() is
// before loadUsers()
// order is important - loading of comments complete the list of users: loadComments() is before loadUsers()
loadComments(collector, dbSession, result);
loadUsers(preloadedResponseData, collector, dbSession, result);
loadUsers(preloadedResponseData, collector, dbSession, result, facets);
loadComponents(preloadedResponseData, collector, dbSession, result);
loadOrganizations(dbSession, result);
loadActionsAndTransitions(collector, result);
@@ -130,20 +131,34 @@ public class SearchResponseLoader {
.collect(Collectors.toList());
}

private void loadUsers(SearchResponseData preloadedResponseData, Collector collector, DbSession dbSession, SearchResponseData result) {
private void loadUsers(SearchResponseData preloadedResponseData, Collector collector, DbSession dbSession, SearchResponseData result, @Nullable Facets facets) {
Set<String> usersUuidToLoad = new HashSet<>();
// logged in user
ofNullable(userSession.getUuid()).ifPresent(usersUuidToLoad::add);

// add from issues (uuid -> login)
result.getIssues().forEach(issue -> ofNullable(issue.getAssigneeUuid()).ifPresent(usersUuidToLoad::add));

// add from facets (uuid -> login)
if (facets != null) {
getAssigneesFacet(facets).forEach((key, value) -> usersUuidToLoad.add(key));
}

// from facets
if (collector.contains(USERS)) {
List<UserDto> preloadedUsers = firstNonNull(preloadedResponseData.getUsers(), emptyList());
Set<String> preloadedLogins = preloadedUsers.stream().map(UserDto::getLogin).collect(MoreCollectors.toSet(preloadedUsers.size()));
Set<String> requestedLogins = collector.get(USERS);
Set<String> loginsToLoad = copyOf(difference(requestedLogins, preloadedLogins));

if (loginsToLoad.isEmpty()) {
result.setUsers(preloadedUsers);
} else {
List<UserDto> loadedUsers = dbClient.userDao().selectByLogins(dbSession, loginsToLoad);
result.setUsers(concat(preloadedUsers.stream(), loadedUsers.stream()).collect(toList(preloadedUsers.size() + loadedUsers.size())));
}
usersUuidToLoad.addAll(collector.get(USERS));
}

List<UserDto> preloadedUsers = firstNonNull(preloadedResponseData.getUsers(), emptyList());
result.addUsers(preloadedUsers);
preloadedUsers.forEach(userDto -> usersUuidToLoad.remove(userDto.getUuid()));

result.addUsers(dbClient.userDao().selectByUuids(dbSession, usersUuidToLoad));
}

private static HashMap<String, Long> getAssigneesFacet(Facets facets) {
LinkedHashMap<String, Long> assigneeFacet = facets.get("assignees");
return assigneeFacet != null ? assigneeFacet : new LinkedHashMap<>();
}

private void loadComponents(SearchResponseData preloadedResponseData, Collector collector, DbSession dbSession, SearchResponseData result) {
@@ -210,6 +225,7 @@ public class SearchResponseLoader {
List<IssueChangeDto> comments = dbClient.issueChangeDao().selectByTypeAndIssueKeys(dbSession, collector.getIssueKeys(), IssueChangeDto.TYPE_COMMENT);
result.setComments(comments);
for (IssueChangeDto comment : comments) {
// TODO GJT when addressing ticket on IssueChangesUuid
collector.add(USERS, comment.getUserLogin());
if (canEditOrDelete(comment)) {
result.addUpdatableComment(comment.getKey());
@@ -301,7 +317,7 @@ public class SearchResponseLoader {
componentUuids.add(issue.getComponentUuid());
projectUuids.add(issue.getProjectUuid());
ruleIds.add(issue.getRuleId());
add(USERS, issue.getAssignee());
add(USERS, issue.getAssigneeUuid());
collectComponentsFromIssueLocations(issue);
}
}

+ 5
- 0
server/sonar-server/src/main/java/org/sonar/server/user/DoPrivileged.java View File

@@ -71,6 +71,11 @@ public final class DoPrivileged {
return null;
}

@Override
public String getUuid() {
return null;
}

@Override
public String getName() {
return null;

+ 6
- 0
server/sonar-server/src/main/java/org/sonar/server/user/ServerUserSession.java View File

@@ -83,6 +83,12 @@ public class ServerUserSession extends AbstractUserSession {
return userDto == null ? null : userDto.getLogin();
}

@Override
@CheckForNull
public String getUuid() {
return userDto == null ? null : userDto.getUuid();
}

@Override
@CheckForNull
public String getName() {

+ 6
- 0
server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java View File

@@ -61,6 +61,12 @@ public class ThreadLocalUserSession implements UserSession {
return get().getLogin();
}

@Override
@CheckForNull
public String getUuid() {
return get().getUuid();
}

@Override
@CheckForNull
public String getName() {

+ 7
- 0
server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java View File

@@ -36,6 +36,13 @@ public interface UserSession {
@CheckForNull
String getLogin();

/**
* Uuid of the authenticated user. Returns {@code null}
* if {@link #isLoggedIn()} is {@code false}.
*/
@CheckForNull
String getUuid();

/**
* Name of the authenticated user. Returns {@code null}
* if {@link #isLoggedIn()} is {@code false}.

+ 1
- 0
server/sonar-server/src/test/java/org/sonar/server/authentication/SafeModeUserSessionTest.java View File

@@ -32,6 +32,7 @@ public class SafeModeUserSessionTest {
@Test
public void session_is_anonymous() {
assertThat(underTest.getLogin()).isNull();
assertThat(underTest.getUuid()).isNull();
assertThat(underTest.isLoggedIn()).isFalse();
assertThat(underTest.getName()).isNull();
assertThat(underTest.getUserId()).isNull();

+ 5
- 3
server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java View File

@@ -33,6 +33,7 @@ import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.user.UserDto;
import org.sonar.scanner.protocol.Constants.Severity;
import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue;
import org.sonar.server.component.TestComponentFinder;
@@ -81,7 +82,7 @@ public class IssuesActionTest {
.setMessage(null)
.setLine(null)
.setChecksum(null)
.setAssignee(null));
.setAssigneeUuid(null));
addPermissionTo(project);

ServerIssue serverIssue = call(project.getKey());
@@ -105,6 +106,7 @@ public class IssuesActionTest {

@Test
public void test_fields_with_non_null_values() throws Exception {
UserDto user = db.users().insertUser(u -> u.setLogin("simon").setName("Simon").setEmail("simon@email.com"));
RuleDefinitionDto rule = db.rules().insert();
ComponentDto project = db.components().insertPrivateProject();
ComponentDto module = db.components().insertComponent(newModuleDto(project));
@@ -117,7 +119,7 @@ public class IssuesActionTest {
.setMessage("the message")
.setLine(10)
.setChecksum("ABC")
.setAssignee("foo"));
.setAssigneeUuid(user.getUuid()));
addPermissionTo(project);

ServerIssue serverIssue = call(project.getKey());
@@ -135,7 +137,7 @@ public class IssuesActionTest {
assertThat(serverIssue.getMsg()).isEqualTo(issue.getMessage());
assertThat(serverIssue.getResolution()).isEqualTo(issue.getResolution());
assertThat(serverIssue.getChecksum()).isEqualTo(issue.getChecksum());
assertThat(serverIssue.getAssigneeLogin()).isEqualTo(issue.getAssignee());
assertThat(serverIssue.getAssigneeLogin()).isEqualTo(user.getLogin());
}

@Test

+ 7
- 7
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultAssigneeTest.java View File

@@ -59,7 +59,7 @@ public class DefaultAssigneeTest {

@Test
public void no_default_assignee() {
assertThat(underTest.loadDefaultAssigneeLogin()).isNull();
assertThat(underTest.loadDefaultAssigneeUuid()).isNull();
}

@Test
@@ -68,14 +68,14 @@ public class DefaultAssigneeTest {
UserDto userDto = db.users().insertUser("erik");
db.organizations().addMember(organizationDto, userDto);

assertThat(underTest.loadDefaultAssigneeLogin()).isEqualTo("erik");
assertThat(underTest.loadDefaultAssigneeUuid()).isEqualTo(userDto.getUuid());
}

@Test
public void configured_login_does_not_exist() {
settings.setProperty(CoreProperties.DEFAULT_ISSUE_ASSIGNEE, "erik");

assertThat(underTest.loadDefaultAssigneeLogin()).isNull();
assertThat(underTest.loadDefaultAssigneeUuid()).isNull();
}

@Test
@@ -83,7 +83,7 @@ public class DefaultAssigneeTest {
settings.setProperty(CoreProperties.DEFAULT_ISSUE_ASSIGNEE, "erik");
db.users().insertUser(user -> user.setLogin("erik").setActive(false));

assertThat(underTest.loadDefaultAssigneeLogin()).isNull();
assertThat(underTest.loadDefaultAssigneeUuid()).isNull();
}

@Test
@@ -93,7 +93,7 @@ public class DefaultAssigneeTest {
UserDto userDto = db.users().insertUser("erik");
db.organizations().addMember(otherOrganization, userDto);

assertThat(underTest.loadDefaultAssigneeLogin()).isNull();
assertThat(underTest.loadDefaultAssigneeUuid()).isNull();
}

@Test
@@ -101,10 +101,10 @@ public class DefaultAssigneeTest {
settings.setProperty(CoreProperties.DEFAULT_ISSUE_ASSIGNEE, "erik");
UserDto userDto = db.users().insertUser("erik");
db.organizations().addMember(organizationDto, userDto);
assertThat(underTest.loadDefaultAssigneeLogin()).isEqualTo("erik");
assertThat(underTest.loadDefaultAssigneeUuid()).isEqualTo(userDto.getUuid());

// The setting is updated but the assignee hasn't changed
settings.setProperty(CoreProperties.DEFAULT_ISSUE_ASSIGNEE, "other");
assertThat(underTest.loadDefaultAssigneeLogin()).isEqualTo("erik");
assertThat(underTest.loadDefaultAssigneeUuid()).isEqualTo(userDto.getUuid());
}
}

+ 6
- 6
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueAssignerTest.java View File

@@ -118,7 +118,7 @@ public class IssueAssignerTest {
public void set_default_assignee_if_author_not_found() {
addScmUser("john", null);
setSingleChangeset("john", 123456789L, "rev-1");
when(defaultAssignee.loadDefaultAssigneeLogin()).thenReturn("John C");
when(defaultAssignee.loadDefaultAssigneeUuid()).thenReturn("John C");
DefaultIssue issue = new DefaultIssue().setLine(1);

underTest.onIssue(FILE, issue);
@@ -144,7 +144,7 @@ public class IssueAssignerTest {
setSingleChangeset("john", 123456789L, "rev-1");
DefaultIssue issue = new DefaultIssue().setLine(1)
.setAuthorLogin("john")
.setAssignee(null);
.setAssigneeUuid(null);

underTest.onIssue(FILE, issue);

@@ -179,7 +179,7 @@ public class IssueAssignerTest {
public void when_noscm_data_is_available_defaultAssignee_should_be_used() {
DefaultIssue issue = new DefaultIssue().setLine(null);

when(defaultAssignee.loadDefaultAssigneeLogin()).thenReturn("DefaultAssignee");
when(defaultAssignee.loadDefaultAssigneeUuid()).thenReturn("DefaultAssignee");
underTest.onIssue(FILE, issue);

assertThat(issue.assignee()).isEqualTo("DefaultAssignee");
@@ -212,7 +212,7 @@ public class IssueAssignerTest {
"No SCM info has been found for issue DefaultIssue[key=<null>,type=VULNERABILITY,componentUuid=<null>,componentKey=<null>," +
"moduleUuid=<null>,moduleUuidPath=<null>,projectUuid=<null>,projectKey=<null>,ruleKey=<null>,language=<null>,severity=<null>," +
"manualSeverity=false,message=<null>,line=2,gap=<null>,effort=<null>,status=<null>,resolution=<null>," +
"assignee=<null>,checksum=<null>,attributes=<null>,authorLogin=<null>,comments=<null>,tags=<null>," +
"assigneeUuid=<null>,checksum=<null>,attributes=<null>,authorLogin=<null>,comments=<null>,tags=<null>," +
"locations=<null>,isFromExternalRuleEngine=false,creationDate=<null>,updateDate=<null>,closeDate=<null>,currentChange=<null>,changes=<null>,isNew=true,isCopied=false," +
"beingClosed=false,onDisabledRule=false,isChanged=false,sendNotifications=false,selectedAt=<null>]");
}
@@ -226,8 +226,8 @@ public class IssueAssignerTest {
.build());
}

private void addScmUser(String scmAccount, String userName) {
when(scmAccountToUser.getNullable(scmAccount)).thenReturn(userName);
private void addScmUser(String scmAccount, String userUuid) {
when(scmAccountToUser.getNullable(scmAccount)).thenReturn(userUuid);
}

}

+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycleTest.java View File

@@ -160,7 +160,7 @@ public class IssueLifecycleTest {
.setResolution(RESOLUTION_FIXED)
.setStatus(STATUS_CLOSED)
.setSeverity(BLOCKER)
.setAssignee("base assignee")
.setAssigneeUuid("base assignee")
.setAuthorLogin("base author")
.setTags(newArrayList("base tag"))
.setOnDisabledRule(true)
@@ -233,7 +233,7 @@ public class IssueLifecycleTest {
.setResolution(RESOLUTION_FIXED)
.setStatus(STATUS_CLOSED)
.setSeverity(BLOCKER)
.setAssignee("base assignee")
.setAssigneeUuid("base assignee")
.setAuthorLogin("base author")
.setTags(newArrayList("base tag"))
.setOnDisabledRule(true)

+ 3
- 2
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ScmAccountToUserLoaderTest.java View File

@@ -35,6 +35,7 @@ import org.sonar.server.user.index.UserIndex;
import org.sonar.server.user.index.UserIndexer;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;

@@ -66,7 +67,7 @@ public class ScmAccountToUserLoaderTest {
ScmAccountToUserLoader underTest = new ScmAccountToUserLoader(index, analysisMetadataHolder);

assertThat(underTest.load("missing")).isNull();
assertThat(underTest.load("jesuis@charlie.com")).isEqualTo(user.getLogin());
assertThat(underTest.load("jesuis@charlie.com")).isEqualTo(user.getUuid());
}

@Test
@@ -90,7 +91,7 @@ public class ScmAccountToUserLoaderTest {
UserIndex index = new UserIndex(es.client(), System2.INSTANCE);
ScmAccountToUserLoader underTest = new ScmAccountToUserLoader(index, analysisMetadataHolder);
try {
underTest.loadAll(Collections.emptyList());
underTest.loadAll(emptyList());
fail();
} catch (UnsupportedOperationException ignored) {
}

+ 12
- 10
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/UpdateConflictResolverTest.java View File

@@ -34,6 +34,8 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.sonar.api.issue.Issue.STATUS_OPEN;
import static org.sonar.api.rules.RuleType.CODE_SMELL;

public class UpdateConflictResolverTest {

@@ -41,28 +43,28 @@ public class UpdateConflictResolverTest {
public void should_reload_issue_and_resolve_conflict() {
DefaultIssue issue = new DefaultIssue()
.setKey("ABCDE")
.setType(RuleType.CODE_SMELL)
.setType(CODE_SMELL)
.setRuleKey(RuleKey.of("squid", "AvoidCycles"))
.setProjectUuid("U1")
.setComponentUuid("U2")
.setNew(false)
.setStatus(Issue.STATUS_OPEN);
.setStatus(STATUS_OPEN);

// Issue as seen and changed by end-user
IssueMapper mapper = mock(IssueMapper.class);
when(mapper.selectByKey("ABCDE")).thenReturn(
new IssueDto()
.setKee("ABCDE")
.setType(RuleType.CODE_SMELL)
.setType(CODE_SMELL)
.setRuleId(10)
.setRuleKey("squid", "AvoidCycles")
.setProjectUuid("U1")
.setComponentUuid("U2")
.setLine(10)
.setStatus(Issue.STATUS_OPEN)
.setStatus(STATUS_OPEN)

// field changed by user
.setAssignee("arthur")
.setAssigneeUuid("arthur-uuid")
);

new UpdateConflictResolver().resolve(issue, mapper);
@@ -71,7 +73,7 @@ public class UpdateConflictResolverTest {
verify(mapper).update(argument.capture());
IssueDto updatedIssue = argument.getValue();
assertThat(updatedIssue.getKee()).isEqualTo("ABCDE");
assertThat(updatedIssue.getAssignee()).isEqualTo("arthur");
assertThat(updatedIssue.getAssigneeUuid()).isEqualTo("arthur-uuid");
}

@Test
@@ -83,7 +85,7 @@ public class UpdateConflictResolverTest {
.setNew(false);

// Before starting scan
issue.setAssignee(null);
issue.setAssigneeUuid(null);
issue.setCreationDate(DateUtils.parseDate("2012-01-01"));
issue.setUpdateDate(DateUtils.parseDate("2012-02-02"));

@@ -106,7 +108,7 @@ public class UpdateConflictResolverTest {
.setLine(10)
.setResolution(Issue.RESOLUTION_FALSE_POSITIVE)
.setStatus(Issue.STATUS_RESOLVED)
.setAssignee("arthur")
.setAssigneeUuid("arthur")
.setSeverity(Severity.MAJOR)
.setManualSeverity(false);

@@ -133,7 +135,7 @@ public class UpdateConflictResolverTest {
.setRuleKey(RuleKey.of("squid", "AvoidCycles"))
.setComponentKey("struts:org.apache.struts.Action")
.setNew(false)
.setStatus(Issue.STATUS_OPEN);
.setStatus(STATUS_OPEN);

// Changed by scan
issue.setSeverity(Severity.BLOCKER);
@@ -142,7 +144,7 @@ public class UpdateConflictResolverTest {
// Issue as seen and changed by end-user
IssueDto dbIssue = new IssueDto()
.setKee("ABCDE")
.setStatus(Issue.STATUS_OPEN)
.setStatus(STATUS_OPEN)
.setSeverity(Severity.INFO)
.setManualSeverity(true);


+ 97
- 72
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/SendIssueNotificationsStepTest.java View File

@@ -19,10 +19,13 @@
*/
package org.sonar.server.computation.task.projectanalysis.step;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -39,8 +42,10 @@ import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.System2;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
import org.sonar.server.computation.task.projectanalysis.component.Component;
@@ -59,8 +64,13 @@ import org.sonar.server.issue.notification.NewIssuesStatistics;
import org.sonar.server.notification.NotificationService;
import org.sonar.server.util.cache.DiskCache;

import static java.util.Arrays.stream;
import static java.util.Collections.shuffle;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Stream.concat;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentCaptor.forClass;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
@@ -76,6 +86,7 @@ import static org.sonar.db.issue.IssueTesting.newIssue;
import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
import static org.sonar.db.rule.RuleTesting.newRule;
import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder;
import static org.sonar.server.computation.task.projectanalysis.step.SendIssueNotificationsStep.NOTIF_TYPES;

public class SendIssueNotificationsStepTest extends BaseStepTest {

@@ -86,7 +97,6 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
private static final int FIVE_MINUTES_IN_MS = 1000 * 60 * 5;

private static final Duration ISSUE_DURATION = Duration.create(100L);
private static final String ISSUE_ASSIGNEE = "John";

private static final Component FILE = builder(Component.Type.FILE, 11).build();
private static final Component PROJECT = builder(Type.PROJECT, 1)
@@ -104,6 +114,8 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
public RuleRepositoryRule ruleRepository = new RuleRepositoryRule();
@Rule
public TemporaryFolder temp = new TemporaryFolder();
@Rule
public DbTester db = DbTester.create(System2.INSTANCE);

private final Random random = new Random();
private final RuleType randomRuleType = RuleType.values()[random.nextInt(RuleType.values().length)];
@@ -119,7 +131,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
public void setUp() throws Exception {
issueCache = new IssueCache(temp.newFile(), System2.INSTANCE);
underTest = new SendIssueNotificationsStep(issueCache, ruleRepository, treeRootHolder, notificationService, analysisMetadataHolder,
newIssuesNotificationFactory);
newIssuesNotificationFactory, db.getDbClient());

when(newIssuesNotificationFactory.newNewIssuesNotification()).thenReturn(newIssuesNotificationMock);
when(newIssuesNotificationFactory.newMyNewIssuesNotification()).thenReturn(myNewIssuesNotificationMock);
@@ -127,7 +139,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {

@Test
public void do_not_send_notifications_if_no_subscribers() {
when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(false);
when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), NOTIF_TYPES)).thenReturn(false);

underTest.execute();

@@ -156,23 +168,23 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
Random random = new Random();
Integer[] efforts = IntStream.range(0, 1 + random.nextInt(10)).mapToObj(i -> 10_000 * i).toArray(Integer[]::new);
Integer[] backDatedEfforts = IntStream.range(0, 1 + random.nextInt(10)).mapToObj(i -> 10 + random.nextInt(100)).toArray(Integer[]::new);
Duration expectedEffort = Duration.create(Arrays.stream(efforts).mapToInt(i -> i).sum());
List<DefaultIssue> issues = Stream.concat(Arrays.stream(efforts)
Duration expectedEffort = Duration.create(stream(efforts).mapToInt(i -> i).sum());
List<DefaultIssue> issues = concat(stream(efforts)
.map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort))
.setCreationDate(new Date(ANALYSE_DATE))),
Arrays.stream(backDatedEfforts)
stream(backDatedEfforts)
.map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort))
.setCreationDate(new Date(ANALYSE_DATE - FIVE_MINUTES_IN_MS))))
.collect(Collectors.toList());
Collections.shuffle(issues);
.collect(toList());
shuffle(issues);
DiskCache<DefaultIssue>.DiskAppender issueCache = this.issueCache.newAppender();
issues.forEach(issueCache::append);
when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), NOTIF_TYPES)).thenReturn(true);

underTest.execute();

verify(notificationService).deliver(newIssuesNotificationMock);
ArgumentCaptor<NewIssuesStatistics.Stats> statsCaptor = ArgumentCaptor.forClass(NewIssuesStatistics.Stats.class);
ArgumentCaptor<NewIssuesStatistics.Stats> statsCaptor = forClass(NewIssuesStatistics.Stats.class);
verify(newIssuesNotificationMock).setStatistics(eq(PROJECT.getName()), statsCaptor.capture());
verify(newIssuesNotificationMock).setDebt(expectedEffort);
NewIssuesStatistics.Stats stats = statsCaptor.getValue();
@@ -190,7 +202,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
new DefaultIssue().setType(randomRuleType).setEffort(ISSUE_DURATION)
.setCreationDate(new Date(ANALYSE_DATE - FIVE_MINUTES_IN_MS)))
.close();
when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), NOTIF_TYPES)).thenReturn(true);

underTest.execute();

@@ -202,7 +214,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
ComponentDto branch = setUpProjectWithBranch();
issueCache.newAppender().append(
new DefaultIssue().setType(randomRuleType).setEffort(ISSUE_DURATION).setCreationDate(new Date(ANALYSE_DATE))).close();
when(notificationService.hasProjectSubscribersForTypes(branch.uuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
when(notificationService.hasProjectSubscribersForTypes(branch.uuid(), NOTIF_TYPES)).thenReturn(true);
analysisMetadataHolder.setBranch(newBranch());

underTest.execute();
@@ -219,7 +231,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
ComponentDto branch = setUpProjectWithBranch();
issueCache.newAppender().append(
new DefaultIssue().setType(randomRuleType).setEffort(ISSUE_DURATION).setCreationDate(new Date(ANALYSE_DATE))).close();
when(notificationService.hasProjectSubscribersForTypes(branch.uuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
when(notificationService.hasProjectSubscribersForTypes(branch.uuid(), NOTIF_TYPES)).thenReturn(true);
analysisMetadataHolder.setBranch(newPullRequest());
analysisMetadataHolder.setPullRequestId(PULL_REQUEST_ID);

@@ -237,7 +249,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
ComponentDto branch = setUpProjectWithBranch();
issueCache.newAppender().append(
new DefaultIssue().setType(randomRuleType).setEffort(ISSUE_DURATION).setCreationDate(new Date(ANALYSE_DATE - FIVE_MINUTES_IN_MS))).close();
when(notificationService.hasProjectSubscribersForTypes(branch.uuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
when(notificationService.hasProjectSubscribersForTypes(branch.uuid(), NOTIF_TYPES)).thenReturn(true);
analysisMetadataHolder.setBranch(newBranch());

underTest.execute();
@@ -247,8 +259,11 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {

@Test
public void send_new_issues_notification_to_user() {

UserDto user = db.users().insertUser();

issueCache.newAppender().append(
new DefaultIssue().setType(randomRuleType).setEffort(ISSUE_DURATION).setAssignee(ISSUE_ASSIGNEE)
new DefaultIssue().setType(randomRuleType).setEffort(ISSUE_DURATION).setAssigneeUuid(user.getUuid())
.setCreationDate(new Date(ANALYSE_DATE)))
.close();
when(notificationService.hasProjectSubscribersForTypes(eq(PROJECT.getUuid()), any())).thenReturn(true);
@@ -257,7 +272,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {

verify(notificationService).deliver(newIssuesNotificationMock);
verify(notificationService).deliver(myNewIssuesNotificationMock);
verify(myNewIssuesNotificationMock).setAssignee(ISSUE_ASSIGNEE);
verify(myNewIssuesNotificationMock).setAssignee(any(UserDto.class));
verify(myNewIssuesNotificationMock).setProject(PROJECT.getPublicKey(), PROJECT.getName(), null, null);
verify(myNewIssuesNotificationMock).setAnalysisDate(new Date(ANALYSE_DATE));
verify(myNewIssuesNotificationMock).setStatistics(eq(PROJECT.getName()), any(NewIssuesStatistics.Stats.class));
@@ -265,50 +280,57 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
}

@Test
public void send_new_issues_notification_to_user_only_for_those_assigned_to_her() {
Random random = new Random();
Integer[] assigned = IntStream.range(0, 1 + random.nextInt(10)).mapToObj(i -> 10_000 * i).toArray(Integer[]::new);
Integer[] assignedToOther = IntStream.range(0, 1 + random.nextInt(10)).mapToObj(i -> 10 + random.nextInt(100)).toArray(Integer[]::new);
Duration expectedEffort = Duration.create(Arrays.stream(assigned).mapToInt(i -> i).sum());
String assignee = randomAlphanumeric(5);
String otherAssignee = randomAlphanumeric(5);
List<DefaultIssue> issues = Stream.concat(Arrays.stream(assigned)
.map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort))
.setAssignee(assignee)
.setCreationDate(new Date(ANALYSE_DATE))),
Arrays.stream(assignedToOther)
public void send_new_issues_notification_to_user_only_for_those_assigned_to_her() throws IOException {

UserDto perceval = db.users().insertUser(u -> u.setLogin("perceval"));
Integer[] assigned = IntStream.range(0, 5).mapToObj(i -> 10_000 * i).toArray(Integer[]::new);
Duration expectedEffort = Duration.create(stream(assigned).mapToInt(i -> i).sum());

UserDto arthur = db.users().insertUser(u -> u.setLogin("arthur"));
Integer[] assignedToOther = IntStream.range(0, 3).mapToObj(i -> 10).toArray(Integer[]::new);

List<DefaultIssue> issues = concat(stream(assigned)
.map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort))
.setAssignee(otherAssignee)
.setAssigneeUuid(perceval.getUuid())
.setCreationDate(new Date(ANALYSE_DATE))),
stream(assignedToOther)
.map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort))
.setAssigneeUuid(arthur.getUuid())
.setCreationDate(new Date(ANALYSE_DATE))))
.collect(Collectors.toList());
Collections.shuffle(issues);
DiskCache<DefaultIssue>.DiskAppender issueCache = this.issueCache.newAppender();
issues.forEach(issueCache::append);
when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
.collect(toList());
shuffle(issues);
IssueCache issueCache = new IssueCache(temp.newFile(), System2.INSTANCE);
DiskCache<DefaultIssue>.DiskAppender newIssueCache = issueCache.newAppender();
issues.forEach(newIssueCache::append);

when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), NOTIF_TYPES)).thenReturn(true);

NewIssuesNotificationFactory newIssuesNotificationFactory = mock(NewIssuesNotificationFactory.class);
NewIssuesNotification newIssuesNotificationMock = createNewIssuesNotificationMock();
when(newIssuesNotificationFactory.newNewIssuesNotification()).thenReturn(newIssuesNotificationMock);

MyNewIssuesNotification myNewIssuesNotificationMock1 = createMyNewIssuesNotificationMock();
MyNewIssuesNotification myNewIssuesNotificationMock2 = createMyNewIssuesNotificationMock();
when(newIssuesNotificationFactory.newMyNewIssuesNotification())
.thenReturn(myNewIssuesNotificationMock)
.thenReturn(myNewIssuesNotificationMock2);
when(newIssuesNotificationFactory.newMyNewIssuesNotification()).thenReturn(myNewIssuesNotificationMock1).thenReturn(myNewIssuesNotificationMock2);

underTest.execute();
new SendIssueNotificationsStep(issueCache, ruleRepository, treeRootHolder, notificationService, analysisMetadataHolder, newIssuesNotificationFactory, db.getDbClient()).execute();

verify(notificationService).deliver(myNewIssuesNotificationMock1);
Map<String, MyNewIssuesNotification> myNewIssuesNotificationMocksByUsersName = new HashMap<>();
ArgumentCaptor<UserDto> userCaptor1 = forClass(UserDto.class);
verify(myNewIssuesNotificationMock1).setAssignee(userCaptor1.capture());
myNewIssuesNotificationMocksByUsersName.put(userCaptor1.getValue().getLogin(), myNewIssuesNotificationMock1);

verify(notificationService).deliver(newIssuesNotificationMock);
verify(notificationService).deliver(myNewIssuesNotificationMock);
verify(notificationService).deliver(myNewIssuesNotificationMock2);
ArgumentCaptor<UserDto> userCaptor2 = forClass(UserDto.class);
verify(myNewIssuesNotificationMock2).setAssignee(userCaptor2.capture());
myNewIssuesNotificationMocksByUsersName.put(userCaptor2.getValue().getLogin(), myNewIssuesNotificationMock2);

MyNewIssuesNotification myNewIssuesNotificationMock = myNewIssuesNotificationMocksByUsersName.get("perceval");
ArgumentCaptor<NewIssuesStatistics.Stats> statsCaptor = forClass(NewIssuesStatistics.Stats.class);
verify(myNewIssuesNotificationMock).setStatistics(eq(PROJECT.getName()), statsCaptor.capture());
verify(myNewIssuesNotificationMock).setDebt(expectedEffort);

MyNewIssuesNotification effectiveMyNewIssuesNotificationMock = this.myNewIssuesNotificationMock;
try {
verify(effectiveMyNewIssuesNotificationMock).setAssignee(assignee);
} catch (ArgumentsAreDifferent e) {
assertThat(e.getMessage())
.contains("Wanted:\nmyNewIssuesNotification.setAssignee(\"" + assignee + "\")")
.contains("Actual invocation has different arguments:\n" +
"myNewIssuesNotification.setAssignee(\"" + otherAssignee + "\")");
effectiveMyNewIssuesNotificationMock = myNewIssuesNotificationMock2;
}
ArgumentCaptor<NewIssuesStatistics.Stats> statsCaptor = ArgumentCaptor.forClass(NewIssuesStatistics.Stats.class);
verify(effectiveMyNewIssuesNotificationMock).setStatistics(eq(PROJECT.getName()), statsCaptor.capture());
verify(effectiveMyNewIssuesNotificationMock).setDebt(expectedEffort);
NewIssuesStatistics.Stats stats = statsCaptor.getValue();
assertThat(stats.hasIssues()).isTrue();
// just checking all issues have been added to the stats
@@ -320,30 +342,31 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {

@Test
public void send_new_issues_notification_to_user_only_for_non_backdated_issues() {
UserDto user = db.users().insertUser();
Random random = new Random();
Integer[] efforts = IntStream.range(0, 1 + random.nextInt(10)).mapToObj(i -> 10_000 * i).toArray(Integer[]::new);
Integer[] backDatedEfforts = IntStream.range(0, 1 + random.nextInt(10)).mapToObj(i -> 10 + random.nextInt(100)).toArray(Integer[]::new);
Duration expectedEffort = Duration.create(Arrays.stream(efforts).mapToInt(i -> i).sum());
List<DefaultIssue> issues = Stream.concat(Arrays.stream(efforts)
Duration expectedEffort = Duration.create(stream(efforts).mapToInt(i -> i).sum());
List<DefaultIssue> issues = concat(stream(efforts)
.map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort))
.setAssignee(ISSUE_ASSIGNEE)
.setAssigneeUuid(user.getUuid())
.setCreationDate(new Date(ANALYSE_DATE))),
Arrays.stream(backDatedEfforts)
stream(backDatedEfforts)
.map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort))
.setAssignee(ISSUE_ASSIGNEE)
.setAssigneeUuid(user.getUuid())
.setCreationDate(new Date(ANALYSE_DATE - FIVE_MINUTES_IN_MS))))
.collect(Collectors.toList());
Collections.shuffle(issues);
.collect(toList());
shuffle(issues);
DiskCache<DefaultIssue>.DiskAppender issueCache = this.issueCache.newAppender();
issues.forEach(issueCache::append);
when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), NOTIF_TYPES)).thenReturn(true);

underTest.execute();

verify(notificationService).deliver(newIssuesNotificationMock);
verify(notificationService).deliver(myNewIssuesNotificationMock);
verify(myNewIssuesNotificationMock).setAssignee(ISSUE_ASSIGNEE);
ArgumentCaptor<NewIssuesStatistics.Stats> statsCaptor = ArgumentCaptor.forClass(NewIssuesStatistics.Stats.class);
verify(myNewIssuesNotificationMock).setAssignee(any(UserDto.class));
ArgumentCaptor<NewIssuesStatistics.Stats> statsCaptor = forClass(NewIssuesStatistics.Stats.class);
verify(myNewIssuesNotificationMock).setStatistics(eq(PROJECT.getName()), statsCaptor.capture());
verify(myNewIssuesNotificationMock).setDebt(expectedEffort);
NewIssuesStatistics.Stats stats = statsCaptor.getValue();
@@ -357,11 +380,12 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {

@Test
public void do_not_send_new_issues_notification_to_user_if_issue_is_backdated() {
UserDto user = db.users().insertUser();
issueCache.newAppender().append(
new DefaultIssue().setType(randomRuleType).setEffort(ISSUE_DURATION).setAssignee(ISSUE_ASSIGNEE)
new DefaultIssue().setType(randomRuleType).setEffort(ISSUE_DURATION).setAssigneeUuid(user.getUuid())
.setCreationDate(new Date(ANALYSE_DATE - FIVE_MINUTES_IN_MS)))
.close();
when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), NOTIF_TYPES)).thenReturn(true);

underTest.execute();

@@ -379,28 +403,29 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
}

private void sendIssueChangeNotification(long issueCreatedAt) {
UserDto user = db.users().insertUser();
ComponentDto project = newPrivateProjectDto(newOrganizationDto()).setDbKey(PROJECT.getKey()).setLongName(PROJECT.getName());
ComponentDto file = newFileDto(project).setDbKey(FILE.getKey()).setLongName(FILE.getName());
RuleDefinitionDto ruleDefinitionDto = newRule();
DefaultIssue issue = newIssue(ruleDefinitionDto, project, file).toDefaultIssue()
.setNew(false).setChanged(true).setSendNotifications(true).setCreationDate(new Date(issueCreatedAt));
.setNew(false).setChanged(true).setSendNotifications(true).setCreationDate(new Date(issueCreatedAt)).setAssigneeUuid(user.getUuid());
ruleRepository.add(ruleDefinitionDto.getKey()).setName(ruleDefinitionDto.getName());
issueCache.newAppender().append(issue).close();
when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
when(notificationService.hasProjectSubscribersForTypes(PROJECT.getUuid(), NOTIF_TYPES)).thenReturn(true);

underTest.execute();

ArgumentCaptor<IssueChangeNotification> issueChangeNotificationCaptor = ArgumentCaptor.forClass(IssueChangeNotification.class);
ArgumentCaptor<IssueChangeNotification> issueChangeNotificationCaptor = forClass(IssueChangeNotification.class);
verify(notificationService).deliver(issueChangeNotificationCaptor.capture());
IssueChangeNotification issueChangeNotification = issueChangeNotificationCaptor.getValue();
assertThat(issueChangeNotification.getFieldValue("key")).isEqualTo(issue.key());
assertThat(issueChangeNotification.getFieldValue("assignee")).isEqualTo(issue.assignee());
assertThat(issueChangeNotification.getFieldValue("message")).isEqualTo(issue.message());
assertThat(issueChangeNotification.getFieldValue("ruleName")).isEqualTo(ruleDefinitionDto.getName());
assertThat(issueChangeNotification.getFieldValue("projectName")).isEqualTo(project.longName());
assertThat(issueChangeNotification.getFieldValue("projectKey")).isEqualTo(project.getKey());
assertThat(issueChangeNotification.getFieldValue("componentKey")).isEqualTo(file.getKey());
assertThat(issueChangeNotification.getFieldValue("componentName")).isEqualTo(file.longName());
assertThat(issueChangeNotification.getFieldValue("assignee")).isEqualTo(user.getLogin());
}

@Test
@@ -427,12 +452,12 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
.setCreationDate(new Date(issueCreatedAt));
ruleRepository.add(ruleDefinitionDto.getKey()).setName(ruleDefinitionDto.getName());
issueCache.newAppender().append(issue).close();
when(notificationService.hasProjectSubscribersForTypes(branch.uuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
when(notificationService.hasProjectSubscribersForTypes(branch.uuid(), NOTIF_TYPES)).thenReturn(true);
analysisMetadataHolder.setBranch(newBranch());

underTest.execute();

ArgumentCaptor<IssueChangeNotification> issueChangeNotificationCaptor = ArgumentCaptor.forClass(IssueChangeNotification.class);
ArgumentCaptor<IssueChangeNotification> issueChangeNotificationCaptor = forClass(IssueChangeNotification.class);
verify(notificationService).deliver(issueChangeNotificationCaptor.capture());
IssueChangeNotification issueChangeNotification = issueChangeNotificationCaptor.getValue();
assertThat(issueChangeNotification.getFieldValue("projectName")).isEqualTo(branch.longName());
@@ -454,7 +479,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {

private MyNewIssuesNotification createMyNewIssuesNotificationMock() {
MyNewIssuesNotification notification = mock(MyNewIssuesNotification.class);
when(notification.setAssignee(any())).thenReturn(notification);
when(notification.setAssignee(any(UserDto.class))).thenReturn(notification);
when(notification.setProject(any(), any(), any(), any())).thenReturn(notification);
when(notification.setProjectVersion(any())).thenReturn(notification);
when(notification.setAnalysisDate(any())).thenReturn(notification);

+ 7
- 5
server/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java View File

@@ -42,22 +42,24 @@ import org.sonar.server.tester.UserSessionRule;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.rules.ExpectedException.none;
import static org.sonar.server.tester.UserSessionRule.standalone;

public class AssignActionTest {

private static final String ISSUE_CURRENT_ASSIGNEE = "current assignee";
private static final String ISSUE_CURRENT_ASSIGNEE_UUID = "current assignee uuid";

@Rule
public ExpectedException expectedException = ExpectedException.none();
public ExpectedException expectedException = none();

@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
public UserSessionRule userSession = standalone();

@Rule
public DbTester db = DbTester.create();

private IssueChangeContext issueChangeContext = IssueChangeContext.createUser(new Date(), "emmerik");
private DefaultIssue issue = new DefaultIssue().setKey("ABC").setAssignee(ISSUE_CURRENT_ASSIGNEE);
private DefaultIssue issue = new DefaultIssue().setKey("ABC").setAssigneeUuid(ISSUE_CURRENT_ASSIGNEE_UUID);
private ComponentDto project;
private Action.Context context;
private OrganizationDto issueOrganizationDto;
@@ -81,7 +83,7 @@ public class AssignActionTest {
boolean executeResult = underTest.execute(properties, context);

assertThat(executeResult).isTrue();
assertThat(issue.assignee()).isEqualTo(assignee.getLogin());
assertThat(issue.assignee()).isEqualTo(assignee.getUuid());
}

@Test

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/issue/IssueDocTesting.java View File

@@ -58,7 +58,7 @@ public class IssueDocTesting {
doc.setKey(Uuids.createFast());
doc.setRuleId(nextInt(1000));
doc.setType(RuleType.CODE_SMELL);
doc.setAssignee("assignee_" + randomAlphabetic(5));
doc.setAssigneeUuid("assignee_uuid_" + randomAlphabetic(26));
doc.setAuthorLogin("author_" + randomAlphabetic(5));
doc.setLanguage("language_" + randomAlphabetic(5));
doc.setComponentUuid(Uuids.createFast());

+ 54
- 54
server/sonar-server/src/test/java/org/sonar/server/issue/IssueFieldsSetterTest.java View File

@@ -33,6 +33,7 @@ import org.sonar.core.issue.IssueChangeContext;
import org.sonar.db.user.UserDto;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.rules.ExpectedException.none;
import static org.sonar.db.user.UserTesting.newUserDto;
import static org.sonar.server.issue.IssueFieldsSetter.ASSIGNEE;
import static org.sonar.server.issue.IssueFieldsSetter.RESOLUTION;
@@ -44,30 +45,29 @@ import static org.sonar.server.issue.IssueFieldsSetter.UNUSED;
public class IssueFieldsSetterTest {

@Rule
public ExpectedException thrown = ExpectedException.none();
public ExpectedException thrown = none();

DefaultIssue issue = new DefaultIssue();
IssueChangeContext context = IssueChangeContext.createUser(new Date(), "emmerik");

IssueFieldsSetter updater = new IssueFieldsSetter();
private DefaultIssue issue = new DefaultIssue();
private IssueChangeContext context = IssueChangeContext.createUser(new Date(), "emmerik");
private IssueFieldsSetter underTest = new IssueFieldsSetter();

@Test
public void assign() {
UserDto user = newUserDto().setLogin("emmerik").setName("Emmerik");

boolean updated = updater.assign(issue, user, context);
boolean updated = underTest.assign(issue, user, context);
assertThat(updated).isTrue();
assertThat(issue.assignee()).isEqualTo("emmerik");
assertThat(issue.assignee()).isEqualTo(user.getUuid());
assertThat(issue.mustSendNotifications()).isTrue();
FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE);
assertThat(diff.oldValue()).isEqualTo(UNUSED);
assertThat(diff.newValue()).isEqualTo("Emmerik");
assertThat(diff.newValue()).isEqualTo(user.getUuid());
}

@Test
public void unassign() {
issue.setAssignee("morgan");
boolean updated = updater.assign(issue, null, context);
issue.setAssigneeUuid("morgan");
boolean updated = underTest.assign(issue, null, context);
assertThat(updated).isTrue();
assertThat(issue.assignee()).isNull();
assertThat(issue.mustSendNotifications()).isTrue();
@@ -80,22 +80,22 @@ public class IssueFieldsSetterTest {
public void change_assignee() {
UserDto user = newUserDto().setLogin("emmerik").setName("Emmerik");

issue.setAssignee("morgan");
boolean updated = updater.assign(issue, user, context);
issue.setAssigneeUuid("morgan");
boolean updated = underTest.assign(issue, user, context);
assertThat(updated).isTrue();
assertThat(issue.assignee()).isEqualTo("emmerik");
assertThat(issue.assignee()).isEqualTo(user.getUuid());
assertThat(issue.mustSendNotifications()).isTrue();
FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE);
assertThat(diff.oldValue()).isEqualTo(UNUSED);
assertThat(diff.newValue()).isEqualTo("Emmerik");
assertThat(diff.newValue()).isEqualTo(user.getUuid());
}

@Test
public void not_change_assignee() {
UserDto user = newUserDto().setLogin("morgan").setName("Morgan");

issue.setAssignee("morgan");
boolean updated = updater.assign(issue, user, context);
issue.setAssigneeUuid(user.getUuid());
boolean updated = underTest.assign(issue, user, context);
assertThat(updated).isFalse();
assertThat(issue.currentChange()).isNull();
assertThat(issue.mustSendNotifications()).isFalse();
@@ -103,7 +103,7 @@ public class IssueFieldsSetterTest {

@Test
public void set_new_assignee() {
boolean updated = updater.setNewAssignee(issue, "simon", context);
boolean updated = underTest.setNewAssignee(issue, "simon", context);
assertThat(updated).isTrue();
assertThat(issue.assignee()).isEqualTo("simon");
assertThat(issue.mustSendNotifications()).isTrue();
@@ -114,7 +114,7 @@ public class IssueFieldsSetterTest {

@Test
public void not_set_new_assignee_if_new_assignee_is_null() {
boolean updated = updater.setNewAssignee(issue, null, context);
boolean updated = underTest.setNewAssignee(issue, null, context);
assertThat(updated).isFalse();
assertThat(issue.currentChange()).isNull();
assertThat(issue.mustSendNotifications()).isFalse();
@@ -122,16 +122,16 @@ public class IssueFieldsSetterTest {

@Test
public void fail_with_ISE_when_setting_new_assignee_on_already_assigned_issue() {
issue.setAssignee("simon");
issue.setAssigneeUuid("simon");

thrown.expect(IllegalStateException.class);
thrown.expectMessage("It's not possible to update the assignee with this method, please use assign()");
updater.setNewAssignee(issue, "julien", context);
underTest.setNewAssignee(issue, "julien", context);
}

@Test
public void set_severity() {
boolean updated = updater.setSeverity(issue, "BLOCKER", context);
boolean updated = underTest.setSeverity(issue, "BLOCKER", context);
assertThat(updated).isTrue();
assertThat(issue.severity()).isEqualTo("BLOCKER");
assertThat(issue.manualSeverity()).isFalse();
@@ -145,7 +145,7 @@ public class IssueFieldsSetterTest {
@Test
public void set_past_severity() {
issue.setSeverity("BLOCKER");
boolean updated = updater.setPastSeverity(issue, "INFO", context);
boolean updated = underTest.setPastSeverity(issue, "INFO", context);
assertThat(updated).isTrue();
assertThat(issue.severity()).isEqualTo("BLOCKER");
assertThat(issue.mustSendNotifications()).isFalse();
@@ -158,7 +158,7 @@ public class IssueFieldsSetterTest {
@Test
public void update_severity() {
issue.setSeverity("BLOCKER");
boolean updated = updater.setSeverity(issue, "MINOR", context);
boolean updated = underTest.setSeverity(issue, "MINOR", context);

assertThat(updated).isTrue();
assertThat(issue.severity()).isEqualTo("MINOR");
@@ -171,7 +171,7 @@ public class IssueFieldsSetterTest {
@Test
public void not_change_severity() {
issue.setSeverity("MINOR");
boolean updated = updater.setSeverity(issue, "MINOR", context);
boolean updated = underTest.setSeverity(issue, "MINOR", context);
assertThat(updated).isFalse();
assertThat(issue.mustSendNotifications()).isFalse();
assertThat(issue.currentChange()).isNull();
@@ -181,7 +181,7 @@ public class IssueFieldsSetterTest {
public void not_revert_manual_severity() {
issue.setSeverity("MINOR").setManualSeverity(true);
try {
updater.setSeverity(issue, "MAJOR", context);
underTest.setSeverity(issue, "MAJOR", context);
} catch (IllegalStateException e) {
assertThat(e).hasMessage("Severity can't be changed");
}
@@ -190,7 +190,7 @@ public class IssueFieldsSetterTest {
@Test
public void set_manual_severity() {
issue.setSeverity("BLOCKER");
boolean updated = updater.setManualSeverity(issue, "MINOR", context);
boolean updated = underTest.setManualSeverity(issue, "MINOR", context);

assertThat(updated).isTrue();
assertThat(issue.severity()).isEqualTo("MINOR");
@@ -204,7 +204,7 @@ public class IssueFieldsSetterTest {
@Test
public void not_change_manual_severity() {
issue.setSeverity("MINOR").setManualSeverity(true);
boolean updated = updater.setManualSeverity(issue, "MINOR", context);
boolean updated = underTest.setManualSeverity(issue, "MINOR", context);
assertThat(updated).isFalse();
assertThat(issue.currentChange()).isNull();
assertThat(issue.mustSendNotifications()).isFalse();
@@ -212,7 +212,7 @@ public class IssueFieldsSetterTest {

@Test
public void set_line() {
boolean updated = updater.setLine(issue, 123);
boolean updated = underTest.setLine(issue, 123);
assertThat(updated).isTrue();
assertThat(issue.line()).isEqualTo(123);
assertThat(issue.mustSendNotifications()).isFalse();
@@ -223,7 +223,7 @@ public class IssueFieldsSetterTest {
@Test
public void set_past_line() {
issue.setLine(42);
boolean updated = updater.setPastLine(issue, 123);
boolean updated = underTest.setPastLine(issue, 123);
assertThat(updated).isTrue();
assertThat(issue.line()).isEqualTo(42);
assertThat(issue.mustSendNotifications()).isFalse();
@@ -235,7 +235,7 @@ public class IssueFieldsSetterTest {
@Test
public void line_is_not_changed() {
issue.setLine(123);
boolean updated = updater.setLine(issue, 123);
boolean updated = underTest.setLine(issue, 123);
assertThat(updated).isFalse();
assertThat(issue.line()).isEqualTo(123);
assertThat(issue.currentChange()).isNull();
@@ -245,7 +245,7 @@ public class IssueFieldsSetterTest {
@Test
public void change_locations() {
issue.setLocations("[1-3]");
boolean updated = updater.setLocations(issue, "[1-4]");
boolean updated = underTest.setLocations(issue, "[1-4]");
assertThat(updated).isTrue();
assertThat(issue.getLocations().toString()).isEqualTo("[1-4]");
assertThat(issue.currentChange()).isNull();
@@ -255,7 +255,7 @@ public class IssueFieldsSetterTest {
@Test
public void do_not_change_locations() {
issue.setLocations("[1-3]");
boolean updated = updater.setLocations(issue, "[1-3]");
boolean updated = underTest.setLocations(issue, "[1-3]");
assertThat(updated).isFalse();
assertThat(issue.getLocations().toString()).isEqualTo("[1-3]");
assertThat(issue.currentChange()).isNull();
@@ -265,7 +265,7 @@ public class IssueFieldsSetterTest {
@Test
public void set_locations_for_the_first_time() {
issue.setLocations(null);
boolean updated = updater.setLocations(issue, "[1-4]");
boolean updated = underTest.setLocations(issue, "[1-4]");
assertThat(updated).isTrue();
assertThat(issue.getLocations().toString()).isEqualTo("[1-4]");
assertThat(issue.currentChange()).isNull();
@@ -274,7 +274,7 @@ public class IssueFieldsSetterTest {

@Test
public void set_resolution() {
boolean updated = updater.setResolution(issue, "OPEN", context);
boolean updated = underTest.setResolution(issue, "OPEN", context);
assertThat(updated).isTrue();
assertThat(issue.resolution()).isEqualTo("OPEN");

@@ -287,7 +287,7 @@ public class IssueFieldsSetterTest {
@Test
public void not_change_resolution() {
issue.setResolution("FIXED");
boolean updated = updater.setResolution(issue, "FIXED", context);
boolean updated = underTest.setResolution(issue, "FIXED", context);
assertThat(updated).isFalse();
assertThat(issue.resolution()).isEqualTo("FIXED");
assertThat(issue.currentChange()).isNull();
@@ -296,7 +296,7 @@ public class IssueFieldsSetterTest {

@Test
public void set_status() {
boolean updated = updater.setStatus(issue, "OPEN", context);
boolean updated = underTest.setStatus(issue, "OPEN", context);
assertThat(updated).isTrue();
assertThat(issue.status()).isEqualTo("OPEN");

@@ -309,7 +309,7 @@ public class IssueFieldsSetterTest {
@Test
public void not_change_status() {
issue.setStatus("CLOSED");
boolean updated = updater.setStatus(issue, "CLOSED", context);
boolean updated = underTest.setStatus(issue, "CLOSED", context);
assertThat(updated).isFalse();
assertThat(issue.status()).isEqualTo("CLOSED");
assertThat(issue.currentChange()).isNull();
@@ -318,7 +318,7 @@ public class IssueFieldsSetterTest {

@Test
public void set_new_attribute_value() {
boolean updated = updater.setAttribute(issue, "JIRA", "FOO-123", context);
boolean updated = underTest.setAttribute(issue, "JIRA", "FOO-123", context);
assertThat(updated).isTrue();
assertThat(issue.attribute("JIRA")).isEqualTo("FOO-123");
assertThat(issue.currentChange().diffs()).hasSize(1);
@@ -330,7 +330,7 @@ public class IssueFieldsSetterTest {
@Test
public void unset_attribute() {
issue.setAttribute("JIRA", "FOO-123");
boolean updated = updater.setAttribute(issue, "JIRA", null, context);
boolean updated = underTest.setAttribute(issue, "JIRA", null, context);
assertThat(updated).isTrue();
assertThat(issue.attribute("JIRA")).isNull();
assertThat(issue.currentChange().diffs()).hasSize(1);
@@ -342,14 +342,14 @@ public class IssueFieldsSetterTest {
@Test
public void not_update_attribute() {
issue.setAttribute("JIRA", "FOO-123");
boolean updated = updater.setAttribute(issue, "JIRA", "FOO-123", context);
boolean updated = underTest.setAttribute(issue, "JIRA", "FOO-123", context);
assertThat(updated).isFalse();
assertThat(issue.mustSendNotifications()).isFalse();
}

@Test
public void set_effort_to_fix() {
boolean updated = updater.setGap(issue, 3.14, context);
boolean updated = underTest.setGap(issue, 3.14, context);
assertThat(updated).isTrue();
assertThat(issue.isChanged()).isTrue();
assertThat(issue.effortToFix()).isEqualTo(3.14);
@@ -359,7 +359,7 @@ public class IssueFieldsSetterTest {
@Test
public void not_set_effort_to_fix_if_unchanged() {
issue.setGap(3.14);
boolean updated = updater.setGap(issue, 3.14, context);
boolean updated = underTest.setGap(issue, 3.14, context);
assertThat(updated).isFalse();
assertThat(issue.isChanged()).isFalse();
assertThat(issue.effortToFix()).isEqualTo(3.14);
@@ -369,7 +369,7 @@ public class IssueFieldsSetterTest {
@Test
public void set_past_effort() {
issue.setGap(3.14);
boolean updated = updater.setPastGap(issue, 1.0, context);
boolean updated = underTest.setPastGap(issue, 1.0, context);
assertThat(updated).isTrue();
assertThat(issue.effortToFix()).isEqualTo(3.14);

@@ -383,7 +383,7 @@ public class IssueFieldsSetterTest {
Duration newDebt = Duration.create(15 * 8 * 60);
Duration previousDebt = Duration.create(10 * 8 * 60);
issue.setEffort(newDebt);
boolean updated = updater.setPastEffort(issue, previousDebt, context);
boolean updated = underTest.setPastEffort(issue, previousDebt, context);
assertThat(updated).isTrue();
assertThat(issue.debt()).isEqualTo(newDebt);
assertThat(issue.mustSendNotifications()).isFalse();
@@ -397,7 +397,7 @@ public class IssueFieldsSetterTest {
public void set_past_technical_debt_without_previous_value() {
Duration newDebt = Duration.create(15 * 8 * 60);
issue.setEffort(newDebt);
boolean updated = updater.setPastEffort(issue, null, context);
boolean updated = underTest.setPastEffort(issue, null, context);
assertThat(updated).isTrue();
assertThat(issue.debt()).isEqualTo(newDebt);
assertThat(issue.mustSendNotifications()).isFalse();
@@ -411,7 +411,7 @@ public class IssueFieldsSetterTest {
public void set_past_technical_debt_with_null_new_value() {
issue.setEffort(null);
Duration previousDebt = Duration.create(10 * 8 * 60);
boolean updated = updater.setPastEffort(issue, previousDebt, context);
boolean updated = underTest.setPastEffort(issue, previousDebt, context);
assertThat(updated).isTrue();
assertThat(issue.debt()).isNull();
assertThat(issue.mustSendNotifications()).isFalse();
@@ -423,7 +423,7 @@ public class IssueFieldsSetterTest {

@Test
public void set_message() {
boolean updated = updater.setMessage(issue, "the message", context);
boolean updated = underTest.setMessage(issue, "the message", context);
assertThat(updated).isTrue();
assertThat(issue.isChanged()).isTrue();
assertThat(issue.message()).isEqualTo("the message");
@@ -433,7 +433,7 @@ public class IssueFieldsSetterTest {
@Test
public void set_past_message() {
issue.setMessage("new message");
boolean updated = updater.setPastMessage(issue, "past message", context);
boolean updated = underTest.setPastMessage(issue, "past message", context);
assertThat(updated).isTrue();
assertThat(issue.message()).isEqualTo("new message");

@@ -444,7 +444,7 @@ public class IssueFieldsSetterTest {

@Test
public void set_author() {
boolean updated = updater.setAuthorLogin(issue, "eric", context);
boolean updated = underTest.setAuthorLogin(issue, "eric", context);
assertThat(updated).isTrue();
assertThat(issue.authorLogin()).isEqualTo("eric");

@@ -456,7 +456,7 @@ public class IssueFieldsSetterTest {

@Test
public void set_new_author() {
boolean updated = updater.setNewAuthor(issue, "simon", context);
boolean updated = underTest.setNewAuthor(issue, "simon", context);
assertThat(updated).isTrue();

FieldDiffs.Diff diff = issue.currentChange().get("author");
@@ -467,7 +467,7 @@ public class IssueFieldsSetterTest {

@Test
public void not_set_new_author_if_new_author_is_null() {
boolean updated = updater.setNewAuthor(issue, null, context);
boolean updated = underTest.setNewAuthor(issue, null, context);
assertThat(updated).isFalse();
assertThat(issue.currentChange()).isNull();
assertThat(issue.mustSendNotifications()).isFalse();
@@ -479,7 +479,7 @@ public class IssueFieldsSetterTest {

thrown.expect(IllegalStateException.class);
thrown.expectMessage("It's not possible to update the author with this method, please use setAuthorLogin()");
updater.setNewAuthor(issue, "julien", context);
underTest.setNewAuthor(issue, "julien", context);
}

@Test
@@ -487,7 +487,7 @@ public class IssueFieldsSetterTest {
String componentUuid = "a";
issue.setComponentUuid(componentUuid);

updater.setIssueMoved(issue, componentUuid, context);
underTest.setIssueMoved(issue, componentUuid, context);

assertThat(issue.changes()).isEmpty();
assertThat(issue.componentUuid()).isEqualTo(componentUuid);
@@ -502,7 +502,7 @@ public class IssueFieldsSetterTest {
String newComponentUuid = "b";
issue.setComponentUuid(oldComponentUuid);

updater.setIssueMoved(issue, newComponentUuid, context);
underTest.setIssueMoved(issue, newComponentUuid, context);

assertThat(issue.changes()).hasSize(1);
FieldDiffs fieldDiffs = issue.changes().get(0);

+ 4
- 3
server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryFactoryTest.java View File

@@ -36,13 +36,13 @@ import org.sonar.db.component.SnapshotDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.rule.RuleDbTester;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.tester.UserSessionRule;

import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.guava.api.Assertions.entry;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -70,6 +70,7 @@ public class IssueQueryFactoryTest {

@Test
public void create_from_parameters() {
UserDto user = db.users().insertUser(u -> u.setLogin("joanna"));
OrganizationDto organization = db.organizations().insert();
ComponentDto project = db.components().insertPrivateProject(organization);
ComponentDto module = db.components().insertComponent(newModuleDto(project));
@@ -88,7 +89,7 @@ public class IssueQueryFactoryTest {
.setModuleUuids(asList(module.uuid()))
.setDirectories(asList("aDirPath"))
.setFileUuids(asList(file.uuid()))
.setAssignees(asList("joanna"))
.setAssigneesUuid(asList(user.getUuid()))
.setLanguages(asList("xoo"))
.setTags(asList("tag1", "tag2"))
.setOrganization(organization.getKey())
@@ -109,7 +110,7 @@ public class IssueQueryFactoryTest {
assertThat(query.projectUuids()).containsOnly(project.uuid());
assertThat(query.moduleUuids()).containsOnly(module.uuid());
assertThat(query.fileUuids()).containsOnly(file.uuid());
assertThat(query.assignees()).containsOnly("joanna");
assertThat(query.assignees()).containsOnly(user.getUuid());
assertThat(query.languages()).containsOnly("xoo");
assertThat(query.tags()).containsOnly("tag1", "tag2");
assertThat(query.organizationUuid()).isEqualTo(organization.getUuid());

+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryTest.java View File

@@ -48,7 +48,7 @@ public class IssueQueryTest {
.componentUuids(newArrayList("org/struts/Action.java"))
.moduleUuids(newArrayList("org.struts:core"))
.rules(newArrayList(rule))
.assignees(newArrayList("gargantua"))
.assigneeUuids(newArrayList("gargantua"))
.languages(newArrayList("xoo"))
.tags(newArrayList("tag1", "tag2"))
.types(newArrayList("RELIABILITY", "SECURITY"))
@@ -121,7 +121,7 @@ public class IssueQueryTest {
.componentUuids(null)
.moduleUuids(null)
.statuses(null)
.assignees(null)
.assigneeUuids(null)
.resolutions(null)
.rules(null)
.severities(null)

+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/issue/IssueStorageTest.java View File

@@ -185,7 +185,7 @@ public class IssueStorageTest {
.setEffort(Duration.create(10L))
.setChecksum("FFFFF")
.setAuthorLogin("simon")
.setAssignee("loic")
.setAssigneeUuid("loic")
.setFieldChange(context, "severity", "INFO", "BLOCKER")
.setResolution("FIXED")
.setStatus("RESOLVED")
@@ -231,7 +231,7 @@ public class IssueStorageTest {
.setEffort(Duration.create(10L))
.setChecksum("FFFFF")
.setAuthorLogin("simon")
.setAssignee("loic")
.setAssigneeUuid("loic")
.setFieldChange(context, "severity", "INFO", "BLOCKER")
.setResolution("FIXED")
.setStatus("RESOLVED")

+ 7
- 1
server/sonar-server/src/test/java/org/sonar/server/issue/IssueUpdaterTest.java View File

@@ -38,6 +38,7 @@ import org.sonar.db.issue.IssueTesting;
import org.sonar.db.rule.RuleDbTester;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.rule.RuleDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.es.EsTester;
import org.sonar.server.issue.index.IssueIndexer;
import org.sonar.server.issue.index.IssueIteratorFactory;
@@ -99,10 +100,14 @@ public class IssueUpdaterTest {

@Test
public void verify_notification() {
UserDto user = dbTester.users().insertUser();
RuleDto rule = ruleDbTester.insertRule(newRuleDto());
ComponentDto project = componentDbTester.insertPrivateProject();
ComponentDto file = componentDbTester.insertComponent(newFileDto(project));
DefaultIssue issue = issueDbTester.insertIssue(IssueTesting.newIssue(rule.getDefinition(), project, file)).setSeverity(MAJOR).toDefaultIssue();
DefaultIssue issue = issueDbTester.insertIssue(IssueTesting.newIssue(rule.getDefinition(), project, file))
.setSeverity(MAJOR)
.setAssigneeUuid(user.getUuid())
.toDefaultIssue();
IssueChangeContext context = IssueChangeContext.createUser(new Date(), "john");
issueFieldsSetter.setSeverity(issue, BLOCKER, context);

@@ -120,6 +125,7 @@ public class IssueUpdaterTest {
assertThat(issueChangeNotification.getFieldValue("ruleName")).isEqualTo(rule.getName());
assertThat(issueChangeNotification.getFieldValue("changeAuthor")).isEqualTo("john");
assertThat(issueChangeNotification.getFieldValue("comment")).isEqualTo("increase severity");
assertThat(issueChangeNotification.getFieldValue("assignee")).isEqualTo(user.getLogin());
}

@Test

+ 16
- 16
server/sonar-server/src/test/java/org/sonar/server/issue/IssuesFinderSortTest.java View File

@@ -34,10 +34,10 @@ public class IssuesFinderSortTest {

@Test
public void should_sort_by_assignee() {
IssueDto issue1 = new IssueDto().setId(1L).setAssignee("perceval");
IssueDto issue2 = new IssueDto().setId(2L).setAssignee("arthur");
IssueDto issue3 = new IssueDto().setId(3L).setAssignee("vincent");
IssueDto issue4 = new IssueDto().setId(4L).setAssignee(null);
IssueDto issue1 = new IssueDto().setId(1L).setAssigneeUuid("perceval");
IssueDto issue2 = new IssueDto().setId(2L).setAssigneeUuid("arthur");
IssueDto issue3 = new IssueDto().setId(3L).setAssigneeUuid("vincent");
IssueDto issue4 = new IssueDto().setId(4L).setAssigneeUuid(null);
List<IssueDto> dtoList = newArrayList(issue1, issue2, issue3, issue4);

IssueQuery query = IssueQuery.builder().sort(IssueQuery.SORT_BY_ASSIGNEE).asc(true).build();
@@ -46,10 +46,10 @@ public class IssuesFinderSortTest {
List<IssueDto> result = newArrayList(issuesFinderSort.sort());

assertThat(result).hasSize(4);
assertThat(result.get(0).getAssignee()).isEqualTo("arthur");
assertThat(result.get(1).getAssignee()).isEqualTo("perceval");
assertThat(result.get(2).getAssignee()).isEqualTo("vincent");
assertThat(result.get(3).getAssignee()).isNull();
assertThat(result.get(0).getAssigneeUuid()).isEqualTo("arthur");
assertThat(result.get(1).getAssigneeUuid()).isEqualTo("perceval");
assertThat(result.get(2).getAssigneeUuid()).isEqualTo("vincent");
assertThat(result.get(3).getAssigneeUuid()).isNull();
}

@Test
@@ -174,10 +174,10 @@ public class IssuesFinderSortTest {

@Test
public void should_not_sort_with_null_sort() {
IssueDto issue1 = new IssueDto().setId(1L).setAssignee("perceval");
IssueDto issue2 = new IssueDto().setId(2L).setAssignee("arthur");
IssueDto issue3 = new IssueDto().setId(3L).setAssignee("vincent");
IssueDto issue4 = new IssueDto().setId(4L).setAssignee(null);
IssueDto issue1 = new IssueDto().setId(1L).setAssigneeUuid("perceval");
IssueDto issue2 = new IssueDto().setId(2L).setAssigneeUuid("arthur");
IssueDto issue3 = new IssueDto().setId(3L).setAssigneeUuid("vincent");
IssueDto issue4 = new IssueDto().setId(4L).setAssigneeUuid(null);
List<IssueDto> dtoList = newArrayList(issue1, issue2, issue3, issue4);

IssueQuery query = IssueQuery.builder().sort(null).build();
@@ -186,10 +186,10 @@ public class IssuesFinderSortTest {
List<IssueDto> result = newArrayList(issuesFinderSort.sort());

assertThat(result).hasSize(4);
assertThat(result.get(0).getAssignee()).isEqualTo("perceval");
assertThat(result.get(1).getAssignee()).isEqualTo("arthur");
assertThat(result.get(2).getAssignee()).isEqualTo("vincent");
assertThat(result.get(3).getAssignee()).isNull();
assertThat(result.get(0).getAssigneeUuid()).isEqualTo("perceval");
assertThat(result.get(1).getAssigneeUuid()).isEqualTo("arthur");
assertThat(result.get(2).getAssigneeUuid()).isEqualTo("vincent");
assertThat(result.get(3).getAssigneeUuid()).isNull();
}

@Test

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueStorageTest.java View File

@@ -133,7 +133,7 @@ public class ServerIssueStorageTest {
.setEffort(Duration.create(10L))
.setChecksum("FFFFF")
.setAuthorLogin("simon")
.setAssignee("loic")
.setAssigneeUuid("loic")
.setFieldChange(context, "severity", "INFO", "BLOCKER")
.setResolution("FIXED")
.setStatus("RESOLVED")

+ 19
- 17
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexDebtTest.java View File

@@ -28,7 +28,6 @@ import org.sonar.api.issue.Issue;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.internal.TestSystem2;
import org.sonar.db.DbTester;
@@ -49,6 +48,9 @@ import org.sonar.server.tester.UserSessionRule;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.sonar.api.issue.Issue.STATUS_CLOSED;
import static org.sonar.api.issue.Issue.STATUS_OPEN;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.DEPRECATED_FACET_MODE_DEBT;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.FACET_MODE_EFFORT;
@@ -149,9 +151,9 @@ public class IssueIndexDebtTest {
ComponentDto file = ComponentTesting.newFileDto(project, null);

indexIssues(
IssueDocTesting.newDoc("I1", file).setStatus(Issue.STATUS_CLOSED).setEffort(10L),
IssueDocTesting.newDoc("I2", file).setStatus(Issue.STATUS_CLOSED).setEffort(10L),
IssueDocTesting.newDoc("I3", file).setStatus(Issue.STATUS_OPEN).setEffort(10L));
IssueDocTesting.newDoc("I1", file).setStatus(STATUS_CLOSED).setEffort(10L),
IssueDocTesting.newDoc("I2", file).setStatus(STATUS_CLOSED).setEffort(10L),
IssueDocTesting.newDoc("I3", file).setStatus(STATUS_OPEN).setEffort(10L));

Facets facets = search("statuses");
assertThat(facets.getNames()).containsOnly("statuses", FACET_MODE_EFFORT);
@@ -199,14 +201,14 @@ public class IssueIndexDebtTest {
ComponentDto file = ComponentTesting.newFileDto(project, null);

indexIssues(
IssueDocTesting.newDoc("I1", file).setAssignee("steph").setEffort(10L),
IssueDocTesting.newDoc("I2", file).setAssignee("simon").setEffort(10L),
IssueDocTesting.newDoc("I3", file).setAssignee("simon").setEffort(10L),
IssueDocTesting.newDoc("I4", file).setAssignee(null).setEffort(10L));
IssueDocTesting.newDoc("I1", file).setAssigneeUuid("uuid-steph").setEffort(10L),
IssueDocTesting.newDoc("I2", file).setAssigneeUuid("uuid-simon").setEffort(10L),
IssueDocTesting.newDoc("I3", file).setAssigneeUuid("uuid-simon").setEffort(10L),
IssueDocTesting.newDoc("I4", file).setAssigneeUuid(null).setEffort(10L));

Facets facets = new Facets(underTest.search(newQueryBuilder().build(), new SearchOptions().addFacets(asList("assignees"))), system2.getDefaultTimeZone());
assertThat(facets.getNames()).containsOnly("assignees", FACET_MODE_EFFORT);
assertThat(facets.get("assignees")).containsOnly(entry("steph", 10L), entry("simon", 20L), entry("", 10L));
assertThat(facets.get("assignees")).containsOnly(entry("uuid-steph", 10L), entry("uuid-simon", 20L), entry("", 10L));
assertThat(facets.get(FACET_MODE_EFFORT)).containsOnly(entry("total", 40L));
}

@@ -231,7 +233,7 @@ public class IssueIndexDebtTest {
public void facet_on_created_at() {
SearchOptions searchOptions = fixtureForCreatedAtFacet();

Builder query = newQueryBuilder().createdBefore(DateUtils.parseDateTime("2016-01-01T00:00:00+0100"));
Builder query = newQueryBuilder().createdBefore(parseDateTime("2016-01-01T00:00:00+0100"));
Map<String, Long> createdAt = new Facets(underTest.search(query.build(), searchOptions), system2.getDefaultTimeZone()).get("createdAt");
assertThat(createdAt).containsOnly(
entry("2011-01-01", 10L),
@@ -263,13 +265,13 @@ public class IssueIndexDebtTest {
ComponentDto project = ComponentTesting.newPrivateProjectDto(newOrganizationDto());
ComponentDto file = ComponentTesting.newFileDto(project, null);

IssueDoc issue0 = IssueDocTesting.newDoc("ISSUE0", file).setEffort(10L).setFuncCreationDate(DateUtils.parseDateTime("2011-04-25T01:05:13+0100"));
IssueDoc issue1 = IssueDocTesting.newDoc("I1", file).setEffort(10L).setFuncCreationDate(DateUtils.parseDateTime("2014-09-01T12:34:56+0100"));
IssueDoc issue2 = IssueDocTesting.newDoc("I2", file).setEffort(10L).setFuncCreationDate(DateUtils.parseDateTime("2014-09-01T23:46:00+0100"));
IssueDoc issue3 = IssueDocTesting.newDoc("I3", file).setEffort(10L).setFuncCreationDate(DateUtils.parseDateTime("2014-09-02T12:34:56+0100"));
IssueDoc issue4 = IssueDocTesting.newDoc("I4", file).setEffort(10L).setFuncCreationDate(DateUtils.parseDateTime("2014-09-05T12:34:56+0100"));
IssueDoc issue5 = IssueDocTesting.newDoc("I5", file).setEffort(10L).setFuncCreationDate(DateUtils.parseDateTime("2014-09-20T12:34:56+0100"));
IssueDoc issue6 = IssueDocTesting.newDoc("I6", file).setEffort(10L).setFuncCreationDate(DateUtils.parseDateTime("2015-01-18T12:34:56+0100"));
IssueDoc issue0 = IssueDocTesting.newDoc("ISSUE0", file).setEffort(10L).setFuncCreationDate(parseDateTime("2011-04-25T01:05:13+0100"));
IssueDoc issue1 = IssueDocTesting.newDoc("I1", file).setEffort(10L).setFuncCreationDate(parseDateTime("2014-09-01T12:34:56+0100"));
IssueDoc issue2 = IssueDocTesting.newDoc("I2", file).setEffort(10L).setFuncCreationDate(parseDateTime("2014-09-01T23:46:00+0100"));
IssueDoc issue3 = IssueDocTesting.newDoc("I3", file).setEffort(10L).setFuncCreationDate(parseDateTime("2014-09-02T12:34:56+0100"));
IssueDoc issue4 = IssueDocTesting.newDoc("I4", file).setEffort(10L).setFuncCreationDate(parseDateTime("2014-09-05T12:34:56+0100"));
IssueDoc issue5 = IssueDocTesting.newDoc("I5", file).setEffort(10L).setFuncCreationDate(parseDateTime("2014-09-20T12:34:56+0100"));
IssueDoc issue6 = IssueDocTesting.newDoc("I6", file).setEffort(10L).setFuncCreationDate(parseDateTime("2015-01-18T12:34:56+0100"));

indexIssues(issue0, issue1, issue2, issue3, issue4, issue5, issue6);


+ 53
- 51
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexProjectStatisticsTest.java View File

@@ -27,6 +27,7 @@ import org.sonar.api.issue.Issue;
import org.sonar.api.utils.System2;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.es.EsTester;
import org.sonar.server.permission.index.AuthorizationTypeSupport;
import org.sonar.server.permission.index.PermissionIndexerDao;
@@ -44,6 +45,7 @@ import static org.sonar.db.component.ComponentTesting.newBranchDto;
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
import static org.sonar.db.component.ComponentTesting.newProjectBranch;
import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
import static org.sonar.db.user.UserTesting.newUserDto;
import static org.sonar.server.issue.IssueDocTesting.newDoc;

public class IssueIndexProjectStatisticsTest {
@@ -73,13 +75,13 @@ public class IssueIndexProjectStatisticsTest {

@Test
public void searchProjectStatistics_returns_something() {
OrganizationDto org = newOrganizationDto();
ComponentDto project = newPrivateProjectDto(org);
String userLogin = randomAlphanumeric(20);
OrganizationDto organization = newOrganizationDto();
ComponentDto project = newPrivateProjectDto(organization);
String userUuid = randomAlphanumeric(40);
long from = 1_111_234_567_890L;
indexIssues(newDoc("issue1", project).setAssignee(userLogin).setFuncCreationDate(new Date(from + 1L)));
indexIssues(newDoc("issue1", project).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)));

List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userLogin);
List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userUuid);

assertThat(result).extracting(ProjectStatistics::getProjectUuid).containsExactly(project.uuid());
}
@@ -88,12 +90,12 @@ public class IssueIndexProjectStatisticsTest {
public void searchProjectStatistics_does_not_return_results_if_assignee_does_not_match() {
OrganizationDto org1 = newOrganizationDto();
ComponentDto project = newPrivateProjectDto(org1);
String userLogin1 = randomAlphanumeric(20);
String userLogin2 = randomAlphanumeric(20);
String user1Uuid = randomAlphanumeric(40);
String user2Uuid = randomAlphanumeric(40);
long from = 1_111_234_567_890L;
indexIssues(newDoc("issue1", project).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1L)));
indexIssues(newDoc("issue1", project).setAssigneeUuid(user1Uuid).setFuncCreationDate(new Date(from + 1L)));

List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userLogin2);
List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), user2Uuid);

assertThat(result).isEmpty();
}
@@ -102,11 +104,11 @@ public class IssueIndexProjectStatisticsTest {
public void searchProjectStatistics_returns_results_if_assignee_matches() {
OrganizationDto org1 = newOrganizationDto();
ComponentDto project = newPrivateProjectDto(org1);
String userLogin1 = randomAlphanumeric(20);
String user1Uuid = randomAlphanumeric(40);
long from = 1_111_234_567_890L;
indexIssues(newDoc("issue1", project).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1L)));
indexIssues(newDoc("issue1", project).setAssigneeUuid(user1Uuid).setFuncCreationDate(new Date(from + 1L)));

List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userLogin1);
List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), user1Uuid);

assertThat(result).extracting(ProjectStatistics::getProjectUuid).containsExactly(project.uuid());
}
@@ -115,11 +117,11 @@ public class IssueIndexProjectStatisticsTest {
public void searchProjectStatistics_returns_results_if_functional_date_is_strictly_after_from_date() {
OrganizationDto org1 = newOrganizationDto();
ComponentDto project = newPrivateProjectDto(org1);
String userLogin1 = randomAlphanumeric(20);
String userUuid = randomAlphanumeric(40);
long from = 1_111_234_567_890L;
indexIssues(newDoc("issue1", project).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1L)));
indexIssues(newDoc("issue1", project).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)));

List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userLogin1);
List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userUuid);

assertThat(result).extracting(ProjectStatistics::getProjectUuid).containsExactly(project.uuid());
}
@@ -128,11 +130,11 @@ public class IssueIndexProjectStatisticsTest {
public void searchProjectStatistics_does_not_return_results_if_functional_date_is_same_as_from_date() {
OrganizationDto org1 = newOrganizationDto();
ComponentDto project = newPrivateProjectDto(org1);
String userLogin1 = randomAlphanumeric(20);
String userUuid = randomAlphanumeric(40);
long from = 1_111_234_567_890L;
indexIssues(newDoc("issue1", project).setAssignee(userLogin1).setFuncCreationDate(new Date(from)));
indexIssues(newDoc("issue1", project).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from)));

List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userLogin1);
List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userUuid);

assertThat(result).extracting(ProjectStatistics::getProjectUuid).containsExactly(project.uuid());
}
@@ -141,15 +143,15 @@ public class IssueIndexProjectStatisticsTest {
public void searchProjectStatistics_does_not_return_resolved_issues() {
OrganizationDto org1 = newOrganizationDto();
ComponentDto project = newPrivateProjectDto(org1);
String userLogin1 = randomAlphanumeric(20);
String userUuid = randomAlphanumeric(40);
long from = 1_111_234_567_890L;
indexIssues(
newDoc("issue1", project).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1L)).setResolution(Issue.RESOLUTION_FALSE_POSITIVE),
newDoc("issue1", project).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1L)).setResolution(Issue.RESOLUTION_FIXED),
newDoc("issue1", project).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1L)).setResolution(Issue.RESOLUTION_REMOVED),
newDoc("issue1", project).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1L)).setResolution(Issue.RESOLUTION_WONT_FIX));
newDoc("issue1", project).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)).setResolution(Issue.RESOLUTION_FALSE_POSITIVE),
newDoc("issue1", project).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)).setResolution(Issue.RESOLUTION_FIXED),
newDoc("issue1", project).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)).setResolution(Issue.RESOLUTION_REMOVED),
newDoc("issue1", project).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)).setResolution(Issue.RESOLUTION_WONT_FIX));

List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userLogin1);
List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userUuid);

assertThat(result).isEmpty();
}
@@ -158,11 +160,11 @@ public class IssueIndexProjectStatisticsTest {
public void searchProjectStatistics_does_not_return_results_if_functional_date_is_before_from_date() {
OrganizationDto org1 = newOrganizationDto();
ComponentDto project = newPrivateProjectDto(org1);
String userLogin1 = randomAlphanumeric(20);
String userUuid = randomAlphanumeric(40);
long from = 1_111_234_567_890L;
indexIssues(newDoc("issue1", project).setAssignee(userLogin1).setFuncCreationDate(new Date(from - 1000L)));
indexIssues(newDoc("issue1", project).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from - 1000L)));

List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userLogin1);
List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userUuid);

assertThat(result).isEmpty();
}
@@ -171,14 +173,14 @@ public class IssueIndexProjectStatisticsTest {
public void searchProjectStatistics_returns_issue_count() {
OrganizationDto org1 = newOrganizationDto();
ComponentDto project = newPrivateProjectDto(org1);
String userLogin1 = randomAlphanumeric(20);
String userUuid = randomAlphanumeric(40);
long from = 1_111_234_567_890L;
indexIssues(
newDoc("issue1", project).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1L)),
newDoc("issue2", project).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1L)),
newDoc("issue3", project).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1L)));
newDoc("issue1", project).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)),
newDoc("issue2", project).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)),
newDoc("issue3", project).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)));

List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userLogin1);
List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userUuid);

assertThat(result).extracting(ProjectStatistics::getIssueCount).containsExactly(3L);
}
@@ -189,20 +191,20 @@ public class IssueIndexProjectStatisticsTest {
ComponentDto project1 = newPrivateProjectDto(org1);
ComponentDto project2 = newPrivateProjectDto(org1);
ComponentDto project3 = newPrivateProjectDto(org1);
String userLogin1 = randomAlphanumeric(20);
String userUuid = randomAlphanumeric(40);
long from = 1_111_234_567_890L;
indexIssues(
newDoc("issue1", project1).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1L)),
newDoc("issue2", project1).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1L)),
newDoc("issue3", project1).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1L)),
newDoc("issue1", project1).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)),
newDoc("issue2", project1).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)),
newDoc("issue3", project1).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)),

newDoc("issue4", project3).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1L)),
newDoc("issue5", project3).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1L)));
newDoc("issue4", project3).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)),
newDoc("issue5", project3).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)));

List<ProjectStatistics> result = underTest.searchProjectStatistics(
asList(project1.uuid(), project2.uuid(), project3.uuid()),
asList(from, from, from),
userLogin1);
userUuid);

assertThat(result)
.extracting(ProjectStatistics::getProjectUuid, ProjectStatistics::getIssueCount)
@@ -217,20 +219,20 @@ public class IssueIndexProjectStatisticsTest {
ComponentDto project1 = newPrivateProjectDto(org1);
ComponentDto project2 = newPrivateProjectDto(org1);
ComponentDto project3 = newPrivateProjectDto(org1);
String userLogin1 = randomAlphanumeric(20);
String userUuid = randomAlphanumeric(40);
long from = 1_111_234_567_000L;
indexIssues(
newDoc("issue1", project1).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 1_000L)),
newDoc("issue2", project1).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 2_000L)),
newDoc("issue3", project1).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 3_000L)),
newDoc("issue1", project1).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1_000L)),
newDoc("issue2", project1).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 2_000L)),
newDoc("issue3", project1).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 3_000L)),

newDoc("issue4", project3).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 4_000L)),
newDoc("issue5", project3).setAssignee(userLogin1).setFuncCreationDate(new Date(from + 5_000L)));
newDoc("issue4", project3).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 4_000L)),
newDoc("issue5", project3).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 5_000L)));

List<ProjectStatistics> result = underTest.searchProjectStatistics(
asList(project1.uuid(), project2.uuid(), project3.uuid()),
asList(from, from, from),
userLogin1);
userUuid);

assertThat(result)
.extracting(ProjectStatistics::getProjectUuid, ProjectStatistics::getLastIssueDate)
@@ -244,14 +246,14 @@ public class IssueIndexProjectStatisticsTest {
OrganizationDto organization = newOrganizationDto();
ComponentDto project = newPrivateProjectDto(organization);
ComponentDto branch = newProjectBranch(project, newBranchDto(project).setKey("branch"));
String userLogin = randomAlphanumeric(20);
String userUuid = randomAlphanumeric(40);
long from = 1_111_234_567_890L;
indexIssues(
newDoc("issue1", branch).setAssignee(userLogin).setFuncCreationDate(new Date(from + 1L)),
newDoc("issue2", branch).setAssignee(userLogin).setFuncCreationDate(new Date(from + 2L)),
newDoc("issue3", project).setAssignee(userLogin).setFuncCreationDate(new Date(from + 1L)));
newDoc("issue1", branch).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)),
newDoc("issue2", branch).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 2L)),
newDoc("issue3", project).setAssigneeUuid(userUuid).setFuncCreationDate(new Date(from + 1L)));

List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userLogin);
List<ProjectStatistics> result = underTest.searchProjectStatistics(singletonList(project.uuid()), singletonList(from), userUuid);

assertThat(result)
.extracting(ProjectStatistics::getIssueCount, ProjectStatistics::getProjectUuid, ProjectStatistics::getLastIssueDate)

+ 30
- 26
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java View File

@@ -27,7 +27,6 @@ import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.assertj.core.api.Fail;
@@ -63,9 +62,11 @@ import static com.google.common.collect.ImmutableSortedSet.of;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.TimeZone.getTimeZone;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.assertj.core.api.Assertions.tuple;
import static org.junit.rules.ExpectedException.none;
import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
import static org.sonar.api.rules.RuleType.BUG;
import static org.sonar.api.rules.RuleType.CODE_SMELL;
@@ -81,18 +82,20 @@ import static org.sonar.db.rule.RuleTesting.newRule;
import static org.sonar.db.user.GroupTesting.newGroupDto;
import static org.sonar.db.user.UserTesting.newUserDto;
import static org.sonar.server.issue.IssueDocTesting.newDoc;
import static org.sonar.server.tester.UserSessionRule.standalone;

public class IssueIndexTest {

@Rule
public EsTester es = EsTester.create();
@Rule
public UserSessionRule userSessionRule = UserSessionRule.standalone();
public UserSessionRule userSessionRule = standalone();
@Rule
public ExpectedException expectedException = ExpectedException.none();
private System2 system2 = new TestSystem2().setNow(1_500_000_000_000L).setDefaultTimeZone(TimeZone.getTimeZone("GMT-01:00"));
public ExpectedException expectedException = none();
private System2 system2 = new TestSystem2().setNow(1_500_000_000_000L).setDefaultTimeZone(getTimeZone("GMT-01:00"));
@Rule
public DbTester db = DbTester.create(system2);

private IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()));
private ViewIndexer viewIndexer = new ViewIndexer(db.getDbClient(), es.client());
private RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient());
@@ -553,13 +556,13 @@ public class IssueIndexTest {
ComponentDto file = newFileDto(project, null);

indexIssues(
newDoc("I1", file).setAssignee("steph"),
newDoc("I2", file).setAssignee("marcel"),
newDoc("I3", file).setAssignee(null));
newDoc("I1", file).setAssigneeUuid("steph-uuid"),
newDoc("I2", file).setAssigneeUuid("marcel-uuid"),
newDoc("I3", file).setAssigneeUuid(null));

assertThatSearchReturnsOnly(IssueQuery.builder().assignees(singletonList("steph")), "I1");
assertThatSearchReturnsOnly(IssueQuery.builder().assignees(asList("steph", "marcel")), "I1", "I2");
assertThatSearchReturnsEmpty(IssueQuery.builder().assignees(singletonList("unknown")));
assertThatSearchReturnsOnly(IssueQuery.builder().assigneeUuids(singletonList("steph-uuid")), "I1");
assertThatSearchReturnsOnly(IssueQuery.builder().assigneeUuids(asList("steph-uuid", "marcel-uuid")), "I1", "I2");
assertThatSearchReturnsEmpty(IssueQuery.builder().assigneeUuids(singletonList("unknown")));
}

@Test
@@ -568,12 +571,12 @@ public class IssueIndexTest {
ComponentDto file = newFileDto(project, null);

indexIssues(
newDoc("I1", file).setAssignee("steph"),
newDoc("I2", file).setAssignee("marcel"),
newDoc("I3", file).setAssignee("marcel"),
newDoc("I4", file).setAssignee(null));
newDoc("I1", file).setAssigneeUuid("steph-uuid"),
newDoc("I2", file).setAssigneeUuid("marcel-uuid"),
newDoc("I3", file).setAssigneeUuid("marcel-uuid"),
newDoc("I4", file).setAssigneeUuid(null));

assertThatFacetHasOnly(IssueQuery.builder(), "assignees", entry("steph", 1L), entry("marcel", 2L), entry("", 1L));
assertThatFacetHasOnly(IssueQuery.builder(), "assignees", entry("steph-uuid", 1L), entry("marcel-uuid", 2L), entry("", 1L));
}

@Test
@@ -582,12 +585,13 @@ public class IssueIndexTest {
ComponentDto file = newFileDto(project, null);

indexIssues(
newDoc("I1", file).setAssignee("j-b"),
newDoc("I2", file).setAssignee("marcel"),
newDoc("I3", file).setAssignee("marcel"),
newDoc("I4", file).setAssignee(null));
newDoc("I1", file).setAssigneeUuid("j-b-uuid"),
newDoc("I2", file).setAssigneeUuid("marcel-uuid"),
newDoc("I3", file).setAssigneeUuid("marcel-uuid"),
newDoc("I4", file).setAssigneeUuid(null));

assertThatFacetHasOnly(IssueQuery.builder().assignees(singletonList("j-b")), "assignees", entry("j-b", 1L), entry("marcel", 2L), entry("", 1L));
assertThatFacetHasOnly(IssueQuery.builder().assigneeUuids(singletonList("j-b")),
"assignees", entry("j-b-uuid", 1L), entry("marcel-uuid", 2L), entry("", 1L));
}

@Test
@@ -596,9 +600,9 @@ public class IssueIndexTest {
ComponentDto file = newFileDto(project, null);

indexIssues(
newDoc("I1", file).setAssignee("steph"),
newDoc("I2", file).setAssignee(null),
newDoc("I3", file).setAssignee(null));
newDoc("I1", file).setAssigneeUuid("steph-uuid"),
newDoc("I2", file).setAssigneeUuid(null),
newDoc("I3", file).setAssigneeUuid(null));

assertThatSearchReturnsOnly(IssueQuery.builder().assigned(true), "I1");
assertThatSearchReturnsOnly(IssueQuery.builder().assigned(false), "I2", "I3");
@@ -613,7 +617,7 @@ public class IssueIndexTest {
indexIssues(
newDoc("I1", file).setAuthorLogin("steph"),
newDoc("I2", file).setAuthorLogin("marcel"),
newDoc("I3", file).setAssignee(null));
newDoc("I3", file).setAssigneeUuid(null));

assertThatSearchReturnsOnly(IssueQuery.builder().authors(singletonList("steph")), "I1");
assertThatSearchReturnsOnly(IssueQuery.builder().authors(asList("steph", "marcel")), "I1", "I2");
@@ -991,8 +995,8 @@ public class IssueIndexTest {
ComponentDto file = newFileDto(project, null);

indexIssues(
newDoc("I1", file).setAssignee("steph"),
newDoc("I2", file).setAssignee("marcel"));
newDoc("I1", file).setAssigneeUuid("steph-uuid"),
newDoc("I2", file).setAssigneeUuid("marcel-uuid"));

IssueQuery.Builder query = IssueQuery.builder().sort(IssueQuery.SORT_BY_ASSIGNEE).asc(true);
assertThatSearchReturnsOnly(query, "I2", "I1");

+ 3
- 2
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexerTest.java View File

@@ -52,6 +52,7 @@ import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.rules.ExpectedException.none;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.server.issue.IssueDocTesting.newDoc;
import static org.sonar.server.issue.index.IssueIndexDefinition.INDEX_TYPE_ISSUE;
@@ -64,7 +65,7 @@ public class IssueIndexerTest {
@Rule
public DbTester db = DbTester.create();
@Rule
public ExpectedException expectedException = ExpectedException.none();
public ExpectedException expectedException = none();
@Rule
public LogTester logTester = new LogTester();

@@ -117,7 +118,7 @@ public class IssueIndexerTest {
IssueDoc doc = es.getDocuments(INDEX_TYPE_ISSUE, IssueDoc.class).get(0);
assertThat(doc.getId()).isEqualTo(issue.getKey());
assertThat(doc.organizationUuid()).isEqualTo(organization.getUuid());
assertThat(doc.assignee()).isEqualTo(issue.getAssignee());
assertThat(doc.assigneeUuid()).isEqualTo(issue.getAssigneeUuid());
assertThat(doc.authorLogin()).isEqualTo(issue.getAuthorLogin());
assertThat(doc.componentUuid()).isEqualTo(file.uuid());
assertThat(doc.projectUuid()).isEqualTo(project.uuid());

+ 5
- 5
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIteratorFactoryTest.java View File

@@ -46,7 +46,7 @@ public class IssueIteratorFactoryTest {
assertThat(issue.resolution()).isEqualTo("FIXED");
assertThat(issue.status()).isEqualTo("RESOLVED");
assertThat(issue.severity()).isEqualTo("BLOCKER");
assertThat(issue.assignee()).isEqualTo("guy1");
assertThat(issue.assigneeUuid()).isEqualTo("uuid-of-guy1");
assertThat(issue.authorLogin()).isEqualTo("guy2");
assertThat(issue.line()).isEqualTo(444);
assertThat(issue.ruleId()).isEqualTo(200);
@@ -70,7 +70,7 @@ public class IssueIteratorFactoryTest {

IssueDoc issue = issuesByKey.get("ABC");
assertThat(issue.key()).isEqualTo("ABC");
assertThat(issue.assignee()).isEqualTo("guy1");
assertThat(issue.assigneeUuid()).isEqualTo("uuid-of-guy1");
assertThat(issue.componentUuid()).isEqualTo("FILE1");
assertThat(issue.projectUuid()).isEqualTo("PROJECT1");
assertThat(issue.moduleUuid()).isEqualTo("PROJECT1");
@@ -83,7 +83,7 @@ public class IssueIteratorFactoryTest {

issue = issuesByKey.get("BCD");
assertThat(issue.key()).isEqualTo("BCD");
assertThat(issue.assignee()).isEqualTo("guy1");
assertThat(issue.assigneeUuid()).isEqualTo("uuid-of-guy1");
assertThat(issue.componentUuid()).isEqualTo("MODULE1");
assertThat(issue.projectUuid()).isEqualTo("PROJECT1");
assertThat(issue.moduleUuid()).isEqualTo("MODULE1");
@@ -96,7 +96,7 @@ public class IssueIteratorFactoryTest {

issue = issuesByKey.get("DEF");
assertThat(issue.key()).isEqualTo("DEF");
assertThat(issue.assignee()).isEqualTo("guy2");
assertThat(issue.assigneeUuid()).isEqualTo("uuid-of-guy2");
assertThat(issue.componentUuid()).isEqualTo("FILE1");
assertThat(issue.projectUuid()).isEqualTo("PROJECT1");
assertThat(issue.moduleUuid()).isEqualTo("PROJECT1");
@@ -109,7 +109,7 @@ public class IssueIteratorFactoryTest {

issue = issuesByKey.get("EFG");
assertThat(issue.key()).isEqualTo("EFG");
assertThat(issue.assignee()).isEqualTo("guy1");
assertThat(issue.assigneeUuid()).isEqualTo("uuid-of-guy1");
assertThat(issue.componentUuid()).isEqualTo("DIR1");
assertThat(issue.projectUuid()).isEqualTo("PROJECT1");
assertThat(issue.moduleUuid()).isEqualTo("MODULE1");

+ 10
- 5
server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangeNotificationTest.java View File

@@ -23,8 +23,10 @@ import org.junit.Test;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.FieldDiffs;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.user.UserDto;

import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.db.user.UserTesting.newUserDto;

public class IssueChangeNotificationTest {

@@ -32,27 +34,30 @@ public class IssueChangeNotificationTest {

@Test
public void set_issue() {

UserDto assignee = newUserDto();

DefaultIssue issue = new DefaultIssue()
.setKey("ABCD")
.setAssignee("simon")
.setAssigneeUuid(assignee.getUuid())
.setMessage("Remove this useless method")
.setComponentKey("MyService")
.setCurrentChange(new FieldDiffs().setDiff("resolution", "FALSE-POSITIVE", "FIXED"));

IssueChangeNotification result = notification.setIssue(issue);
IssueChangeNotification result = notification.setIssue(issue).setAssignee(assignee);

assertThat(result.getFieldValue("key")).isEqualTo("ABCD");
assertThat(result.getFieldValue("assignee")).isEqualTo("simon");
assertThat(result.getFieldValue("message")).isEqualTo("Remove this useless method");
assertThat(result.getFieldValue("old.resolution")).isEqualTo("FALSE-POSITIVE");
assertThat(result.getFieldValue("new.resolution")).isEqualTo("FIXED");
assertThat(result.getFieldValue("assignee")).isEqualTo(assignee.getLogin());
}

@Test
public void set_issue_with_current_change_having_no_old_value() {
DefaultIssue issue = new DefaultIssue()
.setKey("ABCD")
.setAssignee("simon")
.setAssigneeUuid("simon")
.setMessage("Remove this useless method")
.setComponentKey("MyService");

@@ -69,7 +74,7 @@ public class IssueChangeNotificationTest {
public void set_issue_with_current_change_having_no_new_value() {
DefaultIssue issue = new DefaultIssue()
.setKey("ABCD")
.setAssignee("simon")
.setAssigneeUuid("simon")
.setMessage("Remove this useless method")
.setComponentKey("MyService");


+ 6
- 3
server/sonar-server/src/test/java/org/sonar/server/issue/notification/MyNewIssuesNotificationTest.java View File

@@ -22,6 +22,8 @@ package org.sonar.server.issue.notification;
import org.junit.Test;
import org.sonar.api.utils.Durations;
import org.sonar.db.DbClient;
import org.sonar.db.user.UserDto;
import org.sonar.db.user.UserTesting;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -33,14 +35,15 @@ public class MyNewIssuesNotificationTest {

@Test
public void set_assignee() {
underTest.setAssignee("myAssignee");
UserDto user = UserTesting.newUserDto();

assertThat(underTest.getFieldValue(FIELD_ASSIGNEE)).isEqualTo("myAssignee");
underTest.setAssignee(user);

assertThat(underTest.getFieldValue(FIELD_ASSIGNEE)).isEqualTo(user.getLogin());
}

@Test
public void set_with_a_specific_type() {
assertThat(underTest.getType()).isEqualTo(MyNewIssuesNotification.MY_NEW_ISSUES_NOTIF_TYPE);

}
}

+ 31
- 23
server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java View File

@@ -108,13 +108,16 @@ public class NewIssuesNotificationTest {

@Test
public void set_statistics() {
UserDto maynard = db.users().insertUser(u-> u.setLogin("maynard"));
UserDto keenan = db.users().insertUser(u-> u.setLogin("keenan"));

ComponentDto project = db.components().insertPrivateProject();
ComponentDto directory = db.components().insertComponent(newDirectory(project, "path"));
ComponentDto file = db.components().insertComponent(newFileDto(directory));
RuleDefinitionDto rule1 = db.rules().insert(r -> r.setRepositoryKey("SonarQube").setRuleKey("rule1-the-world").setName("Rule the World").setLanguage("Java"));
RuleDefinitionDto rule2 = db.rules().insert(r -> r.setRepositoryKey("SonarQube").setRuleKey("rule1-the-universe").setName("Rule the Universe").setLanguage("Clojure"));
IssueDto issue1 = db.issues().insert(rule1, project, file, i -> i.setType(BUG).setAssignee("maynard").setTags(asList("bug", "owasp")));
IssueDto issue2 = db.issues().insert(rule2, project, directory, i -> i.setType(CODE_SMELL).setAssignee("keenan").setTags(singletonList("owasp")));
IssueDto issue1 = db.issues().insert(rule1, project, file, i -> i.setType(BUG).setAssigneeUuid(maynard.getUuid()).setTags(asList("bug", "owasp")));
IssueDto issue2 = db.issues().insert(rule2, project, directory, i -> i.setType(CODE_SMELL).setAssigneeUuid(keenan.getUuid()).setTags(singletonList("owasp")));
NewIssuesStatistics.Stats stats = new NewIssuesStatistics.Stats(i -> true);
IntStream.rangeClosed(1, 5).forEach(i -> stats.add(issue1.toDefaultIssue()));
IntStream.rangeClosed(1, 3).forEach(i -> stats.add(issue2.toDefaultIssue()));
@@ -123,9 +126,9 @@ public class NewIssuesNotificationTest {

assertThat(underTest.getFieldValue(RULE_TYPE + ".BUG.count")).isEqualTo("5");
assertThat(underTest.getFieldValue(RULE_TYPE + ".CODE_SMELL.count")).isEqualTo("3");
assertThat(underTest.getFieldValue(ASSIGNEE + ".1.label")).isEqualTo("maynard");
assertThat(underTest.getFieldValue(ASSIGNEE + ".1.label")).isEqualTo(maynard.getName());
assertThat(underTest.getFieldValue(ASSIGNEE + ".1.count")).isEqualTo("5");
assertThat(underTest.getFieldValue(ASSIGNEE + ".2.label")).isEqualTo("keenan");
assertThat(underTest.getFieldValue(ASSIGNEE + ".2.label")).isEqualTo(keenan.getName());
assertThat(underTest.getFieldValue(ASSIGNEE + ".2.count")).isEqualTo("3");
assertThat(underTest.getFieldValue(TAG + ".1.label")).isEqualTo("owasp");
assertThat(underTest.getFieldValue(TAG + ".1.count")).isEqualTo("8");
@@ -148,46 +151,51 @@ public class NewIssuesNotificationTest {
ComponentDto file = db.components().insertComponent(newFileDto(project));
RuleDefinitionDto rule = db.rules().insert();
UserDto user = db.users().insertUser();
IssueDto issue1 = db.issues().insert(rule, project, file, i -> i.setAssignee(user.getLogin()));
IssueDto issue2 = db.issues().insert(rule, project, file, i -> i.setAssignee("no_user"));
IssueDto issue = db.issues().insert(rule, project, file, i -> i.setAssigneeUuid(user.getUuid()));
NewIssuesStatistics.Stats stats = new NewIssuesStatistics.Stats(i -> true);
IntStream.rangeClosed(1, 5).forEach(i -> stats.add(issue1.toDefaultIssue()));
IntStream.rangeClosed(1, 3).forEach(i -> stats.add(issue2.toDefaultIssue()));
IntStream.rangeClosed(1, 5).forEach(i -> stats.add(issue.toDefaultIssue()));

underTest.setStatistics(project.longName(), stats);

assertThat(underTest.getFieldValue(ASSIGNEE + ".1.label")).isEqualTo(user.getName());
assertThat(underTest.getFieldValue(ASSIGNEE + ".1.count")).isEqualTo("5");
assertThat(underTest.getFieldValue(ASSIGNEE + ".2.label")).isEqualTo("no_user");
assertThat(underTest.getFieldValue(ASSIGNEE + ".2.count")).isEqualTo("3");
}

@Test
public void add_only_5_assignees_with_biggest_issue_counts() {
UserDto user1 = db.users().insertUser();
UserDto user2 = db.users().insertUser();
UserDto user3 = db.users().insertUser();
UserDto user4 = db.users().insertUser();
UserDto user5 = db.users().insertUser();
UserDto user6 = db.users().insertUser();
UserDto user7 = db.users().insertUser();
UserDto user8 = db.users().insertUser();

ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
RuleDefinitionDto rule = db.rules().insert();
NewIssuesStatistics.Stats stats = new NewIssuesStatistics.Stats(i -> true);
IntStream.rangeClosed(1, 10).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssignee("assignee_1")).toDefaultIssue()));
IntStream.rangeClosed(1, 9).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssignee("assignee_2")).toDefaultIssue()));
IntStream.rangeClosed(1, 8).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssignee("assignee_3")).toDefaultIssue()));
IntStream.rangeClosed(1, 7).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssignee("assignee_4")).toDefaultIssue()));
IntStream.rangeClosed(1, 6).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssignee("assignee_5")).toDefaultIssue()));
IntStream.rangeClosed(1, 5).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssignee("assignee_6")).toDefaultIssue()));
IntStream.rangeClosed(1, 4).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssignee("assignee_7")).toDefaultIssue()));
IntStream.rangeClosed(1, 3).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssignee("assignee_8")).toDefaultIssue()));
IntStream.rangeClosed(1, 10).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssigneeUuid(user1.getUuid())).toDefaultIssue()));
IntStream.rangeClosed(1, 9).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssigneeUuid(user2.getUuid())).toDefaultIssue()));
IntStream.rangeClosed(1, 8).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssigneeUuid(user3.getUuid())).toDefaultIssue()));
IntStream.rangeClosed(1, 7).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssigneeUuid(user4.getUuid())).toDefaultIssue()));
IntStream.rangeClosed(1, 6).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssigneeUuid(user5.getUuid())).toDefaultIssue()));
IntStream.rangeClosed(1, 5).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssigneeUuid(user6.getUuid())).toDefaultIssue()));
IntStream.rangeClosed(1, 4).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssigneeUuid(user7.getUuid())).toDefaultIssue()));
IntStream.rangeClosed(1, 3).forEach(i -> stats.add(db.issues().insert(rule, project, file, issue -> issue.setAssigneeUuid(user8.getUuid())).toDefaultIssue()));

underTest.setStatistics(project.longName(), stats);

assertThat(underTest.getFieldValue(ASSIGNEE + ".1.label")).isEqualTo("assignee_1");
assertThat(underTest.getFieldValue(ASSIGNEE + ".1.label")).isEqualTo(user1.getName());
assertThat(underTest.getFieldValue(ASSIGNEE + ".1.count")).isEqualTo("10");
assertThat(underTest.getFieldValue(ASSIGNEE + ".2.label")).isEqualTo("assignee_2");
assertThat(underTest.getFieldValue(ASSIGNEE + ".2.label")).isEqualTo(user2.getName());
assertThat(underTest.getFieldValue(ASSIGNEE + ".2.count")).isEqualTo("9");
assertThat(underTest.getFieldValue(ASSIGNEE + ".3.label")).isEqualTo("assignee_3");
assertThat(underTest.getFieldValue(ASSIGNEE + ".3.label")).isEqualTo(user3.getName());
assertThat(underTest.getFieldValue(ASSIGNEE + ".3.count")).isEqualTo("8");
assertThat(underTest.getFieldValue(ASSIGNEE + ".4.label")).isEqualTo("assignee_4");
assertThat(underTest.getFieldValue(ASSIGNEE + ".4.label")).isEqualTo(user4.getName());
assertThat(underTest.getFieldValue(ASSIGNEE + ".4.count")).isEqualTo("7");
assertThat(underTest.getFieldValue(ASSIGNEE + ".5.label")).isEqualTo("assignee_5");
assertThat(underTest.getFieldValue(ASSIGNEE + ".5.label")).isEqualTo(user5.getName());
assertThat(underTest.getFieldValue(ASSIGNEE + ".5.count")).isEqualTo("6");
assertThat(underTest.getFieldValue(ASSIGNEE + ".6.label")).isNull();
assertThat(underTest.getFieldValue(ASSIGNEE + ".6.count")).isNull();

+ 22
- 22
server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesStatisticsTest.java View File

@@ -55,7 +55,7 @@ public class NewIssuesStatisticsTest {
@Test
public void add_fails_with_NPE_if_RuleType_is_null() {
String assignee = randomAlphanumeric(10);
DefaultIssue issue = new DefaultIssue().setType(null).setAssignee(assignee).setNew(new Random().nextBoolean());
DefaultIssue issue = new DefaultIssue().setType(null).setAssigneeUuid(assignee).setNew(new Random().nextBoolean());

expectedException.expect(NullPointerException.class);

@@ -65,7 +65,7 @@ public class NewIssuesStatisticsTest {
@Test
public void add_issues_with_correct_global_statistics() {
DefaultIssue issue = new DefaultIssue()
.setAssignee("maynard")
.setAssigneeUuid("maynard")
.setComponentUuid("file-uuid")
.setNew(true)
.setType(RuleType.BUG)
@@ -74,8 +74,8 @@ public class NewIssuesStatisticsTest {
.setEffort(Duration.create(5L));

underTest.add(issue);
underTest.add(issue.setAssignee("james"));
underTest.add(issue.setAssignee("keenan"));
underTest.add(issue.setAssigneeUuid("james"));
underTest.add(issue.setAssigneeUuid("keenan"));

assertThat(countDistributionTotal(Metric.ASSIGNEE, "maynard")).isEqualTo(1);
assertThat(countDistributionTotal(Metric.ASSIGNEE, "james")).isEqualTo(1);
@@ -99,7 +99,7 @@ public class NewIssuesStatisticsTest {
public void add_counts_issue_per_RuleType_on_leak_globally_and_per_assignee() {
String assignee = randomAlphanumeric(10);
Arrays.stream(RuleType.values())
.map(ruleType -> new DefaultIssue().setType(ruleType).setAssignee(assignee).setNew(true))
.map(ruleType -> new DefaultIssue().setType(ruleType).setAssigneeUuid(assignee).setNew(true))
.forEach(underTest::add);

DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.RULE_TYPE);
@@ -112,7 +112,7 @@ public class NewIssuesStatisticsTest {
public void add_counts_issue_per_RuleType_off_leak_globally_and_per_assignee() {
String assignee = randomAlphanumeric(10);
Arrays.stream(RuleType.values())
.map(ruleType -> new DefaultIssue().setType(ruleType).setAssignee(assignee).setNew(false))
.map(ruleType -> new DefaultIssue().setType(ruleType).setAssigneeUuid(assignee).setNew(false))
.forEach(underTest::add);

DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.RULE_TYPE);
@@ -126,7 +126,7 @@ public class NewIssuesStatisticsTest {
List<String> componentUuids = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).collect(Collectors.toList());
String assignee = randomAlphanumeric(10);
componentUuids.stream()
.map(componentUuid -> new DefaultIssue().setType(randomRuleType).setComponentUuid(componentUuid).setAssignee(assignee).setNew(true))
.map(componentUuid -> new DefaultIssue().setType(randomRuleType).setComponentUuid(componentUuid).setAssigneeUuid(assignee).setNew(true))
.forEach(underTest::add);

DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.COMPONENT);
@@ -140,7 +140,7 @@ public class NewIssuesStatisticsTest {
List<String> componentUuids = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).collect(Collectors.toList());
String assignee = randomAlphanumeric(10);
componentUuids.stream()
.map(componentUuid -> new DefaultIssue().setType(randomRuleType).setComponentUuid(componentUuid).setAssignee(assignee).setNew(false))
.map(componentUuid -> new DefaultIssue().setType(randomRuleType).setComponentUuid(componentUuid).setAssigneeUuid(assignee).setNew(false))
.forEach(underTest::add);

DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.COMPONENT);
@@ -153,7 +153,7 @@ public class NewIssuesStatisticsTest {
@Test
public void add_does_not_count_component_if_null_neither_globally_nor_per_assignee() {
String assignee = randomAlphanumeric(10);
underTest.add(new DefaultIssue().setType(randomRuleType).setComponentUuid(null).setAssignee(assignee).setNew(new Random().nextBoolean()));
underTest.add(new DefaultIssue().setType(randomRuleType).setComponentUuid(null).setAssigneeUuid(assignee).setNew(new Random().nextBoolean()));

DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.COMPONENT);
DistributedMetricStatsInt assigneeDistribution = underTest.getAssigneesStatistics().get(assignee).getDistributedMetricStats(Metric.COMPONENT);
@@ -170,7 +170,7 @@ public class NewIssuesStatisticsTest {
List<String> ruleKeys = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).collect(Collectors.toList());
String assignee = randomAlphanumeric(10);
ruleKeys.stream()
.map(ruleKey -> new DefaultIssue().setType(randomRuleType).setRuleKey(RuleKey.of(repository, ruleKey)).setAssignee(assignee).setNew(true))
.map(ruleKey -> new DefaultIssue().setType(randomRuleType).setRuleKey(RuleKey.of(repository, ruleKey)).setAssigneeUuid(assignee).setNew(true))
.forEach(underTest::add);

DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.RULE);
@@ -186,7 +186,7 @@ public class NewIssuesStatisticsTest {
List<String> ruleKeys = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).collect(Collectors.toList());
String assignee = randomAlphanumeric(10);
ruleKeys.stream()
.map(ruleKey -> new DefaultIssue().setType(randomRuleType).setRuleKey(RuleKey.of(repository, ruleKey)).setAssignee(assignee).setNew(false))
.map(ruleKey -> new DefaultIssue().setType(randomRuleType).setRuleKey(RuleKey.of(repository, ruleKey)).setAssigneeUuid(assignee).setNew(false))
.forEach(underTest::add);

DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.RULE);
@@ -198,7 +198,7 @@ public class NewIssuesStatisticsTest {
@Test
public void add_does_not_count_ruleKey_if_null_neither_globally_nor_per_assignee() {
String assignee = randomAlphanumeric(10);
underTest.add(new DefaultIssue().setType(randomRuleType).setRuleKey(null).setAssignee(assignee).setNew(new Random().nextBoolean()));
underTest.add(new DefaultIssue().setType(randomRuleType).setRuleKey(null).setAssigneeUuid(assignee).setNew(new Random().nextBoolean()));

DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.RULE);
DistributedMetricStatsInt assigneeDistribution = underTest.getAssigneesStatistics().get(assignee).getDistributedMetricStats(Metric.RULE);
@@ -213,7 +213,7 @@ public class NewIssuesStatisticsTest {
public void add_counts_issue_per_assignee_on_leak_globally_and_per_assignee() {
List<String> assignees = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).collect(Collectors.toList());
assignees.stream()
.map(assignee -> new DefaultIssue().setType(randomRuleType).setAssignee(assignee).setNew(true))
.map(assignee -> new DefaultIssue().setType(randomRuleType).setAssigneeUuid(assignee).setNew(true))
.forEach(underTest::add);

DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.ASSIGNEE);
@@ -243,7 +243,7 @@ public class NewIssuesStatisticsTest {
public void add_counts_issue_per_assignee_off_leak_globally_and_per_assignee() {
List<String> assignees = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).collect(Collectors.toList());
assignees.stream()
.map(assignee -> new DefaultIssue().setType(randomRuleType).setAssignee(assignee).setNew(false))
.map(assignee -> new DefaultIssue().setType(randomRuleType).setAssigneeUuid(assignee).setNew(false))
.forEach(underTest::add);

DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.ASSIGNEE);
@@ -271,7 +271,7 @@ public class NewIssuesStatisticsTest {

@Test
public void add_does_not_assignee_if_empty_neither_globally_nor_per_assignee() {
underTest.add(new DefaultIssue().setType(randomRuleType).setAssignee(null).setNew(new Random().nextBoolean()));
underTest.add(new DefaultIssue().setType(randomRuleType).setAssigneeUuid(null).setNew(new Random().nextBoolean()));

DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.ASSIGNEE);
assertThat(globalDistribution.getTotal()).isEqualTo(0);
@@ -283,7 +283,7 @@ public class NewIssuesStatisticsTest {
public void add_counts_issue_per_tags_on_leak_globally_and_per_assignee() {
List<String> tags = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).collect(Collectors.toList());
String assignee = randomAlphanumeric(10);
underTest.add(new DefaultIssue().setType(randomRuleType).setTags(tags).setAssignee(assignee).setNew(true));
underTest.add(new DefaultIssue().setType(randomRuleType).setTags(tags).setAssigneeUuid(assignee).setNew(true));

DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.TAG);
DistributedMetricStatsInt assigneeDistribution = underTest.getAssigneesStatistics().get(assignee).getDistributedMetricStats(Metric.TAG);
@@ -295,7 +295,7 @@ public class NewIssuesStatisticsTest {
public void add_counts_issue_per_tags_off_leak_globally_and_per_assignee() {
List<String> tags = IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> randomAlphabetic(3)).collect(Collectors.toList());
String assignee = randomAlphanumeric(10);
underTest.add(new DefaultIssue().setType(randomRuleType).setTags(tags).setAssignee(assignee).setNew(false));
underTest.add(new DefaultIssue().setType(randomRuleType).setTags(tags).setAssigneeUuid(assignee).setNew(false));

DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.TAG);
DistributedMetricStatsInt assigneeDistribution = underTest.getAssigneesStatistics().get(assignee).getDistributedMetricStats(Metric.TAG);
@@ -306,7 +306,7 @@ public class NewIssuesStatisticsTest {
@Test
public void add_does_not_count_tags_if_empty_neither_globally_nor_per_assignee() {
String assignee = randomAlphanumeric(10);
underTest.add(new DefaultIssue().setType(randomRuleType).setTags(Collections.emptyList()).setAssignee(assignee).setNew(new Random().nextBoolean()));
underTest.add(new DefaultIssue().setType(randomRuleType).setTags(Collections.emptyList()).setAssigneeUuid(assignee).setNew(new Random().nextBoolean()));

DistributedMetricStatsInt globalDistribution = underTest.globalStatistics().getDistributedMetricStats(Metric.TAG);
DistributedMetricStatsInt assigneeDistribution = underTest.getAssigneesStatistics().get(assignee).getDistributedMetricStats(Metric.TAG);
@@ -324,7 +324,7 @@ public class NewIssuesStatisticsTest {
int expected = efforts.stream().mapToInt(s -> s).sum();
String assignee = randomAlphanumeric(10);
efforts.stream()
.map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort)).setAssignee(assignee).setNew(true))
.map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort)).setAssigneeUuid(assignee).setNew(true))
.forEach(underTest::add);

MetricStatsLong globalDistribution = underTest.globalStatistics().effort();
@@ -344,7 +344,7 @@ public class NewIssuesStatisticsTest {
int expected = efforts.stream().mapToInt(s -> s).sum();
String assignee = randomAlphanumeric(10);
efforts.stream()
.map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort)).setAssignee(assignee).setNew(false))
.map(effort -> new DefaultIssue().setType(randomRuleType).setEffort(Duration.create(effort)).setAssigneeUuid(assignee).setNew(false))
.forEach(underTest::add);

MetricStatsLong globalDistribution = underTest.globalStatistics().effort();
@@ -360,7 +360,7 @@ public class NewIssuesStatisticsTest {
@Test
public void add_does_not_sum_effort_if_null_neither_globally_nor_per_assignee() {
String assignee = randomAlphanumeric(10);
underTest.add(new DefaultIssue().setType(randomRuleType).setEffort(null).setAssignee(assignee).setNew(new Random().nextBoolean()));
underTest.add(new DefaultIssue().setType(randomRuleType).setEffort(null).setAssigneeUuid(assignee).setNew(new Random().nextBoolean()));

MetricStatsLong globalDistribution = underTest.globalStatistics().effort();
MetricStatsLong assigneeDistribution = underTest.getAssigneesStatistics().get(assignee).effort();
@@ -384,7 +384,7 @@ public class NewIssuesStatisticsTest {
.setType(randomRuleType)
.setComponentUuid(componentUuid)
.setTags(ImmutableSet.of(tag))
.setAssignee(assignee)
.setAssigneeUuid(assignee)
.setRuleKey(ruleKey)
.setEffort(Duration.create(effort)));


+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java View File

@@ -225,7 +225,7 @@ public class IssueWorkflowTest {
.setKey("ABCDE")
.setStatus(STATUS_OPEN)
.setRuleKey(RuleKey.of("squid", "AvoidCycle"))
.setAssignee("morgan");
.setAssigneeUuid("morgan");

workflow.start();
workflow.doTransition(issue, DefaultTransitions.FALSE_POSITIVE, IssueChangeContext.createScan(new Date()));
@@ -243,7 +243,7 @@ public class IssueWorkflowTest {
.setKey("ABCDE")
.setStatus(STATUS_OPEN)
.setRuleKey(RuleKey.of("squid", "AvoidCycle"))
.setAssignee("morgan");
.setAssigneeUuid("morgan");

workflow.start();
workflow.doTransition(issue, DefaultTransitions.WONT_FIX, IssueChangeContext.createScan(new Date()));

+ 50
- 57
server/sonar-server/src/test/java/org/sonar/server/issue/ws/AssignActionTest.java View File

@@ -19,19 +19,17 @@
*/
package org.sonar.server.issue.ws;

import java.util.Optional;
import javax.annotation.Nullable;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.utils.internal.TestSystem2;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.ForbiddenException;
@@ -52,17 +50,17 @@ import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.WsActionTester;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.junit.rules.ExpectedException.none;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.sonar.api.web.UserRole.CODEVIEWER;
import static org.sonar.api.web.UserRole.USER;
import static org.sonar.server.tester.UserSessionRule.standalone;

public class AssignActionTest {

private static final String PREVIOUS_ASSIGNEE = "previous";
private static final String CURRENT_USER_LOGIN = "john";
private static final String CURRENT_USER_UUID = "1";

private static final long PAST = 10_000_000_000L;
private static final long NOW = 50_000_000_000L;
@@ -70,39 +68,41 @@ public class AssignActionTest {
private TestSystem2 system2 = new TestSystem2().setNow(NOW);

@Rule
public ExpectedException expectedException = ExpectedException.none();
public ExpectedException expectedException = none();
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
public UserSessionRule userSession = standalone();
@Rule
public EsTester es = EsTester.create();
@Rule
public DbTester db = DbTester.create(system2);
public DbClient dbClient = db.getDbClient();
private DbSession session = db.getSession();

private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()));
private IssueIndexer issueIndexer = new IssueIndexer(es.client(), dbClient, new IssueIteratorFactory(dbClient));
private OperationResponseWriter responseWriter = mock(OperationResponseWriter.class);
private TestIssueChangePostProcessor issueChangePostProcessor = new TestIssueChangePostProcessor();
private AssignAction underTest = new AssignAction(system2, userSession, db.getDbClient(), new IssueFinder(db.getDbClient(), userSession), new IssueFieldsSetter(),
new IssueUpdater(db.getDbClient(),
new ServerIssueStorage(system2, new DefaultRuleFinder(db.getDbClient(), defaultOrganizationProvider), db.getDbClient(), issueIndexer),
private AssignAction underTest = new AssignAction(system2, userSession, dbClient, new IssueFinder(dbClient, userSession), new IssueFieldsSetter(),
new IssueUpdater(dbClient,
new ServerIssueStorage(system2, new DefaultRuleFinder(dbClient, defaultOrganizationProvider), dbClient, issueIndexer),
mock(NotificationManager.class), issueChangePostProcessor),
responseWriter);
private ArgumentCaptor<SearchResponseData> preloadedSearchResponseDataCaptor = ArgumentCaptor.forClass(SearchResponseData.class);
private WsActionTester ws = new WsActionTester(underTest);

@Test
public void assign_to_someone() {
IssueDto issue = newIssueWithBrowsePermission();
insertUser("arthur");
UserDto arthur = insertUser("arthur");

ws.newRequest()
.setParam("issue", issue.getKey())
.setParam("assignee", "arthur")
.execute();

checkIssueAssignee(issue.getKey(), "arthur");
verify(responseWriter).write(eq(issue.getKey()), preloadedSearchResponseDataCaptor.capture(), any(Request.class), any(Response.class));
verifyContentOfPreloadedSearchResponseData(issue);
checkIssueAssignee(issue.getKey(), arthur.getUuid());
Optional<IssueDto> optionalIssueDto = dbClient.issueDao().selectByKey(session, issue.getKey());
assertThat(optionalIssueDto).isPresent();
assertThat(optionalIssueDto.get().getAssigneeUuid()).isEqualTo(arthur.getUuid());
assertThat(issueChangePostProcessor.wasCalled()).isFalse();
}

@@ -115,9 +115,10 @@ public class AssignActionTest {
.setParam("assignee", "_me")
.execute();

checkIssueAssignee(issue.getKey(), CURRENT_USER_LOGIN);
verify(responseWriter).write(eq(issue.getKey()), preloadedSearchResponseDataCaptor.capture(), any(Request.class), any(Response.class));
verifyContentOfPreloadedSearchResponseData(issue);
checkIssueAssignee(issue.getKey(), CURRENT_USER_UUID);
Optional<IssueDto> optionalIssueDto = dbClient.issueDao().selectByKey(session, issue.getKey());
assertThat(optionalIssueDto).isPresent();
assertThat(optionalIssueDto.get().getAssigneeUuid()).isEqualTo(CURRENT_USER_UUID);
assertThat(issueChangePostProcessor.wasCalled()).isFalse();
}

@@ -130,9 +131,10 @@ public class AssignActionTest {
.setParam("me", "true")
.execute();

checkIssueAssignee(issue.getKey(), CURRENT_USER_LOGIN);
verify(responseWriter).write(eq(issue.getKey()), preloadedSearchResponseDataCaptor.capture(), any(Request.class), any(Response.class));
verifyContentOfPreloadedSearchResponseData(issue);
checkIssueAssignee(issue.getKey(), CURRENT_USER_UUID);
Optional<IssueDto> optionalIssueDto = dbClient.issueDao().selectByKey(session, issue.getKey());
assertThat(optionalIssueDto).isPresent();
assertThat(optionalIssueDto.get().getAssigneeUuid()).isEqualTo(CURRENT_USER_UUID);
}

@Test
@@ -144,8 +146,9 @@ public class AssignActionTest {
.execute();

checkIssueAssignee(issue.getKey(), null);
verify(responseWriter).write(eq(issue.getKey()), preloadedSearchResponseDataCaptor.capture(), any(Request.class), any(Response.class));
verifyContentOfPreloadedSearchResponseData(issue);
Optional<IssueDto> optionalIssueDto = dbClient.issueDao().selectByKey(session, issue.getKey());
assertThat(optionalIssueDto).isPresent();
assertThat(optionalIssueDto.get().getAssigneeUuid()).isNull();
assertThat(issueChangePostProcessor.wasCalled()).isFalse();
}

@@ -159,22 +162,25 @@ public class AssignActionTest {
.execute();

checkIssueAssignee(issue.getKey(), null);
verify(responseWriter).write(eq(issue.getKey()), preloadedSearchResponseDataCaptor.capture(), any(Request.class), any(Response.class));
verifyContentOfPreloadedSearchResponseData(issue);
Optional<IssueDto> optionalIssueDto = dbClient.issueDao().selectByKey(session, issue.getKey());
assertThat(optionalIssueDto).isPresent();
assertThat(optionalIssueDto.get().getAssigneeUuid()).isNull();
assertThat(issueChangePostProcessor.wasCalled()).isFalse();
}

@Test
public void nothing_to_do_when_new_assignee_is_same_as_old_one() {
IssueDto issue = newIssueWithBrowsePermission();
insertUser(PREVIOUS_ASSIGNEE);
UserDto user = insertUser("Bob");
IssueDto issue = newIssue(user.getUuid());
setUserWithBrowsePermission(issue);

ws.newRequest()
.setParam("issue", issue.getKey())
.setParam("assignee", PREVIOUS_ASSIGNEE)
.setParam("assignee", user.getLogin())
.execute();

IssueDto issueReloaded = db.getDbClient().issueDao().selectByKey(db.getSession(), issue.getKey()).get();
assertThat(issueReloaded.getAssignee()).isEqualTo(PREVIOUS_ASSIGNEE);
IssueDto issueReloaded = dbClient.issueDao().selectByKey(db.getSession(), issue.getKey()).get();
assertThat(issueReloaded.getAssigneeUuid()).isEqualTo(user.getUuid());
assertThat(issueReloaded.getUpdatedAt()).isEqualTo(PAST);
assertThat(issueReloaded.getIssueUpdateTime()).isEqualTo(PAST);
}
@@ -206,7 +212,7 @@ public class AssignActionTest {

@Test
public void fail_when_not_authenticated() {
IssueDto issue = newIssue();
IssueDto issue = newIssue(PREVIOUS_ASSIGNEE);
userSession.anonymous();

expectedException.expect(UnauthorizedException.class);
@@ -219,7 +225,7 @@ public class AssignActionTest {

@Test
public void fail_when_missing_browse_permission() {
IssueDto issue = newIssue();
IssueDto issue = newIssue(PREVIOUS_ASSIGNEE);
setUserWithPermission(issue, CODEVIEWER);

expectedException.expect(ForbiddenException.class);
@@ -248,36 +254,23 @@ public class AssignActionTest {
.execute();
}

private void verifyContentOfPreloadedSearchResponseData(IssueDto issue) {
SearchResponseData preloadedSearchResponseData = preloadedSearchResponseDataCaptor.getValue();
assertThat(preloadedSearchResponseData.getIssues())
.extracting(IssueDto::getKey)
.containsOnly(issue.getKey());
assertThat(preloadedSearchResponseData.getRules())
.extracting(RuleDefinitionDto::getKey)
.containsOnly(issue.getRuleKey());
assertThat(preloadedSearchResponseData.getComponents())
.extracting(ComponentDto::uuid)
.containsOnly(issue.getComponentUuid(), issue.getProjectUuid());
}

private UserDto insertUser(String login) {
UserDto user = db.users().insertUser(login);
db.organizations().addMember(db.getDefaultOrganization(), user);
return user;
}

private IssueDto newIssue() {
IssueDto issue = db.issues().insertIssue(
private IssueDto newIssue(String assignee) {
IssueDto issue = db.issues().insertIssue(
issueDto -> issueDto
.setAssignee(PREVIOUS_ASSIGNEE)
.setAssigneeUuid(assignee)
.setCreatedAt(PAST).setIssueCreationTime(PAST)
.setUpdatedAt(PAST).setIssueUpdateTime(PAST));
return issue;
}

private IssueDto newIssueWithBrowsePermission() {
IssueDto issue = newIssue();
IssueDto issue = newIssue(PREVIOUS_ASSIGNEE);
setUserWithBrowsePermission(issue);
return issue;
}
@@ -290,13 +283,13 @@ public class AssignActionTest {
insertUser(CURRENT_USER_LOGIN);
userSession.logIn(CURRENT_USER_LOGIN)
.addProjectPermission(permission,
db.getDbClient().componentDao().selectByUuid(db.getSession(), issue.getProjectUuid()).get(),
db.getDbClient().componentDao().selectByUuid(db.getSession(), issue.getComponentUuid()).get());
dbClient.componentDao().selectByUuid(db.getSession(), issue.getProjectUuid()).get(),
dbClient.componentDao().selectByUuid(db.getSession(), issue.getComponentUuid()).get());
}

private void checkIssueAssignee(String issueKey, @Nullable String expectedAssignee) {
IssueDto issueReloaded = db.getDbClient().issueDao().selectByKey(db.getSession(), issueKey).get();
assertThat(issueReloaded.getAssignee()).isEqualTo(expectedAssignee);
IssueDto issueReloaded = dbClient.issueDao().selectByKey(db.getSession(), issueKey).get();
assertThat(issueReloaded.getAssigneeUuid()).isEqualTo(expectedAssignee);
assertThat(issueReloaded.getIssueUpdateTime()).isEqualTo(NOW);
assertThat(issueReloaded.getUpdatedAt()).isEqualTo(NOW);
}

+ 9
- 13
server/sonar-server/src/test/java/org/sonar/server/issue/ws/BulkChangeActionTest.java View File

@@ -35,7 +35,6 @@ import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.IssueChangeDto;
import org.sonar.db.issue.IssueDbTester;
@@ -46,7 +45,6 @@ import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.rule.RuleDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.issue.Action;
import org.sonar.server.issue.IssueFieldsSetter;
@@ -201,7 +199,7 @@ public class BulkChangeActionTest {
@Test
public void remove_assignee() {
setUserProjectPermissions(USER);
IssueDto issueDto = db.issues().insertIssue(newUnresolvedIssue().setAssignee("arthur"));
IssueDto issueDto = db.issues().insertIssue(newUnresolvedIssue().setAssigneeUuid("arthur"));

BulkChangeWsResponse response = call(builder()
.setIssues(singletonList(issueDto.getKey()))
@@ -210,7 +208,7 @@ public class BulkChangeActionTest {

checkResponse(response, 1, 1, 0, 0);
IssueDto reloaded = getIssueByKeys(issueDto.getKey()).get(0);
assertThat(reloaded.getAssignee()).isNull();
assertThat(reloaded.getAssigneeUuid()).isNull();
assertThat(reloaded.getUpdatedAt()).isEqualTo(NOW);

// no need to refresh measures
@@ -242,9 +240,9 @@ public class BulkChangeActionTest {
UserDto userToAssign = db.users().insertUser("arthur");
db.organizations().addMember(organization, user);
db.organizations().addMember(organization, userToAssign);
IssueDto issue1 = db.issues().insertIssue(newUnresolvedIssue().setAssignee(user.getLogin())).setType(BUG).setSeverity(MINOR);
IssueDto issue2 = db.issues().insertIssue(newUnresolvedIssue().setAssignee(userToAssign.getLogin())).setType(BUG).setSeverity(MAJOR);
IssueDto issue3 = db.issues().insertIssue(newUnresolvedIssue().setAssignee(null)).setType(VULNERABILITY).setSeverity(MAJOR);
IssueDto issue1 = db.issues().insertIssue(newUnresolvedIssue().setAssigneeUuid(user.getUuid())).setType(BUG).setSeverity(MINOR);
IssueDto issue2 = db.issues().insertIssue(newUnresolvedIssue().setAssigneeUuid(userToAssign.getLogin())).setType(BUG).setSeverity(MAJOR);
IssueDto issue3 = db.issues().insertIssue(newUnresolvedIssue().setAssigneeUuid(null)).setType(VULNERABILITY).setSeverity(MAJOR);

BulkChangeWsResponse response = call(builder()
.setIssues(asList(issue1.getKey(), issue2.getKey(), issue3.getKey()))
@@ -255,11 +253,11 @@ public class BulkChangeActionTest {

checkResponse(response, 3, 3, 0, 0);
assertThat(getIssueByKeys(issue1.getKey(), issue2.getKey(), issue3.getKey()))
.extracting(IssueDto::getKey, IssueDto::getAssignee, IssueDto::getType, IssueDto::getSeverity, IssueDto::getUpdatedAt)
.extracting(IssueDto::getKey, IssueDto::getAssigneeUuid, IssueDto::getType, IssueDto::getSeverity, IssueDto::getUpdatedAt)
.containsOnly(
tuple(issue1.getKey(), userToAssign.getLogin(), VULNERABILITY.getDbConstant(), MINOR, NOW),
tuple(issue2.getKey(), userToAssign.getLogin(), VULNERABILITY.getDbConstant(), MINOR, NOW),
tuple(issue3.getKey(), userToAssign.getLogin(), VULNERABILITY.getDbConstant(), MINOR, NOW));
tuple(issue1.getKey(), userToAssign.getUuid(), VULNERABILITY.getDbConstant(), MINOR, NOW),
tuple(issue2.getKey(), userToAssign.getUuid(), VULNERABILITY.getDbConstant(), MINOR, NOW),
tuple(issue3.getKey(), userToAssign.getUuid(), VULNERABILITY.getDbConstant(), MINOR, NOW));

verifyPostProcessorCalled(file);
}
@@ -524,7 +522,6 @@ public class BulkChangeActionTest {
.setResolution(null)
.setRuleId(rule.getId())
.setRuleKey(rule.getRuleKey(), rule.getRepositoryKey())
.setAssignee(user.getLogin())
.setType(BUG)
.setSeverity(MINOR));

@@ -533,7 +530,6 @@ public class BulkChangeActionTest {
.setResolution(null)
.setRuleId(rule.getId())
.setRuleKey(rule.getRuleKey(), rule.getRepositoryKey())
.setAssignee(user.getLogin())
.setType(BUG)
.setSeverity(MAJOR));


+ 167
- 89
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java View File

@@ -24,6 +24,7 @@ import com.google.gson.JsonParser;
import java.time.Clock;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -74,8 +75,14 @@ import org.sonarqube.ws.Issues;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.groups.Tuple.tuple;
import static org.junit.rules.ExpectedException.none;
import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
import static org.sonar.api.utils.DateUtils.parseDate;
import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.issue.IssueTesting.newDto;
import static org.sonar.server.tester.UserSessionRule.standalone;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BRANCH;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.DEPRECATED_FACET_MODE_DEBT;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.FACET_MODE_EFFORT;
@@ -91,13 +98,13 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RULES;
public class SearchActionTest {

@Rule
public UserSessionRule userSessionRule = UserSessionRule.standalone();
public UserSessionRule userSessionRule = standalone();
@Rule
public DbTester db = DbTester.create();
@Rule
public EsTester es = EsTester.create();
@Rule
public ExpectedException expectedException = ExpectedException.none();
public ExpectedException expectedException = none();

private DbClient dbClient = db.getDbClient();
private DbSession session = db.getSession();
@@ -111,10 +118,11 @@ public class SearchActionTest {
private SearchResponseFormat searchResponseFormat = new SearchResponseFormat(new Durations(), new WsResponseCommonFormat(languages), languages, new AvatarResolverImpl());
private WsActionTester ws = new WsActionTester(new SearchAction(userSessionRule, issueIndex, issueQueryFactory, searchResponseLoader, searchResponseFormat, System2.INSTANCE,
dbClient));
private StartupIndexer permissionIndexer = new PermissionIndexer(dbClient, es.client(), issueIndexer);

private OrganizationDto defaultOrganization;
private OrganizationDto otherOrganization1;
private OrganizationDto otherOrganization2;
private StartupIndexer permissionIndexer = new PermissionIndexer(dbClient, es.client(), issueIndexer);

@Before
public void setUp() {
@@ -183,23 +191,23 @@ public class SearchActionTest {

@Test
public void response_contains_all_fields_except_additional_fields() {
db.users().insertUser(u -> u.setLogin("simon").setName("Simon").setEmail("simon@email.com"));
db.users().insertUser(u -> u.setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));
UserDto simon = db.users().insertUser(u -> u.setLogin("simon").setName("Simon").setEmail("simon@email.com"));
UserDto fabrice = db.users().insertUser(u -> u.setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));

ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization2, "PROJECT_ID").setDbKey("PROJECT_KEY"));
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
IssueDto issue = IssueTesting.newDto(newExternalRule(), file, project)
IssueDto issue = newDto(newExternalRule(), file, project)
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
.setEffort(10L)
.setLine(42)
.setChecksum("a227e508d6646b55a086ee11d63b21e9")
.setMessage("the message")
.setStatus(Issue.STATUS_RESOLVED)
.setResolution(Issue.RESOLUTION_FIXED)
.setStatus(STATUS_RESOLVED)
.setResolution(RESOLUTION_FIXED)
.setSeverity("MAJOR")
.setAuthorLogin("John")
.setAssignee("simon")
.setAssigneeUuid(simon.getUuid())
.setTags(asList("bug", "owasp"))
.setIssueCreationDate(DateUtils.parseDateTime("2014-09-04T00:00:00+0100"))
.setIssueUpdateDate(DateUtils.parseDateTime("2017-12-04T00:00:00+0100"));
@@ -250,17 +258,17 @@ public class SearchActionTest {
.setEndOffset(12)
.build())
.build())));
IssueDto issue = IssueTesting.newDto(newRule(), file, project)
IssueDto issue = newDto(newRule(), file, project)
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
.setEffort(10L)
.setLine(42)
.setChecksum("a227e508d6646b55a086ee11d63b21e9")
.setMessage("the message")
.setStatus(Issue.STATUS_RESOLVED)
.setResolution(Issue.RESOLUTION_FIXED)
.setStatus(STATUS_RESOLVED)
.setResolution(RESOLUTION_FIXED)
.setSeverity("MAJOR")
.setAuthorLogin(fabrice.getLogin())
.setAssignee(simon.getLogin())
.setAssigneeUuid(simon.getUuid())
.setTags(asList("bug", "owasp"))
.setLocations(locations.build())
.setIssueCreationDate(DateUtils.parseDateTime("2014-09-04T00:00:00+0100"))
@@ -279,14 +287,15 @@ public class SearchActionTest {
}

@Test
@Ignore // TODO GJT when adressing ticket on IssueChangesUuid
public void issue_with_comments() {
db.users().insertUser(u -> u.setLogin("john").setName("John"));
db.users().insertUser(u -> u.setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));
UserDto john = db.users().insertUser(u -> u.setLogin("john").setName("John"));
UserDto fabrice = db.users().insertUser(u -> u.setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));

ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization2, "PROJECT_ID").setDbKey("PROJECT_KEY"));
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project)
IssueDto issue = newDto(newRule(), file, project)
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2");
dbClient.issueDao().insert(session, issue);

@@ -295,19 +304,19 @@ public class SearchActionTest {
.setKey("COMMENT-ABCD")
.setChangeData("*My comment*")
.setChangeType(IssueChangeDto.TYPE_COMMENT)
.setUserLogin("john")
.setUserLogin(john.getUuid())
.setIssueChangeCreationDate(DateUtils.parseDateTime("2014-09-09T12:00:00+0000").getTime()));
dbClient.issueChangeDao().insert(session,
new IssueChangeDto().setIssueKey(issue.getKey())
.setKey("COMMENT-ABCE")
.setChangeData("Another comment")
.setChangeType(IssueChangeDto.TYPE_COMMENT)
.setUserLogin("fabrice")
.setUserLogin(fabrice.getUuid())
.setIssueChangeCreationDate(DateUtils.parseDateTime("2014-09-10T12:00:00+0000").getTime()));
session.commit();
indexIssues();

userSessionRule.logIn("john");
userSessionRule.logIn(john);
ws.newRequest()
.setParam("additionalFields", "comments,users")
.execute()
@@ -322,7 +331,7 @@ public class SearchActionTest {
ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization1, "PROJECT_ID").setDbKey("PROJECT_KEY"));
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project)
IssueDto issue = newDto(newRule(), file, project)
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2");
dbClient.issueDao().insert(session, issue);

@@ -351,17 +360,15 @@ public class SearchActionTest {

@Test
public void load_additional_fields() {
db.users().insertUser(u -> u.setLogin("simon").setName("Simon").setEmail("simon@email.com"));
db.users().insertUser(u -> u.setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));
UserDto simon = db.users().insertUser(u -> u.setLogin("simon").setName("Simon").setEmail("simon@email.com"));

ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization2, "PROJECT_ID").setDbKey("PROJECT_KEY").setLanguage("java"));
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY").setLanguage("js"));

IssueDto issue = IssueTesting.newDto(newRule(), file, project)
IssueDto issue = newDto(newRule(), file, project)
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
.setAuthorLogin("John")
.setAssignee("simon");
.setAssigneeUuid(simon.getUuid());
dbClient.issueDao().insert(session, issue);
session.commit();
indexIssues();
@@ -374,17 +381,18 @@ public class SearchActionTest {

@Test
public void load_additional_fields_with_issue_admin_permission() {
db.users().insertUser(u -> u.setLogin("simon").setName("Simon").setEmail("simon@email.com"));
db.users().insertUser(u -> u.setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));
UserDto simon = db.users().insertUser(u -> u.setLogin("simon").setName("Simon").setEmail("simon@email.com"));
UserDto fabrice = db.users().insertUser(u -> u.setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));

ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization1, "PROJECT_ID").setDbKey("PROJECT_KEY").setLanguage("java"));
grantPermissionToAnyone(project, ISSUE_ADMIN);
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY").setLanguage("js"));

IssueDto issue = IssueTesting.newDto(newRule(), file, project)
IssueDto issue = newDto(newRule(), file, project)
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
.setAuthorLogin("John")
.setAssignee("simon");
.setAuthorLogin(fabrice.getLogin())
.setAssigneeUuid(simon.getUuid());
dbClient.issueDao().insert(session, issue);
session.commit();
indexIssues();
@@ -427,7 +435,7 @@ public class SearchActionTest {
.setDbKey("REMOVED_FILE_KEY")
.setEnabled(false));

IssueDto issue = IssueTesting.newDto(rule, removedFile, project)
IssueDto issue = newDto(rule, removedFile, project)
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
.setComponent(removedFile)
.setStatus("OPEN").setResolution("OPEN")
@@ -450,7 +458,7 @@ public class SearchActionTest {
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
for (int i = 0; i < SearchOptions.MAX_LIMIT + 1; i++) {
IssueDto issue = IssueTesting.newDto(rule, file, project);
IssueDto issue = newDto(rule, file, project).setAssigneeUuid(null);
dbClient.issueDao().insert(session, issue);
}
session.commit();
@@ -466,7 +474,7 @@ public class SearchActionTest {
indexPermissions();
ComponentDto module = insertComponent(ComponentTesting.newModuleDto(project).setDbKey("ModuleHavingFile"));
ComponentDto file = insertComponent(newFileDto(module, null, "BCDE").setDbKey("FileLinkedToModule"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project);
IssueDto issue = newDto(newRule(), file, project);
dbClient.issueDao().insert(session, issue);
session.commit();
indexIssues();
@@ -480,9 +488,9 @@ public class SearchActionTest {
ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization1, "PROJECT_ID").setDbKey("PROJECT_KEY"));
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
.setIssueUpdateDate(DateUtils.parseDate("2017-12-04"))
IssueDto issue = newDto(newRule(), file, project)
.setIssueCreationDate(parseDate("2014-09-04"))
.setIssueUpdateDate(parseDate("2017-12-04"))
.setEffort(10L)
.setStatus("OPEN")
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
@@ -502,12 +510,14 @@ public class SearchActionTest {

@Test
public void display_facets_in_effort_mode() {
UserDto john = db.users().insertUser(u -> u.setLogin("john").setName("John").setEmail("john@email.com"));

ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization2, "PROJECT_ID").setDbKey("PROJECT_KEY"));
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
.setIssueUpdateDate(DateUtils.parseDate("2017-12-04"))
IssueDto issue = newDto(newRule(), file, project)
.setIssueCreationDate(parseDate("2014-09-04"))
.setIssueUpdateDate(parseDate("2017-12-04"))
.setEffort(10L)
.setStatus("OPEN")
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
@@ -516,7 +526,7 @@ public class SearchActionTest {
session.commit();
indexIssues();

userSessionRule.logIn("john");
userSessionRule.logIn(john);
ws.newRequest()
.setParam("resolved", "false")
.setParam(PARAM_COMPONENT_KEYS, project.getKey())
@@ -528,12 +538,15 @@ public class SearchActionTest {

@Test
public void display_zero_valued_facets_for_selected_items() {
UserDto john = db.users().insertUser(u -> u.setLogin("john").setName("John").setEmail("john@email.com"));


ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization1, "PROJECT_ID").setDbKey("PROJECT_KEY"));
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
.setIssueUpdateDate(DateUtils.parseDate("2017-12-04"))
IssueDto issue = newDto(newRule(), file, project)
.setIssueCreationDate(parseDate("2014-09-04"))
.setIssueUpdateDate(parseDate("2017-12-04"))
.setEffort(10L)
.setStatus("OPEN")
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
@@ -542,7 +555,7 @@ public class SearchActionTest {
session.commit();
indexIssues();

userSessionRule.logIn("john");
userSessionRule.logIn(john);
ws.newRequest()
.setParam(PARAM_COMPONENT_KEYS, project.getKey())
.setParam("resolved", "false")
@@ -556,7 +569,9 @@ public class SearchActionTest {
@Test
public void assignedToMe_facet_must_escape_login_of_authenticated_user() {
// login looks like an invalid regexp
userSessionRule.logIn("foo[");
UserDto user = db.users().insertUser(u -> u.setLogin("foo[").setName("foo").setEmail("foo@email.com"));

userSessionRule.logIn(user);

// should not fail
ws.newRequest()
@@ -568,40 +583,44 @@ public class SearchActionTest {

@Test
public void filter_by_assigned_to_me() {
db.users().insertUser(u -> u.setLogin("john").setName("John").setEmail("john@email.com"));
UserDto john = db.users().insertUser(u -> u.setLogin("john").setName("John").setEmail("john@email.com"));
UserDto alice = db.users().insertUser(u -> u.setLogin("alice").setName("Alice").setEmail("alice@email.com"));


ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(defaultOrganization, "PROJECT_ID").setDbKey("PROJECT_KEY"));
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
RuleDto rule = newRule();
IssueDto issue1 = IssueTesting.newDto(rule, file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
.setIssueUpdateDate(DateUtils.parseDate("2017-12-04"))
IssueDto issue1 = newDto(rule, file, project)
.setIssueCreationDate(parseDate("2014-09-04"))
.setIssueUpdateDate(parseDate("2017-12-04"))
.setEffort(10L)
.setStatus("OPEN")
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
.setSeverity("MAJOR")
.setAssignee("john");
IssueDto issue2 = IssueTesting.newDto(rule, file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
.setIssueUpdateDate(DateUtils.parseDate("2017-12-04"))
.setAssigneeUuid(john.getUuid());
IssueDto issue2 = newDto(rule, file, project)
.setIssueCreationDate(parseDate("2014-09-04"))
.setIssueUpdateDate(parseDate("2017-12-04"))
.setEffort(10L)
.setStatus("OPEN")
.setKee("7b112bd4-b650-4037-80bc-82fd47d4eac2")
.setSeverity("MAJOR")
.setAssignee("alice");
IssueDto issue3 = IssueTesting.newDto(rule, file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
.setIssueUpdateDate(DateUtils.parseDate("2017-12-04"))
.setAssigneeUuid(alice.getUuid());
IssueDto issue3 = newDto(rule, file, project)
.setIssueCreationDate(parseDate("2014-09-04"))
.setIssueUpdateDate(parseDate("2017-12-04"))
.setEffort(10L)
.setStatus("OPEN")
.setKee("82fd47d4-4037-b650-80bc-7b112bd4eac2")
.setSeverity("MAJOR");
.setSeverity("MAJOR")
.setAssigneeUuid(null);
dbClient.issueDao().insert(session, issue1, issue2, issue3);
session.commit();
indexIssues();

userSessionRule.logIn("john");
userSessionRule.logIn(john);

ws.newRequest()
.setParam("resolved", "false")
.setParam("assignees", "__me__")
@@ -610,25 +629,82 @@ public class SearchActionTest {
.assertJson(this.getClass(), "filter_by_assigned_to_me.json");
}

@Test
public void return_empty_when_login_is_unknown() {

UserDto john = db.users().insertUser(u -> u.setLogin("john").setName("John").setEmail("john@email.com"));
UserDto alice = db.users().insertUser(u -> u.setLogin("alice").setName("Alice").setEmail("alice@email.com"));

ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(defaultOrganization, "PROJECT_ID").setDbKey("PROJECT_KEY"));
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
RuleDto rule = newRule();
IssueDto issue1 = newDto(rule, file, project)
.setIssueCreationDate(parseDate("2014-09-04"))
.setIssueUpdateDate(parseDate("2017-12-04"))
.setEffort(10L)
.setStatus("OPEN")
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
.setSeverity("MAJOR")
.setAssigneeUuid(john.getUuid());
IssueDto issue2 = newDto(rule, file, project)
.setIssueCreationDate(parseDate("2014-09-04"))
.setIssueUpdateDate(parseDate("2017-12-04"))
.setEffort(10L)
.setStatus("OPEN")
.setKee("7b112bd4-b650-4037-80bc-82fd47d4eac2")
.setSeverity("MAJOR")
.setAssigneeUuid(alice.getUuid());
IssueDto issue3 = newDto(rule, file, project)
.setIssueCreationDate(parseDate("2014-09-04"))
.setIssueUpdateDate(parseDate("2017-12-04"))
.setEffort(10L)
.setStatus("OPEN")
.setKee("82fd47d4-4037-b650-80bc-7b112bd4eac2")
.setSeverity("MAJOR")
.setAssigneeUuid(null);
dbClient.issueDao().insert(session, issue1, issue2, issue3);
session.commit();
indexIssues();

userSessionRule.logIn(john);

Issues.SearchWsResponse response = ws.newRequest()
.setParam("resolved", "false")
.setParam("assignees", "unknown")
.setParam(WebService.Param.FACETS, "assignees")
.executeProtobuf(Issues.SearchWsResponse.class);

assertThat(response.getIssuesList()).isEmpty();
}

@Test
public void filter_by_assigned_to_me_unauthenticated() {
userSessionRule.logIn();
UserDto poy = db.users().insertUser(u -> u.setLogin("poy").setName("poypoy").setEmail("poypoy@email.com"));
userSessionRule.logIn(poy);

// TODO : check test title w julien


UserDto alice = db.users().insertUser(u -> u.setLogin("alice").setName("Alice").setEmail("alice@email.com"));
UserDto john = db.users().insertUser(u -> u.setLogin("john").setName("John").setEmail("john@email.com"));

ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization1, "PROJECT_ID").setDbKey("PROJECT_KEY"));
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
RuleDto rule = newRule();
IssueDto issue1 = IssueTesting.newDto(rule, file, project)
IssueDto issue1 = newDto(rule, file, project)
.setStatus("OPEN")
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
.setAssignee("john");
IssueDto issue2 = IssueTesting.newDto(rule, file, project)
.setAssigneeUuid(john.getUuid());
IssueDto issue2 = newDto(rule, file, project)
.setStatus("OPEN")
.setKee("7b112bd4-b650-4037-80bc-82fd47d4eac2")
.setAssignee("alice");
IssueDto issue3 = IssueTesting.newDto(rule, file, project)
.setAssigneeUuid(alice.getUuid());
IssueDto issue3 = newDto(rule, file, project)
.setStatus("OPEN")
.setKee("82fd47d4-4037-b650-80bc-7b112bd4eac2");
.setKee("82fd47d4-4037-b650-80bc-7b112bd4eac2")
.setAssigneeUuid(null);
dbClient.issueDao().insert(session, issue1, issue2, issue3);
session.commit();
indexIssues();
@@ -642,40 +718,42 @@ public class SearchActionTest {

@Test
public void assigned_to_me_facet_is_sticky_relative_to_assignees() {
db.users().insertUser(u -> u.setLogin("alice").setName("Alice").setEmail("alice@email.com"));
UserDto alice = db.users().insertUser(u -> u.setLogin("alice").setName("Alice").setEmail("alice@email.com"));
UserDto john = db.users().insertUser(u -> u.setLogin("john-bob.polop").setName("John").setEmail("john@email.com"));

ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization2, "PROJECT_ID").setDbKey("PROJECT_KEY"));
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
RuleDto rule = newRule();
IssueDto issue1 = IssueTesting.newDto(rule, file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
.setIssueUpdateDate(DateUtils.parseDate("2017-12-04"))
IssueDto issue1 = newDto(rule, file, project)
.setIssueCreationDate(parseDate("2014-09-04"))
.setIssueUpdateDate(parseDate("2017-12-04"))
.setEffort(10L)
.setStatus("OPEN")
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
.setSeverity("MAJOR")
.setAssignee("john-bob.polop");
IssueDto issue2 = IssueTesting.newDto(rule, file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
.setIssueUpdateDate(DateUtils.parseDate("2017-12-04"))
.setAssigneeUuid(john.getUuid());
IssueDto issue2 = newDto(rule, file, project)
.setIssueCreationDate(parseDate("2014-09-04"))
.setIssueUpdateDate(parseDate("2017-12-04"))
.setEffort(10L)
.setStatus("OPEN")
.setKee("7b112bd4-b650-4037-80bc-82fd47d4eac2")
.setSeverity("MAJOR")
.setAssignee("alice");
IssueDto issue3 = IssueTesting.newDto(rule, file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
.setIssueUpdateDate(DateUtils.parseDate("2017-12-04"))
.setAssigneeUuid(alice.getUuid());
IssueDto issue3 = newDto(rule, file, project)
.setIssueCreationDate(parseDate("2014-09-04"))
.setIssueUpdateDate(parseDate("2017-12-04"))
.setEffort(10L)
.setStatus("OPEN")
.setKee("82fd47d4-4037-b650-80bc-7b112bd4eac2")
.setSeverity("MAJOR");
.setSeverity("MAJOR")
.setAssigneeUuid(null);
dbClient.issueDao().insert(session, issue1, issue2, issue3);
session.commit();
indexIssues();

userSessionRule.logIn("john-bob.polop");
userSessionRule.logIn(john);
ws.newRequest()
.setParam("resolved", "false")
.setParam("assignees", "alice")
@@ -690,13 +768,13 @@ public class SearchActionTest {
ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization2, "PROJECT_ID").setDbKey("PROJECT_KEY"));
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
dbClient.issueDao().insert(session, IssueTesting.newDto(rule, file, project)
dbClient.issueDao().insert(session, newDto(rule, file, project)
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac1")
.setIssueUpdateDate(DateUtils.parseDateTime("2014-11-02T00:00:00+0100")));
dbClient.issueDao().insert(session, IssueTesting.newDto(rule, file, project)
dbClient.issueDao().insert(session, newDto(rule, file, project)
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
.setIssueUpdateDate(DateUtils.parseDateTime("2014-11-01T00:00:00+0100")));
dbClient.issueDao().insert(session, IssueTesting.newDto(rule, file, project)
dbClient.issueDao().insert(session, newDto(rule, file, project)
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac3")
.setIssueUpdateDate(DateUtils.parseDateTime("2014-11-03T00:00:00+0100")));
session.commit();
@@ -721,7 +799,7 @@ public class SearchActionTest {
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
for (int i = 0; i < 12; i++) {
IssueDto issue = IssueTesting.newDto(rule, file, project);
IssueDto issue = newDto(rule, file, project);
dbClient.issueDao().insert(session, issue);
}
session.commit();
@@ -741,7 +819,7 @@ public class SearchActionTest {
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
for (int i = 0; i < 12; i++) {
IssueDto issue = IssueTesting.newDto(rule, file, project);
IssueDto issue = newDto(rule, file, project);
dbClient.issueDao().insert(session, issue);
}
session.commit();
@@ -761,7 +839,7 @@ public class SearchActionTest {
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
for (int i = 0; i < 12; i++) {
IssueDto issue = IssueTesting.newDto(rule, file, project);
IssueDto issue = newDto(rule, file, project).setAssigneeUuid(null);
dbClient.issueDao().insert(session, issue);
}
session.commit();
@@ -786,9 +864,9 @@ public class SearchActionTest {
ComponentDto project = insertComponent(ComponentTesting.newPublicProjectDto(otherOrganization1, "PROJECT_ID").setDbKey("PROJECT_KEY"));
indexPermissions();
ComponentDto file = insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
IssueDto issue = IssueTesting.newDto(newRule(), file, project)
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
.setIssueUpdateDate(DateUtils.parseDate("2017-12-04"))
IssueDto issue = newDto(newRule(), file, project)
.setIssueCreationDate(parseDate("2014-09-04"))
.setIssueUpdateDate(parseDate("2017-12-04"))
.setEffort(10L)
.setStatus("OPEN")
.setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")

+ 4
- 0
server/sonar-server/src/test/java/org/sonar/server/tester/AnonymousMockUserSession.java View File

@@ -39,6 +39,10 @@ public class AnonymousMockUserSession extends AbstractMockUserSession<AnonymousM
return null;
}

@Override public String getUuid() {
return null;
}

@Override
public String getName() {
return null;

+ 13
- 0
server/sonar-server/src/test/java/org/sonar/server/tester/MockUserSession.java View File

@@ -31,6 +31,7 @@ import static java.util.Arrays.asList;

public class MockUserSession extends AbstractMockUserSession<MockUserSession> {
private final String login;
private String uuid;
private boolean root = false;
private Integer userId;
private String name;
@@ -40,6 +41,7 @@ public class MockUserSession extends AbstractMockUserSession<MockUserSession> {
super(MockUserSession.class);
checkArgument(!login.isEmpty());
this.login = login;
setUuid(login + "uuid");
setUserId(login.hashCode());
setName(login + " name");
}
@@ -48,6 +50,7 @@ public class MockUserSession extends AbstractMockUserSession<MockUserSession> {
super(MockUserSession.class);
checkArgument(!userDto.getLogin().isEmpty());
this.login = userDto.getLogin();
setUuid(userDto.getUuid());
setUserId(userDto.getId());
setName(userDto.getName());
}
@@ -71,6 +74,16 @@ public class MockUserSession extends AbstractMockUserSession<MockUserSession> {
return this.login;
}

@Override
public String getUuid() {
return this.uuid;
}

public MockUserSession setUuid(String uuid) {
this.uuid = Objects.requireNonNull(uuid);
return this;
}

@Override
public String getName() {
return this.name;

+ 1
- 0
server/sonar-server/src/test/java/org/sonar/server/tester/MockUserSessionTest.java View File

@@ -32,6 +32,7 @@ public class MockUserSessionTest {
MockUserSession mock = new MockUserSession("foo").setGroups(group);

assertThat(mock.getLogin()).isEqualTo("foo");
assertThat(mock.getUuid()).isEqualTo("foouuid");
assertThat(mock.getGroups()).extracting(GroupDto::getId).containsOnly(group.getId());
assertThat(mock.isLoggedIn()).isTrue();
}

+ 6
- 0
server/sonar-server/src/test/java/org/sonar/server/tester/UserSessionRule.java View File

@@ -245,6 +245,12 @@ public class UserSessionRule implements TestRule, UserSession {
return currentUserSession.getLogin();
}

@Override
@CheckForNull
public String getUuid() {
return currentUserSession.getUuid();
}

@Override
@CheckForNull
public String getName() {

+ 1
- 0
server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java View File

@@ -63,6 +63,7 @@ public class ServerUserSessionTest {
UserSession session = newAnonymousSession();

assertThat(session.getLogin()).isNull();
assertThat(session.getUuid()).isNull();
assertThat(session.isLoggedIn()).isFalse();
}


+ 5
- 0
server/sonar-server/src/test/java/org/sonar/server/user/TestUserSessionFactory.java View File

@@ -66,6 +66,11 @@ public class TestUserSessionFactory implements UserSessionFactory {
return user != null ? user.getLogin() : null;
}

@Override
public String getUuid() {
return user != null ? user.getUuid() : null;
}

@Override
public String getName() {
return user != null ? user.getName() : null;

+ 2
- 0
server/sonar-server/src/test/java/org/sonar/server/user/ThreadLocalUserSessionTest.java View File

@@ -55,6 +55,7 @@ public class ThreadLocalUserSessionTest {
public void get_session_for_user() {
GroupDto group = GroupTesting.newGroupDto();
MockUserSession expected = new MockUserSession("karadoc")
.setUuid("karadoc-uuid")
.setUserId(123)
.setGroups(group);
threadLocalUserSession.set(expected);
@@ -63,6 +64,7 @@ public class ThreadLocalUserSessionTest {
assertThat(session).isSameAs(expected);
assertThat(threadLocalUserSession.getUserId()).isEqualTo(123);
assertThat(threadLocalUserSession.getLogin()).isEqualTo("karadoc");
assertThat(threadLocalUserSession.getUuid()).isEqualTo("karadoc-uuid");
assertThat(threadLocalUserSession.isLoggedIn()).isTrue();
assertThat(threadLocalUserSession.getGroups()).extracting(GroupDto::getId).containsOnly(group.getId());
}

+ 1
- 1
server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueIteratorFactoryTest/one_issue.xml View File

@@ -67,7 +67,7 @@
status="RESOLVED"
severity="BLOCKER"
manual_severity="[false]"
assignee="guy1"
assignee="uuid-of-guy1"
author_login="guy2"
checksum="FFFFF"
gap="2"

+ 4
- 4
server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueIteratorFactoryTest/shared.xml View File

@@ -67,7 +67,7 @@
status="RESOLVED"
severity="BLOCKER"
manual_severity="[false]"
assignee="guy1"
assignee="uuid-of-guy1"
author_login="guy2"
checksum="FFFFF"
gap="[null]"
@@ -97,7 +97,7 @@
status="RESOLVED"
severity="BLOCKER"
manual_severity="[false]"
assignee="guy1"
assignee="uuid-of-guy1"
author_login="guy2"
checksum="FFFFF"
gap="[null]"
@@ -127,7 +127,7 @@
status="OPEN"
severity="MAJOR"
manual_severity="[false]"
assignee="guy2"
assignee="uuid-of-guy2"
author_login="[null]"
checksum="FFFFF"
gap="[null]"
@@ -157,7 +157,7 @@
status="RESOLVED"
severity="BLOCKER"
manual_severity="[false]"
assignee="guy1"
assignee="uuid-of-guy1"
author_login="guy2"
checksum="FFFFF"
gap="[null]"

+ 4
- 4
sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java View File

@@ -73,7 +73,7 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure.
private Duration effort;
private String status;
private String resolution;
private String assignee;
private String assigneeUuid;
private String checksum;
private Map<String, String> attributes = null;
private String authorLogin = null;
@@ -359,11 +359,11 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure.
@Override
@CheckForNull
public String assignee() {
return assignee;
return assigneeUuid;
}

public DefaultIssue setAssignee(@Nullable String s) {
this.assignee = s;
public DefaultIssue setAssigneeUuid(@Nullable String s) {
this.assigneeUuid = s;
return this;
}


+ 4
- 4
sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java View File

@@ -41,7 +41,7 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder {
private String message;
private String severity;
private Double effortToFix;
private String assignee;
private String assigneeUuid;
private RuleType type;
private Map<String, String> attributes;
private boolean isFromExternalRuleEngine;
@@ -124,8 +124,8 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder {
return this;
}

public DefaultIssueBuilder assignee(@Nullable String s) {
this.assignee = s;
public DefaultIssueBuilder assigneeUuid(@Nullable String s) {
this.assigneeUuid = s;
return this;
}

@@ -161,7 +161,7 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder {
issue.setManualSeverity(false);
issue.setGap(effortToFix);
issue.setLine(line);
issue.setAssignee(assignee);
issue.setAssigneeUuid(assigneeUuid);
issue.setAttributes(attributes);
issue.setResolution(null);
issue.setStatus(Issue.STATUS_OPEN);

+ 1
- 1
sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java View File

@@ -53,7 +53,7 @@ public class DefaultIssueTest {
.setEffort(Duration.create(28800L))
.setStatus(Issue.STATUS_CLOSED)
.setResolution(Issue.RESOLUTION_FIXED)
.setAssignee("julien")
.setAssigneeUuid("julien")
.setAuthorLogin("steph")
.setChecksum("c7b5db46591806455cf082bb348631e8")
.setNew(true)

+ 1
- 1
sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java View File

@@ -153,7 +153,7 @@ public interface Issue extends Serializable {
String reporter();

/**
* Login of the user who is assigned to this issue. Null if the issue is not assigned.
* UUID of the user who is assigned to this issue. Null if the issue is not assigned.
*/
@CheckForNull
String assignee();

+ 2
- 0
tests/src/test/java/org/sonarqube/tests/issue/IssueChangelogTest.java View File

@@ -21,6 +21,7 @@ package org.sonarqube.tests.issue;

import java.util.List;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.sonar.wsclient.issue.Issue;
import org.sonarqube.ws.Issues;
@@ -48,6 +49,7 @@ public class IssueChangelogTest extends AbstractIssueTest {
}

@Test
@Ignore // TODO GJT when adressing ticket on IssueChangesUuid
public void update_changelog_when_assigning_issue_by_user() {
runProjectAnalysis(ORCHESTRATOR, "shared/xoo-sample");
Issue issue = searchRandomIssue();

+ 2
- 0
tests/src/test/java/org/sonarqube/tests/issue/IssueNotificationsTest.java View File

@@ -31,6 +31,7 @@ import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.sonarqube.qa.util.Tester;
@@ -117,6 +118,7 @@ public class IssueNotificationsTest {
}

@Test
@Ignore // TODO GJT when adressing ticket on IssueChangesUuid
public void notification_for_ChangesOnMyIssue() throws Exception {
String version = RandomStringUtils.randomAlphanumeric(10);
Project project = tester.projects().provision();

+ 33
- 0
tests/src/test/java/org/sonarqube/tests/user/SonarCloudUpdateLoginDuringAuthenticationTest.java View File

@@ -28,6 +28,7 @@ import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.sonarqube.qa.util.Tester;
import org.sonarqube.ws.Issues;
import org.sonarqube.ws.Issues.Issue;
import org.sonarqube.ws.Organizations.Organization;
import org.sonarqube.ws.Projects;
@@ -35,6 +36,7 @@ import org.sonarqube.ws.Qualityprofiles;
import org.sonarqube.ws.Settings;
import org.sonarqube.ws.Users;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.issues.AssignRequest;
import org.sonarqube.ws.client.organizations.AddMemberRequest;
import org.sonarqube.ws.client.organizations.SearchRequest;
import org.sonarqube.ws.client.settings.SetRequest;
@@ -110,6 +112,37 @@ public class SonarCloudUpdateLoginDuringAuthenticationTest {
.doesNotContain(loginHavingUpperCase);
}

@Test
public void issue_is_still_assigned_after_login_update() {
String oldLogin = tester.users().generateLogin();
String providerId = tester.users().generateProviderId();

// Create user using authentication
authenticate(oldLogin, providerId);

// Set user as member of the organization
Organization organization = tester.organizations().generate();
tester.organizations().service().addMember(new AddMemberRequest().setOrganization(organization.getKey()).setLogin(oldLogin));
Projects.CreateWsResponse.Project project = tester.projects().provision(organization);
Qualityprofiles.CreateWsResponse.QualityProfile profile = tester.qProfiles().createXooProfile(organization);
tester.qProfiles().assignQProfileToProject(profile, project);
tester.qProfiles().activateRule(profile.getKey(), "xoo:OneIssuePerLine");

// Execute project and assignee an issue to the user
orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample"),
"sonar.organization", organization.getKey(),
"sonar.projectKey", project.getKey(),
"sonar.login", "admin",
"sonar.password", "admin"));
Issues.Issue issue = tester.wsClient().issues().search(new org.sonarqube.ws.client.issues.SearchRequest().setOrganization(organization.getKey())).getIssuesList().get(0);
tester.wsClient().issues().assign(new AssignRequest().setIssue(issue.getKey()).setAssignee(oldLogin));

// Update login during authentication, check issue is assigned to new login
String newLogin = tester.users().generateLogin();
authenticate(newLogin, providerId);
tester.wsClient().issues().assign(new AssignRequest().setIssue(issue.getKey()).setAssignee(newLogin));
}

@Test
public void default_assignee_login_is_updated_after_login_update() {
String oldLogin = tester.users().generateLogin();

Loading…
Cancel
Save