| // Copyright (C) 2017 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.group.db; | 
 |  | 
 | import static com.google.common.truth.Truth.assertThat; | 
 | import static com.google.common.truth.Truth.assert_; | 
 | import static com.google.gerrit.extensions.common.testing.CommitInfoSubject.assertThat; | 
 | import static com.google.gerrit.reviewdb.client.RefNames.REFS_GROUPNAMES; | 
 | import static com.google.gerrit.truth.OptionalSubject.assertThat; | 
 | import static java.nio.charset.StandardCharsets.UTF_8; | 
 | import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; | 
 |  | 
 | import com.google.common.collect.ImmutableList; | 
 | import com.google.gerrit.common.data.GroupReference; | 
 | import com.google.gerrit.common.data.testing.GroupReferenceSubject; | 
 | import com.google.gerrit.extensions.common.CommitInfo; | 
 | import com.google.gerrit.extensions.common.testing.CommitInfoSubject; | 
 | import com.google.gerrit.reviewdb.client.AccountGroup; | 
 | import com.google.gerrit.reviewdb.client.Project; | 
 | import com.google.gerrit.reviewdb.client.RefNames; | 
 | import com.google.gerrit.server.config.AllUsersName; | 
 | import com.google.gerrit.server.config.AllUsersNameProvider; | 
 | import com.google.gerrit.server.extensions.events.GitReferenceUpdated; | 
 | import com.google.gerrit.server.git.meta.MetaDataUpdate; | 
 | import com.google.gerrit.server.update.RefUpdateUtil; | 
 | import com.google.gerrit.server.util.time.TimeUtil; | 
 | import com.google.gerrit.testing.GitTestUtil; | 
 | import com.google.gerrit.testing.TestTimeUtil; | 
 | import com.google.gerrit.truth.ListSubject; | 
 | import com.google.gerrit.truth.OptionalSubject; | 
 | import com.google.gwtorm.client.KeyUtil; | 
 | import com.google.gwtorm.server.OrmDuplicateKeyException; | 
 | import com.google.gwtorm.server.StandardKeyEncoder; | 
 | import java.io.IOException; | 
 | import java.util.Arrays; | 
 | import java.util.List; | 
 | import java.util.Optional; | 
 | import java.util.TimeZone; | 
 | import java.util.concurrent.TimeUnit; | 
 | import java.util.concurrent.atomic.AtomicInteger; | 
 | import org.eclipse.jgit.errors.ConfigInvalidException; | 
 | import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; | 
 | import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; | 
 | import org.eclipse.jgit.junit.TestRepository; | 
 | import org.eclipse.jgit.lib.BatchRefUpdate; | 
 | import org.eclipse.jgit.lib.ObjectId; | 
 | import org.eclipse.jgit.lib.ObjectInserter; | 
 | import org.eclipse.jgit.lib.ObjectReader; | 
 | import org.eclipse.jgit.lib.PersonIdent; | 
 | import org.eclipse.jgit.lib.Ref; | 
 | import org.eclipse.jgit.lib.Repository; | 
 | import org.eclipse.jgit.notes.NoteMap; | 
 | import org.eclipse.jgit.revwalk.RevCommit; | 
 | import org.eclipse.jgit.revwalk.RevWalk; | 
 | import org.junit.After; | 
 | import org.junit.Before; | 
 | import org.junit.Rule; | 
 | import org.junit.Test; | 
 | import org.junit.rules.ExpectedException; | 
 |  | 
 | public class GroupNameNotesTest { | 
 |   static { | 
 |     KeyUtil.setEncoderImpl(new StandardKeyEncoder()); | 
 |   } | 
 |  | 
 |   private static final String SERVER_NAME = "Gerrit Server"; | 
 |   private static final String SERVER_EMAIL = "noreply@gerritcodereview.com"; | 
 |   private static final TimeZone TZ = TimeZone.getTimeZone("America/Los_Angeles"); | 
 |  | 
 |   @Rule public ExpectedException expectedException = ExpectedException.none(); | 
 |  | 
 |   private final AccountGroup.UUID groupUuid = new AccountGroup.UUID("users-XYZ"); | 
 |   private final AccountGroup.NameKey groupName = new AccountGroup.NameKey("users"); | 
 |  | 
 |   private AtomicInteger idCounter; | 
 |   private AllUsersName allUsersName; | 
 |   private Repository repo; | 
 |  | 
 |   @Before | 
 |   public void setUp() { | 
 |     TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS); | 
 |     idCounter = new AtomicInteger(); | 
 |     allUsersName = new AllUsersName(AllUsersNameProvider.DEFAULT); | 
 |     repo = new InMemoryRepository(new DfsRepositoryDescription(AllUsersNameProvider.DEFAULT)); | 
 |   } | 
 |  | 
 |   @After | 
 |   public void tearDown() { | 
 |     TestTimeUtil.useSystemTime(); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void newGroupCanBeCreated() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |  | 
 |     Optional<GroupReference> groupReference = loadGroup(groupName); | 
 |     assertThatGroup(groupReference).value().groupUuid().isEqualTo(groupUuid); | 
 |     assertThatGroup(groupReference).value().name().isEqualTo(groupName.get()); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void uuidOfNewGroupMustNotBeNull() throws Exception { | 
 |     expectedException.expect(NullPointerException.class); | 
 |     GroupNameNotes.forNewGroup(allUsersName, repo, null, groupName); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void nameOfNewGroupMustNotBeNull() throws Exception { | 
 |     expectedException.expect(NullPointerException.class); | 
 |     GroupNameNotes.forNewGroup(allUsersName, repo, groupUuid, null); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void nameOfNewGroupMayBeEmpty() throws Exception { | 
 |     AccountGroup.NameKey emptyName = new AccountGroup.NameKey(""); | 
 |     createGroup(groupUuid, emptyName); | 
 |  | 
 |     Optional<GroupReference> groupReference = loadGroup(emptyName); | 
 |     assertThatGroup(groupReference).value().name().isEqualTo(""); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void newGroupMustNotReuseNameOfAnotherGroup() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |  | 
 |     AccountGroup.UUID anotherGroupUuid = new AccountGroup.UUID("AnotherGroup"); | 
 |     expectedException.expect(OrmDuplicateKeyException.class); | 
 |     expectedException.expectMessage(groupName.get()); | 
 |     GroupNameNotes.forNewGroup(allUsersName, repo, anotherGroupUuid, groupName); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void newGroupMayReuseUuidOfAnotherGroup() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |  | 
 |     AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins"); | 
 |     createGroup(groupUuid, anotherName); | 
 |  | 
 |     Optional<GroupReference> group1 = loadGroup(groupName); | 
 |     assertThatGroup(group1).value().groupUuid().isEqualTo(groupUuid); | 
 |     Optional<GroupReference> group2 = loadGroup(anotherName); | 
 |     assertThatGroup(group2).value().groupUuid().isEqualTo(groupUuid); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void groupCanBeRenamed() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |  | 
 |     AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins"); | 
 |     renameGroup(groupUuid, groupName, anotherName); | 
 |  | 
 |     Optional<GroupReference> groupReference = loadGroup(anotherName); | 
 |     assertThatGroup(groupReference).value().groupUuid().isEqualTo(groupUuid); | 
 |     assertThatGroup(groupReference).value().name().isEqualTo(anotherName.get()); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void previousNameOfGroupCannotBeUsedAfterRename() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |  | 
 |     AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins"); | 
 |     renameGroup(groupUuid, groupName, anotherName); | 
 |  | 
 |     Optional<GroupReference> group = loadGroup(groupName); | 
 |     assertThatGroup(group).isAbsent(); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void groupCannotBeRenamedToNull() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |  | 
 |     expectedException.expect(NullPointerException.class); | 
 |     GroupNameNotes.forRename(allUsersName, repo, groupUuid, groupName, null); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void oldNameOfGroupMustBeSpecifiedForRename() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |  | 
 |     AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins"); | 
 |     expectedException.expect(NullPointerException.class); | 
 |     GroupNameNotes.forRename(allUsersName, repo, groupUuid, null, anotherName); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void groupCannotBeRenamedWhenOldNameIsWrong() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |  | 
 |     AccountGroup.NameKey anotherOldName = new AccountGroup.NameKey("contributors"); | 
 |     AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins"); | 
 |     expectedException.expect(ConfigInvalidException.class); | 
 |     expectedException.expectMessage(anotherOldName.get()); | 
 |     GroupNameNotes.forRename(allUsersName, repo, groupUuid, anotherOldName, anotherName); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void groupCannotBeRenamedToNameOfAnotherGroup() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |     AccountGroup.UUID anotherGroupUuid = new AccountGroup.UUID("admins-ABC"); | 
 |     AccountGroup.NameKey anotherGroupName = new AccountGroup.NameKey("admins"); | 
 |     createGroup(anotherGroupUuid, anotherGroupName); | 
 |  | 
 |     expectedException.expect(OrmDuplicateKeyException.class); | 
 |     expectedException.expectMessage(anotherGroupName.get()); | 
 |     GroupNameNotes.forRename(allUsersName, repo, groupUuid, groupName, anotherGroupName); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void groupCannotBeRenamedWithoutSpecifiedUuid() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |  | 
 |     AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins"); | 
 |     expectedException.expect(NullPointerException.class); | 
 |     GroupNameNotes.forRename(allUsersName, repo, null, groupName, anotherName); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void groupCannotBeRenamedWhenUuidIsWrong() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |  | 
 |     AccountGroup.UUID anotherGroupUuid = new AccountGroup.UUID("admins-ABC"); | 
 |     AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins"); | 
 |     expectedException.expect(ConfigInvalidException.class); | 
 |     expectedException.expectMessage(groupUuid.get()); | 
 |     GroupNameNotes.forRename(allUsersName, repo, anotherGroupUuid, groupName, anotherName); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void firstGroupCreationCreatesARootCommit() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |  | 
 |     Ref ref = repo.exactRef(RefNames.REFS_GROUPNAMES); | 
 |     assertThat(ref.getObjectId()).isNotNull(); | 
 |  | 
 |     try (RevWalk revWalk = new RevWalk(repo)) { | 
 |       RevCommit revCommit = revWalk.parseCommit(ref.getObjectId()); | 
 |       assertThat(revCommit.getParentCount()).isEqualTo(0); | 
 |     } | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void furtherGroupCreationAppendsACommit() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |     ImmutableList<CommitInfo> commitsAfterCreation = log(); | 
 |  | 
 |     AccountGroup.UUID anotherGroupUuid = new AccountGroup.UUID("admins-ABC"); | 
 |     AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins"); | 
 |     createGroup(anotherGroupUuid, anotherName); | 
 |  | 
 |     ImmutableList<CommitInfo> commitsAfterFurtherGroup = log(); | 
 |     assertThatCommits(commitsAfterFurtherGroup).containsAllIn(commitsAfterCreation); | 
 |     assertThatCommits(commitsAfterFurtherGroup).lastElement().isNotIn(commitsAfterCreation); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void groupRenamingAppendsACommit() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |     ImmutableList<CommitInfo> commitsAfterCreation = log(); | 
 |  | 
 |     AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins"); | 
 |     renameGroup(groupUuid, groupName, anotherName); | 
 |  | 
 |     ImmutableList<CommitInfo> commitsAfterRename = log(); | 
 |     assertThatCommits(commitsAfterRename).containsAllIn(commitsAfterCreation); | 
 |     assertThatCommits(commitsAfterRename).lastElement().isNotIn(commitsAfterCreation); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void newCommitIsNotCreatedForRedundantNameUpdate() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |     ImmutableList<CommitInfo> commitsAfterCreation = log(); | 
 |  | 
 |     renameGroup(groupUuid, groupName, groupName); | 
 |  | 
 |     ImmutableList<CommitInfo> commitsAfterRename = log(); | 
 |     assertThatCommits(commitsAfterRename).isEqualTo(commitsAfterCreation); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void newCommitIsNotCreatedWhenCommittingGroupCreationTwice() throws Exception { | 
 |     GroupNameNotes groupNameNotes = | 
 |         GroupNameNotes.forNewGroup(allUsersName, repo, groupUuid, groupName); | 
 |  | 
 |     commit(groupNameNotes); | 
 |     ImmutableList<CommitInfo> commitsAfterFirstCommit = log(); | 
 |     commit(groupNameNotes); | 
 |     ImmutableList<CommitInfo> commitsAfterSecondCommit = log(); | 
 |  | 
 |     assertThatCommits(commitsAfterSecondCommit).isEqualTo(commitsAfterFirstCommit); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void newCommitIsNotCreatedWhenCommittingGroupRenamingTwice() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |  | 
 |     AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins"); | 
 |     GroupNameNotes groupNameNotes = | 
 |         GroupNameNotes.forRename(allUsersName, repo, groupUuid, groupName, anotherName); | 
 |  | 
 |     commit(groupNameNotes); | 
 |     ImmutableList<CommitInfo> commitsAfterFirstCommit = log(); | 
 |     commit(groupNameNotes); | 
 |     ImmutableList<CommitInfo> commitsAfterSecondCommit = log(); | 
 |  | 
 |     assertThatCommits(commitsAfterSecondCommit).isEqualTo(commitsAfterFirstCommit); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void commitMessageMentionsGroupCreation() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |  | 
 |     ImmutableList<CommitInfo> commits = log(); | 
 |     assertThatCommits(commits).lastElement().message().contains("Create"); | 
 |     assertThatCommits(commits).lastElement().message().contains(groupName.get()); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void commitMessageMentionsGroupRenaming() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |  | 
 |     AccountGroup.NameKey anotherName = new AccountGroup.NameKey("admins"); | 
 |     renameGroup(groupUuid, groupName, anotherName); | 
 |  | 
 |     ImmutableList<CommitInfo> commits = log(); | 
 |     assertThatCommits(commits).lastElement().message().contains("Rename"); | 
 |     assertThatCommits(commits).lastElement().message().contains(groupName.get()); | 
 |     assertThatCommits(commits).lastElement().message().contains(anotherName.get()); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void nonExistentNotesRefIsEquivalentToNonExistentGroup() throws Exception { | 
 |     Optional<GroupReference> group = loadGroup(groupName); | 
 |  | 
 |     assertThatGroup(group).isAbsent(); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void nonExistentGroupCannotBeLoaded() throws Exception { | 
 |     createGroup(new AccountGroup.UUID("contributors-MN"), new AccountGroup.NameKey("contributors")); | 
 |     createGroup(groupUuid, groupName); | 
 |  | 
 |     Optional<GroupReference> group = loadGroup(new AccountGroup.NameKey("admins")); | 
 |     assertThatGroup(group).isAbsent(); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void specificGroupCanBeLoaded() throws Exception { | 
 |     createGroup(new AccountGroup.UUID("contributors-MN"), new AccountGroup.NameKey("contributors")); | 
 |     createGroup(groupUuid, groupName); | 
 |     createGroup(new AccountGroup.UUID("admins-ABC"), new AccountGroup.NameKey("admins")); | 
 |  | 
 |     Optional<GroupReference> group = loadGroup(groupName); | 
 |     assertThatGroup(group).value().groupUuid().isEqualTo(groupUuid); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void nonExistentNotesRefIsEquivalentToNotAnyExistingGroups() throws Exception { | 
 |     ImmutableList<GroupReference> allGroups = GroupNameNotes.loadAllGroups(repo); | 
 |  | 
 |     assertThat(allGroups).isEmpty(); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void allGroupsCanBeLoaded() throws Exception { | 
 |     AccountGroup.UUID groupUuid1 = new AccountGroup.UUID("contributors-MN"); | 
 |     AccountGroup.NameKey groupName1 = new AccountGroup.NameKey("contributors"); | 
 |     createGroup(groupUuid1, groupName1); | 
 |     AccountGroup.UUID groupUuid2 = new AccountGroup.UUID("admins-ABC"); | 
 |     AccountGroup.NameKey groupName2 = new AccountGroup.NameKey("admins"); | 
 |     createGroup(groupUuid2, groupName2); | 
 |  | 
 |     ImmutableList<GroupReference> allGroups = GroupNameNotes.loadAllGroups(repo); | 
 |  | 
 |     GroupReference group1 = new GroupReference(groupUuid1, groupName1.get()); | 
 |     GroupReference group2 = new GroupReference(groupUuid2, groupName2.get()); | 
 |     assertThat(allGroups).containsExactly(group1, group2); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void loadedGroupsContainGroupsWithDuplicateGroupUuids() throws Exception { | 
 |     createGroup(groupUuid, groupName); | 
 |     AccountGroup.NameKey anotherGroupName = new AccountGroup.NameKey("admins"); | 
 |     createGroup(groupUuid, anotherGroupName); | 
 |  | 
 |     ImmutableList<GroupReference> allGroups = GroupNameNotes.loadAllGroups(repo); | 
 |  | 
 |     GroupReference group1 = new GroupReference(groupUuid, groupName.get()); | 
 |     GroupReference group2 = new GroupReference(groupUuid, anotherGroupName.get()); | 
 |     assertThat(allGroups).containsExactly(group1, group2); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void updateGroupNames() throws Exception { | 
 |     GroupReference g1 = newGroup("a"); | 
 |     GroupReference g2 = newGroup("b"); | 
 |  | 
 |     PersonIdent ident = newPersonIdent(); | 
 |     updateAllGroups(ident, g1, g2); | 
 |  | 
 |     ImmutableList<CommitInfo> log = log(); | 
 |     assertThat(log).hasSize(1); | 
 |     assertThat(log.get(0)).parents().isEmpty(); | 
 |     assertThat(log.get(0)).message().isEqualTo("Store 2 group names"); | 
 |     assertThat(log.get(0)).author().matches(ident); | 
 |     assertThat(log.get(0)).committer().matches(ident); | 
 |  | 
 |     assertThat(GroupNameNotes.loadAllGroups(repo)).containsExactly(g1, g2); | 
 |  | 
 |     // Updating the same set of names is a no-op. | 
 |     String commit = log.get(0).commit; | 
 |     updateAllGroups(newPersonIdent(), g1, g2); | 
 |     log = log(); | 
 |     assertThat(log).hasSize(1); | 
 |     assertThat(log.get(0)).commit().isEqualTo(commit); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void updateGroupNamesOverwritesExistingNotes() throws Exception { | 
 |     GroupReference g1 = newGroup("a"); | 
 |     GroupReference g2 = newGroup("b"); | 
 |  | 
 |     TestRepository<?> tr = new TestRepository<>(repo); | 
 |     ObjectId k1 = getNoteKey(g1); | 
 |     ObjectId k2 = getNoteKey(g2); | 
 |     ObjectId k3 = GroupNameNotes.getNoteKey(new AccountGroup.NameKey("c")); | 
 |     PersonIdent ident = newPersonIdent(); | 
 |     ObjectId origCommitId = | 
 |         tr.branch(REFS_GROUPNAMES) | 
 |             .commit() | 
 |             .message("Prepopulate group name") | 
 |             .author(ident) | 
 |             .committer(ident) | 
 |             .add(k1.name(), "[group]\n\tuuid = a-1\n\tname = a\nanotherKey = foo\n") | 
 |             .add(k2.name(), "[group]\n\tuuid = a-1\n\tname = b\n") | 
 |             .add(k3.name(), "[group]\n\tuuid = c-3\n\tname = c\n") | 
 |             .create() | 
 |             .copy(); | 
 |  | 
 |     ident = newPersonIdent(); | 
 |     updateAllGroups(ident, g1, g2); | 
 |  | 
 |     assertThat(GroupNameNotes.loadAllGroups(repo)).containsExactly(g1, g2); | 
 |  | 
 |     ImmutableList<CommitInfo> log = log(); | 
 |     assertThat(log).hasSize(2); | 
 |     assertThat(log.get(0)).commit().isEqualTo(origCommitId.name()); | 
 |  | 
 |     assertThat(log.get(1)).message().isEqualTo("Store 2 group names"); | 
 |     assertThat(log.get(1)).author().matches(ident); | 
 |     assertThat(log.get(1)).committer().matches(ident); | 
 |  | 
 |     // Old note content was overwritten. | 
 |     assertThat(readNameNote(g1)).isEqualTo("[group]\n\tuuid = a-1\n\tname = a\n"); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void updateGroupNamesWithEmptyCollectionClearsAllNotes() throws Exception { | 
 |     GroupReference g1 = newGroup("a"); | 
 |     GroupReference g2 = newGroup("b"); | 
 |  | 
 |     PersonIdent ident = newPersonIdent(); | 
 |     updateAllGroups(ident, g1, g2); | 
 |  | 
 |     assertThat(GroupNameNotes.loadAllGroups(repo)).containsExactly(g1, g2); | 
 |  | 
 |     updateAllGroups(ident); | 
 |  | 
 |     assertThat(GroupNameNotes.loadAllGroups(repo)).isEmpty(); | 
 |  | 
 |     ImmutableList<CommitInfo> log = log(); | 
 |     assertThat(log).hasSize(2); | 
 |     assertThat(log.get(1)).message().isEqualTo("Store 0 group names"); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void updateGroupNamesRejectsNonOneToOneGroupReferences() throws Exception { | 
 |     assertIllegalArgument( | 
 |         new GroupReference(new AccountGroup.UUID("uuid1"), "name1"), | 
 |         new GroupReference(new AccountGroup.UUID("uuid1"), "name2")); | 
 |     assertIllegalArgument( | 
 |         new GroupReference(new AccountGroup.UUID("uuid1"), "name1"), | 
 |         new GroupReference(new AccountGroup.UUID("uuid2"), "name1")); | 
 |     assertIllegalArgument( | 
 |         new GroupReference(new AccountGroup.UUID("uuid1"), "name1"), | 
 |         new GroupReference(new AccountGroup.UUID("uuid1"), "name1")); | 
 |   } | 
 |  | 
 |   @Test | 
 |   public void emptyGroupName() throws Exception { | 
 |     GroupReference g = newGroup(""); | 
 |     updateAllGroups(newPersonIdent(), g); | 
 |  | 
 |     assertThat(GroupNameNotes.loadAllGroups(repo)).containsExactly(g); | 
 |     assertThat(readNameNote(g)).isEqualTo("[group]\n\tuuid = -1\n\tname = \n"); | 
 |   } | 
 |  | 
 |   private void createGroup(AccountGroup.UUID groupUuid, AccountGroup.NameKey groupName) | 
 |       throws Exception { | 
 |     GroupNameNotes groupNameNotes = | 
 |         GroupNameNotes.forNewGroup(allUsersName, repo, groupUuid, groupName); | 
 |     commit(groupNameNotes); | 
 |   } | 
 |  | 
 |   private void renameGroup( | 
 |       AccountGroup.UUID groupUuid, AccountGroup.NameKey oldName, AccountGroup.NameKey newName) | 
 |       throws Exception { | 
 |     GroupNameNotes groupNameNotes = | 
 |         GroupNameNotes.forRename(allUsersName, repo, groupUuid, oldName, newName); | 
 |     commit(groupNameNotes); | 
 |   } | 
 |  | 
 |   private Optional<GroupReference> loadGroup(AccountGroup.NameKey groupName) throws Exception { | 
 |     return GroupNameNotes.loadGroup(repo, groupName); | 
 |   } | 
 |  | 
 |   private void commit(GroupNameNotes groupNameNotes) throws IOException { | 
 |     try (MetaDataUpdate metaDataUpdate = createMetaDataUpdate()) { | 
 |       groupNameNotes.commit(metaDataUpdate); | 
 |     } | 
 |   } | 
 |  | 
 |   private MetaDataUpdate createMetaDataUpdate() { | 
 |     PersonIdent serverIdent = newPersonIdent(); | 
 |  | 
 |     MetaDataUpdate metaDataUpdate = | 
 |         new MetaDataUpdate( | 
 |             GitReferenceUpdated.DISABLED, new Project.NameKey("Test Repository"), repo); | 
 |     metaDataUpdate.getCommitBuilder().setCommitter(serverIdent); | 
 |     metaDataUpdate.getCommitBuilder().setAuthor(serverIdent); | 
 |     return metaDataUpdate; | 
 |   } | 
 |  | 
 |   private GroupReference newGroup(String name) { | 
 |     int id = idCounter.incrementAndGet(); | 
 |     return new GroupReference(new AccountGroup.UUID(name + "-" + id), name); | 
 |   } | 
 |  | 
 |   private static PersonIdent newPersonIdent() { | 
 |     return new PersonIdent(SERVER_NAME, SERVER_EMAIL, TimeUtil.nowTs(), TZ); | 
 |   } | 
 |  | 
 |   private static ObjectId getNoteKey(GroupReference g) { | 
 |     return GroupNameNotes.getNoteKey(new AccountGroup.NameKey(g.getName())); | 
 |   } | 
 |  | 
 |   private void updateAllGroups(PersonIdent ident, GroupReference... groupRefs) throws Exception { | 
 |     try (ObjectInserter inserter = repo.newObjectInserter()) { | 
 |       BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate(); | 
 |       GroupNameNotes.updateAllGroups(repo, inserter, bru, Arrays.asList(groupRefs), ident); | 
 |       inserter.flush(); | 
 |       RefUpdateUtil.executeChecked(bru, repo); | 
 |     } | 
 |   } | 
 |  | 
 |   private void assertIllegalArgument(GroupReference... groupRefs) throws Exception { | 
 |     try (ObjectInserter inserter = repo.newObjectInserter()) { | 
 |       BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate(); | 
 |       PersonIdent ident = newPersonIdent(); | 
 |       try { | 
 |         GroupNameNotes.updateAllGroups(repo, inserter, bru, Arrays.asList(groupRefs), ident); | 
 |         assert_().fail("Expected IllegalArgumentException"); | 
 |       } catch (IllegalArgumentException e) { | 
 |         assertThat(e).hasMessageThat().isEqualTo(GroupNameNotes.UNIQUE_REF_ERROR); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private ImmutableList<CommitInfo> log() throws Exception { | 
 |     return GitTestUtil.log(repo, REFS_GROUPNAMES); | 
 |   } | 
 |  | 
 |   private String readNameNote(GroupReference g) throws Exception { | 
 |     ObjectId k = getNoteKey(g); | 
 |     try (RevWalk rw = new RevWalk(repo)) { | 
 |       ObjectReader reader = rw.getObjectReader(); | 
 |       Ref ref = repo.exactRef(RefNames.REFS_GROUPNAMES); | 
 |       NoteMap noteMap = NoteMap.read(reader, rw.parseCommit(ref.getObjectId())); | 
 |       return new String(reader.open(noteMap.get(k), OBJ_BLOB).getCachedBytes(), UTF_8); | 
 |     } | 
 |   } | 
 |  | 
 |   private static OptionalSubject<GroupReferenceSubject, GroupReference> assertThatGroup( | 
 |       Optional<GroupReference> group) { | 
 |     return assertThat(group, GroupReferenceSubject::assertThat); | 
 |   } | 
 |  | 
 |   private static ListSubject<CommitInfoSubject, CommitInfo> assertThatCommits( | 
 |       List<CommitInfo> commits) { | 
 |     return ListSubject.assertThat(commits, CommitInfoSubject::assertThat); | 
 |   } | 
 | } |