/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.ambari.server.security.ldap;

import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.easymock.EasyMock.anyBoolean;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.anyString;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMockBuilder;
import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.naming.directory.SearchControls;

import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.configuration.LdapUsernameCollisionHandlingBehavior;
import org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration;
import org.apache.ambari.server.orm.entities.GroupEntity;
import org.apache.ambari.server.orm.entities.MemberEntity;
import org.apache.ambari.server.orm.entities.PrincipalEntity;
import org.apache.ambari.server.orm.entities.UserAuthenticationEntity;
import org.apache.ambari.server.orm.entities.UserEntity;
import org.apache.ambari.server.security.authorization.AmbariLdapUtils;
import org.apache.ambari.server.security.authorization.Group;
import org.apache.ambari.server.security.authorization.GroupType;
import org.apache.ambari.server.security.authorization.LdapServerProperties;
import org.apache.ambari.server.security.authorization.User;
import org.apache.ambari.server.security.authorization.UserAuthenticationType;
import org.apache.ambari.server.security.authorization.UserName;
import org.apache.ambari.server.security.authorization.Users;
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.ldap.control.PagedResultsCookie;
import org.springframework.ldap.control.PagedResultsDirContextProcessor;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.filter.Filter;
import org.springframework.ldap.support.LdapUtils;

import com.google.common.collect.Sets;
import com.google.inject.Provider;

@RunWith(PowerMockRunner.class)
@PrepareForTest(AmbariLdapUtils.class)
@PowerMockIgnore({"org.apache.logging.log4j.*","org.slf4j.*"})
public class AmbariLdapDataPopulatorTest {
  public static class AmbariLdapDataPopulatorTestInstance extends TestAmbariLdapDataPopulator {
    public AmbariLdapDataPopulatorTestInstance(Provider<AmbariLdapConfiguration> configurationProvider, Users users) {
      super(configurationProvider, users);
    }

    @Override
    protected LdapTemplate loadLdapTemplate() {
      return ldapTemplate;
    }
  }

  public static class TestAmbariLdapDataPopulator extends AmbariLdapDataPopulator {

    protected LdapTemplate ldapTemplate;
    private LdapContextSource ldapContextSource;
    private PagedResultsDirContextProcessor processor;

    public TestAmbariLdapDataPopulator(Provider<AmbariLdapConfiguration> configurationProvider, Users users) {
      super(configurationProvider, users);
    }

    @Override
    protected LdapContextSource createLdapContextSource() {
      return ldapContextSource;
    }

    @Override
    protected LdapTemplate createLdapTemplate(LdapContextSource ldapContextSource) {
      this.ldapContextSource = ldapContextSource;
      return ldapTemplate;
    }

    @Override
    protected PagedResultsDirContextProcessor createPagingProcessor() {
      return processor;
    }

    public void setLdapContextSource(LdapContextSource ldapContextSource) {
      this.ldapContextSource = ldapContextSource;
    }

    public void setProcessor(PagedResultsDirContextProcessor processor) {
      this.processor = processor;
    }

    public void setLdapTemplate(LdapTemplate ldapTemplate) {
      this.ldapTemplate = ldapTemplate;
    }

    public LdapServerProperties getLdapServerProperties() {
      return this.ldapServerProperties;
    }

    public void setLdapServerProperties(LdapServerProperties ldapServerProperties) {
      this.ldapServerProperties = ldapServerProperties;
    }

    public LdapContextSource getLdapContextSource() {
      return ldapContextSource;
    }
  }

  @SuppressWarnings("unchecked")
  @Test
  public void testIsLdapEnabled_badConfiguration() {
    final Provider<AmbariLdapConfiguration> configurationProvider = createNiceMock(Provider.class);
    final AmbariLdapConfiguration configuration = createNiceMock(AmbariLdapConfiguration.class);
    final Users users = createNiceMock(Users.class);

    LdapTemplate ldapTemplate = createNiceMock(LdapTemplate.class);
    LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class);
    expect(configurationProvider.get()).andReturn(configuration).anyTimes();
    expect(configuration.ldapEnabled()).andReturn(true);
    expect(ldapTemplate.search(EasyMock.<String>anyObject(), EasyMock.anyObject(), EasyMock.<AttributesMapper>anyObject())).andThrow(new NullPointerException()).once();
    replay(ldapTemplate, configurationProvider, configuration, ldapServerProperties);

    final AmbariLdapDataPopulatorTestInstance populator = new AmbariLdapDataPopulatorTestInstance(configurationProvider, users);
    populator.setLdapTemplate(ldapTemplate);
    populator.setLdapServerProperties(ldapServerProperties);

    assertFalse(populator.isLdapEnabled());
    verify(populator.loadLdapTemplate(), configurationProvider, configuration);
  }

  @SuppressWarnings("unchecked")
  @Test
  public void testReferralMethod() {
    final Provider<AmbariLdapConfiguration> configurationProvider = createNiceMock(Provider.class);
    final AmbariLdapConfiguration configuration = createNiceMock(AmbariLdapConfiguration.class);
    final Users users = createNiceMock(Users.class);
    LdapContextSource ldapContextSource = createNiceMock(LdapContextSource.class);

    List<String> ldapUrls = Collections.singletonList("url");

    LdapTemplate ldapTemplate = createNiceMock(LdapTemplate.class);
    LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class);
    expect(configurationProvider.get()).andReturn(configuration).anyTimes();
    expect(configuration.getLdapServerProperties()).andReturn(ldapServerProperties).anyTimes();
    expect(ldapServerProperties.getLdapUrls()).andReturn(ldapUrls).anyTimes();
    expect(ldapServerProperties.getReferralMethod()).andReturn("follow");
    ldapContextSource.setReferral("follow");
    ldapTemplate.setIgnorePartialResultException(true);

    replay(ldapTemplate, configurationProvider, configuration, ldapServerProperties, ldapContextSource);

    final TestAmbariLdapDataPopulator populator = new TestAmbariLdapDataPopulator(configurationProvider, users);
    populator.setLdapContextSource(ldapContextSource);
    populator.setLdapTemplate(ldapTemplate);
    populator.setLdapServerProperties(ldapServerProperties);

    populator.loadLdapTemplate();

    verify(ldapTemplate, configurationProvider, configuration, ldapServerProperties, ldapContextSource);
  }

  @Test
  public void testIsLdapEnabled_reallyEnabled() {
    @SuppressWarnings("unchecked")
    final Provider<AmbariLdapConfiguration> configurationProvider = createNiceMock(Provider.class);
    final AmbariLdapConfiguration configuration = createNiceMock(AmbariLdapConfiguration.class);
    final Users users = createNiceMock(Users.class);

    LdapTemplate ldapTemplate = createNiceMock(LdapTemplate.class);
    LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class);
    expect(configurationProvider.get()).andReturn(configuration).anyTimes();
    expect(configuration.ldapEnabled()).andReturn(true);
    expect(ldapTemplate.search(EasyMock.<String>anyObject(), EasyMock.anyObject(), EasyMock.<AttributesMapper>anyObject())).andReturn(Collections.emptyList()).once();
    replay(ldapTemplate, configurationProvider, configuration);

    final AmbariLdapDataPopulatorTestInstance populator = new AmbariLdapDataPopulatorTestInstance(configurationProvider, users);
    populator.setLdapTemplate(ldapTemplate);
    populator.setLdapServerProperties(ldapServerProperties);

    assertTrue(populator.isLdapEnabled());
    verify(populator.loadLdapTemplate(), configurationProvider, configuration);
  }

  @SuppressWarnings("unchecked")
  @Test
  public void testIsLdapEnabled_reallyDisabled() {
    final Provider<AmbariLdapConfiguration> configurationProvider = createNiceMock(Provider.class);
    final AmbariLdapConfiguration configuration = createNiceMock(AmbariLdapConfiguration.class);
    final Users users = createNiceMock(Users.class);

    LdapTemplate ldapTemplate = createNiceMock(LdapTemplate.class);
    LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class);
    expect(configurationProvider.get()).andReturn(configuration).anyTimes();
    expect(configuration.ldapEnabled()).andReturn(false);
    replay(ldapTemplate, ldapServerProperties, configurationProvider, configuration);

    final AmbariLdapDataPopulatorTestInstance populator = new AmbariLdapDataPopulatorTestInstance(configurationProvider, users);
    populator.setLdapTemplate(ldapTemplate);
    populator.setLdapServerProperties(ldapServerProperties);

    assertFalse(populator.isLdapEnabled());
    verify(populator.loadLdapTemplate(), populator.getLdapServerProperties(), configurationProvider, configuration);
  }

  private <T> Set<T> createSet(T... elements) {
    return new HashSet<>(Arrays.asList(elements));
  }

  @SuppressWarnings("unchecked")
  @Test
  public void synchronizeExistingLdapGroups() throws Exception {

    Group group1 = createNiceMock(Group.class);
    Group group2 = createNiceMock(Group.class);
    Group group3 = createNiceMock(Group.class);
    Group group4 = createNiceMock(Group.class);
    Group group5 = createNiceMock(Group.class);
    expect(group1.getGroupName()).andReturn("group1").anyTimes();
    expect(group2.getGroupName()).andReturn("group2").anyTimes();
    expect(group3.getGroupName()).andReturn("group3").anyTimes();
    expect(group4.getGroupName()).andReturn("group4").anyTimes();
    expect(group5.getGroupName()).andReturn("group5").anyTimes();
    expect(group1.isLdapGroup()).andReturn(false).anyTimes();
    expect(group2.isLdapGroup()).andReturn(true).anyTimes();
    expect(group3.isLdapGroup()).andReturn(false).anyTimes();
    expect(group4.isLdapGroup()).andReturn(true).anyTimes();
    expect(group5.isLdapGroup()).andReturn(true).anyTimes();

    List<Group> groupList = Arrays.asList(group1, group2, group3, group4, group5);

    final Provider<AmbariLdapConfiguration> configurationProvider = createNiceMock(Provider.class);
    AmbariLdapConfiguration configuration = createNiceMock(AmbariLdapConfiguration.class);
    Users users = createNiceMock(Users.class);
    LdapTemplate ldapTemplate = createNiceMock(LdapTemplate.class);
    LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class);
    expect(configurationProvider.get()).andReturn(configuration).anyTimes();
    expect(users.getAllGroups()).andReturn(groupList);
    expect(users.getAllUsers()).andReturn(Collections.emptyList());

    replay(ldapTemplate, ldapServerProperties, users, configurationProvider, configuration);
    replay(group1, group2, group3, group4, group5);

    AmbariLdapDataPopulatorTestInstance populator = createMockBuilder(AmbariLdapDataPopulatorTestInstance.class)
        .addMockedMethod("getLdapGroups")
        .addMockedMethod("refreshGroupMembers")
        .withConstructor(configurationProvider, users)
        .createNiceMock();

    expect(populator.getLdapGroups("group2")).andReturn(Collections.emptySet());
    LdapGroupDto externalGroup1 = createNiceMock(LdapGroupDto.class);
    LdapBatchDto batchInfo = new LdapBatchDto();
    populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup1), EasyMock.anyObject(), EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
    expectLastCall();
    expect(populator.getLdapGroups("group4")).andReturn(Collections.singleton(externalGroup1));
    expect(populator.getLdapGroups("group5")).andReturn(Collections.emptySet());
    replay(populator);

    populator.setLdapTemplate(ldapTemplate);
    populator.setLdapServerProperties(ldapServerProperties);

    LdapBatchDto result = populator.synchronizeExistingLdapGroups(batchInfo, false);

    verifyGroupsInSet(result.getGroupsToBeRemoved(), Sets.newHashSet("group2", "group5"));
    assertTrue(result.getGroupsToBecomeLdap().isEmpty());
    assertTrue(result.getGroupsToBeCreated().isEmpty());
    assertTrue(result.getUsersToBeCreated().isEmpty());
    assertTrue(result.getMembershipToAdd().isEmpty());
    assertTrue(result.getMembershipToRemove().isEmpty());
    assertTrue(result.getUsersToBecomeLdap().isEmpty());
    assertTrue(result.getUsersToBeRemoved().isEmpty());
    verify(populator.loadLdapTemplate(), populator);
  }

  @SuppressWarnings("unchecked")
  @Test
  public void testSynchronizeExistingLdapGroups_removeDuringIteration() throws Exception {
    // GIVEN
    Group group1 = createNiceMock(Group.class);
    expect(group1.getGroupId()).andReturn(1).anyTimes();
    expect(group1.getGroupName()).andReturn("group1").anyTimes();
    expect(group1.isLdapGroup()).andReturn(true).anyTimes();

    Group group2 = createNiceMock(Group.class);
    expect(group2.getGroupId()).andReturn(2).anyTimes();
    expect(group2.getGroupName()).andReturn("group2").anyTimes();
    expect(group2.isLdapGroup()).andReturn(true).anyTimes();

    final Provider<AmbariLdapConfiguration> configurationProvider = createNiceMock(Provider.class);
    AmbariLdapConfiguration configuration = createNiceMock(AmbariLdapConfiguration.class);
    expect(configurationProvider.get()).andReturn(configuration).anyTimes();
    Users users = createNiceMock(Users.class);
    expect(users.getAllGroups()).andReturn(Arrays.asList(group1, group2));
    expect(users.getAllUsers()).andReturn(Collections.emptyList());
    expect(configuration.getLdapServerProperties()).andReturn(new LdapServerProperties()).anyTimes();

    LdapGroupDto group1Dto = new LdapGroupDto();
    group1Dto.setGroupName("group1");
    group1Dto.setMemberAttributes(Sets.newHashSet("group2"));
    Set<LdapGroupDto> groupDtos1 = Sets.newHashSet();
    groupDtos1.add(group1Dto);

    LdapGroupDto group2Dto = new LdapGroupDto();
    group2Dto.setGroupName("group2");
    group2Dto.setMemberAttributes(Collections.emptySet());
    Set<LdapGroupDto> groupDtos2 = Sets.newHashSet();
    groupDtos2.add(group2Dto);

    LdapBatchDto batchInfo = new LdapBatchDto();
    replay(configurationProvider, configuration, users, group1, group2);
    AmbariLdapDataPopulator dataPopulator = createMockBuilder(AmbariLdapDataPopulatorTestInstance.class)
        .withConstructor(configurationProvider, users)
        .addMockedMethod("getLdapGroups")
        .addMockedMethod("getLdapUserByMemberAttr")
        .addMockedMethod("getLdapGroupByMemberAttr")
        .createNiceMock();

    expect(dataPopulator.getLdapUserByMemberAttr(anyString())).andReturn(null).anyTimes();
    expect(dataPopulator.getLdapGroupByMemberAttr("group2")).andReturn(group2Dto);
    expect(dataPopulator.getLdapGroups("group1")).andReturn(groupDtos1).anyTimes();
    expect(dataPopulator.getLdapGroups("group2")).andReturn(groupDtos2).anyTimes();

    replay(dataPopulator);
    // WHEN
    dataPopulator.synchronizeExistingLdapGroups(batchInfo, false);
    // THEN
    verify(dataPopulator, group1, group2);

  }

  @SuppressWarnings("unchecked")
  @Test
  public void testSynchronizeLdapGroups_allExist() throws Exception {

    Group group1 = createNiceMock(Group.class);
    Group group2 = createNiceMock(Group.class);
    Group group3 = createNiceMock(Group.class);
    Group group4 = createNiceMock(Group.class);
    expect(group1.getGroupName()).andReturn("group1").anyTimes();
    expect(group2.getGroupName()).andReturn("group2").anyTimes();
    expect(group3.getGroupName()).andReturn("group3").anyTimes();
    expect(group4.getGroupName()).andReturn("group4").anyTimes();
    expect(group1.isLdapGroup()).andReturn(false).anyTimes();
    expect(group2.isLdapGroup()).andReturn(true).anyTimes();
    expect(group3.isLdapGroup()).andReturn(true).anyTimes();
    expect(group4.isLdapGroup()).andReturn(false).anyTimes();

    List<Group> groupList = Arrays.asList(group1, group2, group3, group4);

    final Provider<AmbariLdapConfiguration> configurationProvider = createNiceMock(Provider.class);
    AmbariLdapConfiguration configuration = createNiceMock(AmbariLdapConfiguration.class);
    expect(configurationProvider.get()).andReturn(configuration).anyTimes();
    Users users = createNiceMock(Users.class);
    LdapTemplate ldapTemplate = createNiceMock(LdapTemplate.class);
    LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class);
    expect(users.getAllGroups()).andReturn(groupList);
    expect(users.getAllUsers()).andReturn(Collections.emptyList());

    replay(ldapTemplate, ldapServerProperties, users, configurationProvider, configuration);
    replay(group1, group2, group3, group4);

    AmbariLdapDataPopulatorTestInstance populator = createMockBuilder(AmbariLdapDataPopulatorTestInstance.class)
        .addMockedMethod("getLdapGroups")
        .addMockedMethod("refreshGroupMembers")
        .withConstructor(configurationProvider, users)
        .createNiceMock();

    LdapGroupDto externalGroup1 = createNiceMock(LdapGroupDto.class);
    LdapGroupDto externalGroup2 = createNiceMock(LdapGroupDto.class);
    LdapGroupDto externalGroup3 = createNiceMock(LdapGroupDto.class);
    LdapGroupDto externalGroup4 = createNiceMock(LdapGroupDto.class);
    expect(externalGroup1.getGroupName()).andReturn("group1").anyTimes();
    expect(externalGroup2.getGroupName()).andReturn("group2").anyTimes();
    expect(externalGroup3.getGroupName()).andReturn("xgroup1").anyTimes();
    expect(externalGroup4.getGroupName()).andReturn("xgroup2").anyTimes();
    replay(externalGroup1, externalGroup2, externalGroup3, externalGroup4);

    LdapBatchDto batchInfo = new LdapBatchDto();
    Set<LdapGroupDto> externalGroups = createSet(externalGroup3, externalGroup4);
    for (LdapGroupDto externalGroup : externalGroups) {
      populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup),
          EasyMock.anyObject(), EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
      expectLastCall();
    }
    populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup1),
        EasyMock.anyObject(), EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
    expectLastCall();
    populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup2), EasyMock.anyObject(),
        EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
    expectLastCall();
    expect(populator.getLdapGroups("x*")).andReturn(externalGroups);
    expect(populator.getLdapGroups("group1")).andReturn(Collections.singleton(externalGroup1));
    expect(populator.getLdapGroups("group2")).andReturn(Collections.singleton(externalGroup2));
    replay(populator);

    populator.setLdapTemplate(ldapTemplate);
    populator.setLdapServerProperties(ldapServerProperties);

    LdapBatchDto result = populator.synchronizeLdapGroups(createSet("x*", "group1", "group2"), batchInfo, false);

    verifyGroupsInSet(result.getGroupsToBecomeLdap(), Sets.newHashSet("group1"));
    verifyGroupsInSet(result.getGroupsToBeCreated(), Sets.newHashSet("xgroup1", "xgroup2"));
    assertTrue(result.getGroupsToBeRemoved().isEmpty());
    assertTrue(result.getUsersToBeCreated().isEmpty());
    assertTrue(result.getMembershipToAdd().isEmpty());
    assertTrue(result.getMembershipToRemove().isEmpty());
    assertTrue(result.getUsersToBecomeLdap().isEmpty());
    assertTrue(result.getUsersToBeRemoved().isEmpty());
    verifyGroupsInSet(result.getGroupsProcessedInternal(), Sets.newHashSet("group1", "group2"));
    verify(populator.loadLdapTemplate(), populator);
  }

  @SuppressWarnings("unchecked")
  @Test
  public void testSynchronizeLdapGroups_add() throws Exception {

    Group group1 = createNiceMock(Group.class);
    Group group2 = createNiceMock(Group.class);
    Group group3 = createNiceMock(Group.class);
    Group group4 = createNiceMock(Group.class);
    expect(group1.getGroupName()).andReturn("group1").anyTimes();
    expect(group2.getGroupName()).andReturn("group2").anyTimes();
    expect(group3.getGroupName()).andReturn("group3").anyTimes();
    expect(group4.getGroupName()).andReturn("group4").anyTimes();
    expect(group1.isLdapGroup()).andReturn(false).anyTimes();
    expect(group2.isLdapGroup()).andReturn(true).anyTimes();
    expect(group3.isLdapGroup()).andReturn(true).anyTimes();
    expect(group4.isLdapGroup()).andReturn(false).anyTimes();

    List<Group> groupList = Arrays.asList(group1, group2, group3, group4);

    final Provider<AmbariLdapConfiguration> configurationProvider = createNiceMock(Provider.class);
    AmbariLdapConfiguration configuration = createNiceMock(AmbariLdapConfiguration.class);
    expect(configurationProvider.get()).andReturn(configuration).anyTimes();
    Users users = createNiceMock(Users.class);
    LdapTemplate ldapTemplate = createNiceMock(LdapTemplate.class);
    LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class);
    expect(users.getAllGroups()).andReturn(groupList);
    expect(users.getAllUsers()).andReturn(Collections.emptyList());

    replay(ldapTemplate, ldapServerProperties, users, configurationProvider, configuration);
    replay(group1, group2, group3, group4);

    AmbariLdapDataPopulatorTestInstance populator = createMockBuilder(AmbariLdapDataPopulatorTestInstance.class)
        .addMockedMethod("getLdapGroups")
        .addMockedMethod("refreshGroupMembers")
        .withConstructor(configurationProvider, users)
        .createNiceMock();

    LdapGroupDto externalGroup1 = createNiceMock(LdapGroupDto.class);
    LdapGroupDto externalGroup2 = createNiceMock(LdapGroupDto.class);
    LdapGroupDto externalGroup3 = createNiceMock(LdapGroupDto.class);
    LdapGroupDto externalGroup4 = createNiceMock(LdapGroupDto.class);
    expect(externalGroup1.getGroupName()).andReturn("group1").anyTimes();
    expect(externalGroup2.getGroupName()).andReturn("group2").anyTimes();
    expect(externalGroup3.getGroupName()).andReturn("xgroup1").anyTimes();
    expect(externalGroup4.getGroupName()).andReturn("xgroup2").anyTimes();
    replay(externalGroup1, externalGroup2, externalGroup3, externalGroup4);


    LdapBatchDto batchInfo = new LdapBatchDto();
    Set<LdapGroupDto> externalGroups = createSet(externalGroup3, externalGroup4);
    for (LdapGroupDto externalGroup : externalGroups) {
      populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), EasyMock.anyObject(), EasyMock.anyObject(),
          EasyMock.anyObject(), anyBoolean(), eq(false));
      expectLastCall();
    }
    populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup2), EasyMock.anyObject(),
        EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
    expectLastCall();
    expect(populator.getLdapGroups("x*")).andReturn(externalGroups);
    expect(populator.getLdapGroups("group2")).andReturn(Collections.singleton(externalGroup2));
    replay(populator);

    populator.setLdapTemplate(ldapTemplate);
    populator.setLdapServerProperties(ldapServerProperties);

    LdapBatchDto result = populator.synchronizeLdapGroups(createSet("x*", "group2"), batchInfo, false);

    verifyGroupsInSet(result.getGroupsToBeCreated(), Sets.newHashSet("xgroup1", "xgroup2"));
    assertTrue(result.getGroupsToBeRemoved().isEmpty());
    assertTrue(result.getGroupsToBecomeLdap().isEmpty());
    assertTrue(result.getUsersToBeCreated().isEmpty());
    assertTrue(result.getMembershipToAdd().isEmpty());
    assertTrue(result.getMembershipToRemove().isEmpty());
    assertTrue(result.getUsersToBecomeLdap().isEmpty());
    assertTrue(result.getUsersToBeRemoved().isEmpty());
    verify(populator.loadLdapTemplate(), populator);
  }

  @SuppressWarnings("unchecked")
  @Test
  public void testSynchronizeLdapGroups_update() throws Exception {

    Group group1 = createNiceMock(Group.class);
    Group group2 = createNiceMock(Group.class);
    Group group3 = createNiceMock(Group.class);
    Group group4 = createNiceMock(Group.class);
    expect(group1.getGroupName()).andReturn("group1").anyTimes();
    expect(group2.getGroupName()).andReturn("group2").anyTimes();
    expect(group3.getGroupName()).andReturn("group3").anyTimes();
    expect(group4.getGroupName()).andReturn("group4").anyTimes();
    expect(group1.isLdapGroup()).andReturn(false).anyTimes();
    expect(group2.isLdapGroup()).andReturn(true).anyTimes();
    expect(group3.isLdapGroup()).andReturn(true).anyTimes();
    expect(group4.isLdapGroup()).andReturn(false).anyTimes();

    List<Group> groupList = Arrays.asList(group1, group2, group3, group4);

    final Provider<AmbariLdapConfiguration> configurationProvider = createNiceMock(Provider.class);
    AmbariLdapConfiguration configuration = createNiceMock(AmbariLdapConfiguration.class);
    expect(configurationProvider.get()).andReturn(configuration).anyTimes();
    Users users = createNiceMock(Users.class);
    LdapTemplate ldapTemplate = createNiceMock(LdapTemplate.class);
    LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class);
    expect(users.getAllGroups()).andReturn(groupList);
    expect(users.getAllUsers()).andReturn(Collections.emptyList());

    replay(ldapTemplate, ldapServerProperties, users, configurationProvider, configuration);
    replay(group1, group2, group3, group4);

    AmbariLdapDataPopulatorTestInstance populator = createMockBuilder(AmbariLdapDataPopulatorTestInstance.class)
        .addMockedMethod("getLdapGroups")
        .addMockedMethod("refreshGroupMembers")
        .withConstructor(configurationProvider, users)
        .createNiceMock();

    LdapGroupDto externalGroup1 = createNiceMock(LdapGroupDto.class);
    LdapGroupDto externalGroup2 = createNiceMock(LdapGroupDto.class);
    LdapGroupDto externalGroup3 = createNiceMock(LdapGroupDto.class);
    LdapGroupDto externalGroup4 = createNiceMock(LdapGroupDto.class);
    expect(externalGroup1.getGroupName()).andReturn("group1").anyTimes();
    expect(externalGroup2.getGroupName()).andReturn("group2").anyTimes();
    expect(externalGroup3.getGroupName()).andReturn("group3").anyTimes();
    expect(externalGroup4.getGroupName()).andReturn("group4").anyTimes();
    replay(externalGroup1, externalGroup2, externalGroup3, externalGroup4);

    LdapBatchDto batchInfo = new LdapBatchDto();
    Set<LdapGroupDto> externalGroups = createSet(externalGroup1, externalGroup2, externalGroup3, externalGroup4);
    for (LdapGroupDto externalGroup : externalGroups) {
      populator.refreshGroupMembers(eq(batchInfo), eq(externalGroup), EasyMock.anyObject(),
          EasyMock.anyObject(), EasyMock.anyObject(), anyBoolean(), eq(false));
      expectLastCall();
    }
    expect(populator.getLdapGroups("group*")).andReturn(externalGroups);
    replay(populator);

    populator.setLdapTemplate(ldapTemplate);
    populator.setLdapServerProperties(ldapServerProperties);

    LdapBatchDto result = populator.synchronizeLdapGroups(createSet("group*"), batchInfo, false);

    verifyGroupsInSet(result.getGroupsToBecomeLdap(), Sets.newHashSet("group1", "group4"));
    assertTrue(result.getGroupsToBeCreated().isEmpty());
    assertTrue(result.getGroupsToBeRemoved().isEmpty());
    assertTrue(result.getUsersToBeCreated().isEmpty());
    assertTrue(result.getMembershipToAdd().isEmpty());
    assertTrue(result.getMembershipToRemove().isEmpty());
    assertTrue(result.getUsersToBecomeLdap().isEmpty());
    assertTrue(result.getUsersToBeRemoved().isEmpty());
    verify(populator.loadLdapTemplate(), populator);
  }

  @SuppressWarnings("unchecked")
  @Test(expected = AmbariException.class)
  public void testSynchronizeLdapGroups_absent() throws Exception {

    Group group1 = createNiceMock(Group.class);
    Group group2 = createNiceMock(Group.class);
    Group group3 = createNiceMock(Group.class);
    Group group4 = createNiceMock(Group.class);
    expect(group1.getGroupName()).andReturn("group1").anyTimes();
    expect(group2.getGroupName()).andReturn("group2").anyTimes();
    expect(group3.getGroupName()).andReturn("group3").anyTimes();
    expect(group4.getGroupName()).andReturn("group4").anyTimes();
    expect(group1.isLdapGroup()).andReturn(false).anyTimes();
    expect(group2.isLdapGroup()).andReturn(true).anyTimes();
    expect(group3.isLdapGroup()).andReturn(true).anyTimes();
    expect(group4.isLdapGroup()).andReturn(false).anyTimes();

    List<Group> groupList = Arrays.asList(group1, group2, group3, group4);

    final Provider<AmbariLdapConfiguration> configurationProvider = createNiceMock(Provider.class);
    AmbariLdapConfiguration configuration = createNiceMock(AmbariLdapConfiguration.class);
    expect(configurationProvider.get()).andReturn(configuration).anyTimes();
    Users users = createNiceMock(Users.class);
    LdapTemplate ldapTemplate = createNiceMock(LdapTemplate.class);
    LdapServerProperties ldapServerProperties = createNiceMock(LdapServerProperties.class);
    expect(users.getAllGroups()).andReturn(groupList);
    expect(users.getAllUsers()).andReturn(Collections.emptyList());

    replay(ldapTemplate, ldapServerProperties, users, configurationProvider, configuration);
    replay(group1, group2, group3, group4);

    AmbariLdapDataPopulatorTestInstance populator = createMockBuilder(AmbariLdapDataPopulatorTestInstance.class)
        .addMockedMethod("getLdapGroups")
        .addMockedMethod("refreshGroupMembers")
        .withConstructor(configurationProvider, users)
        .createNiceMock();

    LdapGroupDto externalGroup1 = createNiceMock(LdapGroupDto.class);
    LdapGroupDto externalGroup2 = createNiceMock(LdapGroupDto.class);
    LdapGroupDto externalGroup3 = createNiceMock(LdapGroupDto.class);
    LdapGroupDto externalGroup4 = createNiceMock(LdapGroupDto.class);
    expect(externalGroup1.getGroupName()).andReturn("group1").anyTimes();
    expect(externalGroup2.getGroupName()).andReturn("group2").anyTimes();
    expect(externalGroup3.getGroupName()).andReturn("xgroup1").anyTimes();
    expect(externalGroup4.getGroupName()).andReturn("xgroup2").anyTimes();
    replay(externalGroup1, externalGroup2, externalGroup3, externalGroup4);

    LdapBatchDto batchInfo = new LdapBatchDto();
    Set<LdapGroupDto> externalGroups = createSet(externalGroup3, externalGroup4);
    expect(populator.getLdapGroups("x*")).andReturn(externalGroups);
    expect(populator.getLdapGroups("group1")).andReturn(Collections.emptySet());
    expect(populator.getLdapGroups("group2")).andReturn(Collections.singleton(externalGroup2));
