diff options
Diffstat (limited to 'javatests/com/google/gerrit/acceptance/ProjectResetterTest.java')
-rw-r--r-- | javatests/com/google/gerrit/acceptance/ProjectResetterTest.java | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/javatests/com/google/gerrit/acceptance/ProjectResetterTest.java b/javatests/com/google/gerrit/acceptance/ProjectResetterTest.java new file mode 100644 index 0000000000..53d8ef8799 --- /dev/null +++ b/javatests/com/google/gerrit/acceptance/ProjectResetterTest.java @@ -0,0 +1,536 @@ +// 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.acceptance; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableSet; +import com.google.gerrit.common.Nullable; +import com.google.gerrit.reviewdb.client.Account; +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.account.AccountCache; +import com.google.gerrit.server.account.GroupCache; +import com.google.gerrit.server.account.GroupIncludeCache; +import com.google.gerrit.server.config.AllUsersName; +import com.google.gerrit.server.config.AllUsersNameProvider; +import com.google.gerrit.server.index.account.AccountIndexer; +import com.google.gerrit.server.index.group.GroupIndexer; +import com.google.gerrit.server.project.ProjectCache; +import com.google.gerrit.server.util.time.TimeUtil; +import com.google.gerrit.testing.GerritBaseTests; +import com.google.gerrit.testing.InMemoryRepositoryManager; +import com.google.gerrit.testing.TestTimeUtil; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import org.easymock.EasyMock; +import org.eclipse.jgit.lib.CommitBuilder; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectInserter; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevWalk; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ProjectResetterTest extends GerritBaseTests { + private InMemoryRepositoryManager repoManager; + private Project.NameKey project; + private Repository repo; + + @Before + public void setUp() throws Exception { + repoManager = new InMemoryRepositoryManager(); + project = new Project.NameKey("foo"); + repo = repoManager.createRepository(project); + } + + @Before + public void setTimeForTesting() { + TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS); + } + + @After + public void resetTime() { + TestTimeUtil.useSystemTime(); + } + + @Test + public void resetAllRefs() throws Exception { + Ref matchingRef = createRef("refs/any/test"); + + try (ProjectResetter resetProject = + builder().build(new ProjectResetter.Config().reset(project))) { + updateRef(matchingRef); + } + + // The matching refs are reset to the old state. + assertRef(matchingRef); + } + + @Test + public void onlyResetMatchingRefs() throws Exception { + Ref matchingRef = createRef("refs/match/test"); + Ref anotherMatchingRef = createRef("refs/another-match/test"); + Ref nonMatchingRef = createRef("refs/no-match/test"); + + Ref updatedNonMatchingRef; + try (ProjectResetter resetProject = + builder() + .build( + new ProjectResetter.Config() + .reset(project, "refs/match/*", "refs/another-match/*"))) { + updateRef(matchingRef); + updateRef(anotherMatchingRef); + updatedNonMatchingRef = updateRef(nonMatchingRef); + } + + // The matching refs are reset to the old state. + assertRef(matchingRef); + assertRef(anotherMatchingRef); + + // The non-matching ref is not reset, hence it still has the updated state. + assertRef(updatedNonMatchingRef); + } + + @Test + public void onlyDeleteNewlyCreatedMatchingRefs() throws Exception { + Ref matchingRef; + Ref anotherMatchingRef; + Ref nonMatchingRef; + try (ProjectResetter resetProject = + builder() + .build( + new ProjectResetter.Config() + .reset(project, "refs/match/*", "refs/another-match/*"))) { + matchingRef = createRef("refs/match/test"); + anotherMatchingRef = createRef("refs/another-match/test"); + nonMatchingRef = createRef("refs/no-match/test"); + } + + // The matching refs are deleted since they didn't exist before. + assertDeletedRef(matchingRef); + assertDeletedRef(anotherMatchingRef); + + // The non-matching ref is not deleted. + assertRef(nonMatchingRef); + } + + @Test + public void onlyResetMatchingRefsMultipleProjects() throws Exception { + Project.NameKey project2 = new Project.NameKey("bar"); + Repository repo2 = repoManager.createRepository(project2); + + Ref matchingRefProject1 = createRef("refs/foo/test"); + Ref nonMatchingRefProject1 = createRef("refs/bar/test"); + + Ref matchingRefProject2 = createRef(repo2, "refs/bar/test"); + Ref nonMatchingRefProject2 = createRef(repo2, "refs/foo/test"); + + Ref updatedNonMatchingRefProject1; + Ref updatedNonMatchingRefProject2; + try (ProjectResetter resetProject = + builder() + .build( + new ProjectResetter.Config() + .reset(project, "refs/foo/*") + .reset(project2, "refs/bar/*"))) { + updateRef(matchingRefProject1); + updatedNonMatchingRefProject1 = updateRef(nonMatchingRefProject1); + + updateRef(repo2, matchingRefProject2); + updatedNonMatchingRefProject2 = updateRef(repo2, nonMatchingRefProject2); + } + + // The matching refs are reset to the old state. + assertRef(matchingRefProject1); + assertRef(repo2, matchingRefProject2); + + // The non-matching refs are not reset, hence they still has the updated states. + assertRef(updatedNonMatchingRefProject1); + assertRef(repo2, updatedNonMatchingRefProject2); + } + + @Test + public void onlyDeleteNewlyCreatedMatchingRefsMultipleProjects() throws Exception { + Project.NameKey project2 = new Project.NameKey("bar"); + Repository repo2 = repoManager.createRepository(project2); + + Ref matchingRefProject1; + Ref nonMatchingRefProject1; + Ref matchingRefProject2; + Ref nonMatchingRefProject2; + try (ProjectResetter resetProject = + builder() + .build( + new ProjectResetter.Config() + .reset(project, "refs/foo/*") + .reset(project2, "refs/bar/*"))) { + matchingRefProject1 = createRef("refs/foo/test"); + nonMatchingRefProject1 = createRef("refs/bar/test"); + + matchingRefProject2 = createRef(repo2, "refs/bar/test"); + nonMatchingRefProject2 = createRef(repo2, "refs/foo/test"); + } + + // The matching refs are deleted since they didn't exist before. + assertDeletedRef(matchingRefProject1); + assertDeletedRef(repo2, matchingRefProject2); + + // The non-matching ref is not deleted. + assertRef(nonMatchingRefProject1); + assertRef(repo2, nonMatchingRefProject2); + } + + @Test + public void onlyDeleteNewlyCreatedWithOverlappingRefPatterns() throws Exception { + Ref matchingRef; + try (ProjectResetter resetProject = + builder() + .build( + new ProjectResetter.Config().reset(project, "refs/match/*", "refs/match/test"))) { + // This ref matches 2 ref pattern, ProjectResetter should try to delete it only once. + matchingRef = createRef("refs/match/test"); + } + + // The matching ref is deleted since it didn't exist before. + assertDeletedRef(matchingRef); + } + + @Test + public void projectEvictionIfRefsMetaConfigIsReset() throws Exception { + Project.NameKey project2 = new Project.NameKey("bar"); + Repository repo2 = repoManager.createRepository(project2); + Ref metaConfig = createRef(repo2, RefNames.REFS_CONFIG); + + ProjectCache projectCache = EasyMock.createNiceMock(ProjectCache.class); + projectCache.evict(project2); + EasyMock.expectLastCall(); + EasyMock.replay(projectCache); + + Ref nonMetaConfig = createRef("refs/heads/master"); + + try (ProjectResetter resetProject = + builder(null, null, null, null, null, null, projectCache) + .build(new ProjectResetter.Config().reset(project).reset(project2))) { + updateRef(nonMetaConfig); + updateRef(repo2, metaConfig); + } + + EasyMock.verify(projectCache); + } + + @Test + public void projectEvictionIfRefsMetaConfigIsDeleted() throws Exception { + Project.NameKey project2 = new Project.NameKey("bar"); + Repository repo2 = repoManager.createRepository(project2); + + ProjectCache projectCache = EasyMock.createNiceMock(ProjectCache.class); + projectCache.evict(project2); + EasyMock.expectLastCall(); + EasyMock.replay(projectCache); + + try (ProjectResetter resetProject = + builder(null, null, null, null, null, null, projectCache) + .build(new ProjectResetter.Config().reset(project).reset(project2))) { + createRef("refs/heads/master"); + createRef(repo2, RefNames.REFS_CONFIG); + } + + EasyMock.verify(projectCache); + } + + @Test + public void accountEvictionIfUserBranchIsReset() throws Exception { + Account.Id accountId = new Account.Id(1); + Project.NameKey allUsers = new Project.NameKey(AllUsersNameProvider.DEFAULT); + Repository allUsersRepo = repoManager.createRepository(allUsers); + Ref userBranch = createRef(allUsersRepo, RefNames.refsUsers(accountId)); + + AccountCache accountCache = EasyMock.createNiceMock(AccountCache.class); + accountCache.evict(accountId); + EasyMock.expectLastCall(); + EasyMock.replay(accountCache); + + AccountIndexer accountIndexer = EasyMock.createNiceMock(AccountIndexer.class); + accountIndexer.index(accountId); + EasyMock.expectLastCall(); + EasyMock.replay(accountIndexer); + + // Non-user branch because it's not in All-Users. + Ref nonUserBranch = createRef(RefNames.refsUsers(new Account.Id(2))); + + try (ProjectResetter resetProject = + builder(null, accountCache, accountIndexer, null, null, null, null) + .build(new ProjectResetter.Config().reset(project).reset(allUsers))) { + updateRef(nonUserBranch); + updateRef(allUsersRepo, userBranch); + } + + EasyMock.verify(accountCache, accountIndexer); + } + + @Test + public void accountEvictionIfUserBranchIsDeleted() throws Exception { + Account.Id accountId = new Account.Id(1); + Project.NameKey allUsers = new Project.NameKey(AllUsersNameProvider.DEFAULT); + Repository allUsersRepo = repoManager.createRepository(allUsers); + + AccountCache accountCache = EasyMock.createNiceMock(AccountCache.class); + accountCache.evict(accountId); + EasyMock.expectLastCall(); + EasyMock.replay(accountCache); + + AccountIndexer accountIndexer = EasyMock.createNiceMock(AccountIndexer.class); + accountIndexer.index(accountId); + EasyMock.expectLastCall(); + EasyMock.replay(accountIndexer); + + try (ProjectResetter resetProject = + builder(null, accountCache, accountIndexer, null, null, null, null) + .build(new ProjectResetter.Config().reset(project).reset(allUsers))) { + // Non-user branch because it's not in All-Users. + createRef(RefNames.refsUsers(new Account.Id(2))); + + createRef(allUsersRepo, RefNames.refsUsers(accountId)); + } + + EasyMock.verify(accountCache, accountIndexer); + } + + @Test + public void accountEvictionIfExternalIdsBranchIsReset() throws Exception { + Account.Id accountId = new Account.Id(1); + Project.NameKey allUsers = new Project.NameKey(AllUsersNameProvider.DEFAULT); + Repository allUsersRepo = repoManager.createRepository(allUsers); + Ref externalIds = createRef(allUsersRepo, RefNames.REFS_EXTERNAL_IDS); + createRef(allUsersRepo, RefNames.refsUsers(accountId)); + + Account.Id accountId2 = new Account.Id(2); + + AccountCache accountCache = EasyMock.createNiceMock(AccountCache.class); + accountCache.evict(accountId); + EasyMock.expectLastCall(); + accountCache.evict(accountId2); + EasyMock.expectLastCall(); + EasyMock.replay(accountCache); + + AccountIndexer accountIndexer = EasyMock.createNiceMock(AccountIndexer.class); + accountIndexer.index(accountId); + EasyMock.expectLastCall(); + accountIndexer.index(accountId2); + EasyMock.expectLastCall(); + EasyMock.replay(accountIndexer); + + // Non-user branch because it's not in All-Users. + Ref nonUserBranch = createRef(RefNames.refsUsers(new Account.Id(3))); + + try (ProjectResetter resetProject = + builder(null, accountCache, accountIndexer, null, null, null, null) + .build(new ProjectResetter.Config().reset(project).reset(allUsers))) { + updateRef(nonUserBranch); + updateRef(allUsersRepo, externalIds); + createRef(allUsersRepo, RefNames.refsUsers(accountId2)); + } + + EasyMock.verify(accountCache, accountIndexer); + } + + @Test + public void accountEvictionIfExternalIdsBranchIsDeleted() throws Exception { + Account.Id accountId = new Account.Id(1); + Project.NameKey allUsers = new Project.NameKey(AllUsersNameProvider.DEFAULT); + Repository allUsersRepo = repoManager.createRepository(allUsers); + createRef(allUsersRepo, RefNames.refsUsers(accountId)); + + Account.Id accountId2 = new Account.Id(2); + + AccountCache accountCache = EasyMock.createNiceMock(AccountCache.class); + accountCache.evict(accountId); + EasyMock.expectLastCall(); + accountCache.evict(accountId2); + EasyMock.expectLastCall(); + EasyMock.replay(accountCache); + + AccountIndexer accountIndexer = EasyMock.createNiceMock(AccountIndexer.class); + accountIndexer.index(accountId); + EasyMock.expectLastCall(); + accountIndexer.index(accountId2); + EasyMock.expectLastCall(); + EasyMock.replay(accountIndexer); + + // Non-user branch because it's not in All-Users. + Ref nonUserBranch = createRef(RefNames.refsUsers(new Account.Id(3))); + + try (ProjectResetter resetProject = + builder(null, accountCache, accountIndexer, null, null, null, null) + .build(new ProjectResetter.Config().reset(project).reset(allUsers))) { + updateRef(nonUserBranch); + createRef(allUsersRepo, RefNames.REFS_EXTERNAL_IDS); + createRef(allUsersRepo, RefNames.refsUsers(accountId2)); + } + + EasyMock.verify(accountCache, accountIndexer); + } + + @Test + public void accountEvictionFromAccountCreatorIfUserBranchIsDeleted() throws Exception { + Account.Id accountId = new Account.Id(1); + Project.NameKey allUsers = new Project.NameKey(AllUsersNameProvider.DEFAULT); + Repository allUsersRepo = repoManager.createRepository(allUsers); + + AccountCreator accountCreator = EasyMock.createNiceMock(AccountCreator.class); + accountCreator.evict(ImmutableSet.of(accountId)); + EasyMock.expectLastCall(); + EasyMock.replay(accountCreator); + + try (ProjectResetter resetProject = + builder(accountCreator, null, null, null, null, null, null) + .build(new ProjectResetter.Config().reset(project).reset(allUsers))) { + createRef(allUsersRepo, RefNames.refsUsers(accountId)); + } + + EasyMock.verify(accountCreator); + } + + @Test + public void groupEviction() throws Exception { + AccountGroup.UUID uuid1 = new AccountGroup.UUID("abcd1"); + AccountGroup.UUID uuid2 = new AccountGroup.UUID("abcd2"); + AccountGroup.UUID uuid3 = new AccountGroup.UUID("abcd3"); + Project.NameKey allUsers = new Project.NameKey(AllUsersNameProvider.DEFAULT); + Repository allUsersRepo = repoManager.createRepository(allUsers); + + GroupCache cache = EasyMock.createNiceMock(GroupCache.class); + GroupIndexer indexer = EasyMock.createNiceMock(GroupIndexer.class); + GroupIncludeCache includeCache = EasyMock.createNiceMock(GroupIncludeCache.class); + cache.evict(uuid2); + indexer.index(uuid2); + includeCache.evictParentGroupsOf(uuid2); + cache.evict(uuid3); + indexer.index(uuid3); + includeCache.evictParentGroupsOf(uuid3); + EasyMock.expectLastCall(); + + EasyMock.replay(cache, indexer); + + createRef(allUsersRepo, RefNames.refsGroups(uuid1)); + Ref ref2 = createRef(allUsersRepo, RefNames.refsGroups(uuid2)); + try (ProjectResetter resetProject = + builder(null, null, null, cache, includeCache, indexer, null) + .build(new ProjectResetter.Config().reset(project).reset(allUsers))) { + updateRef(allUsersRepo, ref2); + createRef(allUsersRepo, RefNames.refsGroups(uuid3)); + } + + EasyMock.verify(cache, indexer); + } + + private Ref createRef(String ref) throws IOException { + return createRef(repo, ref); + } + + private Ref createRef(Repository repo, String ref) throws IOException { + try (ObjectInserter oi = repo.newObjectInserter(); + RevWalk rw = new RevWalk(repo)) { + ObjectId emptyCommit = createCommit(repo); + RefUpdate updateRef = repo.updateRef(ref); + updateRef.setExpectedOldObjectId(ObjectId.zeroId()); + updateRef.setNewObjectId(emptyCommit); + assertThat(updateRef.update(rw)).isEqualTo(RefUpdate.Result.NEW); + return repo.exactRef(ref); + } + } + + private Ref updateRef(Ref ref) throws IOException { + return updateRef(repo, ref); + } + + private Ref updateRef(Repository repo, Ref ref) throws IOException { + try (ObjectInserter oi = repo.newObjectInserter(); + RevWalk rw = new RevWalk(repo)) { + ObjectId emptyCommit = createCommit(repo); + RefUpdate updateRef = repo.updateRef(ref.getName()); + updateRef.setExpectedOldObjectId(ref.getObjectId()); + updateRef.setNewObjectId(emptyCommit); + updateRef.setForceUpdate(true); + assertThat(updateRef.update(rw)).isEqualTo(RefUpdate.Result.FORCED); + Ref updatedRef = repo.exactRef(ref.getName()); + assertThat(updatedRef.getObjectId()).isNotEqualTo(ref.getObjectId()); + return updatedRef; + } + } + + private void assertRef(Ref ref) throws IOException { + assertRef(repo, ref); + } + + private void assertRef(Repository repo, Ref ref) throws IOException { + assertThat(repo.exactRef(ref.getName()).getObjectId()).isEqualTo(ref.getObjectId()); + } + + private void assertDeletedRef(Ref ref) throws IOException { + assertDeletedRef(repo, ref); + } + + private void assertDeletedRef(Repository repo, Ref ref) throws IOException { + assertThat(repo.exactRef(ref.getName())).isNull(); + } + + private ObjectId createCommit(Repository repo) throws IOException { + try (ObjectInserter oi = repo.newObjectInserter()) { + PersonIdent ident = + new PersonIdent(new PersonIdent("Foo Bar", "foo.bar@baz.com"), TimeUtil.nowTs()); + CommitBuilder cb = new CommitBuilder(); + cb.setTreeId(oi.insert(Constants.OBJ_TREE, new byte[] {})); + cb.setCommitter(ident); + cb.setAuthor(ident); + cb.setMessage("Test commit"); + + ObjectId commit = oi.insert(cb); + oi.flush(); + return commit; + } + } + + private ProjectResetter.Builder builder() { + return builder(null, null, null, null, null, null, null); + } + + private ProjectResetter.Builder builder( + @Nullable AccountCreator accountCreator, + @Nullable AccountCache accountCache, + @Nullable AccountIndexer accountIndexer, + @Nullable GroupCache groupCache, + @Nullable GroupIncludeCache groupIncludeCache, + @Nullable GroupIndexer groupIndexer, + @Nullable ProjectCache projectCache) { + return new ProjectResetter.Builder( + repoManager, + new AllUsersName(AllUsersNameProvider.DEFAULT), + accountCreator, + accountCache, + accountIndexer, + groupCache, + groupIncludeCache, + groupIndexer, + projectCache); + } +} |