From 580514b445c5489bf376abfb1f8bfd012eb0d48b Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Wed, 26 Jul 2017 14:47:18 +0200 Subject: [PATCH] SONAR-9551 WS api/issues/search search by application --- .../sonar/server/issue/IssueQueryFactory.java | 84 +++--- .../server/issue/IssueQueryFactoryTest.java | 43 +++ .../ws/SearchActionComponentsMediumTest.java | 4 + .../issue/ws/SearchActionComponentsTest.java | 260 ++++++++++++++++++ 4 files changed, 359 insertions(+), 32 deletions(-) create mode 100644 server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java index b988c3d65c7..83b52558167 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java @@ -25,14 +25,13 @@ import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -59,11 +58,13 @@ import org.sonarqube.ws.client.issue.SearchWsRequest; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Lists.newArrayList; import static java.lang.String.format; +import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static org.sonar.api.utils.DateUtils.longToDate; import static org.sonar.api.utils.DateUtils.parseDateOrDateTime; import static org.sonar.api.utils.DateUtils.parseEndingDateOrDateTime; import static org.sonar.api.utils.DateUtils.parseStartingDateOrDateTime; +import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex; import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; import static org.sonar.server.ws.WsUtils.checkRequest; import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENTS; @@ -125,12 +126,7 @@ public class IssueQueryFactory { addComponentParameters(builder, dbSession, effectiveOnComponentOnly, allComponentUuids, - request.getProjectUuids(), - request.getProjectKeys(), - request.getModuleUuids(), - request.getDirectories(), - request.getFileUuids(), - request.getAuthors()); + request); builder.createdAfter(buildCreatedAfterFromRequest(dbSession, request, allComponentUuids)); @@ -205,12 +201,12 @@ public class IssueQueryFactory { } private boolean mergeDeprecatedComponentParameters(DbSession session, @Nullable Boolean onComponentOnly, - @Nullable Collection components, - @Nullable Collection componentUuids, - @Nullable Collection componentKeys, - @Nullable Collection componentRootUuids, - @Nullable Collection componentRoots, - Set allComponentUuids) { + @Nullable Collection components, + @Nullable Collection componentUuids, + @Nullable Collection componentKeys, + @Nullable Collection componentRootUuids, + @Nullable Collection componentRoots, + Set allComponentUuids) { boolean effectiveOnComponentOnly = false; checkArgument(atMostOneNonNullElement(components, componentUuids, componentKeys, componentRootUuids, componentRoots), @@ -243,13 +239,9 @@ public class IssueQueryFactory { } private void addComponentParameters(IssueQuery.Builder builder, DbSession session, - boolean onComponentOnly, - Collection componentUuids, - @Nullable Collection projectUuids, @Nullable Collection projectKeys, - @Nullable Collection moduleUuids, - @Nullable Collection directories, - @Nullable Collection fileUuids, - @Nullable Collection authors) { + boolean onComponentOnly, + Collection componentUuids, + SearchWsRequest request) { builder.onComponentOnly(onComponentOnly); if (onComponentOnly) { @@ -257,46 +249,48 @@ public class IssueQueryFactory { return; } - builder.authors(authors); + builder.authors(request.getAuthors()); + List projectUuids = request.getProjectUuids(); + List projectKeys = request.getProjectKeys(); checkArgument(projectUuids == null || projectKeys == null, "Parameters projects and projectUuids cannot be set simultaneously"); if (projectUuids != null) { builder.projectUuids(projectUuids); } else if (projectKeys != null) { builder.projectUuids(convertComponentKeysToUuids(session, projectKeys)); } - builder.moduleUuids(moduleUuids); - builder.directories(directories); - builder.fileUuids(fileUuids); + builder.moduleUuids(request.getModuleUuids()); + builder.directories(request.getDirectories()); + builder.fileUuids(request.getFileUuids()); if (!componentUuids.isEmpty()) { - addComponentsBasedOnQualifier(builder, session, componentUuids); + addComponentsBasedOnQualifier(builder, session, componentUuids, request); } } - private void addComponentsBasedOnQualifier(IssueQuery.Builder builder, DbSession session, Collection componentUuids) { + private void addComponentsBasedOnQualifier(IssueQuery.Builder builder, DbSession dbSession, Collection componentUuids, SearchWsRequest request) { if (componentUuids.isEmpty()) { builder.componentUuids(componentUuids); return; } - List components = dbClient.componentDao().selectByUuids(session, componentUuids); + List components = dbClient.componentDao().selectByUuids(dbSession, componentUuids); if (components.isEmpty()) { builder.componentUuids(componentUuids); return; } Set qualifiers = components.stream().map(ComponentDto::qualifier).collect(MoreCollectors.toHashSet()); - if (qualifiers.size() > 1) { - throw new IllegalArgumentException("All components must have the same qualifier, found " + Joiner.on(',').join(qualifiers)); - } + checkArgument(qualifiers.size() == 1, "All components must have the same qualifier, found %s", String.join(",", qualifiers)); String qualifier = qualifiers.iterator().next(); switch (qualifier) { case Qualifiers.VIEW: case Qualifiers.SUBVIEW: - case Qualifiers.APP: addViewsOrSubViews(builder, componentUuids); break; + case Qualifiers.APP: + addApplications(builder, dbSession, components, request); + break; case Qualifiers.PROJECT: builder.projectUuids(componentUuids); break; @@ -326,6 +320,32 @@ public class IssueQueryFactory { builder.viewUuids(filteredViewUuids); } + private void addApplications(IssueQuery.Builder builder, DbSession dbSession, List applications, SearchWsRequest request) { + Set authorizedApplicationUuids = applications.stream() + .filter(app -> userSession.hasComponentPermission(UserRole.USER, app)) + .map(ComponentDto::uuid) + .collect(MoreCollectors.toSet()); + + builder.viewUuids(authorizedApplicationUuids.isEmpty() ? singleton(UNKNOWN) : authorizedApplicationUuids); + addCreatedAfterByProjects(builder, dbSession, request, authorizedApplicationUuids); + } + + private void addCreatedAfterByProjects(IssueQuery.Builder builder, DbSession dbSession, SearchWsRequest request, Set applicationUuids) { + if (request.getSinceLeakPeriod() == null || !request.getSinceLeakPeriod()) { + return; + } + + Set projectUuids = applicationUuids.stream() + .flatMap(app -> dbClient.componentDao().selectProjectsFromView(dbSession, app, app).stream()) + .collect(MoreCollectors.toSet()); + + Map leakByProjects = dbClient.snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, projectUuids) + .stream() + .filter(s -> s.getPeriodDate() != null) + .collect(uniqueIndex(SnapshotDto::getComponentUuid, s -> longToDate(s.getPeriodDate()))); + builder.createdAfterByProjectUuids(leakByProjects); + } + private static void addDirectories(IssueQuery.Builder builder, List directories) { Collection directoryModuleUuids = new HashSet<>(); Collection directoryPaths = new HashSet<>(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryFactoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryFactoryTest.java index 3bc04036e0f..32dca3502e1 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryFactoryTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryFactoryTest.java @@ -30,6 +30,7 @@ import org.sonar.api.utils.System2; import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; +import org.sonar.db.component.SnapshotDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.tester.UserSessionRule; @@ -37,10 +38,14 @@ import org.sonarqube.ws.client.issue.SearchWsRequest; import static com.google.common.collect.Lists.newArrayList; 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; +import static org.sonar.api.utils.DateUtils.addDays; +import static org.sonar.db.component.ComponentTesting.newProjectCopy; public class IssueQueryFactoryTest { @@ -199,6 +204,44 @@ public class IssueQueryFactoryTest { assertThat(query.onComponentOnly()).isFalse(); } + @Test + public void application_search_project_issues() { + ComponentDto project1 = db.components().insertPublicProject(); + ComponentDto project2 = db.components().insertPublicProject(); + ComponentDto application = db.components().insertApplication(db.getDefaultOrganization()); + db.components().insertComponents(newProjectCopy("PC1", project1, application)); + db.components().insertComponents(newProjectCopy("PC2", project2, application)); + userSession.registerComponents(application, project1, project2); + + IssueQuery result = underTest.create(new SearchWsRequest().setComponentUuids(singletonList(application.uuid()))); + + assertThat(result.viewUuids()).containsExactlyInAnyOrder(application.uuid()); + } + + @Test + public void application_search_project_issues_on_leak() { + Date now = new Date(); + ComponentDto project1 = db.components().insertPublicProject(); + SnapshotDto analysis1 = db.components().insertSnapshot(project1, s -> s.setPeriodDate(addDays(now, -14).getTime())); + ComponentDto project2 = db.components().insertPublicProject(); + SnapshotDto analysis2 = db.components().insertSnapshot(project2, s -> s.setPeriodDate(null)); + ComponentDto project3 = db.components().insertPublicProject(); + ComponentDto application = db.components().insertApplication(db.getDefaultOrganization()); + db.components().insertComponents(newProjectCopy("PC1", project1, application)); + db.components().insertComponents(newProjectCopy("PC2", project2, application)); + db.components().insertComponents(newProjectCopy("PC3", project3, application)); + userSession.registerComponents(application, project1, project2, project3); + + IssueQuery result = underTest.create(new SearchWsRequest() + .setComponentUuids(singletonList(application.uuid())) + .setSinceLeakPeriod(true) + ); + + assertThat(result.createdAfterByProjectUuids()).containsOnly( + entry(project1.uuid(), new Date(analysis1.getPeriodDate()))); + assertThat(result.viewUuids()).containsExactlyInAnyOrder(application.uuid()); + } + @Test public void return_empty_results_if_not_allowed_to_search_for_subview() { ComponentDto view = db.components().insertView(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java index 4e5ddce0cd3..52df082d7bd 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java @@ -67,6 +67,10 @@ import static org.sonar.db.component.SnapshotTesting.newAnalysis; import static org.sonarqube.ws.client.issue.IssuesWsParameters.ACTION_SEARCH; import static org.sonarqube.ws.client.issue.IssuesWsParameters.CONTROLLER_ISSUES; +/** + * @deprecated use {@link SearchActionComponentsTest} instead + */ +@Deprecated public class SearchActionComponentsMediumTest { @ClassRule diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java new file mode 100644 index 00000000000..7fc36cb2b3e --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java @@ -0,0 +1,260 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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.sonar.server.issue.ws; + +import java.util.Date; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.resources.Languages; +import org.sonar.api.utils.Durations; +import org.sonar.api.utils.System2; +import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.issue.IssueDto; +import org.sonar.server.es.EsTester; +import org.sonar.server.issue.IssueQueryFactory; +import org.sonar.server.issue.index.IssueIndex; +import org.sonar.server.issue.index.IssueIndexDefinition; +import org.sonar.server.issue.index.IssueIndexer; +import org.sonar.server.issue.index.IssueIteratorFactory; +import org.sonar.server.permission.index.AuthorizationTypeSupport; +import org.sonar.server.permission.index.PermissionIndexerTester; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.view.index.ViewIndexDefinition; +import org.sonar.server.view.index.ViewIndexer; +import org.sonar.server.ws.WsActionTester; +import org.sonar.server.ws.WsResponseCommonFormat; +import org.sonarqube.ws.Issues.Issue; +import org.sonarqube.ws.Issues.SearchWsResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.api.utils.DateUtils.addDays; +import static org.sonar.db.component.ComponentTesting.newProjectCopy; +import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENT_KEYS; +import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECT_KEYS; +import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SINCE_LEAK_PERIOD; + +public class SearchActionComponentsTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + @Rule + public DbTester db = DbTester.create(); + @Rule + public EsTester es = new EsTester( + new IssueIndexDefinition(new MapSettings().asConfig()), + new ViewIndexDefinition(new MapSettings().asConfig())); + + private IssueIndex index = new IssueIndex(es.client(), System2.INSTANCE, userSession, new AuthorizationTypeSupport(userSession)); + private IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient())); + private ViewIndexer viewIndexer = new ViewIndexer(db.getDbClient(), es.client()); + private PermissionIndexerTester permissionIndexer = new PermissionIndexerTester(es, issueIndexer); + private IssueQueryFactory issueQueryFactory = new IssueQueryFactory(db.getDbClient(), System2.INSTANCE, userSession); + private SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSession, db.getDbClient(), null, null); + private SearchResponseFormat searchResponseFormat = new SearchResponseFormat(new Durations(), new WsResponseCommonFormat(new Languages()), new Languages(), + new AvatarResolverImpl()); + + private WsActionTester ws = new WsActionTester(new SearchAction(userSession, index, issueQueryFactory, searchResponseLoader, searchResponseFormat)); + + @Test + public void search_by_application_key() throws Exception { + ComponentDto project1 = db.components().insertPublicProject(); + ComponentDto project2 = db.components().insertPublicProject(); + ComponentDto application = db.components().insertApplication(db.getDefaultOrganization()); + db.components().insertComponents(newProjectCopy("PC1", project1, application)); + db.components().insertComponents(newProjectCopy("PC2", project2, application)); + IssueDto issue1 = db.issues().insertIssue(i -> i.setProject(project1)); + IssueDto issue2 = db.issues().insertIssue(i -> i.setProject(project2)); + userSession.registerComponents(application, project1, project2); + permissionIndexer.allowOnlyAnyone(project1); + permissionIndexer.allowOnlyAnyone(project2); + indexIssuesAndViews(); + + SearchWsResponse result = ws.newRequest() + .setParam(PARAM_COMPONENT_KEYS, application.getKey()) + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()).extracting(Issue::getKey) + .containsExactlyInAnyOrder(issue1.getKey(), issue2.getKey()); + } + + @Test + public void ignore_application_without_browse_permission() { + ComponentDto project = db.components().insertPublicProject(); + ComponentDto application = db.components().insertApplication(db.getDefaultOrganization()); + db.components().insertComponents(newProjectCopy("PC1", project, application)); + db.issues().insertIssue(i -> i.setProject(project)); + userSession.registerComponents(project); + permissionIndexer.allowOnlyAnyone(project); + indexIssuesAndViews(); + + SearchWsResponse result = ws.newRequest() + .setParam(PARAM_COMPONENT_KEYS, application.getKey()) + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()).isEmpty(); + } + + @Test + public void search_application_without_projects() { + ComponentDto project = db.components().insertPublicProject(); + ComponentDto application = db.components().insertApplication(db.getDefaultOrganization()); + db.issues().insertIssue(i -> i.setProject(project)); + userSession.registerComponents(application, project); + permissionIndexer.allowOnlyAnyone(project); + indexIssuesAndViews(); + + SearchWsResponse result = ws.newRequest() + .setParam(PARAM_COMPONENT_KEYS, application.getKey()) + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()).isEmpty(); + } + + @Test + public void search_by_application_and_by_leak() throws Exception { + Date now = new Date(); + ComponentDto application = db.components().insertApplication(db.getDefaultOrganization()); + // Project 1 + ComponentDto project1 = db.components().insertPublicProject(); + db.components().insertSnapshot(project1, s -> s.setPeriodDate(addDays(now, -14).getTime())); + db.components().insertComponents(newProjectCopy("PC1", project1, application)); + IssueDto project1Issue1 = db.issues().insertIssue(i -> i.setProject(project1).setIssueCreationDate(addDays(now, -10))); + IssueDto project1Issue2 = db.issues().insertIssue(i -> i.setProject(project1).setIssueCreationDate(addDays(now, -20))); + // Project 2 + ComponentDto project2 = db.components().insertPublicProject(); + db.components().insertSnapshot(project2, s -> s.setPeriodDate(addDays(now, -25).getTime())); + db.components().insertComponents(newProjectCopy("PC2", project2, application)); + IssueDto project2Issue1 = db.issues().insertIssue(i -> i.setProject(project2).setIssueCreationDate(addDays(now, -15))); + IssueDto project2Issue2 = db.issues().insertIssue(i -> i.setProject(project2).setIssueCreationDate(addDays(now, -30))); + // Permissions and index + userSession.registerComponents(application, project1, project2); + permissionIndexer.allowOnlyAnyone(project1); + permissionIndexer.allowOnlyAnyone(project2); + indexIssuesAndViews(); + + SearchWsResponse result = ws.newRequest() + .setParam(PARAM_COMPONENT_KEYS, application.getKey()) + .setParam(PARAM_SINCE_LEAK_PERIOD, "true") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()).extracting(Issue::getKey) + .containsExactlyInAnyOrder(project1Issue1.getKey(), project2Issue1.getKey()) + .doesNotContain(project1Issue2.getKey(), project2Issue2.getKey()); + } + + @Test + public void search_by_application_and_project() throws Exception { + ComponentDto project1 = db.components().insertPublicProject(); + ComponentDto project2 = db.components().insertPublicProject(); + ComponentDto application = db.components().insertApplication(db.getDefaultOrganization()); + db.components().insertComponents(newProjectCopy("PC1", project1, application)); + db.components().insertComponents(newProjectCopy("PC2", project2, application)); + IssueDto issue1 = db.issues().insertIssue(i -> i.setProject(project1)); + IssueDto issue2 = db.issues().insertIssue(i -> i.setProject(project2)); + userSession.registerComponents(application, project1, project2); + permissionIndexer.allowOnlyAnyone(project1); + permissionIndexer.allowOnlyAnyone(project2); + indexIssuesAndViews(); + + SearchWsResponse result = ws.newRequest() + .setParam(PARAM_COMPONENT_KEYS, application.getKey()) + .setParam(PARAM_PROJECT_KEYS, project1.getKey()) + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()).extracting(Issue::getKey) + .containsExactlyInAnyOrder(issue1.getKey()) + .doesNotContain(issue2.getKey()); + } + + @Test + public void search_by_application_and_project_and_leak() throws Exception { + Date now = new Date(); + ComponentDto application = db.components().insertApplication(db.getDefaultOrganization()); + // Project 1 + ComponentDto project1 = db.components().insertPublicProject(); + db.components().insertSnapshot(project1, s -> s.setPeriodDate(addDays(now, -14).getTime())); + db.components().insertComponents(newProjectCopy("PC1", project1, application)); + IssueDto project1Issue1 = db.issues().insertIssue(i -> i.setProject(project1).setIssueCreationDate(addDays(now, -10))); + IssueDto project1Issue2 = db.issues().insertIssue(i -> i.setProject(project1).setIssueCreationDate(addDays(now, -20))); + // Project 2 + ComponentDto project2 = db.components().insertPublicProject(); + db.components().insertSnapshot(project2, s -> s.setPeriodDate(addDays(now, -25).getTime())); + db.components().insertComponents(newProjectCopy("PC2", project2, application)); + IssueDto project2Issue1 = db.issues().insertIssue(i -> i.setProject(project2).setIssueCreationDate(addDays(now, -15))); + IssueDto project2Issue2 = db.issues().insertIssue(i -> i.setProject(project2).setIssueCreationDate(addDays(now, -30))); + // Permissions and index + userSession.registerComponents(application, project1, project2); + permissionIndexer.allowOnlyAnyone(project1); + permissionIndexer.allowOnlyAnyone(project2); + indexIssuesAndViews(); + + SearchWsResponse result = ws.newRequest() + .setParam(PARAM_COMPONENT_KEYS, application.getKey()) + .setParam(PARAM_PROJECT_KEYS, project1.getKey()) + .setParam(PARAM_SINCE_LEAK_PERIOD, "true") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()).extracting(Issue::getKey) + .containsExactlyInAnyOrder(project1Issue1.getKey()) + .doesNotContain(project1Issue2.getKey(), project2Issue1.getKey(), project2Issue2.getKey()); + } + + @Test + public void search_by_application_and_by_leak_when_one_project_has_no_leak() throws Exception { + Date now = new Date(); + ComponentDto application = db.components().insertApplication(db.getDefaultOrganization()); + // Project 1 + ComponentDto project1 = db.components().insertPublicProject(); + db.components().insertSnapshot(project1, s -> s.setPeriodDate(addDays(now, -14).getTime())); + db.components().insertComponents(newProjectCopy("PC1", project1, application)); + IssueDto project1Issue1 = db.issues().insertIssue(i -> i.setProject(project1).setIssueCreationDate(addDays(now, -10))); + IssueDto project1Issue2 = db.issues().insertIssue(i -> i.setProject(project1).setIssueCreationDate(addDays(now, -20))); + // Project 2, without leak => no issue form it should be returned + ComponentDto project2 = db.components().insertPublicProject(); + db.components().insertSnapshot(project2, s -> s.setPeriodDate(null)); + db.components().insertComponents(newProjectCopy("PC2", project2, application)); + IssueDto project2Issue1 = db.issues().insertIssue(i -> i.setProject(project2).setIssueCreationDate(addDays(now, -15))); + IssueDto project2Issue2 = db.issues().insertIssue(i -> i.setProject(project2).setIssueCreationDate(addDays(now, -30))); + // Permissions and index + userSession.registerComponents(application, project1, project2); + permissionIndexer.allowOnlyAnyone(project1); + permissionIndexer.allowOnlyAnyone(project2); + indexIssuesAndViews(); + + SearchWsResponse result = ws.newRequest() + .setParam(PARAM_COMPONENT_KEYS, application.getKey()) + .setParam(PARAM_SINCE_LEAK_PERIOD, "true") + .executeProtobuf(SearchWsResponse.class); + + assertThat(result.getIssuesList()).extracting(Issue::getKey) + .containsExactlyInAnyOrder(project1Issue1.getKey()) + .doesNotContain(project1Issue2.getKey(), project2Issue1.getKey(), project2Issue2.getKey()); + } + + private void indexIssuesAndViews() { + issueIndexer.indexOnStartup(null); + viewIndexer.indexOnStartup(null); + } +} -- 2.39.5