package org.ovirt.engine.core.bll.adbroker;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import java.net.URI;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
/**
* Tests GetRootDSE functionality In this test it is checked how GetRootDSE handles a various scenarios *
*/
public class DirectorySearcherTest extends AbstractLdapTest {
private static final String BAD_URL = "ldap://badurl.com:389";
private DirContext dirContext;
private static final ExecutorService executerService = Executors.newSingleThreadExecutor();;
@BeforeClass
public static void beforeClass() {
System.setProperty("jboss.server.log.dir", "/tmp");
}
@Override
@Before
public void setup() throws Exception {
super.setup();
MockitoAnnotations.initMocks(this);
dirContext = mockDirContext();
}
@Test
public void testGetRootDSEReachableServers() throws Exception {
List<URI> urls = new ArrayList<URI>();
urls.add(new URI("ldap://ldap1.example.com:389"));
urls.add(new URI("ldap://ldap2.example.com:389"));
DirectorySearcher dirSearcher = mockDirectorySearcher(urls);
GetRootDSETask task1 = mockRootDSETask(dirSearcher, "mydomain", urls.get(0));
GetRootDSETask task2 = mockRootDSETask(dirSearcher, "mydomain", urls.get(1));
try {
assertTrue(execute(task1));
assertTrue(execute(task2));
} catch (Exception e) {
Assert.fail("one of the servers was failed to return rootDSE record due to " + e.getMessage());
}
}
protected GetRootDSETask mockRootDSETask(DirectorySearcher dirSearcher, String domain, URI url) {
GetRootDSETask task = new GetRootDSETask(dirSearcher, domain, url);
task = spy(task);
doReturn(mockGetRootDSE(url)).when(task).createGetRootDSE(url);
return task;
}
@Test
public void testGetRootDSEFirstSeverUnreachable() throws Exception {
List<URI> urls = new ArrayList<URI>();
urls.add(new URI(BAD_URL));
urls.add(new URI("ldap://ldap1.example.com:389"));
DirectorySearcher dirSearcher = mockDirectorySearcher(urls);
try {
execute(new GetRootDSETask(dirSearcher, "mydomain", urls.get(0)));
} catch (Exception e) {
// server should timeout!
return;
}
Assert.fail("Get rootDSE task passed with no timeout. Investigate why the task completed instead of throwing TimeoutException");
}
@Test
public void testGetRootDSENoServers() throws Exception {
List<URI> urls = new ArrayList<URI>();
DirectorySearcher dirSearcher = mockDirectorySearcher(urls);
try {
GetRootDSETask getRootDSETask = new GetRootDSETask(dirSearcher, "mydomain", urls.get(0));
execute(getRootDSETask);
} catch (Exception e) {
// should fail
return;
}
Assert.fail("Task didn't exited with error");
}
@Test
public void testGetRootDSENoReachableLdapServers() throws Exception {
List<URI> urls = new ArrayList<URI>();
urls.add(new URI(BAD_URL));
DirectorySearcher dirSearcher = mockDirectorySearcher(urls);
try {
execute(new GetRootDSETask(dirSearcher, "mydomain", urls.get(0)));
} catch (Exception e) {
// non reachable domain should timeout
if (e instanceof TimeoutException) {
return;
}
}
Assert.fail("Task ended with error which is not timout or no error at all");
}
@SuppressWarnings("unchecked")
private DirectorySearcher mockDirectorySearcher(final List<URI> urls) throws NamingException {
DirectorySearcher dirSearcher = spy(new DirectorySearcher(new LdapCredentials("username", "password")));
doAnswer(new Answer<Domain>() {
@Override
public Domain answer(InvocationOnMock invocation) throws Throwable {
return mockDomainObject(urls);
}
}).when(dirSearcher).getDomainObject(any(String.class));
doAnswer(new Answer<GetRootDSE>() {
@Override
public GetRootDSE answer(InvocationOnMock invocation) throws Throwable {
URI uri = (URI) invocation.getArguments()[0];
return mockGetRootDSE(uri);
}
}).when(dirSearcher).createRootDSE(any(URI.class));
return dirSearcher;
}
private Domain mockDomainObject(List<URI> urls) {
final Domain domain = new Domain("");
domain.setLdapServers(urls);
domain.setLdapServers(urls);
domain.setLdapProviderType(LdapProviderType.general);
return domain;
}
@SuppressWarnings("unchecked")
private GetRootDSE mockGetRootDSE(URI uri) {
GetRootDSE getRootDSE = spy(new GetRootDSE(uri));
try {
doReturn(dirContext).when(getRootDSE).createContext(any(Hashtable.class));
} catch (NamingException e) {
}
doAnswer(new Answer<URI>() {
@Override
public URI answer(InvocationOnMock invocation) throws Throwable {
URI uri = (URI) invocation.callRealMethod();
setValidProvider(!uri.toString().equals(BAD_URL));
return uri;
}
}).when(getRootDSE).getLdapURI();
return getRootDSE;
}
private Boolean execute(GetRootDSETask task) throws InterruptedException, ExecutionException, TimeoutException {
// Execution timeout after 20 seconds.
// Why 20?
// Currently GetRootDSE swallows NamingException that is
// thrown during a socket connect, causing the callable to exit with
// no Timeout exception at all, leading to false test results.
//
// To satisfy different machines and enviornments this call will timeout
// earlier than most resonable tcp/ip setups
return executerService.submit(task).get(20, TimeUnit.SECONDS);
}
}