]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10302 Auto issue assignment is now based on org membership
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 6 Feb 2018 12:58:34 +0000 (13:58 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 7 Feb 2018 15:43:01 +0000 (16:43 +0100)
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ScmAccountToUserLoader.java
server/sonar-server/src/main/java/org/sonar/server/user/index/UserIndex.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ScmAccountToUserLoaderTest.java
server/sonar-server/src/test/java/org/sonar/server/user/index/UserIndexTest.java
tests/src/test/java/org/sonarqube/tests/Category6Suite.java
tests/src/test/java/org/sonarqube/tests/issue/OrganizationIssueAssignTest.java
tests/src/test/java/org/sonarqube/tests/issue/OrganizationIssueSuite.java [new file with mode: 0644]

index 44092180e5919219726563f76cd37215bb49705b..aeb3e9dfa72650ebfd07aeeb41d866e30614ecce 100644 (file)
@@ -27,6 +27,7 @@ import java.util.Map;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
 import org.sonar.server.user.index.UserDoc;
 import org.sonar.server.user.index.UserIndex;
 import org.sonar.server.util.cache.CacheLoader;
@@ -37,15 +38,18 @@ import org.sonar.server.util.cache.CacheLoader;
 public class ScmAccountToUserLoader implements CacheLoader<String, String> {
 
   private static final Logger LOGGER = Loggers.get(ScmAccountToUserLoader.class);
+
   private final UserIndex index;
+  private final AnalysisMetadataHolder analysisMetadataHolder;
 
-  public ScmAccountToUserLoader(UserIndex index) {
+  public ScmAccountToUserLoader(UserIndex index, AnalysisMetadataHolder analysisMetadataHolder) {
     this.index = index;
+    this.analysisMetadataHolder = analysisMetadataHolder;
   }
 
   @Override
   public String load(String scmAccount) {
-    List<UserDoc> users = index.getAtMostThreeActiveUsersForScmAccount(scmAccount);
+    List<UserDoc> users = index.getAtMostThreeActiveUsersForScmAccount(scmAccount, analysisMetadataHolder.getOrganization().getUuid());
     if (users.size() == 1) {
       return users.get(0).login();
     }
index 15f59ce0f5c294d287809df881fbcacccca500f4..c33452c9cd2ff51378d34382191f7be04788852d 100644 (file)
@@ -81,13 +81,14 @@ public class UserIndex {
    * Returns the active users (at most 3) who are associated to the given SCM account. This method can be used
    * to detect user conflicts.
    */
-  public List<UserDoc> getAtMostThreeActiveUsersForScmAccount(String scmAccount) {
+  public List<UserDoc> getAtMostThreeActiveUsersForScmAccount(String scmAccount, String organizationUuid) {
     List<UserDoc> result = new ArrayList<>();
     if (!StringUtils.isEmpty(scmAccount)) {
       SearchRequestBuilder request = esClient.prepareSearch(UserIndexDefinition.INDEX_TYPE_USER)
         .setQuery(boolQuery().must(matchAllQuery()).filter(
           boolQuery()
             .must(termQuery(FIELD_ACTIVE, true))
+            .must(termQuery(FIELD_ORGANIZATION_UUIDS, organizationUuid))
             .should(termQuery(FIELD_LOGIN, scmAccount))
             .should(matchQuery(SORTABLE_ANALYZER.subField(FIELD_EMAIL), scmAccount))
             .should(matchQuery(SORTABLE_ANALYZER.subField(FIELD_SCM_ACCOUNTS), scmAccount))))
index 99523d36d415079bfc5c156e4904202c8048460f..c19b136fe4328eadd51dee85d7275eed79300369 100644 (file)
 package org.sonar.server.computation.task.projectanalysis.issue;
 
 import java.util.Collections;
+import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.config.internal.MapSettings;
 import org.sonar.api.utils.System2;
 import org.sonar.api.utils.log.LogTester;
 import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.server.computation.task.projectanalysis.analysis.Organization;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.user.index.UserDoc;
 import org.sonar.server.user.index.UserIndex;
 import org.sonar.server.user.index.UserIndexDefinition;
 
 import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
 
 public class ScmAccountToUserLoaderTest {
 
-  @org.junit.Rule
+  private static final String ORGANIZATION_UUID = "my-organization";
+
+  @Rule
   public EsTester esTester = new EsTester(new UserIndexDefinition(new MapSettings().asConfig()));
 
-  @org.junit.Rule
+  @Rule
   public LogTester logTester = new LogTester();
 
+  @Rule
+  public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule()
+    .setOrganization(Organization.from(new OrganizationDto().setUuid(ORGANIZATION_UUID).setKey("Key").setName("Name").setDefaultQualityGateUuid("QGate")));
+
   @Test
   public void load_login_for_scm_account() {
     UserDoc user = new UserDoc()
@@ -49,11 +60,12 @@ public class ScmAccountToUserLoaderTest {
       .setName("Charlie")
       .setEmail("charlie@hebdo.com")
       .setActive(true)
-      .setScmAccounts(asList("charlie", "jesuis@charlie.com"));
+      .setScmAccounts(asList("charlie", "jesuis@charlie.com"))
+      .setOrganizationUuids(singletonList(ORGANIZATION_UUID));
     esTester.putDocuments(UserIndexDefinition.INDEX_TYPE_USER.getIndex(), UserIndexDefinition.INDEX_TYPE_USER.getType(), user);
 
     UserIndex index = new UserIndex(esTester.client(), System2.INSTANCE);
-    ScmAccountToUserLoader underTest = new ScmAccountToUserLoader(index);
+    ScmAccountToUserLoader underTest = new ScmAccountToUserLoader(index, analysisMetadataHolder);
 
     assertThat(underTest.load("missing")).isNull();
     assertThat(underTest.load("jesuis@charlie.com")).isEqualTo("charlie");
@@ -66,18 +78,20 @@ public class ScmAccountToUserLoaderTest {
       .setName("Charlie")
       .setEmail("charlie@hebdo.com")
       .setActive(true)
-      .setScmAccounts(asList("charlie", "jesuis@charlie.com"));
+      .setScmAccounts(asList("charlie", "jesuis@charlie.com"))
+      .setOrganizationUuids(singletonList(ORGANIZATION_UUID));
     esTester.putDocuments(UserIndexDefinition.INDEX_TYPE_USER.getIndex(), UserIndexDefinition.INDEX_TYPE_USER.getType(), user1);
 
     UserDoc user2 = new UserDoc()
       .setLogin("another.charlie")
       .setName("Another Charlie")
       .setActive(true)
-      .setScmAccounts(asList("charlie"));
+      .setScmAccounts(singletonList("charlie"))
+      .setOrganizationUuids(singletonList(ORGANIZATION_UUID));
     esTester.putDocuments(UserIndexDefinition.INDEX_TYPE_USER.getIndex(), UserIndexDefinition.INDEX_TYPE_USER.getType(), user2);
 
     UserIndex index = new UserIndex(esTester.client(), System2.INSTANCE);
-    ScmAccountToUserLoader underTest = new ScmAccountToUserLoader(index);
+    ScmAccountToUserLoader underTest = new ScmAccountToUserLoader(index, analysisMetadataHolder);
 
     assertThat(underTest.load("charlie")).isNull();
     assertThat(logTester.logs(LoggerLevel.WARN)).contains("Multiple users share the SCM account 'charlie': another.charlie, charlie");
@@ -86,7 +100,7 @@ public class ScmAccountToUserLoaderTest {
   @Test
   public void load_by_multiple_scm_accounts_is_not_supported_yet() {
     UserIndex index = new UserIndex(esTester.client(), System2.INSTANCE);
-    ScmAccountToUserLoader underTest = new ScmAccountToUserLoader(index);
+    ScmAccountToUserLoader underTest = new ScmAccountToUserLoader(index, analysisMetadataHolder);
     try {
       underTest.loadAll(Collections.emptyList());
       fail();
index 3ed10b6452a3e8890be7d19d5ffbbaa2c861a67b..45af16daf7b121842c867c8542d55196ba23ffe6 100644 (file)
@@ -33,11 +33,13 @@ import org.sonar.server.es.SearchOptions;
 import static com.google.common.collect.Lists.newArrayList;
 import static java.util.Arrays.asList;
 import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.sonar.server.user.index.UserIndexDefinition.INDEX_TYPE_USER;
 
 public class UserIndexTest {
 
+  private static final String ORGANIZATION_UUID = "my-organization";
   private static final String USER1_LOGIN = "user1";
   private static final String USER2_LOGIN = "user2";
   private static final long DATE_1 = 1_500_000_000_000L;
@@ -91,24 +93,24 @@ public class UserIndexTest {
     esTester.putDocuments(INDEX_TYPE_USER, user2);
     esTester.putDocuments(INDEX_TYPE_USER, user3);
 
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount(user1.scmAccounts().get(0))).extractingResultOf("login").containsOnly(user1.login());
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount(user1.login())).extractingResultOf("login").containsOnly(user1.login());
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount(user1.scmAccounts().get(0), ORGANIZATION_UUID)).extractingResultOf("login").containsOnly(user1.login());
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount(user1.login(), ORGANIZATION_UUID)).extractingResultOf("login").containsOnly(user1.login());
 
     // both users share the same email
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount(user1.email())).extracting(UserDoc::login).containsOnly(user1.login(), user2.login());
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount(user1.email(), ORGANIZATION_UUID)).extracting(UserDoc::login).containsOnly(user1.login(), user2.login());
 
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("")).isEmpty();
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("unknown")).isEmpty();
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("", ORGANIZATION_UUID)).isEmpty();
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("unknown", ORGANIZATION_UUID)).isEmpty();
   }
 
   @Test
   public void getAtMostThreeActiveUsersForScmAccount_ignores_inactive_user()  {
     String scmAccount = "scmA";
-    UserDoc user = newUser(USER1_LOGIN, asList(scmAccount)).setActive(false);
+    UserDoc user = newUser(USER1_LOGIN, singletonList(scmAccount)).setActive(false);
     esTester.putDocuments(INDEX_TYPE_USER, user);
 
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount(user.login())).isEmpty();
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount(scmAccount)).isEmpty();
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount(user.login(), ORGANIZATION_UUID)).isEmpty();
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount(scmAccount, ORGANIZATION_UUID)).isEmpty();
   }
 
   @Test
@@ -124,37 +126,47 @@ public class UserIndexTest {
     esTester.putDocuments(INDEX_TYPE_USER, user4);
 
     // restrict results to 3 users
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount(email)).hasSize(3);
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount(email, ORGANIZATION_UUID)).hasSize(3);
   }
 
   @Test
   public void getAtMostThreeActiveUsersForScmAccount_is_case_sensitive_for_login()  {
-    UserDoc user = newUser("the_login", asList("John.Smith"));
+    UserDoc user = newUser("the_login", singletonList("John.Smith"));
     esTester.putDocuments(INDEX_TYPE_USER, user);
 
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("the_login")).hasSize(1);
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("the_Login")).isEmpty();
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("the_login", ORGANIZATION_UUID)).hasSize(1);
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("the_Login", ORGANIZATION_UUID)).isEmpty();
   }
 
   @Test
   public void getAtMostThreeActiveUsersForScmAccount_is_case_insensitive_for_email()  {
-    UserDoc user = newUser("the_login", "the_EMAIL@corp.com", asList("John.Smith"));
+    UserDoc user = newUser("the_login", "the_EMAIL@corp.com", singletonList("John.Smith"));
     esTester.putDocuments(INDEX_TYPE_USER, user);
 
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("the_EMAIL@corp.com")).hasSize(1);
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("the_email@corp.com")).hasSize(1);
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("email")).isEmpty();
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("the_EMAIL@corp.com", ORGANIZATION_UUID)).hasSize(1);
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("the_email@corp.com", ORGANIZATION_UUID)).hasSize(1);
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("email", ORGANIZATION_UUID)).isEmpty();
   }
 
   @Test
   public void getAtMostThreeActiveUsersForScmAccount_is_case_insensitive_for_scm_account()  {
-    UserDoc user = newUser("the_login", asList("John.Smith"));
+    UserDoc user = newUser("the_login", singletonList("John.Smith"));
     esTester.putDocuments(INDEX_TYPE_USER, user);
 
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("John.Smith")).hasSize(1);
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("JOHN.SMIth")).hasSize(1);
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("JOHN.SMITH")).hasSize(1);
-    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("JOHN")).isEmpty();
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("John.Smith", ORGANIZATION_UUID)).hasSize(1);
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("JOHN.SMIth", ORGANIZATION_UUID)).hasSize(1);
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("JOHN.SMITH", ORGANIZATION_UUID)).hasSize(1);
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("JOHN", ORGANIZATION_UUID)).isEmpty();
+  }
+
+  @Test
+  public void getAtMostThreeActiveUsersForScmAccount_search_only_user_within_given_organization()  {
+    UserDoc user1 = newUser("user1", singletonList("same_scm")).setOrganizationUuids(singletonList(ORGANIZATION_UUID));
+    UserDoc user2 = newUser("user2", singletonList("same_scm")).setOrganizationUuids(singletonList("another_organization"));
+    esTester.putDocuments(INDEX_TYPE_USER, user1);
+    esTester.putDocuments(INDEX_TYPE_USER, user2);
+
+    assertThat(underTest.getAtMostThreeActiveUsersForScmAccount("same_scm", ORGANIZATION_UUID)).extractingResultOf("login").containsOnly(user1.login());
   }
 
   @Test
@@ -201,7 +213,8 @@ public class UserIndexTest {
       .setName(login.toUpperCase(Locale.ENGLISH))
       .setEmail(login + "@mail.com")
       .setActive(true)
-      .setScmAccounts(scmAccounts);
+      .setScmAccounts(scmAccounts)
+      .setOrganizationUuids(singletonList(ORGANIZATION_UUID));
   }
 
   private static UserDoc newUser(String login, String email, List<String> scmAccounts) {
@@ -210,6 +223,7 @@ public class UserIndexTest {
       .setName(login.toUpperCase(Locale.ENGLISH))
       .setEmail(email)
       .setActive(true)
-      .setScmAccounts(scmAccounts);
+      .setScmAccounts(scmAccounts)
+      .setOrganizationUuids(singletonList(ORGANIZATION_UUID));
   }
 }
index 27db0db9d0dcce4526e35db21769e8df2499317d..dd8ecd2876b2fe50022596fa88de82e2b6703263 100644 (file)
@@ -29,7 +29,6 @@ import org.sonarqube.tests.authorization.PermissionTemplateTest;
 import org.sonarqube.tests.ce.ReportFailureNotificationTest;
 import org.sonarqube.tests.issue.IssueNotificationsTest;
 import org.sonarqube.tests.issue.IssueTagsTest;
-import org.sonarqube.tests.issue.OrganizationIssueAssignTest;
 import org.sonarqube.tests.issue.OrganizationIssuesPageTest;
 import org.sonarqube.tests.qualityProfile.BuiltInQualityProfilesTest;
 import org.sonarqube.tests.qualityProfile.CustomQualityProfilesTest;
@@ -53,7 +52,6 @@ import static util.ItUtils.xooPlugin;
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
   OrganizationIdentityProviderTest.class,
-  OrganizationIssueAssignTest.class,
   OrganizationIssuesPageTest.class,
   OrganizationQualityProfilesUiTest.class,
   BuiltInQualityProfilesTest.class,
index 4e76ba141802b7ca9225585aca396caca3c3021e..01e8e14143198bd3721adb8424479752c5303940 100644 (file)
@@ -21,24 +21,22 @@ package org.sonarqube.tests.issue;
 
 import com.sonar.orchestrator.Orchestrator;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
-import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonarqube.qa.util.Tester;
 import org.sonarqube.qa.util.pageobjects.issues.IssuesPage;
-import org.sonarqube.tests.Category6Suite;
 import org.sonarqube.ws.Issues;
 import org.sonarqube.ws.Issues.Issue;
-import org.sonarqube.ws.Organizations;
+import org.sonarqube.ws.Organizations.Organization;
+import org.sonarqube.ws.Projects.CreateWsResponse.Project;
+import org.sonarqube.ws.Qualityprofiles.CreateWsResponse.QualityProfile;
 import org.sonarqube.ws.Users.CreateWsResponse.User;
 import org.sonarqube.ws.client.issues.AssignRequest;
 import org.sonarqube.ws.client.issues.BulkChangeRequest;
 import org.sonarqube.ws.client.issues.SearchRequest;
-import org.sonarqube.ws.client.qualityprofiles.AddProjectRequest;
-import org.sonarqube.ws.client.projects.CreateRequest;
-import util.issue.IssueRule;
 
 import static java.lang.String.format;
 import static java.util.Collections.singletonList;
@@ -46,101 +44,120 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static util.ItUtils.expectHttpError;
 import static util.ItUtils.restoreProfile;
 import static util.ItUtils.runProjectAnalysis;
-import static util.ItUtils.setServerProperty;
 
 public class OrganizationIssueAssignTest {
 
   private final static String SAMPLE_PROJECT_KEY = "sample";
 
   @ClassRule
-  public static Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
+  public static Orchestrator orchestrator = OrganizationIssueSuite.ORCHESTRATOR;
   @Rule
   public Tester tester = new Tester(orchestrator);
 
-  @Rule
-  public IssueRule issueRule = IssueRule.from(orchestrator);
-
-  private Organizations.Organization org1;
-  private Organizations.Organization org2;
-  private User user;
-
-  @Before
-  public void setUp() {
-    org1 = tester.organizations().generate();
-    org2 = tester.organizations().generate();
-    user = tester.users().generate();
-    restoreProfile(orchestrator, getClass().getResource("/organization/IssueAssignTest/one-issue-per-file-profile.xml"), org1.getKey());
-  }
-
   @Test
-  public void auto_assign_issues_to_user_if_default_assignee_is_member_of_project_organization() {
-    tester.organizations().addMember(org1, user);
+  public void auto_assign_issues_to_default_assignee_if_member_of_project_organization() {
+    Organization organization = tester.organizations().generate();
+    User user = tester.users().generateMember(organization);
+    provisionProjectAndAssociateItToQProfile(SAMPLE_PROJECT_KEY, organization);
+    tester.settings().setProjectSetting("sample", "sonar.issues.defaultAssigneeLogin", user.getLogin());
 
-    provisionProject(SAMPLE_PROJECT_KEY, org1.getKey());
-    setServerProperty(orchestrator, "sample", "sonar.issues.defaultAssigneeLogin", user.getLogin());
+    analyseProject(SAMPLE_PROJECT_KEY, organization);
 
-    analyseProject(SAMPLE_PROJECT_KEY, org1.getKey());
-
-    assertThat(issueRule.getRandomIssue().getAssignee()).isEqualTo(user.getLogin());
+    assertThat(getRandomIssue().getAssignee()).isEqualTo(user.getLogin());
   }
 
   @Test
-  public void does_not_auto_assign_issues_to_user_if_default_assignee_is_not_member_of_project_organization() {
-    tester.organizations().addMember(org2, user);
-    provisionProject(SAMPLE_PROJECT_KEY, org1.getKey());
-    setServerProperty(orchestrator, "sample", "sonar.issues.defaultAssigneeLogin", user.getLogin());
+  public void do_not_auto_assign_issues_to_default_assignee_if_not_member_of_project_organization() {
+    Organization organization1 = tester.organizations().generate();
+    Organization organization2 = tester.organizations().generate();
+    User user = tester.users().generateMember(organization2);
+    provisionProjectAndAssociateItToQProfile(SAMPLE_PROJECT_KEY, organization1);
+    tester.settings().setProjectSetting("sample", "sonar.issues.defaultAssigneeLogin", user.getLogin());
+
+    analyseProject(SAMPLE_PROJECT_KEY, organization1);
 
-    analyseProject(SAMPLE_PROJECT_KEY, org1.getKey());
+    assertThat(getRandomIssue().hasAssignee()).isFalse();
+  }
 
-    assertThat(issueRule.getRandomIssue().hasAssignee()).isFalse();
+  /**
+   * SONAR-10302
+   */
+  @Test
+  public void do_not_auto_assign_issues_to_user_if_assignee_is_not_member_of_project_organization() {
+    Organization organization1 = tester.organizations().generate();
+    Organization organization2 = tester.organizations().generate();
+    User fabrice = tester.users().generateMember(organization1, u -> u.setScmAccount(singletonList("fabrice")));
+    // Simon is not member of project's organization, no issue should be assigned to him
+    User simon = tester.users().generateMember(organization2, u -> u.setScmAccount(singletonList("simon")));
+    provisionProjectAndAssociateItToQProfile(SAMPLE_PROJECT_KEY, organization1);
+
+    analyseProject(SAMPLE_PROJECT_KEY, organization1);
+
+    Set<String> assignees = tester.wsClient().issues().search(new SearchRequest().setComponentKeys(singletonList(SAMPLE_PROJECT_KEY))).getIssuesList()
+      .stream()
+      .map(Issue::getAssignee)
+      .filter(s -> !s.isEmpty())
+      .collect(Collectors.toSet());
+    assertThat(assignees)
+      .containsOnly(fabrice.getLogin())
+      .doesNotContain(simon.getLogin());
   }
 
   @Test
   public void assign_issue_to_user_being_member_of_same_organization_as_project_issue_organization() {
-    tester.organizations().addMember(org1, user);
-    provisionAndAnalyseProject(SAMPLE_PROJECT_KEY, org1.getKey());
-    Issue issue = issueRule.getRandomIssue();
+    Organization organization = tester.organizations().generate();
+    User user = tester.users().generateMember(organization);
+    provisionAndAnalyseProject(SAMPLE_PROJECT_KEY, organization);
+    Issue issue = getRandomIssue();
 
     assignIssueTo(issue, user);
 
-    assertThat(issueRule.getByKey(issue.getKey()).getAssignee()).isEqualTo(user.getLogin());
+    assertThat(getByKey(issue.getKey()).getAssignee()).isEqualTo(user.getLogin());
   }
 
   @Test
   public void fail_to_assign_issue_to_user_not_being_member_of_same_organization_as_project_issue_organization() {
-    tester.organizations().addMember(org2, user);
-    provisionAndAnalyseProject(SAMPLE_PROJECT_KEY, org1.getKey());
-    Issue issue = issueRule.getRandomIssue();
+    Organization organization1 = tester.organizations().generate();
+    Organization organization2 = tester.organizations().generate();
+    User user = tester.users().generateMember(organization2);
+    provisionAndAnalyseProject(SAMPLE_PROJECT_KEY, organization1);
+    Issue issue = getRandomIssue();
 
     expectHttpError(400,
-      format("User '%s' is not member of organization '%s'", user.getLogin(), org1.getKey()),
+      format("User '%s' is not member of organization '%s'", user.getLogin(), organization1.getKey()),
       () -> assignIssueTo(issue, user));
   }
 
   @Test
   public void bulk_assign_issues_to_user_being_only_member_of_same_organization_as_project_issue_organization() {
-    restoreProfile(orchestrator, getClass().getResource("/organization/IssueAssignTest/one-issue-per-file-profile.xml"), org2.getKey());
+    Organization organization1 = tester.organizations().generate();
+    Organization organization2 = tester.organizations().generate();
     // User is only member of org1, not of org2
-    tester.organizations().addMember(org1, user);
-    provisionAndAnalyseProject(SAMPLE_PROJECT_KEY, org1.getKey());
-    provisionAndAnalyseProject("sample2", org2.getKey());
-    List<String> issues = issueRule.search(new SearchRequest()).getIssuesList().stream().map(Issue::getKey).collect(Collectors.toList());
+    User user = tester.users().generateMember(organization1);
+
+    restoreProfile(orchestrator, getClass().getResource("/organization/IssueAssignTest/one-issue-per-file-profile.xml"), organization2.getKey());
+
+    provisionAndAnalyseProject(SAMPLE_PROJECT_KEY, organization1);
+    provisionAndAnalyseProject("sample2", organization2);
+    List<String> issues = tester.wsClient().issues().search(new SearchRequest()).getIssuesList().stream().map(Issue::getKey).collect(Collectors.toList());
 
     Issues.BulkChangeWsResponse response = tester.wsClient().issues()
       .bulkChange(new BulkChangeRequest().setIssues(issues).setAssign(singletonList(user.getLogin())));
 
     assertThat(response.getIgnored()).isGreaterThan(0);
-    assertThat(issueRule.search(new SearchRequest().setProjects(singletonList("sample"))).getIssuesList()).extracting(Issue::getAssignee)
+    assertThat(tester.wsClient().issues().search(new SearchRequest().setProjects(singletonList("sample"))).getIssuesList()).extracting(Issue::getAssignee)
       .containsOnly(user.getLogin());
-    assertThat(issueRule.search(new SearchRequest().setProjects(singletonList("sample2"))).getIssuesList()).extracting(Issue::hasAssignee)
+    assertThat(tester.wsClient().issues().search(new SearchRequest().setProjects(singletonList("sample2"))).getIssuesList()).extracting(Issue::hasAssignee)
       .containsOnly(false);
   }
 
   @Test
   public void single_assign_search_show_only_members_in_global_issues() {
-    tester.organizations().addMember(org1, user);
+    Organization organization = tester.organizations().generate();
+    User user = tester.users().generateMember(organization);
     User otherUser = tester.users().generate();
-    provisionAndAnalyseProject(SAMPLE_PROJECT_KEY, org1.getKey());
+    provisionAndAnalyseProject(SAMPLE_PROJECT_KEY, organization);
+
     IssuesPage page = tester.openBrowser().logIn().submitCredentials(user.getLogin()).openIssues();
     page.getFirstIssue()
       .shouldAllowAssign()
@@ -150,10 +167,11 @@ public class OrganizationIssueAssignTest {
 
   @Test
   public void bulk_assign_search_only_members_of_organization_in_project_issues() {
-    tester.organizations().addMember(org1, user);
+    Organization organization = tester.organizations().generate();
+    User user = tester.users().generateMember(organization);
     User otherUser = tester.users().generate();
+    provisionAndAnalyseProject(SAMPLE_PROJECT_KEY, organization);
 
-    provisionAndAnalyseProject(SAMPLE_PROJECT_KEY, org1.getKey());
     IssuesPage page = tester.openBrowser()
       .logIn().submitCredentials(user.getLogin())
       .openComponentIssues(SAMPLE_PROJECT_KEY);
@@ -165,9 +183,10 @@ public class OrganizationIssueAssignTest {
 
   @Test
   public void bulk_assign_search_all_users_in_global_issues() {
-    tester.organizations().addMember(org1, user);
+    Organization organization = tester.organizations().generate();
+    User user = tester.users().generateMember(organization);
     User otherUser = tester.users().generate();
-    provisionAndAnalyseProject(SAMPLE_PROJECT_KEY, org1.getKey());
+    provisionAndAnalyseProject(SAMPLE_PROJECT_KEY, organization);
     IssuesPage page = tester.openBrowser()
       .logIn().submitCredentials(user.getLogin())
       .openIssues();
@@ -177,37 +196,44 @@ public class OrganizationIssueAssignTest {
       .bulkChangeAssigneeSearchCount(otherUser.getLogin(), 1);
   }
 
-  private void provisionAndAnalyseProject(String projectKey, String organization) {
-    provisionProject(projectKey, organization);
+  private void provisionAndAnalyseProject(String projectKey, Organization organization) {
+    provisionProjectAndAssociateItToQProfile(projectKey, organization);
     analyseProject(projectKey, organization);
   }
 
-  private void provisionProject(String projectKey, String organization) {
-    tester.wsClient().projects().create(
-      new CreateRequest()
-        .setProject(projectKey)
-        .setName(projectKey)
-        .setOrganization(organization));
+  private void provisionProjectAndAssociateItToQProfile(String projectKey, Organization organization) {
+    Project project = tester.projects().provision(organization, p -> p.setProject(projectKey));
+    QualityProfile profile = tester.qProfiles().createXooProfile(organization);
+    tester.qProfiles()
+      .activateRule(profile, "xoo:OneIssuePerLine")
+      .assignQProfileToProject(profile, project);
   }
 
-  private void analyseProject(String projectKey, String organization) {
-    addQualityProfileToProject(organization, projectKey);
+  private void analyseProject(String projectKey, Organization organization) {
     runProjectAnalysis(orchestrator, "issue/xoo-with-scm",
       "sonar.projectKey", projectKey,
-      "sonar.organization", organization,
+      "sonar.organization", organization.getKey(),
       "sonar.login", "admin",
       "sonar.password", "admin",
       "sonar.scm.disabled", "false",
       "sonar.scm.provider", "xoo");
   }
 
-  private void addQualityProfileToProject(String organization, String projectKey) {
-    tester.wsClient().qualityprofiles().addProject(
-      new AddProjectRequest()
-        .setProject(projectKey)
-        .setOrganization(organization)
-        .setLanguage("xoo")
-        .setQualityProfile("one-issue-per-file-profile"));
+  private Issues.Issue getByKey(String issueKey) {
+    return tester.wsClient().issues().search(
+      new SearchRequest()
+        .setComponentKeys(singletonList(SAMPLE_PROJECT_KEY))
+        .setIssues(singletonList(issueKey)))
+      .getIssuesList()
+      .get(0);
+  }
+
+  private Issues.Issue getRandomIssue() {
+    return tester.wsClient().issues().search(
+      new SearchRequest()
+        .setComponentKeys(singletonList(SAMPLE_PROJECT_KEY)))
+      .getIssuesList()
+      .get(0);
   }
 
   private Issues.AssignResponse assignIssueTo(Issue issue, User u) {
diff --git a/tests/src/test/java/org/sonarqube/tests/issue/OrganizationIssueSuite.java b/tests/src/test/java/org/sonarqube/tests/issue/OrganizationIssueSuite.java
new file mode 100644 (file)
index 0000000..2babb0d
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonarqube.tests.issue;
+
+import com.sonar.orchestrator.Orchestrator;
+import org.junit.ClassRule;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+import static util.ItUtils.xooPlugin;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+  OrganizationIssueAssignTest.class
+})
+public class OrganizationIssueSuite {
+
+  @ClassRule
+  public static final Orchestrator ORCHESTRATOR = Orchestrator.builderEnv()
+    .addPlugin(xooPlugin())
+
+    // reduce memory for Elasticsearch to 128M
+    .setServerProperty("sonar.search.javaOpts", "-Xms128m -Xmx128m")
+
+    .build();
+
+}