summaryrefslogtreecommitdiffstats
path: root/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java')
-rw-r--r--javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java747
1 files changed, 747 insertions, 0 deletions
diff --git a/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java b/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java
new file mode 100644
index 0000000000..bf6953afcd
--- /dev/null
+++ b/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java
@@ -0,0 +1,747 @@
+// 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.schema;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+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 com.google.common.collect.ImmutableList;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.extensions.common.CommitInfo;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.AccountGroupById;
+import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
+import com.google.gerrit.reviewdb.client.AccountGroupMember;
+import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
+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.group.db.AuditLogFormatter;
+import com.google.gerrit.server.group.db.AuditLogReader;
+import com.google.gerrit.server.group.db.GroupNameNotes;
+import com.google.gerrit.server.update.RefUpdateUtil;
+import com.google.gerrit.server.util.time.TimeUtil;
+import com.google.gerrit.testing.GerritBaseTests;
+import com.google.gerrit.testing.GitTestUtil;
+import com.google.gerrit.testing.InMemoryRepositoryManager;
+import com.google.gerrit.testing.TestTimeUtil;
+import com.google.gwtorm.server.OrmDuplicateKeyException;
+import java.sql.Timestamp;
+import java.util.Optional;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.IntStream;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class GroupRebuilderTest extends GerritBaseTests {
+ private static final TimeZone TZ = TimeZone.getTimeZone("America/Los_Angeles");
+ private static final String SERVER_ID = "server-id";
+ private static final String SERVER_NAME = "Gerrit Server";
+ private static final String SERVER_EMAIL = "noreply@gerritcodereview.com";
+
+ private AtomicInteger idCounter;
+ private AllUsersName allUsersName;
+ private Repository repo;
+ private GroupRebuilder rebuilder;
+ private GroupBundle.Factory bundleFactory;
+
+ @Before
+ public void setUp() throws Exception {
+ TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
+ idCounter = new AtomicInteger();
+ allUsersName = new AllUsersName(AllUsersNameProvider.DEFAULT);
+ repo = new InMemoryRepositoryManager().createRepository(allUsersName);
+ rebuilder =
+ new GroupRebuilder(
+ GroupRebuilderTest.newPersonIdent(),
+ allUsersName,
+ // Note that the expected name/email values in tests are not necessarily realistic,
+ // since they use these trivial name/email functions.
+ getAuditLogFormatter());
+ bundleFactory = new GroupBundle.Factory(new AuditLogReader(SERVER_ID, allUsersName));
+ }
+
+ @After
+ public void tearDown() {
+ TestTimeUtil.useSystemTime();
+ }
+
+ @Test
+ public void minimalGroupFields() throws Exception {
+ AccountGroup g = newGroup("a");
+ GroupBundle b = builder().group(g).build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ assertMigratedCleanly(reload(g), b);
+ ImmutableList<CommitInfo> log = log(g);
+ assertThat(log).hasSize(1);
+ assertCommit(log.get(0), "Create group", SERVER_NAME, SERVER_EMAIL);
+ assertThat(logGroupNames()).isEmpty();
+ }
+
+ @Test
+ public void allGroupFields() throws Exception {
+ AccountGroup g = newGroup("a");
+ g.setDescription("Description");
+ g.setOwnerGroupUUID(new AccountGroup.UUID("owner"));
+ g.setVisibleToAll(true);
+ GroupBundle b = builder().group(g).build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ assertMigratedCleanly(reload(g), b);
+ ImmutableList<CommitInfo> log = log(g);
+ assertThat(log).hasSize(1);
+ assertServerCommit(log.get(0), "Create group");
+ }
+
+ @Test
+ public void emptyGroupName() throws Exception {
+ AccountGroup g = newGroup("");
+ GroupBundle b = builder().group(g).build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ GroupBundle noteDbBundle = reload(g);
+ assertMigratedCleanly(noteDbBundle, b);
+ assertThat(noteDbBundle.group().getName()).isEmpty();
+ }
+
+ @Test
+ public void nullGroupDescription() throws Exception {
+ AccountGroup g = newGroup("a");
+ g.setDescription(null);
+ assertThat(g.getDescription()).isNull();
+ GroupBundle b = builder().group(g).build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ GroupBundle noteDbBundle = reload(g);
+ assertMigratedCleanly(noteDbBundle, b);
+ assertThat(noteDbBundle.group().getDescription()).isNull();
+ }
+
+ @Test
+ public void emptyGroupDescription() throws Exception {
+ AccountGroup g = newGroup("a");
+ g.setDescription("");
+ assertThat(g.getDescription()).isEmpty();
+ GroupBundle b = builder().group(g).build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ GroupBundle noteDbBundle = reload(g);
+ assertMigratedCleanly(noteDbBundle, b);
+ assertThat(noteDbBundle.group().getDescription()).isNull();
+ }
+
+ @Test
+ public void membersAndSubgroups() throws Exception {
+ AccountGroup g = newGroup("a");
+ GroupBundle b =
+ builder()
+ .group(g)
+ .members(member(g, 1), member(g, 2))
+ .byId(byId(g, "x"), byId(g, "y"))
+ .build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ assertMigratedCleanly(reload(g), b);
+ ImmutableList<CommitInfo> log = log(g);
+ assertThat(log).hasSize(2);
+ assertServerCommit(log.get(0), "Create group");
+ assertServerCommit(
+ log.get(1),
+ "Update group\n"
+ + "\n"
+ + "Add-group: Group x <x>\n"
+ + "Add-group: Group y <y>\n"
+ + "Add: Account 1 <1@server-id>\n"
+ + "Add: Account 2 <2@server-id>");
+ }
+
+ @Test
+ public void memberAudit() throws Exception {
+ AccountGroup g = newGroup("a");
+ Timestamp t1 = TimeUtil.nowTs();
+ Timestamp t2 = TimeUtil.nowTs();
+ Timestamp t3 = TimeUtil.nowTs();
+ GroupBundle b =
+ builder()
+ .group(g)
+ .members(member(g, 1))
+ .memberAudit(addMember(g, 1, 8, t2), addAndRemoveMember(g, 2, 8, t1, 9, t3))
+ .build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ assertMigratedCleanly(reload(g), b);
+ ImmutableList<CommitInfo> log = log(g);
+ assertThat(log).hasSize(4);
+ assertServerCommit(log.get(0), "Create group");
+ assertCommit(
+ log.get(1), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 8", "8@server-id");
+ assertCommit(
+ log.get(2), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
+ assertCommit(
+ log.get(3), "Update group\n\nRemove: Account 2 <2@server-id>", "Account 9", "9@server-id");
+ }
+
+ @Test
+ public void memberAuditLegacyRemoved() throws Exception {
+ AccountGroup g = newGroup("a");
+ GroupBundle b =
+ builder()
+ .group(g)
+ .members(member(g, 2))
+ .memberAudit(
+ addAndLegacyRemoveMember(g, 1, 8, TimeUtil.nowTs()),
+ addMember(g, 2, 8, TimeUtil.nowTs()))
+ .build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ assertMigratedCleanly(reload(g), b);
+ ImmutableList<CommitInfo> log = log(g);
+ assertThat(log).hasSize(4);
+ assertServerCommit(log.get(0), "Create group");
+ assertCommit(
+ log.get(1), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
+ assertCommit(
+ log.get(2), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 8", "8@server-id");
+ assertCommit(
+ log.get(3), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 8", "8@server-id");
+ }
+
+ @Test
+ public void unauditedMembershipsAddedAtEnd() throws Exception {
+ AccountGroup g = newGroup("a");
+ GroupBundle b =
+ builder()
+ .group(g)
+ .members(member(g, 1), member(g, 2), member(g, 3))
+ .memberAudit(addMember(g, 1, 8, TimeUtil.nowTs()))
+ .build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ assertMigratedCleanly(reload(g), b);
+ ImmutableList<CommitInfo> log = log(g);
+ assertThat(log).hasSize(3);
+ assertServerCommit(log.get(0), "Create group");
+ assertCommit(
+ log.get(1), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
+ assertServerCommit(
+ log.get(2), "Update group\n\nAdd: Account 2 <2@server-id>\nAdd: Account 3 <3@server-id>");
+ }
+
+ @Test
+ public void byIdAudit() throws Exception {
+ AccountGroup g = newGroup("a");
+ Timestamp t1 = TimeUtil.nowTs();
+ Timestamp t2 = TimeUtil.nowTs();
+ Timestamp t3 = TimeUtil.nowTs();
+ GroupBundle b =
+ builder()
+ .group(g)
+ .byId(byId(g, "x"))
+ .byIdAudit(addById(g, "x", 8, t2), addAndRemoveById(g, "y", 8, t1, 9, t3))
+ .build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ assertMigratedCleanly(reload(g), b);
+ ImmutableList<CommitInfo> log = log(g);
+ assertThat(log).hasSize(4);
+ assertServerCommit(log.get(0), "Create group");
+ assertCommit(log.get(1), "Update group\n\nAdd-group: Group y <y>", "Account 8", "8@server-id");
+ assertCommit(log.get(2), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
+ assertCommit(
+ log.get(3), "Update group\n\nRemove-group: Group y <y>", "Account 9", "9@server-id");
+ }
+
+ @Test
+ public void unauditedByIdAddedAtEnd() throws Exception {
+ AccountGroup g = newGroup("a");
+ GroupBundle b =
+ builder()
+ .group(g)
+ .byId(byId(g, "x"), byId(g, "y"), byId(g, "z"))
+ .byIdAudit(addById(g, "x", 8, TimeUtil.nowTs()))
+ .build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ assertMigratedCleanly(reload(g), b);
+ ImmutableList<CommitInfo> log = log(g);
+ assertThat(log).hasSize(3);
+ assertServerCommit(log.get(0), "Create group");
+ assertCommit(log.get(1), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
+ assertServerCommit(
+ log.get(2), "Update group\n\nAdd-group: Group y <y>\nAdd-group: Group z <z>");
+ }
+
+ @Test
+ public void auditsAtSameTimestampBrokenDownByType() throws Exception {
+ AccountGroup g = newGroup("a");
+ Timestamp ts = TimeUtil.nowTs();
+ int user = 8;
+ GroupBundle b =
+ builder()
+ .group(g)
+ .members(member(g, 1), member(g, 2))
+ .memberAudit(
+ addMember(g, 1, user, ts),
+ addMember(g, 2, user, ts),
+ addAndRemoveMember(g, 3, user, ts, user, ts))
+ .byId(byId(g, "x"), byId(g, "y"))
+ .byIdAudit(
+ addById(g, "x", user, ts),
+ addById(g, "y", user, ts),
+ addAndRemoveById(g, "z", user, ts, user, ts))
+ .build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ assertMigratedCleanly(reload(g), b);
+ ImmutableList<CommitInfo> log = log(g);
+ assertThat(log).hasSize(5);
+ assertServerCommit(log.get(0), "Create group");
+ assertCommit(
+ log.get(1),
+ "Update group\n"
+ + "\n"
+ + "Add: Account 1 <1@server-id>\n"
+ + "Add: Account 2 <2@server-id>\n"
+ + "Add: Account 3 <3@server-id>",
+ "Account 8",
+ "8@server-id");
+ assertCommit(
+ log.get(2), "Update group\n\nRemove: Account 3 <3@server-id>", "Account 8", "8@server-id");
+ assertCommit(
+ log.get(3),
+ "Update group\n"
+ + "\n"
+ + "Add-group: Group x <x>\n"
+ + "Add-group: Group y <y>\n"
+ + "Add-group: Group z <z>",
+ "Account 8",
+ "8@server-id");
+ assertCommit(
+ log.get(4), "Update group\n\nRemove-group: Group z <z>", "Account 8", "8@server-id");
+ }
+
+ @Test
+ public void auditsAtSameTimestampBrokenDownByUserAndType() throws Exception {
+ AccountGroup g = newGroup("a");
+ Timestamp ts = TimeUtil.nowTs();
+ int user1 = 8;
+ int user2 = 9;
+
+ GroupBundle b =
+ builder()
+ .group(g)
+ .members(member(g, 1), member(g, 2), member(g, 3))
+ .memberAudit(
+ addMember(g, 1, user1, ts), addMember(g, 2, user2, ts), addMember(g, 3, user1, ts))
+ .byId(byId(g, "x"), byId(g, "y"), byId(g, "z"))
+ .byIdAudit(
+ addById(g, "x", user1, ts), addById(g, "y", user2, ts), addById(g, "z", user1, ts))
+ .build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ assertMigratedCleanly(reload(g), b);
+ ImmutableList<CommitInfo> log = log(g);
+ assertThat(log).hasSize(5);
+ assertServerCommit(log.get(0), "Create group");
+ assertCommit(
+ log.get(1),
+ "Update group\n" + "\n" + "Add: Account 1 <1@server-id>\n" + "Add: Account 3 <3@server-id>",
+ "Account 8",
+ "8@server-id");
+ assertCommit(
+ log.get(2),
+ "Update group\n\nAdd-group: Group x <x>\nAdd-group: Group z <z>",
+ "Account 8",
+ "8@server-id");
+ assertCommit(
+ log.get(3), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 9", "9@server-id");
+ assertCommit(log.get(4), "Update group\n\nAdd-group: Group y <y>", "Account 9", "9@server-id");
+ }
+
+ @Test
+ public void fixupCommitPostDatesAllAuditEventsEvenIfAuditEventsAreInTheFuture() throws Exception {
+ AccountGroup g = newGroup("a");
+ IntStream.range(0, 20).forEach(i -> TimeUtil.nowTs());
+ Timestamp future = TimeUtil.nowTs();
+ TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
+
+ GroupBundle b =
+ builder()
+ .group(g)
+ .byId(byId(g, "x"), byId(g, "y"), byId(g, "z"))
+ .byIdAudit(addById(g, "x", 8, future))
+ .build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ assertMigratedCleanly(reload(g), b);
+ ImmutableList<CommitInfo> log = log(g);
+ assertThat(log).hasSize(3);
+ assertServerCommit(log.get(0), "Create group");
+ assertCommit(log.get(1), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
+ assertServerCommit(
+ log.get(2), "Update group\n\nAdd-group: Group y <y>\nAdd-group: Group z <z>");
+
+ assertThat(log.stream().map(c -> c.committer.date).collect(toImmutableList()))
+ .named("%s", log)
+ .isOrdered();
+ assertThat(TimeUtil.nowTs()).isLessThan(future);
+ }
+
+ @Test
+ public void redundantMemberAuditsAreIgnored() throws Exception {
+ AccountGroup g = newGroup("a");
+ Timestamp t1 = TimeUtil.nowTs();
+ Timestamp t2 = TimeUtil.nowTs();
+ Timestamp t3 = TimeUtil.nowTs();
+ Timestamp t4 = TimeUtil.nowTs();
+ Timestamp t5 = TimeUtil.nowTs();
+ GroupBundle b =
+ builder()
+ .group(g)
+ .members(member(g, 2))
+ .memberAudit(
+ addMember(g, 1, 8, t1),
+ addMember(g, 1, 8, t1),
+ addMember(g, 1, 8, t3),
+ addMember(g, 1, 9, t4),
+ addAndRemoveMember(g, 1, 8, t2, 9, t5),
+ addAndLegacyRemoveMember(g, 2, 9, t3),
+ addMember(g, 2, 8, t1),
+ addMember(g, 2, 9, t4),
+ addMember(g, 1, 8, t5))
+ .build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ assertMigratedCleanly(reload(g), b);
+ ImmutableList<CommitInfo> log = log(g);
+ assertThat(log).hasSize(5);
+ assertServerCommit(log.get(0), "Create group");
+ assertCommit(
+ log.get(1),
+ "Update group\n\nAdd: Account 1 <1@server-id>\nAdd: Account 2 <2@server-id>",
+ "Account 8",
+ "8@server-id");
+ assertCommit(
+ log.get(2), "Update group\n\nRemove: Account 2 <2@server-id>", "Account 9", "9@server-id");
+ assertCommit(
+ log.get(3), "Update group\n\nAdd: Account 2 <2@server-id>", "Account 9", "9@server-id");
+ assertCommit(
+ log.get(4), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 9", "9@server-id");
+ }
+
+ @Test
+ public void additionsAndRemovalsWithinSameSecondCanBeMigrated() throws Exception {
+ TestTimeUtil.resetWithClockStep(1, TimeUnit.MILLISECONDS);
+ AccountGroup g = newGroup("a");
+ Timestamp t1 = TimeUtil.nowTs();
+ Timestamp t2 = TimeUtil.nowTs();
+ Timestamp t3 = TimeUtil.nowTs();
+ Timestamp t4 = TimeUtil.nowTs();
+ Timestamp t5 = TimeUtil.nowTs();
+ GroupBundle b =
+ builder()
+ .group(g)
+ .members(member(g, 1))
+ .memberAudit(
+ addAndLegacyRemoveMember(g, 1, 8, t1),
+ addMember(g, 1, 10, t2),
+ addAndRemoveMember(g, 1, 8, t3, 9, t4),
+ addMember(g, 1, 8, t5))
+ .build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ assertMigratedCleanly(reload(g), b);
+ ImmutableList<CommitInfo> log = log(g);
+ assertThat(log).hasSize(6);
+ assertServerCommit(log.get(0), "Create group");
+ assertCommit(
+ log.get(1), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
+ assertCommit(
+ log.get(2), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 8", "8@server-id");
+ assertCommit(
+ log.get(3), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 10", "10@server-id");
+ assertCommit(
+ log.get(4), "Update group\n\nRemove: Account 1 <1@server-id>", "Account 9", "9@server-id");
+ assertCommit(
+ log.get(5), "Update group\n\nAdd: Account 1 <1@server-id>", "Account 8", "8@server-id");
+ }
+
+ @Test
+ public void redundantByIdAuditsAreIgnored() throws Exception {
+ AccountGroup g = newGroup("a");
+ Timestamp t1 = TimeUtil.nowTs();
+ Timestamp t2 = TimeUtil.nowTs();
+ Timestamp t3 = TimeUtil.nowTs();
+ Timestamp t4 = TimeUtil.nowTs();
+ Timestamp t5 = TimeUtil.nowTs();
+ GroupBundle b =
+ builder()
+ .group(g)
+ .byId()
+ .byIdAudit(
+ addById(g, "x", 8, t1),
+ addById(g, "x", 8, t3),
+ addById(g, "x", 9, t4),
+ addAndRemoveById(g, "x", 8, t2, 9, t5))
+ .build();
+
+ rebuilder.rebuild(repo, b, null);
+
+ assertMigratedCleanly(reload(g), b);
+ ImmutableList<CommitInfo> log = log(g);
+ assertThat(log).hasSize(3);
+ assertServerCommit(log.get(0), "Create group");
+ assertCommit(log.get(1), "Update group\n\nAdd-group: Group x <x>", "Account 8", "8@server-id");
+ assertCommit(
+ log.get(2), "Update group\n\nRemove-group: Group x <x>", "Account 9", "9@server-id");
+ }
+
+ @Test
+ public void combineWithBatchGroupNameNotes() throws Exception {
+ AccountGroup g1 = newGroup("a");
+ AccountGroup g2 = newGroup("b");
+ GroupReference gr1 = new GroupReference(g1.getGroupUUID(), g1.getName());
+ GroupReference gr2 = new GroupReference(g2.getGroupUUID(), g2.getName());
+
+ GroupBundle b1 = builder().group(g1).build();
+ GroupBundle b2 = builder().group(g2).build();
+
+ BatchRefUpdate bru = repo.getRefDatabase().newBatchUpdate();
+
+ rebuilder.rebuild(repo, b1, bru);
+ rebuilder.rebuild(repo, b2, bru);
+ try (ObjectInserter inserter = repo.newObjectInserter()) {
+ ImmutableList<GroupReference> refs = ImmutableList.of(gr1, gr2);
+ GroupNameNotes.updateAllGroups(repo, inserter, bru, refs, newPersonIdent());
+ inserter.flush();
+ }
+
+ assertThat(log(g1)).isEmpty();
+ assertThat(log(g2)).isEmpty();
+ assertThat(logGroupNames()).isEmpty();
+
+ RefUpdateUtil.executeChecked(bru, repo);
+
+ assertThat(log(g1)).hasSize(1);
+ assertThat(log(g2)).hasSize(1);
+ assertThat(logGroupNames()).hasSize(1);
+ assertMigratedCleanly(reload(g1), b1);
+ assertMigratedCleanly(reload(g2), b2);
+
+ assertThat(GroupNameNotes.loadAllGroups(repo)).containsExactly(gr1, gr2);
+ }
+
+ @Test
+ public void groupNamesWithLeadingAndTrailingWhitespace() throws Exception {
+ for (String leading : ImmutableList.of("", " ", " ")) {
+ for (String trailing : ImmutableList.of("", " ", " ")) {
+ AccountGroup g = newGroup(leading + "a" + trailing);
+ GroupBundle b = builder().group(g).build();
+ rebuilder.rebuild(repo, b, null);
+ assertMigratedCleanly(reload(g), b);
+ }
+ }
+ }
+
+ @Test
+ public void disallowExisting() throws Exception {
+ AccountGroup g = newGroup("a");
+ GroupBundle b = builder().group(g).build();
+
+ rebuilder.rebuild(repo, b, null);
+ assertMigratedCleanly(reload(g), b);
+ String refName = RefNames.refsGroups(g.getGroupUUID());
+ ObjectId oldId = repo.exactRef(refName).getObjectId();
+
+ try {
+ rebuilder.rebuild(repo, b, null);
+ assert_().fail("expected OrmDuplicateKeyException");
+ } catch (OrmDuplicateKeyException e) {
+ // Expected.
+ }
+
+ assertThat(repo.exactRef(refName).getObjectId()).isEqualTo(oldId);
+ }
+
+ private GroupBundle reload(AccountGroup g) throws Exception {
+ return bundleFactory.fromNoteDb(allUsersName, repo, g.getGroupUUID());
+ }
+
+ private void assertMigratedCleanly(GroupBundle noteDbBundle, GroupBundle expectedReviewDbBundle) {
+ assertThat(GroupBundle.compareWithAudits(expectedReviewDbBundle, noteDbBundle)).isEmpty();
+ }
+
+ private AccountGroup newGroup(String name) {
+ int id = idCounter.incrementAndGet();
+ return new AccountGroup(
+ new AccountGroup.NameKey(name),
+ new AccountGroup.Id(id),
+ new AccountGroup.UUID(name.trim() + "-" + id),
+ TimeUtil.nowTs());
+ }
+
+ private AccountGroupMember member(AccountGroup g, int accountId) {
+ return new AccountGroupMember(new AccountGroupMember.Key(new Account.Id(accountId), g.getId()));
+ }
+
+ private AccountGroupMemberAudit addMember(
+ AccountGroup g, int accountId, int adder, Timestamp addedOn) {
+ return new AccountGroupMemberAudit(member(g, accountId), new Account.Id(adder), addedOn);
+ }
+
+ private AccountGroupMemberAudit addAndLegacyRemoveMember(
+ AccountGroup g, int accountId, int adder, Timestamp addedOn) {
+ AccountGroupMemberAudit a = addMember(g, accountId, adder, addedOn);
+ a.removedLegacy();
+ return a;
+ }
+
+ private AccountGroupMemberAudit addAndRemoveMember(
+ AccountGroup g,
+ int accountId,
+ int adder,
+ Timestamp addedOn,
+ int removedBy,
+ Timestamp removedOn) {
+ AccountGroupMemberAudit a = addMember(g, accountId, adder, addedOn);
+ a.removed(new Account.Id(removedBy), removedOn);
+ return a;
+ }
+
+ private AccountGroupByIdAud addById(
+ AccountGroup g, String subgroupUuid, int adder, Timestamp addedOn) {
+ return new AccountGroupByIdAud(byId(g, subgroupUuid), new Account.Id(adder), addedOn);
+ }
+
+ private AccountGroupByIdAud addAndRemoveById(
+ AccountGroup g,
+ String subgroupUuid,
+ int adder,
+ Timestamp addedOn,
+ int removedBy,
+ Timestamp removedOn) {
+ AccountGroupByIdAud a = addById(g, subgroupUuid, adder, addedOn);
+ a.removed(new Account.Id(removedBy), removedOn);
+ return a;
+ }
+
+ private AccountGroupById byId(AccountGroup g, String subgroupUuid) {
+ return new AccountGroupById(
+ new AccountGroupById.Key(g.getId(), new AccountGroup.UUID(subgroupUuid)));
+ }
+
+ private ImmutableList<CommitInfo> log(AccountGroup g) throws Exception {
+ return GitTestUtil.log(repo, RefNames.refsGroups(g.getGroupUUID()));
+ }
+
+ private ImmutableList<CommitInfo> logGroupNames() throws Exception {
+ return GitTestUtil.log(repo, REFS_GROUPNAMES);
+ }
+
+ private static GroupBundle.Builder builder() {
+ return GroupBundle.builder().source(GroupBundle.Source.REVIEW_DB);
+ }
+
+ private static PersonIdent newPersonIdent() {
+ return new PersonIdent(SERVER_NAME, SERVER_EMAIL, TimeUtil.nowTs(), TZ);
+ }
+
+ private static void assertServerCommit(CommitInfo commitInfo, String expectedMessage) {
+ assertCommit(commitInfo, expectedMessage, SERVER_NAME, SERVER_EMAIL);
+ }
+
+ private static void assertCommit(
+ CommitInfo commitInfo, String expectedMessage, String expectedName, String expectedEmail) {
+ assertThat(commitInfo).message().isEqualTo(expectedMessage);
+ assertThat(commitInfo).author().name().isEqualTo(expectedName);
+ assertThat(commitInfo).author().email().isEqualTo(expectedEmail);
+
+ // Committer should always be the server, regardless of author.
+ assertThat(commitInfo).committer().name().isEqualTo(SERVER_NAME);
+ assertThat(commitInfo).committer().email().isEqualTo(SERVER_EMAIL);
+ assertThat(commitInfo).committer().date().isEqualTo(commitInfo.author.date);
+ assertThat(commitInfo).committer().tz().isEqualTo(commitInfo.author.tz);
+ }
+
+ private static AuditLogFormatter getAuditLogFormatter() {
+ return AuditLogFormatter.create(
+ GroupRebuilderTest::getAccount, GroupRebuilderTest::getGroup, SERVER_ID);
+ }
+
+ private static Optional<Account> getAccount(Account.Id id) {
+ Account account = new Account(id, TimeUtil.nowTs());
+ account.setFullName("Account " + id);
+ return Optional.of(account);
+ }
+
+ private static Optional<GroupDescription.Basic> getGroup(AccountGroup.UUID uuid) {
+ GroupDescription.Basic group =
+ new GroupDescription.Basic() {
+ @Override
+ public AccountGroup.UUID getGroupUUID() {
+ return uuid;
+ }
+
+ @Override
+ public String getName() {
+ return "Group " + uuid;
+ }
+
+ @Nullable
+ @Override
+ public String getEmailAddress() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getUrl() {
+ return null;
+ }
+ };
+ return Optional.of(group);
+ }
+}