| // 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); |
| } |
| } |