*/
package org.sonar.ce.task.projectanalysis.component;
+import java.util.Set;
+
public interface FileStatuses {
/**
* A file is unchanged compared to the last analysis if it was detected as unchanged by the scanner and
boolean isUnchanged(Component component);
boolean isDataUnchanged(Component component);
+
+ Set<String> getFileUuidsMarkedAsUnchanged();
+
}
import org.sonar.db.source.FileHashesDto;
import static com.google.common.base.Preconditions.checkState;
+import static java.util.Collections.unmodifiableSet;
import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER;
public class FileStatusesImpl implements FileStatuses {
return fileUuidsMarkedAsUnchanged.contains(component.getUuid());
}
+ @Override
+ public Set<String> getFileUuidsMarkedAsUnchanged() {
+ failIfNotInitialized();
+ return unmodifiableSet(fileUuidsMarkedAsUnchanged);
+ }
+
private boolean hashEquals(Component component) {
Optional<String> dbHash = previousSourceHashRepository.getDbFile(component).map(FileHashesDto::getSrcHash);
return dbHash.map(hash -> hash.equals(sourceHashRepository.getRawSourceHash(component))).orElse(false);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.ce.task.projectanalysis.component;
+
+
+import java.util.Set;
+
+/**
+ * No operation implementation of {@link FileStatuses}
+ */
+public final class NopFileStatuses implements FileStatuses {
+
+
+ @Override
+ public boolean isUnchanged(Component component) {
+ return false;
+ }
+
+ @Override
+ public boolean isDataUnchanged(Component component) {
+ return false;
+ }
+
+ @Override
+ public Set<String> getFileUuidsMarkedAsUnchanged() {
+ return Set.of();
+ }
+}
*/
package org.sonar.ce.task.projectanalysis.step;
+import java.util.Set;
+import java.util.function.Consumer;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
+import org.sonar.ce.task.projectanalysis.component.FileStatuses;
import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
import org.sonar.ce.task.step.ComputationStep;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
import org.sonar.server.es.ProjectIndexer;
public class IndexAnalysisStep implements ComputationStep {
private static final Logger LOGGER = Loggers.get(IndexAnalysisStep.class);
private final TreeRootHolder treeRootHolder;
+ private final FileStatuses fileStatuses;
private final ProjectIndexer[] indexers;
+ private final DbClient dbClient;
- public IndexAnalysisStep(TreeRootHolder treeRootHolder, ProjectIndexer... indexers) {
+ public IndexAnalysisStep(TreeRootHolder treeRootHolder, FileStatuses fileStatuses, DbClient dbClient,ProjectIndexer... indexers) {
this.treeRootHolder = treeRootHolder;
+ this.fileStatuses = fileStatuses;
this.indexers = indexers;
+ this.dbClient = dbClient;
}
@Override
public void execute(ComputationStep.Context context) {
String branchUuid = treeRootHolder.getRoot().getUuid();
+ Consumer<ProjectIndexer> projectIndexerConsumer = getProjectIndexerConsumer(branchUuid);
for (ProjectIndexer indexer : indexers) {
LOGGER.debug("Call {}", indexer);
- indexer.indexOnAnalysis(branchUuid);
+ projectIndexerConsumer.accept(indexer);
+ }
+ }
+
+ private Consumer<ProjectIndexer> getProjectIndexerConsumer(String branchUuid) {
+ Set<String> fileUuidsMarkedAsUnchanged = fileStatuses.getFileUuidsMarkedAsUnchanged();
+ return isBranchNeedIssueSync(branchUuid)
+ ? (indexer -> indexer.indexOnAnalysis(branchUuid))
+ : (indexer -> indexer.indexOnAnalysis(branchUuid, fileUuidsMarkedAsUnchanged));
+ }
+
+ private boolean isBranchNeedIssueSync(String branchUuid) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ return dbClient.branchDao().isBranchNeedIssueSync(dbSession, branchUuid);
}
}
import org.sonar.db.source.FileHashesDto;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
verify(previousSourceHashRepository).getDbFile(file1);
}
+ @Test
+ public void getFileUuidsMarkedAsUnchanged_whenNotInitialized_shouldFail() {
+ assertThatThrownBy(fileStatuses::getFileUuidsMarkedAsUnchanged)
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Not initialized");
+ }
+
+ @Test
+ public void getFileUuidsMarkedAsUnchanged_shouldReturnMarkAsUnchangedFileUuids() {
+ Component file1 = ReportComponent.builder(Component.Type.FILE, 2, "FILE1_KEY").setStatus(Component.Status.SAME)
+ .setFileAttributes(new FileAttributes(false, null, 10, true, null)).build();
+ Component file2 = ReportComponent.builder(Component.Type.FILE, 3, "FILE2_KEY").setStatus(Component.Status.SAME).build();
+ addDbFileHash(file1, "hash1");
+ addDbFileHash(file2, "hash2");
+ addReportFileHash(file1, "hash1");
+ addReportFileHash(file2, "hash2");
+ Component project = ReportComponent.builder(Component.Type.PROJECT, 1)
+ .setUuid(PROJECT_UUID)
+ .setKey(PROJECT_KEY)
+ .addChildren(file1, file2)
+ .build();
+ treeRootHolder.setRoot(project);
+ fileStatuses.initialize();
+
+ assertThat(fileStatuses.getFileUuidsMarkedAsUnchanged()).contains(file1.getUuid());
+ }
+
private void addDbFileHash(Component file, String hash) {
FileHashesDto fileHashesDto = new FileHashesDto().setSrcHash(hash);
when(previousSourceHashRepository.getDbFile(file)).thenReturn(Optional.of(fileHashesDto));
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.ce.task.projectanalysis.component;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class NopFileStatusesTest {
+
+ private final NopFileStatuses nopFileStatuses = new NopFileStatuses();
+
+ private final Component component = Mockito.mock(Component.class);
+
+ @Test
+ public void isUnchanged() {
+ assertThat(nopFileStatuses.isUnchanged(component)).isFalse();
+ }
+
+ @Test
+ public void isDataUnchanged() {
+ assertThat(nopFileStatuses.isDataUnchanged(component)).isFalse();
+ }
+
+ @Test
+ public void getFileUuidsMarkedAsUnchanged() {
+ assertThat(nopFileStatuses.getFileUuidsMarkedAsUnchanged()).isEmpty();
+ }
+}
*/
package org.sonar.ce.task.projectanalysis.step;
+import java.util.Set;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.FileStatuses;
import org.sonar.ce.task.projectanalysis.component.ReportComponent;
import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
import org.sonar.ce.task.projectanalysis.component.ViewsComponent;
import org.sonar.ce.task.step.ComputationStep;
import org.sonar.ce.task.step.TestComputationStepContext;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchDao;
import org.sonar.server.es.ProjectIndexer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
import static org.sonar.ce.task.projectanalysis.component.Component.Type.VIEW;
@Rule
public BatchReportReaderRule reportReader = new BatchReportReaderRule();
- private ProjectIndexer componentIndexer = mock(ProjectIndexer.class);
- private IndexAnalysisStep underTest = new IndexAnalysisStep(treeRootHolder, componentIndexer);
+ private final DbClient dbClient = mock(DbClient.class);
+
+ private final FileStatuses fileStatuses = mock(FileStatuses.class);
+
+ private final ProjectIndexer projectIndexer = mock(ProjectIndexer.class);
+
+ private final DbSession dbSession = mock(DbSession.class);
+
+ private final BranchDao branchDao = mock(BranchDao.class);
+
+ private final IndexAnalysisStep underTest = new IndexAnalysisStep(treeRootHolder, fileStatuses, dbClient, projectIndexer);
+
+ private TestComputationStepContext testComputationStepContext;
+
+ @Before
+ public void init() {
+ testComputationStepContext = new TestComputationStepContext();
+
+ when(dbClient.openSession(false)).thenReturn(dbSession);
+ when(dbClient.branchDao()).thenReturn(branchDao);
+ }
@Test
public void call_indexByProjectUuid_of_indexer_for_project() {
Component project = ReportComponent.builder(PROJECT, 1).setUuid(PROJECT_UUID).setKey(PROJECT_KEY).build();
treeRootHolder.setRoot(project);
- underTest.execute(new TestComputationStepContext());
+ underTest.execute(testComputationStepContext);
- verify(componentIndexer).indexOnAnalysis(PROJECT_UUID);
+ verify(projectIndexer).indexOnAnalysis(PROJECT_UUID, Set.of());
}
@Test
Component view = ViewsComponent.builder(VIEW, PROJECT_KEY).setUuid(PROJECT_UUID).build();
treeRootHolder.setRoot(view);
- underTest.execute(new TestComputationStepContext());
+ underTest.execute(testComputationStepContext);
+
+ verify(projectIndexer).indexOnAnalysis(PROJECT_UUID, Set.of());
+ }
+
+ @Test
+ public void execute_whenMarkAsUnchangedFlagActivated_shouldCallIndexOnAnalysisWithChangedComponents() {
+ Component project = ReportComponent.builder(PROJECT, 1).setUuid(PROJECT_UUID).setKey(PROJECT_KEY).build();
+ treeRootHolder.setRoot(project);
+ Set<String> anyUuids = Set.of("any-uuid");
+ when(fileStatuses.getFileUuidsMarkedAsUnchanged()).thenReturn(anyUuids);
+
+ underTest.execute(testComputationStepContext);
+
+ verify(projectIndexer).indexOnAnalysis(PROJECT_UUID, anyUuids);
+ }
+
+ @Test
+ public void execute_whenBranchIsNeedIssueSync_shouldReindexEverything() {
+ Component project = ReportComponent.builder(PROJECT, 1).setUuid(PROJECT_UUID).setKey(PROJECT_KEY).build();
+ treeRootHolder.setRoot(project);
+ when(branchDao.isBranchNeedIssueSync(dbSession, PROJECT_UUID)).thenReturn(true);
+
+ underTest.execute(testComputationStepContext);
- verify(componentIndexer).indexOnAnalysis(PROJECT_UUID);
+ verify(projectIndexer).indexOnAnalysis(PROJECT_UUID);
}
@Override
}
return false;
}
+
+ public boolean isBranchNeedIssueSync(DbSession session, String branchUuid) {
+ return selectByUuid(session, branchUuid)
+ .map(BranchDto::isNeedIssueSync)
+ .orElse(false);
+ }
}
import com.google.common.collect.Sets;
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
assertThat(underTest.doAnyOfComponentsNeedIssueSync(dbSession, componentKeys)).isTrue();
}
+
+ @DataProvider
+ public static Object[][] booleanValues() {
+ return new Object[][] {
+ {true},
+ {false}
+ };
+ }
+
+ @Test
+ @UseDataProvider("booleanValues")
+ public void isBranchNeedIssueSync_shouldReturnCorrectValue(boolean needIssueSync) {
+ ComponentDto project = db.components().insertPrivateProject();
+ String branchUuid = db.components().insertProjectBranch(project, branch -> branch.setBranchType(BranchType.BRANCH).setNeedIssueSync(needIssueSync)).uuid();
+
+ assertThat(underTest.isBranchNeedIssueSync(dbSession, branchUuid)).isEqualTo(needIssueSync);
+ }
+
+ @Test
+ public void isBranchNeedIssueSync_whenNoBranch_shouldReturnFalse() {
+ assertThat(underTest.isBranchNeedIssueSync(dbSession, "unknown")).isFalse();
+ }
}
@Override
public void indexOnAnalysis(String branchUuid) {
+ indexOnAnalysis(branchUuid, Set.of());
+ }
+
+ @Override
+ public void indexOnAnalysis(String branchUuid, Set<String> unchangedComponentUuids) {
doIndexByProjectUuid(branchUuid, Size.REGULAR);
}
package org.sonar.server.es;
import java.util.Collection;
+import java.util.Set;
import org.sonar.db.DbSession;
import org.sonar.db.es.EsQueueDto;
*/
void indexOnAnalysis(String branchUuid);
+ void indexOnAnalysis(String branchUuid, Set<String> unchangedComponentUuids);
+
Collection<EsQueueDto> prepareForRecovery(DbSession dbSession, Collection<String> projectUuids, ProjectIndexer.Cause cause);
}
public void indexAllIssues() {
try (IssueIterator issues = issueIteratorFactory.createForAll()) {
- doIndex(issues, Size.REGULAR, IndexingListener.FAIL_ON_ERROR);
+ doIndex(issues, Set.of());
}
}
@Override
public void indexOnAnalysis(String branchUuid) {
try (IssueIterator issues = issueIteratorFactory.createForBranch(branchUuid)) {
- doIndex(issues, Size.REGULAR, IndexingListener.FAIL_ON_ERROR);
+ doIndex(issues, Set.of());
+ }
+ }
+
+ @Override
+ public void indexOnAnalysis(String branchUuid, Set<String> unchangedComponentUuids) {
+ try (IssueIterator issues = issueIteratorFactory.createForBranch(branchUuid)) {
+ doIndex(issues, unchangedComponentUuids);
}
}
return new IndexingResult();
}
IndexingListener listener = new OneToOneResilientIndexingListener(dbClient, dbSession, itemsByIssueKey.values());
- BulkIndexer bulkIndexer = createBulkIndexer(Size.REGULAR, listener);
+ BulkIndexer bulkIndexer = createBulkIndexer(listener);
bulkIndexer.start();
try (IssueIterator issues = issueIteratorFactory.createForIssueKeys(itemsByIssueKey.keySet())) {
// one project, referenced by es_queue.doc_id = many issues
IndexingListener listener = new OneToManyResilientIndexingListener(dbClient, dbSession, itemsByProjectUuid.values());
- BulkIndexer bulkIndexer = createBulkIndexer(Size.REGULAR, listener);
+ BulkIndexer bulkIndexer = createBulkIndexer(listener);
bulkIndexer.start();
for (String projectUuid : itemsByProjectUuid.keySet()) {
return;
}
- BulkIndexer bulkIndexer = createBulkIndexer(Size.REGULAR, IndexingListener.FAIL_ON_ERROR);
+ BulkIndexer bulkIndexer = createBulkIndexer(IndexingListener.FAIL_ON_ERROR);
bulkIndexer.start();
issueKeys.forEach(issueKey -> bulkIndexer.addDeletion(TYPE_ISSUE.getMainType(), issueKey, AuthorizationDoc.idOf(projectUuid)));
bulkIndexer.stop();
@VisibleForTesting
protected void index(Iterator<IssueDoc> issues) {
- doIndex(issues, Size.REGULAR, IndexingListener.FAIL_ON_ERROR);
+ doIndex(issues, Set.of());
}
- private void doIndex(Iterator<IssueDoc> issues, Size size, IndexingListener listener) {
- BulkIndexer bulk = createBulkIndexer(size, listener);
+ private void doIndex(Iterator<IssueDoc> issues, Set<String> unchangedComponentUuids) {
+ BulkIndexer bulk = createBulkIndexer(IndexingListener.FAIL_ON_ERROR);
bulk.start();
while (issues.hasNext()) {
IssueDoc issue = issues.next();
- bulk.add(newIndexRequest(issue));
+ if (shouldReindexIssue(issue, unchangedComponentUuids)) {
+ bulk.add(newIndexRequest(issue));
+ }
}
bulk.stop();
}
+ private static boolean shouldReindexIssue(IssueDoc issue, Set<String> unchangedComponentUuids) {
+ return issue.getFields().get(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID) == null || !unchangedComponentUuids.contains(issue.componentUuid());
+ }
+
private static IndexRequest newIndexRequest(IssueDoc issue) {
return new IndexRequest(TYPE_ISSUE.getMainType().getIndex().getName())
.id(issue.getId())
return EsQueueDto.create(TYPE_ISSUE.format(), docId, docIdType, AuthorizationDoc.idOf(projectUuid));
}
- private BulkIndexer createBulkIndexer(Size size, IndexingListener listener) {
- return new BulkIndexer(esClient, TYPE_ISSUE, size, listener);
+ private BulkIndexer createBulkIndexer(IndexingListener listener) {
+ return new BulkIndexer(esClient, TYPE_ISSUE, Size.REGULAR, listener);
}
}
@Override
public void indexOnAnalysis(String projectUuid) {
+ indexOnAnalysis(projectUuid, Set.of());
+ }
+
+ @Override
+ public void indexOnAnalysis(String projectUuid, Set<String> unchangedComponentUuids) {
doIndex(Size.REGULAR, projectUuid);
}
assertThat(indexer2.calledItems).containsExactlyInAnyOrder(item2);
}
- @Test
- public void commitAndIndex_restricts_indexing_to_projects() {
- // TODO
- }
-
private static class FakeIndexer implements ProjectIndexer {
private final List<EsQueueDto> items;
public void indexOnAnalysis(String branchUuid) {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public void indexOnAnalysis(String branchUuid, Set<String> unchangedComponentUuids) {
+ throw new UnsupportedOperationException();
+ }
}
}
import java.util.Date;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.assertj.core.api.Assertions;
import org.sonar.db.issue.IssueTesting;
import org.sonar.db.rule.RuleDto;
import org.sonar.server.es.EsTester;
+import org.sonar.server.es.IndexType;
import org.sonar.server.es.IndexingResult;
import org.sonar.server.es.ProjectIndexer;
import org.sonar.server.es.StartupIndexer;
@Test
public void verify_security_standards_indexation() {
- RuleDto rule = db.rules().insert(r -> r.setSecurityStandards(new HashSet<>(Arrays.asList("cwe:123", "owaspTop10:a3", "cwe:863","owaspAsvs-4.0:2.1.1"))));
+ RuleDto rule = db.rules().insert(r -> r.setSecurityStandards(new HashSet<>(Arrays.asList("cwe:123", "owaspTop10:a3", "cwe:863", "owaspAsvs-4.0:2.1.1"))));
ComponentDto project = db.components().insertPrivateProject();
ComponentDto dir = db.components().insertComponent(ComponentTesting.newDirectory(project, "src/main/java/foo"));
ComponentDto file = db.components().insertComponent(newFileDto(project, dir, "F1"));
- IssueDto issue = db.issues().insert(rule, project, file);
+ db.issues().insert(rule, project, file);
underTest.indexAllIssues();
es.lockWrites(TYPE_ISSUE);
db.issues().insert();
- try {
- // FIXME : test also message
- assertThatThrownBy(() -> underTest.indexOnStartup(emptySet()))
- .isInstanceOf(IllegalStateException.class);
- } finally {
- assertThatIndexHasSize(0);
- assertThatEsQueueTableHasSize(0);
- es.unlockWrites(TYPE_ISSUE);
- }
+ Set<IndexType> uninitializedIndexTypes = emptySet();
+ assertThatThrownBy(() -> underTest.indexOnStartup(uninitializedIndexTypes))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("SYNCHRONE StartupIndexer must implement indexOnStartup");
+ assertThatIndexHasSize(0);
+ assertThatEsQueueTableHasSize(0);
+ es.unlockWrites(TYPE_ISSUE);
}
@Test
ComponentDto file = db.components().insertComponent(newFileDto(project));
IssueDto issue = db.issues().insert(rule, project, file);
ComponentDto otherProject = db.components().insertPrivateProject();
- ComponentDto fileOnOtherProject = db.components().insertComponent(newFileDto(otherProject));
+ db.components().insertComponent(newFileDto(otherProject));
underTest.indexOnAnalysis(project.uuid());
es.lockWrites(TYPE_ISSUE);
IssueDto issue = db.issues().insert();
- try {
- // FIXME : test also message
- assertThatThrownBy(() -> underTest.indexOnAnalysis(issue.getProjectUuid()))
- .isInstanceOf(IllegalStateException.class);
- } finally {
- assertThatIndexHasSize(0);
- assertThatEsQueueTableHasSize(0);
- es.unlockWrites(TYPE_ISSUE);
- }
+ String projectUuid = issue.getProjectUuid();
+ assertThatThrownBy(() -> underTest.indexOnAnalysis(projectUuid))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Unrecoverable indexation failures: 1 errors among 1 requests. Check Elasticsearch logs for further details.");
+ assertThatIndexHasSize(0);
+ assertThatEsQueueTableHasSize(0);
+ es.unlockWrites(TYPE_ISSUE);
}
@Test
RuleDto rule = db.rules().insert();
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
- IssueDto issue1 = db.issues().insert(rule, project, file);
- IssueDto issue2 = db.issues().insert(rule, project, file);
+ db.issues().insert(rule, project, file);
+ db.issues().insert(rule, project, file);
es.lockWrites(TYPE_ISSUE);
addIssueToIndex("P1", "Issue1");
es.lockWrites(TYPE_ISSUE);
- try {
- // FIXME : test also message
- assertThatThrownBy(() -> underTest.deleteByKeys("P1", asList("Issue1")))
- .isInstanceOf(IllegalStateException.class);
- } finally {
- assertThatIndexHasOnly("Issue1");
- assertThatEsQueueTableHasSize(0);
- es.unlockWrites(TYPE_ISSUE);
- }
+ List<String> issues = List.of("Issue1");
+ assertThatThrownBy(() -> underTest.deleteByKeys("P1", issues))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Unrecoverable indexation failures: 1 errors among 1 requests. Check Elasticsearch logs for further details.");
+ assertThatIndexHasOnly("Issue1");
+ assertThatEsQueueTableHasSize(0);
+ es.unlockWrites(TYPE_ISSUE);
}
@Test
Assertions.assertThat(underTest.getType()).isEqualTo(StartupIndexer.Type.ASYNCHRONOUS);
}
+ @Test
+ public void indexOnAnalysis_whenChangedComponents_shouldReindexOnlyChangedComponents() {
+ RuleDto rule = db.rules().insert();
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto changedComponent1 = db.components().insertComponent(newFileDto(project));
+ ComponentDto unchangedComponent = db.components().insertComponent(newFileDto(project));
+ ComponentDto ChangedComponent2 = db.components().insertComponent(newFileDto(project));
+ IssueDto changedIssue1 = db.issues().insert(rule, project, changedComponent1);
+ IssueDto changedIssue2 = db.issues().insert(rule, project, changedComponent1);
+ IssueDto changedIssue3 = db.issues().insert(rule, project, ChangedComponent2);
+ db.issues().insert(rule, project, unchangedComponent);
+ db.issues().insert(rule, project, unchangedComponent);
+
+ underTest.indexOnAnalysis(project.uuid(), Set.of(unchangedComponent.uuid()));
+
+ assertThatIndexHasOnly(changedIssue1, changedIssue2, changedIssue3);
+ }
+
+ @Test
+ public void indexOnAnalysis_whenEmptyUnchangedComponents_shouldReindexEverything() {
+ RuleDto rule = db.rules().insert();
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto changedComponent = db.components().insertComponent(newFileDto(project));
+ IssueDto changedIssue1 = db.issues().insert(rule, project, changedComponent);
+ IssueDto changedIssue2 = db.issues().insert(rule, project, changedComponent);
+
+ underTest.indexOnAnalysis(project.uuid(), Set.of());
+
+ assertThatIndexHasOnly(changedIssue1, changedIssue2);
+ }
+
+ @Test
+ public void indexOnAnalysis_whenChangedComponentWithoutIssue_shouldReindexNothing() {
+ db.rules().insert();
+ ComponentDto project = db.components().insertPrivateProject();
+ db.components().insertComponent(newFileDto(project));
+
+ underTest.indexOnAnalysis(project.uuid(), Set.of());
+
+ assertThat(es.getDocuments(TYPE_ISSUE)).isEmpty();
+ }
+
private void addIssueToIndex(String projectUuid, String issueKey) {
es.putDocuments(TYPE_ISSUE,
newDoc().setKey(issueKey).setProjectUuid(projectUuid));
// nothing to do, permissions don't change during an analysis
}
+ @Override
+ public void indexOnAnalysis(String branchUuid, Set<String> unchangedComponentUuids) {
+ // nothing to do, permissions don't change during an analysis
+ }
+
@Override
public Collection<EsQueueDto> prepareForRecovery(DbSession dbSession, Collection<String> projectUuids, ProjectIndexer.Cause cause) {
return switch (cause) {
@Override
public void indexOnAnalysis(String branchUuid) {
+ indexOnAnalysis(branchUuid, Set.of());
+ }
+
+ @Override
+ public void indexOnAnalysis(String branchUuid, Set<String> unchangedComponentUuids) {
addToIndex(branchUuid, "bar");
addToIndex(branchUuid, "baz");
}