| // Copyright (C) 2021 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.google.gerrit.server.notedb; |
| |
| import static com.google.common.collect.ImmutableList.toImmutableList; |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.gerrit.entities.LabelId.CODE_REVIEW; |
| import static com.google.gerrit.entities.LabelId.VERIFIED; |
| import static com.google.gerrit.server.notedb.ReviewerStateInternal.CC; |
| import static com.google.gerrit.server.notedb.ReviewerStateInternal.REMOVED; |
| import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER; |
| import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction; |
| import static java.util.Objects.requireNonNull; |
| import static org.mockito.Mockito.mock; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.errorprone.annotations.CanIgnoreReturnValue; |
| import com.google.gerrit.entities.Account; |
| import com.google.gerrit.entities.AttentionSetUpdate; |
| import com.google.gerrit.entities.AttentionSetUpdate.Operation; |
| import com.google.gerrit.entities.Change; |
| import com.google.gerrit.entities.ChangeMessage; |
| import com.google.gerrit.entities.LabelId; |
| import com.google.gerrit.entities.PatchSetApproval; |
| import com.google.gerrit.entities.RefNames; |
| import com.google.gerrit.entities.SubmitRecord; |
| import com.google.gerrit.git.RefUpdateUtil; |
| import com.google.gerrit.json.OutputFormat; |
| import com.google.gerrit.server.CurrentUser; |
| import com.google.gerrit.server.IdentifiedUser; |
| import com.google.gerrit.server.ReviewerStatusUpdate; |
| import com.google.gerrit.server.git.validators.TopicValidator; |
| import com.google.gerrit.server.notedb.ChangeNoteUtil.AttentionStatusInNoteDb; |
| import com.google.gerrit.server.notedb.CommitRewriter.BackfillResult; |
| import com.google.gerrit.server.notedb.CommitRewriter.CommitDiff; |
| import com.google.gerrit.server.notedb.CommitRewriter.RunOptions; |
| import com.google.gerrit.server.util.AccountTemplateUtil; |
| import com.google.gerrit.server.util.time.TimeUtil; |
| import com.google.gson.Gson; |
| import com.google.inject.Inject; |
| import java.time.Instant; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.stream.IntStream; |
| import org.eclipse.jgit.lib.BatchRefUpdate; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.eclipse.jgit.lib.PersonIdent; |
| import org.eclipse.jgit.lib.Ref; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.eclipse.jgit.revwalk.RevSort; |
| import org.eclipse.jgit.revwalk.RevWalk; |
| import org.eclipse.jgit.transport.ReceiveCommand; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| /** Tests for {@link CommitRewriter} */ |
| public class CommitRewriterTest extends AbstractChangeNotesTest { |
| |
| private @Inject CommitRewriter rewriter; |
| @Inject private ChangeNoteUtil changeNoteUtil; |
| |
| private TopicValidator topicValidator; |
| |
| private static final Gson gson = OutputFormat.JSON_COMPACT.newGson(); |
| |
| @Before |
| public void setUp() throws Exception { |
| topicValidator = mock(TopicValidator.class); |
| } |
| |
| @After |
| public void cleanUp() throws Exception { |
| BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate(); |
| bru.setAllowNonFastForwards(true); |
| for (Ref ref : repo.getRefDatabase().getRefsByPrefix(RefNames.REFS_CHANGES)) { |
| Change.Id changeId = Change.Id.fromRef(ref.getName()); |
| if (changeId == null || !ref.getName().equals(RefNames.changeMetaRef(changeId))) { |
| continue; |
| } |
| bru.addCommand(new ReceiveCommand(ref.getObjectId(), ObjectId.zeroId(), ref.getName())); |
| } |
| |
| testRefAction(() -> RefUpdateUtil.executeChecked(bru, repo)); |
| } |
| |
| @Test |
| public void validHistoryNoOp() throws Exception { |
| String tag = "jenkins"; |
| Change c = newChange(); |
| ChangeUpdate update = newUpdate(c, changeOwner); |
| update.setChangeMessage("verification from jenkins"); |
| update.setTag(tag); |
| update.commit(); |
| |
| ChangeUpdate updateWithSubject = newUpdate(c, changeOwner); |
| updateWithSubject.setSubjectForCommit("Update with subject"); |
| updateWithSubject.commit(); |
| |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| Ref metaRefBefore = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult backfillResult = rewriter.backfillProject(project, repo, options); |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| Ref metaRefAfter = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| assertThat(notesBeforeRewrite.getMetaId()).isEqualTo(notesAfterRewrite.getMetaId()); |
| assertThat(metaRefBefore.getObjectId()).isEqualTo(metaRefAfter.getObjectId()); |
| assertThat(backfillResult.fixedRefDiff).isEmpty(); |
| } |
| |
| @Test |
| public void failedVerification() throws Exception { |
| String tag = "jenkins"; |
| Change c = newChange(); |
| ChangeUpdate update = newUpdate(c, changeOwner); |
| update.setChangeMessage("Unknown commit " + changeOwner.getName()); |
| update.setTag(tag); |
| update.commit(); |
| |
| ChangeUpdate updateWithSubject = newUpdate(c, changeOwner); |
| updateWithSubject.setSubjectForCommit("Update with subject"); |
| updateWithSubject.commit(); |
| |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| Ref metaRefBefore = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult backfillResult = rewriter.backfillProject(project, repo, options); |
| assertThat(backfillResult.fixedRefDiff).isEmpty(); |
| assertThat(backfillResult.refsStillInvalidAfterFix) |
| .containsExactly(RefNames.changeMetaRef(c.getId())); |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| Ref metaRefAfter = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| assertThat(notesBeforeRewrite.getMetaId()).isEqualTo(notesAfterRewrite.getMetaId()); |
| assertThat(metaRefBefore.getObjectId()).isEqualTo(metaRefAfter.getObjectId()); |
| } |
| |
| @Test |
| public void outputDiffOff_refsReported() throws Exception { |
| Change c = newChange(); |
| ChangeUpdate update = newUpdate(c, changeOwner); |
| update.setChangeMessage("Change has been successfully merged by " + changeOwner.getName()); |
| ObjectId commitToFix = update.commit(); |
| |
| ChangeUpdate updateWithSubject = newUpdate(c, changeOwner); |
| updateWithSubject.setSubjectForCommit("Update with subject"); |
| updateWithSubject.commit(); |
| |
| Ref metaRefBefore = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| options.outputDiff = false; |
| options.verifyCommits = false; |
| BackfillResult backfillResult = rewriter.backfillProject(project, repo, options); |
| assertThat(backfillResult.fixedRefDiff.keySet()) |
| .containsExactly(RefNames.changeMetaRef(c.getId())); |
| Ref metaRefAfter = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| assertThat(metaRefBefore.getObjectId()).isNotEqualTo(metaRefAfter.getObjectId()); |
| |
| assertFixedCommits(ImmutableList.of(commitToFix), backfillResult, c.getId()); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(backfillResult, c.getId()); |
| assertThat(commitHistoryDiff).containsExactly(""); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void numRefs_greater_maxRefsToUpdate_allFixed() throws Exception { |
| int numberOfChanges = 12; |
| ImmutableMap.Builder<String, Ref> refsToOldMetaBuilder = new ImmutableMap.Builder<>(); |
| for (int i = 0; i < numberOfChanges; i++) { |
| Change c = newChange(); |
| ChangeUpdate update = newUpdate(c, changeOwner); |
| update.setChangeMessage("Change has been successfully merged by " + changeOwner.getName()); |
| update.commit(); |
| ChangeUpdate updateWithSubject = newUpdate(c, changeOwner); |
| updateWithSubject.setSubjectForCommit("Update with subject"); |
| updateWithSubject.commit(); |
| String refName = RefNames.changeMetaRef(c.getId()); |
| Ref metaRefBeforeRewrite = repo.exactRef(refName); |
| refsToOldMetaBuilder.put(refName, metaRefBeforeRewrite); |
| } |
| ImmutableMap<String, Ref> refsToOldMeta = refsToOldMetaBuilder.build(); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| options.outputDiff = false; |
| options.verifyCommits = false; |
| options.maxRefsInBatch = 10; |
| options.maxRefsToUpdate = 12; |
| BackfillResult backfillResult = rewriter.backfillProject(project, repo, options); |
| assertThat(backfillResult.fixedRefDiff.keySet()).isEqualTo(refsToOldMeta.keySet()); |
| for (Map.Entry<String, Ref> refEntry : refsToOldMeta.entrySet()) { |
| Ref metaRefAfterRewrite = repo.exactRef(refEntry.getKey()); |
| assertThat(refEntry.getValue()).isNotEqualTo(metaRefAfterRewrite); |
| } |
| } |
| |
| @Test |
| public void maxRefsToUpdate_coversAllInvalid_inMultipleBatches() throws Exception { |
| testMaxRefsToUpdate( |
| /* numberOfInvalidChanges= */ 11, |
| /* numberOfValidChanges= */ 9, |
| /* maxRefsToUpdate= */ 12, |
| /* maxRefsInBatch= */ 2); |
| } |
| |
| @Test |
| public void maxRefsToUpdate_coversAllInvalid_inSingleBatch() throws Exception { |
| testMaxRefsToUpdate( |
| /* numberOfInvalidChanges= */ 11, |
| /* numberOfValidChanges= */ 9, |
| /* maxRefsToUpdate= */ 12, |
| /* maxRefsInBatch= */ 12); |
| } |
| |
| @Test |
| public void moreInvalidRefs_thenMaxRefsToUpdate_inMultipleBatches() throws Exception { |
| testMaxRefsToUpdate( |
| /* numberOfInvalidChanges= */ 11, |
| /* numberOfValidChanges= */ 9, |
| /* maxRefsToUpdate= */ 10, |
| /* maxRefsInBatch= */ 2); |
| } |
| |
| @Test |
| public void moreInvalidRefs_thenMaxRefsToUpdate_inSingleBatch() throws Exception { |
| testMaxRefsToUpdate( |
| /* numberOfInvalidChanges= */ 11, |
| /* numberOfValidChanges= */ 9, |
| /* maxRefsToUpdate= */ 10, |
| /* maxRefsInBatch= */ 10); |
| } |
| |
| private void testMaxRefsToUpdate( |
| int numberOfInvalidChanges, int numberOfValidChanges, int maxRefsToUpdate, int maxRefsInBatch) |
| throws Exception { |
| ImmutableMap.Builder<String, ObjectId> expectedFixedRefsToOldMetaBuilder = |
| new ImmutableMap.Builder<>(); |
| ImmutableMap.Builder<String, ObjectId> expectedSkippedRefsToOldMetaBuilder = |
| new ImmutableMap.Builder<>(); |
| for (int i = 0; i < numberOfValidChanges; i++) { |
| Change c = newChange(); |
| ChangeUpdate updateWithSubject = newUpdate(c, changeOwner); |
| updateWithSubject.setSubjectForCommit("Update with subject"); |
| updateWithSubject.commit(); |
| String refName = RefNames.changeMetaRef(c.getId()); |
| Ref metaRefBeforeRewrite = repo.exactRef(refName); |
| expectedSkippedRefsToOldMetaBuilder.put(refName, metaRefBeforeRewrite.getObjectId()); |
| } |
| Set<String> invalidRefs = new HashSet<>(); |
| for (int i = 0; i < numberOfInvalidChanges; i++) { |
| Change c = newChange(); |
| ChangeUpdate update = newUpdate(c, changeOwner); |
| update.setChangeMessage("Change has been successfully merged by " + changeOwner.getName()); |
| update.commit(); |
| ChangeUpdate updateWithSubject = newUpdate(c, changeOwner); |
| updateWithSubject.setSubjectForCommit("Update with subject"); |
| updateWithSubject.commit(); |
| String refName = RefNames.changeMetaRef(c.getId()); |
| invalidRefs.add(refName); |
| } |
| int i = 0; |
| for (Ref ref : repo.getRefDatabase().getRefsByPrefix(RefNames.REFS_CHANGES)) { |
| Ref metaRefBeforeRewrite = repo.exactRef(ref.getName()); |
| if (!invalidRefs.contains(ref.getName())) { |
| continue; |
| } |
| if (i < maxRefsToUpdate) { |
| expectedFixedRefsToOldMetaBuilder.put(ref.getName(), metaRefBeforeRewrite.getObjectId()); |
| } else { |
| expectedSkippedRefsToOldMetaBuilder.put(ref.getName(), metaRefBeforeRewrite.getObjectId()); |
| } |
| i++; |
| } |
| ImmutableMap<String, ObjectId> expectedFixedRefsToOldMeta = |
| expectedFixedRefsToOldMetaBuilder.build(); |
| ImmutableMap<String, ObjectId> expectedSkippedRefsToOldMeta = |
| expectedSkippedRefsToOldMetaBuilder.build(); |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| options.outputDiff = false; |
| options.verifyCommits = false; |
| options.maxRefsInBatch = maxRefsInBatch; |
| options.maxRefsToUpdate = maxRefsToUpdate; |
| BackfillResult backfillResult = rewriter.backfillProject(project, repo, options); |
| assertThat(backfillResult.fixedRefDiff.keySet()).isEqualTo(expectedFixedRefsToOldMeta.keySet()); |
| for (Map.Entry<String, ObjectId> refEntry : expectedFixedRefsToOldMeta.entrySet()) { |
| Ref metaRefAfterRewrite = repo.exactRef(refEntry.getKey()); |
| assertThat(refEntry.getValue()).isNotEqualTo(metaRefAfterRewrite.getObjectId()); |
| } |
| for (Map.Entry<String, ObjectId> refEntry : expectedSkippedRefsToOldMeta.entrySet()) { |
| Ref metaRefAfterRewrite = repo.exactRef(refEntry.getKey()); |
| assertThat(refEntry.getValue()).isEqualTo(metaRefAfterRewrite.getObjectId()); |
| } |
| RunOptions secondRunOptions = new RunOptions(); |
| secondRunOptions.dryRun = false; |
| secondRunOptions.outputDiff = false; |
| secondRunOptions.verifyCommits = false; |
| secondRunOptions.maxRefsInBatch = maxRefsInBatch; |
| secondRunOptions.maxRefsToUpdate = numberOfInvalidChanges + numberOfValidChanges; |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| int expectedSecondRunResult = |
| numberOfInvalidChanges > maxRefsToUpdate ? numberOfInvalidChanges - maxRefsToUpdate : 0; |
| assertThat(secondRunResult.fixedRefDiff.keySet().size()).isEqualTo(expectedSecondRunResult); |
| } |
| |
| @Test |
| public void fixAuthorIdent() throws Exception { |
| Change c = newChange(); |
| Instant when = TimeUtil.now(); |
| PersonIdent invalidAuthorIdent = |
| new PersonIdent( |
| changeOwner.getName(), |
| changeNoteUtil.getAccountIdAsEmailAddress(changeOwner.getAccountId()), |
| when, |
| serverIdent.getZoneId()); |
| RevCommit invalidUpdateCommit = |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody(c, /* changeMessage= */ null), |
| invalidAuthorIdent); |
| ChangeUpdate validUpdate = newUpdate(c, changeOwner); |
| validUpdate.setChangeMessage("verification from jenkins"); |
| validUpdate.setTag("jenkins"); |
| validUpdate.commit(); |
| |
| Ref metaRefBeforeRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<RevCommit> commitsBeforeRewrite = logMetaRef(repo, metaRefBeforeRewrite); |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| |
| assertThat(notesAfterRewrite.getChange().getOwner()) |
| .isEqualTo(notesBeforeRewrite.getChange().getOwner()); |
| Ref metaRefAfterRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| assertThat(metaRefAfterRewrite.getObjectId()).isNotEqualTo(metaRefBeforeRewrite.getObjectId()); |
| ImmutableList<RevCommit> commitsAfterRewrite = logMetaRef(repo, metaRefAfterRewrite); |
| int invalidCommitIndex = commitsBeforeRewrite.indexOf(invalidUpdateCommit); |
| |
| assertValidCommits( |
| commitsBeforeRewrite, commitsAfterRewrite, ImmutableList.of(invalidCommitIndex)); |
| assertFixedCommits(ImmutableList.of(invalidUpdateCommit.getId()), result, c.getId()); |
| |
| RevCommit fixedUpdateCommit = commitsAfterRewrite.get(invalidCommitIndex); |
| PersonIdent originalAuthorIdent = invalidUpdateCommit.getAuthorIdent(); |
| PersonIdent fixedAuthorIdent = fixedUpdateCommit.getAuthorIdent(); |
| assertThat(originalAuthorIdent).isNotEqualTo(fixedAuthorIdent); |
| assertThat(fixedUpdateCommit.getAuthorIdent().getName()) |
| .isEqualTo("Gerrit User " + changeOwner.getAccountId()); |
| assertThat(originalAuthorIdent.getEmailAddress()).isEqualTo(fixedAuthorIdent.getEmailAddress()); |
| assertThat(originalAuthorIdent.getWhenAsInstant()) |
| .isEqualTo(fixedAuthorIdent.getWhenAsInstant()); |
| assertThat(originalAuthorIdent.getTimeZone()).isEqualTo(fixedAuthorIdent.getTimeZone()); |
| assertThat(invalidUpdateCommit.getFullMessage()).isEqualTo(fixedUpdateCommit.getFullMessage()); |
| assertThat(invalidUpdateCommit.getCommitterIdent()) |
| .isEqualTo(fixedUpdateCommit.getCommitterIdent()); |
| assertThat(fixedUpdateCommit.getFullMessage()).doesNotContain(changeOwner.getName()); |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff).hasSize(1); |
| assertThat(commitHistoryDiff.get(0)).contains("-author Change Owner <1@gerrit>"); |
| assertThat(commitHistoryDiff.get(0)).contains("+author Gerrit User 1 <1@gerrit>"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixRealUserFooterIdent() throws Exception { |
| Change c = newChange(); |
| |
| String realUserIdentToFix = getAccountIdentToFix(otherUser.getAccount()); |
| ObjectId invalidUpdateCommit = |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody(c, "Comment on behalf of user", "Real-user: " + realUserIdentToFix), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| IdentifiedUser impersonatedChangeOwner = |
| this.userFactory.runAs( |
| /* remotePeer= */ null, |
| changeOwner.getAccountId(), |
| requireNonNull(otherUser).getRealUser()); |
| ChangeUpdate impersonatedChangeMessageUpdate = newUpdate(c, impersonatedChangeOwner); |
| impersonatedChangeMessageUpdate.setChangeMessage("Other comment on behalf of"); |
| impersonatedChangeMessageUpdate.commit(); |
| |
| Ref metaRefBeforeRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<RevCommit> commitsBeforeRewrite = logMetaRef(repo, metaRefBeforeRewrite); |
| |
| int invalidCommitIndex = commitsBeforeRewrite.indexOf(invalidUpdateCommit); |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| assertThat(changeMessages(notesBeforeRewrite)) |
| .containsExactly("Comment on behalf of user", "Other comment on behalf of"); |
| assertThat(notesBeforeRewrite.getChangeMessages().get(0).getAuthor()) |
| .isEqualTo(changeOwner.getAccountId()); |
| assertThat(notesBeforeRewrite.getChangeMessages().get(0).getRealAuthor()) |
| .isEqualTo(otherUser.getAccountId()); |
| assertThat(changeMessages(notesAfterRewrite)) |
| .containsExactly("Comment on behalf of user", "Other comment on behalf of"); |
| assertThat(notesBeforeRewrite.getChangeMessages().get(0).getAuthor()) |
| .isEqualTo(changeOwner.getAccountId()); |
| assertThat(notesBeforeRewrite.getChangeMessages().get(0).getRealAuthor()) |
| .isEqualTo(otherUser.getAccountId()); |
| |
| Ref metaRefAfterRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| assertThat(metaRefAfterRewrite.getObjectId()).isNotEqualTo(metaRefBeforeRewrite.getObjectId()); |
| |
| ImmutableList<RevCommit> commitsAfterRewrite = logMetaRef(repo, metaRefAfterRewrite); |
| assertValidCommits( |
| commitsBeforeRewrite, commitsAfterRewrite, ImmutableList.of(invalidCommitIndex)); |
| assertFixedCommits(ImmutableList.of(invalidUpdateCommit), result, c.getId()); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -9 +9 @@\n" |
| + "-Real-user: Other Account <2@gerrit>\n" |
| + "+Real-user: Gerrit User 2 <2@gerrit>\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixReviewerFooterIdent() throws Exception { |
| Change c = newChange(); |
| String reviewerIdentToFix = getAccountIdentToFix(otherUser.getAccount()); |
| ImmutableList<ObjectId> commitsToFix = |
| new ImmutableList.Builder<ObjectId>() |
| .add( |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| // valid change message that should not be overwritten |
| getChangeUpdateBody( |
| c, |
| "Removed reviewer <GERRIT_ACCOUNT_1>.", |
| "Reviewer: " + reviewerIdentToFix), |
| getAuthorIdent(changeOwner.getAccount()))) |
| .add( |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| // valid change message that should not be overwritten |
| getChangeUpdateBody( |
| c, |
| "Removed cc <GERRIT_ACCOUNT_2> with the following votes:\n\n" |
| + " * Code-Review+2 by <GERRIT_ACCOUNT_2>", |
| "CC: " + reviewerIdentToFix), |
| getAuthorIdent(otherUser.getAccount()))) |
| .add( |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody(c, "Removed cc", "Removed: " + reviewerIdentToFix), |
| getAuthorIdent(changeOwner.getAccount()))) |
| .build(); |
| |
| Ref metaRefBeforeRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<RevCommit> commitsBeforeRewrite = logMetaRef(repo, metaRefBeforeRewrite); |
| |
| ImmutableList<Integer> invalidCommits = |
| commitsToFix.stream() |
| .map(commit -> commitsBeforeRewrite.indexOf(commit)) |
| .collect(toImmutableList()); |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| Instant updateTimestamp = serverIdent.getWhenAsInstant(); |
| ImmutableList<ReviewerStatusUpdate> expectedReviewerUpdates = |
| ImmutableList.of( |
| ReviewerStatusUpdate.createForReviewer( |
| updateTimestamp, changeOwner.getAccountId(), otherUserId, REVIEWER), |
| ReviewerStatusUpdate.createForReviewer(updateTimestamp, otherUserId, otherUserId, CC), |
| ReviewerStatusUpdate.createForReviewer( |
| updateTimestamp, changeOwner.getAccountId(), otherUserId, REMOVED)); |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| |
| assertThat(notesBeforeRewrite.getReviewerUpdates()).isEqualTo(expectedReviewerUpdates); |
| assertThat(notesAfterRewrite.getReviewerUpdates()).isEqualTo(expectedReviewerUpdates); |
| |
| Ref metaRefAfterRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| assertThat(metaRefAfterRewrite.getObjectId()).isNotEqualTo(metaRefBeforeRewrite.getObjectId()); |
| |
| ImmutableList<RevCommit> commitsAfterRewrite = logMetaRef(repo, metaRefAfterRewrite); |
| assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits); |
| assertFixedCommits(commitsToFix, result, c.getId()); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -9 +9 @@\n" |
| + "-Reviewer: Other Account <2@gerrit>\n" |
| + "+Reviewer: Gerrit User 2 <2@gerrit>\n", |
| "@@ -11 +11 @@\n" |
| + "-CC: Other Account <2@gerrit>\n" |
| + "+CC: Gerrit User 2 <2@gerrit>\n", |
| "@@ -9 +9 @@\n" |
| + "-Removed: Other Account <2@gerrit>\n" |
| + "+Removed: Gerrit User 2 <2@gerrit>\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixReviewerMessage() throws Exception { |
| Change c = newChange(); |
| ImmutableList.Builder<ObjectId> commitsToFix = new ImmutableList.Builder<>(); |
| ChangeUpdate addReviewerUpdate = newUpdate(c, changeOwner); |
| addReviewerUpdate.putReviewer(otherUserId, REVIEWER); |
| addReviewerUpdate.commit(); |
| |
| commitsToFix.add( |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| String.format("Removed reviewer %s.", otherUser.getAccount().fullName()), |
| "Removed: " + getValidIdentAsString(otherUser.getAccount())), |
| getAuthorIdent(changeOwner.getAccount()))); |
| |
| ChangeUpdate addCcUpdate = newUpdate(c, changeOwner); |
| addCcUpdate.putReviewer(otherUserId, CC); |
| addCcUpdate.commit(); |
| |
| commitsToFix.add( |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| String.format( |
| "Removed cc %s with the following votes:\n\n * Code-Review+2", |
| otherUser.getAccount().fullName()), |
| "Removed: " + getValidIdentAsString(otherUser.getAccount())), |
| getAuthorIdent(changeOwner.getAccount()))); |
| |
| Ref metaRefBeforeRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<RevCommit> commitsBeforeRewrite = logMetaRef(repo, metaRefBeforeRewrite); |
| |
| ImmutableList<Integer> invalidCommits = |
| commitsToFix.build().stream() |
| .map(commit -> commitsBeforeRewrite.indexOf(commit)) |
| .collect(toImmutableList()); |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| Instant updateTimestamp = serverIdent.getWhenAsInstant(); |
| ImmutableList<ReviewerStatusUpdate> expectedReviewerUpdates = |
| ImmutableList.of( |
| ReviewerStatusUpdate.createForReviewer( |
| addReviewerUpdate.when, changeOwner.getAccountId(), otherUserId, REVIEWER), |
| ReviewerStatusUpdate.createForReviewer( |
| updateTimestamp, changeOwner.getAccountId(), otherUserId, REMOVED), |
| ReviewerStatusUpdate.createForReviewer( |
| addCcUpdate.when, changeOwner.getAccountId(), otherUserId, CC), |
| ReviewerStatusUpdate.createForReviewer( |
| updateTimestamp, changeOwner.getAccountId(), otherUserId, REMOVED)); |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| |
| assertThat(notesBeforeRewrite.getReviewerUpdates()).isEqualTo(expectedReviewerUpdates); |
| assertThat(changeMessages(notesBeforeRewrite)) |
| .containsExactly( |
| "Removed reviewer Other Account.", |
| "Removed cc Other Account with the following votes:\n\n * Code-Review+2"); |
| assertThat(notesAfterRewrite.getReviewerUpdates()).isEqualTo(expectedReviewerUpdates); |
| assertThat(changeMessages(notesAfterRewrite)).containsExactly("Removed reviewer", "Removed cc"); |
| |
| Ref metaRefAfterRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| assertThat(metaRefAfterRewrite.getObjectId()).isNotEqualTo(metaRefBeforeRewrite.getObjectId()); |
| |
| ImmutableList<RevCommit> commitsAfterRewrite = logMetaRef(repo, metaRefAfterRewrite); |
| assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits); |
| assertFixedCommits(commitsToFix.build(), result, c.getId()); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -6 +6 @@\n" + "-Removed reviewer Other Account.\n" + "+Removed reviewer\n", |
| "@@ -6,3 +6 @@\n" |
| + "-Removed cc Other Account with the following votes:\n" |
| + "-\n" |
| + "- * Code-Review+2\n" |
| + "+Removed cc\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixReviewerMessageNoReviewerFooter() throws Exception { |
| Change c = newChange(); |
| |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, String.format("Removed reviewer %s.", otherUser.getAccount().fullName())), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| String.format( |
| "Removed cc %s with the following votes:\n\n * Code-Review+2", |
| otherUser.getAccount().fullName())), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -6 +6 @@\n" + "-Removed reviewer Other Account.\n" + "+Removed reviewer\n", |
| "@@ -6,3 +6 @@\n" |
| + "-Removed cc Other Account with the following votes:\n" |
| + "-\n" |
| + "- * Code-Review+2\n" |
| + "+Removed cc\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixLabelFooterIdent() throws Exception { |
| Change c = newChange(); |
| String approverIdentToFix = getAccountIdentToFix(otherUser.getAccount()); |
| String changeOwnerIdentToFix = getAccountIdentToFix(changeOwner.getAccount()); |
| ChangeUpdate approvalUpdateByOtherUser = newUpdate(c, otherUser); |
| approvalUpdateByOtherUser.putApproval(VERIFIED, (short) -1); |
| approvalUpdateByOtherUser.commit(); |
| |
| ImmutableList<ObjectId> commitsToFix = |
| new ImmutableList.Builder<ObjectId>() |
| .add( |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| /* changeMessage= */ null, |
| "Label: -Verified " + approverIdentToFix, |
| "Label: Custom-Label-1=-1 " + approverIdentToFix, |
| "Label: Verified=+1", |
| "Label: Custom-Label-1=+1", |
| "Label: Custom-Label-2=+2 " + approverIdentToFix, |
| "Label: Custom-Label-3=0 " + approverIdentToFix), |
| getAuthorIdent(changeOwner.getAccount()))) |
| .add( |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| /* changeMessage= */ null, |
| "Label: -Verified " + changeOwnerIdentToFix, |
| "Label: Custom-Label-1=+1"), |
| getAuthorIdent(otherUser.getAccount()))) |
| .build(); |
| |
| Ref metaRefBeforeRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<RevCommit> commitsBeforeRewrite = logMetaRef(repo, metaRefBeforeRewrite); |
| |
| ImmutableList<Integer> invalidCommits = |
| commitsToFix.stream() |
| .map(commit -> commitsBeforeRewrite.indexOf(commit)) |
| .collect(toImmutableList()); |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| Instant updateTimestamp = serverIdent.getWhenAsInstant(); |
| ImmutableList<PatchSetApproval> expectedApprovals = |
| ImmutableList.of( |
| PatchSetApproval.builder() |
| .key( |
| PatchSetApproval.key( |
| c.currentPatchSetId(), |
| changeOwner.getAccountId(), |
| LabelId.create(VERIFIED))) |
| .value(0) |
| .granted(updateTimestamp) |
| .build(), |
| PatchSetApproval.builder() |
| .key( |
| PatchSetApproval.key( |
| c.currentPatchSetId(), |
| changeOwner.getAccountId(), |
| LabelId.create("Custom-Label-1"))) |
| .value(+1) |
| .granted(updateTimestamp) |
| .build(), |
| PatchSetApproval.builder() |
| .key( |
| PatchSetApproval.key( |
| c.currentPatchSetId(), otherUserId, LabelId.create(VERIFIED))) |
| .value(0) |
| .granted(updateTimestamp) |
| .build(), |
| PatchSetApproval.builder() |
| .key( |
| PatchSetApproval.key( |
| c.currentPatchSetId(), otherUserId, LabelId.create("Custom-Label-1"))) |
| .value(+1) |
| .granted(updateTimestamp) |
| .build(), |
| PatchSetApproval.builder() |
| .key( |
| PatchSetApproval.key( |
| c.currentPatchSetId(), otherUserId, LabelId.create("Custom-Label-2"))) |
| .value(+2) |
| .granted(updateTimestamp) |
| .build(), |
| PatchSetApproval.builder() |
| .key( |
| PatchSetApproval.key( |
| c.currentPatchSetId(), otherUserId, LabelId.create("Custom-Label-3"))) |
| .value(0) |
| .granted(updateTimestamp) |
| .build()); |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| |
| assertThat(notesBeforeRewrite.getApprovals().all().get(c.currentPatchSetId())) |
| .containsExactlyElementsIn(expectedApprovals); |
| assertThat(notesAfterRewrite.getApprovals().all().get(c.currentPatchSetId())) |
| .containsExactlyElementsIn(expectedApprovals); |
| |
| Ref metaRefAfterRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| assertThat(metaRefAfterRewrite.getObjectId()).isNotEqualTo(metaRefBeforeRewrite.getObjectId()); |
| |
| ImmutableList<RevCommit> commitsAfterRewrite = logMetaRef(repo, metaRefAfterRewrite); |
| assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits); |
| assertFixedCommits(commitsToFix, result, c.getId()); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -7,2 +7,2 @@\n" |
| + "-Label: -Verified Other Account <2@gerrit>\n" |
| + "-Label: Custom-Label-1=-1 Other Account <2@gerrit>\n" |
| + "+Label: -Verified Gerrit User 2 <2@gerrit>\n" |
| + "+Label: Custom-Label-1=-1 Gerrit User 2 <2@gerrit>\n" |
| + "@@ -11,2 +11,2 @@\n" |
| + "-Label: Custom-Label-2=+2 Other Account <2@gerrit>\n" |
| + "-Label: Custom-Label-3=0 Other Account <2@gerrit>\n" |
| + "+Label: Custom-Label-2=+2 Gerrit User 2 <2@gerrit>\n" |
| + "+Label: Custom-Label-3=0 Gerrit User 2 <2@gerrit>\n", |
| "@@ -7 +7 @@\n" |
| + "-Label: -Verified Change Owner <1@gerrit>\n" |
| + "+Label: -Verified Gerrit User 1 <1@gerrit>\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixRemoveVoteChangeMessage() throws Exception { |
| Change c = newChange(); |
| String approverIdentToFix = getAccountIdentToFix(otherUser.getAccount()); |
| ChangeUpdate approvalUpdateByOtherUser = newUpdate(c, otherUser); |
| approvalUpdateByOtherUser.putApproval(CODE_REVIEW, (short) +2); |
| approvalUpdateByOtherUser.putApproval("Custom-Label", (short) -1); |
| approvalUpdateByOtherUser.putApprovalFor(changeOwner.getAccountId(), VERIFIED, (short) -1); |
| approvalUpdateByOtherUser.commit(); |
| |
| ImmutableList<ObjectId> commitsToFix = |
| new ImmutableList.Builder<ObjectId>() |
| .add( |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| /* changeMessage= */ "Removed Code-Review+2 by " + otherUser.getNameEmail(), |
| "Label: -Code-Review " + approverIdentToFix), |
| getAuthorIdent(changeOwner.getAccount()))) |
| .add( |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| /* changeMessage= */ "Removed Custom-Label-1 by " |
| + otherUser.getNameEmail(), |
| "Label: -Custom-Label " + getValidIdentAsString(otherUser.getAccount())), |
| getAuthorIdent(changeOwner.getAccount()))) |
| .add( |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| /* changeMessage= */ "Removed Verified+2 by " + changeOwner.getNameEmail(), |
| "Label: -Verified"), |
| getAuthorIdent(changeOwner.getAccount()))) |
| .build(); |
| |
| Ref metaRefBeforeRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<RevCommit> commitsBeforeRewrite = logMetaRef(repo, metaRefBeforeRewrite); |
| |
| ImmutableList<Integer> invalidCommits = |
| commitsToFix.stream() |
| .map(commit -> commitsBeforeRewrite.indexOf(commit)) |
| .collect(toImmutableList()); |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| Instant updateTimestamp = serverIdent.getWhenAsInstant(); |
| ImmutableList<PatchSetApproval> expectedApprovals = |
| ImmutableList.of( |
| PatchSetApproval.builder() |
| .key( |
| PatchSetApproval.key( |
| c.currentPatchSetId(), |
| changeOwner.getAccountId(), |
| LabelId.create(VERIFIED))) |
| .value(0) |
| .granted(updateTimestamp) |
| .build(), |
| PatchSetApproval.builder() |
| .key( |
| PatchSetApproval.key( |
| c.currentPatchSetId(), otherUserId, LabelId.create("Custom-Label"))) |
| .value(0) |
| .granted(updateTimestamp) |
| .build(), |
| PatchSetApproval.builder() |
| .key( |
| PatchSetApproval.key( |
| c.currentPatchSetId(), otherUserId, LabelId.create(CODE_REVIEW))) |
| .value(0) |
| .granted(updateTimestamp) |
| .build()); |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| assertThat(changeMessages(notesBeforeRewrite)) |
| .containsExactly( |
| "Removed Code-Review+2 by Other Account <other@account.com>", |
| "Removed Custom-Label-1 by Other Account <other@account.com>", |
| "Removed Verified+2 by Change Owner <change@owner.com>"); |
| |
| assertThat(notesBeforeRewrite.getApprovals().all().get(c.currentPatchSetId())) |
| .containsExactlyElementsIn(expectedApprovals); |
| assertThat(changeMessages(notesAfterRewrite)) |
| .containsExactly( |
| "Removed Code-Review+2 by <GERRIT_ACCOUNT_2>", |
| "Removed Custom-Label-1 by <GERRIT_ACCOUNT_2>", |
| "Removed Verified+2 by <GERRIT_ACCOUNT_1>"); |
| assertThat(notesAfterRewrite.getApprovals().all().get(c.currentPatchSetId())) |
| .containsExactlyElementsIn(expectedApprovals); |
| |
| Ref metaRefAfterRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| assertThat(metaRefAfterRewrite.getObjectId()).isNotEqualTo(metaRefBeforeRewrite.getObjectId()); |
| |
| ImmutableList<RevCommit> commitsAfterRewrite = logMetaRef(repo, metaRefAfterRewrite); |
| assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits); |
| assertFixedCommits(commitsToFix, result, c.getId()); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -6 +6 @@\n" |
| + "-Removed Code-Review+2 by Other Account <other@account.com>\n" |
| + "+Removed Code-Review+2 by <GERRIT_ACCOUNT_2>\n" |
| + "@@ -9 +9 @@\n" |
| + "-Label: -Code-Review Other Account <2@gerrit>\n" |
| + "+Label: -Code-Review Gerrit User 2 <2@gerrit>\n", |
| "@@ -6 +6 @@\n" |
| + "-Removed Custom-Label-1 by Other Account <other@account.com>\n" |
| + "+Removed Custom-Label-1 by <GERRIT_ACCOUNT_2>\n", |
| "@@ -6 +6 @@\n" |
| + "-Removed Verified+2 by Change Owner <change@owner.com>\n" |
| + "+Removed Verified+2 by <GERRIT_ACCOUNT_1>\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixRemoveVoteChangeMessageWithUnparsableAuthorIdent() throws Exception { |
| Change c = newChange(); |
| PersonIdent invalidAuthorIdent = |
| new PersonIdent( |
| changeOwner.getName(), "server@" + serverId, TimeUtil.now(), serverIdent.getZoneId()); |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| /* changeMessage= */ "Removed Verified+2 by " + otherUser.getNameEmail(), |
| "Label: -Verified"), |
| invalidAuthorIdent); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| // Other Account does not applier in any change updates, replaced with default |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -6 +6 @@\n" |
| + "-Removed Verified+2 by Other Account <other@account.com>\n" |
| + "+Removed Verified+2\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixRemoveVoteChangeMessageWithNoFooterLabel() throws Exception { |
| Change c = newChange(); |
| ChangeUpdate approvalUpdate = newUpdate(c, changeOwner); |
| approvalUpdate.putApproval(VERIFIED, (short) +2); |
| |
| approvalUpdate.putApprovalFor(otherUserId, VERIFIED, (short) -1); |
| approvalUpdate.commit(); |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| |
| // Even though footer is missing, accounts are matched among the account in change updates. |
| getChangeUpdateBody(c, /* changeMessage= */ "Removed Verified-1 by Other Account (0002)"), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, /* changeMessage= */ "Removed Verified+2 by " + changeOwner.getNameEmail()), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| // No rewrite for default |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody(c, /* changeMessage= */ "Removed Verified+2 by Gerrit Account"), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -6 +6 @@\n" |
| + "-Removed Verified-1 by Other Account (0002)\n" |
| + "+Removed Verified-1 by <GERRIT_ACCOUNT_2>\n", |
| "@@ -6 +6 @@\n" |
| + "-Removed Verified+2 by Change Owner <change@owner.com>\n" |
| + "+Removed Verified+2 by <GERRIT_ACCOUNT_1>\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixRemoveVoteChangeMessageWithNoFooterLabel_matchByEmail() throws Exception { |
| Change c = newChange(); |
| ChangeUpdate approvalUpdate = newUpdate(c, changeOwner); |
| approvalUpdate.putApproval(VERIFIED, (short) +2); |
| |
| approvalUpdate.putApprovalFor(otherUserId, VERIFIED, (short) -1); |
| approvalUpdate.commit(); |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| /* changeMessage= */ "Removed Verified+2 by Renamed Change Owner <change@owner.com>"), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -6 +6 @@\n" |
| + "-Removed Verified+2 by Renamed Change Owner <change@owner.com>\n" |
| + "+Removed Verified+2 by <GERRIT_ACCOUNT_1>\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixRemoveVoteChangeMessageWithNoFooterLabel_matchByName() throws Exception { |
| Change c = newChange(); |
| ChangeUpdate approvalUpdate = newUpdate(c, changeOwner); |
| approvalUpdate.putApproval(VERIFIED, (short) +2); |
| |
| approvalUpdate.putApprovalFor(otherUserId, VERIFIED, (short) -1); |
| approvalUpdate.commit(); |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody(c, /* changeMessage= */ "Removed Verified+2 by Change Owner"), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -6 +6 @@\n" |
| + "-Removed Verified+2 by Change Owner\n" |
| + "+Removed Verified+2 by <GERRIT_ACCOUNT_1>\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixRemoveVoteChangeMessageWithNoFooterLabel_matchDuplicateAccounts() |
| throws Exception { |
| Account duplicateCodeOwner = |
| Account.builder(Account.id(4), TimeUtil.now()) |
| .setFullName(changeOwner.getName()) |
| .setPreferredEmail("other@test.com") |
| .build(); |
| accountCache.put(duplicateCodeOwner); |
| Change c = newChange(); |
| ChangeUpdate approvalUpdate = newUpdate(c, changeOwner); |
| approvalUpdate.putApproval(VERIFIED, (short) +2); |
| |
| approvalUpdate.putApprovalFor(duplicateCodeOwner.id(), VERIFIED, (short) -1); |
| approvalUpdate.commit(); |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, /* changeMessage= */ "Removed Verified+2 by Change Owner <other@test.com>"), |
| getAuthorIdent(changeOwner.getAccount())); |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, /* changeMessage= */ "Removed Verified+2 by Change Owner <change@owner.com>"), |
| getAuthorIdent(changeOwner.getAccount())); |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, /* changeMessage= */ "Removed Verified-1 by Change Owner <other@test.com>"), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -6 +6 @@\n" |
| + "-Removed Verified+2 by Change Owner <other@test.com>\n" |
| + "+Removed Verified+2 by <GERRIT_ACCOUNT_4>\n", |
| "@@ -6 +6 @@\n" |
| + "-Removed Verified+2 by Change Owner <change@owner.com>\n" |
| + "+Removed Verified+2 by <GERRIT_ACCOUNT_1>\n", |
| "@@ -6 +6 @@\n" |
| + "-Removed Verified-1 by Change Owner <other@test.com>\n" |
| + "+Removed Verified-1 by <GERRIT_ACCOUNT_4>\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixRemoveVotesChangeMessage() throws Exception { |
| Change c = newChange(); |
| ChangeUpdate approvalUpdate = newUpdate(c, changeOwner); |
| approvalUpdate.putApproval(VERIFIED, (short) +2); |
| |
| approvalUpdate.putApprovalFor(otherUserId, VERIFIED, (short) -1); |
| approvalUpdate.commit(); |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| |
| // Even though footer is missing, accounts are matched among the account in change updates. |
| getChangeUpdateBody( |
| c, |
| /* changeMessage= */ "Removed the following votes:\n" |
| + String.format("* Verified-1 by %s\n", otherUser.getNameEmail())), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| /* changeMessage= */ "Removed the following votes:\n" |
| + String.format("* Verified+2 by %s\n", changeOwner.getNameEmail()) |
| + String.format("* Verified-1 by %s\n", changeOwner.getNameEmail()) |
| + String.format("* Code-Review by %s\n", otherUser.getNameEmail())), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| // No rewrite for default |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| /* changeMessage= */ "Removed the following votes:\n" |
| + "* Verified+2 by Gerrit Account\n" |
| + "* Verified-1 by <GERRIT_ACCOUNT_2>\n"), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -7 +7 @@\n" |
| + "-* Verified-1 by Other Account <other@account.com>\n" |
| + "+* Verified-1 by <GERRIT_ACCOUNT_2>\n", |
| "@@ -7,3 +7,3 @@\n" |
| + "-* Verified+2 by Change Owner <change@owner.com>\n" |
| + "-* Verified-1 by Change Owner <change@owner.com>\n" |
| + "-* Code-Review by Other Account <other@account.com>\n" |
| + "+* Verified+2 by <GERRIT_ACCOUNT_1>\n" |
| + "+* Verified-1 by <GERRIT_ACCOUNT_1>\n" |
| + "+* Code-Review by <GERRIT_ACCOUNT_2>\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixAttentionFooter() throws Exception { |
| Change c = newChange(); |
| ImmutableList.Builder<ObjectId> commitsToFix = new ImmutableList.Builder<>(); |
| // Only 'reason' fix is required |
| ChangeUpdate invalidAttentionSetUpdate = newUpdate(c, changeOwner); |
| invalidAttentionSetUpdate.putReviewer(otherUserId, REVIEWER); |
| invalidAttentionSetUpdate.addToPlannedAttentionSetUpdates( |
| AttentionSetUpdate.createForWrite( |
| otherUserId, |
| Operation.ADD, |
| String.format("Added by %s using the hovercard menu", otherUser.getName()))); |
| commitsToFix.add(invalidAttentionSetUpdate.commit()); |
| ChangeUpdate invalidMultipleAttentionSetUpdate = newUpdate(c, changeOwner); |
| invalidMultipleAttentionSetUpdate.addToPlannedAttentionSetUpdates( |
| AttentionSetUpdate.createForWrite( |
| changeOwner.getAccountId(), |
| Operation.ADD, |
| String.format("%s replied on the change", otherUser.getName()))); |
| invalidMultipleAttentionSetUpdate.addToPlannedAttentionSetUpdates( |
| AttentionSetUpdate.createForWrite( |
| otherUserId, |
| Operation.REMOVE, |
| String.format("Removed by %s using the hovercard menu", otherUser.getName()))); |
| commitsToFix.add(invalidMultipleAttentionSetUpdate.commit()); |
| String otherUserIdentToFix = getAccountIdentToFix(otherUser.getAccount()); |
| String changeOwnerIdentToFix = getAccountIdentToFix(changeOwner.getAccount()); |
| commitsToFix.add( |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| /* changeMessage= */ null, |
| // Only 'person_ident' fix is required |
| "Attention: " |
| + gson.toJson( |
| new AttentionStatusInNoteDb( |
| otherUserIdentToFix, |
| Operation.ADD, |
| "Added by someone using the hovercard menu")), |
| // Both 'reason' and 'person_ident' fix is required |
| "Attention: " |
| + gson.toJson( |
| new AttentionStatusInNoteDb( |
| changeOwnerIdentToFix, |
| Operation.REMOVE, |
| String.format("%s replied on the change", otherUser.getName())))), |
| getAuthorIdent(changeOwner.getAccount()))); |
| |
| ChangeUpdate validAttentionSetUpdate = newUpdate(c, changeOwner); |
| validAttentionSetUpdate.addToPlannedAttentionSetUpdates( |
| AttentionSetUpdate.createForWrite(otherUserId, Operation.REMOVE, "Removed by someone")); |
| validAttentionSetUpdate.addToPlannedAttentionSetUpdates( |
| AttentionSetUpdate.createForWrite( |
| changeOwner.getAccountId(), Operation.ADD, "Added by someone")); |
| validAttentionSetUpdate.commit(); |
| |
| ChangeUpdate invalidRemovedByClickUpdate = newUpdate(c, changeOwner); |
| invalidRemovedByClickUpdate.addToPlannedAttentionSetUpdates( |
| AttentionSetUpdate.createForWrite( |
| changeOwner.getAccountId(), |
| Operation.REMOVE, |
| String.format("Removed by %s by clicking the attention icon", otherUser.getName()))); |
| commitsToFix.add(invalidRemovedByClickUpdate.commit()); |
| |
| Ref metaRefBeforeRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<RevCommit> commitsBeforeRewrite = logMetaRef(repo, metaRefBeforeRewrite); |
| |
| ImmutableList<Integer> invalidCommits = |
| commitsToFix.build().stream() |
| .map(commit -> commitsBeforeRewrite.indexOf(commit)) |
| .collect(toImmutableList()); |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| Instant updateTimestamp = serverIdent.getWhenAsInstant(); |
| ImmutableList<AttentionSetUpdate> attentionSetUpdatesBeforeRewrite = |
| ImmutableList.of( |
| AttentionSetUpdate.createFromRead( |
| invalidRemovedByClickUpdate.getWhen(), |
| changeOwner.getAccountId(), |
| Operation.REMOVE, |
| String.format("Removed by %s by clicking the attention icon", otherUser.getName())), |
| AttentionSetUpdate.createFromRead( |
| validAttentionSetUpdate.getWhen(), |
| changeOwner.getAccountId(), |
| Operation.ADD, |
| "Added by someone"), |
| AttentionSetUpdate.createFromRead( |
| validAttentionSetUpdate.getWhen(), |
| otherUserId, |
| Operation.REMOVE, |
| "Removed by someone"), |
| AttentionSetUpdate.createFromRead( |
| updateTimestamp, |
| changeOwner.getAccountId(), |
| Operation.REMOVE, |
| String.format("%s replied on the change", otherUser.getName())), |
| AttentionSetUpdate.createFromRead( |
| updateTimestamp, |
| otherUserId, |
| Operation.ADD, |
| "Added by someone using the hovercard menu"), |
| AttentionSetUpdate.createFromRead( |
| invalidMultipleAttentionSetUpdate.getWhen(), |
| otherUserId, |
| Operation.REMOVE, |
| String.format("Removed by %s using the hovercard menu", otherUser.getName())), |
| AttentionSetUpdate.createFromRead( |
| invalidMultipleAttentionSetUpdate.getWhen(), |
| changeOwner.getAccountId(), |
| Operation.ADD, |
| String.format("%s replied on the change", otherUser.getName())), |
| AttentionSetUpdate.createFromRead( |
| invalidAttentionSetUpdate.getWhen(), |
| otherUserId, |
| Operation.ADD, |
| String.format("Added by %s using the hovercard menu", otherUser.getName()))); |
| |
| ImmutableList<AttentionSetUpdate> attentionSetUpdatesAfterRewrite = |
| ImmutableList.of( |
| AttentionSetUpdate.createFromRead( |
| invalidRemovedByClickUpdate.getWhen(), |
| changeOwner.getAccountId(), |
| Operation.REMOVE, |
| "Removed by someone by clicking the attention icon"), |
| AttentionSetUpdate.createFromRead( |
| validAttentionSetUpdate.getWhen(), |
| changeOwner.getAccountId(), |
| Operation.ADD, |
| "Added by someone"), |
| AttentionSetUpdate.createFromRead( |
| validAttentionSetUpdate.getWhen(), |
| otherUserId, |
| Operation.REMOVE, |
| "Removed by someone"), |
| AttentionSetUpdate.createFromRead( |
| updateTimestamp, |
| changeOwner.getAccountId(), |
| Operation.REMOVE, |
| "Someone replied on the change"), |
| AttentionSetUpdate.createFromRead( |
| updateTimestamp, |
| otherUserId, |
| Operation.ADD, |
| "Added by someone using the hovercard menu"), |
| AttentionSetUpdate.createFromRead( |
| invalidMultipleAttentionSetUpdate.getWhen(), |
| otherUserId, |
| Operation.REMOVE, |
| "Removed by someone using the hovercard menu"), |
| AttentionSetUpdate.createFromRead( |
| invalidMultipleAttentionSetUpdate.getWhen(), |
| changeOwner.getAccountId(), |
| Operation.ADD, |
| "Someone replied on the change"), |
| AttentionSetUpdate.createFromRead( |
| invalidAttentionSetUpdate.getWhen(), |
| otherUserId, |
| Operation.ADD, |
| "Added by someone using the hovercard menu")); |
| |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| |
| assertThat(notesBeforeRewrite.getAttentionSetUpdates()) |
| .containsExactlyElementsIn(attentionSetUpdatesBeforeRewrite); |
| assertThat(notesAfterRewrite.getAttentionSetUpdates()) |
| .containsExactlyElementsIn(attentionSetUpdatesAfterRewrite); |
| |
| Ref metaRefAfterRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| assertThat(metaRefAfterRewrite.getObjectId()).isNotEqualTo(metaRefBeforeRewrite.getObjectId()); |
| |
| ImmutableList<RevCommit> commitsAfterRewrite = logMetaRef(repo, metaRefAfterRewrite); |
| assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits); |
| assertFixedCommits(commitsToFix.build(), result, c.getId()); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff).hasSize(4); |
| assertThat(commitHistoryDiff.get(0)) |
| .isEqualTo( |
| "@@ -8 +8 @@\n" |
| + "-Attention: {\"person_ident\":\"Gerrit User 2" |
| + " \\u003c2@gerrit\\u003e\",\"operation\":\"ADD\",\"reason\":\"Added by Other" |
| + " Account using the hovercard menu\"}\n" |
| + "+Attention: {\"person_ident\":\"Gerrit User 2" |
| + " \\u003c2@gerrit\\u003e\",\"operation\":\"ADD\",\"reason\":\"Added by someone" |
| + " using the hovercard menu\"}\n"); |
| assertThat(Arrays.asList(commitHistoryDiff.get(1).split("\n"))) |
| .containsExactly( |
| "@@ -7,2 +7,2 @@", |
| "-Attention: {\"person_ident\":\"Gerrit User 1" |
| + " \\u003c1@gerrit\\u003e\",\"operation\":\"ADD\",\"reason\":\"Other Account" |
| + " replied on the change\"}", |
| "-Attention: {\"person_ident\":\"Gerrit User 2" |
| + " \\u003c2@gerrit\\u003e\",\"operation\":\"REMOVE\",\"reason\":\"Removed by Other" |
| + " Account using the hovercard menu\"}", |
| "+Attention: {\"person_ident\":\"Gerrit User 1" |
| + " \\u003c1@gerrit\\u003e\",\"operation\":\"ADD\",\"reason\":\"Someone replied on" |
| + " the change\"}", |
| "+Attention: {\"person_ident\":\"Gerrit User 2" |
| + " \\u003c2@gerrit\\u003e\",\"operation\":\"REMOVE\",\"reason\":\"Removed by" |
| + " someone using the hovercard menu\"}"); |
| assertThat(Arrays.asList(commitHistoryDiff.get(2).split("\n"))) |
| .containsExactly( |
| "@@ -7,2 +7,2 @@", |
| "-Attention: {\"person_ident\":\"Other Account" |
| + " \\u003c2@gerrit\\u003e\",\"operation\":\"ADD\",\"reason\":\"Added by someone" |
| + " using the hovercard menu\"}", |
| "-Attention: {\"person_ident\":\"Change Owner" |
| + " \\u003c1@gerrit\\u003e\",\"operation\":\"REMOVE\",\"reason\":\"Other Account" |
| + " replied on the change\"}", |
| "+Attention: {\"person_ident\":\"Gerrit User 2" |
| + " \\u003c2@gerrit\\u003e\",\"operation\":\"ADD\",\"reason\":\"Added by someone" |
| + " using the hovercard menu\"}", |
| "+Attention: {\"person_ident\":\"Gerrit User 1" |
| + " \\u003c1@gerrit\\u003e\",\"operation\":\"REMOVE\",\"reason\":\"Someone replied" |
| + " on the change\"}"); |
| assertThat(commitHistoryDiff.get(3)) |
| .isEqualTo( |
| "@@ -7 +7 @@\n" |
| + "-Attention: {\"person_ident\":\"Gerrit User 1" |
| + " \\u003c1@gerrit\\u003e\",\"operation\":\"REMOVE\",\"reason\":\"Removed by Other" |
| + " Account by clicking the attention icon\"}\n" |
| + "+Attention: {\"person_ident\":\"Gerrit User 1" |
| + " \\u003c1@gerrit\\u003e\",\"operation\":\"REMOVE\",\"reason\":\"Removed by" |
| + " someone by clicking the attention icon\"}\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixAttentionFooter_okReason_noRewrite() throws Exception { |
| Change c = newChange(); |
| ImmutableList<String> okAccountNames = |
| ImmutableList.of( |
| "Someone", |
| "Someone else", |
| "someone", |
| "someone else", |
| "Anonymous", |
| "anonymous", |
| "<GERRIT_ACCOUNT_1>", |
| "<GERRIT_ACCOUNT_2>"); |
| ImmutableList.Builder<AttentionSetUpdate> attentionSetUpdatesBeforeRewrite = |
| new ImmutableList.Builder<>(); |
| for (String okAccountName : okAccountNames) { |
| ChangeUpdate firstAttentionSetUpdate = newUpdate(c, changeOwner); |
| firstAttentionSetUpdate.putReviewer(otherUserId, REVIEWER); |
| firstAttentionSetUpdate.addToPlannedAttentionSetUpdates( |
| AttentionSetUpdate.createForWrite( |
| otherUserId, |
| Operation.ADD, |
| String.format("Added by %s using the hovercard menu", okAccountName))); |
| firstAttentionSetUpdate.commit(); |
| ChangeUpdate secondAttentionSetUpdate = newUpdate(c, changeOwner); |
| secondAttentionSetUpdate.addToPlannedAttentionSetUpdates( |
| AttentionSetUpdate.createForWrite( |
| changeOwner.getAccountId(), |
| Operation.ADD, |
| String.format("%s replied on the change", okAccountName))); |
| secondAttentionSetUpdate.addToPlannedAttentionSetUpdates( |
| AttentionSetUpdate.createForWrite( |
| otherUserId, |
| Operation.REMOVE, |
| String.format("Removed by %s using the hovercard menu", okAccountName))); |
| secondAttentionSetUpdate.commit(); |
| ChangeUpdate thirdAttentionSetUpdate = newUpdate(c, changeOwner); |
| thirdAttentionSetUpdate.addToPlannedAttentionSetUpdates( |
| AttentionSetUpdate.createForWrite( |
| changeOwner.getAccountId(), |
| Operation.REMOVE, |
| String.format("Removed by %s by clicking the attention icon", okAccountName))); |
| thirdAttentionSetUpdate.commit(); |
| attentionSetUpdatesBeforeRewrite.add( |
| AttentionSetUpdate.createFromRead( |
| thirdAttentionSetUpdate.getWhen(), |
| changeOwner.getAccountId(), |
| Operation.REMOVE, |
| String.format("Removed by %s by clicking the attention icon", okAccountName)), |
| AttentionSetUpdate.createFromRead( |
| secondAttentionSetUpdate.getWhen(), |
| otherUserId, |
| Operation.REMOVE, |
| String.format("Removed by %s using the hovercard menu", okAccountName)), |
| AttentionSetUpdate.createFromRead( |
| secondAttentionSetUpdate.getWhen(), |
| changeOwner.getAccountId(), |
| Operation.ADD, |
| String.format("%s replied on the change", okAccountName)), |
| AttentionSetUpdate.createFromRead( |
| firstAttentionSetUpdate.getWhen(), |
| otherUserId, |
| Operation.ADD, |
| String.format("Added by %s using the hovercard menu", okAccountName))); |
| } |
| |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| assertThat(notesBeforeRewrite.getAttentionSetUpdates()) |
| .containsExactlyElementsIn(attentionSetUpdatesBeforeRewrite.build()); |
| |
| Ref metaRefBefore = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult backfillResult = rewriter.backfillProject(project, repo, options); |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| Ref metaRefAfter = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| assertThat(notesBeforeRewrite.getMetaId()).isEqualTo(notesAfterRewrite.getMetaId()); |
| assertThat(metaRefBefore.getObjectId()).isEqualTo(metaRefAfter.getObjectId()); |
| assertThat(backfillResult.fixedRefDiff).isEmpty(); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixSubmitChangeMessage() throws Exception { |
| Change c = newChange(); |
| ImmutableList.Builder<ObjectId> commitsToFix = new ImmutableList.Builder<>(); |
| ChangeUpdate invalidMergedMessageUpdate = newUpdate(c, changeOwner); |
| invalidMergedMessageUpdate.setChangeMessage( |
| "Change has been successfully merged by " + changeOwner.getName()); |
| invalidMergedMessageUpdate.setTopic("", topicValidator); |
| |
| commitsToFix.add(invalidMergedMessageUpdate.commit()); |
| ChangeUpdate invalidCherryPickedMessageUpdate = newUpdate(c, changeOwner); |
| invalidCherryPickedMessageUpdate.setChangeMessage( |
| "Change has been successfully cherry-picked as e40dc1a50dc7f457a37579e2755374f3e1a5413b by " |
| + changeOwner.getName()); |
| |
| commitsToFix.add(invalidCherryPickedMessageUpdate.commit()); |
| ChangeUpdate invalidRebasedMessageUpdate = newUpdate(c, changeOwner); |
| invalidRebasedMessageUpdate.setChangeMessage( |
| "Change has been successfully rebased and submitted as" |
| + " e40dc1a50dc7f457a37579e2755374f3e1a5413b by " |
| + changeOwner.getName()); |
| |
| commitsToFix.add(invalidRebasedMessageUpdate.commit()); |
| ChangeUpdate validSubmitMessageUpdate = newUpdate(c, changeOwner); |
| validSubmitMessageUpdate.setChangeMessage( |
| "Change has been successfully rebased and submitted as" |
| + " e40dc1a50dc7f457a37579e2755374f3e1a5413b"); |
| validSubmitMessageUpdate.commit(); |
| |
| Ref metaRefBeforeRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<RevCommit> commitsBeforeRewrite = logMetaRef(repo, metaRefBeforeRewrite); |
| |
| ImmutableList<Integer> invalidCommits = |
| commitsToFix.build().stream() |
| .map(commit -> commitsBeforeRewrite.indexOf(commit)) |
| .collect(toImmutableList()); |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| |
| assertThat(changeMessages(notesBeforeRewrite)) |
| .containsExactly( |
| "Change has been successfully merged by Change Owner", |
| "Change has been successfully cherry-picked as e40dc1a50dc7f457a37579e2755374f3e1a5413b" |
| + " by Change Owner", |
| "Change has been successfully rebased and submitted as" |
| + " e40dc1a50dc7f457a37579e2755374f3e1a5413b by Change Owner", |
| "Change has been successfully rebased and submitted as" |
| + " e40dc1a50dc7f457a37579e2755374f3e1a5413b"); |
| assertThat(changeMessages(notesAfterRewrite)) |
| .containsExactly( |
| "Change has been successfully merged", |
| "Change has been successfully cherry-picked as" |
| + " e40dc1a50dc7f457a37579e2755374f3e1a5413b", |
| "Change has been successfully rebased and submitted as" |
| + " e40dc1a50dc7f457a37579e2755374f3e1a5413b", |
| "Change has been successfully rebased and submitted as" |
| + " e40dc1a50dc7f457a37579e2755374f3e1a5413b"); |
| |
| Ref metaRefAfterRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| assertThat(metaRefAfterRewrite.getObjectId()).isNotEqualTo(metaRefBeforeRewrite.getObjectId()); |
| |
| ImmutableList<RevCommit> commitsAfterRewrite = logMetaRef(repo, metaRefAfterRewrite); |
| assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits); |
| assertFixedCommits(commitsToFix.build(), result, c.getId()); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -6 +6 @@\n" |
| + "-Change has been successfully merged by Change Owner\n" |
| + "+Change has been successfully merged\n", |
| "@@ -6 +6 @@\n" |
| + "-Change has been successfully cherry-picked as" |
| + " e40dc1a50dc7f457a37579e2755374f3e1a5413b by Change Owner\n" |
| + "+Change has been successfully cherry-picked as" |
| + " e40dc1a50dc7f457a37579e2755374f3e1a5413b\n", |
| "@@ -6 +6 @@\n" |
| + "-Change has been successfully rebased and submitted as" |
| + " e40dc1a50dc7f457a37579e2755374f3e1a5413b by Change Owner\n" |
| + "+Change has been successfully rebased and submitted as" |
| + " e40dc1a50dc7f457a37579e2755374f3e1a5413b\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixSubmitChangeMessageAndFooters() throws Exception { |
| Change c = newChange(); |
| PersonIdent invalidAuthorIdent = |
| new PersonIdent( |
| changeOwner.getName(), |
| changeNoteUtil.getAccountIdAsEmailAddress(changeOwner.getAccountId()), |
| TimeUtil.now(), |
| serverIdent.getZoneId()); |
| String changeOwnerIdentToFix = getAccountIdentToFix(changeOwner.getAccount()); |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| "Change has been successfully merged by " + changeOwner.getName(), |
| "Status: merged", |
| "Tag: autogenerated:gerrit:merged", |
| "Reviewer: " + changeOwnerIdentToFix, |
| "Label: SUBM=+1", |
| "Submission-id: 6310-1521542139810-cfb7e159", |
| "Submitted-with: OK", |
| "Submitted-with: OK: Code-Review: " + changeOwnerIdentToFix), |
| invalidAuthorIdent); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -1 +1 @@\n" |
| + "-author Change Owner <1@gerrit> 1254344405 -0700\n" |
| + "+author Gerrit User 1 <1@gerrit> 1254344405 -0700\n" |
| + "@@ -6 +6 @@\n" |
| + "-Change has been successfully merged by Change Owner\n" |
| + "+Change has been successfully merged\n" |
| + "@@ -11 +11 @@\n" |
| + "-Reviewer: Change Owner <1@gerrit>\n" |
| + "+Reviewer: Gerrit User 1 <1@gerrit>\n" |
| + "@@ -15 +15 @@\n" |
| + "-Submitted-with: OK: Code-Review: Change Owner <1@gerrit>\n" |
| + "+Submitted-with: OK: Code-Review: Gerrit User 1 <1@gerrit>\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixSubmittedWithFooterIdent() throws Exception { |
| Change c = newChange(); |
| |
| ChangeUpdate preSubmitUpdate = newUpdate(c, changeOwner); |
| preSubmitUpdate.setChangeMessage("Per-submit update"); |
| preSubmitUpdate.commit(); |
| |
| String otherUserIdentToFix = getAccountIdentToFix(otherUser.getAccount()); |
| String changeOwnerIdentToFix = getAccountIdentToFix(changeOwner.getAccount()); |
| RevCommit invalidUpdateCommit = |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| /* changeMessage= */ null, |
| "Label: SUBM=+1", |
| "Submission-id: 5271-1496917120975-10a10df9", |
| "Submitted-with: NOT_READY", |
| "Submitted-with: NEED: Code-Review: " + otherUserIdentToFix, |
| "Submitted-with: OK: Code-Style", |
| "Submitted-with: OK: Verified: " + changeOwnerIdentToFix, |
| "Submitted-with: FORCED with error"), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| ChangeUpdate postSubmitUpdate = newUpdate(c, changeOwner); |
| postSubmitUpdate.setChangeMessage("Per-submit update"); |
| postSubmitUpdate.commit(); |
| Ref metaRefBeforeRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<RevCommit> commitsBeforeRewrite = logMetaRef(repo, metaRefBeforeRewrite); |
| |
| int invalidCommitIndex = commitsBeforeRewrite.indexOf(invalidUpdateCommit); |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| ImmutableList<SubmitRecord> expectedRecords = |
| ImmutableList.of( |
| submitRecord( |
| "NOT_READY", |
| null, |
| submitLabel(CODE_REVIEW, "NEED", otherUserId), |
| submitLabel("Code-Style", "OK", null), |
| submitLabel(VERIFIED, "OK", changeOwner.getAccountId())), |
| submitRecord("FORCED", " with error")); |
| assertThat(notesBeforeRewrite.getSubmitRecords()).isEqualTo(expectedRecords); |
| assertThat(notesAfterRewrite.getSubmitRecords()).isEqualTo(expectedRecords); |
| |
| Ref metaRefAfterRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| assertThat(metaRefAfterRewrite.getObjectId()).isNotEqualTo(metaRefBeforeRewrite.getObjectId()); |
| |
| ImmutableList<RevCommit> commitsAfterRewrite = logMetaRef(repo, metaRefAfterRewrite); |
| assertValidCommits( |
| commitsBeforeRewrite, commitsAfterRewrite, ImmutableList.of(invalidCommitIndex)); |
| assertFixedCommits(ImmutableList.of(invalidUpdateCommit.getId()), result, c.getId()); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -10 +10 @@\n" |
| + "-Submitted-with: NEED: Code-Review: Other Account <2@gerrit>\n" |
| + "+Submitted-with: NEED: Code-Review: Gerrit User 2 <2@gerrit>\n" |
| + "@@ -12 +12 @@\n" |
| + "-Submitted-with: OK: Verified: Change Owner <1@gerrit>\n" |
| + "+Submitted-with: OK: Verified: Gerrit User 1 <1@gerrit>\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixDeleteChangeMessageCommitMessage() throws Exception { |
| Change c = newChange(); |
| ImmutableList.Builder<ObjectId> commitsToFix = new ImmutableList.Builder<>(); |
| ChangeUpdate invalidDeleteChangeMessageUpdate = newUpdate(c, changeOwner); |
| invalidDeleteChangeMessageUpdate.setChangeMessage( |
| "Change message removed by: " + changeOwner.getName()); |
| commitsToFix.add(invalidDeleteChangeMessageUpdate.commit()); |
| ChangeUpdate invalidDeleteChangeMessageUpdateWithReason = newUpdate(c, changeOwner); |
| invalidDeleteChangeMessageUpdateWithReason.setChangeMessage( |
| String.format( |
| "Change message removed by: %s\nReason: %s", |
| changeOwner.getName(), "contains confidential information")); |
| commitsToFix.add(invalidDeleteChangeMessageUpdateWithReason.commit()); |
| ChangeUpdate validDeleteChangeMessageUpdate = newUpdate(c, changeOwner); |
| validDeleteChangeMessageUpdate.setChangeMessage( |
| "Change message removed by: <GERRIT_ACCOUNT_1>"); |
| validDeleteChangeMessageUpdate.commit(); |
| ChangeUpdate validDeleteChangeMessageUpdateWithReason = newUpdate(c, changeOwner); |
| validDeleteChangeMessageUpdateWithReason.setChangeMessage( |
| "Change message removed by: <GERRIT_ACCOUNT_1>\nReason: abusive language"); |
| validDeleteChangeMessageUpdateWithReason.commit(); |
| |
| Ref metaRefBeforeRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<RevCommit> commitsBeforeRewrite = logMetaRef(repo, metaRefBeforeRewrite); |
| |
| ImmutableList<Integer> invalidCommits = |
| commitsToFix.build().stream() |
| .map(commit -> commitsBeforeRewrite.indexOf(commit)) |
| .collect(toImmutableList()); |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| |
| assertThat(changeMessages(notesBeforeRewrite)) |
| .containsExactly( |
| "Change message removed by: Change Owner", |
| "Change message removed by: Change Owner\n" |
| + "Reason: contains confidential information", |
| "Change message removed by: <GERRIT_ACCOUNT_1>", |
| "Change message removed by: <GERRIT_ACCOUNT_1>\n" + "Reason: abusive language"); |
| assertThat(changeMessages(notesAfterRewrite)) |
| .containsExactly( |
| "Change message removed", |
| "Change message removed\n" + "Reason: contains confidential information", |
| "Change message removed by: <GERRIT_ACCOUNT_1>", |
| "Change message removed by: <GERRIT_ACCOUNT_1>\n" + "Reason: abusive language"); |
| |
| Ref metaRefAfterRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| assertThat(metaRefAfterRewrite.getObjectId()).isNotEqualTo(metaRefBeforeRewrite.getObjectId()); |
| |
| ImmutableList<RevCommit> commitsAfterRewrite = logMetaRef(repo, metaRefAfterRewrite); |
| assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits); |
| assertFixedCommits(commitsToFix.build(), result, c.getId()); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -6 +6 @@\n" |
| + "-Change message removed by: Change Owner\n" |
| + "+Change message removed\n", |
| "@@ -6 +6 @@\n" |
| + "-Change message removed by: Change Owner\n" |
| + "+Change message removed\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixCodeOwnersOnAddReviewerChangeMessage() throws Exception { |
| |
| Account reviewer = |
| Account.builder(Account.id(3), TimeUtil.now()) |
| .setFullName("Reviewer User") |
| .setPreferredEmail("reviewer@account.com") |
| .build(); |
| accountCache.put(reviewer); |
| Account duplicateCodeOwner = |
| Account.builder(Account.id(4), TimeUtil.now()).setFullName(changeOwner.getName()).build(); |
| accountCache.put(duplicateCodeOwner); |
| Account duplicateReviewer = |
| Account.builder(Account.id(5), TimeUtil.now()).setFullName(reviewer.getName()).build(); |
| accountCache.put(duplicateReviewer); |
| Change c = newChange(); |
| ImmutableList.Builder<ObjectId> commitsToFix = new ImmutableList.Builder<>(); |
| ChangeUpdate addReviewerUpdate = newCodeOwnerAddReviewerUpdate(c, changeOwner); |
| addReviewerUpdate.putReviewer(reviewer.id(), REVIEWER); |
| addReviewerUpdate.commit(); |
| ChangeUpdate invalidOnAddReviewerUpdate = newCodeOwnerAddReviewerUpdate(c, changeOwner); |
| invalidOnAddReviewerUpdate.setChangeMessage( |
| "Reviewer User who was added as reviewer owns the following files:\n" |
| + " * file1.java\n" |
| + " * file2.ts\n"); |
| commitsToFix.add(invalidOnAddReviewerUpdate.commit()); |
| ChangeUpdate addOtherReviewerUpdate = newCodeOwnerAddReviewerUpdate(c, changeOwner); |
| addOtherReviewerUpdate.putReviewer(otherUserId, REVIEWER); |
| addOtherReviewerUpdate.commit(); |
| ChangeUpdate invalidOnAddReviewerMultipleReviewerUpdate = |
| newCodeOwnerAddReviewerUpdate(c, changeOwner); |
| invalidOnAddReviewerMultipleReviewerUpdate.setChangeMessage( |
| "Reviewer User who was added as reviewer owns the following files:\n" |
| + " * file1.java\n" |
| + "\nOther Account who was added as reviewer owns the following files:\n" |
| + " * file3.js\n" |
| + "\nMissing Reviewer who was added as reviewer owns the following files:\n" |
| + " * file4.java\n"); |
| commitsToFix.add(invalidOnAddReviewerMultipleReviewerUpdate.commit()); |
| ChangeUpdate addDuplicateReviewerUpdate = newCodeOwnerAddReviewerUpdate(c, changeOwner); |
| addDuplicateReviewerUpdate.putReviewer(duplicateReviewer.id(), REVIEWER); |
| addDuplicateReviewerUpdate.commit(); |
| // Reviewer name resolves to multiple accounts in the same change |
| ChangeUpdate onAddReviewerUpdateWithDuplicate = newCodeOwnerAddReviewerUpdate(c, changeOwner); |
| onAddReviewerUpdateWithDuplicate.setChangeMessage( |
| "Reviewer User who was added as reviewer owns the following files:\n" |
| + " * file6.java\n"); |
| commitsToFix.add(onAddReviewerUpdateWithDuplicate.commit()); |
| |
| ChangeUpdate validOnAddReviewerUpdate = newCodeOwnerAddReviewerUpdate(c, changeOwner); |
| validOnAddReviewerUpdate.setChangeMessage( |
| "Gerrit Account who was added as reviewer owns the following files:\n" |
| + " * file1.java\n" |
| + "\n<GERRIT_ACCOUNT_1> who was added as reviewer owns the following files:\n" |
| + " * file3.js\n"); |
| validOnAddReviewerUpdate.commit(); |
| |
| Ref metaRefBeforeRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<RevCommit> commitsBeforeRewrite = logMetaRef(repo, metaRefBeforeRewrite); |
| |
| ImmutableList<Integer> invalidCommits = |
| commitsToFix.build().stream() |
| .map(commit -> commitsBeforeRewrite.indexOf(commit)) |
| .collect(toImmutableList()); |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| |
| assertThat(changeMessages(notesBeforeRewrite)).hasSize(4); |
| assertThat(changeMessages(notesAfterRewrite)) |
| .containsExactly( |
| "<GERRIT_ACCOUNT_3>, who was added as reviewer owns the following files:\n" |
| + " * file1.java\n" |
| + " * file2.ts\n", |
| "<GERRIT_ACCOUNT_3>, who was added as reviewer owns the following files:\n" |
| + " * file1.java\n" |
| + "\n<GERRIT_ACCOUNT_2>, who was added as reviewer owns the following files:\n" |
| + " * file3.js\n" |
| + "\nAdded reviewer owns the following files:\n" |
| + " * file4.java\n", |
| "Added reviewer owns the following files:\n" + " * file6.java\n", |
| "Gerrit Account who was added as reviewer owns the following files:\n" |
| + " * file1.java\n" |
| + "\n<GERRIT_ACCOUNT_1> who was added as reviewer owns the following files:\n" |
| + " * file3.js\n"); |
| |
| Ref metaRefAfterRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| assertThat(metaRefAfterRewrite.getObjectId()).isNotEqualTo(metaRefBeforeRewrite.getObjectId()); |
| |
| ImmutableList<RevCommit> commitsAfterRewrite = logMetaRef(repo, metaRefAfterRewrite); |
| assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits); |
| assertFixedCommits(commitsToFix.build(), result, c.getId()); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -6 +6 @@\n" |
| + "-Reviewer User who was added as reviewer owns the following files:\n" |
| + "+<GERRIT_ACCOUNT_3>, who was added as reviewer owns the following files:\n", |
| "@@ -6 +6 @@\n" |
| + "-Reviewer User who was added as reviewer owns the following files:\n" |
| + "+<GERRIT_ACCOUNT_3>, who was added as reviewer owns the following files:\n" |
| + "@@ -9 +9 @@\n" |
| + "-Other Account who was added as reviewer owns the following files:\n" |
| + "+<GERRIT_ACCOUNT_2>, who was added as reviewer owns the following files:\n" |
| + "@@ -12 +12 @@\n" |
| + "-Missing Reviewer who was added as reviewer owns the following files:\n" |
| + "+Added reviewer owns the following files:\n", |
| "@@ -6 +6 @@\n" |
| + "-Reviewer User who was added as reviewer owns the following files:\n" |
| + "+Added reviewer owns the following files:\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixCodeOwnersOnReviewChangeMessage() throws Exception { |
| |
| Change c = newChange(); |
| ImmutableList.Builder<ObjectId> commitsToFix = new ImmutableList.Builder<>(); |
| |
| ChangeUpdate invalidOnReviewUpdate = newUpdate(c, changeOwner); |
| invalidOnReviewUpdate.setChangeMessage( |
| "Patch Set 1: Any-Label+2 Other-Label+2 Code-Review+2\n\n" |
| + "By voting Code-Review+2 the following files are now code-owner approved by Change" |
| + " Owner:\n" |
| + " * file1.java\n" |
| + " * file2.ts\n" |
| + "By voting Any-Label+2 the code-owners submit requirement is overridden by Change" |
| + " Owner\n" |
| + "By voting Other-Label+2 the code-owners submit requirement is still overridden by" |
| + " Change Owner\n"); |
| commitsToFix.add(invalidOnReviewUpdate.commit()); |
| |
| ChangeUpdate invalidOnReviewUpdateAnyOrder = newUpdate(c, changeOwner); |
| invalidOnReviewUpdateAnyOrder.setChangeMessage( |
| "Patch Set 1: Any-Label+2 Other-Label+2 Code-Review+2\n\n" |
| + "By voting Any-Label+2 the code-owners submit requirement is overridden by Change" |
| + " Owner\n" |
| + "By voting Other-Label+2 the code-owners submit requirement is still overridden by" |
| + " Change Owner\n" |
| + "By voting Code-Review+2 the following files are now code-owner approved by Change" |
| + " Owner:\n" |
| + " * file1.java\n" |
| + " * file2.ts\n"); |
| commitsToFix.add(invalidOnReviewUpdateAnyOrder.commit()); |
| ChangeUpdate invalidOnApprovalUpdate = newUpdate(c, otherUser); |
| invalidOnApprovalUpdate.setChangeMessage( |
| "Patch Set 1: -Code-Review\n\n" |
| + "By removing the Code-Review+2 vote the following files are no longer explicitly" |
| + " code-owner approved by Other Account:\n" |
| + " * file1.java\n" |
| + " * file2.ts\n" |
| + "\n" |
| + "The listed files are still implicitly approved by Other Account.\n"); |
| commitsToFix.add(invalidOnApprovalUpdate.commit()); |
| |
| ChangeUpdate invalidOnOverrideUpdate = newUpdate(c, changeOwner); |
| invalidOnOverrideUpdate.setChangeMessage( |
| "Patch Set 1: -Owners-Override\n\n" |
| + "(1 comment)\n\n" |
| + "By removing the Owners-Override+1 vote the code-owners submit requirement is no" |
| + " longer overridden by Change Owner\n"); |
| |
| commitsToFix.add(invalidOnOverrideUpdate.commit()); |
| |
| ChangeUpdate partiallyValidOnReviewUpdate = newUpdate(c, changeOwner); |
| partiallyValidOnReviewUpdate.setChangeMessage( |
| "Patch Set 1: Any-Label+2 Code-Review+2\n\n" |
| + "By voting Code-Review+2 the following files are now code-owner approved by" |
| + " <GERRIT_ACCOUNT_1>:\n" |
| + " * file1.java\n" |
| + " * file2.ts\n" |
| + "By voting Any-Label+2 the code-owners submit requirement is overridden by Change" |
| + " Owner\n"); |
| commitsToFix.add(partiallyValidOnReviewUpdate.commit()); |
| |
| ChangeUpdate validOnApprovalUpdate = newUpdate(c, changeOwner); |
| validOnApprovalUpdate.setChangeMessage( |
| "Patch Set 1: Code-Review-2\n\n" |
| + "By voting Code-Review-2 the following files are no longer explicitly code-owner" |
| + " approved by <GERRIT_ACCOUNT_1>:\n" |
| + " * file4.java\n"); |
| validOnApprovalUpdate.commit(); |
| |
| ChangeUpdate validOnOverrideUpdate = newUpdate(c, changeOwner); |
| validOnOverrideUpdate.setChangeMessage( |
| "Patch Set 1: Owners-Override+1\n\n" |
| + "By voting Owners-Override+1 the code-owners submit requirement is still overridden" |
| + " by <GERRIT_ACCOUNT_1>\n"); |
| validOnOverrideUpdate.commit(); |
| |
| Ref metaRefBeforeRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<RevCommit> commitsBeforeRewrite = logMetaRef(repo, metaRefBeforeRewrite); |
| |
| ImmutableList<Integer> invalidCommits = |
| commitsToFix.build().stream() |
| .map(commit -> commitsBeforeRewrite.indexOf(commit)) |
| .collect(toImmutableList()); |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| |
| assertThat(changeMessages(notesBeforeRewrite)).hasSize(7); |
| assertThat(changeMessages(notesAfterRewrite)) |
| .containsExactly( |
| "Patch Set 1: Any-Label+2 Other-Label+2 Code-Review+2\n\n" |
| + "By voting Code-Review+2 the following files are now code-owner approved by" |
| + " <GERRIT_ACCOUNT_1>:\n" |
| + " * file1.java\n" |
| + " * file2.ts\n" |
| + "By voting Any-Label+2 the code-owners submit requirement is overridden by" |
| + " <GERRIT_ACCOUNT_1>\n" |
| + "By voting Other-Label+2 the code-owners submit requirement is still overridden" |
| + " by <GERRIT_ACCOUNT_1>\n", |
| "Patch Set 1: Any-Label+2 Other-Label+2 Code-Review+2\n\n" |
| + "By voting Any-Label+2 the code-owners submit requirement is overridden by" |
| + " <GERRIT_ACCOUNT_1>\n" |
| + "By voting Other-Label+2 the code-owners submit requirement is still overridden" |
| + " by <GERRIT_ACCOUNT_1>\n" |
| + "By voting Code-Review+2 the following files are now code-owner approved by" |
| + " <GERRIT_ACCOUNT_1>:\n" |
| + " * file1.java\n" |
| + " * file2.ts\n", |
| "Patch Set 1: -Code-Review\n" |
| + "\n" |
| + "By removing the Code-Review+2 vote the following files are no longer explicitly" |
| + " code-owner approved by <GERRIT_ACCOUNT_2>:\n" |
| + " * file1.java\n" |
| + " * file2.ts\n" |
| + "\n" |
| + "The listed files are still implicitly approved by <GERRIT_ACCOUNT_2>.\n", |
| "Patch Set 1: -Owners-Override\n" |
| + "\n" |
| + "(1 comment)\n" |
| + "\n" |
| + "By removing the Owners-Override+1 vote the code-owners submit requirement is no" |
| + " longer overridden by <GERRIT_ACCOUNT_1>\n", |
| "Patch Set 1: Any-Label+2 Code-Review+2\n\n" |
| + "By voting Code-Review+2 the following files are now code-owner approved by" |
| + " <GERRIT_ACCOUNT_1>:\n" |
| + " * file1.java\n" |
| + " * file2.ts\n" |
| + "By voting Any-Label+2 the code-owners submit requirement is overridden by" |
| + " <GERRIT_ACCOUNT_1>\n", |
| "Patch Set 1: Code-Review-2\n\n" |
| + "By voting Code-Review-2 the following files are no longer explicitly code-owner" |
| + " approved by <GERRIT_ACCOUNT_1>:\n" |
| + " * file4.java\n", |
| "Patch Set 1: Owners-Override+1\n" |
| + "\n" |
| + "By voting Owners-Override+1 the code-owners submit requirement is still" |
| + " overridden by <GERRIT_ACCOUNT_1>\n"); |
| |
| Ref metaRefAfterRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| assertThat(metaRefAfterRewrite.getObjectId()).isNotEqualTo(metaRefBeforeRewrite.getObjectId()); |
| |
| ImmutableList<RevCommit> commitsAfterRewrite = logMetaRef(repo, metaRefAfterRewrite); |
| assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits); |
| assertFixedCommits(commitsToFix.build(), result, c.getId()); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -8 +8 @@\n" |
| + "-By voting Code-Review+2 the following files are now code-owner approved by" |
| + " Change Owner:\n" |
| + "+By voting Code-Review+2 the following files are now code-owner approved by" |
| + " <GERRIT_ACCOUNT_1>:\n" |
| + "@@ -11,2 +11,2 @@\n" |
| + "-By voting Any-Label+2 the code-owners submit requirement is overridden by" |
| + " Change Owner\n" |
| + "-By voting Other-Label+2 the code-owners submit requirement is still overridden" |
| + " by Change Owner\n" |
| + "+By voting Any-Label+2 the code-owners submit requirement is overridden by" |
| + " <GERRIT_ACCOUNT_1>\n" |
| + "+By voting Other-Label+2 the code-owners submit requirement is still overridden" |
| + " by <GERRIT_ACCOUNT_1>\n", |
| "@@ -8,3 +8,3 @@\n" |
| + "-By voting Any-Label+2 the code-owners submit requirement is overridden by" |
| + " Change Owner\n" |
| + "-By voting Other-Label+2 the code-owners submit requirement is still overridden" |
| + " by Change Owner\n" |
| + "-By voting Code-Review+2 the following files are now code-owner approved by" |
| + " Change Owner:\n" |
| + "+By voting Any-Label+2 the code-owners submit requirement is overridden by" |
| + " <GERRIT_ACCOUNT_1>\n" |
| + "+By voting Other-Label+2 the code-owners submit requirement is still overridden" |
| + " by <GERRIT_ACCOUNT_1>\n" |
| + "+By voting Code-Review+2 the following files are now code-owner approved by" |
| + " <GERRIT_ACCOUNT_1>:\n", |
| "@@ -8 +8 @@\n" |
| + "-By removing the Code-Review+2 vote the following files are no longer explicitly" |
| + " code-owner approved by Other Account:\n" |
| + "+By removing the Code-Review+2 vote the following files are no longer explicitly" |
| + " code-owner approved by <GERRIT_ACCOUNT_2>:\n" |
| + "@@ -12 +12 @@\n" |
| + "-The listed files are still implicitly approved by Other Account.\n" |
| + "+The listed files are still implicitly approved by <GERRIT_ACCOUNT_2>.\n", |
| "@@ -10 +10 @@\n" |
| + "-By removing the Owners-Override+1 vote the code-owners submit requirement is no" |
| + " longer overridden by Change Owner\n" |
| + "+By removing the Owners-Override+1 vote the code-owners submit requirement is no" |
| + " longer overridden by <GERRIT_ACCOUNT_1>\n", |
| "@@ -11 +11 @@\n" |
| + "-By voting Any-Label+2 the code-owners submit requirement is overridden by" |
| + " Change Owner\n" |
| + "+By voting Any-Label+2 the code-owners submit requirement is overridden by" |
| + " <GERRIT_ACCOUNT_1>\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixAssigneeFooterIdent() throws Exception { |
| Change c = newChange(); |
| |
| String assigneeIdentToFix = getAccountIdentToFix(changeOwner.getAccount()); |
| RevCommit invalidUpdateCommit = |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody(c, "Assignee added", "Assignee: " + assigneeIdentToFix), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| // Valid commits |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| "Assignee added: <GERRIT_ACCOUNT_2>", |
| "Assignee: " + getValidIdentAsString(otherUser.getAccount())), |
| getAuthorIdent(changeOwner.getAccount())); |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody(c, "Assignee deleted: <GERRIT_ACCOUNT_2>", "Assignee:"), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| Ref metaRefBeforeRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<RevCommit> commitsBeforeRewrite = logMetaRef(repo, metaRefBeforeRewrite); |
| |
| int invalidCommitIndex = commitsBeforeRewrite.indexOf(invalidUpdateCommit); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| Ref metaRefAfterRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| assertThat(metaRefAfterRewrite.getObjectId()).isNotEqualTo(metaRefBeforeRewrite.getObjectId()); |
| |
| ImmutableList<RevCommit> commitsAfterRewrite = logMetaRef(repo, metaRefAfterRewrite); |
| assertValidCommits( |
| commitsBeforeRewrite, commitsAfterRewrite, ImmutableList.of(invalidCommitIndex)); |
| assertFixedCommits(ImmutableList.of(invalidUpdateCommit.getId()), result, c.getId()); |
| |
| RevCommit fixedUpdateCommit = commitsAfterRewrite.get(invalidCommitIndex); |
| assertThat(invalidUpdateCommit.getAuthorIdent()).isEqualTo(fixedUpdateCommit.getAuthorIdent()); |
| assertThat(invalidUpdateCommit.getCommitterIdent()) |
| .isEqualTo(fixedUpdateCommit.getCommitterIdent()); |
| assertThat(invalidUpdateCommit.getFullMessage()) |
| .isNotEqualTo(fixedUpdateCommit.getFullMessage()); |
| assertThat(fixedUpdateCommit.getFullMessage()).doesNotContain(changeOwner.getName()); |
| assertThat(invalidUpdateCommit.getFullMessage()).contains(assigneeIdentToFix); |
| String expectedFixedIdent = getValidIdentAsString(changeOwner.getAccount()); |
| assertThat(fixedUpdateCommit.getFullMessage()).contains(expectedFixedIdent); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -9 +9 @@\n" |
| + "-Assignee: Change Owner <1@gerrit>\n" |
| + "+Assignee: Gerrit User 1 <1@gerrit>\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixAssigneeChangeMessage() throws Exception { |
| Change c = newChange(); |
| |
| ImmutableList<ObjectId> commitsToFix = |
| new ImmutableList.Builder<ObjectId>() |
| .add( |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| "Assignee added: " + changeOwner.getNameEmail(), |
| "Assignee: " + getValidIdentAsString(changeOwner.getAccount())), |
| getAuthorIdent(changeOwner.getAccount()))) |
| .add( |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| String.format( |
| "Assignee changed from: %s to: %s", |
| changeOwner.getNameEmail(), otherUser.getNameEmail()), |
| "Assignee: " + getValidIdentAsString(otherUser.getAccount())), |
| getAuthorIdent(changeOwner.getAccount()))) |
| .add( |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, "Assignee deleted: " + otherUser.getNameEmail(), "Assignee:"), |
| getAuthorIdent(changeOwner.getAccount()))) |
| .build(); |
| |
| Ref metaRefBeforeRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<RevCommit> commitsBeforeRewrite = logMetaRef(repo, metaRefBeforeRewrite); |
| |
| ImmutableList<Integer> invalidCommits = |
| commitsToFix.stream() |
| .map(commit -> commitsBeforeRewrite.indexOf(commit)) |
| .collect(toImmutableList()); |
| ChangeNotes notesBeforeRewrite = newNotes(c); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| ChangeNotes notesAfterRewrite = newNotes(c); |
| assertThat(changeMessages(notesBeforeRewrite)) |
| .containsExactly( |
| "Assignee added: Change Owner <change@owner.com>", |
| "Assignee changed from: Change Owner <change@owner.com> to: Other Account" |
| + " <other@account.com>", |
| "Assignee deleted: Other Account <other@account.com>"); |
| |
| assertThat(changeMessages(notesAfterRewrite)) |
| .containsExactly( |
| "Assignee added: " + AccountTemplateUtil.getAccountTemplate(changeOwner.getAccountId()), |
| String.format( |
| "Assignee changed from: %s to: %s", |
| AccountTemplateUtil.getAccountTemplate(changeOwner.getAccountId()), |
| AccountTemplateUtil.getAccountTemplate(otherUser.getAccountId())), |
| "Assignee deleted: " |
| + AccountTemplateUtil.getAccountTemplate(otherUser.getAccountId())); |
| |
| Ref metaRefAfterRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| assertThat(metaRefAfterRewrite.getObjectId()).isNotEqualTo(metaRefBeforeRewrite.getObjectId()); |
| |
| ImmutableList<RevCommit> commitsAfterRewrite = logMetaRef(repo, metaRefAfterRewrite); |
| assertValidCommits(commitsBeforeRewrite, commitsAfterRewrite, invalidCommits); |
| assertFixedCommits(commitsToFix, result, c.getId()); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -6 +6 @@\n" |
| + "-Assignee added: Change Owner <change@owner.com>\n" |
| + "+Assignee added: <GERRIT_ACCOUNT_1>\n", |
| "@@ -6 +6 @@\n" |
| + "-Assignee changed from: Change Owner <change@owner.com> to: Other Account" |
| + " <other@account.com>\n" |
| + "+Assignee changed from: <GERRIT_ACCOUNT_1> to: <GERRIT_ACCOUNT_2>\n", |
| "@@ -6 +6 @@\n" |
| + "-Assignee deleted: Other Account <other@account.com>\n" |
| + "+Assignee deleted: <GERRIT_ACCOUNT_2>\n" |
| // Both empty value and space are parsed as deleted assignee anyway. |
| + "@@ -9 +9 @@\n" |
| + "-Assignee:\n" |
| + "+Assignee: \n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void fixAssigneeChangeMessageNoAssigneeFooter() throws Exception { |
| Change c = newChange(); |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody(c, "Assignee added: " + changeOwner.getName()), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| String.format( |
| "Assignee changed from: %s to: %s", |
| changeOwner.getNameEmail(), otherUser.getNameEmail())), |
| getAuthorIdent(otherUser.getAccount())); |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody(c, "Assignee deleted: " + otherUser.getName()), |
| getAuthorIdent(changeOwner.getAccount())); |
| Account reviewer = |
| Account.builder(Account.id(3), TimeUtil.now()) |
| .setFullName("Reviewer User") |
| .setPreferredEmail("reviewer@account.com") |
| .build(); |
| accountCache.put(reviewer); |
| // Even though account is present in the cache, it won't be used because it does not appear in |
| // the history of this change. |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody(c, "Assignee added: " + reviewer.getName()), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody(c, "Assignee deleted: Gerrit Account"), |
| getAuthorIdent(changeOwner.getAccount())); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff) |
| .containsExactly( |
| "@@ -6 +6 @@\n" |
| + "-Assignee added: Change Owner\n" |
| + "+Assignee added: <GERRIT_ACCOUNT_1>\n", |
| "@@ -6 +6 @@\n" |
| + "-Assignee changed from: Change Owner <change@owner.com> to: Other Account" |
| + " <other@account.com>\n" |
| + "+Assignee changed from: <GERRIT_ACCOUNT_1> to: <GERRIT_ACCOUNT_2>\n", |
| "@@ -6 +6 @@\n" |
| + "-Assignee deleted: Other Account\n" |
| + "+Assignee deleted: <GERRIT_ACCOUNT_2>\n", |
| "@@ -6 +6 @@\n" + "-Assignee added: Reviewer User\n" + "+Assignee was added.\n"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @Test |
| public void singleRunFixesAll() throws Exception { |
| Change c = newChange(); |
| Instant when = TimeUtil.now(); |
| String assigneeIdentToFix = getAccountIdentToFix(otherUser.getAccount()); |
| PersonIdent authorIdentToFix = |
| new PersonIdent( |
| changeOwner.getName(), |
| changeNoteUtil.getAccountIdAsEmailAddress(changeOwner.getAccountId()), |
| when, |
| serverIdent.getZoneId()); |
| |
| RevCommit invalidUpdateCommit = |
| writeUpdate( |
| RefNames.changeMetaRef(c.getId()), |
| getChangeUpdateBody( |
| c, |
| "Assignee added: Other Account <other@account.com>", |
| "Assignee: " + assigneeIdentToFix), |
| authorIdentToFix); |
| Ref metaRefBeforeRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| |
| ImmutableList<RevCommit> commitsBeforeRewrite = logMetaRef(repo, metaRefBeforeRewrite); |
| |
| int invalidCommitIndex = commitsBeforeRewrite.indexOf(invalidUpdateCommit); |
| |
| RunOptions options = new RunOptions(); |
| options.dryRun = false; |
| BackfillResult result = rewriter.backfillProject(project, repo, options); |
| assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId())); |
| |
| Ref metaRefAfterRewrite = repo.exactRef(RefNames.changeMetaRef(c.getId())); |
| assertThat(metaRefAfterRewrite.getObjectId()).isNotEqualTo(metaRefBeforeRewrite.getObjectId()); |
| |
| ImmutableList<RevCommit> commitsAfterRewrite = logMetaRef(repo, metaRefAfterRewrite); |
| assertValidCommits( |
| commitsBeforeRewrite, commitsAfterRewrite, ImmutableList.of(invalidCommitIndex)); |
| assertFixedCommits(ImmutableList.of(invalidUpdateCommit.getId()), result, c.getId()); |
| |
| RevCommit fixedUpdateCommit = commitsAfterRewrite.get(invalidCommitIndex); |
| assertThat(invalidUpdateCommit.getAuthorIdent()) |
| .isNotEqualTo(fixedUpdateCommit.getAuthorIdent()); |
| assertThat(invalidUpdateCommit.getFullMessage()).contains(otherUser.getName()); |
| assertThat(fixedUpdateCommit.getFullMessage()).doesNotContain(changeOwner.getName()); |
| assertThat(fixedUpdateCommit.getFullMessage()).doesNotContain(otherUser.getName()); |
| |
| ImmutableList<String> commitHistoryDiff = commitHistoryDiff(result, c.getId()); |
| assertThat(commitHistoryDiff).hasSize(1); |
| assertThat(commitHistoryDiff.get(0)).contains("-author Change Owner <1@gerrit>"); |
| assertThat(commitHistoryDiff.get(0)).contains("+author Gerrit User 1 <1@gerrit>"); |
| assertThat(commitHistoryDiff.get(0)) |
| .contains( |
| "@@ -6 +6 @@\n" |
| + "-Assignee added: Other Account <other@account.com>\n" |
| + "+Assignee added: <GERRIT_ACCOUNT_2>\n" |
| + "@@ -9 +9 @@\n" |
| + "-Assignee: Other Account <2@gerrit>\n" |
| + "+Assignee: Gerrit User 2 <2@gerrit>"); |
| BackfillResult secondRunResult = rewriter.backfillProject(project, repo, options); |
| assertThat(secondRunResult.fixedRefDiff.keySet()).isEmpty(); |
| assertThat(secondRunResult.refsFailedToFix).isEmpty(); |
| } |
| |
| @CanIgnoreReturnValue |
| private RevCommit writeUpdate(String metaRef, String body, PersonIdent author) throws Exception { |
| return tr.branch(metaRef).commit().message(body).author(author).committer(serverIdent).create(); |
| } |
| |
| private String getChangeUpdateBody(Change change, String changeMessage, String... footers) { |
| StringBuilder commitBody = new StringBuilder(); |
| commitBody.append("Update patch set ").append(change.currentPatchSetId().get()); |
| commitBody.append("\n\n"); |
| if (changeMessage != null) { |
| commitBody.append(changeMessage); |
| commitBody.append("\n\n"); |
| } |
| commitBody.append("Patch-set: " + change.currentPatchSetId().get()); |
| commitBody.append("\n"); |
| for (String footer : footers) { |
| commitBody.append(footer); |
| commitBody.append("\n"); |
| } |
| return commitBody.toString(); |
| } |
| |
| private ImmutableList<RevCommit> logMetaRef(Repository repo, Ref metaRef) throws Exception { |
| try (RevWalk rw = new RevWalk(repo)) { |
| rw.sort(RevSort.TOPO); |
| rw.sort(RevSort.REVERSE); |
| if (metaRef == null) { |
| return ImmutableList.of(); |
| } |
| rw.markStart(rw.parseCommit(metaRef.getObjectId())); |
| return ImmutableList.copyOf(rw); |
| } |
| } |
| |
| private void assertValidCommits( |
| ImmutableList<RevCommit> commitsBeforeRewrite, |
| ImmutableList<RevCommit> commitsAfterRewrite, |
| ImmutableList<Integer> invalidCommits) { |
| ImmutableList<RevCommit> validCommitsBeforeRewrite = |
| IntStream.range(0, commitsBeforeRewrite.size()) |
| .filter(i -> !invalidCommits.contains(i)) |
| .mapToObj(commitsBeforeRewrite::get) |
| .collect(toImmutableList()); |
| |
| ImmutableList<RevCommit> validCommitsAfterRewrite = |
| IntStream.range(0, commitsAfterRewrite.size()) |
| .filter(i -> !invalidCommits.contains(i)) |
| .mapToObj(commitsAfterRewrite::get) |
| .collect(toImmutableList()); |
| |
| assertThat(validCommitsBeforeRewrite).hasSize(validCommitsAfterRewrite.size()); |
| for (int i = 0; i < validCommitsAfterRewrite.size(); i++) { |
| RevCommit actual = validCommitsAfterRewrite.get(i); |
| RevCommit expected = validCommitsBeforeRewrite.get(i); |
| assertThat(actual.getAuthorIdent()).isEqualTo(expected.getAuthorIdent()); |
| assertThat(actual.getCommitterIdent()).isEqualTo(expected.getCommitterIdent()); |
| assertThat(actual.getFullMessage()).isEqualTo(expected.getFullMessage()); |
| } |
| } |
| |
| private void assertFixedCommits( |
| ImmutableList<ObjectId> expectedFixedCommits, BackfillResult result, Change.Id changeId) { |
| assertThat( |
| result.fixedRefDiff.get(RefNames.changeMetaRef(changeId)).stream() |
| .map(CommitDiff::oldSha1) |
| .collect(toImmutableList())) |
| .containsExactlyElementsIn(expectedFixedCommits); |
| } |
| |
| private String getAccountIdentToFix(Account account) { |
| return String.format("%s <%s>", account.getName(), account.id().get() + "@" + serverId); |
| } |
| |
| private String getValidIdentAsString(Account account) { |
| return String.format( |
| "%s <%s>", |
| ChangeNoteUtil.getAccountIdAsUsername(account.id()), account.id().get() + "@" + serverId); |
| } |
| |
| private ImmutableList<String> changeMessages(ChangeNotes changeNotes) { |
| return changeNotes.getChangeMessages().stream() |
| .map(ChangeMessage::getMessage) |
| .collect(toImmutableList()); |
| } |
| |
| protected ChangeUpdate newCodeOwnerAddReviewerUpdate(Change c, CurrentUser user) |
| throws Exception { |
| ChangeUpdate update = newUpdate(c, user, true); |
| update.setTag("autogenerated:gerrit:code-owners:addReviewer"); |
| return update; |
| } |
| |
| private ImmutableList<String> commitHistoryDiff(BackfillResult result, Change.Id changeId) { |
| return result.fixedRefDiff.get(RefNames.changeMetaRef(changeId)).stream() |
| .map(CommitDiff::diff) |
| .collect(toImmutableList()); |
| } |
| |
| private PersonIdent getAuthorIdent(Account account) { |
| return changeNoteUtil.newAccountIdIdent(account.id(), TimeUtil.now(), serverIdent); |
| } |
| } |