| // Copyright (C) 2016 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.query.account; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static java.util.stream.Collectors.toList; |
| import static org.junit.Assert.fail; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.Iterables; |
| import com.google.gerrit.common.data.Permission; |
| import com.google.gerrit.extensions.api.GerritApi; |
| import com.google.gerrit.extensions.api.access.AccessSectionInfo; |
| import com.google.gerrit.extensions.api.access.PermissionInfo; |
| import com.google.gerrit.extensions.api.access.PermissionRuleInfo; |
| import com.google.gerrit.extensions.api.access.ProjectAccessInput; |
| import com.google.gerrit.extensions.api.accounts.Accounts.QueryRequest; |
| import com.google.gerrit.extensions.api.groups.GroupInput; |
| import com.google.gerrit.extensions.api.projects.ProjectInput; |
| import com.google.gerrit.extensions.client.ListAccountsOption; |
| import com.google.gerrit.extensions.client.ProjectWatchInfo; |
| import com.google.gerrit.extensions.common.AccountInfo; |
| import com.google.gerrit.extensions.common.ChangeInfo; |
| import com.google.gerrit.extensions.common.ChangeInput; |
| import com.google.gerrit.extensions.common.GroupInfo; |
| import com.google.gerrit.extensions.restapi.ResourceNotFoundException; |
| import com.google.gerrit.extensions.restapi.RestApiException; |
| import com.google.gerrit.lifecycle.LifecycleManager; |
| import com.google.gerrit.reviewdb.client.Account; |
| import com.google.gerrit.reviewdb.client.Project; |
| import com.google.gerrit.reviewdb.server.ReviewDb; |
| import com.google.gerrit.server.AnonymousUser; |
| import com.google.gerrit.server.CurrentUser; |
| import com.google.gerrit.server.IdentifiedUser; |
| import com.google.gerrit.server.account.AccountCache; |
| import com.google.gerrit.server.account.AccountManager; |
| import com.google.gerrit.server.account.AccountState; |
| import com.google.gerrit.server.account.AuthRequest; |
| import com.google.gerrit.server.config.AllProjectsName; |
| import com.google.gerrit.server.index.account.AccountIndex; |
| import com.google.gerrit.server.index.account.AccountIndexCollection; |
| import com.google.gerrit.server.schema.SchemaCreator; |
| import com.google.gerrit.server.util.ManualRequestContext; |
| import com.google.gerrit.server.util.OneOffRequestContext; |
| import com.google.gerrit.server.util.RequestContext; |
| import com.google.gerrit.server.util.ThreadLocalRequestContext; |
| import com.google.gerrit.testutil.ConfigSuite; |
| import com.google.gerrit.testutil.GerritServerTests; |
| import com.google.gerrit.testutil.InMemoryDatabase; |
| import com.google.inject.Inject; |
| import com.google.inject.Injector; |
| import com.google.inject.Provider; |
| import com.google.inject.util.Providers; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import org.eclipse.jgit.lib.Config; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Ignore; |
| import org.junit.Test; |
| |
| @Ignore |
| public abstract class AbstractQueryAccountsTest extends GerritServerTests { |
| @ConfigSuite.Default |
| public static Config defaultConfig() { |
| Config cfg = new Config(); |
| cfg.setInt("index", null, "maxPages", 10); |
| return cfg; |
| } |
| |
| @Inject protected AccountCache accountCache; |
| |
| @Inject protected AccountManager accountManager; |
| |
| @Inject protected GerritApi gApi; |
| |
| @Inject protected IdentifiedUser.GenericFactory userFactory; |
| |
| @Inject private Provider<AnonymousUser> anonymousUser; |
| |
| @Inject protected InMemoryDatabase schemaFactory; |
| |
| @Inject protected SchemaCreator schemaCreator; |
| |
| @Inject protected ThreadLocalRequestContext requestContext; |
| |
| @Inject protected OneOffRequestContext oneOffRequestContext; |
| |
| @Inject protected InternalAccountQuery internalAccountQuery; |
| |
| @Inject protected AllProjectsName allProjects; |
| |
| @Inject protected AccountIndexCollection accountIndexes; |
| |
| protected Injector injector; |
| protected LifecycleManager lifecycle; |
| protected ReviewDb db; |
| protected AccountInfo currentUserInfo; |
| protected CurrentUser user; |
| |
| protected abstract Injector createInjector(); |
| |
| @Before |
| public void setUpInjector() throws Exception { |
| lifecycle = new LifecycleManager(); |
| injector = createInjector(); |
| lifecycle.add(injector); |
| injector.injectMembers(this); |
| lifecycle.start(); |
| initAfterLifecycleStart(); |
| db = schemaFactory.open(); |
| schemaCreator.create(db); |
| |
| Account.Id userId = createAccount("user", "User", "user@example.com", true); |
| user = userFactory.create(userId); |
| requestContext.setContext(newRequestContext(userId)); |
| currentUserInfo = gApi.accounts().id(userId.get()).get(); |
| } |
| |
| @After |
| public void cleanUp() { |
| lifecycle.stop(); |
| db.close(); |
| } |
| |
| protected void initAfterLifecycleStart() throws Exception {} |
| |
| protected RequestContext newRequestContext(Account.Id requestUserId) { |
| final CurrentUser requestUser = userFactory.create(requestUserId); |
| return new RequestContext() { |
| @Override |
| public CurrentUser getUser() { |
| return requestUser; |
| } |
| |
| @Override |
| public Provider<ReviewDb> getReviewDbProvider() { |
| return Providers.of(db); |
| } |
| }; |
| } |
| |
| protected void setAnonymous() { |
| requestContext.setContext( |
| new RequestContext() { |
| @Override |
| public CurrentUser getUser() { |
| return anonymousUser.get(); |
| } |
| |
| @Override |
| public Provider<ReviewDb> getReviewDbProvider() { |
| return Providers.of(db); |
| } |
| }); |
| } |
| |
| @After |
| public void tearDownInjector() { |
| if (lifecycle != null) { |
| lifecycle.stop(); |
| } |
| requestContext.setContext(null); |
| if (db != null) { |
| db.close(); |
| } |
| InMemoryDatabase.drop(schemaFactory); |
| } |
| |
| @Test |
| public void byId() throws Exception { |
| AccountInfo user = newAccount("user"); |
| |
| assertQuery("9999999"); |
| assertQuery(currentUserInfo._accountId, currentUserInfo); |
| assertQuery(user._accountId, user); |
| } |
| |
| @Test |
| public void bySelf() throws Exception { |
| assertQuery("self", currentUserInfo); |
| } |
| |
| @Test |
| public void byEmail() throws Exception { |
| AccountInfo user1 = newAccountWithEmail("user1", name("user1@example.com")); |
| |
| String domain = name("test.com"); |
| AccountInfo user2 = newAccountWithEmail("user2", "user2@" + domain); |
| AccountInfo user3 = newAccountWithEmail("user3", "user3@" + domain); |
| |
| String prefix = name("prefix"); |
| AccountInfo user4 = newAccountWithEmail("user4", prefix + "user4@example.com"); |
| |
| AccountInfo user5 = newAccountWithEmail("user5", name("user5MixedCase@example.com")); |
| |
| assertQuery("notexisting@test.com"); |
| |
| assertQuery(currentUserInfo.email, currentUserInfo); |
| assertQuery("email:" + currentUserInfo.email, currentUserInfo); |
| |
| assertQuery(user1.email, user1); |
| assertQuery("email:" + user1.email, user1); |
| |
| assertQuery(domain, user2, user3); |
| |
| assertQuery("email:" + prefix, user4); |
| |
| assertQuery(user5.email, user5); |
| assertQuery("email:" + user5.email, user5); |
| assertQuery("email:" + user5.email.toUpperCase(), user5); |
| } |
| |
| @Test |
| public void byUsername() throws Exception { |
| AccountInfo user1 = newAccount("myuser"); |
| |
| assertQuery("notexisting"); |
| assertQuery("Not Existing"); |
| |
| assertQuery(user1.username, user1); |
| assertQuery("username:" + user1.username, user1); |
| assertQuery("username:" + user1.username.toUpperCase(), user1); |
| } |
| |
| @Test |
| public void isActive() throws Exception { |
| String domain = name("test.com"); |
| AccountInfo user1 = newAccountWithEmail("user1", "user1@" + domain); |
| AccountInfo user2 = newAccountWithEmail("user2", "user2@" + domain); |
| AccountInfo user3 = newAccount("user3", "user3@" + domain, false); |
| AccountInfo user4 = newAccount("user4", "user4@" + domain, false); |
| |
| // by default only active accounts are returned |
| assertQuery(domain, user1, user2); |
| assertQuery("name:" + domain, user1, user2); |
| |
| assertQuery("is:active name:" + domain, user1, user2); |
| |
| assertQuery("is:inactive name:" + domain, user3, user4); |
| } |
| |
| @Test |
| public void byName() throws Exception { |
| AccountInfo user1 = newAccountWithFullName("jdoe", "John Doe"); |
| AccountInfo user2 = newAccountWithFullName("jroe", "Jane Roe"); |
| AccountInfo user3 = newAccountWithFullName("user3", "Mr Selfish"); |
| |
| assertQuery("notexisting"); |
| assertQuery("Not Existing"); |
| |
| assertQuery(quote(user1.name), user1); |
| assertQuery("name:" + quote(user1.name), user1); |
| assertQuery("John", user1); |
| assertQuery("john", user1); |
| assertQuery("Doe", user1); |
| assertQuery("doe", user1); |
| assertQuery("DOE", user1); |
| assertQuery("Jo Do", user1); |
| assertQuery("jo do", user1); |
| assertQuery("self", currentUserInfo, user3); |
| assertQuery("name:John", user1); |
| assertQuery("name:john", user1); |
| assertQuery("name:Doe", user1); |
| assertQuery("name:doe", user1); |
| assertQuery("name:DOE", user1); |
| assertQuery("name:self", user3); |
| |
| assertQuery(quote(user2.name), user2); |
| assertQuery("name:" + quote(user2.name), user2); |
| } |
| |
| @Test |
| public void byCansee() throws Exception { |
| String domain = name("test.com"); |
| AccountInfo user1 = newAccountWithEmail("account1", "account1@" + domain); |
| AccountInfo user2 = newAccountWithEmail("account2", "account2@" + domain); |
| AccountInfo user3 = newAccountWithEmail("account3", "account3@" + domain); |
| |
| Project.NameKey p = createProject(name("p")); |
| ChangeInfo c = createChange(p); |
| assertQuery("name:" + domain + " cansee:" + c.changeId, user1, user2, user3); |
| |
| GroupInfo group = createGroup(name("group"), user1, user2); |
| blockRead(p, group); |
| assertQuery("name:" + domain + " cansee:" + c.changeId, user3); |
| } |
| |
| @Test |
| public void byWatchedProject() throws Exception { |
| Project.NameKey p = createProject(name("p")); |
| Project.NameKey p2 = createProject(name("p2")); |
| AccountInfo user1 = newAccountWithFullName("jdoe", "John Doe"); |
| AccountInfo user2 = newAccountWithFullName("jroe", "Jane Roe"); |
| AccountInfo user3 = newAccountWithFullName("user3", "Mr Selfish"); |
| |
| assertThat(internalAccountQuery.byWatchedProject(p)).isEmpty(); |
| |
| watch(user1, p, null); |
| assertAccounts(internalAccountQuery.byWatchedProject(p), user1); |
| |
| watch(user2, p, "keyword"); |
| assertAccounts(internalAccountQuery.byWatchedProject(p), user1, user2); |
| |
| watch(user3, p2, "keyword"); |
| watch(user3, allProjects, "keyword"); |
| assertAccounts(internalAccountQuery.byWatchedProject(p), user1, user2); |
| assertAccounts(internalAccountQuery.byWatchedProject(p2), user3); |
| assertAccounts(internalAccountQuery.byWatchedProject(allProjects), user3); |
| } |
| |
| @Test |
| public void byDeletedAccount() throws Exception { |
| AccountInfo user = newAccountWithFullName("jdoe", "John Doe"); |
| Account.Id userId = Account.Id.parse(user._accountId.toString()); |
| assertQuery("John", user); |
| |
| for (AccountIndex index : accountIndexes.getWriteIndexes()) { |
| index.delete(userId); |
| } |
| assertQuery("John"); |
| } |
| |
| @Test |
| public void withLimit() throws Exception { |
| String domain = name("test.com"); |
| AccountInfo user1 = newAccountWithEmail("user1", "user1@" + domain); |
| AccountInfo user2 = newAccountWithEmail("user2", "user2@" + domain); |
| AccountInfo user3 = newAccountWithEmail("user3", "user3@" + domain); |
| |
| List<AccountInfo> result = assertQuery(domain, user1, user2, user3); |
| assertThat(Iterables.getLast(result)._moreAccounts).isNull(); |
| |
| result = assertQuery(newQuery(domain).withLimit(2), result.subList(0, 2)); |
| assertThat(Iterables.getLast(result)._moreAccounts).isTrue(); |
| } |
| |
| @Test |
| public void withStart() throws Exception { |
| String domain = name("test.com"); |
| AccountInfo user1 = newAccountWithEmail("user1", "user1@" + domain); |
| AccountInfo user2 = newAccountWithEmail("user2", "user2@" + domain); |
| AccountInfo user3 = newAccountWithEmail("user3", "user3@" + domain); |
| |
| List<AccountInfo> result = assertQuery(domain, user1, user2, user3); |
| assertQuery(newQuery(domain).withStart(1), result.subList(1, 3)); |
| } |
| |
| @Test |
| public void withDetails() throws Exception { |
| AccountInfo user1 = newAccount("myuser", "My User", "my.user@example.com", true); |
| |
| List<AccountInfo> result = assertQuery(user1.username, user1); |
| AccountInfo ai = result.get(0); |
| assertThat(ai._accountId).isEqualTo(user1._accountId); |
| assertThat(ai.name).isNull(); |
| assertThat(ai.username).isNull(); |
| assertThat(ai.email).isNull(); |
| assertThat(ai.avatars).isNull(); |
| |
| result = assertQuery(newQuery(user1.username).withOption(ListAccountsOption.DETAILS), user1); |
| ai = result.get(0); |
| assertThat(ai._accountId).isEqualTo(user1._accountId); |
| assertThat(ai.name).isEqualTo(user1.name); |
| assertThat(ai.username).isEqualTo(user1.username); |
| assertThat(ai.email).isEqualTo(user1.email); |
| assertThat(ai.avatars).isNull(); |
| } |
| |
| @Test |
| public void withSecondaryEmails() throws Exception { |
| AccountInfo user1 = newAccount("myuser", "My User", "my.user@example.com", true); |
| String[] secondaryEmails = new String[] {"bar@example.com", "foo@example.com"}; |
| addEmails(user1, secondaryEmails); |
| |
| List<AccountInfo> result = assertQuery(user1.username, user1); |
| assertThat(result.get(0).secondaryEmails).isNull(); |
| |
| result = assertQuery(newQuery(user1.username).withOption(ListAccountsOption.DETAILS), user1); |
| assertThat(result.get(0).secondaryEmails).isNull(); |
| |
| result = assertQuery(newQuery(user1.username).withOption(ListAccountsOption.ALL_EMAILS), user1); |
| assertThat(result.get(0).secondaryEmails) |
| .containsExactlyElementsIn(Arrays.asList(secondaryEmails)) |
| .inOrder(); |
| |
| result = |
| assertQuery( |
| newQuery(user1.username) |
| .withOptions(ListAccountsOption.DETAILS, ListAccountsOption.ALL_EMAILS), |
| user1); |
| assertThat(result.get(0).secondaryEmails) |
| .containsExactlyElementsIn(Arrays.asList(secondaryEmails)) |
| .inOrder(); |
| } |
| |
| @Test |
| public void asAnonymous() throws Exception { |
| AccountInfo user1 = newAccount("user1"); |
| |
| setAnonymous(); |
| assertQuery("9999999"); |
| assertQuery("self"); |
| assertQuery("username:" + user1.username, user1); |
| } |
| |
| // reindex permissions are tested by {@link AccountIT#reindexPermissions} |
| @Test |
| public void reindex() throws Exception { |
| AccountInfo user1 = newAccountWithFullName("tester", "Test Usre"); |
| |
| // update account in the database so that account index is stale |
| String newName = "Test User"; |
| Account account = db.accounts().get(new Account.Id(user1._accountId)); |
| account.setFullName(newName); |
| db.accounts().update(Collections.singleton(account)); |
| |
| assertQuery("name:" + quote(user1.name), user1); |
| assertQuery("name:" + quote(newName)); |
| |
| gApi.accounts().id(user1.username).index(); |
| assertQuery("name:" + quote(user1.name)); |
| assertQuery("name:" + quote(newName), user1); |
| } |
| |
| protected AccountInfo newAccount(String username) throws Exception { |
| return newAccountWithEmail(username, null); |
| } |
| |
| protected AccountInfo newAccountWithEmail(String username, String email) throws Exception { |
| return newAccount(username, email, true); |
| } |
| |
| protected AccountInfo newAccountWithFullName(String username, String fullName) throws Exception { |
| return newAccount(username, fullName, null, true); |
| } |
| |
| protected AccountInfo newAccount(String username, String email, boolean active) throws Exception { |
| return newAccount(username, null, email, active); |
| } |
| |
| protected AccountInfo newAccount(String username, String fullName, String email, boolean active) |
| throws Exception { |
| String uniqueName = name(username); |
| |
| try { |
| gApi.accounts().id(uniqueName).get(); |
| fail("user " + uniqueName + " already exists"); |
| } catch (ResourceNotFoundException e) { |
| // expected: user does not exist yet |
| } |
| |
| Account.Id id = createAccount(uniqueName, fullName, email, active); |
| return gApi.accounts().id(id.get()).get(); |
| } |
| |
| protected Project.NameKey createProject(String name) throws RestApiException { |
| ProjectInput in = new ProjectInput(); |
| in.name = name; |
| in.createEmptyCommit = true; |
| gApi.projects().create(in); |
| return new Project.NameKey(name); |
| } |
| |
| protected void blockRead(Project.NameKey project, GroupInfo group) throws RestApiException { |
| ProjectAccessInput in = new ProjectAccessInput(); |
| in.add = new HashMap<>(); |
| |
| AccessSectionInfo a = new AccessSectionInfo(); |
| PermissionInfo p = new PermissionInfo(null, null); |
| p.rules = |
| ImmutableMap.of(group.id, new PermissionRuleInfo(PermissionRuleInfo.Action.BLOCK, false)); |
| a.permissions = ImmutableMap.of(Permission.READ, p); |
| in.add = ImmutableMap.of("refs/*", a); |
| |
| gApi.projects().name(project.get()).access(in); |
| } |
| |
| protected ChangeInfo createChange(Project.NameKey project) throws RestApiException { |
| ChangeInput in = new ChangeInput(); |
| in.subject = "A change"; |
| in.project = project.get(); |
| in.branch = "master"; |
| return gApi.changes().create(in).get(); |
| } |
| |
| protected GroupInfo createGroup(String name, AccountInfo... members) throws RestApiException { |
| GroupInput in = new GroupInput(); |
| in.name = name; |
| in.members = |
| Arrays.asList(members).stream().map(a -> String.valueOf(a._accountId)).collect(toList()); |
| return gApi.groups().create(in).get(); |
| } |
| |
| protected void watch(AccountInfo account, Project.NameKey project, String filter) |
| throws RestApiException { |
| List<ProjectWatchInfo> projectsToWatch = new ArrayList<>(); |
| ProjectWatchInfo pwi = new ProjectWatchInfo(); |
| pwi.project = project.get(); |
| pwi.filter = filter; |
| pwi.notifyAbandonedChanges = true; |
| pwi.notifyNewChanges = true; |
| pwi.notifyAllComments = true; |
| projectsToWatch.add(pwi); |
| gApi.accounts().id(account._accountId).setWatchedProjects(projectsToWatch); |
| } |
| |
| protected String quote(String s) { |
| return "\"" + s + "\""; |
| } |
| |
| protected String name(String name) { |
| if (name == null) { |
| return null; |
| } |
| String suffix = testName.getMethodName().toLowerCase(); |
| if (name.contains("@")) { |
| return name + "." + suffix; |
| } |
| return name + "_" + suffix; |
| } |
| |
| private Account.Id createAccount(String username, String fullName, String email, boolean active) |
| throws Exception { |
| try (ManualRequestContext ctx = oneOffRequestContext.open()) { |
| Account.Id id = accountManager.authenticate(AuthRequest.forUser(username)).getAccountId(); |
| if (email != null) { |
| accountManager.link(id, AuthRequest.forEmail(email)); |
| } |
| Account a = db.accounts().get(id); |
| a.setFullName(fullName); |
| a.setPreferredEmail(email); |
| a.setActive(active); |
| db.accounts().update(ImmutableList.of(a)); |
| accountCache.evict(id); |
| return id; |
| } |
| } |
| |
| private void addEmails(AccountInfo account, String... emails) throws Exception { |
| Account.Id id = new Account.Id(account._accountId); |
| for (String email : emails) { |
| accountManager.link(id, AuthRequest.forEmail(email)); |
| } |
| accountCache.evict(id); |
| } |
| |
| protected QueryRequest newQuery(Object query) throws RestApiException { |
| return gApi.accounts().query(query.toString()); |
| } |
| |
| protected List<AccountInfo> assertQuery(Object query, AccountInfo... accounts) throws Exception { |
| return assertQuery(newQuery(query), accounts); |
| } |
| |
| protected List<AccountInfo> assertQuery(QueryRequest query, AccountInfo... accounts) |
| throws Exception { |
| return assertQuery(query, Arrays.asList(accounts)); |
| } |
| |
| protected List<AccountInfo> assertQuery(QueryRequest query, List<AccountInfo> accounts) |
| throws Exception { |
| List<AccountInfo> result = query.get(); |
| Iterable<Integer> ids = ids(result); |
| assertThat(ids) |
| .named(format(query, result, accounts)) |
| .containsExactlyElementsIn(ids(accounts)) |
| .inOrder(); |
| return result; |
| } |
| |
| protected void assertAccounts(List<AccountState> accounts, AccountInfo... expectedAccounts) { |
| assertThat(accounts.stream().map(a -> a.getAccount().getId().get()).collect(toList())) |
| .containsExactlyElementsIn( |
| Arrays.asList(expectedAccounts).stream().map(a -> a._accountId).collect(toList())); |
| } |
| |
| private String format( |
| QueryRequest query, List<AccountInfo> actualIds, List<AccountInfo> expectedAccounts) { |
| StringBuilder b = new StringBuilder(); |
| b.append("query '").append(query.getQuery()).append("' with expected accounts "); |
| b.append(format(expectedAccounts)); |
| b.append(" and result "); |
| b.append(format(actualIds)); |
| return b.toString(); |
| } |
| |
| private String format(Iterable<AccountInfo> accounts) { |
| StringBuilder b = new StringBuilder(); |
| b.append("["); |
| Iterator<AccountInfo> it = accounts.iterator(); |
| while (it.hasNext()) { |
| AccountInfo a = it.next(); |
| b.append("{") |
| .append(a._accountId) |
| .append(", ") |
| .append("name=") |
| .append(a.name) |
| .append(", ") |
| .append("email=") |
| .append(a.email) |
| .append(", ") |
| .append("username=") |
| .append(a.username) |
| .append("}"); |
| if (it.hasNext()) { |
| b.append(", "); |
| } |
| } |
| b.append("]"); |
| return b.toString(); |
| } |
| |
| protected static Iterable<Integer> ids(AccountInfo... accounts) { |
| return ids(Arrays.asList(accounts)); |
| } |
| |
| protected static Iterable<Integer> ids(List<AccountInfo> accounts) { |
| return accounts.stream().map(a -> a._accountId).collect(toList()); |
| } |
| } |